From 70129f71d578df363a302a13b9e1e96aa877ac40 Mon Sep 17 00:00:00 2001 From: Quorafind Date: Fri, 17 Mar 2023 07:58:15 +0800 Subject: [PATCH] feat: support preview and editing in modal --- src/floatSearchIndex.ts | 142 ++++++++++++++++++++++++++++------ src/leafView.ts | 2 +- src/types/types-obsidian.d.ts | 1 + styles.css | 24 ++++++ 4 files changed, 146 insertions(+), 23 deletions(-) diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts index aee405b..61c78ba 100644 --- a/src/floatSearchIndex.ts +++ b/src/floatSearchIndex.ts @@ -7,7 +7,7 @@ import { WorkspaceContainer, WorkspaceItem, WorkspaceLeaf } from 'obsidian'; -import { EmbeddedView, isDailyNoteLeaf, spawnLeafView } from "./leafView"; +import { EmbeddedView, isEmebeddedLeaf, spawnLeafView } from "./leafView"; import { around } from "monkey-around"; export default class FloatSearchPlugin extends Plugin { @@ -106,12 +106,12 @@ export default class FloatSearchPlugin extends Plugin { setPinned(old) { return function (pinned: boolean) { old.call(this, pinned); - if(isDailyNoteLeaf(this) && !pinned) this.setPinned(true); + if(isEmebeddedLeaf(this) && !pinned) this.setPinned(true); } }, openFile(old) { return function (file: TFile, openState?: OpenViewState) { - if (isDailyNoteLeaf(this)) { + if (isEmebeddedLeaf(this)) { setTimeout( around(Workspace.prototype, { recordMostRecentOpenedFile(old) { @@ -150,8 +150,13 @@ export default class FloatSearchPlugin extends Plugin { class FloatSearchModal extends Modal { private readonly plugin: FloatSearchPlugin; - private embeddedView: EmbeddedView; - private leaf: WorkspaceLeaf; + private searchEmbeddedView: EmbeddedView; + private fileEmbeddedView: EmbeddedView; + private searchLeaf: WorkspaceLeaf; + private fileLeaf: WorkspaceLeaf | undefined; + + private searchCtnEl: HTMLElement; + private fileEl: HTMLElement; constructor(app: App, plugin: FloatSearchPlugin) { super(app); @@ -159,34 +164,38 @@ class FloatSearchModal extends Modal { } async onOpen() { - const { contentEl } = this; + const { contentEl, containerEl, modalEl } = this; - this.initCss(contentEl); - await this.initView(contentEl); + this.searchCtnEl = contentEl.createDiv({ cls: "float-search-modal-search-ctn" }); + + this.initCss(contentEl, modalEl, containerEl); + await this.initSearchView(this.searchCtnEl); this.initInput(); this.initContent(); } onClose() { const { contentEl } = this; - this.leaf.detach(); - this.embeddedView.unload(); + this.searchLeaf.detach(); + this.fileLeaf?.detach(); + this.searchEmbeddedView.unload(); + this.fileEmbeddedView?.unload(); contentEl.empty(); } - initCss(contentEl: HTMLElement) { - contentEl.classList.add("float-search-modal"); - contentEl.parentElement?.classList.add("float-search-modal-parent"); - contentEl.parentElement?.parentElement?.classList.add("float-search-modal-container"); + initCss(contentEl: HTMLElement, modalEl: HTMLElement, containerEl: HTMLElement) { + contentEl.classList.add("float-search-modal-content"); + modalEl.classList.add("float-search-modal"); + containerEl.classList.add("float-search-modal-container"); } - async initView(contentEl: HTMLElement) { + async initSearchView(contentEl: HTMLElement) { const [createdLeaf, embeddedView] = spawnLeafView(this.plugin, contentEl); - this.leaf = createdLeaf; - this.embeddedView = embeddedView; + this.searchLeaf = createdLeaf; + this.searchEmbeddedView = embeddedView; - this.leaf.setPinned(true); - await this.leaf.setViewState({ + this.searchLeaf.setPinned(true); + await this.searchLeaf.setViewState({ type: "search", }); } @@ -195,7 +204,7 @@ class FloatSearchModal extends Modal { const inputEl = this.contentEl.getElementsByTagName("input")[0]; inputEl.focus(); inputEl.onkeydown = (e) => { - const currentView = this.leaf.view as SearchView; + const currentView = this.searchLeaf.view as SearchView; switch (e.key) { case "ArrowDown": if (e.shiftKey) { @@ -225,6 +234,29 @@ class FloatSearchModal extends Modal { this.close(); } break; + case "Tab": + if(e.shiftKey) { + if(this.fileLeaf) { + this.fileLeaf?.detach(); + this.fileLeaf = undefined; + this.fileEmbeddedView?.unload(); + this.modalEl.toggleClass("float-search-width", false); + this.fileEl.detach(); + + break; + } + } + + if(currentView.dom.focusedItem) { + const item = currentView.dom.focusedItem; + const file = item.parent.file instanceof TFile ? item.parent.file : item.file; + + item.parent.file instanceof TFile ? this.initFileView(file, {match: { + content: item.content, + matches: item.matches + }}) : this.initFileView(file, undefined); + } + break; } } } @@ -237,17 +269,83 @@ class FloatSearchModal extends Modal { return; } + if(this.fileLeaf) { + let targetElement = e.target as HTMLElement | null; + + if((this.searchCtnEl as Node).contains(targetElement as Node)) { + while (targetElement) { + if (targetElement.classList.contains('tree-item')) { + console.log(targetElement); + break; + } + targetElement = targetElement.parentElement; + } + const fileInnerEl = targetElement?.getElementsByClassName("tree-item-inner")[0] as HTMLElement; + const innerText = fileInnerEl.innerText; + const file = app.metadataCache.getFirstLinkpathDest(innerText, ""); + + const currentView = this.searchLeaf.view as SearchView; + if(file) { + const item = currentView.dom.resultDomLookup.get(file); + console.log(item); + currentView.dom.setFocusedItem(item); + this.initFileView(file, undefined); + (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus(); + } + } + + + return; + } + + console.log(e.target); + const target = e.target as HTMLElement; const classList = target.classList; const navElement = contentEl.getElementsByClassName('nav-header')[0]; - if (e.target !== navElement && navElement.contains(e.target as Node)) { + if (e.target !== navElement && (navElement as Node).contains(e.target as Node)) { return; } - if(!(classList.contains("tree-item-icon") || classList.contains("float-search-modal") || classList.contains("right-triangle") || target.parentElement?.classList.contains("right-triangle") || classList.contains("search-input-container") || target?.parentElement?.classList.contains("search-input-container") || classList.contains("search-result-hover-button"))) { + const fileElement = contentEl.getElementsByClassName('float-search-modal-file-ctn')[0]; + if ((fileElement as Node)?.contains(e.target as Node)) { + return; + } + + if(!(classList.contains("tree-item-icon") || classList.contains("float-search-modal-content") || classList.contains("right-triangle") || target.parentElement?.classList.contains("right-triangle") || classList.contains("search-input-container") || target?.parentElement?.classList.contains("search-input-container") || classList.contains("search-result-hover-button"))) { this.close(); } } } + + async initFileView(file: TFile, state: any) { + if(this.fileLeaf) { + await this.fileLeaf.openFile(file, { + active: false, + eState: state + }); + setTimeout(() => { + (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus(); + }, 0); + + return; + } + + const { contentEl } = this; + this.fileEl = contentEl.createDiv({ cls: "float-search-modal-file-ctn" }); + this.modalEl.toggleClass("float-search-width", true); + + const [createdLeaf, embeddedView] = spawnLeafView(this.plugin, this.fileEl); + this.fileLeaf = createdLeaf; + this.fileEmbeddedView = embeddedView; + + this.fileLeaf.setPinned(true); + await this.fileLeaf.openFile(file, { + active: false, + eState: state + }); + + (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus(); + } } diff --git a/src/leafView.ts b/src/leafView.ts index d99ecf5..3c22146 100644 --- a/src/leafView.ts +++ b/src/leafView.ts @@ -31,7 +31,7 @@ export interface EmbeddedViewParent { const popovers = new WeakMap(); type ConstructableWorkspaceSplit = new (ws: Workspace, dir: "horizontal"|"vertical") => WorkspaceSplit; -export function isDailyNoteLeaf(leaf: WorkspaceLeaf) { +export function isEmebeddedLeaf(leaf: WorkspaceLeaf) { // Work around missing enhance.js API by checking match condition instead of looking up parent return leaf.containerEl.matches(".fs-block.fs-leaf-view .workspace-leaf"); } diff --git a/src/types/types-obsidian.d.ts b/src/types/types-obsidian.d.ts index bd942cd..fd5f1e3 100644 --- a/src/types/types-obsidian.d.ts +++ b/src/types/types-obsidian.d.ts @@ -154,6 +154,7 @@ declare module "obsidian" { onKeyShowMoreBefore(e: KeyboardEvent): void; onKeyShowMoreAfter(e: KeyboardEvent): void; dom: any; + searchComponent: SearchComponent; } interface EmptyView extends View { diff --git a/styles.css b/styles.css index 79b58bd..92e6cd6 100644 --- a/styles.css +++ b/styles.css @@ -7,7 +7,27 @@ If your plugin does not need CSS, delete this file. */ .float-search-modal { + width: 600px; +} + +.float-search-modal.float-search-width { + width: 1200px; +} + +.float-search-modal-search-ctn, .float-search-modal-file-ctn { + height: 100%; + width: 100%; +} + +.float-search-modal-file-ctn .view-header { + display: none; +} + +.float-search-modal-content { height: 800px; + display: flex; + align-items: center; + flex-direction: row; } .fs-content .workspace-split.mod-vertical { @@ -18,6 +38,10 @@ If your plugin does not need CSS, delete this file. height: 100%; } +.fs-content .cm-scroller { + margin-top: 20px; +} + .fs-block { height: 100% !important; }