adding fuzzy tree search
This commit is contained in:
parent
e60325a729
commit
978e58086c
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -30,5 +30,5 @@ export const indentHook = (
|
|||
});
|
||||
}
|
||||
}
|
||||
}, [p.page.tree]);
|
||||
}, [p.page.tree, active.item_id]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export const nodeRender: NodeRender<EdMeta> = (node, prm) => {
|
|||
}}
|
||||
onClick={() => {
|
||||
active.item_id = item.id;
|
||||
p.ui.tree.search = "";
|
||||
p.render();
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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()}>
|
||||
|
|
|
|||
Loading…
Reference in New Issue