@ehfuse/outliner 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -2
- package/dist/FormatToolbar.d.ts +40 -0
- package/dist/OutlineList.d.ts +27 -0
- package/dist/Outliner.d.ts +20 -0
- package/dist/history.d.ts +11 -0
- package/dist/i18n.d.ts +153 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +7 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/measure.d.ts +2 -0
- package/dist/tree.d.ts +72 -0
- package/package.json +65 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 김영진 (Kim Young Jin)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,62 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ehfuse/outliner
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Outliner for React + MUI. One row per node, fold / unfold, indent / outdent, drag to reorder, per-node formatting
|
|
4
|
+
(font size · text color · background · bold · underline · strikethrough) and undo. 8 UI languages (ko · en · ja · zh · de · fr · it · vi).
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install @ehfuse/outliner @mui/material @mui/icons-material @emotion/react @emotion/styled
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { useState } from "react";
|
|
12
|
+
import { Outliner, createTree, type TreeNode } from "@ehfuse/outliner";
|
|
13
|
+
|
|
14
|
+
export function Notes() {
|
|
15
|
+
const [tree, setTree] = useState<TreeNode>(createTree("Topic"));
|
|
16
|
+
return <Outliner value={tree} onChange={setTree} locale="en" height={480} />;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Keys
|
|
21
|
+
|
|
22
|
+
| Key | Action |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| Enter | New row (first child of an open parent, otherwise next sibling) |
|
|
25
|
+
| Tab / Shift+Tab | Indent / outdent |
|
|
26
|
+
| Alt+↑ / Alt+↓ | Move among siblings |
|
|
27
|
+
| ↑ / ↓ | Previous / next row |
|
|
28
|
+
| Backspace on an empty row | Delete the row |
|
|
29
|
+
| Ctrl/⌘+Z, Ctrl/⌘+Shift+Z or Ctrl+Y | Undo / redo |
|
|
30
|
+
|
|
31
|
+
Drag the handle at the start of a row onto another row (upper half = before, lower half = after).
|
|
32
|
+
|
|
33
|
+
## Data
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
interface TreeNode {
|
|
37
|
+
id: string;
|
|
38
|
+
topic: string;
|
|
39
|
+
children?: TreeNode[];
|
|
40
|
+
expanded?: boolean; // false = folded
|
|
41
|
+
style?: { fontSize?: string; color?: string; background?: string; fontWeight?: string; textDecoration?: string };
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Same shape as Mind Elixir `nodeData`. Unknown keys are kept.
|
|
46
|
+
|
|
47
|
+
## Building blocks
|
|
48
|
+
|
|
49
|
+
`Outliner` is `FormatToolbar` + `OutlineList` + `useTreeHistory`. Use them directly for a custom layout, and the pure tree
|
|
50
|
+
operations (`addChild`, `addSibling`, `indentNode`, `outdentNode`, `moveNode`, `setTopic`, `setNodeStyle`, `toggleFold`, …) for your own UI.
|
|
51
|
+
|
|
52
|
+
| Prop | Type | |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `value` / `onChange` | `TreeNode` | Controlled tree |
|
|
55
|
+
| `locale` | `string` | `ko` · `en` · `ja` · `zh` · `de` · `fr` · `it` · `vi` (default English) |
|
|
56
|
+
| `strings` | `Partial<OutlinerStrings>` | Override single labels |
|
|
57
|
+
| `readOnly` | `boolean` | |
|
|
58
|
+
| `toolbar` | `boolean` | Show the toolbar (default `true`) |
|
|
59
|
+
| `toolbarStart` / `toolbarEnd` | `ReactNode` | Your own controls in the toolbar |
|
|
60
|
+
| `height` | `number \| string` | Default `100%` |
|
|
61
|
+
|
|
62
|
+
License: MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting toolbar — font size · bold · underline · strike · text color · background · clear format, plus undo / redo.
|
|
3
|
+
* Shared by the outline and the mind map; it edits the style of the selected node.
|
|
4
|
+
* Looks like the @ehfuse/editor toolbar on purpose: same icons (@ehfuse/toolbar-icons), same 32px buttons,
|
|
5
|
+
* same black tooltip and the same color picker (@ehfuse/mui-color-picker).
|
|
6
|
+
* `start` / `end` / `children` let the host add its own controls (view switch, map tools, …).
|
|
7
|
+
*/
|
|
8
|
+
import { type MouseEvent, type ReactNode } from "react";
|
|
9
|
+
import { type TreeNodeStyle } from "./tree";
|
|
10
|
+
import type { OutlinerStrings } from "./i18n";
|
|
11
|
+
export declare const FONT_SIZES: number[];
|
|
12
|
+
export interface FormatToolbarProps {
|
|
13
|
+
strings: OutlinerStrings;
|
|
14
|
+
/** Style of the node being edited (undefined → nothing selected; format buttons are disabled). */
|
|
15
|
+
style: TreeNodeStyle | undefined;
|
|
16
|
+
onStyle: (patch: Partial<TreeNodeStyle>) => void;
|
|
17
|
+
canUndo?: boolean;
|
|
18
|
+
canRedo?: boolean;
|
|
19
|
+
onUndo?: () => void;
|
|
20
|
+
onRedo?: () => void;
|
|
21
|
+
start?: ReactNode;
|
|
22
|
+
children?: ReactNode;
|
|
23
|
+
end?: ReactNode;
|
|
24
|
+
readOnly?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Same tooltip as the editor toolbar — black, 14px, arrow, below. */
|
|
27
|
+
export declare function ToolTip({ title, children }: {
|
|
28
|
+
title: string;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
}): import("react").JSX.Element;
|
|
31
|
+
/** Same button as the editor toolbar — 32px, 4px radius, light grey when on. */
|
|
32
|
+
export declare function ToolButton({ label, active, disabled, onClick, children }: {
|
|
33
|
+
label: string;
|
|
34
|
+
active?: boolean;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
onClick: (event: MouseEvent<HTMLElement>) => void;
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
}): import("react").JSX.Element;
|
|
39
|
+
export declare const ToolDivider: () => import("react").JSX.Element;
|
|
40
|
+
export declare function FormatToolbar({ strings, style, onStyle, canUndo, canRedo, onUndo, onRedo, start, children, end, readOnly }: FormatToolbarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outline rows — one node per line, fold / unfold, indent / outdent, reorder, drag to move, per-node style.
|
|
3
|
+
*
|
|
4
|
+
* Keys: Enter new line · Tab indent · Shift+Tab outdent · Alt+↑/↓ reorder · ↑/↓ previous / next row ·
|
|
5
|
+
* Backspace on an empty row deletes it · Ctrl/⌘+Z undo · Ctrl/⌘+Shift+Z or Ctrl+Y redo.
|
|
6
|
+
* Dragging follows the pointer directly (native drag & drop doesn't fire drops reliably inside drawers / modals).
|
|
7
|
+
*/
|
|
8
|
+
import { type CSSProperties } from "react";
|
|
9
|
+
import { type TreeNode, type TreeNodeStyle } from "./tree";
|
|
10
|
+
import type { OutlinerStrings } from "./i18n";
|
|
11
|
+
export interface OutlineListProps {
|
|
12
|
+
root: TreeNode;
|
|
13
|
+
/** A change; `mergeKey` is set for typing so history can merge it. */
|
|
14
|
+
onEdit: (root: TreeNode, mergeKey?: string) => void;
|
|
15
|
+
selectedId?: string | null;
|
|
16
|
+
onSelect?: (id: string) => void;
|
|
17
|
+
strings: OutlinerStrings;
|
|
18
|
+
readOnly?: boolean;
|
|
19
|
+
onUndo?: () => void;
|
|
20
|
+
onRedo?: () => void;
|
|
21
|
+
/** Extra space above the first row / below the last row (px). */
|
|
22
|
+
paddingTop?: number;
|
|
23
|
+
paddingBottom?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Node style → input style (font size is clamped so the list stays a list). */
|
|
26
|
+
export declare function outlineTextStyle(style: TreeNodeStyle | undefined, depth: number): CSSProperties;
|
|
27
|
+
export declare function OutlineList({ root, onEdit, selectedId, onSelect, strings, readOnly, onUndo, onRedo, paddingTop, paddingBottom }: OutlineListProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outliner — toolbar + rows, with its own undo history. Controlled: pass `value` and `onChange`.
|
|
3
|
+
* For a custom layout use `OutlineList` + `FormatToolbar` + `useTreeHistory` directly (the mind map does).
|
|
4
|
+
*/
|
|
5
|
+
import { type ReactNode } from "react";
|
|
6
|
+
import { type OutlinerStrings } from "./i18n";
|
|
7
|
+
import { type TreeNode } from "./tree";
|
|
8
|
+
export interface OutlinerProps {
|
|
9
|
+
value: TreeNode;
|
|
10
|
+
onChange: (value: TreeNode) => void;
|
|
11
|
+
locale?: string;
|
|
12
|
+
strings?: Partial<OutlinerStrings>;
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
/** Show the formatting toolbar (default true). */
|
|
15
|
+
toolbar?: boolean;
|
|
16
|
+
toolbarStart?: ReactNode;
|
|
17
|
+
toolbarEnd?: ReactNode;
|
|
18
|
+
height?: number | string;
|
|
19
|
+
}
|
|
20
|
+
export declare function Outliner({ value, onChange, locale, strings: overrides, readOnly, toolbar, toolbarStart, toolbarEnd, height }: OutlinerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TreeNode } from "./tree";
|
|
2
|
+
export interface TreeHistory {
|
|
3
|
+
canUndo: boolean;
|
|
4
|
+
canRedo: boolean;
|
|
5
|
+
/** Records the tree before a change. `mergeKey` merges consecutive changes with the same key (e.g. typing in one node). */
|
|
6
|
+
record: (before: TreeNode, mergeKey?: string) => void;
|
|
7
|
+
undo: (current: TreeNode) => TreeNode | null;
|
|
8
|
+
redo: (current: TreeNode) => TreeNode | null;
|
|
9
|
+
clear: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function useTreeHistory(limit?: number, mergeMs?: number): TreeHistory;
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/** UI strings (ko · en · ja · zh · de · fr · it · vi). Unknown locale → English. */
|
|
2
|
+
declare const STRINGS: {
|
|
3
|
+
readonly ko: {
|
|
4
|
+
readonly clearFormat: "서식 지우기";
|
|
5
|
+
readonly fontSize: "글자 크기";
|
|
6
|
+
readonly sizeDefault: "기본";
|
|
7
|
+
readonly bold: "진하게";
|
|
8
|
+
readonly underline: "밑줄";
|
|
9
|
+
readonly strike: "취소선";
|
|
10
|
+
readonly textColor: "글자색";
|
|
11
|
+
readonly background: "배경색";
|
|
12
|
+
readonly noColor: "없음";
|
|
13
|
+
readonly undo: "되돌리기";
|
|
14
|
+
readonly redo: "다시 하기";
|
|
15
|
+
readonly fold: "접기";
|
|
16
|
+
readonly unfold: "펼치기";
|
|
17
|
+
readonly drag: "끌어서 옮기기";
|
|
18
|
+
readonly root: "주제";
|
|
19
|
+
readonly hint: "Enter: 새 줄 · Tab / Shift+Tab: 들여쓰기 / 내어쓰기 · Alt+↑↓: 순서 · 줄 앞 손잡이 끌기: 옮기기 · 빈 줄 Backspace: 지우기";
|
|
20
|
+
};
|
|
21
|
+
readonly en: {
|
|
22
|
+
readonly clearFormat: "Clear format";
|
|
23
|
+
readonly fontSize: "Font size";
|
|
24
|
+
readonly sizeDefault: "Default";
|
|
25
|
+
readonly bold: "Bold";
|
|
26
|
+
readonly underline: "Underline";
|
|
27
|
+
readonly strike: "Strikethrough";
|
|
28
|
+
readonly textColor: "Text color";
|
|
29
|
+
readonly background: "Background";
|
|
30
|
+
readonly noColor: "None";
|
|
31
|
+
readonly undo: "Undo";
|
|
32
|
+
readonly redo: "Redo";
|
|
33
|
+
readonly fold: "Collapse";
|
|
34
|
+
readonly unfold: "Expand";
|
|
35
|
+
readonly drag: "Drag to move";
|
|
36
|
+
readonly root: "Topic";
|
|
37
|
+
readonly hint: "Enter: new line · Tab / Shift+Tab: indent / outdent · Alt+↑↓: reorder · Drag the row handle: move · Backspace on empty: delete";
|
|
38
|
+
};
|
|
39
|
+
readonly ja: {
|
|
40
|
+
readonly clearFormat: "書式をクリア";
|
|
41
|
+
readonly fontSize: "文字サイズ";
|
|
42
|
+
readonly sizeDefault: "標準";
|
|
43
|
+
readonly bold: "太字";
|
|
44
|
+
readonly underline: "下線";
|
|
45
|
+
readonly strike: "取り消し線";
|
|
46
|
+
readonly textColor: "文字色";
|
|
47
|
+
readonly background: "背景色";
|
|
48
|
+
readonly noColor: "なし";
|
|
49
|
+
readonly undo: "元に戻す";
|
|
50
|
+
readonly redo: "やり直す";
|
|
51
|
+
readonly fold: "折りたたむ";
|
|
52
|
+
readonly unfold: "展開";
|
|
53
|
+
readonly drag: "ドラッグで移動";
|
|
54
|
+
readonly root: "テーマ";
|
|
55
|
+
readonly hint: "Enter: 新しい行 · Tab / Shift+Tab: 字下げ / 字上げ · Alt+↑↓: 並べ替え · 行頭のハンドルをドラッグ: 移動 · 空行で Backspace: 削除";
|
|
56
|
+
};
|
|
57
|
+
readonly zh: {
|
|
58
|
+
readonly clearFormat: "清除格式";
|
|
59
|
+
readonly fontSize: "字号";
|
|
60
|
+
readonly sizeDefault: "默认";
|
|
61
|
+
readonly bold: "加粗";
|
|
62
|
+
readonly underline: "下划线";
|
|
63
|
+
readonly strike: "删除线";
|
|
64
|
+
readonly textColor: "文字颜色";
|
|
65
|
+
readonly background: "背景色";
|
|
66
|
+
readonly noColor: "无";
|
|
67
|
+
readonly undo: "撤销";
|
|
68
|
+
readonly redo: "重做";
|
|
69
|
+
readonly fold: "折叠";
|
|
70
|
+
readonly unfold: "展开";
|
|
71
|
+
readonly drag: "拖动以移动";
|
|
72
|
+
readonly root: "主题";
|
|
73
|
+
readonly hint: "Enter: 新行 · Tab / Shift+Tab: 缩进 / 取消缩进 · Alt+↑↓: 顺序 · 拖动行首手柄: 移动 · 空行 Backspace: 删除";
|
|
74
|
+
};
|
|
75
|
+
readonly de: {
|
|
76
|
+
readonly clearFormat: "Formatierung löschen";
|
|
77
|
+
readonly fontSize: "Schriftgröße";
|
|
78
|
+
readonly sizeDefault: "Standard";
|
|
79
|
+
readonly bold: "Fett";
|
|
80
|
+
readonly underline: "Unterstrichen";
|
|
81
|
+
readonly strike: "Durchgestrichen";
|
|
82
|
+
readonly textColor: "Textfarbe";
|
|
83
|
+
readonly background: "Hintergrund";
|
|
84
|
+
readonly noColor: "Keine";
|
|
85
|
+
readonly undo: "Rückgängig";
|
|
86
|
+
readonly redo: "Wiederholen";
|
|
87
|
+
readonly fold: "Einklappen";
|
|
88
|
+
readonly unfold: "Ausklappen";
|
|
89
|
+
readonly drag: "Zum Verschieben ziehen";
|
|
90
|
+
readonly root: "Thema";
|
|
91
|
+
readonly hint: "Enter: neue Zeile · Tab / Shift+Tab: ein- / ausrücken · Alt+↑↓: Reihenfolge · Griff ziehen: verschieben · Backspace leer: löschen";
|
|
92
|
+
};
|
|
93
|
+
readonly fr: {
|
|
94
|
+
readonly clearFormat: "Effacer la mise en forme";
|
|
95
|
+
readonly fontSize: "Taille";
|
|
96
|
+
readonly sizeDefault: "Par défaut";
|
|
97
|
+
readonly bold: "Gras";
|
|
98
|
+
readonly underline: "Souligné";
|
|
99
|
+
readonly strike: "Barré";
|
|
100
|
+
readonly textColor: "Couleur du texte";
|
|
101
|
+
readonly background: "Arrière-plan";
|
|
102
|
+
readonly noColor: "Aucune";
|
|
103
|
+
readonly undo: "Annuler";
|
|
104
|
+
readonly redo: "Rétablir";
|
|
105
|
+
readonly fold: "Replier";
|
|
106
|
+
readonly unfold: "Déplier";
|
|
107
|
+
readonly drag: "Glisser pour déplacer";
|
|
108
|
+
readonly root: "Sujet";
|
|
109
|
+
readonly hint: "Entrée : nouvelle ligne · Tab / Maj+Tab : indenter / désindenter · Alt+↑↓ : ordre · Glisser la poignée : déplacer · Retour arrière vide : supprimer";
|
|
110
|
+
};
|
|
111
|
+
readonly it: {
|
|
112
|
+
readonly clearFormat: "Cancella formattazione";
|
|
113
|
+
readonly fontSize: "Dimensione";
|
|
114
|
+
readonly sizeDefault: "Predefinita";
|
|
115
|
+
readonly bold: "Grassetto";
|
|
116
|
+
readonly underline: "Sottolineato";
|
|
117
|
+
readonly strike: "Barrato";
|
|
118
|
+
readonly textColor: "Colore testo";
|
|
119
|
+
readonly background: "Sfondo";
|
|
120
|
+
readonly noColor: "Nessuno";
|
|
121
|
+
readonly undo: "Annulla";
|
|
122
|
+
readonly redo: "Ripeti";
|
|
123
|
+
readonly fold: "Comprimi";
|
|
124
|
+
readonly unfold: "Espandi";
|
|
125
|
+
readonly drag: "Trascina per spostare";
|
|
126
|
+
readonly root: "Argomento";
|
|
127
|
+
readonly hint: "Invio: nuova riga · Tab / Maiusc+Tab: rientro / riduci · Alt+↑↓: ordine · Trascina la maniglia: sposta · Backspace su vuota: elimina";
|
|
128
|
+
};
|
|
129
|
+
readonly vi: {
|
|
130
|
+
readonly clearFormat: "Xóa định dạng";
|
|
131
|
+
readonly fontSize: "Cỡ chữ";
|
|
132
|
+
readonly sizeDefault: "Mặc định";
|
|
133
|
+
readonly bold: "Đậm";
|
|
134
|
+
readonly underline: "Gạch chân";
|
|
135
|
+
readonly strike: "Gạch ngang";
|
|
136
|
+
readonly textColor: "Màu chữ";
|
|
137
|
+
readonly background: "Màu nền";
|
|
138
|
+
readonly noColor: "Không";
|
|
139
|
+
readonly undo: "Hoàn tác";
|
|
140
|
+
readonly redo: "Làm lại";
|
|
141
|
+
readonly fold: "Thu gọn";
|
|
142
|
+
readonly unfold: "Mở rộng";
|
|
143
|
+
readonly drag: "Kéo để di chuyển";
|
|
144
|
+
readonly root: "Chủ đề";
|
|
145
|
+
readonly hint: "Enter: dòng mới · Tab / Shift+Tab: thụt vào / ra · Alt+↑↓: thứ tự · Kéo tay cầm: di chuyển · Backspace dòng trống: xóa";
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
export type OutlinerStrings = {
|
|
149
|
+
[K in keyof (typeof STRINGS)["en"]]: string;
|
|
150
|
+
};
|
|
151
|
+
/** Strings for a locale ("ko", "ko-KR", …). `overrides` replaces single entries. */
|
|
152
|
+
export declare function outlinerStrings(locale?: string, overrides?: Partial<OutlinerStrings>): OutlinerStrings;
|
|
153
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Outliner, type OutlinerProps } from "./Outliner";
|
|
2
|
+
export { OutlineList, outlineTextStyle, type OutlineListProps } from "./OutlineList";
|
|
3
|
+
export { FormatToolbar, ToolButton, ToolDivider, ToolTip, FONT_SIZES, type FormatToolbarProps } from "./FormatToolbar";
|
|
4
|
+
export { useTreeHistory, type TreeHistory } from "./history";
|
|
5
|
+
export { outlinerStrings, type OutlinerStrings } from "./i18n";
|
|
6
|
+
export { measureText, DEFAULT_FONT_FAMILY } from "./measure";
|
|
7
|
+
export * from "./tree";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useState as _e}from"react";import{Box as Te}from"@mui/material";import{useState as fe}from"react";import{Box as w,ButtonBase as ve,Menu as Be,MenuItem as ce,Tooltip as Ee}from"@mui/material";import{ColorPicker as ze}from"@ehfuse/mui-color-picker";import{BoldIcon as Fe,FontColorIcon as Ie,FontFillIcon as Re,RedoIcon as Me,RemoveFormatIcon as we,StrikeIcon as Pe,UnderlineIcon as Ae,UndoIcon as He}from"@ehfuse/toolbar-icons";var Q=()=>Math.random().toString(16).slice(2,18),Ve=e=>({id:"root",topic:e,children:[]}),ke=e=>structuredClone(e);function h(e,o){if(e.id===o)return{node:e,parent:null,index:0};let t=[e];for(;t.length;){let i=t.pop(),r=i.children??[];for(let l=0;l<r.length;l++){if(r[l].id===o)return{node:r[l],parent:i,index:l};t.push(r[l])}}return null}var A=(e,o)=>e.id===o||(e.children??[]).some(t=>A(t,o)),Xe=e=>e.expanded===!1&&(e.children?.length??0)>0;function ee(e){let o=[],t=(i,r,l)=>{if(o.push({node:i,depth:r,parentId:l}),i.expanded!==!1)for(let d of i.children??[])t(d,r+1,i.id)};return t(e,0,null),o}function B(e,o){let t=ke(e),i=o(t);return i===!1?null:{root:t,focusId:i||void 0}}var De=(e,o,t="",i=!1)=>B(e,r=>{let l=h(r,o);if(!l)return!1;let d={id:Q(),topic:t};return l.node.children=i?[d,...l.node.children??[]]:[...l.node.children??[],d],l.node.expanded=!0,d.id}),Ce=(e,o,t="")=>B(e,i=>{let r=h(i,o);if(!r)return!1;let l={id:Q(),topic:t};return r.parent?r.parent.children.splice(r.index+1,0,l):(r.node.children=[l,...r.node.children??[]],r.node.expanded=!0),l.id}),oe=(e,o)=>{let t=h(e,o);return t&&(!t.parent||(t.node.children?.length??0)>0&&t.node.expanded!==!1)?De(e,o,"",!0):Ce(e,o)},ne=(e,o)=>B(e,t=>{let i=h(t,o);return i?.parent?(i.parent.children.splice(i.index,1),i.parent.children[i.index-1]?.id??i.parent.id):!1}),te=(e,o)=>B(e,t=>{let i=h(t,o);if(!i?.parent||i.index===0)return!1;let r=i.parent.children[i.index-1];return i.parent.children.splice(i.index,1),r.children=[...r.children??[],i.node],r.expanded=!0,o}),re=(e,o)=>B(e,t=>{let i=h(t,o);if(!i?.parent)return!1;let r=h(t,i.parent.id);return r?.parent?(i.parent.children.splice(i.index,1),r.parent.children.splice(r.index+1,0,i.node),o):!1}),ie=(e,o,t)=>B(e,i=>{let r=h(i,o);if(!r?.parent)return!1;let l=r.parent.children,d=r.index+t;return d<0||d>=l.length?!1:([l[r.index],l[d]]=[l[d],l[r.index]],o)}),le=(e,o,t,i)=>B(e,r=>{let l=h(r,o);if(!l?.parent||A(l.node,t))return!1;l.parent.children.splice(l.index,1);let d=h(r,t);return d?(i==="child"||!d.parent?(d.node.children=[...d.node.children??[],l.node],d.node.expanded=!0):d.parent.children.splice(d.index+(i==="after"?1:0),0,l.node),o):!1}),de=(e,o,t)=>B(e,i=>{let r=h(i,o);if(!r||r.node.topic===t)return!1;r.node.topic=t}),ae=(e,o,t)=>B(e,i=>{let r=h(i,o);if(!r||!(r.node.children?.length??0))return!1;r.node.expanded=t??r.node.expanded===!1}),se=(e,o,t)=>B(e,i=>{let r=!1;for(let l of o){let d=h(i,l);if(!d)continue;let c={...d.node.style??{}};for(let[g,k]of Object.entries(t))k===void 0||k===""?delete c[g]:c[g]=k;d.node.style=Object.keys(c).length?c:void 0,d.node.style||delete d.node.style,r=!0}return r?void 0:!1});function U(e,o){return(e?.textDecoration??"").split(/\s+/).includes(o)}function W(e,o){let t=new Set((e?.textDecoration??"").split(/\s+/).filter(Boolean));return t.has(o)?t.delete(o):t.add(o),t.size?[...t].join(" "):void 0}import{Fragment as ge,jsx as a,jsxs as M}from"react/jsx-runtime";var pe=[12,14,16,18,20,24,28,32];function K({title:e,children:o}){return a(Ee,{title:e,placement:"bottom",arrow:!0,disableInteractive:!0,leaveDelay:0,slotProps:{tooltip:{sx:{bgcolor:"#000000",color:"#ffffff",fontSize:"14px",fontWeight:"normal","& .MuiTooltip-arrow":{color:"#000000"}}}},children:a("span",{style:{display:"inline-flex"},children:o})})}function F({label:e,active:o=!1,disabled:t=!1,onClick:i,children:r}){return a(K,{title:e,children:a(w,{component:"button",type:"button","aria-label":e,"aria-pressed":o,disabled:t,onMouseDown:l=>l.preventDefault(),onClick:l=>{l.preventDefault(),t||i(l)},sx:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:32,height:32,p:"6px",border:"none",borderRadius:"4px",bgcolor:o?"rgba(0, 0, 0, 0.08)":"transparent",color:t?"rgba(0, 0, 0, 0.26)":"inherit",cursor:t?"not-allowed":"pointer",transition:"background-color 150ms cubic-bezier(0.4, 0, 0.2, 1)","&:hover:not(:disabled)":{bgcolor:o?"rgba(0, 0, 0, 0.08)":"rgba(0, 0, 0, 0.04)"},"&:focus":{outline:"none"},"& svg":{width:20,height:20}},children:r})})}var H=()=>a(w,{sx:{width:"1px",alignSelf:"stretch",my:"8px",mx:"4px",bgcolor:"#e0e0e0"}});function ue({label:e,type:o,value:t,disabled:i,onPick:r}){let[l,d]=fe(null),c=t??(o==="color"?"#000000":"#FFFFFF");return M(ge,{children:[a(F,{label:e,disabled:i,onClick:g=>d(g.currentTarget),children:o==="color"?a(Ie,{width:"20px",height:"20px",barColor:c}):a(Re,{width:"20px",fillColor:c})}),l?a(ze,{value:c,onChange:g=>{r(g),d(null)},anchor:l,anchorMode:"auto",onClose:()=>d(null),position:"bottomCenter",advanced:{position:"bottom"},shape:"square",colorSize:16,gap:4,padding:5,popover:{disableEnforceFocus:!0,disableAutoFocus:!0,disableRestoreFocus:!0,sx:{zIndex:10001}}}):null]})}function G({strings:e,style:o,onStyle:t,canUndo:i,canRedo:r,onUndo:l,onRedo:d,start:c,children:g,end:k,readOnly:m}){let[f,u]=fe(null),p=!!m||!o,x=o?.fontSize?parseFloat(o.fontSize):null,z=o?.fontWeight==="bold"||Number(o?.fontWeight)>=600,y=!!(o&&Object.keys(o).length>0);return M(w,{sx:{display:"flex",alignItems:"center",flexWrap:"wrap",gap:"2px",minHeight:44,px:1,py:"4px",borderBottom:"1px solid #e0e0e0",bgcolor:"#fff",color:"#333",flexShrink:0},children:[c,c?a(H,{}):null,l?M(ge,{children:[a(F,{label:e.undo,disabled:m||!i,onClick:l,children:a(He,{})}),a(F,{label:e.redo,disabled:m||!r,onClick:()=>d?.(),children:a(Me,{})}),a(H,{})]}):null,a(K,{title:e.fontSize,children:M(ve,{disabled:p,onMouseDown:s=>s.preventDefault(),onClick:s=>u(s.currentTarget),sx:{height:32,minWidth:64,px:1,gap:.5,justifyContent:"space-between",border:"1px solid #d0d0d0",borderRadius:"4px",fontSize:14,color:p?"rgba(0,0,0,0.26)":"#333",bgcolor:"#fff","&:hover":{borderColor:"#999"}},children:[x??e.sizeDefault,a(w,{component:"span",sx:{fontSize:10,color:"#888"},children:"\u25BC"})]})}),M(Be,{open:!!f,anchorEl:f,onClose:()=>u(null),transitionDuration:0,disableRestoreFocus:!0,slotProps:{paper:{sx:{maxHeight:320}}},children:[a(ce,{dense:!0,selected:x===null,onClick:()=>{t({fontSize:void 0}),u(null)},children:e.sizeDefault}),pe.map(s=>a(ce,{dense:!0,selected:x===s,onClick:()=>{t({fontSize:`${s}px`}),u(null)},children:s},s))]}),a(F,{label:e.bold,active:z,disabled:p,onClick:()=>t({fontWeight:z?void 0:"bold"}),children:a(Fe,{})}),a(F,{label:e.underline,active:U(o,"underline"),disabled:p,onClick:()=>t({textDecoration:W(o,"underline")}),children:a(Ae,{})}),a(F,{label:e.strike,active:U(o,"line-through"),disabled:p,onClick:()=>t({textDecoration:W(o,"line-through")}),children:a(Pe,{})}),a(ue,{label:e.textColor,type:"color",value:o?.color,disabled:p,onPick:s=>t({color:s})}),a(ue,{label:e.background,type:"background",value:o?.background,disabled:p,onPick:s=>t({background:s.toLowerCase()==="#ffffff"?void 0:s})}),a(F,{label:e.clearFormat,disabled:p||!y,onClick:()=>t({fontSize:void 0,color:void 0,background:void 0,fontWeight:void 0,textDecoration:void 0}),children:a(we,{})}),g?a(H,{}):null,g,a(w,{sx:{flex:1}}),k]})}import{useEffect as Oe,useRef as xe,useState as j}from"react";import{Box as R,IconButton as Le}from"@mui/material";import Ue from"@mui/icons-material/ArrowRight";import We from"@mui/icons-material/ArrowDropDown";import Ke from"@mui/icons-material/DragIndicator";var P=null,O=new Map,he="-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif";function $(e,o,t=400,i=he){let r=`${t} ${o}px ${i}`,l=`${r}|${e}`,d=O.get(l);if(d!==void 0)return d;!P&&typeof document<"u"&&(P=document.createElement("canvas").getContext("2d"));let c=0;if(P)P.font=r,c=P.measureText(e).width;else for(let g of e)c+=(/[ᄀ-ᅵ]/.test(g)?1:.55)*o;return O.size>5e3&&O.clear(),O.set(l,c),c}import{jsx as E,jsxs as je}from"react/jsx-runtime";var Ge=22,$e=15;function be(e,o){return{fontSize:e?.fontSize?Math.min(28,Math.max(12,parseFloat(e.fontSize))):$e,fontWeight:e?.fontWeight??(o===0?700:400),color:e?.color,textDecoration:e?.textDecoration,background:e?.background}}function Y({root:e,onEdit:o,selectedId:t,onSelect:i,strings:r,readOnly:l,onUndo:d,onRedo:c,paddingTop:g=8,paddingBottom:k=40}){let m=xe(new Map),f=xe(new Map),[u,p]=j(null),[x,z]=j(null),[y,s]=j(null),D=ee(e);Oe(()=>{if(!u)return;let n=m.current.get(u);n&&(n.focus(),n.setSelectionRange(n.value.length,n.value.length),p(null))},[u,e]);let I=(n,T)=>{n&&(o(n.root,T),n.focusId&&(p(n.focusId),i?.(n.focusId)))},Ne=(n,T)=>{if(n.nativeEvent.isComposing)return;let C=D[T],S=C.node.id,v=n.ctrlKey||n.metaKey;if(v&&(n.key==="z"||n.key==="Z")){n.preventDefault(),n.shiftKey?c?.():d?.();return}if(v&&(n.key==="y"||n.key==="Y")){n.preventDefault(),c?.();return}if(!l){if(n.key==="Enter")n.preventDefault(),I(oe(e,S));else if(n.key==="Tab")n.preventDefault(),I(n.shiftKey?re(e,S):te(e,S));else if(n.altKey&&(n.key==="ArrowUp"||n.key==="ArrowDown"))n.preventDefault(),I(ie(e,S,n.key==="ArrowUp"?-1:1));else if(n.key==="ArrowUp"||n.key==="ArrowDown"){let b=D[T+(n.key==="ArrowUp"?-1:1)];b&&(n.preventDefault(),p(b.node.id),i?.(b.node.id))}else if(n.key==="Backspace"&&n.currentTarget.value===""&&C.depth>0&&!(C.node.children?.length??0)){n.preventDefault();let b=ne(e,S);b&&I({root:b.root,focusId:D[T-1]?.node.id})}}},X=y?h(e,y)?.node??null:null,ye=(n,T)=>{n.button!==0||l||(n.preventDefault(),n.currentTarget.setPointerCapture(n.pointerId),s(T),z(null))},Se=n=>{if(!y||!X)return;let T=null;for(let C of D){let S=f.current.get(C.node.id);if(!S)continue;let v=S.getBoundingClientRect();if(!(n.clientY<v.top||n.clientY>=v.bottom)){A(X,C.node.id)||(T={id:C.node.id,place:n.clientY<v.top+v.height/2?"before":"after"});break}}(T?.id!==x?.id||T?.place!==x?.place)&&z(T)},J=n=>{n.currentTarget.hasPointerCapture(n.pointerId)&&n.currentTarget.releasePointerCapture(n.pointerId),y&&x&&I(le(e,y,x.id,x.place)),s(null),z(null)};return E(R,{sx:{position:"relative",height:"100%",overflowY:"auto",bgcolor:"#fff",pt:`${g}px`,pb:`${k}px`,px:1.5},children:D.map((n,T)=>{let C=(n.node.children?.length??0)>0,S=n.node.expanded===!1,v=x?.id===n.node.id?x.place:null,b=be(n.node.style,n.depth);return je(R,{ref:N=>{N?f.current.set(n.node.id,N):f.current.delete(n.node.id)},sx:{display:"flex",alignItems:"center",minHeight:26,pl:`${n.depth*Ge}px`,borderRadius:.75,bgcolor:t===n.node.id?"rgba(26,115,232,0.06)":"transparent",boxShadow:N=>v==="before"?`inset 0 2px 0 ${N.palette.primary.main}`:v==="after"?`inset 0 -2px 0 ${N.palette.primary.main}`:"none",opacity:y===n.node.id?.4:1,"&:hover .ol-handle":{visibility:"visible"}},children:[E(R,{className:"ol-handle",title:r.drag,onPointerDown:n.depth>0?N=>ye(N,n.node.id):void 0,onPointerMove:Se,onPointerUp:J,onPointerCancel:J,sx:{display:"flex",color:"#b0b4ba",cursor:n.depth>0&&!l?y?"grabbing":"grab":"default",visibility:y===n.node.id?"visible":"hidden",width:18,flexShrink:0,touchAction:"none"},children:n.depth>0&&!l?E(Ke,{sx:{fontSize:16}}):null}),C?E(Le,{size:"small","aria-label":S?r.unfold:r.fold,title:S?r.unfold:r.fold,onClick:()=>I(ae(e,n.node.id)),sx:{width:22,height:22,p:0,color:"text.secondary"},children:S?E(Ue,{sx:{fontSize:20}}):E(We,{sx:{fontSize:20}})}):E(R,{sx:{width:22,display:"flex",justifyContent:"center",flexShrink:0},children:E(R,{sx:{width:5,height:5,borderRadius:"50%",bgcolor:"#9aa0a6"}})}),E(R,{sx:{flex:1,minWidth:0,display:"flex",ml:.5},children:E("input",{ref:N=>{N?m.current.set(n.node.id,N):m.current.delete(n.node.id)},value:n.node.topic,readOnly:l,placeholder:n.depth===0?r.root:"",spellCheck:!1,autoComplete:"off",onFocus:()=>i?.(n.node.id),onChange:N=>I(de(e,n.node.id,N.target.value),`topic:${n.node.id}`),onKeyDown:N=>Ne(N,T),style:{width:b.background?Math.ceil($(n.node.topic||" ",Number(b.fontSize),b.fontWeight))+16:"100%",maxWidth:"100%",border:0,outline:0,font:"inherit",padding:b.background?"2px 6px":"2px 0",borderRadius:4,background:b.background??"transparent",color:b.color??"inherit",fontSize:b.fontSize,fontWeight:b.fontWeight,textDecoration:[b.textDecoration,S&&C?"underline dotted #9aa0a6":""].filter(Boolean).join(" ")||void 0}})})]},n.node.id)})})}import{useCallback as L,useRef as _,useState as Ye}from"react";function Z(e=100,o=1200){let t=_([]),i=_([]),r=_(null),[,l]=Ye(0),d=()=>l(f=>f+1),c=L((f,u)=>{let p=Date.now(),x=u&&r.current?.key===u&&p-r.current.at<o;r.current=u?{key:u,at:p}:null,i.current=[],x||(t.current.push(f),t.current.length>e&&t.current.shift()),d()},[e,o]),g=L(f=>{let u=t.current.pop();return u?(i.current.push(f),r.current=null,d(),u):null},[]),k=L(f=>{let u=i.current.pop();return u?(t.current.push(f),r.current=null,d(),u):null},[]),m=L(()=>{t.current=[],i.current=[],r.current=null,d()},[]);return{canUndo:t.current.length>0,canRedo:i.current.length>0,record:c,undo:g,redo:k,clear:m}}var me={ko:{clearFormat:"\uC11C\uC2DD \uC9C0\uC6B0\uAE30",fontSize:"\uAE00\uC790 \uD06C\uAE30",sizeDefault:"\uAE30\uBCF8",bold:"\uC9C4\uD558\uAC8C",underline:"\uBC11\uC904",strike:"\uCDE8\uC18C\uC120",textColor:"\uAE00\uC790\uC0C9",background:"\uBC30\uACBD\uC0C9",noColor:"\uC5C6\uC74C",undo:"\uB418\uB3CC\uB9AC\uAE30",redo:"\uB2E4\uC2DC \uD558\uAE30",fold:"\uC811\uAE30",unfold:"\uD3BC\uCE58\uAE30",drag:"\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30",root:"\uC8FC\uC81C",hint:"Enter: \uC0C8 \uC904 \xB7 Tab / Shift+Tab: \uB4E4\uC5EC\uC4F0\uAE30 / \uB0B4\uC5B4\uC4F0\uAE30 \xB7 Alt+\u2191\u2193: \uC21C\uC11C \xB7 \uC904 \uC55E \uC190\uC7A1\uC774 \uB04C\uAE30: \uC62E\uAE30\uAE30 \xB7 \uBE48 \uC904 Backspace: \uC9C0\uC6B0\uAE30"},en:{clearFormat:"Clear format",fontSize:"Font size",sizeDefault:"Default",bold:"Bold",underline:"Underline",strike:"Strikethrough",textColor:"Text color",background:"Background",noColor:"None",undo:"Undo",redo:"Redo",fold:"Collapse",unfold:"Expand",drag:"Drag to move",root:"Topic",hint:"Enter: new line \xB7 Tab / Shift+Tab: indent / outdent \xB7 Alt+\u2191\u2193: reorder \xB7 Drag the row handle: move \xB7 Backspace on empty: delete"},ja:{clearFormat:"\u66F8\u5F0F\u3092\u30AF\u30EA\u30A2",fontSize:"\u6587\u5B57\u30B5\u30A4\u30BA",sizeDefault:"\u6A19\u6E96",bold:"\u592A\u5B57",underline:"\u4E0B\u7DDA",strike:"\u53D6\u308A\u6D88\u3057\u7DDA",textColor:"\u6587\u5B57\u8272",background:"\u80CC\u666F\u8272",noColor:"\u306A\u3057",undo:"\u5143\u306B\u623B\u3059",redo:"\u3084\u308A\u76F4\u3059",fold:"\u6298\u308A\u305F\u305F\u3080",unfold:"\u5C55\u958B",drag:"\u30C9\u30E9\u30C3\u30B0\u3067\u79FB\u52D5",root:"\u30C6\u30FC\u30DE",hint:"Enter: \u65B0\u3057\u3044\u884C \xB7 Tab / Shift+Tab: \u5B57\u4E0B\u3052 / \u5B57\u4E0A\u3052 \xB7 Alt+\u2191\u2193: \u4E26\u3079\u66FF\u3048 \xB7 \u884C\u982D\u306E\u30CF\u30F3\u30C9\u30EB\u3092\u30C9\u30E9\u30C3\u30B0: \u79FB\u52D5 \xB7 \u7A7A\u884C\u3067 Backspace: \u524A\u9664"},zh:{clearFormat:"\u6E05\u9664\u683C\u5F0F",fontSize:"\u5B57\u53F7",sizeDefault:"\u9ED8\u8BA4",bold:"\u52A0\u7C97",underline:"\u4E0B\u5212\u7EBF",strike:"\u5220\u9664\u7EBF",textColor:"\u6587\u5B57\u989C\u8272",background:"\u80CC\u666F\u8272",noColor:"\u65E0",undo:"\u64A4\u9500",redo:"\u91CD\u505A",fold:"\u6298\u53E0",unfold:"\u5C55\u5F00",drag:"\u62D6\u52A8\u4EE5\u79FB\u52A8",root:"\u4E3B\u9898",hint:"Enter: \u65B0\u884C \xB7 Tab / Shift+Tab: \u7F29\u8FDB / \u53D6\u6D88\u7F29\u8FDB \xB7 Alt+\u2191\u2193: \u987A\u5E8F \xB7 \u62D6\u52A8\u884C\u9996\u624B\u67C4: \u79FB\u52A8 \xB7 \u7A7A\u884C Backspace: \u5220\u9664"},de:{clearFormat:"Formatierung l\xF6schen",fontSize:"Schriftgr\xF6\xDFe",sizeDefault:"Standard",bold:"Fett",underline:"Unterstrichen",strike:"Durchgestrichen",textColor:"Textfarbe",background:"Hintergrund",noColor:"Keine",undo:"R\xFCckg\xE4ngig",redo:"Wiederholen",fold:"Einklappen",unfold:"Ausklappen",drag:"Zum Verschieben ziehen",root:"Thema",hint:"Enter: neue Zeile \xB7 Tab / Shift+Tab: ein- / ausr\xFCcken \xB7 Alt+\u2191\u2193: Reihenfolge \xB7 Griff ziehen: verschieben \xB7 Backspace leer: l\xF6schen"},fr:{clearFormat:"Effacer la mise en forme",fontSize:"Taille",sizeDefault:"Par d\xE9faut",bold:"Gras",underline:"Soulign\xE9",strike:"Barr\xE9",textColor:"Couleur du texte",background:"Arri\xE8re-plan",noColor:"Aucune",undo:"Annuler",redo:"R\xE9tablir",fold:"Replier",unfold:"D\xE9plier",drag:"Glisser pour d\xE9placer",root:"Sujet",hint:"Entr\xE9e : nouvelle ligne \xB7 Tab / Maj+Tab : indenter / d\xE9sindenter \xB7 Alt+\u2191\u2193 : ordre \xB7 Glisser la poign\xE9e : d\xE9placer \xB7 Retour arri\xE8re vide : supprimer"},it:{clearFormat:"Cancella formattazione",fontSize:"Dimensione",sizeDefault:"Predefinita",bold:"Grassetto",underline:"Sottolineato",strike:"Barrato",textColor:"Colore testo",background:"Sfondo",noColor:"Nessuno",undo:"Annulla",redo:"Ripeti",fold:"Comprimi",unfold:"Espandi",drag:"Trascina per spostare",root:"Argomento",hint:"Invio: nuova riga \xB7 Tab / Maiusc+Tab: rientro / riduci \xB7 Alt+\u2191\u2193: ordine \xB7 Trascina la maniglia: sposta \xB7 Backspace su vuota: elimina"},vi:{clearFormat:"X\xF3a \u0111\u1ECBnh d\u1EA1ng",fontSize:"C\u1EE1 ch\u1EEF",sizeDefault:"M\u1EB7c \u0111\u1ECBnh",bold:"\u0110\u1EADm",underline:"G\u1EA1ch ch\xE2n",strike:"G\u1EA1ch ngang",textColor:"M\xE0u ch\u1EEF",background:"M\xE0u n\u1EC1n",noColor:"Kh\xF4ng",undo:"Ho\xE0n t\xE1c",redo:"L\xE0m l\u1EA1i",fold:"Thu g\u1ECDn",unfold:"M\u1EDF r\u1ED9ng",drag:"K\xE9o \u0111\u1EC3 di chuy\u1EC3n",root:"Ch\u1EE7 \u0111\u1EC1",hint:"Enter: d\xF2ng m\u1EDBi \xB7 Tab / Shift+Tab: th\u1EE5t v\xE0o / ra \xB7 Alt+\u2191\u2193: th\u1EE9 t\u1EF1 \xB7 K\xE9o tay c\u1EA7m: di chuy\u1EC3n \xB7 Backspace d\xF2ng tr\u1ED1ng: x\xF3a"}};function q(e,o){let t=(e??"en").slice(0,2).toLowerCase();return{...me[t]??me.en,...o}}import{jsx as V,jsxs as qe}from"react/jsx-runtime";function Ze({value:e,onChange:o,locale:t,strings:i,readOnly:r,toolbar:l=!0,toolbarStart:d,toolbarEnd:c,height:g="100%"}){let k=q(t,i),m=Z(),[f,u]=_e(null),p=(s,D)=>{m.record(e,D),o(s)},x=()=>{let s=m.undo(e);s&&o(s)},z=()=>{let s=m.redo(e);s&&o(s)},y=f?h(e,f)?.node:void 0;return qe(Te,{sx:{display:"flex",flexDirection:"column",height:g,minHeight:0,bgcolor:"#fff"},children:[l?V(G,{strings:k,style:y?y.style??{}:void 0,onStyle:s=>{if(!f)return;let D=se(e,[f],s);D&&p(D.root)},canUndo:m.canUndo,canRedo:m.canRedo,onUndo:x,onRedo:z,start:d,end:c,readOnly:r}):null,V(Te,{sx:{flex:1,minHeight:0},children:V(Y,{root:e,onEdit:p,selectedId:f,onSelect:u,strings:k,readOnly:r,onUndo:x,onRedo:z})})]})}export{he as DEFAULT_FONT_FAMILY,pe as FONT_SIZES,G as FormatToolbar,Y as OutlineList,Ze as Outliner,F as ToolButton,H as ToolDivider,K as ToolTip,oe as addBelow,De as addChild,Ce as addSibling,A as containsNode,Ve as createTree,U as hasDecoration,te as indentNode,Xe as isFolded,h as locateNode,$ as measureText,le as moveNode,Q as newNodeId,re as outdentNode,be as outlineTextStyle,q as outlinerStrings,ne as removeNode,se as setNodeStyle,de as setTopic,ie as shiftNode,W as toggleDecoration,ae as toggleFold,Z as useTreeHistory,ee as visibleRows};
|
|
2
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/Outliner.tsx", "../src/FormatToolbar.tsx", "../src/tree.ts", "../src/OutlineList.tsx", "../src/measure.ts", "../src/history.ts", "../src/i18n.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Outliner \u2014 toolbar + rows, with its own undo history. Controlled: pass `value` and `onChange`.\n * For a custom layout use `OutlineList` + `FormatToolbar` + `useTreeHistory` directly (the mind map does).\n */\nimport { useState, type ReactNode } from \"react\";\nimport { Box } from \"@mui/material\";\nimport { FormatToolbar } from \"./FormatToolbar\";\nimport { OutlineList } from \"./OutlineList\";\nimport { useTreeHistory } from \"./history\";\nimport { outlinerStrings, type OutlinerStrings } from \"./i18n\";\nimport { locateNode, setNodeStyle, type TreeNode } from \"./tree\";\n\nexport interface OutlinerProps {\n value: TreeNode;\n onChange: (value: TreeNode) => void;\n locale?: string;\n strings?: Partial<OutlinerStrings>;\n readOnly?: boolean;\n /** Show the formatting toolbar (default true). */\n toolbar?: boolean;\n toolbarStart?: ReactNode;\n toolbarEnd?: ReactNode;\n height?: number | string;\n}\n\nexport function Outliner({ value, onChange, locale, strings: overrides, readOnly, toolbar = true, toolbarStart, toolbarEnd, height = \"100%\" }: OutlinerProps) {\n const strings = outlinerStrings(locale, overrides);\n const history = useTreeHistory();\n const [selected, setSelected] = useState<string | null>(null);\n const commit = (next: TreeNode, mergeKey?: string) => {\n history.record(value, mergeKey);\n onChange(next);\n };\n const undo = () => {\n const prev = history.undo(value);\n if (prev) onChange(prev);\n };\n const redo = () => {\n const next = history.redo(value);\n if (next) onChange(next);\n };\n const selectedNode = selected ? locateNode(value, selected)?.node : undefined;\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height, minHeight: 0, bgcolor: \"#fff\" }}>\n {toolbar ? (\n <FormatToolbar\n strings={strings}\n style={selectedNode ? (selectedNode.style ?? {}) : undefined}\n onStyle={(patch) => {\n if (!selected) return;\n const result = setNodeStyle(value, [selected], patch);\n if (result) commit(result.root);\n }}\n canUndo={history.canUndo}\n canRedo={history.canRedo}\n onUndo={undo}\n onRedo={redo}\n start={toolbarStart}\n end={toolbarEnd}\n readOnly={readOnly}\n />\n ) : null}\n <Box sx={{ flex: 1, minHeight: 0 }}>\n <OutlineList root={value} onEdit={commit} selectedId={selected} onSelect={setSelected} strings={strings} readOnly={readOnly} onUndo={undo} onRedo={redo} />\n </Box>\n </Box>\n );\n}\n", "/**\n * Formatting toolbar \u2014 font size \u00B7 bold \u00B7 underline \u00B7 strike \u00B7 text color \u00B7 background \u00B7 clear format, plus undo / redo.\n * Shared by the outline and the mind map; it edits the style of the selected node.\n * Looks like the @ehfuse/editor toolbar on purpose: same icons (@ehfuse/toolbar-icons), same 32px buttons,\n * same black tooltip and the same color picker (@ehfuse/mui-color-picker).\n * `start` / `end` / `children` let the host add its own controls (view switch, map tools, \u2026).\n */\nimport { useState, type MouseEvent, type ReactNode } from \"react\";\nimport { Box, ButtonBase, Menu, MenuItem, Tooltip } from \"@mui/material\";\nimport { ColorPicker } from \"@ehfuse/mui-color-picker\";\nimport { BoldIcon, FontColorIcon, FontFillIcon, RedoIcon, RemoveFormatIcon, StrikeIcon, UnderlineIcon, UndoIcon } from \"@ehfuse/toolbar-icons\";\nimport { hasDecoration, toggleDecoration, type TreeNodeStyle } from \"./tree\";\nimport type { OutlinerStrings } from \"./i18n\";\n\nexport const FONT_SIZES = [12, 14, 16, 18, 20, 24, 28, 32];\n\nexport interface FormatToolbarProps {\n strings: OutlinerStrings;\n /** Style of the node being edited (undefined \u2192 nothing selected; format buttons are disabled). */\n style: TreeNodeStyle | undefined;\n onStyle: (patch: Partial<TreeNodeStyle>) => void;\n canUndo?: boolean;\n canRedo?: boolean;\n onUndo?: () => void;\n onRedo?: () => void;\n start?: ReactNode;\n children?: ReactNode;\n end?: ReactNode;\n readOnly?: boolean;\n}\n\n/** Same tooltip as the editor toolbar \u2014 black, 14px, arrow, below. */\nexport function ToolTip({ title, children }: { title: string; children: ReactNode }) {\n return (\n <Tooltip\n title={title}\n placement=\"bottom\"\n arrow\n disableInteractive\n leaveDelay={0}\n slotProps={{ tooltip: { sx: { bgcolor: \"#000000\", color: \"#ffffff\", fontSize: \"14px\", fontWeight: \"normal\", \"& .MuiTooltip-arrow\": { color: \"#000000\" } } } }}\n >\n {/* inline-flex \u2014 a plain span adds line descent under the button and lifts it 2px off center. */}\n <span style={{ display: \"inline-flex\" }}>{children}</span>\n </Tooltip>\n );\n}\n\n/** Same button as the editor toolbar \u2014 32px, 4px radius, light grey when on. */\nexport function ToolButton({ label, active = false, disabled = false, onClick, children }: { label: string; active?: boolean; disabled?: boolean; onClick: (event: MouseEvent<HTMLElement>) => void; children: ReactNode }) {\n return (\n <ToolTip title={label}>\n <Box\n component=\"button\"\n type=\"button\"\n aria-label={label}\n aria-pressed={active}\n disabled={disabled}\n onMouseDown={(event: MouseEvent) => event.preventDefault()}\n onClick={(event: MouseEvent<HTMLElement>) => {\n event.preventDefault();\n if (!disabled) onClick(event);\n }}\n sx={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 32,\n height: 32,\n p: \"6px\",\n border: \"none\",\n borderRadius: \"4px\",\n bgcolor: active ? \"rgba(0, 0, 0, 0.08)\" : \"transparent\",\n color: disabled ? \"rgba(0, 0, 0, 0.26)\" : \"inherit\",\n cursor: disabled ? \"not-allowed\" : \"pointer\",\n transition: \"background-color 150ms cubic-bezier(0.4, 0, 0.2, 1)\",\n \"&:hover:not(:disabled)\": { bgcolor: active ? \"rgba(0, 0, 0, 0.08)\" : \"rgba(0, 0, 0, 0.04)\" },\n \"&:focus\": { outline: \"none\" },\n \"& svg\": { width: 20, height: 20 },\n }}\n >\n {children}\n </Box>\n </ToolTip>\n );\n}\n\nexport const ToolDivider = () => <Box sx={{ width: \"1px\", alignSelf: \"stretch\", my: \"8px\", mx: \"4px\", bgcolor: \"#e0e0e0\" }} />;\n\nfunction ColorButton({ label, type, value, disabled, onPick }: { label: string; type: \"color\" | \"background\"; value?: string; disabled: boolean; onPick: (color: string) => void }) {\n const [anchor, setAnchor] = useState<HTMLElement | null>(null);\n const shown = value ?? (type === \"color\" ? \"#000000\" : \"#FFFFFF\");\n return (\n <>\n <ToolButton label={label} disabled={disabled} onClick={(event) => setAnchor(event.currentTarget)}>\n {type === \"color\" ? <FontColorIcon width=\"20px\" height=\"20px\" barColor={shown} /> : <FontFillIcon width=\"20px\" fillColor={shown} />}\n </ToolButton>\n {anchor ? (\n <ColorPicker\n value={shown}\n onChange={(hex: string) => {\n onPick(hex);\n setAnchor(null);\n }}\n anchor={anchor}\n anchorMode=\"auto\"\n onClose={() => setAnchor(null)}\n position=\"bottomCenter\"\n advanced={{ position: \"bottom\" }}\n shape=\"square\"\n colorSize={16}\n gap={4}\n padding={5}\n popover={{ disableEnforceFocus: true, disableAutoFocus: true, disableRestoreFocus: true, sx: { zIndex: 10001 } }}\n />\n ) : null}\n </>\n );\n}\n\nexport function FormatToolbar({ strings, style, onStyle, canUndo, canRedo, onUndo, onRedo, start, children, end, readOnly }: FormatToolbarProps) {\n const [sizeAnchor, setSizeAnchor] = useState<HTMLElement | null>(null);\n const disabled = Boolean(readOnly) || !style;\n const size = style?.fontSize ? parseFloat(style.fontSize) : null;\n const bold = style?.fontWeight === \"bold\" || Number(style?.fontWeight) >= 600;\n const hasStyle = Boolean(style && Object.keys(style).length > 0);\n return (\n <Box sx={{ display: \"flex\", alignItems: \"center\", flexWrap: \"wrap\", gap: \"2px\", minHeight: 44, px: 1, py: \"4px\", borderBottom: \"1px solid #e0e0e0\", bgcolor: \"#fff\", color: \"#333\", flexShrink: 0 }}>\n {start}\n {start ? <ToolDivider /> : null}\n {onUndo ? (\n <>\n <ToolButton label={strings.undo} disabled={readOnly || !canUndo} onClick={onUndo}>\n <UndoIcon />\n </ToolButton>\n <ToolButton label={strings.redo} disabled={readOnly || !canRedo} onClick={() => onRedo?.()}>\n <RedoIcon />\n </ToolButton>\n <ToolDivider />\n </>\n ) : null}\n <ToolTip title={strings.fontSize}>\n <ButtonBase\n disabled={disabled}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => setSizeAnchor(event.currentTarget)}\n sx={{ height: 32, minWidth: 64, px: 1, gap: 0.5, justifyContent: \"space-between\", border: \"1px solid #d0d0d0\", borderRadius: \"4px\", fontSize: 14, color: disabled ? \"rgba(0,0,0,0.26)\" : \"#333\", bgcolor: \"#fff\", \"&:hover\": { borderColor: \"#999\" } }}\n >\n {size ?? strings.sizeDefault}\n <Box component=\"span\" sx={{ fontSize: 10, color: \"#888\" }}>\n \u25BC\n </Box>\n </ButtonBase>\n </ToolTip>\n <Menu open={Boolean(sizeAnchor)} anchorEl={sizeAnchor} onClose={() => setSizeAnchor(null)} transitionDuration={0} disableRestoreFocus slotProps={{ paper: { sx: { maxHeight: 320 } } }}>\n <MenuItem\n dense\n selected={size === null}\n onClick={() => {\n onStyle({ fontSize: undefined });\n setSizeAnchor(null);\n }}\n >\n {strings.sizeDefault}\n </MenuItem>\n {FONT_SIZES.map((value) => (\n <MenuItem\n key={value}\n dense\n selected={size === value}\n onClick={() => {\n onStyle({ fontSize: `${value}px` });\n setSizeAnchor(null);\n }}\n >\n {value}\n </MenuItem>\n ))}\n </Menu>\n <ToolButton label={strings.bold} active={bold} disabled={disabled} onClick={() => onStyle({ fontWeight: bold ? undefined : \"bold\" })}>\n <BoldIcon />\n </ToolButton>\n <ToolButton label={strings.underline} active={hasDecoration(style, \"underline\")} disabled={disabled} onClick={() => onStyle({ textDecoration: toggleDecoration(style, \"underline\") })}>\n <UnderlineIcon />\n </ToolButton>\n <ToolButton label={strings.strike} active={hasDecoration(style, \"line-through\")} disabled={disabled} onClick={() => onStyle({ textDecoration: toggleDecoration(style, \"line-through\") })}>\n <StrikeIcon />\n </ToolButton>\n <ColorButton label={strings.textColor} type=\"color\" value={style?.color} disabled={disabled} onPick={(color) => onStyle({ color })} />\n <ColorButton label={strings.background} type=\"background\" value={style?.background} disabled={disabled} onPick={(color) => onStyle({ background: color.toLowerCase() === \"#ffffff\" ? undefined : color })} />\n <ToolButton\n label={strings.clearFormat}\n disabled={disabled || !hasStyle}\n onClick={() => onStyle({ fontSize: undefined, color: undefined, background: undefined, fontWeight: undefined, textDecoration: undefined })}\n >\n <RemoveFormatIcon />\n </ToolButton>\n {children ? <ToolDivider /> : null}\n {children}\n <Box sx={{ flex: 1 }} />\n {end}\n </Box>\n );\n}\n", "/**\n * Tree data and edit operations shared by the outline and the mind map.\n *\n * The shape follows Mind Elixir's `nodeData` so data saved by it opens unchanged:\n * `{ id, topic, children?, expanded?, style? }` \u2014 unknown keys are carried along untouched.\n * Every operation is pure: it returns a new root (structural copy) and, when useful,\n * the id that should be focused next.\n */\n\n/** Text style of one node (same keys as Mind Elixir `style`). */\nexport interface TreeNodeStyle {\n fontSize?: string;\n color?: string;\n background?: string;\n fontWeight?: string;\n textDecoration?: string;\n}\n\nexport interface TreeNode {\n id: string;\n topic: string;\n children?: TreeNode[];\n /** `false` = folded. Missing means expanded. */\n expanded?: boolean;\n style?: TreeNodeStyle;\n [key: string]: unknown;\n}\n\nexport interface TreeEdit {\n root: TreeNode;\n /** Node to select / focus after the edit. */\n focusId?: string;\n}\n\nexport interface TreeRow {\n node: TreeNode;\n depth: number;\n parentId: string | null;\n}\n\n/** Short random id (hex, like Mind Elixir). */\nexport const newNodeId = () => Math.random().toString(16).slice(2, 18);\n\nexport const createTree = (topic: string): TreeNode => ({ id: \"root\", topic, children: [] });\n\nconst clone = (root: TreeNode): TreeNode => structuredClone(root) as TreeNode;\n\n/** Finds a node with its parent and index (in the given tree). */\nexport function locateNode(root: TreeNode, id: string): { node: TreeNode; parent: TreeNode | null; index: number } | null {\n if (root.id === id) return { node: root, parent: null, index: 0 };\n const stack: TreeNode[] = [root];\n while (stack.length) {\n const parent = stack.pop()!;\n const list = parent.children ?? [];\n for (let i = 0; i < list.length; i++) {\n if (list[i].id === id) return { node: list[i], parent, index: i };\n stack.push(list[i]);\n }\n }\n return null;\n}\n\n/** True when `id` is `node` itself or one of its descendants. */\nexport const containsNode = (node: TreeNode, id: string): boolean => node.id === id || (node.children ?? []).some((child) => containsNode(child, id));\n\nexport const isFolded = (node: TreeNode) => node.expanded === false && (node.children?.length ?? 0) > 0;\n\n/** Rows that are visible (folded subtrees hidden), in reading order. */\nexport function visibleRows(root: TreeNode): TreeRow[] {\n const rows: TreeRow[] = [];\n const walk = (node: TreeNode, depth: number, parentId: string | null) => {\n rows.push({ node, depth, parentId });\n if (node.expanded !== false) for (const child of node.children ?? []) walk(child, depth + 1, node.id);\n };\n walk(root, 0, null);\n return rows;\n}\n\n/** Runs `run` on a copy; returns null when nothing changed (run returned false). */\nfunction edit(root: TreeNode, run: (copy: TreeNode) => string | void | false): TreeEdit | null {\n const copy = clone(root);\n const focusId = run(copy);\n if (focusId === false) return null;\n return { root: copy, focusId: focusId || undefined };\n}\n\n/** New child at the end (or `first`). */\nexport const addChild = (root: TreeNode, id: string, topic = \"\", first = false) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at) return false;\n const fresh: TreeNode = { id: newNodeId(), topic };\n at.node.children = first ? [fresh, ...(at.node.children ?? [])] : [...(at.node.children ?? []), fresh];\n at.node.expanded = true;\n return fresh.id;\n });\n\n/** New sibling right after (root \u2192 first child). */\nexport const addSibling = (root: TreeNode, id: string, topic = \"\") =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at) return false;\n const fresh: TreeNode = { id: newNodeId(), topic };\n if (!at.parent) {\n at.node.children = [fresh, ...(at.node.children ?? [])];\n at.node.expanded = true;\n } else at.parent.children!.splice(at.index + 1, 0, fresh);\n return fresh.id;\n });\n\n/** Outline \"Enter\": first child when the row is an open parent, otherwise next sibling. */\nexport const addBelow = (root: TreeNode, id: string) => {\n const at = locateNode(root, id);\n if (at && (!at.parent || ((at.node.children?.length ?? 0) > 0 && at.node.expanded !== false))) return addChild(root, id, \"\", true);\n return addSibling(root, id);\n};\n\n/** Removes the node (not the root). Focus goes to the previous sibling, else the parent. */\nexport const removeNode = (root: TreeNode, id: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent) return false;\n at.parent.children!.splice(at.index, 1);\n return at.parent.children![at.index - 1]?.id ?? at.parent.id;\n });\n\n/** Becomes the last child of the previous sibling. */\nexport const indentNode = (root: TreeNode, id: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent || at.index === 0) return false;\n const prev = at.parent.children![at.index - 1];\n at.parent.children!.splice(at.index, 1);\n prev.children = [...(prev.children ?? []), at.node];\n prev.expanded = true;\n return id;\n });\n\n/** Becomes the sibling right after its parent. */\nexport const outdentNode = (root: TreeNode, id: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent) return false;\n const up = locateNode(copy, at.parent.id);\n if (!up?.parent) return false;\n at.parent.children!.splice(at.index, 1);\n up.parent.children!.splice(up.index + 1, 0, at.node);\n return id;\n });\n\n/** Moves one step among its siblings. */\nexport const shiftNode = (root: TreeNode, id: string, step: -1 | 1) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent) return false;\n const list = at.parent.children!;\n const to = at.index + step;\n if (to < 0 || to >= list.length) return false;\n [list[at.index], list[to]] = [list[to], list[at.index]];\n return id;\n });\n\n/** Moves `from` before / after `to`, or into `to` as its last child. */\nexport const moveNode = (root: TreeNode, from: string, to: string, place: \"before\" | \"after\" | \"child\") =>\n edit(root, (copy) => {\n const src = locateNode(copy, from);\n if (!src?.parent || containsNode(src.node, to)) return false;\n src.parent.children!.splice(src.index, 1);\n const dst = locateNode(copy, to);\n if (!dst) return false;\n if (place === \"child\" || !dst.parent) {\n dst.node.children = [...(dst.node.children ?? []), src.node];\n dst.node.expanded = true;\n } else dst.parent.children!.splice(dst.index + (place === \"after\" ? 1 : 0), 0, src.node);\n return from;\n });\n\nexport const setTopic = (root: TreeNode, id: string, topic: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at || at.node.topic === topic) return false;\n at.node.topic = topic;\n });\n\nexport const toggleFold = (root: TreeNode, id: string, expanded?: boolean) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at || !(at.node.children?.length ?? 0)) return false;\n at.node.expanded = expanded ?? at.node.expanded === false;\n });\n\n/** Merges a style patch; `undefined` values remove the key. */\nexport const setNodeStyle = (root: TreeNode, ids: string[], patch: Partial<TreeNodeStyle>) =>\n edit(root, (copy) => {\n let changed = false;\n for (const id of ids) {\n const at = locateNode(copy, id);\n if (!at) continue;\n const next: TreeNodeStyle = { ...(at.node.style ?? {}) };\n for (const [key, value] of Object.entries(patch) as [keyof TreeNodeStyle, string | undefined][]) {\n if (value === undefined || value === \"\") delete next[key];\n else next[key] = value;\n }\n at.node.style = Object.keys(next).length ? next : undefined;\n if (!at.node.style) delete at.node.style;\n changed = true;\n }\n return changed ? undefined : false;\n });\n\n/** `textDecoration` holds both underline and line-through. */\nexport function hasDecoration(style: TreeNodeStyle | undefined, kind: \"underline\" | \"line-through\") {\n return (style?.textDecoration ?? \"\").split(/\\s+/).includes(kind);\n}\nexport function toggleDecoration(style: TreeNodeStyle | undefined, kind: \"underline\" | \"line-through\"): string | undefined {\n const set = new Set((style?.textDecoration ?? \"\").split(/\\s+/).filter(Boolean));\n if (set.has(kind)) set.delete(kind);\n else set.add(kind);\n return set.size ? [...set].join(\" \") : undefined;\n}\n", "/**\n * Outline rows \u2014 one node per line, fold / unfold, indent / outdent, reorder, drag to move, per-node style.\n *\n * Keys: Enter new line \u00B7 Tab indent \u00B7 Shift+Tab outdent \u00B7 Alt+\u2191/\u2193 reorder \u00B7 \u2191/\u2193 previous / next row \u00B7\n * Backspace on an empty row deletes it \u00B7 Ctrl/\u2318+Z undo \u00B7 Ctrl/\u2318+Shift+Z or Ctrl+Y redo.\n * Dragging follows the pointer directly (native drag & drop doesn't fire drops reliably inside drawers / modals).\n */\nimport { useEffect, useRef, useState, type CSSProperties, type KeyboardEvent, type PointerEvent } from \"react\";\nimport { Box, IconButton } from \"@mui/material\";\nimport ArrowRightIcon from \"@mui/icons-material/ArrowRight\";\nimport ArrowDropDownIcon from \"@mui/icons-material/ArrowDropDown\";\nimport DragIndicatorIcon from \"@mui/icons-material/DragIndicator\";\nimport {\n addBelow,\n containsNode,\n indentNode,\n locateNode,\n moveNode,\n outdentNode,\n removeNode,\n setTopic,\n shiftNode,\n toggleFold,\n visibleRows,\n type TreeEdit,\n type TreeNode,\n type TreeNodeStyle,\n} from \"./tree\";\nimport type { OutlinerStrings } from \"./i18n\";\nimport { measureText } from \"./measure\";\n\nconst INDENT = 22;\nconst BASE_FONT = 15;\n\nexport interface OutlineListProps {\n root: TreeNode;\n /** A change; `mergeKey` is set for typing so history can merge it. */\n onEdit: (root: TreeNode, mergeKey?: string) => void;\n selectedId?: string | null;\n onSelect?: (id: string) => void;\n strings: OutlinerStrings;\n readOnly?: boolean;\n onUndo?: () => void;\n onRedo?: () => void;\n /** Extra space above the first row / below the last row (px). */\n paddingTop?: number;\n paddingBottom?: number;\n}\n\n/** Node style \u2192 input style (font size is clamped so the list stays a list). */\nexport function outlineTextStyle(style: TreeNodeStyle | undefined, depth: number): CSSProperties {\n const size = style?.fontSize ? Math.min(28, Math.max(12, parseFloat(style.fontSize))) : BASE_FONT;\n return {\n fontSize: size,\n fontWeight: style?.fontWeight ?? (depth === 0 ? 700 : 400),\n color: style?.color,\n textDecoration: style?.textDecoration,\n background: style?.background,\n };\n}\n\nexport function OutlineList({ root, onEdit, selectedId, onSelect, strings, readOnly, onUndo, onRedo, paddingTop = 8, paddingBottom = 40 }: OutlineListProps) {\n const inputs = useRef(new Map<string, HTMLInputElement>());\n const rowEls = useRef(new Map<string, HTMLDivElement>());\n const [focus, setFocus] = useState<string | null>(null);\n const [drop, setDrop] = useState<{ id: string; place: \"before\" | \"after\" } | null>(null);\n const [dragging, setDragging] = useState<string | null>(null);\n const rows = visibleRows(root);\n\n useEffect(() => {\n if (!focus) return;\n const el = inputs.current.get(focus);\n if (!el) return;\n el.focus();\n el.setSelectionRange(el.value.length, el.value.length);\n setFocus(null);\n }, [focus, root]);\n\n const apply = (result: TreeEdit | null, mergeKey?: string) => {\n if (!result) return;\n onEdit(result.root, mergeKey);\n if (result.focusId) {\n setFocus(result.focusId);\n onSelect?.(result.focusId);\n }\n };\n\n const onKey = (event: KeyboardEvent<HTMLInputElement>, index: number) => {\n if (event.nativeEvent.isComposing) return;\n const row = rows[index];\n const id = row.node.id;\n const mod = event.ctrlKey || event.metaKey;\n if (mod && (event.key === \"z\" || event.key === \"Z\")) {\n event.preventDefault();\n if (event.shiftKey) onRedo?.();\n else onUndo?.();\n return;\n }\n if (mod && (event.key === \"y\" || event.key === \"Y\")) {\n event.preventDefault();\n onRedo?.();\n return;\n }\n if (readOnly) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n apply(addBelow(root, id));\n } else if (event.key === \"Tab\") {\n event.preventDefault();\n apply(event.shiftKey ? outdentNode(root, id) : indentNode(root, id));\n } else if (event.altKey && (event.key === \"ArrowUp\" || event.key === \"ArrowDown\")) {\n event.preventDefault();\n apply(shiftNode(root, id, event.key === \"ArrowUp\" ? -1 : 1));\n } else if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n const next = rows[index + (event.key === \"ArrowUp\" ? -1 : 1)];\n if (next) {\n event.preventDefault();\n setFocus(next.node.id);\n onSelect?.(next.node.id);\n }\n } else if (event.key === \"Backspace\" && event.currentTarget.value === \"\" && row.depth > 0 && !(row.node.children?.length ?? 0)) {\n event.preventDefault();\n const result = removeNode(root, id);\n if (result) apply({ root: result.root, focusId: rows[index - 1]?.node.id });\n }\n };\n\n // Drag \u2014 follow the pointer on the handle; drop before / after the row under it (never into itself).\n const dragNode = dragging ? locateNode(root, dragging)?.node ?? null : null;\n const onHandleDown = (event: PointerEvent<HTMLDivElement>, id: string) => {\n if (event.button !== 0 || readOnly) return;\n event.preventDefault();\n event.currentTarget.setPointerCapture(event.pointerId);\n setDragging(id);\n setDrop(null);\n };\n const onHandleMove = (event: PointerEvent<HTMLDivElement>) => {\n if (!dragging || !dragNode) return;\n let next: { id: string; place: \"before\" | \"after\" } | null = null;\n for (const row of rows) {\n const el = rowEls.current.get(row.node.id);\n if (!el) continue;\n const r = el.getBoundingClientRect();\n if (event.clientY < r.top || event.clientY >= r.bottom) continue;\n if (!containsNode(dragNode, row.node.id)) next = { id: row.node.id, place: event.clientY < r.top + r.height / 2 ? \"before\" : \"after\" };\n break;\n }\n if (next?.id !== drop?.id || next?.place !== drop?.place) setDrop(next);\n };\n const onHandleUp = (event: PointerEvent<HTMLDivElement>) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);\n if (dragging && drop) apply(moveNode(root, dragging, drop.id, drop.place));\n setDragging(null);\n setDrop(null);\n };\n\n return (\n <Box sx={{ position: \"relative\", height: \"100%\", overflowY: \"auto\", bgcolor: \"#fff\", pt: `${paddingTop}px`, pb: `${paddingBottom}px`, px: 1.5 }}>\n {rows.map((row, index) => {\n const hasKids = (row.node.children?.length ?? 0) > 0;\n const folded = row.node.expanded === false;\n const line = drop?.id === row.node.id ? drop.place : null;\n const text = outlineTextStyle(row.node.style, row.depth);\n return (\n <Box\n key={row.node.id}\n ref={(el: HTMLDivElement | null) => {\n if (el) rowEls.current.set(row.node.id, el);\n else rowEls.current.delete(row.node.id);\n }}\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n minHeight: 26,\n pl: `${row.depth * INDENT}px`,\n borderRadius: 0.75,\n bgcolor: selectedId === row.node.id ? \"rgba(26,115,232,0.06)\" : \"transparent\",\n // Drop line \u2014 inset shadow so it takes no height.\n boxShadow: (theme) =>\n line === \"before\" ? `inset 0 2px 0 ${theme.palette.primary.main}` : line === \"after\" ? `inset 0 -2px 0 ${theme.palette.primary.main}` : \"none\",\n opacity: dragging === row.node.id ? 0.4 : 1,\n \"&:hover .ol-handle\": { visibility: \"visible\" },\n }}\n >\n <Box\n className=\"ol-handle\"\n title={strings.drag}\n onPointerDown={row.depth > 0 ? (event: PointerEvent<HTMLDivElement>) => onHandleDown(event, row.node.id) : undefined}\n onPointerMove={onHandleMove}\n onPointerUp={onHandleUp}\n onPointerCancel={onHandleUp}\n sx={{\n display: \"flex\",\n color: \"#b0b4ba\",\n cursor: row.depth > 0 && !readOnly ? (dragging ? \"grabbing\" : \"grab\") : \"default\",\n visibility: dragging === row.node.id ? \"visible\" : \"hidden\",\n width: 18,\n flexShrink: 0,\n touchAction: \"none\",\n }}\n >\n {row.depth > 0 && !readOnly ? <DragIndicatorIcon sx={{ fontSize: 16 }} /> : null}\n </Box>\n {hasKids ? (\n <IconButton\n size=\"small\"\n aria-label={folded ? strings.unfold : strings.fold}\n title={folded ? strings.unfold : strings.fold}\n onClick={() => apply(toggleFold(root, row.node.id))}\n sx={{ width: 22, height: 22, p: 0, color: \"text.secondary\" }}\n >\n {folded ? <ArrowRightIcon sx={{ fontSize: 20 }} /> : <ArrowDropDownIcon sx={{ fontSize: 20 }} />}\n </IconButton>\n ) : (\n <Box sx={{ width: 22, display: \"flex\", justifyContent: \"center\", flexShrink: 0 }}>\n <Box sx={{ width: 5, height: 5, borderRadius: \"50%\", bgcolor: \"#9aa0a6\" }} />\n </Box>\n )}\n <Box sx={{ flex: 1, minWidth: 0, display: \"flex\", ml: 0.5 }}>\n <input\n ref={(el) => {\n if (el) inputs.current.set(row.node.id, el);\n else inputs.current.delete(row.node.id);\n }}\n value={row.node.topic}\n readOnly={readOnly}\n placeholder={row.depth === 0 ? strings.root : \"\"}\n spellCheck={false}\n autoComplete=\"off\"\n onFocus={() => onSelect?.(row.node.id)}\n onChange={(event) => apply(setTopic(root, row.node.id, event.target.value), `topic:${row.node.id}`)}\n onKeyDown={(event) => onKey(event, index)}\n style={{\n // Background hugs the text like a highlighter; the input keeps full width for clicks.\n width: text.background ? Math.ceil(measureText(row.node.topic || \" \", Number(text.fontSize), text.fontWeight as string)) + 16 : \"100%\",\n maxWidth: \"100%\",\n border: 0,\n outline: 0,\n font: \"inherit\",\n padding: text.background ? \"2px 6px\" : \"2px 0\",\n borderRadius: 4,\n background: text.background ?? \"transparent\",\n color: text.color ?? \"inherit\",\n fontSize: text.fontSize,\n fontWeight: text.fontWeight,\n textDecoration: [text.textDecoration, folded && hasKids ? \"underline dotted #9aa0a6\" : \"\"].filter(Boolean).join(\" \") || undefined,\n }}\n />\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n}\n", "/** Text width with a cached canvas (used for highlight width in the outline and node sizes in the map). */\nlet ctx: CanvasRenderingContext2D | null = null;\nconst cache = new Map<string, number>();\n\nexport const DEFAULT_FONT_FAMILY = \"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif\";\n\nexport function measureText(text: string, fontSize: number, fontWeight: string | number = 400, fontFamily = DEFAULT_FONT_FAMILY): number {\n const font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n const key = `${font}|${text}`;\n const hit = cache.get(key);\n if (hit !== undefined) return hit;\n if (!ctx && typeof document !== \"undefined\") ctx = document.createElement(\"canvas\").getContext(\"2d\");\n // No canvas (SSR / tests) \u2014 rough guess: wide glyphs 1em, others 0.55em.\n let width = 0;\n if (ctx) {\n ctx.font = font;\n width = ctx.measureText(text).width;\n } else for (const ch of text) width += (/[\u1100-\uFFDC]/.test(ch) ? 1 : 0.55) * fontSize;\n if (cache.size > 5000) cache.clear();\n cache.set(key, width);\n return width;\n}\n", "/**\n * Undo / redo for a controlled tree.\n * Typing into one node within `mergeMs` is merged into one step, so undo doesn't go back one letter at a time.\n */\nimport { useCallback, useRef, useState } from \"react\";\nimport type { TreeNode } from \"./tree\";\n\nexport interface TreeHistory {\n canUndo: boolean;\n canRedo: boolean;\n /** Records the tree before a change. `mergeKey` merges consecutive changes with the same key (e.g. typing in one node). */\n record: (before: TreeNode, mergeKey?: string) => void;\n undo: (current: TreeNode) => TreeNode | null;\n redo: (current: TreeNode) => TreeNode | null;\n clear: () => void;\n}\n\nexport function useTreeHistory(limit = 100, mergeMs = 1200): TreeHistory {\n const past = useRef<TreeNode[]>([]);\n const future = useRef<TreeNode[]>([]);\n const last = useRef<{ key: string; at: number } | null>(null);\n const [, bump] = useState(0);\n const refresh = () => bump((n) => n + 1);\n\n const record = useCallback(\n (before: TreeNode, mergeKey?: string) => {\n const now = Date.now();\n const merge = mergeKey && last.current?.key === mergeKey && now - last.current.at < mergeMs;\n last.current = mergeKey ? { key: mergeKey, at: now } : null;\n future.current = [];\n if (!merge) {\n past.current.push(before);\n if (past.current.length > limit) past.current.shift();\n }\n refresh();\n },\n [limit, mergeMs]\n );\n const undo = useCallback((current: TreeNode) => {\n const prev = past.current.pop();\n if (!prev) return null;\n future.current.push(current);\n last.current = null;\n refresh();\n return prev;\n }, []);\n const redo = useCallback((current: TreeNode) => {\n const next = future.current.pop();\n if (!next) return null;\n past.current.push(current);\n last.current = null;\n refresh();\n return next;\n }, []);\n const clear = useCallback(() => {\n past.current = [];\n future.current = [];\n last.current = null;\n refresh();\n }, []);\n return { canUndo: past.current.length > 0, canRedo: future.current.length > 0, record, undo, redo, clear };\n}\n", "/** UI strings (ko \u00B7 en \u00B7 ja \u00B7 zh \u00B7 de \u00B7 fr \u00B7 it \u00B7 vi). Unknown locale \u2192 English. */\nconst STRINGS = {\n ko: {\n clearFormat: \"\uC11C\uC2DD \uC9C0\uC6B0\uAE30\",\n fontSize: \"\uAE00\uC790 \uD06C\uAE30\",\n sizeDefault: \"\uAE30\uBCF8\",\n bold: \"\uC9C4\uD558\uAC8C\",\n underline: \"\uBC11\uC904\",\n strike: \"\uCDE8\uC18C\uC120\",\n textColor: \"\uAE00\uC790\uC0C9\",\n background: \"\uBC30\uACBD\uC0C9\",\n noColor: \"\uC5C6\uC74C\",\n undo: \"\uB418\uB3CC\uB9AC\uAE30\",\n redo: \"\uB2E4\uC2DC \uD558\uAE30\",\n fold: \"\uC811\uAE30\",\n unfold: \"\uD3BC\uCE58\uAE30\",\n drag: \"\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30\",\n root: \"\uC8FC\uC81C\",\n hint: \"Enter: \uC0C8 \uC904 \u00B7 Tab / Shift+Tab: \uB4E4\uC5EC\uC4F0\uAE30 / \uB0B4\uC5B4\uC4F0\uAE30 \u00B7 Alt+\u2191\u2193: \uC21C\uC11C \u00B7 \uC904 \uC55E \uC190\uC7A1\uC774 \uB04C\uAE30: \uC62E\uAE30\uAE30 \u00B7 \uBE48 \uC904 Backspace: \uC9C0\uC6B0\uAE30\",\n },\n en: {\n clearFormat: \"Clear format\",\n fontSize: \"Font size\",\n sizeDefault: \"Default\",\n bold: \"Bold\",\n underline: \"Underline\",\n strike: \"Strikethrough\",\n textColor: \"Text color\",\n background: \"Background\",\n noColor: \"None\",\n undo: \"Undo\",\n redo: \"Redo\",\n fold: \"Collapse\",\n unfold: \"Expand\",\n drag: \"Drag to move\",\n root: \"Topic\",\n hint: \"Enter: new line \u00B7 Tab / Shift+Tab: indent / outdent \u00B7 Alt+\u2191\u2193: reorder \u00B7 Drag the row handle: move \u00B7 Backspace on empty: delete\",\n },\n ja: {\n clearFormat: \"\u66F8\u5F0F\u3092\u30AF\u30EA\u30A2\",\n fontSize: \"\u6587\u5B57\u30B5\u30A4\u30BA\",\n sizeDefault: \"\u6A19\u6E96\",\n bold: \"\u592A\u5B57\",\n underline: \"\u4E0B\u7DDA\",\n strike: \"\u53D6\u308A\u6D88\u3057\u7DDA\",\n textColor: \"\u6587\u5B57\u8272\",\n background: \"\u80CC\u666F\u8272\",\n noColor: \"\u306A\u3057\",\n undo: \"\u5143\u306B\u623B\u3059\",\n redo: \"\u3084\u308A\u76F4\u3059\",\n fold: \"\u6298\u308A\u305F\u305F\u3080\",\n unfold: \"\u5C55\u958B\",\n drag: \"\u30C9\u30E9\u30C3\u30B0\u3067\u79FB\u52D5\",\n root: \"\u30C6\u30FC\u30DE\",\n hint: \"Enter: \u65B0\u3057\u3044\u884C \u00B7 Tab / Shift+Tab: \u5B57\u4E0B\u3052 / \u5B57\u4E0A\u3052 \u00B7 Alt+\u2191\u2193: \u4E26\u3079\u66FF\u3048 \u00B7 \u884C\u982D\u306E\u30CF\u30F3\u30C9\u30EB\u3092\u30C9\u30E9\u30C3\u30B0: \u79FB\u52D5 \u00B7 \u7A7A\u884C\u3067 Backspace: \u524A\u9664\",\n },\n zh: {\n clearFormat: \"\u6E05\u9664\u683C\u5F0F\",\n fontSize: \"\u5B57\u53F7\",\n sizeDefault: \"\u9ED8\u8BA4\",\n bold: \"\u52A0\u7C97\",\n underline: \"\u4E0B\u5212\u7EBF\",\n strike: \"\u5220\u9664\u7EBF\",\n textColor: \"\u6587\u5B57\u989C\u8272\",\n background: \"\u80CC\u666F\u8272\",\n noColor: \"\u65E0\",\n undo: \"\u64A4\u9500\",\n redo: \"\u91CD\u505A\",\n fold: \"\u6298\u53E0\",\n unfold: \"\u5C55\u5F00\",\n drag: \"\u62D6\u52A8\u4EE5\u79FB\u52A8\",\n root: \"\u4E3B\u9898\",\n hint: \"Enter: \u65B0\u884C \u00B7 Tab / Shift+Tab: \u7F29\u8FDB / \u53D6\u6D88\u7F29\u8FDB \u00B7 Alt+\u2191\u2193: \u987A\u5E8F \u00B7 \u62D6\u52A8\u884C\u9996\u624B\u67C4: \u79FB\u52A8 \u00B7 \u7A7A\u884C Backspace: \u5220\u9664\",\n },\n de: {\n clearFormat: \"Formatierung l\u00F6schen\",\n fontSize: \"Schriftgr\u00F6\u00DFe\",\n sizeDefault: \"Standard\",\n bold: \"Fett\",\n underline: \"Unterstrichen\",\n strike: \"Durchgestrichen\",\n textColor: \"Textfarbe\",\n background: \"Hintergrund\",\n noColor: \"Keine\",\n undo: \"R\u00FCckg\u00E4ngig\",\n redo: \"Wiederholen\",\n fold: \"Einklappen\",\n unfold: \"Ausklappen\",\n drag: \"Zum Verschieben ziehen\",\n root: \"Thema\",\n hint: \"Enter: neue Zeile \u00B7 Tab / Shift+Tab: ein- / ausr\u00FCcken \u00B7 Alt+\u2191\u2193: Reihenfolge \u00B7 Griff ziehen: verschieben \u00B7 Backspace leer: l\u00F6schen\",\n },\n fr: {\n clearFormat: \"Effacer la mise en forme\",\n fontSize: \"Taille\",\n sizeDefault: \"Par d\u00E9faut\",\n bold: \"Gras\",\n underline: \"Soulign\u00E9\",\n strike: \"Barr\u00E9\",\n textColor: \"Couleur du texte\",\n background: \"Arri\u00E8re-plan\",\n noColor: \"Aucune\",\n undo: \"Annuler\",\n redo: \"R\u00E9tablir\",\n fold: \"Replier\",\n unfold: \"D\u00E9plier\",\n drag: \"Glisser pour d\u00E9placer\",\n root: \"Sujet\",\n hint: \"Entr\u00E9e : nouvelle ligne \u00B7 Tab / Maj+Tab : indenter / d\u00E9sindenter \u00B7 Alt+\u2191\u2193 : ordre \u00B7 Glisser la poign\u00E9e : d\u00E9placer \u00B7 Retour arri\u00E8re vide : supprimer\",\n },\n it: {\n clearFormat: \"Cancella formattazione\",\n fontSize: \"Dimensione\",\n sizeDefault: \"Predefinita\",\n bold: \"Grassetto\",\n underline: \"Sottolineato\",\n strike: \"Barrato\",\n textColor: \"Colore testo\",\n background: \"Sfondo\",\n noColor: \"Nessuno\",\n undo: \"Annulla\",\n redo: \"Ripeti\",\n fold: \"Comprimi\",\n unfold: \"Espandi\",\n drag: \"Trascina per spostare\",\n root: \"Argomento\",\n hint: \"Invio: nuova riga \u00B7 Tab / Maiusc+Tab: rientro / riduci \u00B7 Alt+\u2191\u2193: ordine \u00B7 Trascina la maniglia: sposta \u00B7 Backspace su vuota: elimina\",\n },\n vi: {\n clearFormat: \"X\u00F3a \u0111\u1ECBnh d\u1EA1ng\",\n fontSize: \"C\u1EE1 ch\u1EEF\",\n sizeDefault: \"M\u1EB7c \u0111\u1ECBnh\",\n bold: \"\u0110\u1EADm\",\n underline: \"G\u1EA1ch ch\u00E2n\",\n strike: \"G\u1EA1ch ngang\",\n textColor: \"M\u00E0u ch\u1EEF\",\n background: \"M\u00E0u n\u1EC1n\",\n noColor: \"Kh\u00F4ng\",\n undo: \"Ho\u00E0n t\u00E1c\",\n redo: \"L\u00E0m l\u1EA1i\",\n fold: \"Thu g\u1ECDn\",\n unfold: \"M\u1EDF r\u1ED9ng\",\n drag: \"K\u00E9o \u0111\u1EC3 di chuy\u1EC3n\",\n root: \"Ch\u1EE7 \u0111\u1EC1\",\n hint: \"Enter: d\u00F2ng m\u1EDBi \u00B7 Tab / Shift+Tab: th\u1EE5t v\u00E0o / ra \u00B7 Alt+\u2191\u2193: th\u1EE9 t\u1EF1 \u00B7 K\u00E9o tay c\u1EA7m: di chuy\u1EC3n \u00B7 Backspace d\u00F2ng tr\u1ED1ng: x\u00F3a\",\n },\n} as const;\n\nexport type OutlinerStrings = { [K in keyof (typeof STRINGS)[\"en\"]]: string };\n\n/** Strings for a locale (\"ko\", \"ko-KR\", \u2026). `overrides` replaces single entries. */\nexport function outlinerStrings(locale?: string, overrides?: Partial<OutlinerStrings>): OutlinerStrings {\n const lang = (locale ?? \"en\").slice(0, 2).toLowerCase() as keyof typeof STRINGS;\n return { ...(STRINGS[lang] ?? STRINGS.en), ...overrides };\n}\n"],
|
|
5
|
+
"mappings": "AAIA,OAAS,YAAAA,OAAgC,QACzC,OAAS,OAAAC,OAAW,gBCEpB,OAAS,YAAAC,OAAiD,QAC1D,OAAS,OAAAC,EAAK,cAAAC,GAAY,QAAAC,GAAM,YAAAC,GAAU,WAAAC,OAAe,gBACzD,OAAS,eAAAC,OAAmB,2BAC5B,OAAS,YAAAC,GAAU,iBAAAC,GAAe,gBAAAC,GAAc,YAAAC,GAAU,oBAAAC,GAAkB,cAAAC,GAAY,iBAAAC,GAAe,YAAAC,OAAgB,wBC+BhH,IAAMC,EAAY,IAAM,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,EAAE,EAExDC,GAAcC,IAA6B,CAAE,GAAI,OAAQ,MAAAA,EAAO,SAAU,CAAC,CAAE,GAEpFC,GAASC,GAA6B,gBAAgBA,CAAI,EAGzD,SAASC,EAAWD,EAAgBE,EAA+E,CACtH,GAAIF,EAAK,KAAOE,EAAI,MAAO,CAAE,KAAMF,EAAM,OAAQ,KAAM,MAAO,CAAE,EAChE,IAAMG,EAAoB,CAACH,CAAI,EAC/B,KAAOG,EAAM,QAAQ,CACjB,IAAMC,EAASD,EAAM,IAAI,EACnBE,EAAOD,EAAO,UAAY,CAAC,EACjC,QAASE,EAAI,EAAGA,EAAID,EAAK,OAAQC,IAAK,CAClC,GAAID,EAAKC,CAAC,EAAE,KAAOJ,EAAI,MAAO,CAAE,KAAMG,EAAKC,CAAC,EAAG,OAAAF,EAAQ,MAAOE,CAAE,EAChEH,EAAM,KAAKE,EAAKC,CAAC,CAAC,CACtB,CACJ,CACA,OAAO,IACX,CAGO,IAAMC,EAAe,CAACC,EAAgBN,IAAwBM,EAAK,KAAON,IAAOM,EAAK,UAAY,CAAC,GAAG,KAAMC,GAAUF,EAAaE,EAAOP,CAAE,CAAC,EAEvIQ,GAAYF,GAAmBA,EAAK,WAAa,KAAUA,EAAK,UAAU,QAAU,GAAK,EAG/F,SAASG,GAAYX,EAA2B,CACnD,IAAMY,EAAkB,CAAC,EACnBC,EAAO,CAACL,EAAgBM,EAAeC,IAA4B,CAErE,GADAH,EAAK,KAAK,CAAE,KAAAJ,EAAM,MAAAM,EAAO,SAAAC,CAAS,CAAC,EAC/BP,EAAK,WAAa,GAAO,QAAWC,KAASD,EAAK,UAAY,CAAC,EAAGK,EAAKJ,EAAOK,EAAQ,EAAGN,EAAK,EAAE,CACxG,EACA,OAAAK,EAAKb,EAAM,EAAG,IAAI,EACXY,CACX,CAGA,SAASI,EAAKhB,EAAgBiB,EAAiE,CAC3F,IAAMC,EAAOnB,GAAMC,CAAI,EACjBmB,EAAUF,EAAIC,CAAI,EACxB,OAAIC,IAAY,GAAc,KACvB,CAAE,KAAMD,EAAM,QAASC,GAAW,MAAU,CACvD,CAGO,IAAMC,GAAW,CAACpB,EAAgBE,EAAYJ,EAAQ,GAAIuB,EAAQ,KACrEL,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,EAAI,MAAO,GAChB,IAAMC,EAAkB,CAAE,GAAI3B,EAAU,EAAG,MAAAE,CAAM,EACjD,OAAAwB,EAAG,KAAK,SAAWD,EAAQ,CAACE,EAAO,GAAID,EAAG,KAAK,UAAY,CAAC,CAAE,EAAI,CAAC,GAAIA,EAAG,KAAK,UAAY,CAAC,EAAIC,CAAK,EACrGD,EAAG,KAAK,SAAW,GACZC,EAAM,EACjB,CAAC,EAGQC,GAAa,CAACxB,EAAgBE,EAAYJ,EAAQ,KAC3DkB,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,EAAI,MAAO,GAChB,IAAMC,EAAkB,CAAE,GAAI3B,EAAU,EAAG,MAAAE,CAAM,EACjD,OAAKwB,EAAG,OAGDA,EAAG,OAAO,SAAU,OAAOA,EAAG,MAAQ,EAAG,EAAGC,CAAK,GAFpDD,EAAG,KAAK,SAAW,CAACC,EAAO,GAAID,EAAG,KAAK,UAAY,CAAC,CAAE,EACtDA,EAAG,KAAK,SAAW,IAEhBC,EAAM,EACjB,CAAC,EAGQE,GAAW,CAACzB,EAAgBE,IAAe,CACpD,IAAMoB,EAAKrB,EAAWD,EAAME,CAAE,EAC9B,OAAIoB,IAAO,CAACA,EAAG,SAAYA,EAAG,KAAK,UAAU,QAAU,GAAK,GAAKA,EAAG,KAAK,WAAa,IAAgBF,GAASpB,EAAME,EAAI,GAAI,EAAI,EAC1HsB,GAAWxB,EAAME,CAAE,CAC9B,EAGawB,GAAa,CAAC1B,EAAgBE,IACvCc,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,OAAKoB,GAAI,QACTA,EAAG,OAAO,SAAU,OAAOA,EAAG,MAAO,CAAC,EAC/BA,EAAG,OAAO,SAAUA,EAAG,MAAQ,CAAC,GAAG,IAAMA,EAAG,OAAO,IAFlC,EAG5B,CAAC,EAGQK,GAAa,CAAC3B,EAAgBE,IACvCc,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,GAAI,QAAUA,EAAG,QAAU,EAAG,MAAO,GAC1C,IAAMM,EAAON,EAAG,OAAO,SAAUA,EAAG,MAAQ,CAAC,EAC7C,OAAAA,EAAG,OAAO,SAAU,OAAOA,EAAG,MAAO,CAAC,EACtCM,EAAK,SAAW,CAAC,GAAIA,EAAK,UAAY,CAAC,EAAIN,EAAG,IAAI,EAClDM,EAAK,SAAW,GACT1B,CACX,CAAC,EAGQ2B,GAAc,CAAC7B,EAAgBE,IACxCc,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,GAAI,OAAQ,MAAO,GACxB,IAAMQ,EAAK7B,EAAWiB,EAAMI,EAAG,OAAO,EAAE,EACxC,OAAKQ,GAAI,QACTR,EAAG,OAAO,SAAU,OAAOA,EAAG,MAAO,CAAC,EACtCQ,EAAG,OAAO,SAAU,OAAOA,EAAG,MAAQ,EAAG,EAAGR,EAAG,IAAI,EAC5CpB,GAHiB,EAI5B,CAAC,EAGQ6B,GAAY,CAAC/B,EAAgBE,EAAY8B,IAClDhB,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,GAAI,OAAQ,MAAO,GACxB,IAAMjB,EAAOiB,EAAG,OAAO,SACjBW,EAAKX,EAAG,MAAQU,EACtB,OAAIC,EAAK,GAAKA,GAAM5B,EAAK,OAAe,IACxC,CAACA,EAAKiB,EAAG,KAAK,EAAGjB,EAAK4B,CAAE,CAAC,EAAI,CAAC5B,EAAK4B,CAAE,EAAG5B,EAAKiB,EAAG,KAAK,CAAC,EAC/CpB,EACX,CAAC,EAGQgC,GAAW,CAAClC,EAAgBmC,EAAcF,EAAYG,IAC/DpB,EAAKhB,EAAOkB,GAAS,CACjB,IAAMmB,EAAMpC,EAAWiB,EAAMiB,CAAI,EACjC,GAAI,CAACE,GAAK,QAAU9B,EAAa8B,EAAI,KAAMJ,CAAE,EAAG,MAAO,GACvDI,EAAI,OAAO,SAAU,OAAOA,EAAI,MAAO,CAAC,EACxC,IAAMC,EAAMrC,EAAWiB,EAAMe,CAAE,EAC/B,OAAKK,GACDF,IAAU,SAAW,CAACE,EAAI,QAC1BA,EAAI,KAAK,SAAW,CAAC,GAAIA,EAAI,KAAK,UAAY,CAAC,EAAID,EAAI,IAAI,EAC3DC,EAAI,KAAK,SAAW,IACjBA,EAAI,OAAO,SAAU,OAAOA,EAAI,OAASF,IAAU,QAAU,EAAI,GAAI,EAAGC,EAAI,IAAI,EAChFF,GALU,EAMrB,CAAC,EAEQI,GAAW,CAACvC,EAAgBE,EAAYJ,IACjDkB,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,GAAMA,EAAG,KAAK,QAAUxB,EAAO,MAAO,GAC3CwB,EAAG,KAAK,MAAQxB,CACpB,CAAC,EAEQ0C,GAAa,CAACxC,EAAgBE,EAAYuC,IACnDzB,EAAKhB,EAAOkB,GAAS,CACjB,IAAMI,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,GAAM,EAAEA,EAAG,KAAK,UAAU,QAAU,GAAI,MAAO,GACpDA,EAAG,KAAK,SAAWmB,GAAYnB,EAAG,KAAK,WAAa,EACxD,CAAC,EAGQoB,GAAe,CAAC1C,EAAgB2C,EAAeC,IACxD5B,EAAKhB,EAAOkB,GAAS,CACjB,IAAI2B,EAAU,GACd,QAAW3C,KAAMyC,EAAK,CAClB,IAAMrB,EAAKrB,EAAWiB,EAAMhB,CAAE,EAC9B,GAAI,CAACoB,EAAI,SACT,IAAMwB,EAAsB,CAAE,GAAIxB,EAAG,KAAK,OAAS,CAAC,CAAG,EACvD,OAAW,CAACyB,EAAKC,CAAK,IAAK,OAAO,QAAQJ,CAAK,EACvCI,IAAU,QAAaA,IAAU,GAAI,OAAOF,EAAKC,CAAG,EACnDD,EAAKC,CAAG,EAAIC,EAErB1B,EAAG,KAAK,MAAQ,OAAO,KAAKwB,CAAI,EAAE,OAASA,EAAO,OAC7CxB,EAAG,KAAK,OAAO,OAAOA,EAAG,KAAK,MACnCuB,EAAU,EACd,CACA,OAAOA,EAAU,OAAY,EACjC,CAAC,EAGE,SAASI,EAAcC,EAAkCC,EAAoC,CAChG,OAAQD,GAAO,gBAAkB,IAAI,MAAM,KAAK,EAAE,SAASC,CAAI,CACnE,CACO,SAASC,EAAiBF,EAAkCC,EAAwD,CACvH,IAAME,EAAM,IAAI,KAAKH,GAAO,gBAAkB,IAAI,MAAM,KAAK,EAAE,OAAO,OAAO,CAAC,EAC9E,OAAIG,EAAI,IAAIF,CAAI,EAAGE,EAAI,OAAOF,CAAI,EAC7BE,EAAI,IAAIF,CAAI,EACVE,EAAI,KAAO,CAAC,GAAGA,CAAG,EAAE,KAAK,GAAG,EAAI,MAC3C,CDhLY,OAkDJ,YAAAC,GAlDI,OAAAC,EAkDJ,QAAAC,MAlDI,oBA7BL,IAAMC,GAAa,CAAC,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,EAAE,EAkBlD,SAASC,EAAQ,CAAE,MAAAC,EAAO,SAAAC,CAAS,EAA2C,CACjF,OACIL,EAACM,GAAA,CACG,MAAOF,EACP,UAAU,SACV,MAAK,GACL,mBAAkB,GAClB,WAAY,EACZ,UAAW,CAAE,QAAS,CAAE,GAAI,CAAE,QAAS,UAAW,MAAO,UAAW,SAAU,OAAQ,WAAY,SAAU,sBAAuB,CAAE,MAAO,SAAU,CAAE,CAAE,CAAE,EAG5J,SAAAJ,EAAC,QAAK,MAAO,CAAE,QAAS,aAAc,EAAI,SAAAK,EAAS,EACvD,CAER,CAGO,SAASE,EAAW,CAAE,MAAAC,EAAO,OAAAC,EAAS,GAAO,SAAAC,EAAW,GAAO,QAAAC,EAAS,SAAAN,CAAS,EAAoI,CACxN,OACIL,EAACG,EAAA,CAAQ,MAAOK,EACZ,SAAAR,EAACY,EAAA,CACG,UAAU,SACV,KAAK,SACL,aAAYJ,EACZ,eAAcC,EACd,SAAUC,EACV,YAAcG,GAAsBA,EAAM,eAAe,EACzD,QAAUA,GAAmC,CACzCA,EAAM,eAAe,EAChBH,GAAUC,EAAQE,CAAK,CAChC,EACA,GAAI,CACA,QAAS,cACT,WAAY,SACZ,eAAgB,SAChB,MAAO,GACP,OAAQ,GACR,EAAG,MACH,OAAQ,OACR,aAAc,MACd,QAASJ,EAAS,sBAAwB,cAC1C,MAAOC,EAAW,sBAAwB,UAC1C,OAAQA,EAAW,cAAgB,UACnC,WAAY,sDACZ,yBAA0B,CAAE,QAASD,EAAS,sBAAwB,qBAAsB,EAC5F,UAAW,CAAE,QAAS,MAAO,EAC7B,QAAS,CAAE,MAAO,GAAI,OAAQ,EAAG,CACrC,EAEC,SAAAJ,EACL,EACJ,CAER,CAEO,IAAMS,EAAc,IAAMd,EAACY,EAAA,CAAI,GAAI,CAAE,MAAO,MAAO,UAAW,UAAW,GAAI,MAAO,GAAI,MAAO,QAAS,SAAU,EAAG,EAE5H,SAASG,GAAY,CAAE,MAAAP,EAAO,KAAAQ,EAAM,MAAAC,EAAO,SAAAP,EAAU,OAAAQ,CAAO,EAAwH,CAChL,GAAM,CAACC,EAAQC,CAAS,EAAIC,GAA6B,IAAI,EACvDC,EAAQL,IAAUD,IAAS,QAAU,UAAY,WACvD,OACIf,EAAAF,GAAA,CACI,UAAAC,EAACO,EAAA,CAAW,MAAOC,EAAO,SAAUE,EAAU,QAAUG,GAAUO,EAAUP,EAAM,aAAa,EAC1F,SAAAG,IAAS,QAAUhB,EAACuB,GAAA,CAAc,MAAM,OAAO,OAAO,OAAO,SAAUD,EAAO,EAAKtB,EAACwB,GAAA,CAAa,MAAM,OAAO,UAAWF,EAAO,EACrI,EACCH,EACGnB,EAACyB,GAAA,CACG,MAAOH,EACP,SAAWI,GAAgB,CACvBR,EAAOQ,CAAG,EACVN,EAAU,IAAI,CAClB,EACA,OAAQD,EACR,WAAW,OACX,QAAS,IAAMC,EAAU,IAAI,EAC7B,SAAS,eACT,SAAU,CAAE,SAAU,QAAS,EAC/B,MAAM,SACN,UAAW,GACX,IAAK,EACL,QAAS,EACT,QAAS,CAAE,oBAAqB,GAAM,iBAAkB,GAAM,oBAAqB,GAAM,GAAI,CAAE,OAAQ,KAAM,CAAE,EACnH,EACA,MACR,CAER,CAEO,SAASO,EAAc,CAAE,QAAAC,EAAS,MAAAC,EAAO,QAAAC,EAAS,QAAAC,EAAS,QAAAC,EAAS,OAAAC,EAAQ,OAAAC,EAAQ,MAAAC,EAAO,SAAA9B,EAAU,IAAA+B,EAAK,SAAAC,CAAS,EAAuB,CAC7I,GAAM,CAACC,EAAYC,CAAa,EAAIlB,GAA6B,IAAI,EAC/DX,EAAW,EAAQ2B,GAAa,CAACR,EACjCW,EAAOX,GAAO,SAAW,WAAWA,EAAM,QAAQ,EAAI,KACtDY,EAAOZ,GAAO,aAAe,QAAU,OAAOA,GAAO,UAAU,GAAK,IACpEa,EAAW,GAAQb,GAAS,OAAO,KAAKA,CAAK,EAAE,OAAS,GAC9D,OACI5B,EAACW,EAAA,CAAI,GAAI,CAAE,QAAS,OAAQ,WAAY,SAAU,SAAU,OAAQ,IAAK,MAAO,UAAW,GAAI,GAAI,EAAG,GAAI,MAAO,aAAc,oBAAqB,QAAS,OAAQ,MAAO,OAAQ,WAAY,CAAE,EAC7L,UAAAuB,EACAA,EAAQnC,EAACc,EAAA,EAAY,EAAK,KAC1BmB,EACGhC,EAAAF,GAAA,CACI,UAAAC,EAACO,EAAA,CAAW,MAAOqB,EAAQ,KAAM,SAAUS,GAAY,CAACN,EAAS,QAASE,EACtE,SAAAjC,EAAC2C,GAAA,EAAS,EACd,EACA3C,EAACO,EAAA,CAAW,MAAOqB,EAAQ,KAAM,SAAUS,GAAY,CAACL,EAAS,QAAS,IAAME,IAAS,EACrF,SAAAlC,EAAC4C,GAAA,EAAS,EACd,EACA5C,EAACc,EAAA,EAAY,GACjB,EACA,KACJd,EAACG,EAAA,CAAQ,MAAOyB,EAAQ,SACpB,SAAA3B,EAAC4C,GAAA,CACG,SAAUnC,EACV,YAAcG,GAAUA,EAAM,eAAe,EAC7C,QAAUA,GAAU0B,EAAc1B,EAAM,aAAa,EACrD,GAAI,CAAE,OAAQ,GAAI,SAAU,GAAI,GAAI,EAAG,IAAK,GAAK,eAAgB,gBAAiB,OAAQ,oBAAqB,aAAc,MAAO,SAAU,GAAI,MAAOH,EAAW,mBAAqB,OAAQ,QAAS,OAAQ,UAAW,CAAE,YAAa,MAAO,CAAE,EAEpP,UAAA8B,GAAQZ,EAAQ,YACjB5B,EAACY,EAAA,CAAI,UAAU,OAAO,GAAI,CAAE,SAAU,GAAI,MAAO,MAAO,EAAG,kBAE3D,GACJ,EACJ,EACAX,EAAC6C,GAAA,CAAK,KAAM,EAAQR,EAAa,SAAUA,EAAY,QAAS,IAAMC,EAAc,IAAI,EAAG,mBAAoB,EAAG,oBAAmB,GAAC,UAAW,CAAE,MAAO,CAAE,GAAI,CAAE,UAAW,GAAI,CAAE,CAAE,EACjL,UAAAvC,EAAC+C,GAAA,CACG,MAAK,GACL,SAAUP,IAAS,KACnB,QAAS,IAAM,CACXV,EAAQ,CAAE,SAAU,MAAU,CAAC,EAC/BS,EAAc,IAAI,CACtB,EAEC,SAAAX,EAAQ,YACb,EACC1B,GAAW,IAAKe,GACbjB,EAAC+C,GAAA,CAEG,MAAK,GACL,SAAUP,IAASvB,EACnB,QAAS,IAAM,CACXa,EAAQ,CAAE,SAAU,GAAGb,CAAK,IAAK,CAAC,EAClCsB,EAAc,IAAI,CACtB,EAEC,SAAAtB,GARIA,CAST,CACH,GACL,EACAjB,EAACO,EAAA,CAAW,MAAOqB,EAAQ,KAAM,OAAQa,EAAM,SAAU/B,EAAU,QAAS,IAAMoB,EAAQ,CAAE,WAAYW,EAAO,OAAY,MAAO,CAAC,EAC/H,SAAAzC,EAACgD,GAAA,EAAS,EACd,EACAhD,EAACO,EAAA,CAAW,MAAOqB,EAAQ,UAAW,OAAQqB,EAAcpB,EAAO,WAAW,EAAG,SAAUnB,EAAU,QAAS,IAAMoB,EAAQ,CAAE,eAAgBoB,EAAiBrB,EAAO,WAAW,CAAE,CAAC,EAChL,SAAA7B,EAACmD,GAAA,EAAc,EACnB,EACAnD,EAACO,EAAA,CAAW,MAAOqB,EAAQ,OAAQ,OAAQqB,EAAcpB,EAAO,cAAc,EAAG,SAAUnB,EAAU,QAAS,IAAMoB,EAAQ,CAAE,eAAgBoB,EAAiBrB,EAAO,cAAc,CAAE,CAAC,EACnL,SAAA7B,EAACoD,GAAA,EAAW,EAChB,EACApD,EAACe,GAAA,CAAY,MAAOa,EAAQ,UAAW,KAAK,QAAQ,MAAOC,GAAO,MAAO,SAAUnB,EAAU,OAAS2C,GAAUvB,EAAQ,CAAE,MAAAuB,CAAM,CAAC,EAAG,EACpIrD,EAACe,GAAA,CAAY,MAAOa,EAAQ,WAAY,KAAK,aAAa,MAAOC,GAAO,WAAY,SAAUnB,EAAU,OAAS2C,GAAUvB,EAAQ,CAAE,WAAYuB,EAAM,YAAY,IAAM,UAAY,OAAYA,CAAM,CAAC,EAAG,EAC3MrD,EAACO,EAAA,CACG,MAAOqB,EAAQ,YACf,SAAUlB,GAAY,CAACgC,EACvB,QAAS,IAAMZ,EAAQ,CAAE,SAAU,OAAW,MAAO,OAAW,WAAY,OAAW,WAAY,OAAW,eAAgB,MAAU,CAAC,EAEzI,SAAA9B,EAACsD,GAAA,EAAiB,EACtB,EACCjD,EAAWL,EAACc,EAAA,EAAY,EAAK,KAC7BT,EACDL,EAACY,EAAA,CAAI,GAAI,CAAE,KAAM,CAAE,EAAG,EACrBwB,GACL,CAER,CEpMA,OAAS,aAAAmB,GAAW,UAAAC,GAAQ,YAAAC,MAA2E,QACvG,OAAS,OAAAC,EAAK,cAAAC,OAAkB,gBAChC,OAAOC,OAAoB,iCAC3B,OAAOC,OAAuB,oCAC9B,OAAOC,OAAuB,oCCV9B,IAAIC,EAAuC,KACrCC,EAAQ,IAAI,IAELC,GAAsB,oHAE5B,SAASC,EAAYC,EAAcC,EAAkBC,EAA8B,IAAKC,EAAaL,GAA6B,CACrI,IAAMM,EAAO,GAAGF,CAAU,IAAID,CAAQ,MAAME,CAAU,GAChDE,EAAM,GAAGD,CAAI,IAAIJ,CAAI,GACrBM,EAAMT,EAAM,IAAIQ,CAAG,EACzB,GAAIC,IAAQ,OAAW,OAAOA,EAC1B,CAACV,GAAO,OAAO,SAAa,MAAaA,EAAM,SAAS,cAAc,QAAQ,EAAE,WAAW,IAAI,GAEnG,IAAIW,EAAQ,EACZ,GAAIX,EACAA,EAAI,KAAOQ,EACXG,EAAQX,EAAI,YAAYI,CAAI,EAAE,UAC3B,SAAWQ,KAAMR,EAAMO,IAAU,QAAQ,KAAKC,CAAE,EAAI,EAAI,KAAQP,EACvE,OAAIJ,EAAM,KAAO,KAAMA,EAAM,MAAM,EACnCA,EAAM,IAAIQ,EAAKE,CAAK,EACbA,CACX,CD+IoB,OAqCsC,OAAAE,EArCtC,QAAAC,OAAA,oBArIpB,IAAMC,GAAS,GACTC,GAAY,GAkBX,SAASC,GAAiBC,EAAkCC,EAA8B,CAE7F,MAAO,CACH,SAFSD,GAAO,SAAW,KAAK,IAAI,GAAI,KAAK,IAAI,GAAI,WAAWA,EAAM,QAAQ,CAAC,CAAC,EAAIF,GAGpF,WAAYE,GAAO,aAAeC,IAAU,EAAI,IAAM,KACtD,MAAOD,GAAO,MACd,eAAgBA,GAAO,eACvB,WAAYA,GAAO,UACvB,CACJ,CAEO,SAASE,EAAY,CAAE,KAAAC,EAAM,OAAAC,EAAQ,WAAAC,EAAY,SAAAC,EAAU,QAAAC,EAAS,SAAAC,EAAU,OAAAC,EAAQ,OAAAC,EAAQ,WAAAC,EAAa,EAAG,cAAAC,EAAgB,EAAG,EAAqB,CACzJ,IAAMC,EAASC,GAAO,IAAI,GAA+B,EACnDC,EAASD,GAAO,IAAI,GAA6B,EACjD,CAACE,EAAOC,CAAQ,EAAIC,EAAwB,IAAI,EAChD,CAACC,EAAMC,CAAO,EAAIF,EAA2D,IAAI,EACjF,CAACG,EAAUC,CAAW,EAAIJ,EAAwB,IAAI,EACtDK,EAAOC,GAAYrB,CAAI,EAE7BsB,GAAU,IAAM,CACZ,GAAI,CAACT,EAAO,OACZ,IAAMU,EAAKb,EAAO,QAAQ,IAAIG,CAAK,EAC9BU,IACLA,EAAG,MAAM,EACTA,EAAG,kBAAkBA,EAAG,MAAM,OAAQA,EAAG,MAAM,MAAM,EACrDT,EAAS,IAAI,EACjB,EAAG,CAACD,EAAOb,CAAI,CAAC,EAEhB,IAAMwB,EAAQ,CAACC,EAAyBC,IAAsB,CACrDD,IACLxB,EAAOwB,EAAO,KAAMC,CAAQ,EACxBD,EAAO,UACPX,EAASW,EAAO,OAAO,EACvBtB,IAAWsB,EAAO,OAAO,GAEjC,EAEME,GAAQ,CAACC,EAAwCC,IAAkB,CACrE,GAAID,EAAM,YAAY,YAAa,OACnC,IAAME,EAAMV,EAAKS,CAAK,EAChBE,EAAKD,EAAI,KAAK,GACdE,EAAMJ,EAAM,SAAWA,EAAM,QACnC,GAAII,IAAQJ,EAAM,MAAQ,KAAOA,EAAM,MAAQ,KAAM,CACjDA,EAAM,eAAe,EACjBA,EAAM,SAAUrB,IAAS,EACxBD,IAAS,EACd,MACJ,CACA,GAAI0B,IAAQJ,EAAM,MAAQ,KAAOA,EAAM,MAAQ,KAAM,CACjDA,EAAM,eAAe,EACrBrB,IAAS,EACT,MACJ,CACA,GAAI,CAAAF,GACJ,GAAIuB,EAAM,MAAQ,QACdA,EAAM,eAAe,EACrBJ,EAAMS,GAASjC,EAAM+B,CAAE,CAAC,UACjBH,EAAM,MAAQ,MACrBA,EAAM,eAAe,EACrBJ,EAAMI,EAAM,SAAWM,GAAYlC,EAAM+B,CAAE,EAAII,GAAWnC,EAAM+B,CAAE,CAAC,UAC5DH,EAAM,SAAWA,EAAM,MAAQ,WAAaA,EAAM,MAAQ,aACjEA,EAAM,eAAe,EACrBJ,EAAMY,GAAUpC,EAAM+B,EAAIH,EAAM,MAAQ,UAAY,GAAK,CAAC,CAAC,UACpDA,EAAM,MAAQ,WAAaA,EAAM,MAAQ,YAAa,CAC7D,IAAMS,EAAOjB,EAAKS,GAASD,EAAM,MAAQ,UAAY,GAAK,EAAE,EACxDS,IACAT,EAAM,eAAe,EACrBd,EAASuB,EAAK,KAAK,EAAE,EACrBlC,IAAWkC,EAAK,KAAK,EAAE,EAE/B,SAAWT,EAAM,MAAQ,aAAeA,EAAM,cAAc,QAAU,IAAME,EAAI,MAAQ,GAAK,EAAEA,EAAI,KAAK,UAAU,QAAU,GAAI,CAC5HF,EAAM,eAAe,EACrB,IAAMH,EAASa,GAAWtC,EAAM+B,CAAE,EAC9BN,GAAQD,EAAM,CAAE,KAAMC,EAAO,KAAM,QAASL,EAAKS,EAAQ,CAAC,GAAG,KAAK,EAAG,CAAC,CAC9E,EACJ,EAGMU,EAAWrB,EAAWsB,EAAWxC,EAAMkB,CAAQ,GAAG,MAAQ,KAAO,KACjEuB,GAAe,CAACb,EAAqCG,IAAe,CAClEH,EAAM,SAAW,GAAKvB,IAC1BuB,EAAM,eAAe,EACrBA,EAAM,cAAc,kBAAkBA,EAAM,SAAS,EACrDT,EAAYY,CAAE,EACdd,EAAQ,IAAI,EAChB,EACMyB,GAAgBd,GAAwC,CAC1D,GAAI,CAACV,GAAY,CAACqB,EAAU,OAC5B,IAAIF,EAAyD,KAC7D,QAAWP,KAAOV,EAAM,CACpB,IAAMG,EAAKX,EAAO,QAAQ,IAAIkB,EAAI,KAAK,EAAE,EACzC,GAAI,CAACP,EAAI,SACT,IAAMoB,EAAIpB,EAAG,sBAAsB,EACnC,GAAI,EAAAK,EAAM,QAAUe,EAAE,KAAOf,EAAM,SAAWe,EAAE,QAChD,CAAKC,EAAaL,EAAUT,EAAI,KAAK,EAAE,IAAGO,EAAO,CAAE,GAAIP,EAAI,KAAK,GAAI,MAAOF,EAAM,QAAUe,EAAE,IAAMA,EAAE,OAAS,EAAI,SAAW,OAAQ,GACrI,MACJ,EACIN,GAAM,KAAOrB,GAAM,IAAMqB,GAAM,QAAUrB,GAAM,QAAOC,EAAQoB,CAAI,CAC1E,EACMQ,EAAcjB,GAAwC,CACpDA,EAAM,cAAc,kBAAkBA,EAAM,SAAS,GAAGA,EAAM,cAAc,sBAAsBA,EAAM,SAAS,EACjHV,GAAYF,GAAMQ,EAAMsB,GAAS9C,EAAMkB,EAAUF,EAAK,GAAIA,EAAK,KAAK,CAAC,EACzEG,EAAY,IAAI,EAChBF,EAAQ,IAAI,CAChB,EAEA,OACIzB,EAACuD,EAAA,CAAI,GAAI,CAAE,SAAU,WAAY,OAAQ,OAAQ,UAAW,OAAQ,QAAS,OAAQ,GAAI,GAAGvC,CAAU,KAAM,GAAI,GAAGC,CAAa,KAAM,GAAI,GAAI,EACzI,SAAAW,EAAK,IAAI,CAACU,EAAKD,IAAU,CACtB,IAAMmB,GAAWlB,EAAI,KAAK,UAAU,QAAU,GAAK,EAC7CmB,EAASnB,EAAI,KAAK,WAAa,GAC/BoB,EAAOlC,GAAM,KAAOc,EAAI,KAAK,GAAKd,EAAK,MAAQ,KAC/CmC,EAAOvD,GAAiBkC,EAAI,KAAK,MAAOA,EAAI,KAAK,EACvD,OACIrC,GAACsD,EAAA,CAEG,IAAMxB,GAA8B,CAC5BA,EAAIX,EAAO,QAAQ,IAAIkB,EAAI,KAAK,GAAIP,CAAE,EACrCX,EAAO,QAAQ,OAAOkB,EAAI,KAAK,EAAE,CAC1C,EACA,GAAI,CACA,QAAS,OACT,WAAY,SACZ,UAAW,GACX,GAAI,GAAGA,EAAI,MAAQpC,EAAM,KACzB,aAAc,IACd,QAASQ,IAAe4B,EAAI,KAAK,GAAK,wBAA0B,cAEhE,UAAYsB,GACRF,IAAS,SAAW,iBAAiBE,EAAM,QAAQ,QAAQ,IAAI,GAAKF,IAAS,QAAU,kBAAkBE,EAAM,QAAQ,QAAQ,IAAI,GAAK,OAC5I,QAASlC,IAAaY,EAAI,KAAK,GAAK,GAAM,EAC1C,qBAAsB,CAAE,WAAY,SAAU,CAClD,EAEA,UAAAtC,EAACuD,EAAA,CACG,UAAU,YACV,MAAO3C,EAAQ,KACf,cAAe0B,EAAI,MAAQ,EAAKF,GAAwCa,GAAab,EAAOE,EAAI,KAAK,EAAE,EAAI,OAC3G,cAAeY,GACf,YAAaG,EACb,gBAAiBA,EACjB,GAAI,CACA,QAAS,OACT,MAAO,UACP,OAAQf,EAAI,MAAQ,GAAK,CAACzB,EAAYa,EAAW,WAAa,OAAU,UACxE,WAAYA,IAAaY,EAAI,KAAK,GAAK,UAAY,SACnD,MAAO,GACP,WAAY,EACZ,YAAa,MACjB,EAEC,SAAAA,EAAI,MAAQ,GAAK,CAACzB,EAAWb,EAAC6D,GAAA,CAAkB,GAAI,CAAE,SAAU,EAAG,EAAG,EAAK,KAChF,EACCL,EACGxD,EAAC8D,GAAA,CACG,KAAK,QACL,aAAYL,EAAS7C,EAAQ,OAASA,EAAQ,KAC9C,MAAO6C,EAAS7C,EAAQ,OAASA,EAAQ,KACzC,QAAS,IAAMoB,EAAM+B,GAAWvD,EAAM8B,EAAI,KAAK,EAAE,CAAC,EAClD,GAAI,CAAE,MAAO,GAAI,OAAQ,GAAI,EAAG,EAAG,MAAO,gBAAiB,EAE1D,SAAAmB,EAASzD,EAACgE,GAAA,CAAe,GAAI,CAAE,SAAU,EAAG,EAAG,EAAKhE,EAACiE,GAAA,CAAkB,GAAI,CAAE,SAAU,EAAG,EAAG,EAClG,EAEAjE,EAACuD,EAAA,CAAI,GAAI,CAAE,MAAO,GAAI,QAAS,OAAQ,eAAgB,SAAU,WAAY,CAAE,EAC3E,SAAAvD,EAACuD,EAAA,CAAI,GAAI,CAAE,MAAO,EAAG,OAAQ,EAAG,aAAc,MAAO,QAAS,SAAU,EAAG,EAC/E,EAEJvD,EAACuD,EAAA,CAAI,GAAI,CAAE,KAAM,EAAG,SAAU,EAAG,QAAS,OAAQ,GAAI,EAAI,EACtD,SAAAvD,EAAC,SACG,IAAM+B,GAAO,CACLA,EAAIb,EAAO,QAAQ,IAAIoB,EAAI,KAAK,GAAIP,CAAE,EACrCb,EAAO,QAAQ,OAAOoB,EAAI,KAAK,EAAE,CAC1C,EACA,MAAOA,EAAI,KAAK,MAChB,SAAUzB,EACV,YAAayB,EAAI,QAAU,EAAI1B,EAAQ,KAAO,GAC9C,WAAY,GACZ,aAAa,MACb,QAAS,IAAMD,IAAW2B,EAAI,KAAK,EAAE,EACrC,SAAWF,GAAUJ,EAAMkC,GAAS1D,EAAM8B,EAAI,KAAK,GAAIF,EAAM,OAAO,KAAK,EAAG,SAASE,EAAI,KAAK,EAAE,EAAE,EAClG,UAAYF,GAAUD,GAAMC,EAAOC,CAAK,EACxC,MAAO,CAEH,MAAOsB,EAAK,WAAa,KAAK,KAAKQ,EAAY7B,EAAI,KAAK,OAAS,IAAK,OAAOqB,EAAK,QAAQ,EAAGA,EAAK,UAAoB,CAAC,EAAI,GAAK,OAChI,SAAU,OACV,OAAQ,EACR,QAAS,EACT,KAAM,UACN,QAASA,EAAK,WAAa,UAAY,QACvC,aAAc,EACd,WAAYA,EAAK,YAAc,cAC/B,MAAOA,EAAK,OAAS,UACrB,SAAUA,EAAK,SACf,WAAYA,EAAK,WACjB,eAAgB,CAACA,EAAK,eAAgBF,GAAUD,EAAU,2BAA6B,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAK,MAC5H,EACJ,EACJ,IAnFKlB,EAAI,KAAK,EAoFlB,CAER,CAAC,EACL,CAER,CE1PA,OAAS,eAAA8B,EAAa,UAAAC,EAAQ,YAAAC,OAAgB,QAavC,SAASC,EAAeC,EAAQ,IAAKC,EAAU,KAAmB,CACrE,IAAMC,EAAOL,EAAmB,CAAC,CAAC,EAC5BM,EAASN,EAAmB,CAAC,CAAC,EAC9BO,EAAOP,EAA2C,IAAI,EACtD,CAAC,CAAEQ,CAAI,EAAIP,GAAS,CAAC,EACrBQ,EAAU,IAAMD,EAAME,GAAMA,EAAI,CAAC,EAEjCC,EAASZ,EACX,CAACa,EAAkBC,IAAsB,CACrC,IAAMC,EAAM,KAAK,IAAI,EACfC,EAAQF,GAAYN,EAAK,SAAS,MAAQM,GAAYC,EAAMP,EAAK,QAAQ,GAAKH,EACpFG,EAAK,QAAUM,EAAW,CAAE,IAAKA,EAAU,GAAIC,CAAI,EAAI,KACvDR,EAAO,QAAU,CAAC,EACbS,IACDV,EAAK,QAAQ,KAAKO,CAAM,EACpBP,EAAK,QAAQ,OAASF,GAAOE,EAAK,QAAQ,MAAM,GAExDI,EAAQ,CACZ,EACA,CAACN,EAAOC,CAAO,CACnB,EACMY,EAAOjB,EAAakB,GAAsB,CAC5C,IAAMC,EAAOb,EAAK,QAAQ,IAAI,EAC9B,OAAKa,GACLZ,EAAO,QAAQ,KAAKW,CAAO,EAC3BV,EAAK,QAAU,KACfE,EAAQ,EACDS,GAJW,IAKtB,EAAG,CAAC,CAAC,EACCC,EAAOpB,EAAakB,GAAsB,CAC5C,IAAMG,EAAOd,EAAO,QAAQ,IAAI,EAChC,OAAKc,GACLf,EAAK,QAAQ,KAAKY,CAAO,EACzBV,EAAK,QAAU,KACfE,EAAQ,EACDW,GAJW,IAKtB,EAAG,CAAC,CAAC,EACCC,EAAQtB,EAAY,IAAM,CAC5BM,EAAK,QAAU,CAAC,EAChBC,EAAO,QAAU,CAAC,EAClBC,EAAK,QAAU,KACfE,EAAQ,CACZ,EAAG,CAAC,CAAC,EACL,MAAO,CAAE,QAASJ,EAAK,QAAQ,OAAS,EAAG,QAASC,EAAO,QAAQ,OAAS,EAAG,OAAAK,EAAQ,KAAAK,EAAM,KAAAG,EAAM,MAAAE,CAAM,CAC7G,CC5DA,IAAMC,GAAU,CACZ,GAAI,CACA,YAAa,kCACb,SAAU,4BACV,YAAa,eACb,KAAM,qBACN,UAAW,eACX,OAAQ,qBACR,UAAW,qBACX,WAAY,qBACZ,QAAS,eACT,KAAM,2BACN,KAAM,4BACN,KAAM,eACN,OAAQ,qBACR,KAAM,wCACN,KAAM,eACN,KAAM,4PACV,EACA,GAAI,CACA,YAAa,eACb,SAAU,YACV,YAAa,UACb,KAAM,OACN,UAAW,YACX,OAAQ,gBACR,UAAW,aACX,WAAY,aACZ,QAAS,OACT,KAAM,OACN,KAAM,OACN,KAAM,WACN,OAAQ,SACR,KAAM,eACN,KAAM,QACN,KAAM,sJACV,EACA,GAAI,CACA,YAAa,uCACb,SAAU,iCACV,YAAa,eACb,KAAM,eACN,UAAW,eACX,OAAQ,iCACR,UAAW,qBACX,WAAY,qBACZ,QAAS,eACT,KAAM,2BACN,KAAM,2BACN,KAAM,iCACN,OAAQ,eACR,KAAM,6CACN,KAAM,qBACN,KAAM,2RACV,EACA,GAAI,CACA,YAAa,2BACb,SAAU,eACV,YAAa,eACb,KAAM,eACN,UAAW,qBACX,OAAQ,qBACR,UAAW,2BACX,WAAY,qBACZ,QAAS,SACT,KAAM,eACN,KAAM,eACN,KAAM,eACN,OAAQ,eACR,KAAM,iCACN,KAAM,eACN,KAAM,yNACV,EACA,GAAI,CACA,YAAa,0BACb,SAAU,qBACV,YAAa,WACb,KAAM,OACN,UAAW,gBACX,OAAQ,kBACR,UAAW,YACX,WAAY,cACZ,QAAS,QACT,KAAM,mBACN,KAAM,cACN,KAAM,aACN,OAAQ,aACR,KAAM,yBACN,KAAM,QACN,KAAM,+JACV,EACA,GAAI,CACA,YAAa,2BACb,SAAU,SACV,YAAa,gBACb,KAAM,OACN,UAAW,cACX,OAAQ,WACR,UAAW,mBACX,WAAY,kBACZ,QAAS,SACT,KAAM,UACN,KAAM,cACN,KAAM,UACN,OAAQ,aACR,KAAM,2BACN,KAAM,QACN,KAAM,0LACV,EACA,GAAI,CACA,YAAa,yBACb,SAAU,aACV,YAAa,cACb,KAAM,YACN,UAAW,eACX,OAAQ,UACR,UAAW,eACX,WAAY,SACZ,QAAS,UACT,KAAM,UACN,KAAM,SACN,KAAM,WACN,OAAQ,UACR,KAAM,wBACN,KAAM,YACN,KAAM,4JACV,EACA,GAAI,CACA,YAAa,kCACb,SAAU,mBACV,YAAa,0BACb,KAAM,gBACN,UAAW,oBACX,OAAQ,kBACR,UAAW,kBACX,WAAY,kBACZ,QAAS,WACT,KAAM,iBACN,KAAM,kBACN,KAAM,eACN,OAAQ,oBACR,KAAM,qCACN,KAAM,wBACN,KAAM,gMACV,CACJ,EAKO,SAASC,EAAgBC,EAAiBC,EAAuD,CACpG,IAAMC,GAAQF,GAAU,MAAM,MAAM,EAAG,CAAC,EAAE,YAAY,EACtD,MAAO,CAAE,GAAIF,GAAQI,CAAI,GAAKJ,GAAQ,GAAK,GAAGG,CAAU,CAC5D,CN/GQ,OAEQ,OAAAE,EAFR,QAAAC,OAAA,oBAlBD,SAASC,GAAS,CAAE,MAAAC,EAAO,SAAAC,EAAU,OAAAC,EAAQ,QAASC,EAAW,SAAAC,EAAU,QAAAC,EAAU,GAAM,aAAAC,EAAc,WAAAC,EAAY,OAAAC,EAAS,MAAO,EAAkB,CAC1J,IAAMC,EAAUC,EAAgBR,EAAQC,CAAS,EAC3CQ,EAAUC,EAAe,EACzB,CAACC,EAAUC,CAAW,EAAIC,GAAwB,IAAI,EACtDC,EAAS,CAACC,EAAgBC,IAAsB,CAClDP,EAAQ,OAAOX,EAAOkB,CAAQ,EAC9BjB,EAASgB,CAAI,CACjB,EACME,EAAO,IAAM,CACf,IAAMC,EAAOT,EAAQ,KAAKX,CAAK,EAC3BoB,GAAMnB,EAASmB,CAAI,CAC3B,EACMC,EAAO,IAAM,CACf,IAAMJ,EAAON,EAAQ,KAAKX,CAAK,EAC3BiB,GAAMhB,EAASgB,CAAI,CAC3B,EACMK,EAAeT,EAAWU,EAAWvB,EAAOa,CAAQ,GAAG,KAAO,OACpE,OACIf,GAAC0B,GAAA,CAAI,GAAI,CAAE,QAAS,OAAQ,cAAe,SAAU,OAAAhB,EAAQ,UAAW,EAAG,QAAS,MAAO,EACtF,UAAAH,EACGR,EAAC4B,EAAA,CACG,QAAShB,EACT,MAAOa,EAAgBA,EAAa,OAAS,CAAC,EAAK,OACnD,QAAUI,GAAU,CAChB,GAAI,CAACb,EAAU,OACf,IAAMc,EAASC,GAAa5B,EAAO,CAACa,CAAQ,EAAGa,CAAK,EAChDC,GAAQX,EAAOW,EAAO,IAAI,CAClC,EACA,QAAShB,EAAQ,QACjB,QAASA,EAAQ,QACjB,OAAQQ,EACR,OAAQE,EACR,MAAOf,EACP,IAAKC,EACL,SAAUH,EACd,EACA,KACJP,EAAC2B,GAAA,CAAI,GAAI,CAAE,KAAM,EAAG,UAAW,CAAE,EAC7B,SAAA3B,EAACgC,EAAA,CAAY,KAAM7B,EAAO,OAAQgB,EAAQ,WAAYH,EAAU,SAAUC,EAAa,QAASL,EAAS,SAAUL,EAAU,OAAQe,EAAM,OAAQE,EAAM,EAC7J,GACJ,CAER",
|
|
6
|
+
"names": ["useState", "Box", "useState", "Box", "ButtonBase", "Menu", "MenuItem", "Tooltip", "ColorPicker", "BoldIcon", "FontColorIcon", "FontFillIcon", "RedoIcon", "RemoveFormatIcon", "StrikeIcon", "UnderlineIcon", "UndoIcon", "newNodeId", "createTree", "topic", "clone", "root", "locateNode", "id", "stack", "parent", "list", "i", "containsNode", "node", "child", "isFolded", "visibleRows", "rows", "walk", "depth", "parentId", "edit", "run", "copy", "focusId", "addChild", "first", "at", "fresh", "addSibling", "addBelow", "removeNode", "indentNode", "prev", "outdentNode", "up", "shiftNode", "step", "to", "moveNode", "from", "place", "src", "dst", "setTopic", "toggleFold", "expanded", "setNodeStyle", "ids", "patch", "changed", "next", "key", "value", "hasDecoration", "style", "kind", "toggleDecoration", "set", "Fragment", "jsx", "jsxs", "FONT_SIZES", "ToolTip", "title", "children", "Tooltip", "ToolButton", "label", "active", "disabled", "onClick", "Box", "event", "ToolDivider", "ColorButton", "type", "value", "onPick", "anchor", "setAnchor", "useState", "shown", "FontColorIcon", "FontFillIcon", "ColorPicker", "hex", "FormatToolbar", "strings", "style", "onStyle", "canUndo", "canRedo", "onUndo", "onRedo", "start", "end", "readOnly", "sizeAnchor", "setSizeAnchor", "size", "bold", "hasStyle", "UndoIcon", "RedoIcon", "ButtonBase", "Menu", "MenuItem", "BoldIcon", "hasDecoration", "toggleDecoration", "UnderlineIcon", "StrikeIcon", "color", "RemoveFormatIcon", "useEffect", "useRef", "useState", "Box", "IconButton", "ArrowRightIcon", "ArrowDropDownIcon", "DragIndicatorIcon", "ctx", "cache", "DEFAULT_FONT_FAMILY", "measureText", "text", "fontSize", "fontWeight", "fontFamily", "font", "key", "hit", "width", "ch", "jsx", "jsxs", "INDENT", "BASE_FONT", "outlineTextStyle", "style", "depth", "OutlineList", "root", "onEdit", "selectedId", "onSelect", "strings", "readOnly", "onUndo", "onRedo", "paddingTop", "paddingBottom", "inputs", "useRef", "rowEls", "focus", "setFocus", "useState", "drop", "setDrop", "dragging", "setDragging", "rows", "visibleRows", "useEffect", "el", "apply", "result", "mergeKey", "onKey", "event", "index", "row", "id", "mod", "addBelow", "outdentNode", "indentNode", "shiftNode", "next", "removeNode", "dragNode", "locateNode", "onHandleDown", "onHandleMove", "r", "containsNode", "onHandleUp", "moveNode", "Box", "hasKids", "folded", "line", "text", "theme", "DragIndicatorIcon", "IconButton", "toggleFold", "ArrowRightIcon", "ArrowDropDownIcon", "setTopic", "measureText", "useCallback", "useRef", "useState", "useTreeHistory", "limit", "mergeMs", "past", "future", "last", "bump", "refresh", "n", "record", "before", "mergeKey", "now", "merge", "undo", "current", "prev", "redo", "next", "clear", "STRINGS", "outlinerStrings", "locale", "overrides", "lang", "jsx", "jsxs", "Outliner", "value", "onChange", "locale", "overrides", "readOnly", "toolbar", "toolbarStart", "toolbarEnd", "height", "strings", "outlinerStrings", "history", "useTreeHistory", "selected", "setSelected", "useState", "commit", "next", "mergeKey", "undo", "prev", "redo", "selectedNode", "locateNode", "Box", "FormatToolbar", "patch", "result", "setNodeStyle", "OutlineList"]
|
|
7
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var Fe=Object.create;var W=Object.defineProperty;var Ie=Object.getOwnPropertyDescriptor;var Re=Object.getOwnPropertyNames;var Me=Object.getPrototypeOf,we=Object.prototype.hasOwnProperty;var Pe=(e,o)=>{for(var n in o)W(e,n,{get:o[n],enumerable:!0})},xe=(e,o,n,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of Re(o))!we.call(e,r)&&r!==n&&W(e,r,{get:()=>o[r],enumerable:!(i=Ie(o,r))||i.enumerable});return e};var X=(e,o,n)=>(n=e!=null?Fe(Me(e)):{},xe(o||!e||!e.__esModule?W(n,"default",{value:e,enumerable:!0}):n,e)),Ae=e=>xe(W({},"__esModule",{value:!0}),e);var Ke={};Pe(Ke,{DEFAULT_FONT_FAMILY:()=>ue,FONT_SIZES:()=>ce,FormatToolbar:()=>j,OutlineList:()=>Z,Outliner:()=>ve,ToolButton:()=>M,ToolDivider:()=>L,ToolTip:()=>$,addBelow:()=>ee,addChild:()=>be,addSibling:()=>me,containsNode:()=>O,createTree:()=>He,hasDecoration:()=>K,indentNode:()=>ne,isFolded:()=>Le,locateNode:()=>g,measureText:()=>_,moveNode:()=>ie,newNodeId:()=>J,outdentNode:()=>te,outlineTextStyle:()=>fe,outlinerStrings:()=>V,removeNode:()=>oe,setNodeStyle:()=>ae,setTopic:()=>le,shiftNode:()=>re,toggleDecoration:()=>G,toggleFold:()=>de,useTreeHistory:()=>q,visibleRows:()=>Q});module.exports=Ae(Ke);var Ce=require("react"),pe=require("@mui/material");var se=require("react"),N=require("@mui/material"),Ne=require("@ehfuse/mui-color-picker"),m=require("@ehfuse/toolbar-icons");var J=()=>Math.random().toString(16).slice(2,18),He=e=>({id:"root",topic:e,children:[]}),Oe=e=>structuredClone(e);function g(e,o){if(e.id===o)return{node:e,parent:null,index:0};let n=[e];for(;n.length;){let i=n.pop(),r=i.children??[];for(let l=0;l<r.length;l++){if(r[l].id===o)return{node:r[l],parent:i,index:l};n.push(r[l])}}return null}var O=(e,o)=>e.id===o||(e.children??[]).some(n=>O(n,o)),Le=e=>e.expanded===!1&&(e.children?.length??0)>0;function Q(e){let o=[],n=(i,r,l)=>{if(o.push({node:i,depth:r,parentId:l}),i.expanded!==!1)for(let d of i.children??[])n(d,r+1,i.id)};return n(e,0,null),o}function I(e,o){let n=Oe(e),i=o(n);return i===!1?null:{root:n,focusId:i||void 0}}var be=(e,o,n="",i=!1)=>I(e,r=>{let l=g(r,o);if(!l)return!1;let d={id:J(),topic:n};return l.node.children=i?[d,...l.node.children??[]]:[...l.node.children??[],d],l.node.expanded=!0,d.id}),me=(e,o,n="")=>I(e,i=>{let r=g(i,o);if(!r)return!1;let l={id:J(),topic:n};return r.parent?r.parent.children.splice(r.index+1,0,l):(r.node.children=[l,...r.node.children??[]],r.node.expanded=!0),l.id}),ee=(e,o)=>{let n=g(e,o);return n&&(!n.parent||(n.node.children?.length??0)>0&&n.node.expanded!==!1)?be(e,o,"",!0):me(e,o)},oe=(e,o)=>I(e,n=>{let i=g(n,o);return i?.parent?(i.parent.children.splice(i.index,1),i.parent.children[i.index-1]?.id??i.parent.id):!1}),ne=(e,o)=>I(e,n=>{let i=g(n,o);if(!i?.parent||i.index===0)return!1;let r=i.parent.children[i.index-1];return i.parent.children.splice(i.index,1),r.children=[...r.children??[],i.node],r.expanded=!0,o}),te=(e,o)=>I(e,n=>{let i=g(n,o);if(!i?.parent)return!1;let r=g(n,i.parent.id);return r?.parent?(i.parent.children.splice(i.index,1),r.parent.children.splice(r.index+1,0,i.node),o):!1}),re=(e,o,n)=>I(e,i=>{let r=g(i,o);if(!r?.parent)return!1;let l=r.parent.children,d=r.index+n;return d<0||d>=l.length?!1:([l[r.index],l[d]]=[l[d],l[r.index]],o)}),ie=(e,o,n,i)=>I(e,r=>{let l=g(r,o);if(!l?.parent||O(l.node,n))return!1;l.parent.children.splice(l.index,1);let d=g(r,n);return d?(i==="child"||!d.parent?(d.node.children=[...d.node.children??[],l.node],d.node.expanded=!0):d.parent.children.splice(d.index+(i==="after"?1:0),0,l.node),o):!1}),le=(e,o,n)=>I(e,i=>{let r=g(i,o);if(!r||r.node.topic===n)return!1;r.node.topic=n}),de=(e,o,n)=>I(e,i=>{let r=g(i,o);if(!r||!(r.node.children?.length??0))return!1;r.node.expanded=n??r.node.expanded===!1}),ae=(e,o,n)=>I(e,i=>{let r=!1;for(let l of o){let d=g(i,l);if(!d)continue;let c={...d.node.style??{}};for(let[h,v]of Object.entries(n))v===void 0||v===""?delete c[h]:c[h]=v;d.node.style=Object.keys(c).length?c:void 0,d.node.style||delete d.node.style,r=!0}return r?void 0:!1});function K(e,o){return(e?.textDecoration??"").split(/\s+/).includes(o)}function G(e,o){let n=new Set((e?.textDecoration??"").split(/\s+/).filter(Boolean));return n.has(o)?n.delete(o):n.add(o),n.size?[...n].join(" "):void 0}var a=require("react/jsx-runtime"),ce=[12,14,16,18,20,24,28,32];function $({title:e,children:o}){return(0,a.jsx)(N.Tooltip,{title:e,placement:"bottom",arrow:!0,disableInteractive:!0,leaveDelay:0,slotProps:{tooltip:{sx:{bgcolor:"#000000",color:"#ffffff",fontSize:"14px",fontWeight:"normal","& .MuiTooltip-arrow":{color:"#000000"}}}},children:(0,a.jsx)("span",{style:{display:"inline-flex"},children:o})})}function M({label:e,active:o=!1,disabled:n=!1,onClick:i,children:r}){return(0,a.jsx)($,{title:e,children:(0,a.jsx)(N.Box,{component:"button",type:"button","aria-label":e,"aria-pressed":o,disabled:n,onMouseDown:l=>l.preventDefault(),onClick:l=>{l.preventDefault(),n||i(l)},sx:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:32,height:32,p:"6px",border:"none",borderRadius:"4px",bgcolor:o?"rgba(0, 0, 0, 0.08)":"transparent",color:n?"rgba(0, 0, 0, 0.26)":"inherit",cursor:n?"not-allowed":"pointer",transition:"background-color 150ms cubic-bezier(0.4, 0, 0.2, 1)","&:hover:not(:disabled)":{bgcolor:o?"rgba(0, 0, 0, 0.08)":"rgba(0, 0, 0, 0.04)"},"&:focus":{outline:"none"},"& svg":{width:20,height:20}},children:r})})}var L=()=>(0,a.jsx)(N.Box,{sx:{width:"1px",alignSelf:"stretch",my:"8px",mx:"4px",bgcolor:"#e0e0e0"}});function Te({label:e,type:o,value:n,disabled:i,onPick:r}){let[l,d]=(0,se.useState)(null),c=n??(o==="color"?"#000000":"#FFFFFF");return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(M,{label:e,disabled:i,onClick:h=>d(h.currentTarget),children:o==="color"?(0,a.jsx)(m.FontColorIcon,{width:"20px",height:"20px",barColor:c}):(0,a.jsx)(m.FontFillIcon,{width:"20px",fillColor:c})}),l?(0,a.jsx)(Ne.ColorPicker,{value:c,onChange:h=>{r(h),d(null)},anchor:l,anchorMode:"auto",onClose:()=>d(null),position:"bottomCenter",advanced:{position:"bottom"},shape:"square",colorSize:16,gap:4,padding:5,popover:{disableEnforceFocus:!0,disableAutoFocus:!0,disableRestoreFocus:!0,sx:{zIndex:10001}}}):null]})}function j({strings:e,style:o,onStyle:n,canUndo:i,canRedo:r,onUndo:l,onRedo:d,start:c,children:h,end:v,readOnly:T}){let[f,u]=(0,se.useState)(null),p=!!T||!o,x=o?.fontSize?parseFloat(o.fontSize):null,R=o?.fontWeight==="bold"||Number(o?.fontWeight)>=600,k=!!(o&&Object.keys(o).length>0);return(0,a.jsxs)(N.Box,{sx:{display:"flex",alignItems:"center",flexWrap:"wrap",gap:"2px",minHeight:44,px:1,py:"4px",borderBottom:"1px solid #e0e0e0",bgcolor:"#fff",color:"#333",flexShrink:0},children:[c,c?(0,a.jsx)(L,{}):null,l?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(M,{label:e.undo,disabled:T||!i,onClick:l,children:(0,a.jsx)(m.UndoIcon,{})}),(0,a.jsx)(M,{label:e.redo,disabled:T||!r,onClick:()=>d?.(),children:(0,a.jsx)(m.RedoIcon,{})}),(0,a.jsx)(L,{})]}):null,(0,a.jsx)($,{title:e.fontSize,children:(0,a.jsxs)(N.ButtonBase,{disabled:p,onMouseDown:s=>s.preventDefault(),onClick:s=>u(s.currentTarget),sx:{height:32,minWidth:64,px:1,gap:.5,justifyContent:"space-between",border:"1px solid #d0d0d0",borderRadius:"4px",fontSize:14,color:p?"rgba(0,0,0,0.26)":"#333",bgcolor:"#fff","&:hover":{borderColor:"#999"}},children:[x??e.sizeDefault,(0,a.jsx)(N.Box,{component:"span",sx:{fontSize:10,color:"#888"},children:"\u25BC"})]})}),(0,a.jsxs)(N.Menu,{open:!!f,anchorEl:f,onClose:()=>u(null),transitionDuration:0,disableRestoreFocus:!0,slotProps:{paper:{sx:{maxHeight:320}}},children:[(0,a.jsx)(N.MenuItem,{dense:!0,selected:x===null,onClick:()=>{n({fontSize:void 0}),u(null)},children:e.sizeDefault}),ce.map(s=>(0,a.jsx)(N.MenuItem,{dense:!0,selected:x===s,onClick:()=>{n({fontSize:`${s}px`}),u(null)},children:s},s))]}),(0,a.jsx)(M,{label:e.bold,active:R,disabled:p,onClick:()=>n({fontWeight:R?void 0:"bold"}),children:(0,a.jsx)(m.BoldIcon,{})}),(0,a.jsx)(M,{label:e.underline,active:K(o,"underline"),disabled:p,onClick:()=>n({textDecoration:G(o,"underline")}),children:(0,a.jsx)(m.UnderlineIcon,{})}),(0,a.jsx)(M,{label:e.strike,active:K(o,"line-through"),disabled:p,onClick:()=>n({textDecoration:G(o,"line-through")}),children:(0,a.jsx)(m.StrikeIcon,{})}),(0,a.jsx)(Te,{label:e.textColor,type:"color",value:o?.color,disabled:p,onPick:s=>n({color:s})}),(0,a.jsx)(Te,{label:e.background,type:"background",value:o?.background,disabled:p,onPick:s=>n({background:s.toLowerCase()==="#ffffff"?void 0:s})}),(0,a.jsx)(M,{label:e.clearFormat,disabled:p||!k,onClick:()=>n({fontSize:void 0,color:void 0,background:void 0,fontWeight:void 0,textDecoration:void 0}),children:(0,a.jsx)(m.RemoveFormatIcon,{})}),h?(0,a.jsx)(L,{}):null,h,(0,a.jsx)(N.Box,{sx:{flex:1}}),v]})}var P=require("react"),w=require("@mui/material"),ye=X(require("@mui/icons-material/ArrowRight")),Se=X(require("@mui/icons-material/ArrowDropDown")),ke=X(require("@mui/icons-material/DragIndicator"));var U=null,Y=new Map,ue="-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif";function _(e,o,n=400,i=ue){let r=`${n} ${o}px ${i}`,l=`${r}|${e}`,d=Y.get(l);if(d!==void 0)return d;!U&&typeof document<"u"&&(U=document.createElement("canvas").getContext("2d"));let c=0;if(U)U.font=r,c=U.measureText(e).width;else for(let h of e)c+=(/[ᄀ-ᅵ]/.test(h)?1:.55)*o;return Y.size>5e3&&Y.clear(),Y.set(l,c),c}var C=require("react/jsx-runtime"),Ue=22,We=15;function fe(e,o){return{fontSize:e?.fontSize?Math.min(28,Math.max(12,parseFloat(e.fontSize))):We,fontWeight:e?.fontWeight??(o===0?700:400),color:e?.color,textDecoration:e?.textDecoration,background:e?.background}}function Z({root:e,onEdit:o,selectedId:n,onSelect:i,strings:r,readOnly:l,onUndo:d,onRedo:c,paddingTop:h=8,paddingBottom:v=40}){let T=(0,P.useRef)(new Map),f=(0,P.useRef)(new Map),[u,p]=(0,P.useState)(null),[x,R]=(0,P.useState)(null),[k,s]=(0,P.useState)(null),E=Q(e);(0,P.useEffect)(()=>{if(!u)return;let t=T.current.get(u);t&&(t.focus(),t.setSelectionRange(t.value.length,t.value.length),p(null))},[u,e]);let A=(t,y)=>{t&&(o(t.root,y),t.focusId&&(p(t.focusId),i?.(t.focusId)))},Be=(t,y)=>{if(t.nativeEvent.isComposing)return;let z=E[y],D=z.node.id,F=t.ctrlKey||t.metaKey;if(F&&(t.key==="z"||t.key==="Z")){t.preventDefault(),t.shiftKey?c?.():d?.();return}if(F&&(t.key==="y"||t.key==="Y")){t.preventDefault(),c?.();return}if(!l){if(t.key==="Enter")t.preventDefault(),A(ee(e,D));else if(t.key==="Tab")t.preventDefault(),A(t.shiftKey?te(e,D):ne(e,D));else if(t.altKey&&(t.key==="ArrowUp"||t.key==="ArrowDown"))t.preventDefault(),A(re(e,D,t.key==="ArrowUp"?-1:1));else if(t.key==="ArrowUp"||t.key==="ArrowDown"){let b=E[y+(t.key==="ArrowUp"?-1:1)];b&&(t.preventDefault(),p(b.node.id),i?.(b.node.id))}else if(t.key==="Backspace"&&t.currentTarget.value===""&&z.depth>0&&!(z.node.children?.length??0)){t.preventDefault();let b=oe(e,D);b&&A({root:b.root,focusId:E[y-1]?.node.id})}}},ge=k?g(e,k)?.node??null:null,Ee=(t,y)=>{t.button!==0||l||(t.preventDefault(),t.currentTarget.setPointerCapture(t.pointerId),s(y),R(null))},ze=t=>{if(!k||!ge)return;let y=null;for(let z of E){let D=f.current.get(z.node.id);if(!D)continue;let F=D.getBoundingClientRect();if(!(t.clientY<F.top||t.clientY>=F.bottom)){O(ge,z.node.id)||(y={id:z.node.id,place:t.clientY<F.top+F.height/2?"before":"after"});break}}(y?.id!==x?.id||y?.place!==x?.place)&&R(y)},he=t=>{t.currentTarget.hasPointerCapture(t.pointerId)&&t.currentTarget.releasePointerCapture(t.pointerId),k&&x&&A(ie(e,k,x.id,x.place)),s(null),R(null)};return(0,C.jsx)(w.Box,{sx:{position:"relative",height:"100%",overflowY:"auto",bgcolor:"#fff",pt:`${h}px`,pb:`${v}px`,px:1.5},children:E.map((t,y)=>{let z=(t.node.children?.length??0)>0,D=t.node.expanded===!1,F=x?.id===t.node.id?x.place:null,b=fe(t.node.style,t.depth);return(0,C.jsxs)(w.Box,{ref:S=>{S?f.current.set(t.node.id,S):f.current.delete(t.node.id)},sx:{display:"flex",alignItems:"center",minHeight:26,pl:`${t.depth*Ue}px`,borderRadius:.75,bgcolor:n===t.node.id?"rgba(26,115,232,0.06)":"transparent",boxShadow:S=>F==="before"?`inset 0 2px 0 ${S.palette.primary.main}`:F==="after"?`inset 0 -2px 0 ${S.palette.primary.main}`:"none",opacity:k===t.node.id?.4:1,"&:hover .ol-handle":{visibility:"visible"}},children:[(0,C.jsx)(w.Box,{className:"ol-handle",title:r.drag,onPointerDown:t.depth>0?S=>Ee(S,t.node.id):void 0,onPointerMove:ze,onPointerUp:he,onPointerCancel:he,sx:{display:"flex",color:"#b0b4ba",cursor:t.depth>0&&!l?k?"grabbing":"grab":"default",visibility:k===t.node.id?"visible":"hidden",width:18,flexShrink:0,touchAction:"none"},children:t.depth>0&&!l?(0,C.jsx)(ke.default,{sx:{fontSize:16}}):null}),z?(0,C.jsx)(w.IconButton,{size:"small","aria-label":D?r.unfold:r.fold,title:D?r.unfold:r.fold,onClick:()=>A(de(e,t.node.id)),sx:{width:22,height:22,p:0,color:"text.secondary"},children:D?(0,C.jsx)(ye.default,{sx:{fontSize:20}}):(0,C.jsx)(Se.default,{sx:{fontSize:20}})}):(0,C.jsx)(w.Box,{sx:{width:22,display:"flex",justifyContent:"center",flexShrink:0},children:(0,C.jsx)(w.Box,{sx:{width:5,height:5,borderRadius:"50%",bgcolor:"#9aa0a6"}})}),(0,C.jsx)(w.Box,{sx:{flex:1,minWidth:0,display:"flex",ml:.5},children:(0,C.jsx)("input",{ref:S=>{S?T.current.set(t.node.id,S):T.current.delete(t.node.id)},value:t.node.topic,readOnly:l,placeholder:t.depth===0?r.root:"",spellCheck:!1,autoComplete:"off",onFocus:()=>i?.(t.node.id),onChange:S=>A(le(e,t.node.id,S.target.value),`topic:${t.node.id}`),onKeyDown:S=>Be(S,y),style:{width:b.background?Math.ceil(_(t.node.topic||" ",Number(b.fontSize),b.fontWeight))+16:"100%",maxWidth:"100%",border:0,outline:0,font:"inherit",padding:b.background?"2px 6px":"2px 0",borderRadius:4,background:b.background??"transparent",color:b.color??"inherit",fontSize:b.fontSize,fontWeight:b.fontWeight,textDecoration:[b.textDecoration,D&&z?"underline dotted #9aa0a6":""].filter(Boolean).join(" ")||void 0}})})]},t.node.id)})})}var B=require("react");function q(e=100,o=1200){let n=(0,B.useRef)([]),i=(0,B.useRef)([]),r=(0,B.useRef)(null),[,l]=(0,B.useState)(0),d=()=>l(f=>f+1),c=(0,B.useCallback)((f,u)=>{let p=Date.now(),x=u&&r.current?.key===u&&p-r.current.at<o;r.current=u?{key:u,at:p}:null,i.current=[],x||(n.current.push(f),n.current.length>e&&n.current.shift()),d()},[e,o]),h=(0,B.useCallback)(f=>{let u=n.current.pop();return u?(i.current.push(f),r.current=null,d(),u):null},[]),v=(0,B.useCallback)(f=>{let u=i.current.pop();return u?(n.current.push(f),r.current=null,d(),u):null},[]),T=(0,B.useCallback)(()=>{n.current=[],i.current=[],r.current=null,d()},[]);return{canUndo:n.current.length>0,canRedo:i.current.length>0,record:c,undo:h,redo:v,clear:T}}var De={ko:{clearFormat:"\uC11C\uC2DD \uC9C0\uC6B0\uAE30",fontSize:"\uAE00\uC790 \uD06C\uAE30",sizeDefault:"\uAE30\uBCF8",bold:"\uC9C4\uD558\uAC8C",underline:"\uBC11\uC904",strike:"\uCDE8\uC18C\uC120",textColor:"\uAE00\uC790\uC0C9",background:"\uBC30\uACBD\uC0C9",noColor:"\uC5C6\uC74C",undo:"\uB418\uB3CC\uB9AC\uAE30",redo:"\uB2E4\uC2DC \uD558\uAE30",fold:"\uC811\uAE30",unfold:"\uD3BC\uCE58\uAE30",drag:"\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30",root:"\uC8FC\uC81C",hint:"Enter: \uC0C8 \uC904 \xB7 Tab / Shift+Tab: \uB4E4\uC5EC\uC4F0\uAE30 / \uB0B4\uC5B4\uC4F0\uAE30 \xB7 Alt+\u2191\u2193: \uC21C\uC11C \xB7 \uC904 \uC55E \uC190\uC7A1\uC774 \uB04C\uAE30: \uC62E\uAE30\uAE30 \xB7 \uBE48 \uC904 Backspace: \uC9C0\uC6B0\uAE30"},en:{clearFormat:"Clear format",fontSize:"Font size",sizeDefault:"Default",bold:"Bold",underline:"Underline",strike:"Strikethrough",textColor:"Text color",background:"Background",noColor:"None",undo:"Undo",redo:"Redo",fold:"Collapse",unfold:"Expand",drag:"Drag to move",root:"Topic",hint:"Enter: new line \xB7 Tab / Shift+Tab: indent / outdent \xB7 Alt+\u2191\u2193: reorder \xB7 Drag the row handle: move \xB7 Backspace on empty: delete"},ja:{clearFormat:"\u66F8\u5F0F\u3092\u30AF\u30EA\u30A2",fontSize:"\u6587\u5B57\u30B5\u30A4\u30BA",sizeDefault:"\u6A19\u6E96",bold:"\u592A\u5B57",underline:"\u4E0B\u7DDA",strike:"\u53D6\u308A\u6D88\u3057\u7DDA",textColor:"\u6587\u5B57\u8272",background:"\u80CC\u666F\u8272",noColor:"\u306A\u3057",undo:"\u5143\u306B\u623B\u3059",redo:"\u3084\u308A\u76F4\u3059",fold:"\u6298\u308A\u305F\u305F\u3080",unfold:"\u5C55\u958B",drag:"\u30C9\u30E9\u30C3\u30B0\u3067\u79FB\u52D5",root:"\u30C6\u30FC\u30DE",hint:"Enter: \u65B0\u3057\u3044\u884C \xB7 Tab / Shift+Tab: \u5B57\u4E0B\u3052 / \u5B57\u4E0A\u3052 \xB7 Alt+\u2191\u2193: \u4E26\u3079\u66FF\u3048 \xB7 \u884C\u982D\u306E\u30CF\u30F3\u30C9\u30EB\u3092\u30C9\u30E9\u30C3\u30B0: \u79FB\u52D5 \xB7 \u7A7A\u884C\u3067 Backspace: \u524A\u9664"},zh:{clearFormat:"\u6E05\u9664\u683C\u5F0F",fontSize:"\u5B57\u53F7",sizeDefault:"\u9ED8\u8BA4",bold:"\u52A0\u7C97",underline:"\u4E0B\u5212\u7EBF",strike:"\u5220\u9664\u7EBF",textColor:"\u6587\u5B57\u989C\u8272",background:"\u80CC\u666F\u8272",noColor:"\u65E0",undo:"\u64A4\u9500",redo:"\u91CD\u505A",fold:"\u6298\u53E0",unfold:"\u5C55\u5F00",drag:"\u62D6\u52A8\u4EE5\u79FB\u52A8",root:"\u4E3B\u9898",hint:"Enter: \u65B0\u884C \xB7 Tab / Shift+Tab: \u7F29\u8FDB / \u53D6\u6D88\u7F29\u8FDB \xB7 Alt+\u2191\u2193: \u987A\u5E8F \xB7 \u62D6\u52A8\u884C\u9996\u624B\u67C4: \u79FB\u52A8 \xB7 \u7A7A\u884C Backspace: \u5220\u9664"},de:{clearFormat:"Formatierung l\xF6schen",fontSize:"Schriftgr\xF6\xDFe",sizeDefault:"Standard",bold:"Fett",underline:"Unterstrichen",strike:"Durchgestrichen",textColor:"Textfarbe",background:"Hintergrund",noColor:"Keine",undo:"R\xFCckg\xE4ngig",redo:"Wiederholen",fold:"Einklappen",unfold:"Ausklappen",drag:"Zum Verschieben ziehen",root:"Thema",hint:"Enter: neue Zeile \xB7 Tab / Shift+Tab: ein- / ausr\xFCcken \xB7 Alt+\u2191\u2193: Reihenfolge \xB7 Griff ziehen: verschieben \xB7 Backspace leer: l\xF6schen"},fr:{clearFormat:"Effacer la mise en forme",fontSize:"Taille",sizeDefault:"Par d\xE9faut",bold:"Gras",underline:"Soulign\xE9",strike:"Barr\xE9",textColor:"Couleur du texte",background:"Arri\xE8re-plan",noColor:"Aucune",undo:"Annuler",redo:"R\xE9tablir",fold:"Replier",unfold:"D\xE9plier",drag:"Glisser pour d\xE9placer",root:"Sujet",hint:"Entr\xE9e : nouvelle ligne \xB7 Tab / Maj+Tab : indenter / d\xE9sindenter \xB7 Alt+\u2191\u2193 : ordre \xB7 Glisser la poign\xE9e : d\xE9placer \xB7 Retour arri\xE8re vide : supprimer"},it:{clearFormat:"Cancella formattazione",fontSize:"Dimensione",sizeDefault:"Predefinita",bold:"Grassetto",underline:"Sottolineato",strike:"Barrato",textColor:"Colore testo",background:"Sfondo",noColor:"Nessuno",undo:"Annulla",redo:"Ripeti",fold:"Comprimi",unfold:"Espandi",drag:"Trascina per spostare",root:"Argomento",hint:"Invio: nuova riga \xB7 Tab / Maiusc+Tab: rientro / riduci \xB7 Alt+\u2191\u2193: ordine \xB7 Trascina la maniglia: sposta \xB7 Backspace su vuota: elimina"},vi:{clearFormat:"X\xF3a \u0111\u1ECBnh d\u1EA1ng",fontSize:"C\u1EE1 ch\u1EEF",sizeDefault:"M\u1EB7c \u0111\u1ECBnh",bold:"\u0110\u1EADm",underline:"G\u1EA1ch ch\xE2n",strike:"G\u1EA1ch ngang",textColor:"M\xE0u ch\u1EEF",background:"M\xE0u n\u1EC1n",noColor:"Kh\xF4ng",undo:"Ho\xE0n t\xE1c",redo:"L\xE0m l\u1EA1i",fold:"Thu g\u1ECDn",unfold:"M\u1EDF r\u1ED9ng",drag:"K\xE9o \u0111\u1EC3 di chuy\u1EC3n",root:"Ch\u1EE7 \u0111\u1EC1",hint:"Enter: d\xF2ng m\u1EDBi \xB7 Tab / Shift+Tab: th\u1EE5t v\xE0o / ra \xB7 Alt+\u2191\u2193: th\u1EE9 t\u1EF1 \xB7 K\xE9o tay c\u1EA7m: di chuy\u1EC3n \xB7 Backspace d\xF2ng tr\u1ED1ng: x\xF3a"}};function V(e,o){let n=(e??"en").slice(0,2).toLowerCase();return{...De[n]??De.en,...o}}var H=require("react/jsx-runtime");function ve({value:e,onChange:o,locale:n,strings:i,readOnly:r,toolbar:l=!0,toolbarStart:d,toolbarEnd:c,height:h="100%"}){let v=V(n,i),T=q(),[f,u]=(0,Ce.useState)(null),p=(s,E)=>{T.record(e,E),o(s)},x=()=>{let s=T.undo(e);s&&o(s)},R=()=>{let s=T.redo(e);s&&o(s)},k=f?g(e,f)?.node:void 0;return(0,H.jsxs)(pe.Box,{sx:{display:"flex",flexDirection:"column",height:h,minHeight:0,bgcolor:"#fff"},children:[l?(0,H.jsx)(j,{strings:v,style:k?k.style??{}:void 0,onStyle:s=>{if(!f)return;let E=ae(e,[f],s);E&&p(E.root)},canUndo:T.canUndo,canRedo:T.canRedo,onUndo:x,onRedo:R,start:d,end:c,readOnly:r}):null,(0,H.jsx)(pe.Box,{sx:{flex:1,minHeight:0},children:(0,H.jsx)(Z,{root:e,onEdit:p,selectedId:f,onSelect:u,strings:v,readOnly:r,onUndo:x,onRedo:R})})]})}
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/Outliner.tsx", "../src/FormatToolbar.tsx", "../src/tree.ts", "../src/OutlineList.tsx", "../src/measure.ts", "../src/history.ts", "../src/i18n.ts"],
|
|
4
|
+
"sourcesContent": ["export { Outliner, type OutlinerProps } from \"./Outliner\";\nexport { OutlineList, outlineTextStyle, type OutlineListProps } from \"./OutlineList\";\nexport { FormatToolbar, ToolButton, ToolDivider, ToolTip, FONT_SIZES, type FormatToolbarProps } from \"./FormatToolbar\";\nexport { useTreeHistory, type TreeHistory } from \"./history\";\nexport { outlinerStrings, type OutlinerStrings } from \"./i18n\";\nexport { measureText, DEFAULT_FONT_FAMILY } from \"./measure\";\nexport * from \"./tree\";\n", "/**\n * Outliner \u2014 toolbar + rows, with its own undo history. Controlled: pass `value` and `onChange`.\n * For a custom layout use `OutlineList` + `FormatToolbar` + `useTreeHistory` directly (the mind map does).\n */\nimport { useState, type ReactNode } from \"react\";\nimport { Box } from \"@mui/material\";\nimport { FormatToolbar } from \"./FormatToolbar\";\nimport { OutlineList } from \"./OutlineList\";\nimport { useTreeHistory } from \"./history\";\nimport { outlinerStrings, type OutlinerStrings } from \"./i18n\";\nimport { locateNode, setNodeStyle, type TreeNode } from \"./tree\";\n\nexport interface OutlinerProps {\n value: TreeNode;\n onChange: (value: TreeNode) => void;\n locale?: string;\n strings?: Partial<OutlinerStrings>;\n readOnly?: boolean;\n /** Show the formatting toolbar (default true). */\n toolbar?: boolean;\n toolbarStart?: ReactNode;\n toolbarEnd?: ReactNode;\n height?: number | string;\n}\n\nexport function Outliner({ value, onChange, locale, strings: overrides, readOnly, toolbar = true, toolbarStart, toolbarEnd, height = \"100%\" }: OutlinerProps) {\n const strings = outlinerStrings(locale, overrides);\n const history = useTreeHistory();\n const [selected, setSelected] = useState<string | null>(null);\n const commit = (next: TreeNode, mergeKey?: string) => {\n history.record(value, mergeKey);\n onChange(next);\n };\n const undo = () => {\n const prev = history.undo(value);\n if (prev) onChange(prev);\n };\n const redo = () => {\n const next = history.redo(value);\n if (next) onChange(next);\n };\n const selectedNode = selected ? locateNode(value, selected)?.node : undefined;\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height, minHeight: 0, bgcolor: \"#fff\" }}>\n {toolbar ? (\n <FormatToolbar\n strings={strings}\n style={selectedNode ? (selectedNode.style ?? {}) : undefined}\n onStyle={(patch) => {\n if (!selected) return;\n const result = setNodeStyle(value, [selected], patch);\n if (result) commit(result.root);\n }}\n canUndo={history.canUndo}\n canRedo={history.canRedo}\n onUndo={undo}\n onRedo={redo}\n start={toolbarStart}\n end={toolbarEnd}\n readOnly={readOnly}\n />\n ) : null}\n <Box sx={{ flex: 1, minHeight: 0 }}>\n <OutlineList root={value} onEdit={commit} selectedId={selected} onSelect={setSelected} strings={strings} readOnly={readOnly} onUndo={undo} onRedo={redo} />\n </Box>\n </Box>\n );\n}\n", "/**\n * Formatting toolbar \u2014 font size \u00B7 bold \u00B7 underline \u00B7 strike \u00B7 text color \u00B7 background \u00B7 clear format, plus undo / redo.\n * Shared by the outline and the mind map; it edits the style of the selected node.\n * Looks like the @ehfuse/editor toolbar on purpose: same icons (@ehfuse/toolbar-icons), same 32px buttons,\n * same black tooltip and the same color picker (@ehfuse/mui-color-picker).\n * `start` / `end` / `children` let the host add its own controls (view switch, map tools, \u2026).\n */\nimport { useState, type MouseEvent, type ReactNode } from \"react\";\nimport { Box, ButtonBase, Menu, MenuItem, Tooltip } from \"@mui/material\";\nimport { ColorPicker } from \"@ehfuse/mui-color-picker\";\nimport { BoldIcon, FontColorIcon, FontFillIcon, RedoIcon, RemoveFormatIcon, StrikeIcon, UnderlineIcon, UndoIcon } from \"@ehfuse/toolbar-icons\";\nimport { hasDecoration, toggleDecoration, type TreeNodeStyle } from \"./tree\";\nimport type { OutlinerStrings } from \"./i18n\";\n\nexport const FONT_SIZES = [12, 14, 16, 18, 20, 24, 28, 32];\n\nexport interface FormatToolbarProps {\n strings: OutlinerStrings;\n /** Style of the node being edited (undefined \u2192 nothing selected; format buttons are disabled). */\n style: TreeNodeStyle | undefined;\n onStyle: (patch: Partial<TreeNodeStyle>) => void;\n canUndo?: boolean;\n canRedo?: boolean;\n onUndo?: () => void;\n onRedo?: () => void;\n start?: ReactNode;\n children?: ReactNode;\n end?: ReactNode;\n readOnly?: boolean;\n}\n\n/** Same tooltip as the editor toolbar \u2014 black, 14px, arrow, below. */\nexport function ToolTip({ title, children }: { title: string; children: ReactNode }) {\n return (\n <Tooltip\n title={title}\n placement=\"bottom\"\n arrow\n disableInteractive\n leaveDelay={0}\n slotProps={{ tooltip: { sx: { bgcolor: \"#000000\", color: \"#ffffff\", fontSize: \"14px\", fontWeight: \"normal\", \"& .MuiTooltip-arrow\": { color: \"#000000\" } } } }}\n >\n {/* inline-flex \u2014 a plain span adds line descent under the button and lifts it 2px off center. */}\n <span style={{ display: \"inline-flex\" }}>{children}</span>\n </Tooltip>\n );\n}\n\n/** Same button as the editor toolbar \u2014 32px, 4px radius, light grey when on. */\nexport function ToolButton({ label, active = false, disabled = false, onClick, children }: { label: string; active?: boolean; disabled?: boolean; onClick: (event: MouseEvent<HTMLElement>) => void; children: ReactNode }) {\n return (\n <ToolTip title={label}>\n <Box\n component=\"button\"\n type=\"button\"\n aria-label={label}\n aria-pressed={active}\n disabled={disabled}\n onMouseDown={(event: MouseEvent) => event.preventDefault()}\n onClick={(event: MouseEvent<HTMLElement>) => {\n event.preventDefault();\n if (!disabled) onClick(event);\n }}\n sx={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 32,\n height: 32,\n p: \"6px\",\n border: \"none\",\n borderRadius: \"4px\",\n bgcolor: active ? \"rgba(0, 0, 0, 0.08)\" : \"transparent\",\n color: disabled ? \"rgba(0, 0, 0, 0.26)\" : \"inherit\",\n cursor: disabled ? \"not-allowed\" : \"pointer\",\n transition: \"background-color 150ms cubic-bezier(0.4, 0, 0.2, 1)\",\n \"&:hover:not(:disabled)\": { bgcolor: active ? \"rgba(0, 0, 0, 0.08)\" : \"rgba(0, 0, 0, 0.04)\" },\n \"&:focus\": { outline: \"none\" },\n \"& svg\": { width: 20, height: 20 },\n }}\n >\n {children}\n </Box>\n </ToolTip>\n );\n}\n\nexport const ToolDivider = () => <Box sx={{ width: \"1px\", alignSelf: \"stretch\", my: \"8px\", mx: \"4px\", bgcolor: \"#e0e0e0\" }} />;\n\nfunction ColorButton({ label, type, value, disabled, onPick }: { label: string; type: \"color\" | \"background\"; value?: string; disabled: boolean; onPick: (color: string) => void }) {\n const [anchor, setAnchor] = useState<HTMLElement | null>(null);\n const shown = value ?? (type === \"color\" ? \"#000000\" : \"#FFFFFF\");\n return (\n <>\n <ToolButton label={label} disabled={disabled} onClick={(event) => setAnchor(event.currentTarget)}>\n {type === \"color\" ? <FontColorIcon width=\"20px\" height=\"20px\" barColor={shown} /> : <FontFillIcon width=\"20px\" fillColor={shown} />}\n </ToolButton>\n {anchor ? (\n <ColorPicker\n value={shown}\n onChange={(hex: string) => {\n onPick(hex);\n setAnchor(null);\n }}\n anchor={anchor}\n anchorMode=\"auto\"\n onClose={() => setAnchor(null)}\n position=\"bottomCenter\"\n advanced={{ position: \"bottom\" }}\n shape=\"square\"\n colorSize={16}\n gap={4}\n padding={5}\n popover={{ disableEnforceFocus: true, disableAutoFocus: true, disableRestoreFocus: true, sx: { zIndex: 10001 } }}\n />\n ) : null}\n </>\n );\n}\n\nexport function FormatToolbar({ strings, style, onStyle, canUndo, canRedo, onUndo, onRedo, start, children, end, readOnly }: FormatToolbarProps) {\n const [sizeAnchor, setSizeAnchor] = useState<HTMLElement | null>(null);\n const disabled = Boolean(readOnly) || !style;\n const size = style?.fontSize ? parseFloat(style.fontSize) : null;\n const bold = style?.fontWeight === \"bold\" || Number(style?.fontWeight) >= 600;\n const hasStyle = Boolean(style && Object.keys(style).length > 0);\n return (\n <Box sx={{ display: \"flex\", alignItems: \"center\", flexWrap: \"wrap\", gap: \"2px\", minHeight: 44, px: 1, py: \"4px\", borderBottom: \"1px solid #e0e0e0\", bgcolor: \"#fff\", color: \"#333\", flexShrink: 0 }}>\n {start}\n {start ? <ToolDivider /> : null}\n {onUndo ? (\n <>\n <ToolButton label={strings.undo} disabled={readOnly || !canUndo} onClick={onUndo}>\n <UndoIcon />\n </ToolButton>\n <ToolButton label={strings.redo} disabled={readOnly || !canRedo} onClick={() => onRedo?.()}>\n <RedoIcon />\n </ToolButton>\n <ToolDivider />\n </>\n ) : null}\n <ToolTip title={strings.fontSize}>\n <ButtonBase\n disabled={disabled}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => setSizeAnchor(event.currentTarget)}\n sx={{ height: 32, minWidth: 64, px: 1, gap: 0.5, justifyContent: \"space-between\", border: \"1px solid #d0d0d0\", borderRadius: \"4px\", fontSize: 14, color: disabled ? \"rgba(0,0,0,0.26)\" : \"#333\", bgcolor: \"#fff\", \"&:hover\": { borderColor: \"#999\" } }}\n >\n {size ?? strings.sizeDefault}\n <Box component=\"span\" sx={{ fontSize: 10, color: \"#888\" }}>\n \u25BC\n </Box>\n </ButtonBase>\n </ToolTip>\n <Menu open={Boolean(sizeAnchor)} anchorEl={sizeAnchor} onClose={() => setSizeAnchor(null)} transitionDuration={0} disableRestoreFocus slotProps={{ paper: { sx: { maxHeight: 320 } } }}>\n <MenuItem\n dense\n selected={size === null}\n onClick={() => {\n onStyle({ fontSize: undefined });\n setSizeAnchor(null);\n }}\n >\n {strings.sizeDefault}\n </MenuItem>\n {FONT_SIZES.map((value) => (\n <MenuItem\n key={value}\n dense\n selected={size === value}\n onClick={() => {\n onStyle({ fontSize: `${value}px` });\n setSizeAnchor(null);\n }}\n >\n {value}\n </MenuItem>\n ))}\n </Menu>\n <ToolButton label={strings.bold} active={bold} disabled={disabled} onClick={() => onStyle({ fontWeight: bold ? undefined : \"bold\" })}>\n <BoldIcon />\n </ToolButton>\n <ToolButton label={strings.underline} active={hasDecoration(style, \"underline\")} disabled={disabled} onClick={() => onStyle({ textDecoration: toggleDecoration(style, \"underline\") })}>\n <UnderlineIcon />\n </ToolButton>\n <ToolButton label={strings.strike} active={hasDecoration(style, \"line-through\")} disabled={disabled} onClick={() => onStyle({ textDecoration: toggleDecoration(style, \"line-through\") })}>\n <StrikeIcon />\n </ToolButton>\n <ColorButton label={strings.textColor} type=\"color\" value={style?.color} disabled={disabled} onPick={(color) => onStyle({ color })} />\n <ColorButton label={strings.background} type=\"background\" value={style?.background} disabled={disabled} onPick={(color) => onStyle({ background: color.toLowerCase() === \"#ffffff\" ? undefined : color })} />\n <ToolButton\n label={strings.clearFormat}\n disabled={disabled || !hasStyle}\n onClick={() => onStyle({ fontSize: undefined, color: undefined, background: undefined, fontWeight: undefined, textDecoration: undefined })}\n >\n <RemoveFormatIcon />\n </ToolButton>\n {children ? <ToolDivider /> : null}\n {children}\n <Box sx={{ flex: 1 }} />\n {end}\n </Box>\n );\n}\n", "/**\n * Tree data and edit operations shared by the outline and the mind map.\n *\n * The shape follows Mind Elixir's `nodeData` so data saved by it opens unchanged:\n * `{ id, topic, children?, expanded?, style? }` \u2014 unknown keys are carried along untouched.\n * Every operation is pure: it returns a new root (structural copy) and, when useful,\n * the id that should be focused next.\n */\n\n/** Text style of one node (same keys as Mind Elixir `style`). */\nexport interface TreeNodeStyle {\n fontSize?: string;\n color?: string;\n background?: string;\n fontWeight?: string;\n textDecoration?: string;\n}\n\nexport interface TreeNode {\n id: string;\n topic: string;\n children?: TreeNode[];\n /** `false` = folded. Missing means expanded. */\n expanded?: boolean;\n style?: TreeNodeStyle;\n [key: string]: unknown;\n}\n\nexport interface TreeEdit {\n root: TreeNode;\n /** Node to select / focus after the edit. */\n focusId?: string;\n}\n\nexport interface TreeRow {\n node: TreeNode;\n depth: number;\n parentId: string | null;\n}\n\n/** Short random id (hex, like Mind Elixir). */\nexport const newNodeId = () => Math.random().toString(16).slice(2, 18);\n\nexport const createTree = (topic: string): TreeNode => ({ id: \"root\", topic, children: [] });\n\nconst clone = (root: TreeNode): TreeNode => structuredClone(root) as TreeNode;\n\n/** Finds a node with its parent and index (in the given tree). */\nexport function locateNode(root: TreeNode, id: string): { node: TreeNode; parent: TreeNode | null; index: number } | null {\n if (root.id === id) return { node: root, parent: null, index: 0 };\n const stack: TreeNode[] = [root];\n while (stack.length) {\n const parent = stack.pop()!;\n const list = parent.children ?? [];\n for (let i = 0; i < list.length; i++) {\n if (list[i].id === id) return { node: list[i], parent, index: i };\n stack.push(list[i]);\n }\n }\n return null;\n}\n\n/** True when `id` is `node` itself or one of its descendants. */\nexport const containsNode = (node: TreeNode, id: string): boolean => node.id === id || (node.children ?? []).some((child) => containsNode(child, id));\n\nexport const isFolded = (node: TreeNode) => node.expanded === false && (node.children?.length ?? 0) > 0;\n\n/** Rows that are visible (folded subtrees hidden), in reading order. */\nexport function visibleRows(root: TreeNode): TreeRow[] {\n const rows: TreeRow[] = [];\n const walk = (node: TreeNode, depth: number, parentId: string | null) => {\n rows.push({ node, depth, parentId });\n if (node.expanded !== false) for (const child of node.children ?? []) walk(child, depth + 1, node.id);\n };\n walk(root, 0, null);\n return rows;\n}\n\n/** Runs `run` on a copy; returns null when nothing changed (run returned false). */\nfunction edit(root: TreeNode, run: (copy: TreeNode) => string | void | false): TreeEdit | null {\n const copy = clone(root);\n const focusId = run(copy);\n if (focusId === false) return null;\n return { root: copy, focusId: focusId || undefined };\n}\n\n/** New child at the end (or `first`). */\nexport const addChild = (root: TreeNode, id: string, topic = \"\", first = false) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at) return false;\n const fresh: TreeNode = { id: newNodeId(), topic };\n at.node.children = first ? [fresh, ...(at.node.children ?? [])] : [...(at.node.children ?? []), fresh];\n at.node.expanded = true;\n return fresh.id;\n });\n\n/** New sibling right after (root \u2192 first child). */\nexport const addSibling = (root: TreeNode, id: string, topic = \"\") =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at) return false;\n const fresh: TreeNode = { id: newNodeId(), topic };\n if (!at.parent) {\n at.node.children = [fresh, ...(at.node.children ?? [])];\n at.node.expanded = true;\n } else at.parent.children!.splice(at.index + 1, 0, fresh);\n return fresh.id;\n });\n\n/** Outline \"Enter\": first child when the row is an open parent, otherwise next sibling. */\nexport const addBelow = (root: TreeNode, id: string) => {\n const at = locateNode(root, id);\n if (at && (!at.parent || ((at.node.children?.length ?? 0) > 0 && at.node.expanded !== false))) return addChild(root, id, \"\", true);\n return addSibling(root, id);\n};\n\n/** Removes the node (not the root). Focus goes to the previous sibling, else the parent. */\nexport const removeNode = (root: TreeNode, id: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent) return false;\n at.parent.children!.splice(at.index, 1);\n return at.parent.children![at.index - 1]?.id ?? at.parent.id;\n });\n\n/** Becomes the last child of the previous sibling. */\nexport const indentNode = (root: TreeNode, id: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent || at.index === 0) return false;\n const prev = at.parent.children![at.index - 1];\n at.parent.children!.splice(at.index, 1);\n prev.children = [...(prev.children ?? []), at.node];\n prev.expanded = true;\n return id;\n });\n\n/** Becomes the sibling right after its parent. */\nexport const outdentNode = (root: TreeNode, id: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent) return false;\n const up = locateNode(copy, at.parent.id);\n if (!up?.parent) return false;\n at.parent.children!.splice(at.index, 1);\n up.parent.children!.splice(up.index + 1, 0, at.node);\n return id;\n });\n\n/** Moves one step among its siblings. */\nexport const shiftNode = (root: TreeNode, id: string, step: -1 | 1) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at?.parent) return false;\n const list = at.parent.children!;\n const to = at.index + step;\n if (to < 0 || to >= list.length) return false;\n [list[at.index], list[to]] = [list[to], list[at.index]];\n return id;\n });\n\n/** Moves `from` before / after `to`, or into `to` as its last child. */\nexport const moveNode = (root: TreeNode, from: string, to: string, place: \"before\" | \"after\" | \"child\") =>\n edit(root, (copy) => {\n const src = locateNode(copy, from);\n if (!src?.parent || containsNode(src.node, to)) return false;\n src.parent.children!.splice(src.index, 1);\n const dst = locateNode(copy, to);\n if (!dst) return false;\n if (place === \"child\" || !dst.parent) {\n dst.node.children = [...(dst.node.children ?? []), src.node];\n dst.node.expanded = true;\n } else dst.parent.children!.splice(dst.index + (place === \"after\" ? 1 : 0), 0, src.node);\n return from;\n });\n\nexport const setTopic = (root: TreeNode, id: string, topic: string) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at || at.node.topic === topic) return false;\n at.node.topic = topic;\n });\n\nexport const toggleFold = (root: TreeNode, id: string, expanded?: boolean) =>\n edit(root, (copy) => {\n const at = locateNode(copy, id);\n if (!at || !(at.node.children?.length ?? 0)) return false;\n at.node.expanded = expanded ?? at.node.expanded === false;\n });\n\n/** Merges a style patch; `undefined` values remove the key. */\nexport const setNodeStyle = (root: TreeNode, ids: string[], patch: Partial<TreeNodeStyle>) =>\n edit(root, (copy) => {\n let changed = false;\n for (const id of ids) {\n const at = locateNode(copy, id);\n if (!at) continue;\n const next: TreeNodeStyle = { ...(at.node.style ?? {}) };\n for (const [key, value] of Object.entries(patch) as [keyof TreeNodeStyle, string | undefined][]) {\n if (value === undefined || value === \"\") delete next[key];\n else next[key] = value;\n }\n at.node.style = Object.keys(next).length ? next : undefined;\n if (!at.node.style) delete at.node.style;\n changed = true;\n }\n return changed ? undefined : false;\n });\n\n/** `textDecoration` holds both underline and line-through. */\nexport function hasDecoration(style: TreeNodeStyle | undefined, kind: \"underline\" | \"line-through\") {\n return (style?.textDecoration ?? \"\").split(/\\s+/).includes(kind);\n}\nexport function toggleDecoration(style: TreeNodeStyle | undefined, kind: \"underline\" | \"line-through\"): string | undefined {\n const set = new Set((style?.textDecoration ?? \"\").split(/\\s+/).filter(Boolean));\n if (set.has(kind)) set.delete(kind);\n else set.add(kind);\n return set.size ? [...set].join(\" \") : undefined;\n}\n", "/**\n * Outline rows \u2014 one node per line, fold / unfold, indent / outdent, reorder, drag to move, per-node style.\n *\n * Keys: Enter new line \u00B7 Tab indent \u00B7 Shift+Tab outdent \u00B7 Alt+\u2191/\u2193 reorder \u00B7 \u2191/\u2193 previous / next row \u00B7\n * Backspace on an empty row deletes it \u00B7 Ctrl/\u2318+Z undo \u00B7 Ctrl/\u2318+Shift+Z or Ctrl+Y redo.\n * Dragging follows the pointer directly (native drag & drop doesn't fire drops reliably inside drawers / modals).\n */\nimport { useEffect, useRef, useState, type CSSProperties, type KeyboardEvent, type PointerEvent } from \"react\";\nimport { Box, IconButton } from \"@mui/material\";\nimport ArrowRightIcon from \"@mui/icons-material/ArrowRight\";\nimport ArrowDropDownIcon from \"@mui/icons-material/ArrowDropDown\";\nimport DragIndicatorIcon from \"@mui/icons-material/DragIndicator\";\nimport {\n addBelow,\n containsNode,\n indentNode,\n locateNode,\n moveNode,\n outdentNode,\n removeNode,\n setTopic,\n shiftNode,\n toggleFold,\n visibleRows,\n type TreeEdit,\n type TreeNode,\n type TreeNodeStyle,\n} from \"./tree\";\nimport type { OutlinerStrings } from \"./i18n\";\nimport { measureText } from \"./measure\";\n\nconst INDENT = 22;\nconst BASE_FONT = 15;\n\nexport interface OutlineListProps {\n root: TreeNode;\n /** A change; `mergeKey` is set for typing so history can merge it. */\n onEdit: (root: TreeNode, mergeKey?: string) => void;\n selectedId?: string | null;\n onSelect?: (id: string) => void;\n strings: OutlinerStrings;\n readOnly?: boolean;\n onUndo?: () => void;\n onRedo?: () => void;\n /** Extra space above the first row / below the last row (px). */\n paddingTop?: number;\n paddingBottom?: number;\n}\n\n/** Node style \u2192 input style (font size is clamped so the list stays a list). */\nexport function outlineTextStyle(style: TreeNodeStyle | undefined, depth: number): CSSProperties {\n const size = style?.fontSize ? Math.min(28, Math.max(12, parseFloat(style.fontSize))) : BASE_FONT;\n return {\n fontSize: size,\n fontWeight: style?.fontWeight ?? (depth === 0 ? 700 : 400),\n color: style?.color,\n textDecoration: style?.textDecoration,\n background: style?.background,\n };\n}\n\nexport function OutlineList({ root, onEdit, selectedId, onSelect, strings, readOnly, onUndo, onRedo, paddingTop = 8, paddingBottom = 40 }: OutlineListProps) {\n const inputs = useRef(new Map<string, HTMLInputElement>());\n const rowEls = useRef(new Map<string, HTMLDivElement>());\n const [focus, setFocus] = useState<string | null>(null);\n const [drop, setDrop] = useState<{ id: string; place: \"before\" | \"after\" } | null>(null);\n const [dragging, setDragging] = useState<string | null>(null);\n const rows = visibleRows(root);\n\n useEffect(() => {\n if (!focus) return;\n const el = inputs.current.get(focus);\n if (!el) return;\n el.focus();\n el.setSelectionRange(el.value.length, el.value.length);\n setFocus(null);\n }, [focus, root]);\n\n const apply = (result: TreeEdit | null, mergeKey?: string) => {\n if (!result) return;\n onEdit(result.root, mergeKey);\n if (result.focusId) {\n setFocus(result.focusId);\n onSelect?.(result.focusId);\n }\n };\n\n const onKey = (event: KeyboardEvent<HTMLInputElement>, index: number) => {\n if (event.nativeEvent.isComposing) return;\n const row = rows[index];\n const id = row.node.id;\n const mod = event.ctrlKey || event.metaKey;\n if (mod && (event.key === \"z\" || event.key === \"Z\")) {\n event.preventDefault();\n if (event.shiftKey) onRedo?.();\n else onUndo?.();\n return;\n }\n if (mod && (event.key === \"y\" || event.key === \"Y\")) {\n event.preventDefault();\n onRedo?.();\n return;\n }\n if (readOnly) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n apply(addBelow(root, id));\n } else if (event.key === \"Tab\") {\n event.preventDefault();\n apply(event.shiftKey ? outdentNode(root, id) : indentNode(root, id));\n } else if (event.altKey && (event.key === \"ArrowUp\" || event.key === \"ArrowDown\")) {\n event.preventDefault();\n apply(shiftNode(root, id, event.key === \"ArrowUp\" ? -1 : 1));\n } else if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n const next = rows[index + (event.key === \"ArrowUp\" ? -1 : 1)];\n if (next) {\n event.preventDefault();\n setFocus(next.node.id);\n onSelect?.(next.node.id);\n }\n } else if (event.key === \"Backspace\" && event.currentTarget.value === \"\" && row.depth > 0 && !(row.node.children?.length ?? 0)) {\n event.preventDefault();\n const result = removeNode(root, id);\n if (result) apply({ root: result.root, focusId: rows[index - 1]?.node.id });\n }\n };\n\n // Drag \u2014 follow the pointer on the handle; drop before / after the row under it (never into itself).\n const dragNode = dragging ? locateNode(root, dragging)?.node ?? null : null;\n const onHandleDown = (event: PointerEvent<HTMLDivElement>, id: string) => {\n if (event.button !== 0 || readOnly) return;\n event.preventDefault();\n event.currentTarget.setPointerCapture(event.pointerId);\n setDragging(id);\n setDrop(null);\n };\n const onHandleMove = (event: PointerEvent<HTMLDivElement>) => {\n if (!dragging || !dragNode) return;\n let next: { id: string; place: \"before\" | \"after\" } | null = null;\n for (const row of rows) {\n const el = rowEls.current.get(row.node.id);\n if (!el) continue;\n const r = el.getBoundingClientRect();\n if (event.clientY < r.top || event.clientY >= r.bottom) continue;\n if (!containsNode(dragNode, row.node.id)) next = { id: row.node.id, place: event.clientY < r.top + r.height / 2 ? \"before\" : \"after\" };\n break;\n }\n if (next?.id !== drop?.id || next?.place !== drop?.place) setDrop(next);\n };\n const onHandleUp = (event: PointerEvent<HTMLDivElement>) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);\n if (dragging && drop) apply(moveNode(root, dragging, drop.id, drop.place));\n setDragging(null);\n setDrop(null);\n };\n\n return (\n <Box sx={{ position: \"relative\", height: \"100%\", overflowY: \"auto\", bgcolor: \"#fff\", pt: `${paddingTop}px`, pb: `${paddingBottom}px`, px: 1.5 }}>\n {rows.map((row, index) => {\n const hasKids = (row.node.children?.length ?? 0) > 0;\n const folded = row.node.expanded === false;\n const line = drop?.id === row.node.id ? drop.place : null;\n const text = outlineTextStyle(row.node.style, row.depth);\n return (\n <Box\n key={row.node.id}\n ref={(el: HTMLDivElement | null) => {\n if (el) rowEls.current.set(row.node.id, el);\n else rowEls.current.delete(row.node.id);\n }}\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n minHeight: 26,\n pl: `${row.depth * INDENT}px`,\n borderRadius: 0.75,\n bgcolor: selectedId === row.node.id ? \"rgba(26,115,232,0.06)\" : \"transparent\",\n // Drop line \u2014 inset shadow so it takes no height.\n boxShadow: (theme) =>\n line === \"before\" ? `inset 0 2px 0 ${theme.palette.primary.main}` : line === \"after\" ? `inset 0 -2px 0 ${theme.palette.primary.main}` : \"none\",\n opacity: dragging === row.node.id ? 0.4 : 1,\n \"&:hover .ol-handle\": { visibility: \"visible\" },\n }}\n >\n <Box\n className=\"ol-handle\"\n title={strings.drag}\n onPointerDown={row.depth > 0 ? (event: PointerEvent<HTMLDivElement>) => onHandleDown(event, row.node.id) : undefined}\n onPointerMove={onHandleMove}\n onPointerUp={onHandleUp}\n onPointerCancel={onHandleUp}\n sx={{\n display: \"flex\",\n color: \"#b0b4ba\",\n cursor: row.depth > 0 && !readOnly ? (dragging ? \"grabbing\" : \"grab\") : \"default\",\n visibility: dragging === row.node.id ? \"visible\" : \"hidden\",\n width: 18,\n flexShrink: 0,\n touchAction: \"none\",\n }}\n >\n {row.depth > 0 && !readOnly ? <DragIndicatorIcon sx={{ fontSize: 16 }} /> : null}\n </Box>\n {hasKids ? (\n <IconButton\n size=\"small\"\n aria-label={folded ? strings.unfold : strings.fold}\n title={folded ? strings.unfold : strings.fold}\n onClick={() => apply(toggleFold(root, row.node.id))}\n sx={{ width: 22, height: 22, p: 0, color: \"text.secondary\" }}\n >\n {folded ? <ArrowRightIcon sx={{ fontSize: 20 }} /> : <ArrowDropDownIcon sx={{ fontSize: 20 }} />}\n </IconButton>\n ) : (\n <Box sx={{ width: 22, display: \"flex\", justifyContent: \"center\", flexShrink: 0 }}>\n <Box sx={{ width: 5, height: 5, borderRadius: \"50%\", bgcolor: \"#9aa0a6\" }} />\n </Box>\n )}\n <Box sx={{ flex: 1, minWidth: 0, display: \"flex\", ml: 0.5 }}>\n <input\n ref={(el) => {\n if (el) inputs.current.set(row.node.id, el);\n else inputs.current.delete(row.node.id);\n }}\n value={row.node.topic}\n readOnly={readOnly}\n placeholder={row.depth === 0 ? strings.root : \"\"}\n spellCheck={false}\n autoComplete=\"off\"\n onFocus={() => onSelect?.(row.node.id)}\n onChange={(event) => apply(setTopic(root, row.node.id, event.target.value), `topic:${row.node.id}`)}\n onKeyDown={(event) => onKey(event, index)}\n style={{\n // Background hugs the text like a highlighter; the input keeps full width for clicks.\n width: text.background ? Math.ceil(measureText(row.node.topic || \" \", Number(text.fontSize), text.fontWeight as string)) + 16 : \"100%\",\n maxWidth: \"100%\",\n border: 0,\n outline: 0,\n font: \"inherit\",\n padding: text.background ? \"2px 6px\" : \"2px 0\",\n borderRadius: 4,\n background: text.background ?? \"transparent\",\n color: text.color ?? \"inherit\",\n fontSize: text.fontSize,\n fontWeight: text.fontWeight,\n textDecoration: [text.textDecoration, folded && hasKids ? \"underline dotted #9aa0a6\" : \"\"].filter(Boolean).join(\" \") || undefined,\n }}\n />\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n}\n", "/** Text width with a cached canvas (used for highlight width in the outline and node sizes in the map). */\nlet ctx: CanvasRenderingContext2D | null = null;\nconst cache = new Map<string, number>();\n\nexport const DEFAULT_FONT_FAMILY = \"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif\";\n\nexport function measureText(text: string, fontSize: number, fontWeight: string | number = 400, fontFamily = DEFAULT_FONT_FAMILY): number {\n const font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n const key = `${font}|${text}`;\n const hit = cache.get(key);\n if (hit !== undefined) return hit;\n if (!ctx && typeof document !== \"undefined\") ctx = document.createElement(\"canvas\").getContext(\"2d\");\n // No canvas (SSR / tests) \u2014 rough guess: wide glyphs 1em, others 0.55em.\n let width = 0;\n if (ctx) {\n ctx.font = font;\n width = ctx.measureText(text).width;\n } else for (const ch of text) width += (/[\u1100-\uFFDC]/.test(ch) ? 1 : 0.55) * fontSize;\n if (cache.size > 5000) cache.clear();\n cache.set(key, width);\n return width;\n}\n", "/**\n * Undo / redo for a controlled tree.\n * Typing into one node within `mergeMs` is merged into one step, so undo doesn't go back one letter at a time.\n */\nimport { useCallback, useRef, useState } from \"react\";\nimport type { TreeNode } from \"./tree\";\n\nexport interface TreeHistory {\n canUndo: boolean;\n canRedo: boolean;\n /** Records the tree before a change. `mergeKey` merges consecutive changes with the same key (e.g. typing in one node). */\n record: (before: TreeNode, mergeKey?: string) => void;\n undo: (current: TreeNode) => TreeNode | null;\n redo: (current: TreeNode) => TreeNode | null;\n clear: () => void;\n}\n\nexport function useTreeHistory(limit = 100, mergeMs = 1200): TreeHistory {\n const past = useRef<TreeNode[]>([]);\n const future = useRef<TreeNode[]>([]);\n const last = useRef<{ key: string; at: number } | null>(null);\n const [, bump] = useState(0);\n const refresh = () => bump((n) => n + 1);\n\n const record = useCallback(\n (before: TreeNode, mergeKey?: string) => {\n const now = Date.now();\n const merge = mergeKey && last.current?.key === mergeKey && now - last.current.at < mergeMs;\n last.current = mergeKey ? { key: mergeKey, at: now } : null;\n future.current = [];\n if (!merge) {\n past.current.push(before);\n if (past.current.length > limit) past.current.shift();\n }\n refresh();\n },\n [limit, mergeMs]\n );\n const undo = useCallback((current: TreeNode) => {\n const prev = past.current.pop();\n if (!prev) return null;\n future.current.push(current);\n last.current = null;\n refresh();\n return prev;\n }, []);\n const redo = useCallback((current: TreeNode) => {\n const next = future.current.pop();\n if (!next) return null;\n past.current.push(current);\n last.current = null;\n refresh();\n return next;\n }, []);\n const clear = useCallback(() => {\n past.current = [];\n future.current = [];\n last.current = null;\n refresh();\n }, []);\n return { canUndo: past.current.length > 0, canRedo: future.current.length > 0, record, undo, redo, clear };\n}\n", "/** UI strings (ko \u00B7 en \u00B7 ja \u00B7 zh \u00B7 de \u00B7 fr \u00B7 it \u00B7 vi). Unknown locale \u2192 English. */\nconst STRINGS = {\n ko: {\n clearFormat: \"\uC11C\uC2DD \uC9C0\uC6B0\uAE30\",\n fontSize: \"\uAE00\uC790 \uD06C\uAE30\",\n sizeDefault: \"\uAE30\uBCF8\",\n bold: \"\uC9C4\uD558\uAC8C\",\n underline: \"\uBC11\uC904\",\n strike: \"\uCDE8\uC18C\uC120\",\n textColor: \"\uAE00\uC790\uC0C9\",\n background: \"\uBC30\uACBD\uC0C9\",\n noColor: \"\uC5C6\uC74C\",\n undo: \"\uB418\uB3CC\uB9AC\uAE30\",\n redo: \"\uB2E4\uC2DC \uD558\uAE30\",\n fold: \"\uC811\uAE30\",\n unfold: \"\uD3BC\uCE58\uAE30\",\n drag: \"\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30\",\n root: \"\uC8FC\uC81C\",\n hint: \"Enter: \uC0C8 \uC904 \u00B7 Tab / Shift+Tab: \uB4E4\uC5EC\uC4F0\uAE30 / \uB0B4\uC5B4\uC4F0\uAE30 \u00B7 Alt+\u2191\u2193: \uC21C\uC11C \u00B7 \uC904 \uC55E \uC190\uC7A1\uC774 \uB04C\uAE30: \uC62E\uAE30\uAE30 \u00B7 \uBE48 \uC904 Backspace: \uC9C0\uC6B0\uAE30\",\n },\n en: {\n clearFormat: \"Clear format\",\n fontSize: \"Font size\",\n sizeDefault: \"Default\",\n bold: \"Bold\",\n underline: \"Underline\",\n strike: \"Strikethrough\",\n textColor: \"Text color\",\n background: \"Background\",\n noColor: \"None\",\n undo: \"Undo\",\n redo: \"Redo\",\n fold: \"Collapse\",\n unfold: \"Expand\",\n drag: \"Drag to move\",\n root: \"Topic\",\n hint: \"Enter: new line \u00B7 Tab / Shift+Tab: indent / outdent \u00B7 Alt+\u2191\u2193: reorder \u00B7 Drag the row handle: move \u00B7 Backspace on empty: delete\",\n },\n ja: {\n clearFormat: \"\u66F8\u5F0F\u3092\u30AF\u30EA\u30A2\",\n fontSize: \"\u6587\u5B57\u30B5\u30A4\u30BA\",\n sizeDefault: \"\u6A19\u6E96\",\n bold: \"\u592A\u5B57\",\n underline: \"\u4E0B\u7DDA\",\n strike: \"\u53D6\u308A\u6D88\u3057\u7DDA\",\n textColor: \"\u6587\u5B57\u8272\",\n background: \"\u80CC\u666F\u8272\",\n noColor: \"\u306A\u3057\",\n undo: \"\u5143\u306B\u623B\u3059\",\n redo: \"\u3084\u308A\u76F4\u3059\",\n fold: \"\u6298\u308A\u305F\u305F\u3080\",\n unfold: \"\u5C55\u958B\",\n drag: \"\u30C9\u30E9\u30C3\u30B0\u3067\u79FB\u52D5\",\n root: \"\u30C6\u30FC\u30DE\",\n hint: \"Enter: \u65B0\u3057\u3044\u884C \u00B7 Tab / Shift+Tab: \u5B57\u4E0B\u3052 / \u5B57\u4E0A\u3052 \u00B7 Alt+\u2191\u2193: \u4E26\u3079\u66FF\u3048 \u00B7 \u884C\u982D\u306E\u30CF\u30F3\u30C9\u30EB\u3092\u30C9\u30E9\u30C3\u30B0: \u79FB\u52D5 \u00B7 \u7A7A\u884C\u3067 Backspace: \u524A\u9664\",\n },\n zh: {\n clearFormat: \"\u6E05\u9664\u683C\u5F0F\",\n fontSize: \"\u5B57\u53F7\",\n sizeDefault: \"\u9ED8\u8BA4\",\n bold: \"\u52A0\u7C97\",\n underline: \"\u4E0B\u5212\u7EBF\",\n strike: \"\u5220\u9664\u7EBF\",\n textColor: \"\u6587\u5B57\u989C\u8272\",\n background: \"\u80CC\u666F\u8272\",\n noColor: \"\u65E0\",\n undo: \"\u64A4\u9500\",\n redo: \"\u91CD\u505A\",\n fold: \"\u6298\u53E0\",\n unfold: \"\u5C55\u5F00\",\n drag: \"\u62D6\u52A8\u4EE5\u79FB\u52A8\",\n root: \"\u4E3B\u9898\",\n hint: \"Enter: \u65B0\u884C \u00B7 Tab / Shift+Tab: \u7F29\u8FDB / \u53D6\u6D88\u7F29\u8FDB \u00B7 Alt+\u2191\u2193: \u987A\u5E8F \u00B7 \u62D6\u52A8\u884C\u9996\u624B\u67C4: \u79FB\u52A8 \u00B7 \u7A7A\u884C Backspace: \u5220\u9664\",\n },\n de: {\n clearFormat: \"Formatierung l\u00F6schen\",\n fontSize: \"Schriftgr\u00F6\u00DFe\",\n sizeDefault: \"Standard\",\n bold: \"Fett\",\n underline: \"Unterstrichen\",\n strike: \"Durchgestrichen\",\n textColor: \"Textfarbe\",\n background: \"Hintergrund\",\n noColor: \"Keine\",\n undo: \"R\u00FCckg\u00E4ngig\",\n redo: \"Wiederholen\",\n fold: \"Einklappen\",\n unfold: \"Ausklappen\",\n drag: \"Zum Verschieben ziehen\",\n root: \"Thema\",\n hint: \"Enter: neue Zeile \u00B7 Tab / Shift+Tab: ein- / ausr\u00FCcken \u00B7 Alt+\u2191\u2193: Reihenfolge \u00B7 Griff ziehen: verschieben \u00B7 Backspace leer: l\u00F6schen\",\n },\n fr: {\n clearFormat: \"Effacer la mise en forme\",\n fontSize: \"Taille\",\n sizeDefault: \"Par d\u00E9faut\",\n bold: \"Gras\",\n underline: \"Soulign\u00E9\",\n strike: \"Barr\u00E9\",\n textColor: \"Couleur du texte\",\n background: \"Arri\u00E8re-plan\",\n noColor: \"Aucune\",\n undo: \"Annuler\",\n redo: \"R\u00E9tablir\",\n fold: \"Replier\",\n unfold: \"D\u00E9plier\",\n drag: \"Glisser pour d\u00E9placer\",\n root: \"Sujet\",\n hint: \"Entr\u00E9e : nouvelle ligne \u00B7 Tab / Maj+Tab : indenter / d\u00E9sindenter \u00B7 Alt+\u2191\u2193 : ordre \u00B7 Glisser la poign\u00E9e : d\u00E9placer \u00B7 Retour arri\u00E8re vide : supprimer\",\n },\n it: {\n clearFormat: \"Cancella formattazione\",\n fontSize: \"Dimensione\",\n sizeDefault: \"Predefinita\",\n bold: \"Grassetto\",\n underline: \"Sottolineato\",\n strike: \"Barrato\",\n textColor: \"Colore testo\",\n background: \"Sfondo\",\n noColor: \"Nessuno\",\n undo: \"Annulla\",\n redo: \"Ripeti\",\n fold: \"Comprimi\",\n unfold: \"Espandi\",\n drag: \"Trascina per spostare\",\n root: \"Argomento\",\n hint: \"Invio: nuova riga \u00B7 Tab / Maiusc+Tab: rientro / riduci \u00B7 Alt+\u2191\u2193: ordine \u00B7 Trascina la maniglia: sposta \u00B7 Backspace su vuota: elimina\",\n },\n vi: {\n clearFormat: \"X\u00F3a \u0111\u1ECBnh d\u1EA1ng\",\n fontSize: \"C\u1EE1 ch\u1EEF\",\n sizeDefault: \"M\u1EB7c \u0111\u1ECBnh\",\n bold: \"\u0110\u1EADm\",\n underline: \"G\u1EA1ch ch\u00E2n\",\n strike: \"G\u1EA1ch ngang\",\n textColor: \"M\u00E0u ch\u1EEF\",\n background: \"M\u00E0u n\u1EC1n\",\n noColor: \"Kh\u00F4ng\",\n undo: \"Ho\u00E0n t\u00E1c\",\n redo: \"L\u00E0m l\u1EA1i\",\n fold: \"Thu g\u1ECDn\",\n unfold: \"M\u1EDF r\u1ED9ng\",\n drag: \"K\u00E9o \u0111\u1EC3 di chuy\u1EC3n\",\n root: \"Ch\u1EE7 \u0111\u1EC1\",\n hint: \"Enter: d\u00F2ng m\u1EDBi \u00B7 Tab / Shift+Tab: th\u1EE5t v\u00E0o / ra \u00B7 Alt+\u2191\u2193: th\u1EE9 t\u1EF1 \u00B7 K\u00E9o tay c\u1EA7m: di chuy\u1EC3n \u00B7 Backspace d\u00F2ng tr\u1ED1ng: x\u00F3a\",\n },\n} as const;\n\nexport type OutlinerStrings = { [K in keyof (typeof STRINGS)[\"en\"]]: string };\n\n/** Strings for a locale (\"ko\", \"ko-KR\", \u2026). `overrides` replaces single entries. */\nexport function outlinerStrings(locale?: string, overrides?: Partial<OutlinerStrings>): OutlinerStrings {\n const lang = (locale ?? \"en\").slice(0, 2).toLowerCase() as keyof typeof STRINGS;\n return { ...(STRINGS[lang] ?? STRINGS.en), ...overrides };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["index_exports", "__export", "DEFAULT_FONT_FAMILY", "FONT_SIZES", "FormatToolbar", "OutlineList", "Outliner", "ToolButton", "ToolDivider", "ToolTip", "addBelow", "addChild", "addSibling", "containsNode", "createTree", "hasDecoration", "indentNode", "isFolded", "locateNode", "measureText", "moveNode", "newNodeId", "outdentNode", "outlineTextStyle", "outlinerStrings", "removeNode", "setNodeStyle", "setTopic", "shiftNode", "toggleDecoration", "toggleFold", "useTreeHistory", "visibleRows", "__toCommonJS", "import_react", "import_material", "import_react", "import_material", "import_mui_color_picker", "import_toolbar_icons", "newNodeId", "createTree", "topic", "clone", "root", "locateNode", "id", "stack", "parent", "list", "i", "containsNode", "node", "child", "isFolded", "visibleRows", "rows", "walk", "depth", "parentId", "edit", "run", "copy", "focusId", "addChild", "first", "at", "fresh", "addSibling", "addBelow", "removeNode", "indentNode", "prev", "outdentNode", "up", "shiftNode", "step", "to", "moveNode", "from", "place", "src", "dst", "setTopic", "toggleFold", "expanded", "setNodeStyle", "ids", "patch", "changed", "next", "key", "value", "hasDecoration", "style", "kind", "toggleDecoration", "set", "import_jsx_runtime", "FONT_SIZES", "ToolTip", "title", "children", "ToolButton", "label", "active", "disabled", "onClick", "event", "ToolDivider", "ColorButton", "type", "value", "onPick", "anchor", "setAnchor", "shown", "hex", "FormatToolbar", "strings", "style", "onStyle", "canUndo", "canRedo", "onUndo", "onRedo", "start", "end", "readOnly", "sizeAnchor", "setSizeAnchor", "size", "bold", "hasStyle", "hasDecoration", "toggleDecoration", "color", "import_react", "import_material", "import_ArrowRight", "import_ArrowDropDown", "import_DragIndicator", "ctx", "cache", "DEFAULT_FONT_FAMILY", "measureText", "text", "fontSize", "fontWeight", "fontFamily", "font", "key", "hit", "width", "ch", "import_jsx_runtime", "INDENT", "BASE_FONT", "outlineTextStyle", "style", "depth", "OutlineList", "root", "onEdit", "selectedId", "onSelect", "strings", "readOnly", "onUndo", "onRedo", "paddingTop", "paddingBottom", "inputs", "rowEls", "focus", "setFocus", "drop", "setDrop", "dragging", "setDragging", "rows", "visibleRows", "el", "apply", "result", "mergeKey", "onKey", "event", "index", "row", "id", "mod", "addBelow", "outdentNode", "indentNode", "shiftNode", "next", "removeNode", "dragNode", "locateNode", "onHandleDown", "onHandleMove", "r", "containsNode", "onHandleUp", "moveNode", "hasKids", "folded", "line", "text", "theme", "DragIndicatorIcon", "toggleFold", "ArrowRightIcon", "ArrowDropDownIcon", "setTopic", "measureText", "import_react", "useTreeHistory", "limit", "mergeMs", "past", "future", "last", "bump", "refresh", "n", "record", "before", "mergeKey", "now", "merge", "undo", "current", "prev", "redo", "next", "clear", "STRINGS", "outlinerStrings", "locale", "overrides", "lang", "import_jsx_runtime", "Outliner", "value", "onChange", "locale", "overrides", "readOnly", "toolbar", "toolbarStart", "toolbarEnd", "height", "strings", "outlinerStrings", "history", "useTreeHistory", "selected", "setSelected", "commit", "next", "mergeKey", "undo", "prev", "redo", "selectedNode", "locateNode", "FormatToolbar", "patch", "result", "setNodeStyle", "OutlineList"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const DEFAULT_FONT_FAMILY = "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif";
|
|
2
|
+
export declare function measureText(text: string, fontSize: number, fontWeight?: string | number, fontFamily?: string): number;
|
package/dist/tree.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree data and edit operations shared by the outline and the mind map.
|
|
3
|
+
*
|
|
4
|
+
* The shape follows Mind Elixir's `nodeData` so data saved by it opens unchanged:
|
|
5
|
+
* `{ id, topic, children?, expanded?, style? }` — unknown keys are carried along untouched.
|
|
6
|
+
* Every operation is pure: it returns a new root (structural copy) and, when useful,
|
|
7
|
+
* the id that should be focused next.
|
|
8
|
+
*/
|
|
9
|
+
/** Text style of one node (same keys as Mind Elixir `style`). */
|
|
10
|
+
export interface TreeNodeStyle {
|
|
11
|
+
fontSize?: string;
|
|
12
|
+
color?: string;
|
|
13
|
+
background?: string;
|
|
14
|
+
fontWeight?: string;
|
|
15
|
+
textDecoration?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface TreeNode {
|
|
18
|
+
id: string;
|
|
19
|
+
topic: string;
|
|
20
|
+
children?: TreeNode[];
|
|
21
|
+
/** `false` = folded. Missing means expanded. */
|
|
22
|
+
expanded?: boolean;
|
|
23
|
+
style?: TreeNodeStyle;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
export interface TreeEdit {
|
|
27
|
+
root: TreeNode;
|
|
28
|
+
/** Node to select / focus after the edit. */
|
|
29
|
+
focusId?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface TreeRow {
|
|
32
|
+
node: TreeNode;
|
|
33
|
+
depth: number;
|
|
34
|
+
parentId: string | null;
|
|
35
|
+
}
|
|
36
|
+
/** Short random id (hex, like Mind Elixir). */
|
|
37
|
+
export declare const newNodeId: () => string;
|
|
38
|
+
export declare const createTree: (topic: string) => TreeNode;
|
|
39
|
+
/** Finds a node with its parent and index (in the given tree). */
|
|
40
|
+
export declare function locateNode(root: TreeNode, id: string): {
|
|
41
|
+
node: TreeNode;
|
|
42
|
+
parent: TreeNode | null;
|
|
43
|
+
index: number;
|
|
44
|
+
} | null;
|
|
45
|
+
/** True when `id` is `node` itself or one of its descendants. */
|
|
46
|
+
export declare const containsNode: (node: TreeNode, id: string) => boolean;
|
|
47
|
+
export declare const isFolded: (node: TreeNode) => boolean;
|
|
48
|
+
/** Rows that are visible (folded subtrees hidden), in reading order. */
|
|
49
|
+
export declare function visibleRows(root: TreeNode): TreeRow[];
|
|
50
|
+
/** New child at the end (or `first`). */
|
|
51
|
+
export declare const addChild: (root: TreeNode, id: string, topic?: string, first?: boolean) => TreeEdit | null;
|
|
52
|
+
/** New sibling right after (root → first child). */
|
|
53
|
+
export declare const addSibling: (root: TreeNode, id: string, topic?: string) => TreeEdit | null;
|
|
54
|
+
/** Outline "Enter": first child when the row is an open parent, otherwise next sibling. */
|
|
55
|
+
export declare const addBelow: (root: TreeNode, id: string) => TreeEdit | null;
|
|
56
|
+
/** Removes the node (not the root). Focus goes to the previous sibling, else the parent. */
|
|
57
|
+
export declare const removeNode: (root: TreeNode, id: string) => TreeEdit | null;
|
|
58
|
+
/** Becomes the last child of the previous sibling. */
|
|
59
|
+
export declare const indentNode: (root: TreeNode, id: string) => TreeEdit | null;
|
|
60
|
+
/** Becomes the sibling right after its parent. */
|
|
61
|
+
export declare const outdentNode: (root: TreeNode, id: string) => TreeEdit | null;
|
|
62
|
+
/** Moves one step among its siblings. */
|
|
63
|
+
export declare const shiftNode: (root: TreeNode, id: string, step: -1 | 1) => TreeEdit | null;
|
|
64
|
+
/** Moves `from` before / after `to`, or into `to` as its last child. */
|
|
65
|
+
export declare const moveNode: (root: TreeNode, from: string, to: string, place: "before" | "after" | "child") => TreeEdit | null;
|
|
66
|
+
export declare const setTopic: (root: TreeNode, id: string, topic: string) => TreeEdit | null;
|
|
67
|
+
export declare const toggleFold: (root: TreeNode, id: string, expanded?: boolean) => TreeEdit | null;
|
|
68
|
+
/** Merges a style patch; `undefined` values remove the key. */
|
|
69
|
+
export declare const setNodeStyle: (root: TreeNode, ids: string[], patch: Partial<TreeNodeStyle>) => TreeEdit | null;
|
|
70
|
+
/** `textDecoration` holds both underline and line-through. */
|
|
71
|
+
export declare function hasDecoration(style: TreeNodeStyle | undefined, kind: "underline" | "line-through"): boolean;
|
|
72
|
+
export declare function toggleDecoration(style: TreeNodeStyle | undefined, kind: "underline" | "line-through"): string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
"name": "@ehfuse/outliner",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Outliner for React + MUI — one row per node, fold, indent / outdent, drag to reorder, formatting toolbar (size · color · background · bold · underline · strike), undo",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.esm.js",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "node build.js",
|
|
21
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"react",
|
|
26
|
+
"outliner",
|
|
27
|
+
"outline",
|
|
28
|
+
"tree",
|
|
29
|
+
"list",
|
|
30
|
+
"mui",
|
|
31
|
+
"material-ui"
|
|
32
|
+
],
|
|
33
|
+
"author": "김영진 (Kim Young Jin) <ehfuse@gmail.com>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/ehfuse/mindmap.git",
|
|
38
|
+
"directory": "packages/outliner"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@emotion/react": "^11.0.0",
|
|
45
|
+
"@emotion/styled": "^11.0.0",
|
|
46
|
+
"@mui/icons-material": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
47
|
+
"@mui/material": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
48
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
49
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@emotion/react": "^11.13.3",
|
|
53
|
+
"@emotion/styled": "^11.13.0",
|
|
54
|
+
"@mui/icons-material": "^7.3.5",
|
|
55
|
+
"@mui/material": "^7.3.5",
|
|
56
|
+
"@types/react": "^19.0.0",
|
|
57
|
+
"@types/react-dom": "^19.0.0",
|
|
58
|
+
"esbuild": "^0.24.0",
|
|
59
|
+
"react": "^19.0.0",
|
|
60
|
+
"react-dom": "^19.0.0",
|
|
61
|
+
"typescript": "^5.6.3"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@ehfuse/mui-color-picker": "^1.5.4",
|
|
65
|
+
"@ehfuse/toolbar-icons": "^0.1.0"
|
|
66
|
+
}
|
|
6
67
|
}
|