Files
Flora/qml/panels/RightPanel.qml
T

221 lines
7.6 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()
}
}
}
}
}
}