@xom11/whiteboard 0.2.1 → 0.6.1
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/README.md +14 -7
- package/dist/{ExcalidrawWithMenus-YGFFNZYY.mjs → ExcalidrawWithMenus-KBLDWPM2.mjs} +2 -2
- package/dist/ExcalidrawWithMenus-KBLDWPM2.mjs.map +1 -0
- package/dist/index.css +29 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +194 -11
- package/dist/index.d.ts +194 -11
- package/dist/index.js +4978 -1715
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4970 -1718
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -27
- package/dist/ExcalidrawWithMenus-YGFFNZYY.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -4,11 +4,14 @@ Excalidraw-based whiteboard component dùng cho HocTotBachKhoa classroom: bút/s
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
+
Cài qua git URL (repo public, không cần npm publish):
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
# pin tag (recommended)
|
|
11
|
+
npm install github:xom11/whiteboard#v0.2.0
|
|
12
|
+
|
|
13
|
+
# hoặc trong package.json
|
|
14
|
+
"@xom11/whiteboard": "github:xom11/whiteboard#v0.2.0"
|
|
12
15
|
```
|
|
13
16
|
|
|
14
17
|
Peer deps: `react >=18`, `react-dom >=18`, `next >=14`.
|
|
@@ -43,13 +46,17 @@ npm run dev # tsup watch mode
|
|
|
43
46
|
|
|
44
47
|
## Workflow phát hành phiên bản mới
|
|
45
48
|
|
|
49
|
+
`npm ci --ignore-scripts` ở consumer skip prepare hook → phải commit `dist/`:
|
|
50
|
+
|
|
46
51
|
```bash
|
|
47
|
-
npm
|
|
52
|
+
npm run build
|
|
53
|
+
git add dist/
|
|
54
|
+
git commit -am "release vX.Y.Z"
|
|
55
|
+
npm version patch # bump package.json + tạo tag
|
|
48
56
|
git push --follow-tags
|
|
49
|
-
npm publish # prepublishOnly tự chạy typecheck + build
|
|
50
57
|
```
|
|
51
58
|
|
|
52
|
-
|
|
59
|
+
Consumer pin tag mới trong `package.json` rồi `npm install`.
|
|
53
60
|
|
|
54
61
|
## Architecture
|
|
55
62
|
|
|
@@ -18,5 +18,5 @@ function ExcalidrawWithMenus(props) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export { ExcalidrawWithMenus };
|
|
21
|
-
//# sourceMappingURL=ExcalidrawWithMenus-
|
|
22
|
-
//# sourceMappingURL=ExcalidrawWithMenus-
|
|
21
|
+
//# sourceMappingURL=ExcalidrawWithMenus-KBLDWPM2.mjs.map
|
|
22
|
+
//# sourceMappingURL=ExcalidrawWithMenus-KBLDWPM2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ExcalidrawWithMenus.tsx"],"names":[],"mappings":";;;AAiBO,SAAS,oBAAoB,KAAA,EAAwB;AAC1D,EAAA,MAAM,EAAE,QAAA,EAAU,GAAG,IAAA,EAAK,GAAI,KAAA;AAC9B,EAAA,uBACE,IAAA,CAAC,UAAA,EAAA,EAAY,GAAG,IAAA,EAEd,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,QAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,SAAA,EAAtB,EAAgC,CAAA;AAAA,sBACjC,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,WAAA,EAAtB,EAAkC,CAAA;AAAA,sBACnC,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,WAAA,EAAtB,EAAkC,CAAA;AAAA,sBACnC,GAAA,CAAC,QAAA,CAAS,YAAA,CAAa,WAAA,EAAtB,EAAkC;AAAA,KAAA,EACrC,CAAA;AAAA,oBAEA,GAAA,CAAC,MAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EACR,CAAA;AAAA,oBAGA,GAAA,CAAC,aAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EACR,CAAA;AAAA,IACC;AAAA,GAAA,EACH,CAAA;AAEJ","file":"ExcalidrawWithMenus-KBLDWPM2.mjs","sourcesContent":["'use client';\n\n// Client-only wrapper around Excalidraw that lets us reach for static helpers\n// like `MainMenu.DefaultItems.*`. Whiteboard dynamic-imports this\n// file so SSR never evaluates @excalidraw/excalidraw, while inside this file we\n// can use plain static imports (the entire module loads on the client).\n\nimport React from 'react';\nimport {\n Excalidraw,\n MainMenu,\n Footer,\n WelcomeScreen,\n} from '@excalidraw/excalidraw';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ExcalidrawProps = any;\n\nexport function ExcalidrawWithMenus(props: ExcalidrawProps) {\n const { children, ...rest } = props;\n return (\n <Excalidraw {...rest}>\n {/* Replace default menu with curated items — no socials/help/branding */}\n <MainMenu>\n <MainMenu.DefaultItems.LoadScene />\n <MainMenu.DefaultItems.SaveAsImage />\n <MainMenu.DefaultItems.ClearCanvas />\n <MainMenu.DefaultItems.ToggleTheme />\n </MainMenu>\n {/* Footer slot with no content suppresses default \"Made with Excalidraw\" link */}\n <Footer>\n <span />\n </Footer>\n {/* WelcomeScreen slot (empty) prevents the default Excalidraw welcome panel\n * (which includes the Excalidraw logo and social links) from appearing. */}\n <WelcomeScreen>\n <span />\n </WelcomeScreen>\n {children}\n </Excalidraw>\n );\n}\n"]}
|
package/dist/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* src/
|
|
1
|
+
/* src/stamps/shared/stamp.css */
|
|
2
2
|
.theme--dark [data-stamp-area=true] {
|
|
3
3
|
color: rgb(226 232 240);
|
|
4
4
|
}
|
|
@@ -71,4 +71,32 @@
|
|
|
71
71
|
border-color: rgb(71 85 105);
|
|
72
72
|
color: rgb(226 232 240);
|
|
73
73
|
}
|
|
74
|
+
.theme--dark [data-stamp-area=true] .bg-emerald-100 {
|
|
75
|
+
background-color: rgb(6 78 59);
|
|
76
|
+
}
|
|
77
|
+
.theme--dark [data-stamp-area=true] .border-emerald-500 {
|
|
78
|
+
border-color: rgb(52 211 153);
|
|
79
|
+
}
|
|
80
|
+
.theme--dark [data-stamp-area=true] .ring-emerald-300 {
|
|
81
|
+
--tw-ring-color: rgb(110 231 183);
|
|
82
|
+
}
|
|
83
|
+
.theme--dark [data-stamp-area=true] .border-rose-300 {
|
|
84
|
+
border-color: rgb(190 18 60);
|
|
85
|
+
}
|
|
86
|
+
.theme--dark [data-stamp-area=true] .hover\:bg-rose-100:hover {
|
|
87
|
+
background-color: rgb(127 29 29);
|
|
88
|
+
}
|
|
89
|
+
.theme--dark [data-stamp-area=true] .bg-gradient-to-r.from-emerald-600 {
|
|
90
|
+
background-image:
|
|
91
|
+
linear-gradient(
|
|
92
|
+
to right,
|
|
93
|
+
rgb(6 95 70),
|
|
94
|
+
rgb(15 76 76));
|
|
95
|
+
}
|
|
96
|
+
.theme--dark [data-stamp-area=true] .bg-emerald-600 {
|
|
97
|
+
background-color: rgb(6 95 70);
|
|
98
|
+
}
|
|
99
|
+
.theme--dark [data-stamp-area=true] .hover\:bg-emerald-700:hover {
|
|
100
|
+
background-color: rgb(4 120 87);
|
|
101
|
+
}
|
|
74
102
|
/*# sourceMappingURL=index.css.map */
|
package/dist/index.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/stamps/shared/stamp.css"],"sourcesContent":["/* Dark-mode overrides cho math-stamp panels.\n * Active khi outer wrapper có class `theme--dark` (mirrored từ Excalidraw appState).\n * Selector `[data-stamp-area=\"true\"]` đã có sẵn trên mọi panel để dùng cho click-outside detection. */\n\n.theme--dark [data-stamp-area=\"true\"] {\n color: rgb(226 232 240);\n}\n\n/* ---- Backgrounds ---- */\n.theme--dark [data-stamp-area=\"true\"].bg-white,\n.theme--dark [data-stamp-area=\"true\"] .bg-white {\n background-color: rgb(30 41 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-slate-50 {\n background-color: rgb(15 23 42);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-emerald-50 {\n background-color: rgb(6 78 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-rose-50 {\n background-color: rgb(127 29 29);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-slate-100:hover {\n background-color: rgb(51 65 85);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-slate-50:hover {\n background-color: rgb(15 23 42);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-emerald-50:hover {\n background-color: rgb(6 78 59);\n}\n\n/* Left-panel header có gradient sáng — đổi sang slate */\n.theme--dark [data-testid=\"stamp-left-panel\"] > header.bg-gradient-to-r {\n background-image: linear-gradient(to right, rgb(15 23 42), rgb(30 41 59));\n}\n\n/* ---- Borders ---- */\n.theme--dark [data-stamp-area=\"true\"] .border-slate-200,\n.theme--dark [data-stamp-area=\"true\"] .border-slate-300 {\n border-color: rgb(51 65 85);\n}\n\n/* ---- Text ---- */\n.theme--dark [data-stamp-area=\"true\"] .text-slate-400 {\n color: rgb(100 116 139);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-slate-500 {\n color: rgb(148 163 184);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-slate-600,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-700,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-800,\n.theme--dark [data-stamp-area=\"true\"] .text-slate-900 {\n color: rgb(226 232 240);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:text-slate-800:hover,\n.theme--dark [data-stamp-area=\"true\"] .hover\\:text-slate-900:hover {\n color: rgb(241 245 249);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-emerald-700 {\n color: rgb(110 231 183);\n}\n.theme--dark [data-stamp-area=\"true\"] .text-rose-700 {\n color: rgb(253 164 175);\n}\n\n/* ---- Inputs ---- */\n.theme--dark [data-stamp-area=\"true\"] input[type=\"text\"],\n.theme--dark [data-stamp-area=\"true\"] textarea {\n background-color: rgb(15 23 42);\n color: rgb(241 245 249);\n}\n.theme--dark [data-stamp-area=\"true\"] input::placeholder,\n.theme--dark [data-stamp-area=\"true\"] textarea::placeholder {\n color: rgb(100 116 139);\n}\n\n/* ---- Kbd badge ---- */\n.theme--dark [data-stamp-area=\"true\"] kbd {\n background-color: rgb(15 23 42);\n border-color: rgb(71 85 105);\n color: rgb(226 232 240);\n}\n\n/* --- Popover & swatch active states (Task 13) --- */\n.theme--dark [data-stamp-area=\"true\"] .bg-emerald-100 {\n background-color: rgb(6 78 59);\n}\n.theme--dark [data-stamp-area=\"true\"] .border-emerald-500 {\n border-color: rgb(52 211 153);\n}\n.theme--dark [data-stamp-area=\"true\"] .ring-emerald-300 {\n --tw-ring-color: rgb(110 231 183);\n}\n.theme--dark [data-stamp-area=\"true\"] .border-rose-300 {\n border-color: rgb(190 18 60);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-rose-100:hover {\n background-color: rgb(127 29 29);\n}\n\n/* --- Geometry header dimmer trong dark --- */\n.theme--dark [data-stamp-area=\"true\"] .bg-gradient-to-r.from-emerald-600 {\n background-image: linear-gradient(to right, rgb(6 95 70), rgb(15 76 76));\n}\n.theme--dark [data-stamp-area=\"true\"] .bg-emerald-600 {\n background-color: rgb(6 95 70);\n}\n.theme--dark [data-stamp-area=\"true\"] .hover\\:bg-emerald-700:hover {\n background-color: rgb(4 120 87);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
|
|
2
|
+
import { NonDeletedExcalidrawElement, ExcalidrawElement } from '@excalidraw/excalidraw/element/types';
|
|
3
3
|
export { ExcalidrawElement, NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
|
|
4
4
|
import { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
|
|
5
5
|
export { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
|
|
6
|
+
import { ReactNode, ForwardRefExoticComponent, RefAttributes } from 'react';
|
|
6
7
|
|
|
7
8
|
interface SyncableAppState {
|
|
8
9
|
viewBackgroundColor: string;
|
|
@@ -17,19 +18,201 @@ interface ExcalidrawSceneSnapshot {
|
|
|
17
18
|
appState: SyncableAppState;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Kết quả trả về từ `restoreFileFromCustomData`. Chứa đủ thông tin để
|
|
23
|
+
* consumer gọi `api.addFiles(...)`.
|
|
24
|
+
*/
|
|
25
|
+
interface RestoredStampFile {
|
|
26
|
+
fileId: string;
|
|
27
|
+
dataURL: string;
|
|
28
|
+
mimeType: 'image/svg+xml' | 'image/png';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Tối thiểu mọi custom data của stamp cần có. Các stamp cụ thể (geometry,
|
|
32
|
+
* latex, ...) extend interface này với fields riêng.
|
|
33
|
+
*/
|
|
34
|
+
interface BaseStampCustomData {
|
|
35
|
+
kind: string;
|
|
36
|
+
version: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Props mà mỗi StampHost nhận từ Whiteboard. Host component tự
|
|
40
|
+
* quản lý state nội bộ (panel ref, undo stack, displayMode...) — main view
|
|
41
|
+
* chỉ điều phối show/hide.
|
|
42
|
+
*/
|
|
43
|
+
interface StampHostProps {
|
|
44
|
+
api: any;
|
|
45
|
+
/**
|
|
46
|
+
* Element đang re-edit (double-click) hoặc null nếu đang tạo mới.
|
|
47
|
+
* Host tự parse customData để load state ban đầu.
|
|
48
|
+
*/
|
|
49
|
+
editingElement: {
|
|
50
|
+
id: string;
|
|
51
|
+
customData: unknown;
|
|
52
|
+
} | null;
|
|
53
|
+
/** Đóng stamp panel (gọi sau khi insert hoặc khi user huỷ). */
|
|
54
|
+
onClose: () => void;
|
|
55
|
+
/** Dark theme flag. */
|
|
56
|
+
isDark: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Imperative API mà main view truy cập qua ref:
|
|
60
|
+
* - tryInsert(): khi user click ra ngoài → auto-commit nếu valid.
|
|
61
|
+
* Trả về true nếu chèn thành công, false nếu chưa có nội dung.
|
|
62
|
+
* - hasContent(): có nội dung để chèn không.
|
|
63
|
+
*/
|
|
64
|
+
interface StampHostHandle {
|
|
65
|
+
tryInsert(): boolean;
|
|
66
|
+
hasContent(): boolean;
|
|
67
|
+
}
|
|
68
|
+
type StampHostComponent = ForwardRefExoticComponent<StampHostProps & RefAttributes<StampHostHandle>>;
|
|
69
|
+
/**
|
|
70
|
+
* Định nghĩa 1 loại stamp. Mỗi stamp khai báo:
|
|
71
|
+
* - kind: unique string (khớp với customData.kind)
|
|
72
|
+
* - phím tắt + UI toolbar
|
|
73
|
+
* - cách nhận biết customData thuộc về stamp này (matchesCustomData)
|
|
74
|
+
* - cách re-render SVG từ customData (cho restore sau reload)
|
|
75
|
+
* - Host component: bọc trọn editor + left panel + insert logic
|
|
76
|
+
*
|
|
77
|
+
* Main view dispatch generic: `<stamp.Host ... />` — không cần biết kind.
|
|
78
|
+
*/
|
|
79
|
+
interface StampType {
|
|
80
|
+
/** Unique kind. VD: 'geometry', 'latex'. Phải khớp với customData.kind. */
|
|
81
|
+
kind: string;
|
|
82
|
+
/** Phím tắt mở/đóng stamp (lowercase, 1 ký tự). VD: 'g', 'l'. */
|
|
83
|
+
shortcutKey: string;
|
|
84
|
+
/** Chữ hiển thị overlay góc dưới nút toolbar (e.g. "G"). */
|
|
85
|
+
toolbarLabel: string;
|
|
86
|
+
/** Tooltip + aria-label của nút toolbar. */
|
|
87
|
+
toolbarTitle: string;
|
|
88
|
+
/** Icon SVG (ReactNode) trong nút toolbar. */
|
|
89
|
+
toolbarIcon: ReactNode;
|
|
90
|
+
/** Test data-testid cho nút toolbar (optional). */
|
|
91
|
+
toolbarTestId?: string;
|
|
92
|
+
/** Type guard: customData có thuộc về stamp này không. */
|
|
93
|
+
matchesCustomData(data: unknown): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Re-render SVG từ customData. Dùng khi restore math-stamp file sau reload
|
|
96
|
+
* page (Excalidraw không persist binary file payload, chỉ giữ fileId trong
|
|
97
|
+
* element). SVG render với light palette (nét đậm) — Excalidraw tự đảo
|
|
98
|
+
* màu trong dark mode qua CSS filter.
|
|
99
|
+
*/
|
|
100
|
+
renderSvgFromCustomData(data: unknown): Promise<string>;
|
|
101
|
+
/**
|
|
102
|
+
* Regenerate file SVG/PNG cho element thuộc stamp này khi reload từ persisted
|
|
103
|
+
* snapshot. Trả về `RestoredStampFile` để consumer gọi `api.addFiles`, hoặc
|
|
104
|
+
* `null` nếu element không cần file (vd stamp chỉ là text overlay).
|
|
105
|
+
*
|
|
106
|
+
* Khi method này có mặt, `restoreMissingStampFiles` sẽ ưu tiên gọi method
|
|
107
|
+
* này thay vì dùng `renderSvgFromCustomData`. Stamp tự chịu trách nhiệm lấy
|
|
108
|
+
* `fileId` từ element và render file.
|
|
109
|
+
*/
|
|
110
|
+
restoreFileFromCustomData?: (element: ExcalidrawElement) => Promise<RestoredStampFile | null>;
|
|
111
|
+
/**
|
|
112
|
+
* Host component bọc toàn bộ UI editing (panel + left panel + insert
|
|
113
|
+
* handler). Whiteboard mount Host khi activeStamp khớp kind.
|
|
114
|
+
*/
|
|
115
|
+
Host: StampHostComponent;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface GeometryCustomData extends BaseStampCustomData {
|
|
119
|
+
kind: 'geometry';
|
|
120
|
+
version: 1;
|
|
121
|
+
jsonState: string;
|
|
122
|
+
svgWidth: number;
|
|
123
|
+
svgHeight: number;
|
|
124
|
+
}
|
|
125
|
+
declare function isGeometryCustomData(data: unknown): data is GeometryCustomData;
|
|
126
|
+
declare const geometryStamp: StampType;
|
|
127
|
+
|
|
128
|
+
interface LatexCustomData extends BaseStampCustomData {
|
|
129
|
+
kind: 'latex';
|
|
130
|
+
version: 1;
|
|
131
|
+
src: string;
|
|
132
|
+
displayMode: boolean;
|
|
133
|
+
}
|
|
134
|
+
declare function isLatexCustomData(data: unknown): data is LatexCustomData;
|
|
135
|
+
declare const latexStamp: StampType;
|
|
136
|
+
|
|
137
|
+
interface Geometry3DCustomData extends BaseStampCustomData {
|
|
138
|
+
kind: 'geometry3d';
|
|
139
|
+
version: 1;
|
|
140
|
+
jsonState: string;
|
|
141
|
+
svgWidth: number;
|
|
142
|
+
svgHeight: number;
|
|
143
|
+
}
|
|
144
|
+
declare function isGeometry3DCustomData(data: unknown): data is Geometry3DCustomData;
|
|
145
|
+
|
|
146
|
+
declare const geometry3dStamp: StampType;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Set stamp mặc định dùng trong Whiteboard. Consumer có thể
|
|
150
|
+
* truyền custom array để bật/tắt từng stamp hoặc đăng ký stamp mới.
|
|
151
|
+
*
|
|
152
|
+
* Để thêm 1 stamp mới (vd chart):
|
|
153
|
+
* 1. Tạo `src/stamp/registry/chart.tsx` với StampType object.
|
|
154
|
+
* 2. Add vào DEFAULT_STAMPS ở dưới, HOẶC consumer truyền
|
|
155
|
+
* `<Whiteboard stamps={[...DEFAULT_STAMPS, chartStamp]} />`.
|
|
156
|
+
*/
|
|
157
|
+
declare const DEFAULT_STAMPS: ReadonlyArray<StampType>;
|
|
158
|
+
/** Tìm stamp tương ứng với customData của element. null nếu không match. */
|
|
159
|
+
declare function findStampForCustomData(data: unknown, stamps?: ReadonlyArray<StampType>): StampType | null;
|
|
160
|
+
/** isMathStamp version dựa trên registry — replace logic hardcode trong types.ts. */
|
|
161
|
+
declare function isStampElement<T extends {
|
|
162
|
+
customData?: unknown;
|
|
163
|
+
}>(element: T, stamps?: ReadonlyArray<StampType>): boolean;
|
|
164
|
+
|
|
165
|
+
interface WhiteboardProps {
|
|
166
|
+
/**
|
|
167
|
+
* Storage key cho persist client-side.
|
|
168
|
+
* - Scene -> localStorage['whiteboard:scene:'+storageKey]
|
|
169
|
+
* - Files raster -> IndexedDB 'whiteboard-files' index theo storageKey
|
|
170
|
+
* - Default: 'default'
|
|
171
|
+
* - Truyen `null` de tat persist (consumer drive state qua onApi).
|
|
172
|
+
*/
|
|
173
|
+
storageKey?: string | null;
|
|
174
|
+
/** View-only (Excalidraw viewModeEnabled). Default false. */
|
|
175
|
+
readOnly?: boolean;
|
|
176
|
+
/** Local edits -> consumer broadcast. Optional. */
|
|
177
|
+
onSceneChange?: (snapshot: ExcalidrawSceneSnapshot) => void;
|
|
178
|
+
onFilesChange?: (files: BinaryFiles, newFileIds: string[]) => void;
|
|
179
|
+
/** Excalidraw imperative API. Consumer dung inject remote scene khi can. */
|
|
180
|
+
onApi?: (api: any) => void;
|
|
28
181
|
/** Excalidraw UI language. Defaults to 'vi-VN'. See @excalidraw/excalidraw locales. */
|
|
29
182
|
langCode?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +
|
|
185
|
+
* Host component (UI editing). Mặc định DEFAULT_STAMPS (geometry + latex).
|
|
186
|
+
* Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới.
|
|
187
|
+
*/
|
|
188
|
+
stamps?: ReadonlyArray<StampType>;
|
|
30
189
|
}
|
|
31
|
-
declare function
|
|
190
|
+
declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
|
|
32
191
|
|
|
33
192
|
declare function pickSyncableAppState(s: AppState): SyncableAppState;
|
|
34
193
|
|
|
35
|
-
|
|
194
|
+
interface ElementLike {
|
|
195
|
+
id: string;
|
|
196
|
+
type?: string;
|
|
197
|
+
fileId?: string | null;
|
|
198
|
+
customData?: unknown;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Find stamp elements whose binary file is missing from Excalidraw, then
|
|
202
|
+
* regenerate via registry dispatch. Idempotent: safe to call on every scene
|
|
203
|
+
* update.
|
|
204
|
+
*
|
|
205
|
+
* Stamps that implement `restoreFileFromCustomData` are handled via the new
|
|
206
|
+
* registry-driven path (stamp receives the full element and returns the file
|
|
207
|
+
* record). Stamps that only implement `renderSvgFromCustomData` use the legacy
|
|
208
|
+
* path (filter type=image + fileId, skip already-present files).
|
|
209
|
+
*
|
|
210
|
+
* @param api Excalidraw imperative API.
|
|
211
|
+
* @param elements Tất cả elements trong scene.
|
|
212
|
+
* @param stamps Registry. Default = DEFAULT_STAMPS.
|
|
213
|
+
*/
|
|
214
|
+
declare function restoreMissingStampFiles(api: any, elements: readonly ElementLike[], stamps?: ReadonlyArray<StampType>): Promise<void>;
|
|
215
|
+
|
|
216
|
+
type StampCustomData = GeometryCustomData | LatexCustomData | Geometry3DCustomData;
|
|
217
|
+
|
|
218
|
+
export { type BaseStampCustomData, DEFAULT_STAMPS, type ExcalidrawSceneSnapshot, type Geometry3DCustomData, type GeometryCustomData, type LatexCustomData, type StampCustomData, type StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, findStampForCustomData, geometry3dStamp, geometryStamp, isGeometry3DCustomData, isGeometryCustomData, isLatexCustomData, isStampElement, latexStamp, pickSyncableAppState, restoreMissingStampFiles };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
|
|
2
|
+
import { NonDeletedExcalidrawElement, ExcalidrawElement } from '@excalidraw/excalidraw/element/types';
|
|
3
3
|
export { ExcalidrawElement, NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/element/types';
|
|
4
4
|
import { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
|
|
5
5
|
export { AppState, BinaryFiles } from '@excalidraw/excalidraw/types';
|
|
6
|
+
import { ReactNode, ForwardRefExoticComponent, RefAttributes } from 'react';
|
|
6
7
|
|
|
7
8
|
interface SyncableAppState {
|
|
8
9
|
viewBackgroundColor: string;
|
|
@@ -17,19 +18,201 @@ interface ExcalidrawSceneSnapshot {
|
|
|
17
18
|
appState: SyncableAppState;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Kết quả trả về từ `restoreFileFromCustomData`. Chứa đủ thông tin để
|
|
23
|
+
* consumer gọi `api.addFiles(...)`.
|
|
24
|
+
*/
|
|
25
|
+
interface RestoredStampFile {
|
|
26
|
+
fileId: string;
|
|
27
|
+
dataURL: string;
|
|
28
|
+
mimeType: 'image/svg+xml' | 'image/png';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Tối thiểu mọi custom data của stamp cần có. Các stamp cụ thể (geometry,
|
|
32
|
+
* latex, ...) extend interface này với fields riêng.
|
|
33
|
+
*/
|
|
34
|
+
interface BaseStampCustomData {
|
|
35
|
+
kind: string;
|
|
36
|
+
version: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Props mà mỗi StampHost nhận từ Whiteboard. Host component tự
|
|
40
|
+
* quản lý state nội bộ (panel ref, undo stack, displayMode...) — main view
|
|
41
|
+
* chỉ điều phối show/hide.
|
|
42
|
+
*/
|
|
43
|
+
interface StampHostProps {
|
|
44
|
+
api: any;
|
|
45
|
+
/**
|
|
46
|
+
* Element đang re-edit (double-click) hoặc null nếu đang tạo mới.
|
|
47
|
+
* Host tự parse customData để load state ban đầu.
|
|
48
|
+
*/
|
|
49
|
+
editingElement: {
|
|
50
|
+
id: string;
|
|
51
|
+
customData: unknown;
|
|
52
|
+
} | null;
|
|
53
|
+
/** Đóng stamp panel (gọi sau khi insert hoặc khi user huỷ). */
|
|
54
|
+
onClose: () => void;
|
|
55
|
+
/** Dark theme flag. */
|
|
56
|
+
isDark: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Imperative API mà main view truy cập qua ref:
|
|
60
|
+
* - tryInsert(): khi user click ra ngoài → auto-commit nếu valid.
|
|
61
|
+
* Trả về true nếu chèn thành công, false nếu chưa có nội dung.
|
|
62
|
+
* - hasContent(): có nội dung để chèn không.
|
|
63
|
+
*/
|
|
64
|
+
interface StampHostHandle {
|
|
65
|
+
tryInsert(): boolean;
|
|
66
|
+
hasContent(): boolean;
|
|
67
|
+
}
|
|
68
|
+
type StampHostComponent = ForwardRefExoticComponent<StampHostProps & RefAttributes<StampHostHandle>>;
|
|
69
|
+
/**
|
|
70
|
+
* Định nghĩa 1 loại stamp. Mỗi stamp khai báo:
|
|
71
|
+
* - kind: unique string (khớp với customData.kind)
|
|
72
|
+
* - phím tắt + UI toolbar
|
|
73
|
+
* - cách nhận biết customData thuộc về stamp này (matchesCustomData)
|
|
74
|
+
* - cách re-render SVG từ customData (cho restore sau reload)
|
|
75
|
+
* - Host component: bọc trọn editor + left panel + insert logic
|
|
76
|
+
*
|
|
77
|
+
* Main view dispatch generic: `<stamp.Host ... />` — không cần biết kind.
|
|
78
|
+
*/
|
|
79
|
+
interface StampType {
|
|
80
|
+
/** Unique kind. VD: 'geometry', 'latex'. Phải khớp với customData.kind. */
|
|
81
|
+
kind: string;
|
|
82
|
+
/** Phím tắt mở/đóng stamp (lowercase, 1 ký tự). VD: 'g', 'l'. */
|
|
83
|
+
shortcutKey: string;
|
|
84
|
+
/** Chữ hiển thị overlay góc dưới nút toolbar (e.g. "G"). */
|
|
85
|
+
toolbarLabel: string;
|
|
86
|
+
/** Tooltip + aria-label của nút toolbar. */
|
|
87
|
+
toolbarTitle: string;
|
|
88
|
+
/** Icon SVG (ReactNode) trong nút toolbar. */
|
|
89
|
+
toolbarIcon: ReactNode;
|
|
90
|
+
/** Test data-testid cho nút toolbar (optional). */
|
|
91
|
+
toolbarTestId?: string;
|
|
92
|
+
/** Type guard: customData có thuộc về stamp này không. */
|
|
93
|
+
matchesCustomData(data: unknown): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Re-render SVG từ customData. Dùng khi restore math-stamp file sau reload
|
|
96
|
+
* page (Excalidraw không persist binary file payload, chỉ giữ fileId trong
|
|
97
|
+
* element). SVG render với light palette (nét đậm) — Excalidraw tự đảo
|
|
98
|
+
* màu trong dark mode qua CSS filter.
|
|
99
|
+
*/
|
|
100
|
+
renderSvgFromCustomData(data: unknown): Promise<string>;
|
|
101
|
+
/**
|
|
102
|
+
* Regenerate file SVG/PNG cho element thuộc stamp này khi reload từ persisted
|
|
103
|
+
* snapshot. Trả về `RestoredStampFile` để consumer gọi `api.addFiles`, hoặc
|
|
104
|
+
* `null` nếu element không cần file (vd stamp chỉ là text overlay).
|
|
105
|
+
*
|
|
106
|
+
* Khi method này có mặt, `restoreMissingStampFiles` sẽ ưu tiên gọi method
|
|
107
|
+
* này thay vì dùng `renderSvgFromCustomData`. Stamp tự chịu trách nhiệm lấy
|
|
108
|
+
* `fileId` từ element và render file.
|
|
109
|
+
*/
|
|
110
|
+
restoreFileFromCustomData?: (element: ExcalidrawElement) => Promise<RestoredStampFile | null>;
|
|
111
|
+
/**
|
|
112
|
+
* Host component bọc toàn bộ UI editing (panel + left panel + insert
|
|
113
|
+
* handler). Whiteboard mount Host khi activeStamp khớp kind.
|
|
114
|
+
*/
|
|
115
|
+
Host: StampHostComponent;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface GeometryCustomData extends BaseStampCustomData {
|
|
119
|
+
kind: 'geometry';
|
|
120
|
+
version: 1;
|
|
121
|
+
jsonState: string;
|
|
122
|
+
svgWidth: number;
|
|
123
|
+
svgHeight: number;
|
|
124
|
+
}
|
|
125
|
+
declare function isGeometryCustomData(data: unknown): data is GeometryCustomData;
|
|
126
|
+
declare const geometryStamp: StampType;
|
|
127
|
+
|
|
128
|
+
interface LatexCustomData extends BaseStampCustomData {
|
|
129
|
+
kind: 'latex';
|
|
130
|
+
version: 1;
|
|
131
|
+
src: string;
|
|
132
|
+
displayMode: boolean;
|
|
133
|
+
}
|
|
134
|
+
declare function isLatexCustomData(data: unknown): data is LatexCustomData;
|
|
135
|
+
declare const latexStamp: StampType;
|
|
136
|
+
|
|
137
|
+
interface Geometry3DCustomData extends BaseStampCustomData {
|
|
138
|
+
kind: 'geometry3d';
|
|
139
|
+
version: 1;
|
|
140
|
+
jsonState: string;
|
|
141
|
+
svgWidth: number;
|
|
142
|
+
svgHeight: number;
|
|
143
|
+
}
|
|
144
|
+
declare function isGeometry3DCustomData(data: unknown): data is Geometry3DCustomData;
|
|
145
|
+
|
|
146
|
+
declare const geometry3dStamp: StampType;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Set stamp mặc định dùng trong Whiteboard. Consumer có thể
|
|
150
|
+
* truyền custom array để bật/tắt từng stamp hoặc đăng ký stamp mới.
|
|
151
|
+
*
|
|
152
|
+
* Để thêm 1 stamp mới (vd chart):
|
|
153
|
+
* 1. Tạo `src/stamp/registry/chart.tsx` với StampType object.
|
|
154
|
+
* 2. Add vào DEFAULT_STAMPS ở dưới, HOẶC consumer truyền
|
|
155
|
+
* `<Whiteboard stamps={[...DEFAULT_STAMPS, chartStamp]} />`.
|
|
156
|
+
*/
|
|
157
|
+
declare const DEFAULT_STAMPS: ReadonlyArray<StampType>;
|
|
158
|
+
/** Tìm stamp tương ứng với customData của element. null nếu không match. */
|
|
159
|
+
declare function findStampForCustomData(data: unknown, stamps?: ReadonlyArray<StampType>): StampType | null;
|
|
160
|
+
/** isMathStamp version dựa trên registry — replace logic hardcode trong types.ts. */
|
|
161
|
+
declare function isStampElement<T extends {
|
|
162
|
+
customData?: unknown;
|
|
163
|
+
}>(element: T, stamps?: ReadonlyArray<StampType>): boolean;
|
|
164
|
+
|
|
165
|
+
interface WhiteboardProps {
|
|
166
|
+
/**
|
|
167
|
+
* Storage key cho persist client-side.
|
|
168
|
+
* - Scene -> localStorage['whiteboard:scene:'+storageKey]
|
|
169
|
+
* - Files raster -> IndexedDB 'whiteboard-files' index theo storageKey
|
|
170
|
+
* - Default: 'default'
|
|
171
|
+
* - Truyen `null` de tat persist (consumer drive state qua onApi).
|
|
172
|
+
*/
|
|
173
|
+
storageKey?: string | null;
|
|
174
|
+
/** View-only (Excalidraw viewModeEnabled). Default false. */
|
|
175
|
+
readOnly?: boolean;
|
|
176
|
+
/** Local edits -> consumer broadcast. Optional. */
|
|
177
|
+
onSceneChange?: (snapshot: ExcalidrawSceneSnapshot) => void;
|
|
178
|
+
onFilesChange?: (files: BinaryFiles, newFileIds: string[]) => void;
|
|
179
|
+
/** Excalidraw imperative API. Consumer dung inject remote scene khi can. */
|
|
180
|
+
onApi?: (api: any) => void;
|
|
28
181
|
/** Excalidraw UI language. Defaults to 'vi-VN'. See @excalidraw/excalidraw locales. */
|
|
29
182
|
langCode?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +
|
|
185
|
+
* Host component (UI editing). Mặc định DEFAULT_STAMPS (geometry + latex).
|
|
186
|
+
* Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới.
|
|
187
|
+
*/
|
|
188
|
+
stamps?: ReadonlyArray<StampType>;
|
|
30
189
|
}
|
|
31
|
-
declare function
|
|
190
|
+
declare function Whiteboard({ storageKey, readOnly, onSceneChange, onFilesChange, onApi, langCode, stamps, }: WhiteboardProps): react_jsx_runtime.JSX.Element;
|
|
32
191
|
|
|
33
192
|
declare function pickSyncableAppState(s: AppState): SyncableAppState;
|
|
34
193
|
|
|
35
|
-
|
|
194
|
+
interface ElementLike {
|
|
195
|
+
id: string;
|
|
196
|
+
type?: string;
|
|
197
|
+
fileId?: string | null;
|
|
198
|
+
customData?: unknown;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Find stamp elements whose binary file is missing from Excalidraw, then
|
|
202
|
+
* regenerate via registry dispatch. Idempotent: safe to call on every scene
|
|
203
|
+
* update.
|
|
204
|
+
*
|
|
205
|
+
* Stamps that implement `restoreFileFromCustomData` are handled via the new
|
|
206
|
+
* registry-driven path (stamp receives the full element and returns the file
|
|
207
|
+
* record). Stamps that only implement `renderSvgFromCustomData` use the legacy
|
|
208
|
+
* path (filter type=image + fileId, skip already-present files).
|
|
209
|
+
*
|
|
210
|
+
* @param api Excalidraw imperative API.
|
|
211
|
+
* @param elements Tất cả elements trong scene.
|
|
212
|
+
* @param stamps Registry. Default = DEFAULT_STAMPS.
|
|
213
|
+
*/
|
|
214
|
+
declare function restoreMissingStampFiles(api: any, elements: readonly ElementLike[], stamps?: ReadonlyArray<StampType>): Promise<void>;
|
|
215
|
+
|
|
216
|
+
type StampCustomData = GeometryCustomData | LatexCustomData | Geometry3DCustomData;
|
|
217
|
+
|
|
218
|
+
export { type BaseStampCustomData, DEFAULT_STAMPS, type ExcalidrawSceneSnapshot, type Geometry3DCustomData, type GeometryCustomData, type LatexCustomData, type StampCustomData, type StampType, type SyncableAppState, Whiteboard, type WhiteboardProps, findStampForCustomData, geometry3dStamp, geometryStamp, isGeometry3DCustomData, isGeometryCustomData, isLatexCustomData, isStampElement, latexStamp, pickSyncableAppState, restoreMissingStampFiles };
|