From d6452c3cdc1f3a0a00bbee06936f8b65cf71006f Mon Sep 17 00:00:00 2001 From: Ross Keenan Date: Fri, 19 Nov 2021 11:26:05 +0200 Subject: [PATCH] feat(List/Matrix View): :sparkles: Metadata field to sort items in L/M view! (#92) --- main.js | 72 ++++++++++++++------ src/BreadcrumbsSettingTab.ts | 20 +++++- src/MatrixView.ts | 21 ++++++ src/constants.ts | 1 + src/interfaces.ts | 8 ++- src/main.ts | 22 ++++++- src/sharedFunctions.ts | 123 ++++++++++++++++------------------- 7 files changed, 172 insertions(+), 95 deletions(-) diff --git a/main.js b/main.js index 011bbb05..44d84b32 100644 --- a/main.js +++ b/main.js @@ -2606,6 +2606,7 @@ const DEFAULT_SETTINGS = { dvWaitTime: 5000, refreshIntervalTime: 0, defaultView: true, + orderField: "order", showNameOrType: true, showRelationType: true, filterImpliedSiblingsOfDifferentTypes: false, @@ -20072,15 +20073,13 @@ function getFieldValues(frontmatterCache, field, settings) { const strs = splits.map((link) => link.match(dropHeaderOrAlias)[1].split("/").last()); values.push(...strs); } - else { + else values.push(rawItemAsString.split("/").last()); - } } else if (value.path !== undefined) { const lastSplit = value.path.split("/").last(); - if (lastSplit !== undefined) { + if (lastSplit !== undefined) values.push(lastSplit); - } } }); }); @@ -20107,23 +20106,21 @@ async function getNeighbourObjArr(plugin, fileFrontmatterArr) { } let jugglLinks = []; if (plugin.app.plugins.plugins.juggl !== undefined || - plugin.settings.parseJugglLinksWithoutJuggl) { - jugglLinks = await getJugglLinks(plugin.app, plugin.settings); + settings.parseJugglLinksWithoutJuggl) { + jugglLinks = await getJugglLinks(plugin.app, settings); } const neighbourObjArr = fileFrontmatterArr.map((fileFrontmatter) => { + var _a; + const { file } = fileFrontmatter; + const order = (_a = fileFrontmatter[settings.orderField]) !== null && _a !== void 0 ? _a : 9999; const currNode = fileFrontmatter.file.basename || fileFrontmatter.file.name; const hierFields = { - current: fileFrontmatter.file, + current: file, + order, hierarchies: [], }; userHierarchies.forEach((hier) => { - const newHier = { - up: {}, - same: {}, - down: {}, - next: {}, - prev: {}, - }; + const newHier = blankDirObjs(); // Add regular metadata links if (settings.useAllMetadata) { for (const dir of DIRECTIONS) { @@ -20406,8 +20403,8 @@ function addNodeIfNot(g, node, attr) { g.addNode(node, attr); } function addEdgeIfNot(g, source, target, attr) { - addNodeIfNot(g, source); - addNodeIfNot(g, target); + // addNodeIfNot(g, source, attr); + // addNodeIfNot(g, target, attr); if (!g.hasEdge(source, target)) g.addEdge(source, target, attr); } @@ -22110,6 +22107,7 @@ class MatrixView extends obsidian.ItemView { constructor(leaf, plugin) { super(leaf); this.icon = TRAIL_ICON; + this.getOrder = (node) => Number.parseInt(this.plugin.currGraphs.main.getNodeAttribute(node, "order")); this.plugin = plugin; } async onload() { @@ -22202,6 +22200,7 @@ class MatrixView extends obsidian.ItemView { to, cls: linkClass(this.app, to, realQ), alt: this.getAlt(to, settings), + order: this.getOrder(to), }); }); return internalLinkObjArr; @@ -22305,6 +22304,7 @@ class MatrixView extends obsidian.ItemView { to: impliedSibling, cls: linkClass(this.app, impliedSibling, false), alt: this.getAlt(impliedSibling, settings), + order: this.getOrder(impliedSibling), }); }); }); @@ -22326,6 +22326,7 @@ class MatrixView extends obsidian.ItemView { to, cls: linkClass(this.app, to, item.real), alt: this.getAlt(to, settings), + order: this.getOrder(to), }; }); }); @@ -22348,6 +22349,18 @@ class MatrixView extends obsidian.ItemView { ? `${hier[getOppDir(dir)].join(",")}<${dir}]>` : hier[dir].join(", "); }; + [ + rUp, + rSame, + rDown, + rNext, + rPrev, + iUp, + iSameArr, + iDown, + iNext, + iPrev, + ].forEach((a) => a.sort((a, b) => a.order - b.order)); return [ { realItems: rUp, @@ -23573,6 +23586,14 @@ class BCSettingTab extends obsidian.PluginSettingTab { await plugin.saveSettings(); await plugin.getActiveMatrixView().draw(); })); + new obsidian.Setting(MLViewDetails) + .setName("Sorting Field Name") + .setDesc("The metadata field name used to indicate the order in which items should be sorted in the L/M view.") + .addText((text) => text.setValue(settings.orderField).onChange(async (value) => { + settings.orderField = value; + await plugin.saveSettings(); + await plugin.getActiveMatrixView().draw(); + })); new obsidian.Setting(MLViewDetails) .setName("Filter Implied Siblings") .setDesc("Implied siblings are: 1) notes with the same parent, or 2) notes that are real siblings. This setting only applies to type 1 implied siblings. If enabled, Breadcrumbs will filter type 1 implied siblings so that they not only share the same parent, but the parent relation has the exact same type. For example, the two real relations B --parent-> A, and A --parent-> A create an implied sibling between B and C (they have the same parent, A). The two real relations B --parent-> A, and A --up-> A create an implied sibling between B and C (they also have the same parent, A). But if this setting is turned on, the second implied sibling would not show, because the parent types are differnet (parent versus up).") @@ -23591,7 +23612,7 @@ class BCSettingTab extends obsidian.PluginSettingTab { settings.rlLeaf = value; await plugin.saveSettings(); await ((_a = plugin.getActiveMatrixView()) === null || _a === void 0 ? void 0 : _a.onClose()); - await openView(this.app, MATRIX_VIEW, MatrixView); + await openView(this.app, MATRIX_VIEW, MatrixView, value ? "right" : "left"); })); const trailDetails = containerEl.createEl("details"); trailDetails.createEl("summary", { text: "Trail/Grid" }); @@ -34937,6 +34958,7 @@ class BCPlugin extends obsidian.Plugin { super(...arguments); this.visited = []; this.activeLeafChange = undefined; + this.statusBatItemEl = undefined; this.initEverything = async () => { const { settings } = this; this.currGraphs = await this.initGraphs(); @@ -35147,6 +35169,7 @@ class BCPlugin extends obsidian.Plugin { }); // TODO get a better icon for this this.addRibbonIcon("dice", "Breadcrumbs Visualisation", () => new VisModal(this.app, this).open()); + this.statusBatItemEl = this.addStatusBarItem(); this.addSettingTab(new BCSettingTab(this.app, this)); } getActiveMatrixView() { @@ -35258,7 +35281,6 @@ class BCPlugin extends obsidian.Plugin { const CSVRows = useCSV ? await this.getCSVRows() : []; neighbourObjArr.forEach((neighbours) => { const currFileName = neighbours.current.basename || neighbours.current.name; - addNodeIfNot(graphs.main, currFileName); neighbours.hierarchies.forEach((hier, i) => { DIRECTIONS.forEach((dir) => { for (const fieldName in hier[dir]) { @@ -35266,8 +35288,18 @@ class BCPlugin extends obsidian.Plugin { const targets = hier[dir][fieldName]; this.populateGraph(g, currFileName, targets, dir, fieldName); targets.forEach((target) => { - // addEdgeIfNot also addsNodeIfNot - // addNodeIfNot(graphs.main, target); + var _a, _b; + addNodeIfNot(graphs.main, currFileName, { + dir, + fieldName, + order: neighbours.order, + }); + addNodeIfNot(graphs.main, target, { + dir, + fieldName, + order: (_b = (_a = neighbourObjArr.find((neighbour) => (neighbour.current.basename || neighbour.current.name) === + target)) === null || _a === void 0 ? void 0 : _a.order) !== null && _b !== void 0 ? _b : 9999, + }); addEdgeIfNot(graphs.main, currFileName, target, { dir, fieldName, diff --git a/src/BreadcrumbsSettingTab.ts b/src/BreadcrumbsSettingTab.ts index a189b931..dba54f6c 100644 --- a/src/BreadcrumbsSettingTab.ts +++ b/src/BreadcrumbsSettingTab.ts @@ -323,6 +323,19 @@ export class BCSettingTab extends PluginSettingTab { }) ); + new Setting(MLViewDetails) + .setName("Sorting Field Name") + .setDesc( + "The metadata field name used to indicate the order in which items should be sorted in the L/M view." + ) + .addText((text) => + text.setValue(settings.orderField).onChange(async (value) => { + settings.orderField = value; + await plugin.saveSettings(); + await plugin.getActiveMatrixView().draw(); + }) + ); + new Setting(MLViewDetails) .setName("Filter Implied Siblings") .setDesc( @@ -348,7 +361,12 @@ export class BCSettingTab extends PluginSettingTab { settings.rlLeaf = value; await plugin.saveSettings(); await plugin.getActiveMatrixView()?.onClose(); - await openView(this.app, MATRIX_VIEW, MatrixView); + await openView( + this.app, + MATRIX_VIEW, + MatrixView, + value ? "right" : "left" + ); }) ); diff --git a/src/MatrixView.ts b/src/MatrixView.ts index a82ba254..56582436 100644 --- a/src/MatrixView.ts +++ b/src/MatrixView.ts @@ -169,6 +169,7 @@ export default class MatrixView extends ItemView { to, cls: linkClass(this.app, to, realQ), alt: this.getAlt(to, settings), + order: this.getOrder(to), }); }); @@ -272,6 +273,11 @@ export default class MatrixView extends ItemView { return index; } + getOrder = (node: string) => + Number.parseInt( + this.plugin.currGraphs.main.getNodeAttribute(node, "order") + ); + getHierSquares( userHierarchies: userHierarchy[], data: { [dir in Directions]: Graph }[], @@ -329,6 +335,7 @@ export default class MatrixView extends ItemView { to: impliedSibling, cls: linkClass(this.app, impliedSibling, false), alt: this.getAlt(impliedSibling, settings), + order: this.getOrder(impliedSibling), }); }); }); @@ -356,6 +363,7 @@ export default class MatrixView extends ItemView { to, cls: linkClass(this.app, to, item.real), alt: this.getAlt(to, settings), + order: this.getOrder(to), }; }); }); @@ -381,6 +389,19 @@ export default class MatrixView extends ItemView { : hier[dir].join(", "); }; + [ + rUp, + rSame, + rDown, + rNext, + rPrev, + iUp, + iSameArr, + iDown, + iNext, + iPrev, + ].forEach((a) => a.sort((a, b) => a.order - b.order)); + return [ { realItems: rUp, diff --git a/src/constants.ts b/src/constants.ts index 29ef8305..533d923a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -76,6 +76,7 @@ export const DEFAULT_SETTINGS: BCSettings = { dvWaitTime: 5000, refreshIntervalTime: 0, defaultView: true, + orderField: "order", showNameOrType: true, showRelationType: true, filterImpliedSiblingsOfDifferentTypes: false, diff --git a/src/interfaces.ts b/src/interfaces.ts index c5bf2fcd..ca4f9b07 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -16,6 +16,7 @@ export interface BCSettings { dvWaitTime: number; refreshIntervalTime: number; defaultView: boolean; + orderField: string; showNameOrType: boolean; showRelationType: boolean; filterImpliedSiblingsOfDifferentTypes: boolean; @@ -81,9 +82,9 @@ export interface JugglLink { export interface neighbourObj { current: TFile; - parents: string[]; - siblings: string[]; - children: string[]; + /** DV only recognises it if it's a string? */ + order: string; + hierarchies: HierarchyFields[]; } export type relObj = { [key: string]: string[] } | { current: TFile }; @@ -102,6 +103,7 @@ export interface internalLinkObj { to: string; cls: string; alt: string | null; + order: number; } export interface SquareProps { diff --git a/src/main.ts b/src/main.ts index c475fe36..194059ca 100644 --- a/src/main.ts +++ b/src/main.ts @@ -62,6 +62,7 @@ export default class BCPlugin extends Plugin { refreshIntervalID: number; currGraphs: BCIndex; activeLeafChange: EventRef = undefined; + statusBatItemEl: HTMLElement = undefined; async refreshIndex() { if (!this.activeLeafChange) this.registerActiveLeafEvent(); @@ -230,6 +231,8 @@ export default class BCPlugin extends Plugin { new VisModal(this.app, this).open() ); + this.statusBatItemEl = this.addStatusBarItem(); + this.addSettingTab(new BCSettingTab(this.app, this)); } @@ -476,16 +479,29 @@ export default class BCPlugin extends Plugin { neighbourObjArr.forEach((neighbours) => { const currFileName = neighbours.current.basename || neighbours.current.name; - addNodeIfNot(graphs.main, currFileName); neighbours.hierarchies.forEach((hier, i) => { DIRECTIONS.forEach((dir) => { for (const fieldName in hier[dir]) { const g = graphs.hierGs[i][dir][fieldName]; const targets = hier[dir][fieldName]; + this.populateGraph(g, currFileName, targets, dir, fieldName); targets.forEach((target) => { - // addEdgeIfNot also addsNodeIfNot - // addNodeIfNot(graphs.main, target); + addNodeIfNot(graphs.main, currFileName, { + dir, + fieldName, + order: neighbours.order, + }); + addNodeIfNot(graphs.main, target, { + dir, + fieldName, + order: + neighbourObjArr.find( + (neighbour) => + (neighbour.current.basename || neighbour.current.name) === + target + )?.order ?? 9999, + }); addEdgeIfNot(graphs.main, currFileName, target, { dir, fieldName, diff --git a/src/sharedFunctions.ts b/src/sharedFunctions.ts index bfda65e4..54301b06 100644 --- a/src/sharedFunctions.ts +++ b/src/sharedFunctions.ts @@ -11,6 +11,7 @@ import { } from "obsidian"; import { ARROW_DIRECTIONS, + blankDirObjs, DIRECTIONS, dropHeaderOrAlias, splitLinksRegex, @@ -24,6 +25,7 @@ import type { HierarchyFields, HierarchyGraphs, JugglLink, + neighbourObj, PrevNext, userHierarchy, } from "src/interfaces"; @@ -299,14 +301,10 @@ export function getFieldValues( link.match(dropHeaderOrAlias)[1].split("/").last() ); values.push(...strs); - } else { - values.push(rawItemAsString.split("/").last()); - } + } else values.push(rawItemAsString.split("/").last()); } else if (value.path !== undefined) { const lastSplit = value.path.split("/").last(); - if (lastSplit !== undefined) { - values.push(lastSplit); - } + if (lastSplit !== undefined) values.push(lastSplit); } }); }); @@ -330,12 +328,7 @@ export const splitAndTrim = (fields: string): string[] => export async function getNeighbourObjArr( plugin: BCPlugin, fileFrontmatterArr: dvFrontmatterCache[] -): Promise< - { - current: TFile; - hierarchies: HierarchyFields[]; - }[] -> { +): Promise { const { settings } = plugin; const { userHierarchies } = settings; @@ -346,71 +339,65 @@ export async function getNeighbourObjArr( let jugglLinks: JugglLink[] = []; if ( plugin.app.plugins.plugins.juggl !== undefined || - plugin.settings.parseJugglLinksWithoutJuggl + settings.parseJugglLinksWithoutJuggl ) { - jugglLinks = await getJugglLinks(plugin.app, plugin.settings); + jugglLinks = await getJugglLinks(plugin.app, settings); } - const neighbourObjArr: { - current: TFile; - hierarchies: HierarchyFields[]; - }[] = fileFrontmatterArr.map((fileFrontmatter) => { - const currNode = fileFrontmatter.file.basename || fileFrontmatter.file.name; - const hierFields: { - current: TFile; - hierarchies: HierarchyFields[]; - } = { - current: fileFrontmatter.file, - hierarchies: [], - }; - - userHierarchies.forEach((hier) => { - const newHier: HierarchyFields = { - up: {}, - same: {}, - down: {}, - next: {}, - prev: {}, + const neighbourObjArr: neighbourObj[] = fileFrontmatterArr.map( + (fileFrontmatter) => { + const { file } = fileFrontmatter; + const order = fileFrontmatter[settings.orderField] ?? 9999; + const currNode = + fileFrontmatter.file.basename || fileFrontmatter.file.name; + const hierFields: neighbourObj = { + current: file, + order, + hierarchies: [], }; - // Add regular metadata links - if (settings.useAllMetadata) { - for (const dir of DIRECTIONS) { - hier[dir].forEach((field) => { - newHier[dir][field] = getFieldValues( - fileFrontmatter, - field, - settings - ); - }); + userHierarchies.forEach((hier) => { + const newHier: HierarchyFields = blankDirObjs(); + + // Add regular metadata links + if (settings.useAllMetadata) { + for (const dir of DIRECTIONS) { + hier[dir].forEach((field) => { + newHier[dir][field] = getFieldValues( + fileFrontmatter, + field, + settings + ); + }); + } } - } - - // Add Juggl Links - if (jugglLinks.length) { - const jugglLinksInFile = jugglLinks.find((jugglLink) => { - return jugglLink.note === currNode; - }); - if (jugglLinksInFile) { - jugglLinksInFile.links.forEach((line) => { - if ((hier[line.dir] as string[])?.includes(line.type)) { - newHier[line.dir][line.type] = [ - ...new Set([ - ...(newHier[line.dir][line.type] ?? []), - ...line.linksInLine, - ]), - ]; - } + // Add Juggl Links + if (jugglLinks.length) { + const jugglLinksInFile = jugglLinks.find((jugglLink) => { + return jugglLink.note === currNode; }); + + if (jugglLinksInFile) { + jugglLinksInFile.links.forEach((line) => { + if ((hier[line.dir] as string[])?.includes(line.type)) { + newHier[line.dir][line.type] = [ + ...new Set([ + ...(newHier[line.dir][line.type] ?? []), + ...line.linksInLine, + ]), + ]; + } + }); + } } - } - hierFields.hierarchies.push(newHier); - }); + hierFields.hierarchies.push(newHier); + }); - return hierFields; - }); + return hierFields; + } + ); debug(settings, { neighbourObjArr }); if (settings.debugMode || settings.superDebugMode) { @@ -780,8 +767,8 @@ export function addEdgeIfNot( target: string, attr?: NodeAttributes ) { - addNodeIfNot(g, source); - addNodeIfNot(g, target); + // addNodeIfNot(g, source, attr); + // addNodeIfNot(g, target, attr); if (!g.hasEdge(source, target)) g.addEdge(source, target, attr); }