generated from obsidianmd/obsidian-sample-plugin
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Hierarchy Note): ✨ Hierarchy Note Adjuster! (#143)
- Loading branch information
1 parent
ce8e8dd
commit b5afae2
Showing
6 changed files
with
373 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<script lang="ts"> | ||
import { error } from "console"; | ||
import { Notice, TFile } from "obsidian"; | ||
import { onMount } from "svelte"; | ||
import { ARROW_DIRECTIONS } from "../constants"; | ||
import type { BCSettings } from "../interfaces"; | ||
import type { ModifyHierItemModal } from "../ModifyHierItemModal"; | ||
import { dropWikilinks, makeWiki } from "../sharedFunctions"; | ||
export let modal: ModifyHierItemModal; | ||
export let settings: BCSettings; | ||
export let hnItem: HNItem; | ||
export let file: TFile; | ||
export let rel: "up" | "same" | "down"; | ||
interface HNItem { | ||
depth: number; | ||
line: string; | ||
lineNo: number; | ||
} | ||
let inputEl: HTMLInputElement; | ||
let newItem = ""; | ||
const buildNewItem = ( | ||
newItem: string, | ||
depth = hnItem.depth, | ||
preview = false | ||
) => | ||
`${" ".repeat(Math.round(depth / (preview ? 2 : 1)))}- ${ | ||
preview ? newItem || "<Empty>" : makeWiki(newItem) | ||
}`; | ||
onMount(() => inputEl.focus()); | ||
</script> | ||
|
||
<h5>Add an {ARROW_DIRECTIONS[rel]} to {dropWikilinks(hnItem.line)}</h5> | ||
<div> | ||
{#if rel === "up"} | ||
{#if hnItem.depth === 0} | ||
<div>Can't add parent to top level item, choose another direction</div> | ||
{:else} | ||
<div> | ||
<pre> | ||
{buildNewItem(newItem, hnItem.depth - 4, true)} | ||
</pre> | ||
</div> | ||
{/if} | ||
{/if} | ||
<div> | ||
<pre> | ||
<strong>{buildNewItem(dropWikilinks(hnItem.line), hnItem.depth, true)}</strong> | ||
</pre> | ||
</div> | ||
{#if rel === "same"} | ||
<div> | ||
<pre> | ||
{buildNewItem(newItem, hnItem.depth, true)} | ||
</pre> | ||
</div> | ||
{:else if rel === "down"} | ||
<div> | ||
<pre> | ||
{buildNewItem(newItem, hnItem.depth + 4, true)} | ||
</pre> | ||
</div> | ||
{/if} | ||
|
||
<!-- svelte-ignore a11y-no-onchange --> | ||
<select class="dropdown" width="1" bind:value={rel}> | ||
<option value="up">up</option> | ||
<option value="same">same</option> | ||
<option value="down">down</option> | ||
</select> | ||
|
||
<input | ||
type="text" | ||
placeholder="New item" | ||
bind:this={inputEl} | ||
bind:value={newItem} | ||
/> | ||
|
||
<button | ||
on:click={async (e) => { | ||
if (rel === "up" && hnItem.depth === 0) { | ||
new Notice( | ||
"Can't add parent to top level item, choose another direction" | ||
); | ||
return; | ||
} else { | ||
try { | ||
const content = await modal.app.vault.read(file); | ||
const lines = content.split("\n"); | ||
const lineNo = rel === "up" ? hnItem.lineNo : hnItem.lineNo + 1; | ||
|
||
const depth = | ||
rel === "up" | ||
? hnItem.depth - 4 | ||
: rel === "down" | ||
? hnItem.depth + 4 | ||
: hnItem.depth; | ||
|
||
lines.splice(lineNo, 0, buildNewItem(newItem, depth)); | ||
await modal.app.vault.modify(file, lines.join("\n")); | ||
modal.close(); | ||
} catch (err) { | ||
error(err); | ||
new Notice("An error occured, please check the console"); | ||
} | ||
} | ||
}}>Add</button | ||
> | ||
</div> | ||
|
||
<style> | ||
pre { | ||
display: inline; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { App, FuzzyMatch, FuzzySuggestModal, Notice } from "obsidian"; | ||
import { HierarchyNoteManipulator } from "./HierarchyNoteManipulator"; | ||
import type { BCSettings } from "./interfaces"; | ||
import type BCPlugin from "./main"; | ||
|
||
export class HierarchyNoteSelectorModal extends FuzzySuggestModal<string> { | ||
app: App; | ||
plugin: BCPlugin; | ||
settings: BCSettings; | ||
|
||
constructor(app: App, plugin: BCPlugin) { | ||
super(app); | ||
this.app = app; | ||
this.plugin = plugin; | ||
this.settings = this.plugin.settings; | ||
} | ||
|
||
onOpen(): void { | ||
this.setPlaceholder("HN Chooser"); | ||
const { hierarchyNotes } = this.settings; | ||
if (hierarchyNotes.length === 0) { | ||
this.close(); | ||
new Notice("No hierarchy notes found"); | ||
} else if (hierarchyNotes.length === 1) { | ||
this.close(); | ||
new HierarchyNoteManipulator( | ||
this.app, | ||
this.plugin, | ||
hierarchyNotes[0] | ||
).open(); | ||
} else { | ||
super.onOpen(); | ||
} | ||
} | ||
|
||
getItems(): string[] { | ||
return this.settings.hierarchyNotes; | ||
} | ||
|
||
getItemText(item: string): string { | ||
return `${item}`; | ||
} | ||
|
||
renderSuggestion(item: FuzzyMatch<string>, el: HTMLElement) { | ||
super.renderSuggestion(item, el); | ||
} | ||
|
||
onChooseItem(item: string, evt: MouseEvent | KeyboardEvent): void { | ||
new HierarchyNoteManipulator(this.app, this.plugin, item).open(); | ||
this.close(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { error } from "loglevel"; | ||
import { | ||
App, | ||
FuzzyMatch, | ||
FuzzySuggestModal, | ||
ListItemCache, | ||
MarkdownView, | ||
Notice, | ||
TFile, | ||
} from "obsidian"; | ||
import { ModifyHierItemModal } from "./ModifyHierItemModal"; | ||
import type { BCSettings } from "./interfaces"; | ||
import type BCPlugin from "./main"; | ||
import { dropWikilinks } from "./sharedFunctions"; | ||
|
||
interface HNItem { | ||
depth: number; | ||
line: string; | ||
lineNo: number; | ||
} | ||
|
||
export class HierarchyNoteManipulator extends FuzzySuggestModal<HNItem> { | ||
app: App; | ||
plugin: BCPlugin; | ||
settings: BCSettings; | ||
hierNoteName: string; | ||
lines: string[]; | ||
listItems: ListItemCache[]; | ||
file: TFile; | ||
|
||
constructor(app: App, plugin: BCPlugin, hierNoteName: string) { | ||
super(app); | ||
this.app = app; | ||
this.plugin = plugin; | ||
this.settings = this.plugin.settings; | ||
this.hierNoteName = hierNoteName; | ||
|
||
const chooseOverride = (evt: KeyboardEvent) => { | ||
// @ts-ignore | ||
this.chooser.useSelectedItem(evt); | ||
return false; | ||
}; | ||
this.scope.register([], "Delete", chooseOverride); | ||
this.scope.register(["Shift"], "ArrowUp", chooseOverride); | ||
this.scope.register(["Shift"], "ArrowRight", chooseOverride); | ||
this.scope.register(["Shift"], "ArrowDown", chooseOverride); | ||
} | ||
|
||
async onOpen(): Promise<void> { | ||
this.setPlaceholder("HN Manipulator"); | ||
this.setInstructions([ | ||
{ command: "Enter/Click", purpose: "Jump to item" }, | ||
{ command: "Shift + ↑", purpose: "Add parent" }, | ||
{ command: "Shift + →", purpose: "Add sibling" }, | ||
{ command: "Shift + ↓", purpose: "Add child" }, | ||
{ command: "Delete", purpose: "Delete item" }, | ||
]); | ||
|
||
this.file = this.app.metadataCache.getFirstLinkpathDest( | ||
this.hierNoteName, | ||
"" | ||
); | ||
if (!this.file) this.lines = []; | ||
const content = await this.app.vault.cachedRead(this.file); | ||
this.lines = content.split("\n"); | ||
|
||
this.listItems = this.app.metadataCache.getFileCache(this.file).listItems; | ||
|
||
super.onOpen(); | ||
} | ||
|
||
getItems(): HNItem[] { | ||
const items = this.listItems | ||
.map((item) => { | ||
const i = item.position.start.line; | ||
return { i, line: this.lines[i] }; | ||
}) | ||
.map((item) => { | ||
const splits = item.line.split("- "); | ||
const depth = splits[0].length; | ||
const line = splits.slice(1).join("- "); | ||
|
||
return { depth, line, lineNo: item.i }; | ||
}); | ||
|
||
return items; | ||
} | ||
|
||
getItemText(item: HNItem): string { | ||
return `${" ".repeat(item.depth)}- ${dropWikilinks(item.line)}`; | ||
} | ||
|
||
renderSuggestion(item: FuzzyMatch<HNItem>, el: HTMLElement) { | ||
super.renderSuggestion(item, el); | ||
el.innerText = `${" ".repeat(item.item.depth)}- ${dropWikilinks( | ||
item.item.line | ||
)}`; | ||
} | ||
|
||
async deleteItem(item: HNItem): Promise<void> { | ||
try { | ||
this.lines.splice(item.lineNo, 1); | ||
this.listItems.splice(item.lineNo, 1); | ||
await this.app.vault.modify(this.file, this.lines.join("\n")); | ||
new Notice("Item deleted Succesfully"); | ||
} catch (err) { | ||
error(err); | ||
new Notice("An error occured. Please check the console"); | ||
} | ||
} | ||
|
||
onChooseItem(item: HNItem, evt: MouseEvent | KeyboardEvent): void { | ||
if (evt instanceof KeyboardEvent && evt.key === "Delete") { | ||
this.deleteItem(item); | ||
} else if (evt instanceof KeyboardEvent && evt.shiftKey) { | ||
const rel = | ||
evt.key === "ArrowUp" | ||
? "up" | ||
: evt.key === "ArrowDown" | ||
? "down" | ||
: "same"; | ||
|
||
new ModifyHierItemModal( | ||
this.app, | ||
this.plugin, | ||
item, | ||
this.file, | ||
rel | ||
).open(); | ||
this.close(); | ||
} else { | ||
const view = this.app.workspace.getActiveViewOfType(MarkdownView); | ||
const { editor } = view ?? {}; | ||
if (!editor) return; | ||
//@ts-ignore | ||
view.leaf.openFile(this.file, { active: true, mode: "source" }); | ||
editor.setCursor({ line: item.lineNo, ch: item.depth + 2 }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { App, Modal, TFile } from "obsidian"; | ||
import ModifyHNItemComp from "./Components/ModifyHNItemComp.svelte"; | ||
import type BCPlugin from "./main"; | ||
|
||
interface HNItem { | ||
depth: number; | ||
line: string; | ||
lineNo: number; | ||
} | ||
|
||
export class ModifyHierItemModal extends Modal { | ||
plugin: BCPlugin; | ||
modal: ModifyHierItemModal; | ||
hnItem: HNItem; | ||
file: TFile; | ||
rel: "up" | "same" | "down"; | ||
|
||
constructor( | ||
app: App, | ||
plugin: BCPlugin, | ||
hnItem: HNItem, | ||
file: TFile, | ||
rel: "up" | "same" | "down" | ||
) { | ||
super(app); | ||
this.plugin = plugin; | ||
this.modal = this; | ||
this.hnItem = hnItem; | ||
this.file = file; | ||
this.rel = rel; | ||
} | ||
|
||
onOpen() { | ||
const { contentEl } = this; | ||
contentEl.empty(); | ||
|
||
new ModifyHNItemComp({ | ||
target: contentEl, | ||
props: { | ||
modal: this, | ||
settings: this.plugin.settings, | ||
hnItem: this.hnItem, | ||
file: this.file, | ||
rel: this.rel, | ||
}, | ||
}); | ||
} | ||
|
||
onClose() { | ||
this.contentEl.empty(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters