adding fuzzy tree search

This commit is contained in:
Rizky 2023-10-23 18:58:26 +07:00
parent e60325a729
commit 978e58086c
9 changed files with 132 additions and 5 deletions

View File

@ -8,6 +8,7 @@
"dependencies": {
"@babel/parser": "^7.23.0",
"@floating-ui/react": "^0.26.0",
"@leeoniya/ufuzzy": "^1.0.11",
"@minoru/react-dnd-treeview": "^3.4.4",
"@monaco-editor/react": "^4.6.0",
"@paralleldrive/cuid2": "2.2.2",

View File

@ -5,6 +5,7 @@ import { IItem, MItem } from "../../../utils/types/item";
import { DComp, DPage, IRoot } from "../../../utils/types/root";
import { ISection } from "../../../utils/types/section";
import { IText, MText } from "../../../utils/types/text";
import { ReactElement } from "react";
const EmptySite = {
id: "",
@ -55,6 +56,7 @@ export type EdMeta = {
mitem: MItem;
mcomp: MItem;
};
el?: ReactElement;
};
export const EDGlobal = {
@ -83,6 +85,7 @@ export const EDGlobal = {
},
ui: {
tree: {
search: "",
open: {} as Record<string, string[]>,
},
popup: {

View File

@ -1,6 +1,7 @@
import {
Tree as DNDTree,
DragPreviewRender,
NodeModel,
PlaceholderRender,
TreeMethods,
} from "@minoru/react-dnd-treeview";
@ -11,6 +12,7 @@ import { canDrop, nodeOnDrop } from "./node/on-drop";
import { nodeRender } from "./node/render";
import { FC } from "react";
import { DEPTH_WIDTH } from "./node/item/indent";
import { doTreeSearch } from "./search";
export const EdTreeBody = () => {
const p = useGlobal(EDGlobal, "EDITOR");
@ -19,10 +21,17 @@ export const EdTreeBody = () => {
indentHook(p, local);
let tree: NodeModel<EdMeta>[] = [];
if (p.ui.tree.search) {
tree = doTreeSearch(p);
} else {
tree = p.page.tree;
}
if (p.page.tree.length === 0) return <div>No Item </div>;
return (
<TypedTree
tree={p.page.tree}
tree={tree}
rootId={"root"}
insertDroppableFirst={false}
classes={treeClasses}

View File

@ -30,5 +30,5 @@ export const indentHook = (
});
}
}
}, [p.page.tree]);
}, [p.page.tree, active.item_id]);
};

View File

@ -13,7 +13,7 @@ export const EdTreeName = ({
return (
<div className="text-[14px] flex items-center cursor-pointer flex-1">
<div className="flex flex-col">
{item.name}
{node.data?.el || item.name}
{/* <div className={"text-[11px] text-gray-500 -mt-1"}>{item.id}</div> */}
</div>
</div>

View File

@ -31,6 +31,7 @@ export const nodeRender: NodeRender<EdMeta> = (node, prm) => {
}}
onClick={() => {
active.item_id = item.id;
p.ui.tree.search = "";
p.render();
}}
>

View File

@ -0,0 +1,113 @@
import { useGlobal, useLocal } from "web-utils";
import { EDGlobal, EdMeta, PG } from "../../logic/ed-global";
import { NodeModel } from "@minoru/react-dnd-treeview";
import uFuzzy from "@leeoniya/ufuzzy";
import { useEffect, useState } from "react";
const uf = new uFuzzy({});
export const EdTreeSearch = () => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({
sref: null as HTMLInputElement | null,
focus: false,
hover: false,
cursor: null as number | null,
});
useEffect(() => {
const input = local.sref;
if (input) input.setSelectionRange(local.cursor, local.cursor);
}, [local.sref, local.cursor, p.ui.tree.search]);
return (
<div className="flex items-stretch h-[24px] border-b">
<input
name="search"
ref={(ref) => {
local.sref = ref;
}}
type="search"
autoComplete="off"
className={cx("flex-1 outline-none px-2 text-[13px] ")}
placeholder="Search..."
value={p.ui.tree.search}
spellCheck={false}
onInput={(e) => {
local.cursor = e.currentTarget.selectionStart;
p.ui.tree.search = e.currentTarget.value;
p.render();
}}
onFocus={() => {
local.focus = true;
local.render();
}}
onBlur={() => {
if (!local.hover && !p.ui.tree.search) {
local.focus = false;
local.render();
}
}}
/>
</div>
);
};
export const doTreeSearch = (p: PG) => {
let tree: NodeModel<EdMeta>[] = [];
const [idxs, info] = uf.search(
p.page.tree.map((e) => e.text),
p.ui.tree.search
);
if (idxs && info) {
let i = 0;
for (const idx of idxs) {
const item = p.page.tree[idx];
const range = info.ranges[i++];
let text = "";
let cur = range.shift();
let open = true;
for (let i = 0; i < item.text.length; i++) {
if (typeof cur === "number") {
if (i === cur) {
if (open) {
text += `<b>`;
open = false;
} else {
text += `</b>`;
open = true;
}
cur = range.shift();
}
text += item.text[i];
} else {
text += item.text[i];
}
}
const el = (
<div
className={css`
b {
font-weight: bold;
color: #df9100;
text-decoration: underline;
}
`}
dangerouslySetInnerHTML={{ __html: text }}
/>
);
tree.push({
...item,
parent: "root",
data: item.data
? {
...item.data,
el,
}
: undefined,
});
}
}
return tree;
};

View File

@ -3,12 +3,12 @@ import { DndProvider } from "react-dnd";
import { useGlobal } from "web-utils";
import { EDGlobal } from "../../logic/ed-global";
import { EdTreeBody } from "./body";
import { EdTreeSearch } from "./search";
export const EdTree = () => {
const p = useGlobal(EDGlobal, "EDITOR");
return (
<div className="flex flex-col min-w-[300px] relative border-r">
<div className=""></div>
<EdTreeSearch />
<div className="tree-body flex relative flex-1 overflow-y-auto overflow-x-hidden">
<div className="absolute inset-0">
<DndProvider backend={MultiBackend} options={getBackendOptions()}>

BIN
bun.lockb

Binary file not shown.