@bayonai/rich-text-editor 0.1.2 → 1.0.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/BEHAVIOR.md +396 -0
- package/CHANGELOG.md +22 -0
- package/README.md +25 -6
- package/dist/core/blockTree.d.ts +14 -0
- package/dist/core/blockTree.js +126 -0
- package/dist/core/blockTypes.d.ts +6 -0
- package/dist/core/blockTypes.js +5 -0
- package/dist/core/exportImport.d.ts +59 -0
- package/dist/core/exportImport.js +51 -0
- package/dist/core/features.d.ts +59 -0
- package/dist/core/features.js +57 -0
- package/dist/core/imageBlockDiagnostics.d.ts +4 -0
- package/dist/core/imageBlockDiagnostics.js +19 -0
- package/dist/core/proFeatures.d.ts +60 -0
- package/dist/core/proFeatures.js +64 -0
- package/dist/{richText.d.ts → core/richText.d.ts} +2 -0
- package/dist/core/richText.js +566 -0
- package/dist/core/types.d.ts +78 -0
- package/dist/index.d.ts +14 -8
- package/dist/index.js +8 -5
- package/dist/react/editor/RichTextBody.d.ts +28 -0
- package/dist/react/editor/RichTextBody.js +131 -0
- package/dist/react/editor/RichTextEditor.d.ts +138 -0
- package/dist/react/editor/RichTextEditor.js +2925 -0
- package/dist/react/editor/RichTextRenderedBlock.d.ts +20 -0
- package/dist/react/editor/RichTextRenderedBlock.js +162 -0
- package/dist/react/editor/RichTextRenderer.d.ts +13 -0
- package/dist/react/editor/RichTextRenderer.js +16 -0
- package/dist/react/{RichTextTitleInput.d.ts → editor/RichTextTitleInput.d.ts} +11 -1
- package/dist/react/{RichTextTitleInput.js → editor/RichTextTitleInput.js} +17 -2
- package/dist/react/editor/blockActions.d.ts +48 -0
- package/dist/react/editor/blockActions.js +495 -0
- package/dist/react/editor/editorHistory.d.ts +55 -0
- package/dist/react/editor/editorHistory.js +111 -0
- package/dist/react/{editorNavigation.d.ts → editor/editorNavigation.d.ts} +2 -0
- package/dist/react/{editorNavigation.js → editor/editorNavigation.js} +16 -0
- package/dist/react/editor/editorOperations.d.ts +10 -0
- package/dist/react/editor/editorOperations.js +3 -0
- package/dist/react/editor/editorSelection.d.ts +3 -0
- package/dist/react/editor/editorSelection.js +215 -0
- package/dist/react/{editorShortcuts.d.ts → editor/editorShortcuts.d.ts} +10 -0
- package/dist/react/{editorShortcuts.js → editor/editorShortcuts.js} +17 -1
- package/dist/react/{RichTextIcons.d.ts → icons/RichTextIcons.d.ts} +3 -0
- package/dist/react/{RichTextIcons.js → icons/RichTextIcons.js} +9 -0
- package/dist/react/index.d.ts +12 -9
- package/dist/react/index.js +7 -6
- package/dist/react/{EditorSessionProvider.d.ts → session/EditorSessionProvider.d.ts} +2 -2
- package/dist/react/{EditorSessionProvider.js → session/EditorSessionProvider.js} +3 -3
- package/dist/react/{UnsavedChangesDialog.js → session/UnsavedChangesDialog.js} +1 -1
- package/dist/react/styles/RichTextStyles.js +1362 -0
- package/dist/react/{BlockActionTool.d.ts → tools/BlockActionTool.d.ts} +1 -1
- package/dist/react/{BlockActionTool.js → tools/BlockActionTool.js} +6 -2
- package/dist/react/tools/LinkCreationInput.d.ts +9 -0
- package/dist/react/tools/LinkCreationInput.js +38 -0
- package/dist/react/{SelectionFormatToolbar.d.ts → tools/SelectionFormatToolbar.d.ts} +3 -2
- package/dist/react/{SelectionFormatToolbar.js → tools/SelectionFormatToolbar.js} +3 -3
- package/dist/react/tools/SpecialBlockOption.d.ts +9 -0
- package/dist/react/tools/SpecialBlockOption.js +8 -0
- package/dist/react/tools/SpecialBlockTool.d.ts +91 -0
- package/dist/react/tools/SpecialBlockTool.js +125 -0
- package/dist/react/{TranscriptionControl.d.ts → tools/TranscriptionControl.d.ts} +9 -0
- package/dist/react/{TranscriptionControl.js → tools/TranscriptionControl.js} +70 -9
- package/dist/react/tools/blockActionToolState.d.ts +41 -0
- package/dist/react/tools/blockActionToolState.js +177 -0
- package/dist/react/tools/imageBlockDiagnostics.d.ts +2 -0
- package/dist/react/tools/imageBlockDiagnostics.js +12 -0
- package/dist/{session.d.ts → session/session.d.ts} +1 -1
- package/dist-cjs/core/blockTree.js +137 -0
- package/dist-cjs/core/blockTypes.js +9 -0
- package/dist-cjs/core/exportImport.js +56 -0
- package/dist-cjs/core/features.js +62 -0
- package/dist-cjs/core/proFeatures.js +70 -0
- package/dist-cjs/core/richText.js +578 -0
- package/dist-cjs/index.js +22 -6
- package/dist-cjs/react/editor/RichTextBody.js +134 -0
- package/dist-cjs/react/editor/RichTextEditor.js +2956 -0
- package/dist-cjs/react/editor/RichTextRenderedBlock.js +166 -0
- package/dist-cjs/react/editor/RichTextRenderer.js +20 -0
- package/dist-cjs/react/{RichTextTitleInput.js → editor/RichTextTitleInput.js} +18 -2
- package/dist-cjs/react/editor/blockActions.js +518 -0
- package/dist-cjs/react/editor/editorHistory.js +120 -0
- package/dist-cjs/react/{editorNavigation.js → editor/editorNavigation.js} +17 -0
- package/dist-cjs/react/editor/editorOperations.js +6 -0
- package/dist-cjs/react/editor/editorSelection.js +219 -0
- package/dist-cjs/react/{editorShortcuts.js → editor/editorShortcuts.js} +17 -1
- package/dist-cjs/react/{RichTextIcons.js → icons/RichTextIcons.js} +12 -0
- package/dist-cjs/react/index.js +9 -7
- package/dist-cjs/react/{EditorSessionProvider.js → session/EditorSessionProvider.js} +3 -3
- package/dist-cjs/react/{UnsavedChangesDialog.js → session/UnsavedChangesDialog.js} +1 -1
- package/dist-cjs/react/styles/RichTextStyles.js +1365 -0
- package/dist-cjs/react/{BlockActionTool.js → tools/BlockActionTool.js} +6 -2
- package/dist-cjs/react/tools/LinkCreationInput.js +41 -0
- package/dist-cjs/react/{SelectionFormatToolbar.js → tools/SelectionFormatToolbar.js} +3 -3
- package/dist-cjs/react/tools/SpecialBlockOption.js +11 -0
- package/dist-cjs/react/tools/SpecialBlockTool.js +129 -0
- package/dist-cjs/react/{TranscriptionControl.js → tools/TranscriptionControl.js} +71 -9
- package/dist-cjs/react/tools/blockActionToolState.js +186 -0
- package/package.json +3 -2
- package/dist/react/RichTextBody.d.ts +0 -18
- package/dist/react/RichTextBody.js +0 -66
- package/dist/react/RichTextEditor.d.ts +0 -45
- package/dist/react/RichTextEditor.js +0 -1096
- package/dist/react/RichTextRenderedBlock.d.ts +0 -4
- package/dist/react/RichTextRenderedBlock.js +0 -36
- package/dist/react/RichTextRenderer.d.ts +0 -4
- package/dist/react/RichTextRenderer.js +0 -8
- package/dist/react/RichTextStyles.js +0 -719
- package/dist/react/SpecialBlockOption.d.ts +0 -7
- package/dist/react/SpecialBlockOption.js +0 -7
- package/dist/react/SpecialBlockTool.d.ts +0 -42
- package/dist/react/SpecialBlockTool.js +0 -50
- package/dist/react/blockActionToolState.d.ts +0 -18
- package/dist/react/blockActionToolState.js +0 -53
- package/dist/react/blockActions.d.ts +0 -8
- package/dist/react/blockActions.js +0 -111
- package/dist/richText.js +0 -297
- package/dist/types.d.ts +0 -34
- package/dist-cjs/react/RichTextBody.js +0 -69
- package/dist-cjs/react/RichTextEditor.js +0 -1108
- package/dist-cjs/react/RichTextRenderedBlock.js +0 -39
- package/dist-cjs/react/RichTextRenderer.js +0 -11
- package/dist-cjs/react/RichTextStyles.js +0 -722
- package/dist-cjs/react/SpecialBlockOption.js +0 -10
- package/dist-cjs/react/SpecialBlockTool.js +0 -54
- package/dist-cjs/react/blockActionToolState.js +0 -58
- package/dist-cjs/react/blockActions.js +0 -119
- package/dist-cjs/richText.js +0 -307
- /package/dist/{types.js → core/types.js} +0 -0
- /package/dist/{writingStats.d.ts → core/writingStats.d.ts} +0 -0
- /package/dist/{writingStats.js → core/writingStats.js} +0 -0
- /package/dist/react/{RichTextDocumentSurface.d.ts → editor/RichTextDocumentSurface.d.ts} +0 -0
- /package/dist/react/{RichTextDocumentSurface.js → editor/RichTextDocumentSurface.js} +0 -0
- /package/dist/react/{UnsavedChangesDialog.d.ts → session/UnsavedChangesDialog.d.ts} +0 -0
- /package/dist/react/{RichTextStyles.d.ts → styles/RichTextStyles.d.ts} +0 -0
- /package/dist/react/{richTextBlockStyles.d.ts → styles/richTextBlockStyles.d.ts} +0 -0
- /package/dist/react/{richTextBlockStyles.js → styles/richTextBlockStyles.js} +0 -0
- /package/dist/react/{specialBlockStyles.d.ts → styles/specialBlockStyles.d.ts} +0 -0
- /package/dist/react/{specialBlockStyles.js → styles/specialBlockStyles.js} +0 -0
- /package/dist/{saveControl.d.ts → session/saveControl.d.ts} +0 -0
- /package/dist/{saveControl.js → session/saveControl.js} +0 -0
- /package/dist/{session.js → session/session.js} +0 -0
- /package/dist/{sessionRegistry.d.ts → session/sessionRegistry.d.ts} +0 -0
- /package/dist/{sessionRegistry.js → session/sessionRegistry.js} +0 -0
- /package/dist-cjs/{types.js → core/types.js} +0 -0
- /package/dist-cjs/{writingStats.js → core/writingStats.js} +0 -0
- /package/dist-cjs/react/{RichTextDocumentSurface.js → editor/RichTextDocumentSurface.js} +0 -0
- /package/dist-cjs/react/{richTextBlockStyles.js → styles/richTextBlockStyles.js} +0 -0
- /package/dist-cjs/react/{specialBlockStyles.js → styles/specialBlockStyles.js} +0 -0
- /package/dist-cjs/{saveControl.js → session/saveControl.js} +0 -0
- /package/dist-cjs/{session.js → session/session.js} +0 -0
- /package/dist-cjs/{sessionRegistry.js → session/sessionRegistry.js} +0 -0
|
@@ -4,13 +4,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.BlockActionTool = BlockActionTool;
|
|
5
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
6
|
const react_1 = require("react");
|
|
7
|
-
const RichTextIcons_1 = require("
|
|
7
|
+
const RichTextIcons_1 = require("../icons/RichTextIcons");
|
|
8
|
+
const blockActionToolState_1 = require("../tools/blockActionToolState");
|
|
8
9
|
function BlockActionTool({ activeBlockId, draggedBlockId, onAction, onHoverChange, onPointerDragEnd, onPointerDragMove, onPointerDragStart, onToggleMenu, placement, }) {
|
|
9
10
|
const menuOpen = activeBlockId === placement.blockId;
|
|
10
11
|
const dragging = draggedBlockId === placement.blockId;
|
|
11
12
|
const pointerStartRef = (0, react_1.useRef)(null);
|
|
12
13
|
const suppressClickRef = (0, react_1.useRef)(false);
|
|
13
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: `bayon-rte-block-tool${menuOpen ? " bayon-rte-block-tool--menu-open" : ""}
|
|
14
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: `bayon-rte-block-tool${menuOpen ? " bayon-rte-block-tool--menu-open" : ""}${placement.blockType === "image" ? " bayon-rte-block-tool--image" : ""}`, "data-block-action-tool": placement.blockId, onMouseEnter: () => onHoverChange(true), onMouseLeave: () => onHoverChange(false), style: {
|
|
15
|
+
"--bayon-rte-block-tool-left": `${placement.left}px`,
|
|
16
|
+
"--bayon-rte-block-tool-top": `${(0, blockActionToolState_1.getBlockActionToolRenderTop)(placement)}px`,
|
|
17
|
+
}, children: [menuOpen ? ((0, jsx_runtime_1.jsx)("button", { "aria-label": "Close block actions", className: "bayon-rte-icon-button bayon-rte-block-handle bayon-rte-block-handle--menu-open", onClick: () => onToggleMenu(placement.blockId), onMouseDown: (event) => event.preventDefault(), title: "Close block actions", type: "button", children: (0, jsx_runtime_1.jsx)(RichTextIcons_1.CloseIcon, { size: 19 }) })) : ((0, jsx_runtime_1.jsx)("button", { "aria-label": "Block actions", "aria-pressed": dragging, className: `bayon-rte-icon-button bayon-rte-block-handle${dragging ? " bayon-rte-block-handle--dragging" : ""}`, onClick: () => {
|
|
14
18
|
if (suppressClickRef.current) {
|
|
15
19
|
suppressClickRef.current = false;
|
|
16
20
|
return;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.LinkCreationInput = LinkCreationInput;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const RichTextIcons_1 = require("../icons/RichTextIcons");
|
|
8
|
+
// Renders the focused URL input used by selection link creation.
|
|
9
|
+
function LinkCreationInput({ left, onCancel, onSubmit, placement, top, }) {
|
|
10
|
+
const inputRef = (0, react_1.useRef)(null);
|
|
11
|
+
const [href, setHref] = (0, react_1.useState)("https://");
|
|
12
|
+
(0, react_1.useEffect)(() => {
|
|
13
|
+
inputRef.current?.focus();
|
|
14
|
+
inputRef.current?.select();
|
|
15
|
+
}, []);
|
|
16
|
+
function handleSubmit(event) {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
submitHref();
|
|
19
|
+
}
|
|
20
|
+
function submitHref() {
|
|
21
|
+
const nextHref = href.trim();
|
|
22
|
+
if (!nextHref) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
onSubmit(nextHref);
|
|
26
|
+
}
|
|
27
|
+
function guardButtonMouseDown(event) {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
event.stopPropagation();
|
|
30
|
+
}
|
|
31
|
+
function handleKeyDown(event) {
|
|
32
|
+
if (event.key === "Escape") {
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
onCancel();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return ((0, jsx_runtime_1.jsxs)("form", { "aria-label": "Create link", className: `bayon-rte-link-input bayon-rte-link-input--${placement}`, onSubmit: handleSubmit, style: {
|
|
38
|
+
"--bayon-rte-link-input-left": `${left}px`,
|
|
39
|
+
"--bayon-rte-link-input-top": `${top}px`,
|
|
40
|
+
}, children: [(0, jsx_runtime_1.jsx)("input", { "aria-label": "Link URL", className: "bayon-rte-link-input__field", onChange: (event) => setHref(event.target.value), onKeyDown: handleKeyDown, placeholder: "Paste or type a URL", ref: inputRef, type: "url", value: href }), (0, jsx_runtime_1.jsx)("button", { "aria-label": "Apply link", className: "bayon-rte-icon-button bayon-rte-link-input__action", onClick: submitHref, onMouseDown: guardButtonMouseDown, title: "Apply link", type: "button", children: (0, jsx_runtime_1.jsx)(RichTextIcons_1.LinkIcon, { size: 16 }) }), (0, jsx_runtime_1.jsx)("button", { "aria-label": "Cancel link", className: "bayon-rte-icon-button bayon-rte-link-input__action", onClick: onCancel, onMouseDown: guardButtonMouseDown, title: "Cancel link", type: "button", children: (0, jsx_runtime_1.jsx)(RichTextIcons_1.CloseIcon, { size: 16 }) })] }));
|
|
41
|
+
}
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.selectionActions = void 0;
|
|
5
5
|
exports.SelectionFormatToolbar = SelectionFormatToolbar;
|
|
6
6
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
-
const RichTextIcons_1 = require("
|
|
7
|
+
const RichTextIcons_1 = require("../icons/RichTextIcons");
|
|
8
8
|
exports.selectionActions = [
|
|
9
9
|
{ command: "bold", icon: RichTextIcons_1.BoldIcon, label: "Bold" },
|
|
10
10
|
{ command: "italic", icon: RichTextIcons_1.ItalicIcon, label: "Italic" },
|
|
@@ -14,8 +14,8 @@ exports.selectionActions = [
|
|
|
14
14
|
{ command: "code", icon: RichTextIcons_1.CodeIcon, label: "Code" },
|
|
15
15
|
];
|
|
16
16
|
// Renders the floating toolbar used to format the current text selection.
|
|
17
|
-
function SelectionFormatToolbar({ left, onAction, top, }) {
|
|
18
|
-
return ((0, jsx_runtime_1.jsx)("div", { className:
|
|
17
|
+
function SelectionFormatToolbar({ left, onAction, placement, top, }) {
|
|
18
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `bayon-rte-toolbar bayon-rte-toolbar--${placement}`, style: {
|
|
19
19
|
"--bayon-rte-toolbar-left": `${left}px`,
|
|
20
20
|
"--bayon-rte-toolbar-top": `${top}px`,
|
|
21
21
|
}, children: exports.selectionActions.map(({ command, icon: Icon, label }, index) => ((0, jsx_runtime_1.jsx)("button", { "aria-label": label, className: `bayon-rte-icon-button bayon-rte-toolbar__button${index === 3 ? " bayon-rte-toolbar__button--divider" : ""}`, onClick: () => onAction(command), onMouseDown: (event) => event.preventDefault(), title: label, type: "button", children: (0, jsx_runtime_1.jsx)(Icon, { size: 18 }) }, command))) }));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SpecialBlockOption = SpecialBlockOption;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
// Renders one selectable special-block action inside the insertion tool.
|
|
7
|
+
function SpecialBlockOption({ action, disabled = false, onInsert, title = action.label, }) {
|
|
8
|
+
const Icon = action.icon;
|
|
9
|
+
const label = disabled ? `${action.label} unavailable` : action.label;
|
|
10
|
+
return ((0, jsx_runtime_1.jsx)("button", { "aria-label": label, className: "bayon-rte-icon-button bayon-rte-special-button", disabled: disabled, onClick: () => onInsert(action), onMouseDown: (event) => event.preventDefault(), title: title, type: "button", children: (0, jsx_runtime_1.jsx)(Icon, { size: 18 }) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.specialBlockActions = void 0;
|
|
5
|
+
exports.SpecialBlockTool = SpecialBlockTool;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const features_1 = require("../../core/features");
|
|
8
|
+
const RichTextIcons_1 = require("../icons/RichTextIcons");
|
|
9
|
+
const SpecialBlockOption_1 = require("./SpecialBlockOption");
|
|
10
|
+
const specialBlockIcons = [
|
|
11
|
+
RichTextIcons_1.ImageIcon,
|
|
12
|
+
RichTextIcons_1.QuoteIcon,
|
|
13
|
+
RichTextIcons_1.TitleIcon,
|
|
14
|
+
RichTextIcons_1.BulletListIcon,
|
|
15
|
+
RichTextIcons_1.ToggleIcon,
|
|
16
|
+
RichTextIcons_1.CodeIcon,
|
|
17
|
+
RichTextIcons_1.DataObjectIcon,
|
|
18
|
+
RichTextIcons_1.DividerIcon,
|
|
19
|
+
];
|
|
20
|
+
exports.specialBlockActions = [
|
|
21
|
+
{
|
|
22
|
+
icon: RichTextIcons_1.PasteIcon,
|
|
23
|
+
kind: "paste",
|
|
24
|
+
label: "Paste",
|
|
25
|
+
selectDefault: false,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
featureId: "image-upload",
|
|
29
|
+
fallbackHtml: '<figure data-placeholder="image"><p>Image placeholder</p></figure>',
|
|
30
|
+
icon: RichTextIcons_1.ImageIcon,
|
|
31
|
+
kind: "image-upload",
|
|
32
|
+
label: "Image",
|
|
33
|
+
selectDefault: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
html: '<blockquote data-placeholder="quote"></blockquote>',
|
|
37
|
+
icon: RichTextIcons_1.QuoteIcon,
|
|
38
|
+
label: "Quote",
|
|
39
|
+
selectDefault: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
html: "<h2>Title</h2>",
|
|
43
|
+
icon: RichTextIcons_1.TitleIcon,
|
|
44
|
+
label: "Title",
|
|
45
|
+
selectDefault: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
html: '<ul data-rich-text-children=""><li data-rich-text-bullet=""><div data-rich-text-row=""><span data-bullet-marker="" contenteditable="false" aria-hidden="true"></span><span data-bullet-label="" data-placeholder="List item"></span></div></li></ul>',
|
|
49
|
+
icon: RichTextIcons_1.BulletListIcon,
|
|
50
|
+
label: "Bullet list",
|
|
51
|
+
selectDefault: false,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
focusPosition: "start",
|
|
55
|
+
focusSelector: "[data-toggle-label]",
|
|
56
|
+
html: '<ul data-rich-text-children=""><li data-rich-text-toggle="" data-toggle-collapsed="false"><div data-rich-text-row=""><button aria-label="Collapse toggle" class="bayon-rte-toggle-button" contenteditable="false" data-toggle-collapse="" title="Collapse toggle" type="button"><span class="bayon-rte-toggle-caret" aria-hidden="true"></span></button><span data-toggle-label="" data-placeholder="Toggle title"></span></div><div data-toggle-content="" data-placeholder="Toggle content"></div></li></ul>',
|
|
57
|
+
icon: RichTextIcons_1.ToggleIcon,
|
|
58
|
+
label: "Toggle",
|
|
59
|
+
selectDefault: false,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
html: "<pre><code>Code block</code></pre>",
|
|
63
|
+
icon: RichTextIcons_1.CodeIcon,
|
|
64
|
+
label: "Code block",
|
|
65
|
+
selectDefault: true,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
html: "<pre><code>Embedded HTML</code></pre>",
|
|
69
|
+
icon: RichTextIcons_1.DataObjectIcon,
|
|
70
|
+
label: "Embedded HTML",
|
|
71
|
+
selectDefault: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
html: "<hr>",
|
|
75
|
+
icon: RichTextIcons_1.DividerIcon,
|
|
76
|
+
label: "Divider",
|
|
77
|
+
selectDefault: false,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
// Renders the floating insertion control for image, quote, title, code, and divider blocks.
|
|
81
|
+
function SpecialBlockTool({ features, lockedFeatureMode = "hide", onFeatureGate, onHoverChange, onInsert, onOpenChange, open, toolHover, top, visible, }) {
|
|
82
|
+
const actionItems = exports.specialBlockActions
|
|
83
|
+
.map((action) => ({
|
|
84
|
+
access: getSpecialBlockActionAccess(features, action),
|
|
85
|
+
action,
|
|
86
|
+
}))
|
|
87
|
+
.filter((item) => item.access.enabled ||
|
|
88
|
+
!hasSpecialBlockActionFeature(item.action) ||
|
|
89
|
+
lockedFeatureMode === "disabled");
|
|
90
|
+
return ((0, jsx_runtime_1.jsxs)("div", { "aria-hidden": !visible, className: `bayon-rte-special-tool${visible ? " bayon-rte-special-tool--visible" : ""}`, onMouseEnter: () => onHoverChange(true), onMouseLeave: () => {
|
|
91
|
+
onHoverChange(false);
|
|
92
|
+
onOpenChange(false);
|
|
93
|
+
}, style: { "--bayon-rte-special-tool-top": `${top}px` }, children: [(0, jsx_runtime_1.jsx)("button", { "aria-label": open ? "Close special blocks" : "Add special block", "aria-expanded": open, className: "bayon-rte-icon-button bayon-rte-special-button bayon-rte-special-toggle", onClick: () => onOpenChange(!open), style: {
|
|
94
|
+
transform: visible
|
|
95
|
+
? "scale(1) rotate(0deg)"
|
|
96
|
+
: "scale(0.84) rotate(-12deg)",
|
|
97
|
+
}, title: open ? "Close special blocks" : "Add special block", type: "button", children: open ? (0, jsx_runtime_1.jsx)(RichTextIcons_1.CloseIcon, { size: 20 }) : (0, jsx_runtime_1.jsx)(RichTextIcons_1.AddIcon, { size: 20 }) }), open ? ((0, jsx_runtime_1.jsx)("div", { className: "bayon-rte-special-tool__actions", children: actionItems.map(({ access, action }) => {
|
|
98
|
+
const disabled = !access.enabled;
|
|
99
|
+
return ((0, jsx_runtime_1.jsx)(SpecialBlockOption_1.SpecialBlockOption, { action: action, disabled: disabled, onInsert: disabled
|
|
100
|
+
? () => onFeatureGate?.({
|
|
101
|
+
featureId: access.featureId,
|
|
102
|
+
label: access.label,
|
|
103
|
+
reason: access.reason,
|
|
104
|
+
})
|
|
105
|
+
: onInsert, title: disabled
|
|
106
|
+
? `${access.label}: ${getLockedFeatureTitle(access)}`
|
|
107
|
+
: action.label }, action.label));
|
|
108
|
+
}) })) : null] }));
|
|
109
|
+
}
|
|
110
|
+
function getSpecialBlockActionAccess(features, action) {
|
|
111
|
+
return hasSpecialBlockActionFeature(action)
|
|
112
|
+
? (0, features_1.getRichTextFeatureAccess)(features, action.featureId)
|
|
113
|
+
: { enabled: true };
|
|
114
|
+
}
|
|
115
|
+
function hasSpecialBlockActionFeature(action) {
|
|
116
|
+
return "featureId" in action;
|
|
117
|
+
}
|
|
118
|
+
function getLockedFeatureTitle(access) {
|
|
119
|
+
if (access.enabled) {
|
|
120
|
+
return access.label;
|
|
121
|
+
}
|
|
122
|
+
if (access.reason === "premium") {
|
|
123
|
+
return "Premium feature";
|
|
124
|
+
}
|
|
125
|
+
if (access.reason === "unavailable") {
|
|
126
|
+
return "Unavailable";
|
|
127
|
+
}
|
|
128
|
+
return "Disabled";
|
|
129
|
+
}
|
|
@@ -3,37 +3,39 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.TranscriptionControl = TranscriptionControl;
|
|
5
5
|
exports.getTranscriptionControlState = getTranscriptionControlState;
|
|
6
|
+
exports.resolveSpeechRecognitionSupport = resolveSpeechRecognitionSupport;
|
|
6
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
8
|
const react_1 = require("react");
|
|
8
|
-
const RichTextIcons_1 = require("
|
|
9
|
+
const RichTextIcons_1 = require("../icons/RichTextIcons");
|
|
9
10
|
function TranscriptionControl({ disabled = false, language, onTranscript, }) {
|
|
10
11
|
const recognitionRef = (0, react_1.useRef)(null);
|
|
11
12
|
const [errorMessage, setErrorMessage] = (0, react_1.useState)(null);
|
|
12
13
|
const [lastInserted, setLastInserted] = (0, react_1.useState)(false);
|
|
13
14
|
const [recording, setRecording] = (0, react_1.useState)(false);
|
|
14
|
-
const
|
|
15
|
+
const recognitionSupport = getSpeechRecognitionSupport();
|
|
15
16
|
const controlState = getTranscriptionControlState({
|
|
16
17
|
disabled,
|
|
17
18
|
errorMessage,
|
|
18
19
|
lastInserted,
|
|
19
20
|
recording,
|
|
20
|
-
supported:
|
|
21
|
+
supported: recognitionSupport.supported,
|
|
21
22
|
});
|
|
22
23
|
(0, react_1.useEffect)(() => {
|
|
23
24
|
return () => {
|
|
24
|
-
recognitionRef.current
|
|
25
|
+
stopRecognitionInstance(recognitionRef.current);
|
|
25
26
|
recognitionRef.current = null;
|
|
26
27
|
};
|
|
27
28
|
}, []);
|
|
28
29
|
function stopRecording() {
|
|
29
|
-
recognitionRef.current
|
|
30
|
+
stopRecognitionInstance(recognitionRef.current);
|
|
30
31
|
recognitionRef.current = null;
|
|
31
32
|
setRecording(false);
|
|
32
33
|
setLastInserted(false);
|
|
33
34
|
}
|
|
34
35
|
function startRecording() {
|
|
36
|
+
const support = getSpeechRecognitionSupport();
|
|
35
37
|
const Recognition = getSpeechRecognitionConstructor();
|
|
36
|
-
if (!Recognition || disabled) {
|
|
38
|
+
if (!support.supported || !Recognition || disabled) {
|
|
37
39
|
setErrorMessage("Browser transcription is unavailable.");
|
|
38
40
|
setLastInserted(false);
|
|
39
41
|
return;
|
|
@@ -63,8 +65,17 @@ function TranscriptionControl({ disabled = false, language, onTranscript, }) {
|
|
|
63
65
|
recognitionRef.current = recognition;
|
|
64
66
|
setErrorMessage(null);
|
|
65
67
|
setLastInserted(false);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
try {
|
|
69
|
+
recognition.start();
|
|
70
|
+
setRecording(true);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.warn("[rich-text-editor] Failed to start browser transcription.", error);
|
|
74
|
+
recognitionRef.current = null;
|
|
75
|
+
setErrorMessage("Transcription stopped.");
|
|
76
|
+
setLastInserted(false);
|
|
77
|
+
setRecording(false);
|
|
78
|
+
}
|
|
68
79
|
}
|
|
69
80
|
return ((0, jsx_runtime_1.jsxs)("div", { "aria-label": "Browser transcription", className: `bayon-rte-transcription bayon-rte-transcription--${controlState.tone}`, children: [(0, jsx_runtime_1.jsx)("button", { "aria-label": controlState.buttonLabel, "aria-pressed": recording, className: `bayon-rte-icon-button bayon-rte-transcription__button${recording ? " bayon-rte-transcription__button--recording" : ""}`, disabled: controlState.disabled, onClick: () => (recording ? stopRecording() : startRecording()), title: controlState.buttonLabel, type: "button", children: recording ? (0, jsx_runtime_1.jsx)(RichTextIcons_1.StopIcon, { size: 18 }) : (0, jsx_runtime_1.jsx)(RichTextIcons_1.MicIcon, { size: 18 }) }), controlState.statusLabel ? ((0, jsx_runtime_1.jsxs)("small", { className: "bayon-rte-transcription__status", role: "status", children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", className: "bayon-rte-transcription__dot" }), controlState.statusLabel] })) : null] }));
|
|
70
81
|
}
|
|
@@ -113,7 +124,58 @@ function getSpeechRecognitionConstructor() {
|
|
|
113
124
|
return null;
|
|
114
125
|
}
|
|
115
126
|
const speechWindow = window;
|
|
116
|
-
return speechWindow.SpeechRecognition ??
|
|
127
|
+
return (speechWindow.SpeechRecognition ??
|
|
128
|
+
speechWindow.webkitSpeechRecognition ??
|
|
129
|
+
null);
|
|
130
|
+
}
|
|
131
|
+
function getSpeechRecognitionSupport() {
|
|
132
|
+
if (typeof window === "undefined") {
|
|
133
|
+
return {
|
|
134
|
+
reason: "available",
|
|
135
|
+
supported: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const speechWindow = window;
|
|
139
|
+
return resolveSpeechRecognitionSupport({
|
|
140
|
+
hasSpeechRecognition: Boolean(speechWindow.SpeechRecognition),
|
|
141
|
+
hasWebkitSpeechRecognition: Boolean(speechWindow.webkitSpeechRecognition),
|
|
142
|
+
userAgent: window.navigator.userAgent,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function resolveSpeechRecognitionSupport({ hasSpeechRecognition, hasWebkitSpeechRecognition, userAgent, }) {
|
|
146
|
+
if (!hasSpeechRecognition && !hasWebkitSpeechRecognition) {
|
|
147
|
+
return {
|
|
148
|
+
reason: "missing-api",
|
|
149
|
+
supported: false,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (hasWebkitSpeechRecognition && isMobileSafariUserAgent(userAgent)) {
|
|
153
|
+
return {
|
|
154
|
+
reason: "mobile-safari",
|
|
155
|
+
supported: false,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
reason: "available",
|
|
160
|
+
supported: true,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function isMobileSafariUserAgent(userAgent) {
|
|
164
|
+
const normalizedUserAgent = userAgent.toLowerCase();
|
|
165
|
+
const isIosMobile = /\b(iphone|ipod|ipad)\b/.test(normalizedUserAgent) ||
|
|
166
|
+
(normalizedUserAgent.includes("macintosh") &&
|
|
167
|
+
normalizedUserAgent.includes("mobile"));
|
|
168
|
+
const isSafari = normalizedUserAgent.includes("safari");
|
|
169
|
+
const isOtherIosBrowser = /\b(crios|fxios|edgios|opios)\b/.test(normalizedUserAgent);
|
|
170
|
+
return isIosMobile && isSafari && !isOtherIosBrowser;
|
|
171
|
+
}
|
|
172
|
+
function stopRecognitionInstance(recognition) {
|
|
173
|
+
try {
|
|
174
|
+
recognition?.stop();
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.warn("[rich-text-editor] Failed to stop browser transcription.", error);
|
|
178
|
+
}
|
|
117
179
|
}
|
|
118
180
|
function readFinalTranscript(event) {
|
|
119
181
|
let text = "";
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBlockActionToolAnchorTop = getBlockActionToolAnchorTop;
|
|
4
|
+
exports.getBlockActionToolRenderTop = getBlockActionToolRenderTop;
|
|
5
|
+
exports.getVisibleBlockActionToolPlacements = getVisibleBlockActionToolPlacements;
|
|
6
|
+
exports.getPointerDropTarget = getPointerDropTarget;
|
|
7
|
+
exports.getPointerDragDropResult = getPointerDragDropResult;
|
|
8
|
+
exports.getPointerHoverLaneBlockId = getPointerHoverLaneBlockId;
|
|
9
|
+
exports.getUniqueBlockActionToolPlacements = getUniqueBlockActionToolPlacements;
|
|
10
|
+
const hoverLaneLeftWidth = 72;
|
|
11
|
+
const hoverLaneRightWidth = 36;
|
|
12
|
+
const hoverLaneVerticalPadding = 10;
|
|
13
|
+
const raisedBlockActionToolTopOffset = -4;
|
|
14
|
+
function getBlockActionToolAnchorTop(blockType, top) {
|
|
15
|
+
return blockType === "checkbox" ? top + raisedBlockActionToolTopOffset : top;
|
|
16
|
+
}
|
|
17
|
+
function getBlockActionToolRenderTop(placement) {
|
|
18
|
+
return placement.toolTop ?? placement.top;
|
|
19
|
+
}
|
|
20
|
+
function getVisibleBlockActionToolPlacements(placements, state) {
|
|
21
|
+
const uniquePlacements = getUniqueBlockActionToolPlacements(placements);
|
|
22
|
+
const visibleBlockId = state.activeBlockId ??
|
|
23
|
+
state.draggedBlockId ??
|
|
24
|
+
state.hoveredBlockId ??
|
|
25
|
+
state.focusedBlockId;
|
|
26
|
+
return visibleBlockId
|
|
27
|
+
? uniquePlacements.filter((placement) => placement.blockId === visibleBlockId)
|
|
28
|
+
: [];
|
|
29
|
+
}
|
|
30
|
+
function getPointerDropTarget(placements, draggedBlockId, pointerClientY, pointerClientX) {
|
|
31
|
+
const targetPlacements = placements.filter((placement) => {
|
|
32
|
+
return placement.blockId !== draggedBlockId;
|
|
33
|
+
});
|
|
34
|
+
const rowPlacements = targetPlacements.filter((placement) => {
|
|
35
|
+
return placement.dropRole !== "content";
|
|
36
|
+
});
|
|
37
|
+
const contentPlacements = targetPlacements.filter((placement) => {
|
|
38
|
+
return placement.dropRole === "content";
|
|
39
|
+
});
|
|
40
|
+
const targetPlacement = findContainingPlacement(rowPlacements, pointerClientY) ??
|
|
41
|
+
findContainingPlacement(contentPlacements, pointerClientY) ??
|
|
42
|
+
getClosestPlacement(rowPlacements, pointerClientY) ??
|
|
43
|
+
getClosestPlacement(contentPlacements, pointerClientY);
|
|
44
|
+
if (!targetPlacement) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (targetPlacement.dropRole === "content") {
|
|
48
|
+
return {
|
|
49
|
+
placement: "inside",
|
|
50
|
+
targetBlockId: targetPlacement.blockId,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (pointerClientX !== undefined &&
|
|
54
|
+
pointerClientX >= targetPlacement.left + 24) {
|
|
55
|
+
return {
|
|
56
|
+
placement: "inside",
|
|
57
|
+
targetBlockId: targetPlacement.blockId,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const visualAncestorDropTarget = pointerClientX === undefined
|
|
61
|
+
? null
|
|
62
|
+
: getVisualAncestorDropTarget(rowPlacements, targetPlacement, pointerClientX);
|
|
63
|
+
if (visualAncestorDropTarget) {
|
|
64
|
+
return visualAncestorDropTarget;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
placement: pointerClientY >
|
|
68
|
+
targetPlacement.top + (targetPlacement.bottom - targetPlacement.top) / 2
|
|
69
|
+
? "after"
|
|
70
|
+
: "before",
|
|
71
|
+
targetBlockId: targetPlacement.blockId,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function getPointerDragDropResult(placements, draggedBlockId, pointerClientY, pointerClientX) {
|
|
75
|
+
const containingPlacement = findContainingPlacement(placements, pointerClientY);
|
|
76
|
+
if (containingPlacement?.blockId === draggedBlockId) {
|
|
77
|
+
return {
|
|
78
|
+
dropTarget: getDropTargetFromPlacement(containingPlacement, pointerClientY, pointerClientX),
|
|
79
|
+
status: "invalid",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const dropTarget = getPointerDropTarget(placements, draggedBlockId, pointerClientY, pointerClientX);
|
|
83
|
+
if (!dropTarget) {
|
|
84
|
+
return { status: "invalid" };
|
|
85
|
+
}
|
|
86
|
+
const targetPlacement = placements.find((placement) => {
|
|
87
|
+
return (placement.blockId === dropTarget.targetBlockId &&
|
|
88
|
+
(dropTarget.placement === "inside" || placement.dropRole !== "content"));
|
|
89
|
+
});
|
|
90
|
+
if (dropTarget.targetBlockId === draggedBlockId ||
|
|
91
|
+
targetPlacement?.ancestorBlockIds?.includes(draggedBlockId)) {
|
|
92
|
+
return { dropTarget, status: "invalid" };
|
|
93
|
+
}
|
|
94
|
+
return { dropTarget, status: "valid" };
|
|
95
|
+
}
|
|
96
|
+
function getDropTargetFromPlacement(placement, pointerClientY, pointerClientX) {
|
|
97
|
+
if (placement.dropRole === "content" ||
|
|
98
|
+
(pointerClientX !== undefined && pointerClientX >= placement.left + 24)) {
|
|
99
|
+
return {
|
|
100
|
+
placement: "inside",
|
|
101
|
+
targetBlockId: placement.blockId,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
placement: pointerClientY > placement.top + (placement.bottom - placement.top) / 2
|
|
106
|
+
? "after"
|
|
107
|
+
: "before",
|
|
108
|
+
targetBlockId: placement.blockId,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function getPointerHoverLaneBlockId(placements, pointerTop, pointerLeft) {
|
|
112
|
+
const placement = getUniqueBlockActionToolPlacements(placements).find((currentPlacement) => {
|
|
113
|
+
return (pointerTop >= currentPlacement.top - hoverLaneVerticalPadding &&
|
|
114
|
+
pointerTop <= currentPlacement.bottom + hoverLaneVerticalPadding &&
|
|
115
|
+
pointerLeft >= currentPlacement.left - hoverLaneLeftWidth &&
|
|
116
|
+
pointerLeft <= currentPlacement.left + hoverLaneRightWidth);
|
|
117
|
+
});
|
|
118
|
+
return placement?.blockId ?? null;
|
|
119
|
+
}
|
|
120
|
+
function getUniqueBlockActionToolPlacements(placements) {
|
|
121
|
+
const seenBlockIds = new Set();
|
|
122
|
+
return placements.filter((placement) => {
|
|
123
|
+
if (placement.dropRole === "content") {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (seenBlockIds.has(placement.blockId)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
seenBlockIds.add(placement.blockId);
|
|
130
|
+
return true;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function getVisualAncestorDropTarget(rowPlacements, targetPlacement, pointerClientX) {
|
|
134
|
+
const targetDepth = targetPlacement.depth ?? targetPlacement.ancestorBlockIds?.length ?? 0;
|
|
135
|
+
if (targetDepth <= 0 || !targetPlacement.ancestorBlockIds?.length) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const desiredDepth = clamp(Math.round((pointerClientX - getMinimumPlacementLeft(rowPlacements)) /
|
|
139
|
+
getPlacementIndentSize(rowPlacements)), 0, targetDepth);
|
|
140
|
+
if (desiredDepth >= targetDepth) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const targetBlockId = targetPlacement.ancestorBlockIds[desiredDepth];
|
|
144
|
+
return targetBlockId
|
|
145
|
+
? {
|
|
146
|
+
placement: "after",
|
|
147
|
+
targetBlockId,
|
|
148
|
+
}
|
|
149
|
+
: null;
|
|
150
|
+
}
|
|
151
|
+
function getPlacementIndentSize(placements) {
|
|
152
|
+
const leftPositions = Array.from(new Set(placements.map((placement) => Math.round(placement.left)))).sort((a, b) => a - b);
|
|
153
|
+
const positiveDiffs = leftPositions
|
|
154
|
+
.slice(1)
|
|
155
|
+
.map((left, index) => left - (leftPositions[index] ?? left))
|
|
156
|
+
.filter((diff) => diff > 0);
|
|
157
|
+
return Math.min(...positiveDiffs, 42);
|
|
158
|
+
}
|
|
159
|
+
function getMinimumPlacementLeft(placements) {
|
|
160
|
+
return Math.min(...placements.map((placement) => placement.left), 0);
|
|
161
|
+
}
|
|
162
|
+
function clamp(value, min, max) {
|
|
163
|
+
return Math.min(Math.max(value, min), max);
|
|
164
|
+
}
|
|
165
|
+
function getDistanceToPlacement(placement, pointerClientY) {
|
|
166
|
+
if (pointerClientY >= placement.top && pointerClientY <= placement.bottom) {
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
return Math.min(Math.abs(pointerClientY - placement.top), Math.abs(pointerClientY - placement.bottom));
|
|
170
|
+
}
|
|
171
|
+
function findContainingPlacement(placements, pointerClientY) {
|
|
172
|
+
return placements.find((placement) => {
|
|
173
|
+
return (pointerClientY >= placement.top && pointerClientY <= placement.bottom);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function getClosestPlacement(placements, pointerClientY) {
|
|
177
|
+
return placements.reduce((closestPlacement, placement) => {
|
|
178
|
+
if (!closestPlacement) {
|
|
179
|
+
return placement;
|
|
180
|
+
}
|
|
181
|
+
return getDistanceToPlacement(placement, pointerClientY) <
|
|
182
|
+
getDistanceToPlacement(closestPlacement, pointerClientY)
|
|
183
|
+
? placement
|
|
184
|
+
: closestPlacement;
|
|
185
|
+
}, null);
|
|
186
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bayonai/rich-text-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Reusable rich text editing, rendering, and unsaved-change session controls.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"main": "./dist/index.
|
|
7
|
+
"main": "./dist-cjs/index.js",
|
|
8
8
|
"module": "./dist/index.js",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"exports": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"dist",
|
|
24
24
|
"dist-cjs",
|
|
25
25
|
"CHANGELOG.md",
|
|
26
|
+
"BEHAVIOR.md",
|
|
26
27
|
"README.md"
|
|
27
28
|
],
|
|
28
29
|
"scripts": {
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { RefObject } from "react";
|
|
2
|
-
import { type EditorKeyboardShortcutCommand } from "./editorNavigation";
|
|
3
|
-
type RichTextBodyProps = {
|
|
4
|
-
bodyRef: RefObject<HTMLDivElement | null>;
|
|
5
|
-
disabled: boolean;
|
|
6
|
-
label: string;
|
|
7
|
-
onArrowUpFromFirstLine: () => void;
|
|
8
|
-
onBackspace: () => boolean;
|
|
9
|
-
onEnter: (event: {
|
|
10
|
-
shiftKey: boolean;
|
|
11
|
-
}) => boolean;
|
|
12
|
-
onFocus: () => void;
|
|
13
|
-
onInput: () => void;
|
|
14
|
-
onSelectionChange: () => void;
|
|
15
|
-
onShortcutCommand: (command: EditorKeyboardShortcutCommand) => void;
|
|
16
|
-
};
|
|
17
|
-
export declare function RichTextBody({ bodyRef, disabled, label, onArrowUpFromFirstLine, onBackspace, onEnter, onFocus, onInput, onSelectionChange, onShortcutCommand, }: RichTextBodyProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
-
export {};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { getCodeBlockSelectionTarget, getEditorKeyboardShortcut, isCursorOnFirstLine, isSelectAllShortcut, } from "./editorNavigation.js";
|
|
4
|
-
// Renders the editable rich-text body and reports input and selection changes.
|
|
5
|
-
export function RichTextBody({ bodyRef, disabled, label, onArrowUpFromFirstLine, onBackspace, onEnter, onFocus, onInput, onSelectionChange, onShortcutCommand, }) {
|
|
6
|
-
return (_jsx("div", { "aria-label": label, className: "bayon-rte-body", contentEditable: !disabled, "data-placeholder": "Tell your story...", dir: "auto", onBlur: onSelectionChange, onClick: (event) => {
|
|
7
|
-
if (event.target instanceof HTMLInputElement &&
|
|
8
|
-
event.target.type === "checkbox") {
|
|
9
|
-
onInput();
|
|
10
|
-
}
|
|
11
|
-
}, onFocus: onFocus, onInput: onInput, onKeyDown: (event) => {
|
|
12
|
-
if (isSelectAllShortcut(event)) {
|
|
13
|
-
const code = getCodeBlockSelectionTarget(event.currentTarget, window.getSelection());
|
|
14
|
-
if (code) {
|
|
15
|
-
event.preventDefault();
|
|
16
|
-
selectElementContents(code);
|
|
17
|
-
onSelectionChange();
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
if (event.key === "Enter" && onEnter(event)) {
|
|
22
|
-
event.preventDefault();
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (event.key === "Backspace" && onBackspace()) {
|
|
26
|
-
event.preventDefault();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const shortcutCommand = getEditorKeyboardShortcut(event);
|
|
30
|
-
if (shortcutCommand) {
|
|
31
|
-
event.preventDefault();
|
|
32
|
-
onShortcutCommand(shortcutCommand);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if (event.key === "ArrowUp" &&
|
|
36
|
-
isCursorOnFirstLine(event.currentTarget.textContent ?? "", getTextOffsetWithin(event.currentTarget))) {
|
|
37
|
-
event.preventDefault();
|
|
38
|
-
onArrowUpFromFirstLine();
|
|
39
|
-
}
|
|
40
|
-
}, onKeyUp: onSelectionChange, onMouseUp: onSelectionChange, onPaste: (event) => {
|
|
41
|
-
event.preventDefault();
|
|
42
|
-
document.execCommand("insertText", false, event.clipboardData.getData("text/plain"));
|
|
43
|
-
onInput();
|
|
44
|
-
}, ref: bodyRef, role: "textbox", suppressContentEditableWarning: true, tabIndex: disabled ? -1 : 0 }));
|
|
45
|
-
}
|
|
46
|
-
function selectElementContents(element) {
|
|
47
|
-
const range = document.createRange();
|
|
48
|
-
range.selectNodeContents(element);
|
|
49
|
-
const selection = window.getSelection();
|
|
50
|
-
selection?.removeAllRanges();
|
|
51
|
-
selection?.addRange(range);
|
|
52
|
-
}
|
|
53
|
-
function getTextOffsetWithin(root) {
|
|
54
|
-
const selection = window.getSelection();
|
|
55
|
-
if (!selection || selection.rangeCount === 0) {
|
|
56
|
-
return 0;
|
|
57
|
-
}
|
|
58
|
-
const selectionRange = selection.getRangeAt(0);
|
|
59
|
-
if (!root.contains(selectionRange.startContainer)) {
|
|
60
|
-
return 0;
|
|
61
|
-
}
|
|
62
|
-
const textRange = document.createRange();
|
|
63
|
-
textRange.selectNodeContents(root);
|
|
64
|
-
textRange.setEnd(selectionRange.startContainer, selectionRange.startOffset);
|
|
65
|
-
return textRange.toString().length;
|
|
66
|
-
}
|