Refactored events to use a small global event bus
This commit is contained in:
@@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
"version": "node version-bump.mjs && git add manifest.json versions.json",
|
||||||
|
"lint": "eslint . --ext .ts --fix --ignore-path .gitignore"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -17,43 +17,12 @@ onMount(async () => {
|
|||||||
const debouncedOnInput = debounce(() => {
|
const debouncedOnInput = debounce(() => {
|
||||||
dispatch("input", value)
|
dispatch("input", value)
|
||||||
}, 100)
|
}, 100)
|
||||||
|
|
||||||
function moveNoteSelection(ev: KeyboardEvent): void {
|
|
||||||
switch (ev.key) {
|
|
||||||
case "ArrowDown":
|
|
||||||
ev.preventDefault()
|
|
||||||
dispatch("arrow-down")
|
|
||||||
break
|
|
||||||
case "ArrowUp":
|
|
||||||
ev.preventDefault()
|
|
||||||
dispatch("arrow-up")
|
|
||||||
break
|
|
||||||
|
|
||||||
case "Enter":
|
|
||||||
ev.preventDefault()
|
|
||||||
if (ev.ctrlKey || ev.metaKey) {
|
|
||||||
// Open in a new pane
|
|
||||||
dispatch("ctrl-enter")
|
|
||||||
} else if (ev.shiftKey) {
|
|
||||||
// Create a new note
|
|
||||||
dispatch("shift-enter")
|
|
||||||
} else if (ev.altKey) {
|
|
||||||
// Expand in-note results
|
|
||||||
dispatch("alt-enter")
|
|
||||||
} else {
|
|
||||||
// Open in current pane
|
|
||||||
dispatch("enter")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
bind:value
|
bind:value
|
||||||
bind:this={elInput}
|
bind:this={elInput}
|
||||||
on:input={debouncedOnInput}
|
on:input={debouncedOnInput}
|
||||||
on:keydown={moveNoteSelection}
|
|
||||||
type="text"
|
type="text"
|
||||||
class="prompt-input"
|
class="prompt-input"
|
||||||
placeholder="Type to search through your notes"
|
placeholder="Type to search through your notes"
|
||||||
|
|||||||
@@ -5,9 +5,14 @@ let lastSearch = ""
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import CmpInput from "./CmpInput.svelte"
|
import CmpInput from "./CmpInput.svelte"
|
||||||
import CmpResultInFile from "./CmpResultInFile.svelte"
|
import CmpResultInFile from "./CmpResultInFile.svelte"
|
||||||
import { excerptAfter, type ResultNote, type SearchMatch } from "./globals"
|
import {
|
||||||
|
eventBus,
|
||||||
|
excerptAfter,
|
||||||
|
type ResultNote,
|
||||||
|
type SearchMatch,
|
||||||
|
} from "./globals"
|
||||||
import { loopIndex } from "./utils"
|
import { loopIndex } from "./utils"
|
||||||
import { onMount, tick } from "svelte"
|
import { onDestroy, onMount, tick } from "svelte"
|
||||||
import { MarkdownView } from "obsidian"
|
import { MarkdownView } from "obsidian"
|
||||||
import { getSuggestions } from "./search"
|
import { getSuggestions } from "./search"
|
||||||
import type { ModalInFile, ModalVault } from "./modal"
|
import type { ModalInFile, ModalVault } from "./modal"
|
||||||
@@ -23,6 +28,14 @@ let note: ResultNote | null = null
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
searchQuery = lastSearch
|
searchQuery = lastSearch
|
||||||
|
eventBus.disable("vault")
|
||||||
|
eventBus.on("infile", "enter", openSelection)
|
||||||
|
eventBus.on("infile", "arrow-up", () => moveIndex(-1))
|
||||||
|
eventBus.on("infile", "arrow-down", () => moveIndex(1))
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
eventBus.enable("vaut")
|
||||||
})
|
})
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
@@ -109,13 +122,7 @@ async function openSelection(): Promise<void> {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="modal-title">Omnisearch - File</div>
|
<div class="modal-title">Omnisearch - File</div>
|
||||||
<CmpInput
|
<CmpInput value={searchQuery} on:input={(e) => (searchQuery = e.detail)} />
|
||||||
value={searchQuery}
|
|
||||||
on:input={(e) => (searchQuery = e.detail)}
|
|
||||||
on:enter={openSelection}
|
|
||||||
on:arrow-up={() => moveIndex(-1)}
|
|
||||||
on:arrow-down={() => moveIndex(1)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="prompt-results">
|
<div class="prompt-results">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { TFile } from "obsidian"
|
|||||||
import { onMount, tick } from "svelte"
|
import { onMount, tick } from "svelte"
|
||||||
import CmpInput from "./CmpInput.svelte"
|
import CmpInput from "./CmpInput.svelte"
|
||||||
import CmpResultNote from "./CmpResultNote.svelte"
|
import CmpResultNote from "./CmpResultNote.svelte"
|
||||||
import type { ResultNote } from "./globals"
|
import { eventBus, type ResultNote } from "./globals"
|
||||||
import { ModalInFile, type ModalVault } from "./modal"
|
import { ModalInFile, type ModalVault } from "./modal"
|
||||||
import { createNote, openNote } from "./notes"
|
import { createNote, openNote } from "./notes"
|
||||||
import { getSuggestions } from "./search"
|
import { getSuggestions } from "./search"
|
||||||
@@ -30,6 +30,12 @@ $: {
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
searchQuery = lastSearch
|
searchQuery = lastSearch
|
||||||
|
eventBus.on("vault", "enter", onInputEnter)
|
||||||
|
eventBus.on("vault", "shift-enter", onInputShiftEnter)
|
||||||
|
eventBus.on("vault", "ctrl-enter", onInputCtrlEnter)
|
||||||
|
eventBus.on("vault", "alt-enter", onInputAltEnter)
|
||||||
|
eventBus.on("vault", "arrow-up", () => moveIndex(-1))
|
||||||
|
eventBus.on("vault", "arrow-down", () => moveIndex(1))
|
||||||
})
|
})
|
||||||
|
|
||||||
function onClick() {
|
function onClick() {
|
||||||
@@ -84,16 +90,7 @@ function scrollIntoView(): void {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="modal-title">Omnisearch - Vault</div>
|
<div class="modal-title">Omnisearch - Vault</div>
|
||||||
<CmpInput
|
<CmpInput value={lastSearch} on:input={(e) => (searchQuery = e.detail)} />
|
||||||
value={lastSearch}
|
|
||||||
on:input={(e) => (searchQuery = e.detail)}
|
|
||||||
on:enter={onInputEnter}
|
|
||||||
on:shift-enter={onInputShiftEnter}
|
|
||||||
on:ctrl-enter={onInputCtrlEnter}
|
|
||||||
on:alt-enter={onInputAltEnter}
|
|
||||||
on:arrow-up={() => moveIndex(-1)}
|
|
||||||
on:arrow-down={() => moveIndex(1)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="prompt-results">
|
<div class="prompt-results">
|
||||||
|
|||||||
36
src/event-bus.ts
Normal file
36
src/event-bus.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
export type EventBusCallback = (...args: any[]) => any
|
||||||
|
|
||||||
|
export class EventBus {
|
||||||
|
private handlers: Map<string, EventBusCallback> = new Map()
|
||||||
|
private disabled: string[] = []
|
||||||
|
|
||||||
|
public on(ctx: string, event: string, callback: EventBusCallback): void {
|
||||||
|
if (ctx.includes('@') || event.includes('@')) {
|
||||||
|
throw new Error('Invalid ctx/event name - Cannot contain @')
|
||||||
|
}
|
||||||
|
this.handlers.set(`${ctx}@${event}`, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
public off(ctx: string, event: string): void {
|
||||||
|
this.handlers.delete(`${ctx}@${event}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
public disable(ctx: string): void {
|
||||||
|
this.enable(ctx)
|
||||||
|
this.disabled.push(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
public enable(ctx: string): void {
|
||||||
|
this.disabled = this.disabled.filter(v => v !== ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
public emit(event: string, ...args: any[]): void {
|
||||||
|
for (const [key, handler] of this.handlers.entries()) {
|
||||||
|
const ctx = key.split('@')[0]
|
||||||
|
if (this.disabled.includes(ctx)) continue
|
||||||
|
if (key.endsWith(`@${event}`)) {
|
||||||
|
handler(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { EventBus } from './event-bus'
|
||||||
|
|
||||||
// Matches a wikiling that begins a string
|
// Matches a wikiling that begins a string
|
||||||
export const regexWikilink = /^!?\[\[(?<name>.+?)(\|(?<alias>.+?))?\]\]/
|
export const regexWikilink = /^!?\[\[(?<name>.+?)(\|(?<alias>.+?))?\]\]/
|
||||||
export const regexLineSplit = /\r?\n|\r|((\.|\?|!)( |\r?\n|\r))/g
|
export const regexLineSplit = /\r?\n|\r|((\.|\?|!)( |\r?\n|\r))/g
|
||||||
@@ -8,6 +10,10 @@ export const excerptAfter = 180
|
|||||||
|
|
||||||
export const highlightClass = 'suggestion-highlight omnisearch-highlight'
|
export const highlightClass = 'suggestion-highlight omnisearch-highlight'
|
||||||
|
|
||||||
|
export const eventBus = new EventBus()
|
||||||
|
|
||||||
|
// export const eventBus = new EventBus()
|
||||||
|
|
||||||
export type SearchNote = {
|
export type SearchNote = {
|
||||||
path: string
|
path: string
|
||||||
basename: string
|
basename: string
|
||||||
|
|||||||
35
src/modal.ts
35
src/modal.ts
@@ -1,6 +1,7 @@
|
|||||||
import { App, Modal, TFile } from 'obsidian'
|
import { App, Modal, TFile } from 'obsidian'
|
||||||
import CmpModalVault from './CmpModalVault.svelte'
|
import CmpModalVault from './CmpModalVault.svelte'
|
||||||
import CmpModalInFile from './CmpModalInFile.svelte'
|
import CmpModalInFile from './CmpModalInFile.svelte'
|
||||||
|
import { eventBus } from './globals'
|
||||||
|
|
||||||
abstract class ModalOmnisearch extends Modal {
|
abstract class ModalOmnisearch extends Modal {
|
||||||
constructor(app: App) {
|
constructor(app: App) {
|
||||||
@@ -12,13 +13,45 @@ abstract class ModalOmnisearch extends Modal {
|
|||||||
this.modalEl.replaceChildren()
|
this.modalEl.replaceChildren()
|
||||||
this.modalEl.append(closeEl)
|
this.modalEl.append(closeEl)
|
||||||
this.modalEl.addClass('omnisearch-modal', 'prompt')
|
this.modalEl.addClass('omnisearch-modal', 'prompt')
|
||||||
|
|
||||||
|
this.modalEl.tabIndex = -1
|
||||||
|
this.modalEl.onkeydown = ev => {
|
||||||
|
switch (ev.key) {
|
||||||
|
case 'ArrowDown':
|
||||||
|
ev.preventDefault()
|
||||||
|
eventBus.emit('arrow-down')
|
||||||
|
break
|
||||||
|
case 'ArrowUp':
|
||||||
|
ev.preventDefault()
|
||||||
|
eventBus.emit('arrow-up')
|
||||||
|
break
|
||||||
|
case 'Enter':
|
||||||
|
ev.preventDefault()
|
||||||
|
if (ev.ctrlKey || ev.metaKey) {
|
||||||
|
// Open in a new pane
|
||||||
|
eventBus.emit('ctrl-enter')
|
||||||
|
}
|
||||||
|
else if (ev.shiftKey) {
|
||||||
|
// Create a new note
|
||||||
|
eventBus.emit('shift-enter')
|
||||||
|
}
|
||||||
|
else if (ev.altKey) {
|
||||||
|
// Expand in-note results
|
||||||
|
eventBus.emit('alt-enter')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Open in current pane
|
||||||
|
eventBus.emit('enter')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ModalVault extends ModalOmnisearch {
|
export class ModalVault extends ModalOmnisearch {
|
||||||
constructor(app: App) {
|
constructor(app: App) {
|
||||||
super(app)
|
super(app)
|
||||||
|
|
||||||
new CmpModalVault({
|
new CmpModalVault({
|
||||||
target: this.modalEl,
|
target: this.modalEl,
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
type ResultNote,
|
type ResultNote,
|
||||||
type SearchMatch,
|
type SearchMatch,
|
||||||
} from './globals'
|
} from './globals'
|
||||||
import { get } from 'svelte/store'
|
|
||||||
import { extractHeadingsFromCache, stringsToRegex, wait } from './utils'
|
import { extractHeadingsFromCache, stringsToRegex, wait } from './utils'
|
||||||
|
|
||||||
let minisearchInstance: MiniSearch<IndexedNote>
|
let minisearchInstance: MiniSearch<IndexedNote>
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
import { writable } from 'svelte/store'
|
|
||||||
import type OmnisearchPlugin from './main'
|
|
||||||
@@ -32,7 +32,7 @@ export function removeFrontMatter(text: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function wait(ms: number): Promise<void> {
|
export function wait(ms: number): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(resolve => {
|
||||||
setTimeout(resolve, ms)
|
setTimeout(resolve, ms)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": false,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"target": "ES6",
|
"target": "ES6",
|
||||||
|
|||||||
Reference in New Issue
Block a user