From ba083960abd68d0555070a50ea4a6bfc70d9d140 Mon Sep 17 00:00:00 2001 From: Ross Keenan Date: Wed, 8 Sep 2021 21:07:42 +0200 Subject: [PATCH] feat(LimitTrailFields): :sparkles: Ability to limit which fields are shown in the trail/grid view --- main.js | 94 +++++++++++++++++++++++++++++++++++- src/BreadcrumbsSettingTab.ts | 60 +++++++++++++++++++++++ src/interfaces.ts | 2 + src/main.ts | 43 ++++++++++++++--- src/sharedFunctions.ts | 8 +++ 5 files changed, 197 insertions(+), 10 deletions(-) diff --git a/main.js b/main.js index 329c8e8c..639bf324 100644 --- a/main.js +++ b/main.js @@ -6614,6 +6614,21 @@ function getAllGsInDir(userHierarchies, currGraphs, dir) { // console.log({ allXGs, allGsInDir }); return allGsInDir; } +function getAllFieldGs(fields, currGraphs) { + const fieldGs = []; + currGraphs.forEach(hierGs => { + DIRECTIONS.forEach(dir => { + Object.keys(hierGs[dir]).forEach(fieldName => { + if (fields.includes(fieldName)) { + const fieldG = hierGs[dir][fieldName]; + if (fieldG instanceof graphlib.Graph) + fieldGs.push(fieldG); + } + }); + }); + }); + return fieldGs; +} function hierToStr(hier) { return `↑: ${hier.up.join(", ")} →: ${hier.same.join(", ")} @@ -6670,7 +6685,15 @@ const writeBCToFile = (app, plugin, currGraphs, file) => { }); }); }); -}; +}; +function oppFields(field, dir, userHierarchies) { + var _a, _b; + let oppDir = 'same'; + if (dir !== "same") { + oppDir = dir === "up" ? "down" : "up"; + } + return (_b = (_a = userHierarchies.find(hier => hier[oppDir].includes(field))) === null || _a === void 0 ? void 0 : _a[oppDir]) !== null && _b !== void 0 ? _b : []; +} /** * @license @@ -24258,6 +24281,18 @@ class BreadcrumbsSettingTab extends obsidian.PluginSettingTab { saveButton.toggleClass("hierarchy-unsaved", true); saveButton.textContent = "Save"; })); + async function resetLimitTrailCheckboxes() { + settings.limitTrailCheckboxStates = {}; + settings.userHierarchies.forEach(userHier => { + userHier.up.forEach(async (field) => { + // First sort out limitTrailCheckboxStates + settings.limitTrailCheckboxStates[field] = true; + await plugin.saveSettings(); + }); + }); + await plugin.saveSettings(); + drawLimitTrailCheckboxes(checkboxDiv); + } row.createEl("button", { text: "X" }, (el) => { el.addEventListener("click", async () => { row.remove(); @@ -24266,6 +24301,8 @@ class BreadcrumbsSettingTab extends obsidian.PluginSettingTab { settings.userHierarchies.splice(removeIndex, 1); await plugin.saveSettings(); } + // Refresh limitTrailFields + resetLimitTrailCheckboxes(); new obsidian.Notice("Hierarchy Removed."); }); }); @@ -24283,6 +24320,7 @@ class BreadcrumbsSettingTab extends obsidian.PluginSettingTab { if (removeIndex > -1) { settings.userHierarchies.splice(removeIndex, 1); await plugin.saveSettings(); + resetLimitTrailCheckboxes(); } } cleanInputs = [upInput.value, sameInput.value, downInput.value].map(splitAndTrim); @@ -24299,6 +24337,7 @@ class BreadcrumbsSettingTab extends obsidian.PluginSettingTab { }); await plugin.saveSettings(); new obsidian.Notice("Hierarchy saved."); + resetLimitTrailCheckboxes(); } }); }); @@ -24563,6 +24602,33 @@ class BreadcrumbsSettingTab extends obsidian.PluginSettingTab { await plugin.saveSettings(); await plugin.drawTrail(); })); + const limitTrailFieldsDiv = trailDetails.createDiv({ cls: 'limit-ML-fields' }); + limitTrailFieldsDiv.createEl('strong', { 'text': 'Limit M/L View to only show certain fields' }); + const checkboxDiv = limitTrailFieldsDiv.createDiv({ cls: 'checkboxes' }); + function drawLimitTrailCheckboxes(div) { + checkboxDiv.empty(); + const checkboxStates = settings.limitTrailCheckboxStates; + settings.userHierarchies.forEach(userHier => { + userHier.up.forEach(async (field) => { + // First sort out limitTrailCheckboxStates + if (checkboxStates[field] === undefined) { + checkboxStates[field] = true; + await plugin.saveSettings(); + } + const cbDiv = div.createDiv(); + const checkedQ = checkboxStates[field]; + const cb = cbDiv.createEl('input', { type: 'checkbox', attr: { id: field } }); + cb.checked = checkedQ; + cbDiv.createEl('label', { text: field, attr: { for: field } }); + cb.addEventListener('change', async (event) => { + checkboxStates[field] = cb.checked; + await plugin.saveSettings(); + console.log(settings.limitTrailCheckboxStates); + }); + }); + }); + } + drawLimitTrailCheckboxes(checkboxDiv); new obsidian.Setting(trailDetails) .setName("Field name to hide trail") .setDesc("A note-specific toggle to hide the Trail View. By default, it is `hide-trail`. So, to hide the trail on a specific note, add the field to that note's yaml, like so: `hide-trail: {{anything}}`.") @@ -24784,6 +24850,9 @@ class BreadcrumbsSettingTab extends obsidian.PluginSettingTab { settings.superDebugMode = value; await plugin.saveSettings(); })); + debugDetails.createEl('button', { text: 'Console log `settings`' }, (el) => { + el.addEventListener('click', () => console.log(settings)); + }); new KoFi({ target: this.containerEl }); } } @@ -37022,6 +37091,7 @@ const DEFAULT_SETTINGS = { filterImpliedSiblingsOfDifferentTypes: false, rlLeaf: true, showTrail: true, + limitTrailCheckboxStates: {}, hideTrailFieldName: 'hide-trail', trailOrTable: 3, gridDots: false, @@ -37376,6 +37446,7 @@ class BreadcrumbsPlugin extends obsidian.Plugin { hierGs: [], mergedGs: { up: undefined, same: undefined, down: undefined }, closedGs: { up: undefined, same: undefined, down: undefined }, + limitTrailG: undefined }; userHierarchies.forEach((hier, i) => { const newGraphs = { up: {}, same: {}, down: {} }; @@ -37436,6 +37507,25 @@ class BreadcrumbsPlugin extends obsidian.Plugin { graphs.closedGs[dir] = closeImpliedLinks(graphs.mergedGs[dir], graphs.mergedGs[dir]); } }); + // LimitTrailG + if (Object.values(settings.limitTrailCheckboxStates).every(val => val)) { + graphs.limitTrailG = graphs.closedGs.up; + } + else { + const allUps = getAllGsInDir(userHierarchies, graphs.hierGs, 'up'); + const allLimitedTrailsGsKeys = Object.keys(allUps).filter(field => settings.limitTrailCheckboxStates[field]); + const allLimitedTrailsGs = []; + allLimitedTrailsGsKeys.forEach(key => allLimitedTrailsGs.push(allUps[key])); + const mergedLimitedUpGs = mergeGs(...allLimitedTrailsGs); + const allLimitedDownGs = []; + Object.keys(settings.limitTrailCheckboxStates).forEach(limitedField => { + const oppFieldsArr = oppFields(limitedField, 'up', userHierarchies); + const oppGs = getAllFieldGs(oppFieldsArr, graphs.hierGs); + allLimitedDownGs.push(...oppGs); + }); + const mergedLimitedDownGs = mergeGs(...allLimitedDownGs); + graphs.limitTrailG = closeImpliedLinks(mergedLimitedUpGs, mergedLimitedDownGs); + } debug(settings, "graphs inited"); debug(settings, { graphs }); debugGroupEnd(settings, "debugMode"); @@ -37545,7 +37635,7 @@ class BreadcrumbsPlugin extends obsidian.Plugin { debugGroupEnd(settings, "debugMode"); return; } - const closedUp = this.currGraphs.closedGs.up; + const closedUp = this.currGraphs.limitTrailG; const sortedTrails = this.getBreadcrumbs(closedUp, currFile); debug(settings, { sortedTrails }); // Get the container div of the active note diff --git a/src/BreadcrumbsSettingTab.ts b/src/BreadcrumbsSettingTab.ts index 20fa5b13..abc61185 100644 --- a/src/BreadcrumbsSettingTab.ts +++ b/src/BreadcrumbsSettingTab.ts @@ -8,6 +8,7 @@ import { } from "obsidian"; import { ALLUNLINKED, + DIRECTIONS, REAlCLOSED, RELATIONS, VIEW_TYPE_BREADCRUMBS_MATRIX, @@ -80,6 +81,20 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { }) ); + async function resetLimitTrailCheckboxes() { + settings.limitTrailCheckboxStates = {} + settings.userHierarchies.forEach(userHier => { + userHier.up.forEach(async field => { + // First sort out limitTrailCheckboxStates + settings.limitTrailCheckboxStates[field] = true; + await plugin.saveSettings() + + }) + }) + await plugin.saveSettings() + drawLimitTrailCheckboxes(checkboxDiv) + } + const deleteButton = row.createEl("button", { text: "X" }, (el) => { el.addEventListener("click", async () => { row.remove(); @@ -94,6 +109,10 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { settings.userHierarchies.splice(removeIndex, 1); await plugin.saveSettings(); } + + // Refresh limitTrailFields + resetLimitTrailCheckboxes() + new Notice("Hierarchy Removed."); }); }); @@ -128,6 +147,7 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { if (removeIndex > -1) { settings.userHierarchies.splice(removeIndex, 1); await plugin.saveSettings(); + resetLimitTrailCheckboxes() } } cleanInputs = [upInput.value, sameInput.value, downInput.value].map( @@ -149,6 +169,8 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { }); await plugin.saveSettings(); new Notice("Hierarchy saved."); + + resetLimitTrailCheckboxes() } }); } @@ -424,6 +446,7 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { }) ); + // TODO I don't think this setting works anymore. I removed it's functionality when adding multiple hierarchies new Setting(MLViewDetails) .setName("Show all field names or just relation types") @@ -496,6 +519,39 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { }) ); + const limitTrailFieldsDiv = trailDetails.createDiv({ cls: 'limit-ML-fields' }) + limitTrailFieldsDiv.createEl('strong', { 'text': 'Limit M/L View to only show certain fields' }) + + const checkboxDiv = limitTrailFieldsDiv.createDiv({ cls: 'checkboxes' }) + + function drawLimitTrailCheckboxes(div: HTMLDivElement) { + checkboxDiv.empty() + const checkboxStates = settings.limitTrailCheckboxStates + + settings.userHierarchies.forEach(userHier => { + userHier.up.forEach(async field => { + // First sort out limitTrailCheckboxStates + if (checkboxStates[field] === undefined) { + checkboxStates[field] = true; + await plugin.saveSettings() + } + const cbDiv = div.createDiv() + const checkedQ = checkboxStates[field] + const cb = cbDiv.createEl('input', { type: 'checkbox', attr: { id: field } }) + cb.checked = checkedQ + const label = cbDiv.createEl('label', { text: field, attr: { for: field } }) + + cb.addEventListener('change', async (event) => { + checkboxStates[field] = cb.checked; + await plugin.saveSettings() + console.log(settings.limitTrailCheckboxStates) + }) + }) + }) + } + + drawLimitTrailCheckboxes(checkboxDiv) + new Setting(trailDetails) .setName("Field name to hide trail") @@ -786,6 +842,10 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { }) ); + debugDetails.createEl('button', { text: 'Console log `settings`' }, (el) => { + el.addEventListener('click', () => console.log(settings)) + }) + new KoFi({ target: this.containerEl }); } } diff --git a/src/interfaces.ts b/src/interfaces.ts index e0bb83a7..4f9adb0e 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -18,6 +18,7 @@ export interface BreadcrumbsSettings { filterImpliedSiblingsOfDifferentTypes: boolean; rlLeaf: boolean; showTrail: boolean; + limitTrailCheckboxStates: { [field: string]: boolean }; hideTrailFieldName: string; trailOrTable: 1 | 2 | 3; gridDots: boolean; @@ -180,6 +181,7 @@ export interface BCIndex { hierGs: HierarchyGraphs[]; mergedGs: MergedGraphs; closedGs: ClosedGraphs; + limitTrailG: Graph; } export type HierarchyGraphs = { diff --git a/src/main.ts b/src/main.ts index dc355597..cf75a05f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,7 +6,7 @@ import { Notice, Plugin, TFile, - WorkspaceLeaf, + WorkspaceLeaf } from "obsidian"; import { BreadcrumbsSettingTab } from "src/BreadcrumbsSettingTab"; import { @@ -14,14 +14,14 @@ import { TRAIL_ICON, TRAIL_ICON_SVG, VIEW_TYPE_BREADCRUMBS_MATRIX, - VIEW_TYPE_BREADCRUMBS_STATS, + VIEW_TYPE_BREADCRUMBS_STATS } from "src/constants"; import type { BCIndex, BreadcrumbsSettings, Directions, dvFrontmatterCache, - HierarchyGraphs, + HierarchyGraphs } from "src/interfaces"; import MatrixView from "src/MatrixView"; import { @@ -29,14 +29,14 @@ import { debug, debugGroupEnd, debugGroupStart, + getAllFieldGs, getAllGsInDir, getDVMetadataCache, getNeighbourObjArr, getObsMetadataCache, mergeGs, - removeDuplicates, - writeBCToFile, - + oppFields, removeDuplicates, + writeBCToFile } from "src/sharedFunctions"; import StatsView from "src/StatsView"; import { VisModal } from "src/VisModal"; @@ -61,6 +61,7 @@ const DEFAULT_SETTINGS: BreadcrumbsSettings = { filterImpliedSiblingsOfDifferentTypes: false, rlLeaf: true, showTrail: true, + limitTrailCheckboxStates: {}, hideTrailFieldName: 'hide-trail', trailOrTable: 3, gridDots: false, @@ -478,7 +479,7 @@ export default class BreadcrumbsPlugin extends Plugin { }[] = []; if (settings.hierarchyNotes[0] !== "") { const currPath = this.app.workspace.getActiveFile().path; - const contentArr = []; + const contentArr: string[] = []; settings.hierarchyNotes.forEach(async (note) => { const file = this.app.metadataCache.getFirstLinkpathDest( @@ -508,6 +509,7 @@ export default class BreadcrumbsPlugin extends Plugin { hierGs: [], mergedGs: { up: undefined, same: undefined, down: undefined }, closedGs: { up: undefined, same: undefined, down: undefined }, + limitTrailG: undefined }; userHierarchies.forEach((hier, i) => { @@ -590,6 +592,29 @@ export default class BreadcrumbsPlugin extends Plugin { } }); + // LimitTrailG + if (Object.values(settings.limitTrailCheckboxStates).every(val => val)) { + graphs.limitTrailG = graphs.closedGs.up + } else { + const allUps = getAllGsInDir(userHierarchies, graphs.hierGs, 'up'); + const allLimitedTrailsGsKeys: string[] = Object.keys(allUps).filter(field => settings.limitTrailCheckboxStates[field]) + const allLimitedTrailsGs: Graph[] = []; + allLimitedTrailsGsKeys.forEach(key => allLimitedTrailsGs.push(allUps[key])) + + const mergedLimitedUpGs = mergeGs(...allLimitedTrailsGs); + + const allLimitedDownGs: Graph[] = []; + + Object.keys(settings.limitTrailCheckboxStates).forEach(limitedField => { + const oppFieldsArr = oppFields(limitedField, 'up', userHierarchies); + const oppGs = getAllFieldGs(oppFieldsArr, graphs.hierGs) + allLimitedDownGs.push(...oppGs) + }) + + const mergedLimitedDownGs = mergeGs(...allLimitedDownGs) + graphs.limitTrailG = closeImpliedLinks(mergedLimitedUpGs, mergedLimitedDownGs) + } + debug(settings, "graphs inited"); debug(settings, { graphs }); @@ -725,7 +750,9 @@ export default class BreadcrumbsPlugin extends Plugin { return; } - const closedUp = this.currGraphs.closedGs.up; + + + const closedUp = this.currGraphs.limitTrailG; const sortedTrails = this.getBreadcrumbs(closedUp, currFile); debug(settings, { sortedTrails }); diff --git a/src/sharedFunctions.ts b/src/sharedFunctions.ts index 3cc35a1b..f8198258 100644 --- a/src/sharedFunctions.ts +++ b/src/sharedFunctions.ts @@ -681,4 +681,12 @@ export const writeBCToFile = (app: App, plugin: BreadcrumbsPlugin, currGraphs: B }) }) }) +} + +export function oppFields(field: string, dir: Directions, userHierarchies: userHierarchy[]): string[] { + let oppDir: Directions = 'same'; + if (dir !== "same") { + oppDir = dir === "up" ? "down" : "up" + } + return userHierarchies.find(hier => hier[oppDir].includes(field))?.[oppDir] ?? [] } \ No newline at end of file