Files
Flora/qml/panels/RightPanel.qml
T

221 lines
7.6 KiB
QML
Raw Normal View History

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
// Правая панель: календарь (заметки дня), «Ссылки и теги» активного файла,
// граф связей (файлы + теги, рёбра по ссылкам).
Item {
id: root
function activePath() {
return documentManager.pathAt(documentManager.activeIndex)
}
function refreshLinksTags() {
const p = root.activePath()
linksModel.clear()
if (p.length > 0) {
const links = vaultIndex.linksOfFile(p)
for (let i = 0; i < links.length; ++i) {
linksModel.append({ text: links[i].text, path: links[i].path })
}
}
tagsModel.clear()
const tags = p.length > 0 ? vaultIndex.tagsOfFile(p) : []
for (let i = 0; i < tags.length; ++i)
tagsModel.append({ tag: tags[i] })
}
Component.onCompleted: {
if (fileStore.ready)
vaultIndex.rebuild(fileStore.vault)
}
Connections {
target: fileStore
function onVaultChanged() {
if (fileStore.ready)
vaultIndex.rebuild(fileStore.vault)
}
function onReadyChanged() {
if (fileStore.ready)
vaultIndex.rebuild(fileStore.vault)
}
}
Connections {
target: documentManager
function onActiveIndexChanged() { root.refreshLinksTags() }
function onOpenTabsChanged() { root.refreshLinksTags() }
function onContentChanged(i) {
if (i === documentManager.activeIndex) root.refreshLinksTags()
}
}
Connections {
target: vaultIndex
function onIndexChanged() { root.refreshLinksTags() }
}
ColumnLayout {
anchors.fill: parent
spacing: 0
// ---------- Календарь ----------
Rectangle {
Layout.fillWidth: true
implicitHeight: 218
color: "#fafafa"
ColumnLayout {
anchors.fill: parent
spacing: 0
Label {
text: qsTr("Календарь")
font.bold: true
color: "#555"
padding: 6
}
MonthCalendar {
Layout.fillWidth: true
Layout.fillHeight: true
onDaySelected: (ds) => {
const path = fileStore.dailyNotePath(ds)
if (path.length > 0)
documentManager.openDocument(path)
}
} }
}
// ---------- Ссылки и теги активного файла ----------
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "#f4f4f4"
ColumnLayout {
anchors.fill: parent
anchors.margins: 6
spacing: 4
Label {
text: qsTr("Ссылки и теги")
font.bold: true
color: "#555"
}
Label {
text: root.activePath().length > 0
? qsTr("Файл: %1").arg(root.baseName(activePath()))
: qsTr("Нет активного файла")
elide: Text.ElideMiddle
color: "#888"
Layout.fillWidth: true
font.pixelSize: 11
}
Label { text: qsTr("Ссылки"); color: "#666"; font.pixelSize: 11 }
ListView {
id: linksList
Layout.fillWidth: true
Layout.preferredHeight: Math.min(120, linksModel.count * 22)
model: ListModel { id: linksModel }
clip: true
delegate: ItemDelegate {
width: ListView.view.width
height: 22
text: model.text
font.pixelSize: 12
onClicked: documentManager.openDocument(model.path)
}
}
Label {
visible: linksModel.count === 0
text: qsTr("— нет ссылок —")
color: "#bbb"
font.pixelSize: 11
}
Label { text: qsTr("Теги"); color: "#666"; font.pixelSize: 11 }
Flow {
Layout.fillWidth: true
readonly property var m: ListModel { id: tagsModel }
Repeater {
model: tagsModel
delegate: TagChip {
text: "#" + model.tag
onClicked: {
tagFilesModel.clear()
const files = vaultIndex.filesWithTag(model.tag)
for (let i = 0; i < files.length; ++i)
tagFilesModel.append({ path: files[i], name: baseName(files[i]) })
tagFilesTitle.text = qsTr("Файлы с тегом #%1 (%2)").arg(model.tag).arg(files.length)
tagFilesPopup.open()
}
}
}
}
Item { Layout.fillHeight: true }
}
}
// ---------- Граф связей ----------
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "#eef1f5"
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.fillWidth: true
Label {
text: qsTr("Граф связей")
font.bold: true
color: "#555"
padding: 6
}
Item { Layout.fillWidth: true }
Button {
text: qsTr("Обновить")
font.pixelSize: 11
implicitHeight: 22
onClicked: vaultIndex.rebuild(fileStore.vault)
}
}
GraphView {
Layout.fillWidth: true
Layout.fillHeight: true
nodes: vaultIndex.nodes
edges: vaultIndex.edges
onFileClicked: (path) => documentManager.openDocument(path)
}
}
}
}
function baseName(p) {
const idx = p.lastIndexOf("/")
return idx >= 0 ? p.substring(idx + 1) : p
}
Popup {
id: tagFilesPopup
modal: true
anchors.centerIn: parent
width: 300
ColumnLayout {
width: parent.width
Label { id: tagFilesTitle; font.bold: true }
ListView {
Layout.preferredHeight: Math.min(220, contentHeight)
Layout.fillWidth: true
model: ListModel { id: tagFilesModel }
clip: true
delegate: ItemDelegate {
width: ListView.view.width
text: model.name
onClicked: {
documentManager.openDocument(model.path)
tagFilesPopup.close()
}
}
}
}
}
}