@haklex/rich-plugin-toolbar 0.0.56
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 +28 -0
- package/dist/ToolbarButton.d.ts +13 -0
- package/dist/ToolbarButton.d.ts.map +1 -0
- package/dist/ToolbarDropdown.d.ts +18 -0
- package/dist/ToolbarDropdown.d.ts.map +1 -0
- package/dist/ToolbarPlugin.d.ts +6 -0
- package/dist/ToolbarPlugin.d.ts.map +1 -0
- package/dist/ToolbarSeparator.d.ts +2 -0
- package/dist/ToolbarSeparator.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +566 -0
- package/dist/rich-plugin-toolbar.css +1 -0
- package/dist/styles.css.d.ts +10 -0
- package/dist/styles.css.d.ts.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Innei
|
|
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.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Additional Terms and Conditions
|
|
25
|
+
|
|
26
|
+
----------------
|
|
27
|
+
|
|
28
|
+
Use of this software is governed by the terms of MIT and, in addition, by the terms and conditions described in the additional file (ADDITIONAL_TERMS.md). By using this software, you agree to abide by these additional terms and conditions.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ToolbarTooltipHandle } from './types';
|
|
3
|
+
export interface ToolbarButtonProps {
|
|
4
|
+
icon: ReactNode;
|
|
5
|
+
title: string;
|
|
6
|
+
shortcut?: string;
|
|
7
|
+
active?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onClick: () => void;
|
|
10
|
+
tooltipHandle?: ToolbarTooltipHandle;
|
|
11
|
+
}
|
|
12
|
+
export declare function ToolbarButton({ icon, title, shortcut, active, disabled, onClick, tooltipHandle, }: ToolbarButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=ToolbarButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolbarButton.d.ts","sourceRoot":"","sources":["../src/ToolbarButton.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAA;AAE1E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,aAAa,CAAC,EAAE,oBAAoB,CAAA;CACrC;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,OAAO,EACP,aAAa,GACd,EAAE,kBAAkB,2CAoCpB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { ToolbarTooltipHandle } from './types';
|
|
3
|
+
export interface ToolbarDropdownItem {
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: ReactNode;
|
|
6
|
+
active?: boolean;
|
|
7
|
+
style?: CSSProperties;
|
|
8
|
+
onSelect: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface ToolbarDropdownProps {
|
|
11
|
+
label: string;
|
|
12
|
+
title: string;
|
|
13
|
+
items: ToolbarDropdownItem[];
|
|
14
|
+
triggerWidth?: number;
|
|
15
|
+
tooltipHandle?: ToolbarTooltipHandle;
|
|
16
|
+
}
|
|
17
|
+
export declare function ToolbarDropdown({ label, title, items, triggerWidth, tooltipHandle, }: ToolbarDropdownProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=ToolbarDropdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolbarDropdown.d.ts","sourceRoot":"","sources":["../src/ToolbarDropdown.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGrD,OAAO,KAAK,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAA;AAE1E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,mBAAmB,EAAE,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,oBAAoB,CAAA;CACrC;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,KAAK,EACL,KAAK,EACL,YAAY,EACZ,aAAa,GACd,EAAE,oBAAoB,2CAyEtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolbarPlugin.d.ts","sourceRoot":"","sources":["../src/ToolbarPlugin.tsx"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAA;AAsHxD,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE,kBAAkB,GAAG,YAAY,CAqY7E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolbarSeparator.d.ts","sourceRoot":"","sources":["../src/ToolbarSeparator.tsx"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,4CAE/B"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { collectCommandItems } from "@haklex/rich-editor";
|
|
3
|
+
import { TooltipTrigger, TooltipRoot, TooltipContent, DropdownMenuContent, DropdownMenuItem, DropdownMenu, DropdownMenuTrigger, createTooltipHandle, TooltipProvider } from "@haklex/rich-editor-ui";
|
|
4
|
+
import { $isListNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_CHECK_LIST_COMMAND } from "@lexical/list";
|
|
5
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
|
+
import { $createHeadingNode, $isHeadingNode } from "@lexical/rich-text";
|
|
7
|
+
import { $getSelectionStyleValueForProperty, $patchStyleText, $setBlocksType } from "@lexical/selection";
|
|
8
|
+
import { $findMatchingParent } from "@lexical/utils";
|
|
9
|
+
import { $getSelection, $isRangeSelection, $isRootOrShadowRoot, CAN_UNDO_COMMAND, COMMAND_PRIORITY_LOW, CAN_REDO_COMMAND, $createParagraphNode, UNDO_COMMAND, REDO_COMMAND, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND } from "lexical";
|
|
10
|
+
import { ChevronDown, Pilcrow, Heading1, Heading2, Heading3, Undo, Redo, Bold, Italic, Underline, Strikethrough, Code, Highlighter, List, ListOrdered, ListChecks, AlignLeft, AlignCenter, AlignRight, AlignJustify } from "lucide-react";
|
|
11
|
+
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
12
|
+
var toolbarContainer = "_1e1ersc0";
|
|
13
|
+
var toolbarRow = "_1e1ersc1";
|
|
14
|
+
var toolbarButton = "_1e1ersc2";
|
|
15
|
+
var toolbarButtonActive = "_1e1ersc3";
|
|
16
|
+
var toolbarDropdownTrigger = "_1e1ersc4";
|
|
17
|
+
var toolbarDropdownTriggerChevron = "_1e1ersc5";
|
|
18
|
+
var toolbarSeparator = "_1e1ersc6";
|
|
19
|
+
var toolbarDropdownItemActive = "_1e1ersc7";
|
|
20
|
+
var tooltipShortcut = "_1e1ersc8";
|
|
21
|
+
function ToolbarButton({
|
|
22
|
+
icon,
|
|
23
|
+
title,
|
|
24
|
+
shortcut,
|
|
25
|
+
active,
|
|
26
|
+
disabled,
|
|
27
|
+
onClick,
|
|
28
|
+
tooltipHandle
|
|
29
|
+
}) {
|
|
30
|
+
const button = /* @__PURE__ */ jsx(
|
|
31
|
+
"button",
|
|
32
|
+
{
|
|
33
|
+
type: "button",
|
|
34
|
+
className: `${toolbarButton}${active ? ` ${toolbarButtonActive}` : ""}`,
|
|
35
|
+
disabled,
|
|
36
|
+
onMouseDown: (e) => {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
onClick();
|
|
39
|
+
},
|
|
40
|
+
"aria-label": title,
|
|
41
|
+
"aria-pressed": active
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
if (tooltipHandle) {
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
46
|
+
TooltipTrigger,
|
|
47
|
+
{
|
|
48
|
+
handle: tooltipHandle,
|
|
49
|
+
payload: { title, shortcut },
|
|
50
|
+
render: button,
|
|
51
|
+
children: icon
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return /* @__PURE__ */ jsxs(TooltipRoot, { children: [
|
|
56
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { render: button, children: icon }),
|
|
57
|
+
/* @__PURE__ */ jsxs(TooltipContent, { side: "bottom", sideOffset: 4, children: [
|
|
58
|
+
title,
|
|
59
|
+
shortcut && /* @__PURE__ */ jsx("span", { className: tooltipShortcut, children: shortcut })
|
|
60
|
+
] })
|
|
61
|
+
] });
|
|
62
|
+
}
|
|
63
|
+
function ToolbarDropdown({
|
|
64
|
+
label,
|
|
65
|
+
title,
|
|
66
|
+
items,
|
|
67
|
+
triggerWidth,
|
|
68
|
+
tooltipHandle
|
|
69
|
+
}) {
|
|
70
|
+
const triggerStyle = triggerWidth ? { width: triggerWidth } : void 0;
|
|
71
|
+
const trigger = /* @__PURE__ */ jsx(
|
|
72
|
+
DropdownMenuTrigger,
|
|
73
|
+
{
|
|
74
|
+
className: toolbarDropdownTrigger,
|
|
75
|
+
style: triggerStyle,
|
|
76
|
+
render: /* @__PURE__ */ jsx("button", { type: "button" })
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
const triggerContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
80
|
+
label,
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: toolbarDropdownTriggerChevron, children: /* @__PURE__ */ jsx(ChevronDown, { size: 12 }) })
|
|
82
|
+
] });
|
|
83
|
+
const menu = /* @__PURE__ */ jsx(DropdownMenuContent, { sideOffset: 4, children: items.map((item) => /* @__PURE__ */ jsxs(
|
|
84
|
+
DropdownMenuItem,
|
|
85
|
+
{
|
|
86
|
+
className: item.active ? toolbarDropdownItemActive : void 0,
|
|
87
|
+
style: item.style,
|
|
88
|
+
onClick: item.onSelect,
|
|
89
|
+
children: [
|
|
90
|
+
item.icon && /* @__PURE__ */ jsx(
|
|
91
|
+
"span",
|
|
92
|
+
{
|
|
93
|
+
style: {
|
|
94
|
+
marginRight: 8,
|
|
95
|
+
display: "inline-flex",
|
|
96
|
+
transform: "scale(0.8)",
|
|
97
|
+
transformOrigin: "left center"
|
|
98
|
+
},
|
|
99
|
+
children: item.icon
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
item.label
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
item.label
|
|
106
|
+
)) });
|
|
107
|
+
if (tooltipHandle) {
|
|
108
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
110
|
+
TooltipTrigger,
|
|
111
|
+
{
|
|
112
|
+
handle: tooltipHandle,
|
|
113
|
+
payload: { title },
|
|
114
|
+
render: trigger,
|
|
115
|
+
children: triggerContent
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
menu
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
122
|
+
/* @__PURE__ */ jsxs(TooltipRoot, { children: [
|
|
123
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { render: trigger, children: triggerContent }),
|
|
124
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", sideOffset: 4, children: title })
|
|
125
|
+
] }),
|
|
126
|
+
menu
|
|
127
|
+
] });
|
|
128
|
+
}
|
|
129
|
+
function ToolbarSeparator() {
|
|
130
|
+
return /* @__PURE__ */ jsx("div", { className: toolbarSeparator });
|
|
131
|
+
}
|
|
132
|
+
const ICON_SIZE = 15;
|
|
133
|
+
const ICON_STROKE = 2;
|
|
134
|
+
const FONT_FAMILIES = [
|
|
135
|
+
{ label: "默认", value: "" },
|
|
136
|
+
{
|
|
137
|
+
label: "宋体",
|
|
138
|
+
value: '"Noto Serif CJK SC", "Source Han Serif SC", SimSun, serif'
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
label: "黑体",
|
|
142
|
+
value: '"Noto Sans CJK SC", "Source Han Sans SC", SimHei, sans-serif'
|
|
143
|
+
},
|
|
144
|
+
{ label: "楷体", value: "KaiTi, STKaiti, serif" },
|
|
145
|
+
{ label: "Sans", value: "system-ui, -apple-system, sans-serif" },
|
|
146
|
+
{ label: "Serif", value: 'Georgia, "Times New Roman", serif' },
|
|
147
|
+
{ label: "Mono", value: 'ui-monospace, "SF Mono", "Fira Code", monospace' }
|
|
148
|
+
];
|
|
149
|
+
function getFontLabel(fontFamily) {
|
|
150
|
+
if (!fontFamily) return "默认";
|
|
151
|
+
const match = FONT_FAMILIES.find((f) => f.value === fontFamily);
|
|
152
|
+
if (match) return match.label;
|
|
153
|
+
for (const def of FONT_FAMILIES) {
|
|
154
|
+
if (def.value && fontFamily.startsWith(def.value.split(",")[0])) {
|
|
155
|
+
return def.label;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return "默认";
|
|
159
|
+
}
|
|
160
|
+
const BLOCK_TYPE_LABELS = {
|
|
161
|
+
paragraph: "Text",
|
|
162
|
+
h1: "Heading 1",
|
|
163
|
+
h2: "Heading 2",
|
|
164
|
+
h3: "Heading 3",
|
|
165
|
+
bullet: "Bulleted List",
|
|
166
|
+
number: "Numbered List",
|
|
167
|
+
check: "To-do List",
|
|
168
|
+
other: "Other"
|
|
169
|
+
};
|
|
170
|
+
const INITIAL_STATE = {
|
|
171
|
+
canUndo: false,
|
|
172
|
+
canRedo: false,
|
|
173
|
+
blockType: "paragraph",
|
|
174
|
+
fontFamily: "",
|
|
175
|
+
isBold: false,
|
|
176
|
+
isItalic: false,
|
|
177
|
+
isUnderline: false,
|
|
178
|
+
isStrikethrough: false,
|
|
179
|
+
isCode: false,
|
|
180
|
+
isHighlight: false
|
|
181
|
+
};
|
|
182
|
+
function getBlockType(anchorNode) {
|
|
183
|
+
if ($isHeadingNode(anchorNode)) {
|
|
184
|
+
const tag = anchorNode.getTag();
|
|
185
|
+
if (tag === "h1" || tag === "h2" || tag === "h3") return tag;
|
|
186
|
+
return "other";
|
|
187
|
+
}
|
|
188
|
+
if ($isListNode(anchorNode)) {
|
|
189
|
+
const listType = anchorNode.getListType();
|
|
190
|
+
if (listType === "bullet") return "bullet";
|
|
191
|
+
if (listType === "number") return "number";
|
|
192
|
+
if (listType === "check") return "check";
|
|
193
|
+
return "other";
|
|
194
|
+
}
|
|
195
|
+
const type = anchorNode.getType();
|
|
196
|
+
if (type === "paragraph") return "paragraph";
|
|
197
|
+
return "other";
|
|
198
|
+
}
|
|
199
|
+
function ToolbarPlugin({ className }) {
|
|
200
|
+
const [editor] = useLexicalComposerContext();
|
|
201
|
+
const [state, setState] = useState(INITIAL_STATE);
|
|
202
|
+
const [toolbarItems, setToolbarItems] = useState([]);
|
|
203
|
+
const [tooltipHandle] = useState(
|
|
204
|
+
() => createTooltipHandle()
|
|
205
|
+
);
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
const items = collectCommandItems(editor).filter(
|
|
208
|
+
(item) => item.placement?.includes("toolbar") && item.group === "insert"
|
|
209
|
+
);
|
|
210
|
+
setToolbarItems(items);
|
|
211
|
+
}, [editor]);
|
|
212
|
+
const updateToolbar = useCallback(() => {
|
|
213
|
+
const selection = $getSelection();
|
|
214
|
+
if (!$isRangeSelection(selection)) return;
|
|
215
|
+
const anchorNode = selection.anchor.getNode();
|
|
216
|
+
let element = anchorNode.getKey() === "root" ? anchorNode : $findMatchingParent(anchorNode, (e) => {
|
|
217
|
+
const parent = e.getParent();
|
|
218
|
+
return parent !== null && $isRootOrShadowRoot(parent);
|
|
219
|
+
}) ?? anchorNode.getTopLevelElementOrThrow();
|
|
220
|
+
if ($isListNode(element)) {
|
|
221
|
+
const parentList = $findMatchingParent(
|
|
222
|
+
anchorNode,
|
|
223
|
+
(node) => $isListNode(node)
|
|
224
|
+
);
|
|
225
|
+
if (parentList) {
|
|
226
|
+
element = parentList;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const blockType = getBlockType(element);
|
|
230
|
+
const fontFamily = $getSelectionStyleValueForProperty(
|
|
231
|
+
selection,
|
|
232
|
+
"font-family",
|
|
233
|
+
""
|
|
234
|
+
);
|
|
235
|
+
setState((prev) => ({
|
|
236
|
+
...prev,
|
|
237
|
+
blockType,
|
|
238
|
+
fontFamily,
|
|
239
|
+
isBold: selection.hasFormat("bold"),
|
|
240
|
+
isItalic: selection.hasFormat("italic"),
|
|
241
|
+
isUnderline: selection.hasFormat("underline"),
|
|
242
|
+
isStrikethrough: selection.hasFormat("strikethrough"),
|
|
243
|
+
isCode: selection.hasFormat("code"),
|
|
244
|
+
isHighlight: selection.hasFormat("highlight")
|
|
245
|
+
}));
|
|
246
|
+
}, []);
|
|
247
|
+
useEffect(() => {
|
|
248
|
+
const unregisterUndo = editor.registerCommand(
|
|
249
|
+
CAN_UNDO_COMMAND,
|
|
250
|
+
(payload) => {
|
|
251
|
+
setState((prev) => ({ ...prev, canUndo: payload }));
|
|
252
|
+
return false;
|
|
253
|
+
},
|
|
254
|
+
COMMAND_PRIORITY_LOW
|
|
255
|
+
);
|
|
256
|
+
const unregisterRedo = editor.registerCommand(
|
|
257
|
+
CAN_REDO_COMMAND,
|
|
258
|
+
(payload) => {
|
|
259
|
+
setState((prev) => ({ ...prev, canRedo: payload }));
|
|
260
|
+
return false;
|
|
261
|
+
},
|
|
262
|
+
COMMAND_PRIORITY_LOW
|
|
263
|
+
);
|
|
264
|
+
const unregisterUpdate = editor.registerUpdateListener(
|
|
265
|
+
({ editorState }) => {
|
|
266
|
+
editorState.read(() => {
|
|
267
|
+
updateToolbar();
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
return () => {
|
|
272
|
+
unregisterUndo();
|
|
273
|
+
unregisterRedo();
|
|
274
|
+
unregisterUpdate();
|
|
275
|
+
};
|
|
276
|
+
}, [editor, updateToolbar]);
|
|
277
|
+
const applyFontFamily = useCallback(
|
|
278
|
+
(value) => {
|
|
279
|
+
editor.update(() => {
|
|
280
|
+
const selection = $getSelection();
|
|
281
|
+
if ($isRangeSelection(selection)) {
|
|
282
|
+
$patchStyleText(selection, { "font-family": value || "" });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
[editor]
|
|
287
|
+
);
|
|
288
|
+
const fontFamilyItems = useMemo(
|
|
289
|
+
() => FONT_FAMILIES.map((def) => ({
|
|
290
|
+
label: def.label,
|
|
291
|
+
active: state.fontFamily === def.value,
|
|
292
|
+
style: def.value ? { fontFamily: def.value } : void 0,
|
|
293
|
+
onSelect: () => applyFontFamily(def.value)
|
|
294
|
+
})),
|
|
295
|
+
[state.fontFamily, applyFontFamily]
|
|
296
|
+
);
|
|
297
|
+
const headingItems = useMemo(
|
|
298
|
+
() => [
|
|
299
|
+
{
|
|
300
|
+
label: "Text",
|
|
301
|
+
icon: /* @__PURE__ */ jsx(Pilcrow, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
302
|
+
active: state.blockType === "paragraph",
|
|
303
|
+
onSelect: () => {
|
|
304
|
+
editor.update(() => {
|
|
305
|
+
const selection = $getSelection();
|
|
306
|
+
if ($isRangeSelection(selection)) {
|
|
307
|
+
$setBlocksType(selection, () => $createParagraphNode());
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
label: "Heading 1",
|
|
314
|
+
icon: /* @__PURE__ */ jsx(Heading1, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
315
|
+
active: state.blockType === "h1",
|
|
316
|
+
onSelect: () => {
|
|
317
|
+
editor.update(() => {
|
|
318
|
+
const selection = $getSelection();
|
|
319
|
+
if ($isRangeSelection(selection)) {
|
|
320
|
+
$setBlocksType(selection, () => $createHeadingNode("h1"));
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
label: "Heading 2",
|
|
327
|
+
icon: /* @__PURE__ */ jsx(Heading2, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
328
|
+
active: state.blockType === "h2",
|
|
329
|
+
onSelect: () => {
|
|
330
|
+
editor.update(() => {
|
|
331
|
+
const selection = $getSelection();
|
|
332
|
+
if ($isRangeSelection(selection)) {
|
|
333
|
+
$setBlocksType(selection, () => $createHeadingNode("h2"));
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
label: "Heading 3",
|
|
340
|
+
icon: /* @__PURE__ */ jsx(Heading3, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
341
|
+
active: state.blockType === "h3",
|
|
342
|
+
onSelect: () => {
|
|
343
|
+
editor.update(() => {
|
|
344
|
+
const selection = $getSelection();
|
|
345
|
+
if ($isRangeSelection(selection)) {
|
|
346
|
+
$setBlocksType(selection, () => $createHeadingNode("h3"));
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
],
|
|
352
|
+
[editor, state.blockType]
|
|
353
|
+
);
|
|
354
|
+
const containerClassName = className ? `${toolbarContainer} ${className}` : toolbarContainer;
|
|
355
|
+
const h = tooltipHandle;
|
|
356
|
+
return /* @__PURE__ */ jsxs(TooltipProvider, { delay: 300, children: [
|
|
357
|
+
/* @__PURE__ */ jsx(TooltipRoot, { handle: tooltipHandle, disableHoverablePopup: true, children: (({ payload }) => payload !== void 0 ? /* @__PURE__ */ jsxs(TooltipContent, { side: "bottom", sideOffset: 4, children: [
|
|
358
|
+
payload.title,
|
|
359
|
+
payload.shortcut && /* @__PURE__ */ jsx("span", { className: tooltipShortcut, children: payload.shortcut })
|
|
360
|
+
] }) : null) }),
|
|
361
|
+
/* @__PURE__ */ jsx(
|
|
362
|
+
"div",
|
|
363
|
+
{
|
|
364
|
+
className: containerClassName,
|
|
365
|
+
role: "toolbar",
|
|
366
|
+
"aria-label": "Editor toolbar",
|
|
367
|
+
children: /* @__PURE__ */ jsxs("div", { className: toolbarRow, children: [
|
|
368
|
+
/* @__PURE__ */ jsx(
|
|
369
|
+
ToolbarDropdown,
|
|
370
|
+
{
|
|
371
|
+
label: getFontLabel(state.fontFamily),
|
|
372
|
+
title: "Font family",
|
|
373
|
+
items: fontFamilyItems,
|
|
374
|
+
triggerWidth: 76,
|
|
375
|
+
tooltipHandle: h
|
|
376
|
+
}
|
|
377
|
+
),
|
|
378
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
379
|
+
/* @__PURE__ */ jsx(
|
|
380
|
+
ToolbarDropdown,
|
|
381
|
+
{
|
|
382
|
+
label: BLOCK_TYPE_LABELS[state.blockType] ?? "Text",
|
|
383
|
+
title: "Block type",
|
|
384
|
+
items: headingItems,
|
|
385
|
+
triggerWidth: 120,
|
|
386
|
+
tooltipHandle: h
|
|
387
|
+
}
|
|
388
|
+
),
|
|
389
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
390
|
+
/* @__PURE__ */ jsx(
|
|
391
|
+
ToolbarButton,
|
|
392
|
+
{
|
|
393
|
+
icon: /* @__PURE__ */ jsx(Undo, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
394
|
+
title: "Undo",
|
|
395
|
+
shortcut: "Ctrl+Z",
|
|
396
|
+
disabled: !state.canUndo,
|
|
397
|
+
onClick: () => editor.dispatchCommand(UNDO_COMMAND, void 0),
|
|
398
|
+
tooltipHandle: h
|
|
399
|
+
}
|
|
400
|
+
),
|
|
401
|
+
/* @__PURE__ */ jsx(
|
|
402
|
+
ToolbarButton,
|
|
403
|
+
{
|
|
404
|
+
icon: /* @__PURE__ */ jsx(Redo, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
405
|
+
title: "Redo",
|
|
406
|
+
shortcut: "Ctrl+Y",
|
|
407
|
+
disabled: !state.canRedo,
|
|
408
|
+
onClick: () => editor.dispatchCommand(REDO_COMMAND, void 0),
|
|
409
|
+
tooltipHandle: h
|
|
410
|
+
}
|
|
411
|
+
),
|
|
412
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
413
|
+
/* @__PURE__ */ jsx(
|
|
414
|
+
ToolbarButton,
|
|
415
|
+
{
|
|
416
|
+
icon: /* @__PURE__ */ jsx(Bold, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
417
|
+
title: "Bold",
|
|
418
|
+
shortcut: "Ctrl+B",
|
|
419
|
+
active: state.isBold,
|
|
420
|
+
onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold"),
|
|
421
|
+
tooltipHandle: h
|
|
422
|
+
}
|
|
423
|
+
),
|
|
424
|
+
/* @__PURE__ */ jsx(
|
|
425
|
+
ToolbarButton,
|
|
426
|
+
{
|
|
427
|
+
icon: /* @__PURE__ */ jsx(Italic, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
428
|
+
title: "Italic",
|
|
429
|
+
shortcut: "Ctrl+I",
|
|
430
|
+
active: state.isItalic,
|
|
431
|
+
onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic"),
|
|
432
|
+
tooltipHandle: h
|
|
433
|
+
}
|
|
434
|
+
),
|
|
435
|
+
/* @__PURE__ */ jsx(
|
|
436
|
+
ToolbarButton,
|
|
437
|
+
{
|
|
438
|
+
icon: /* @__PURE__ */ jsx(Underline, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
439
|
+
title: "Underline",
|
|
440
|
+
shortcut: "Ctrl+U",
|
|
441
|
+
active: state.isUnderline,
|
|
442
|
+
onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "underline"),
|
|
443
|
+
tooltipHandle: h
|
|
444
|
+
}
|
|
445
|
+
),
|
|
446
|
+
/* @__PURE__ */ jsx(
|
|
447
|
+
ToolbarButton,
|
|
448
|
+
{
|
|
449
|
+
icon: /* @__PURE__ */ jsx(Strikethrough, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
450
|
+
title: "Strikethrough",
|
|
451
|
+
active: state.isStrikethrough,
|
|
452
|
+
onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "strikethrough"),
|
|
453
|
+
tooltipHandle: h
|
|
454
|
+
}
|
|
455
|
+
),
|
|
456
|
+
/* @__PURE__ */ jsx(
|
|
457
|
+
ToolbarButton,
|
|
458
|
+
{
|
|
459
|
+
icon: /* @__PURE__ */ jsx(Code, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
460
|
+
title: "Inline Code",
|
|
461
|
+
active: state.isCode,
|
|
462
|
+
onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "code"),
|
|
463
|
+
tooltipHandle: h
|
|
464
|
+
}
|
|
465
|
+
),
|
|
466
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
467
|
+
/* @__PURE__ */ jsx(
|
|
468
|
+
ToolbarButton,
|
|
469
|
+
{
|
|
470
|
+
icon: /* @__PURE__ */ jsx(Highlighter, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
471
|
+
title: "Highlight",
|
|
472
|
+
active: state.isHighlight,
|
|
473
|
+
onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "highlight"),
|
|
474
|
+
tooltipHandle: h
|
|
475
|
+
}
|
|
476
|
+
),
|
|
477
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
478
|
+
/* @__PURE__ */ jsx(
|
|
479
|
+
ToolbarButton,
|
|
480
|
+
{
|
|
481
|
+
icon: /* @__PURE__ */ jsx(List, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
482
|
+
title: "Bulleted List",
|
|
483
|
+
active: state.blockType === "bullet",
|
|
484
|
+
onClick: () => editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0),
|
|
485
|
+
tooltipHandle: h
|
|
486
|
+
}
|
|
487
|
+
),
|
|
488
|
+
/* @__PURE__ */ jsx(
|
|
489
|
+
ToolbarButton,
|
|
490
|
+
{
|
|
491
|
+
icon: /* @__PURE__ */ jsx(ListOrdered, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
492
|
+
title: "Numbered List",
|
|
493
|
+
active: state.blockType === "number",
|
|
494
|
+
onClick: () => editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0),
|
|
495
|
+
tooltipHandle: h
|
|
496
|
+
}
|
|
497
|
+
),
|
|
498
|
+
/* @__PURE__ */ jsx(
|
|
499
|
+
ToolbarButton,
|
|
500
|
+
{
|
|
501
|
+
icon: /* @__PURE__ */ jsx(ListChecks, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
502
|
+
title: "Checklist",
|
|
503
|
+
active: state.blockType === "check",
|
|
504
|
+
onClick: () => editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, void 0),
|
|
505
|
+
tooltipHandle: h
|
|
506
|
+
}
|
|
507
|
+
),
|
|
508
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
509
|
+
/* @__PURE__ */ jsx(
|
|
510
|
+
ToolbarButton,
|
|
511
|
+
{
|
|
512
|
+
icon: /* @__PURE__ */ jsx(AlignLeft, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
513
|
+
title: "Align Left",
|
|
514
|
+
onClick: () => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "left"),
|
|
515
|
+
tooltipHandle: h
|
|
516
|
+
}
|
|
517
|
+
),
|
|
518
|
+
/* @__PURE__ */ jsx(
|
|
519
|
+
ToolbarButton,
|
|
520
|
+
{
|
|
521
|
+
icon: /* @__PURE__ */ jsx(AlignCenter, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
522
|
+
title: "Align Center",
|
|
523
|
+
onClick: () => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "center"),
|
|
524
|
+
tooltipHandle: h
|
|
525
|
+
}
|
|
526
|
+
),
|
|
527
|
+
/* @__PURE__ */ jsx(
|
|
528
|
+
ToolbarButton,
|
|
529
|
+
{
|
|
530
|
+
icon: /* @__PURE__ */ jsx(AlignRight, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
531
|
+
title: "Align Right",
|
|
532
|
+
onClick: () => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "right"),
|
|
533
|
+
tooltipHandle: h
|
|
534
|
+
}
|
|
535
|
+
),
|
|
536
|
+
/* @__PURE__ */ jsx(
|
|
537
|
+
ToolbarButton,
|
|
538
|
+
{
|
|
539
|
+
icon: /* @__PURE__ */ jsx(AlignJustify, { size: ICON_SIZE, strokeWidth: ICON_STROKE }),
|
|
540
|
+
title: "Justify",
|
|
541
|
+
onClick: () => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, "justify"),
|
|
542
|
+
tooltipHandle: h
|
|
543
|
+
}
|
|
544
|
+
),
|
|
545
|
+
toolbarItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
546
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
547
|
+
toolbarItems.map((item) => /* @__PURE__ */ jsx(
|
|
548
|
+
ToolbarButton,
|
|
549
|
+
{
|
|
550
|
+
icon: item.icon,
|
|
551
|
+
title: item.title,
|
|
552
|
+
shortcut: item.shortcut,
|
|
553
|
+
onClick: () => item.onSelect(editor, ""),
|
|
554
|
+
tooltipHandle: h
|
|
555
|
+
},
|
|
556
|
+
item.title
|
|
557
|
+
))
|
|
558
|
+
] })
|
|
559
|
+
] })
|
|
560
|
+
}
|
|
561
|
+
)
|
|
562
|
+
] });
|
|
563
|
+
}
|
|
564
|
+
export {
|
|
565
|
+
ToolbarPlugin
|
|
566
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #2563eb;--rc-quote-bg: #eff6ff;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}:root.dark{--rc-text: #fafafa;--rc-text-secondary: #a1a1aa;--rc-text-tertiary: #71717a;--rc-text-quaternary: #52525b;--rc-bg: #09090b;--rc-bg-secondary: #18181b;--rc-bg-tertiary: #27272a;--rc-fill: #2a2a2f;--rc-fill-secondary: #222226;--rc-fill-tertiary: #1b1b1f;--rc-fill-quaternary: #131316;--rc-border: #27272a;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #e4e4e7;--rc-code-bg: #27272a;--rc-hr-border: #27272a;--rc-quote-border: #60a5fa;--rc-quote-bg: #1e3a5f;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._88yfio0{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #2563eb;--rc-quote-bg: #eff6ff;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._88yfio1{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #2563eb;--rc-quote-bg: #eff6ff;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.8;--rc-line-height-tight: 1.4;--rc-font-family: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._88yfio2{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #a1a1aa;--rc-quote-bg: #fafafa;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: none;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 2px;--rc-space-sm: 4px;--rc-space-md: 10px;--rc-space-lg: 16px;--rc-space-xl: 20px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 14px;--rc-font-size-small: 12px;--rc-line-height: 1.5;--rc-line-height-tight: 1.3;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 3px;--rc-radius-md: 6px;--rc-radius-lg: 8px}.dark ._88yfio0,[data-theme=dark] ._88yfio0,.dark._88yfio0,[data-theme=dark]._88yfio0,.dark ._88yfio1,[data-theme=dark] ._88yfio1,.dark._88yfio1,[data-theme=dark]._88yfio1,.dark ._88yfio2,[data-theme=dark] ._88yfio2,.dark._88yfio2,[data-theme=dark]._88yfio2{--rc-text: #fafafa;--rc-text-secondary: #a1a1aa;--rc-text-tertiary: #71717a;--rc-text-quaternary: #52525b;--rc-bg: #09090b;--rc-bg-secondary: #18181b;--rc-bg-tertiary: #27272a;--rc-fill: #2a2a2f;--rc-fill-secondary: #222226;--rc-fill-tertiary: #1b1b1f;--rc-fill-quaternary: #131316;--rc-border: #27272a;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #e4e4e7;--rc-code-bg: #27272a;--rc-hr-border: #27272a;--rc-quote-border: #60a5fa;--rc-quote-bg: #1e3a5f;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4)}._1e1ersc0{display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--rc-border);background-color:var(--rc-bg)}._1e1ersc1{display:flex;flex-direction:row;align-items:center;gap:4px;flex-wrap:wrap;min-height:36px;max-width:var(--rc-max-width);margin:0 auto;padding:6px 16px}._1e1ersc2{position:relative;display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:transparent;border-radius:6px;color:var(--rc-text-secondary);cursor:pointer;padding:0;transition:color .15s,background-color .15s}._1e1ersc2:hover{background-color:var(--rc-fill-quaternary);color:var(--rc-text)}._1e1ersc2:active{background-color:var(--rc-fill-tertiary)}._1e1ersc2:disabled{opacity:.35;cursor:default;background-color:transparent;color:var(--rc-text-quaternary)}._1e1ersc2 svg{width:15px;height:15px;stroke-width:2}._1e1ersc3,._1e1ersc3:hover{color:var(--rc-accent);background-color:var(--rc-fill-tertiary)}._1e1ersc4{display:inline-flex;align-items:center;gap:4px;height:32px;padding:0 10px;border:none;background:transparent;border-radius:6px;color:var(--rc-text-secondary);cursor:pointer;font-size:13px;font-weight:500;line-height:1;transition:color .15s,background-color .15s}._1e1ersc4:hover{background-color:var(--rc-fill-quaternary);color:var(--rc-text)}._1e1ersc5{margin-left:auto;display:inline-flex;flex-shrink:0}._1e1ersc6{width:1px;height:24px;background-color:var(--rc-border);margin-inline:6px;flex-shrink:0}._1e1ersc7{background-color:var(--rc-fill-tertiary);color:var(--rc-accent)}._1e1ersc8{font-size:11px;opacity:.7;margin-left:6px}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const toolbarContainer: string;
|
|
2
|
+
export declare const toolbarRow: string;
|
|
3
|
+
export declare const toolbarButton: string;
|
|
4
|
+
export declare const toolbarButtonActive: string;
|
|
5
|
+
export declare const toolbarDropdownTrigger: string;
|
|
6
|
+
export declare const toolbarDropdownTriggerChevron: string;
|
|
7
|
+
export declare const toolbarSeparator: string;
|
|
8
|
+
export declare const toolbarDropdownItemActive: string;
|
|
9
|
+
export declare const tooltipShortcut: string;
|
|
10
|
+
//# sourceMappingURL=styles.css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,QAM3B,CAAA;AAEF,eAAO,MAAM,UAAU,QAUrB,CAAA;AAEF,eAAO,MAAM,aAAa,QA6BxB,CAAA;AAQF,eAAO,MAAM,mBAAmB,QAO9B,CAAA;AAEF,eAAO,MAAM,sBAAsB,QAmBjC,CAAA;AAEF,eAAO,MAAM,6BAA6B,QAIxC,CAAA;AAEF,eAAO,MAAM,gBAAgB,QAM3B,CAAA;AAEF,eAAO,MAAM,yBAAyB,QAGpC,CAAA;AAEF,eAAO,MAAM,eAAe,QAI1B,CAAA"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createTooltipHandle } from '@haklex/rich-editor-ui';
|
|
2
|
+
export interface ToolbarTooltipPayload {
|
|
3
|
+
title: string;
|
|
4
|
+
shortcut?: string;
|
|
5
|
+
}
|
|
6
|
+
export type ToolbarTooltipHandle = ReturnType<typeof createTooltipHandle<ToolbarTooltipPayload>>;
|
|
7
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjE,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAC3C,OAAO,mBAAmB,CAAC,qBAAqB,CAAC,CAClD,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@haklex/rich-plugin-toolbar",
|
|
3
|
+
"version": "0.0.56",
|
|
4
|
+
"description": "Top toolbar plugin for rich editor",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./style.css": "./dist/rich-plugin-toolbar.css"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.mjs",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@haklex/rich-editor": "0.0.56",
|
|
20
|
+
"@haklex/rich-editor-ui": "0.0.56",
|
|
21
|
+
"@haklex/rich-style-token": "0.0.56"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@lexical/list": "^0.41.0",
|
|
25
|
+
"@lexical/react": "^0.41.0",
|
|
26
|
+
"@lexical/rich-text": "^0.41.0",
|
|
27
|
+
"@lexical/selection": "^0.41.0",
|
|
28
|
+
"@lexical/utils": "^0.41.0",
|
|
29
|
+
"@types/react": "^19.2.14",
|
|
30
|
+
"@types/react-dom": "^19.2.3",
|
|
31
|
+
"@vanilla-extract/css": "^1.18.0",
|
|
32
|
+
"@vanilla-extract/vite-plugin": "^5.1.4",
|
|
33
|
+
"lexical": "^0.41.0",
|
|
34
|
+
"lucide-react": "^0.575.0",
|
|
35
|
+
"react": "19.2.4",
|
|
36
|
+
"react-dom": "19.2.4",
|
|
37
|
+
"typescript": "^5.9.3",
|
|
38
|
+
"vite": "^7.3.1",
|
|
39
|
+
"vite-plugin-dts": "^4.5.4"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@lexical/list": "^0.41.0",
|
|
43
|
+
"@lexical/react": "^0.41.0",
|
|
44
|
+
"@lexical/rich-text": "^0.41.0",
|
|
45
|
+
"@lexical/selection": "^0.41.0",
|
|
46
|
+
"@lexical/utils": "^0.41.0",
|
|
47
|
+
"lexical": "^0.41.0",
|
|
48
|
+
"lucide-react": "^0.574.0",
|
|
49
|
+
"react": ">=19",
|
|
50
|
+
"react-dom": ">=19"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "vite build",
|
|
57
|
+
"dev:build": "vite build --watch"
|
|
58
|
+
},
|
|
59
|
+
"types": "./dist/index.d.ts"
|
|
60
|
+
}
|