@malaya_jeeva/rich-text-editor 1.0.6 → 1.0.8
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 +50 -17
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +34 -36
- package/dist/index.mjs +34 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,15 +49,48 @@ export default function Page() {
|
|
|
49
49
|
| ---------- | ------------------------- | -------- | ---------------------------------------- |
|
|
50
50
|
| `value` | `string` | No | Initial HTML content (read on first mount only) |
|
|
51
51
|
| `onChange` | `(value: string) => void` | No | Fires with full `innerHTML` on every change |
|
|
52
|
+
| `toolbar` | `ToolbarItem[]` | No | Controls which toolbar buttons are shown. If omitted, all buttons are shown |
|
|
52
53
|
|
|
53
54
|
### Type export
|
|
54
55
|
|
|
55
56
|
```ts
|
|
56
|
-
import type { RichTextEditorProps } from '@malaya_jeeva/rich-text-editor'
|
|
57
|
+
import type { RichTextEditorProps, ToolbarItem } from '@malaya_jeeva/rich-text-editor'
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
---
|
|
60
61
|
|
|
62
|
+
## 🛠️ Toolbar Customization
|
|
63
|
+
|
|
64
|
+
Pass a `toolbar` array to control exactly which buttons appear. If the prop is omitted, **all buttons are shown** — fully backward compatible.
|
|
65
|
+
|
|
66
|
+
### All available `ToolbarItem` keys
|
|
67
|
+
|
|
68
|
+
| Key | Button | Description |
|
|
69
|
+
| --- | ------ | ----------- |
|
|
70
|
+
| `bold` | **B** | Bold text |
|
|
71
|
+
| `italic` | *I* | Italic text |
|
|
72
|
+
| `underline` | <u>U</u> | Underline text |
|
|
73
|
+
| `color` | A̲ | Text color picker (palette + HSV wheel + remove) |
|
|
74
|
+
| `blockFormat` | Paragraph ▾ | Block format dropdown (Paragraph, H1, H2, H3) |
|
|
75
|
+
| `alignLeft` | ≡ | Align text left |
|
|
76
|
+
| `alignCenter` | ≡ | Align text center |
|
|
77
|
+
| `alignRight` | ≡ | Align text right |
|
|
78
|
+
| `alignJustify` | ≡ | Justify text |
|
|
79
|
+
| `bulletList` | • | Unordered / bullet list |
|
|
80
|
+
| `numberedList` | 1. | Ordered / numbered list |
|
|
81
|
+
| `indent` | → | Increase indent (or go deeper in list) |
|
|
82
|
+
| `outdent` | ← | Decrease indent (or go back up in list) |
|
|
83
|
+
| `link` | 🔗 | Insert / edit hyperlink |
|
|
84
|
+
| `codeBlock` | `</>` | Wrap selection in `<pre><code>` |
|
|
85
|
+
| `image` | 🖼 | Insert image (upload, URL, or drop) |
|
|
86
|
+
| `table` | ⊞ | Insert table (grid picker up to 8×8) |
|
|
87
|
+
| `html` | HTML | Toggle between Visual and HTML source view |
|
|
88
|
+
| `copyHtml` | ⎘ | Copy current HTML to clipboard |
|
|
89
|
+
|
|
90
|
+
> **Note:** Separators between groups are rendered automatically — they appear only when buttons exist on both sides, so you never get a leading, trailing, or double separator regardless of your selection.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
61
94
|
## ✨ Features
|
|
62
95
|
|
|
63
96
|
### Text Formatting
|
|
@@ -112,15 +145,15 @@ import type { RichTextEditorProps } from '@malaya_jeeva/rich-text-editor'
|
|
|
112
145
|
|
|
113
146
|
## 🧠 Keyboard Shortcuts
|
|
114
147
|
|
|
115
|
-
| Shortcut
|
|
116
|
-
|
|
|
117
|
-
| `Ctrl + B`
|
|
118
|
-
| `Ctrl + I`
|
|
119
|
-
| `Ctrl + U`
|
|
120
|
-
| `Tab`
|
|
121
|
-
| `Shift + Tab`
|
|
122
|
-
| `Enter` (link bar) | Apply link
|
|
123
|
-
| `Escape` (link bar) | Dismiss link bar
|
|
148
|
+
| Shortcut | Action |
|
|
149
|
+
| -------- | ------ |
|
|
150
|
+
| `Ctrl + B` | Bold |
|
|
151
|
+
| `Ctrl + I` | Italic |
|
|
152
|
+
| `Ctrl + U` | Underline |
|
|
153
|
+
| `Tab` | Indent list item / move to next table cell |
|
|
154
|
+
| `Shift + Tab` | Outdent list item / move to previous table cell |
|
|
155
|
+
| `Enter` (link bar) | Apply link |
|
|
156
|
+
| `Escape` (link bar) | Dismiss link bar |
|
|
124
157
|
|
|
125
158
|
---
|
|
126
159
|
|
|
@@ -129,13 +162,13 @@ import type { RichTextEditorProps } from '@malaya_jeeva/rich-text-editor'
|
|
|
129
162
|
Click any inserted image to open the image control panel.
|
|
130
163
|
|
|
131
164
|
### Style tab
|
|
132
|
-
| Control
|
|
133
|
-
|
|
|
134
|
-
| Display
|
|
135
|
-
| Alignment
|
|
136
|
-
| Width
|
|
137
|
-
| Alt text
|
|
138
|
-
| Delete
|
|
165
|
+
| Control | Options |
|
|
166
|
+
| ------- | ------- |
|
|
167
|
+
| Display | Block (full row) · Inline (flows with text) |
|
|
168
|
+
| Alignment | Left · Center · Right |
|
|
169
|
+
| Width | 25% · 50% · 75% · 100% · Original size · Custom (e.g. `300px`) |
|
|
170
|
+
| Alt text | Free text input |
|
|
171
|
+
| Delete | Removes the image from the editor |
|
|
139
172
|
|
|
140
173
|
### Caption tab
|
|
141
174
|
Wraps the image in `<figure>` + `<figcaption>`. The caption always renders below the image regardless of float alignment.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
2
|
|
|
3
|
+
type ToolbarItem = "bold" | "italic" | "underline" | "color" | "blockFormat" | "alignLeft" | "alignCenter" | "alignRight" | "alignJustify" | "bulletList" | "numberedList" | "indent" | "outdent" | "link" | "codeBlock" | "image" | "table" | "html" | "copyHtml";
|
|
3
4
|
type RichTextEditorProps = {
|
|
4
5
|
value?: string;
|
|
5
6
|
onChange?: (value: string) => void;
|
|
7
|
+
toolbar?: ToolbarItem[];
|
|
6
8
|
};
|
|
7
9
|
|
|
8
|
-
declare function RichTextEditor({ value, onChange }: RichTextEditorProps): ReactElement;
|
|
10
|
+
declare function RichTextEditor({ value, onChange, toolbar }: RichTextEditorProps): ReactElement;
|
|
9
11
|
|
|
10
12
|
export { type RichTextEditorProps, RichTextEditor as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
2
|
|
|
3
|
+
type ToolbarItem = "bold" | "italic" | "underline" | "color" | "blockFormat" | "alignLeft" | "alignCenter" | "alignRight" | "alignJustify" | "bulletList" | "numberedList" | "indent" | "outdent" | "link" | "codeBlock" | "image" | "table" | "html" | "copyHtml";
|
|
3
4
|
type RichTextEditorProps = {
|
|
4
5
|
value?: string;
|
|
5
6
|
onChange?: (value: string) => void;
|
|
7
|
+
toolbar?: ToolbarItem[];
|
|
6
8
|
};
|
|
7
9
|
|
|
8
|
-
declare function RichTextEditor({ value, onChange }: RichTextEditorProps): ReactElement;
|
|
10
|
+
declare function RichTextEditor({ value, onChange, toolbar }: RichTextEditorProps): ReactElement;
|
|
9
11
|
|
|
10
12
|
export { type RichTextEditorProps, RichTextEditor as default };
|
package/dist/index.js
CHANGED
|
@@ -66,7 +66,7 @@ var PALETTE = [
|
|
|
66
66
|
];
|
|
67
67
|
var CSS = `
|
|
68
68
|
*{box-sizing:border-box;}
|
|
69
|
-
.rte-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:1px;padding:4px 8px;background:#f8f8f8;border-bottom:1px solid #e0e0e0;}
|
|
69
|
+
.rte-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:1px;padding:4px 8px;background:#f8f8f8;border-bottom:1px solid #e0e0e0;position: sticky;top: 0;z-index: 9;}
|
|
70
70
|
@media(prefers-color-scheme:dark){.rte-toolbar{background:#1e1e1e;border-color:#333;}}
|
|
71
71
|
.rte-btn{background:transparent;border:none;border-radius:3px;cursor:pointer;height:30px;min-width:30px;padding:0 6px;color:#444;font-size:13px;font-weight:500;font-family:var(--font-sans);display:inline-flex;align-items:center;justify-content:center;user-select:none;white-space:nowrap;transition:background 0.1s;flex-shrink:0;}
|
|
72
72
|
.rte-btn:hover{background:#e8e8e8;}.rte-btn.active{background:#d0e4ff;color:#1a5fb4;}
|
|
@@ -170,6 +170,12 @@ var CSS = `
|
|
|
170
170
|
.rte-ie-apply{flex:1;height:26px;background:#1a6fc4;color:#fff;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer;}
|
|
171
171
|
.rte-ie-remove{flex:1;height:26px;background:#fdecea;color:#c0392b;border:1px solid #f5c6c2;border-radius:4px;font-size:11px;cursor:pointer;}
|
|
172
172
|
.rte-handle{position:absolute;width:10px;height:10px;background:#fff;border:2px solid #1a5fb4;border-radius:2px;pointer-events:all;z-index:53;}
|
|
173
|
+
.customeditor {
|
|
174
|
+
max-height: 350px;
|
|
175
|
+
overflow-y: auto;
|
|
176
|
+
position: relative;
|
|
177
|
+
}
|
|
178
|
+
|
|
173
179
|
`;
|
|
174
180
|
|
|
175
181
|
// src/rte/utils.ts
|
|
@@ -1109,7 +1115,7 @@ function ImageEditor({ img, containerRef, onClose, onDelete, onChange }) {
|
|
|
1109
1115
|
|
|
1110
1116
|
// src/RichTextEditor.tsx
|
|
1111
1117
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1112
|
-
function RichTextEditor({ value, onChange }) {
|
|
1118
|
+
function RichTextEditor({ value, onChange, toolbar }) {
|
|
1113
1119
|
var _a;
|
|
1114
1120
|
const editorRef = (0, import_react5.useRef)(null);
|
|
1115
1121
|
const editorAreaRef = (0, import_react5.useRef)(null);
|
|
@@ -1461,15 +1467,16 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1461
1467
|
const canMerge = selCells.length >= 2;
|
|
1462
1468
|
const canSplit = fmt.cellMerged;
|
|
1463
1469
|
const backdrop = { position: "fixed", inset: 0, zIndex: 199 };
|
|
1470
|
+
const show = (key) => !toolbar || toolbar.includes(key);
|
|
1464
1471
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { padding: "1rem 0" }, children: [
|
|
1465
1472
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: CSS }),
|
|
1466
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "customeditor", style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
|
|
1467
1474
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rte-toolbar", children: [
|
|
1468
1475
|
!isCode && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1469
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
|
|
1470
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
|
|
1471
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
|
|
1472
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1476
|
+
show("bold") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
|
|
1477
|
+
show("italic") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
|
|
1478
|
+
show("underline") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
|
|
1479
|
+
show("color") && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1473
1480
|
showColor && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: backdrop, onMouseDown: () => setShowColor(false) }),
|
|
1474
1481
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1475
1482
|
Btn,
|
|
@@ -1501,8 +1508,8 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1501
1508
|
}
|
|
1502
1509
|
) })
|
|
1503
1510
|
] }),
|
|
1504
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1505
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1511
|
+
(show("bold") || show("italic") || show("underline") || show("color")) && (show("blockFormat") || show("alignLeft") || show("alignCenter") || show("alignRight") || show("alignJustify")) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1512
|
+
show("blockFormat") && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1506
1513
|
"select",
|
|
1507
1514
|
{
|
|
1508
1515
|
className: "rte-select",
|
|
@@ -1525,20 +1532,20 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1525
1532
|
]
|
|
1526
1533
|
}
|
|
1527
1534
|
),
|
|
1528
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1529
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "left" }) }),
|
|
1530
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "center" }) }),
|
|
1531
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "right" }) }),
|
|
1532
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "justify" }) }),
|
|
1533
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1534
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoUL, {}) }),
|
|
1535
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoOL, {}) }),
|
|
1536
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoIndent, {}) }),
|
|
1537
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoOutdent, {}) }),
|
|
1538
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1539
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoLink, {}) }),
|
|
1540
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoCode, {}) }),
|
|
1541
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1535
|
+
show("blockFormat") && (show("alignLeft") || show("alignCenter") || show("alignRight") || show("alignJustify")) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1536
|
+
show("alignLeft") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "left" }) }),
|
|
1537
|
+
show("alignCenter") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "center" }) }),
|
|
1538
|
+
show("alignRight") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "right" }) }),
|
|
1539
|
+
show("alignJustify") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "justify" }) }),
|
|
1540
|
+
(show("alignLeft") || show("alignCenter") || show("alignRight") || show("alignJustify")) && (show("bulletList") || show("numberedList") || show("indent") || show("outdent")) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1541
|
+
show("bulletList") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoUL, {}) }),
|
|
1542
|
+
show("numberedList") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoOL, {}) }),
|
|
1543
|
+
show("indent") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoIndent, {}) }),
|
|
1544
|
+
show("outdent") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoOutdent, {}) }),
|
|
1545
|
+
(show("bulletList") || show("numberedList") || show("indent") || show("outdent")) && (show("link") || show("codeBlock") || show("image") || show("table")) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
|
|
1546
|
+
show("link") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoLink, {}) }),
|
|
1547
|
+
show("codeBlock") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoCode, {}) }),
|
|
1548
|
+
show("image") && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1542
1549
|
showImagePicker && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: backdrop, onMouseDown: () => setShowImagePicker(false) }),
|
|
1543
1550
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => setShowImagePicker((v) => !v), title: "Insert image", active: showImagePicker, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoImage, {}) }),
|
|
1544
1551
|
showImagePicker && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
@@ -1552,7 +1559,7 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1552
1559
|
}
|
|
1553
1560
|
) })
|
|
1554
1561
|
] }),
|
|
1555
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1562
|
+
show("table") && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1556
1563
|
showTable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: backdrop, onMouseDown: () => setShowTable(false) }),
|
|
1557
1564
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => setShowTable((v) => !v), title: "Insert table", active: showTable || !!fmt.inTable, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoTable, {}) }),
|
|
1558
1565
|
showTable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
@@ -1566,19 +1573,10 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1566
1573
|
}
|
|
1567
1574
|
) })
|
|
1568
1575
|
] }),
|
|
1569
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {})
|
|
1576
|
+
(show("link") || show("codeBlock") || show("image") || show("table")) && (show("html") || show("copyHtml")) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {})
|
|
1570
1577
|
] }),
|
|
1571
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1572
|
-
|
|
1573
|
-
{
|
|
1574
|
-
onClick: isCode ? toVisual : toCode,
|
|
1575
|
-
title: "Toggle HTML",
|
|
1576
|
-
active: isCode,
|
|
1577
|
-
style: { fontSize: 12, fontWeight: 600, letterSpacing: "0.03em", padding: "0 8px" },
|
|
1578
|
-
children: isCode ? "Visual" : "HTML"
|
|
1579
|
-
}
|
|
1580
|
-
),
|
|
1581
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: copyHtml, title: "Copy HTML", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoCopy, {}) }),
|
|
1578
|
+
show("html") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: isCode ? toVisual : toCode, title: "Toggle HTML", active: isCode, style: { fontSize: 12, fontWeight: 600, letterSpacing: "0.03em", padding: "0 8px" }, children: isCode ? "Visual" : "HTML" }),
|
|
1579
|
+
show("copyHtml") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: copyHtml, title: "Copy HTML", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoCopy, {}) }),
|
|
1582
1580
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { flex: 1 } }),
|
|
1583
1581
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: 12, color: "#aaa" }, children: [
|
|
1584
1582
|
words,
|
package/dist/index.mjs
CHANGED
|
@@ -45,7 +45,7 @@ var PALETTE = [
|
|
|
45
45
|
];
|
|
46
46
|
var CSS = `
|
|
47
47
|
*{box-sizing:border-box;}
|
|
48
|
-
.rte-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:1px;padding:4px 8px;background:#f8f8f8;border-bottom:1px solid #e0e0e0;}
|
|
48
|
+
.rte-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:1px;padding:4px 8px;background:#f8f8f8;border-bottom:1px solid #e0e0e0;position: sticky;top: 0;z-index: 9;}
|
|
49
49
|
@media(prefers-color-scheme:dark){.rte-toolbar{background:#1e1e1e;border-color:#333;}}
|
|
50
50
|
.rte-btn{background:transparent;border:none;border-radius:3px;cursor:pointer;height:30px;min-width:30px;padding:0 6px;color:#444;font-size:13px;font-weight:500;font-family:var(--font-sans);display:inline-flex;align-items:center;justify-content:center;user-select:none;white-space:nowrap;transition:background 0.1s;flex-shrink:0;}
|
|
51
51
|
.rte-btn:hover{background:#e8e8e8;}.rte-btn.active{background:#d0e4ff;color:#1a5fb4;}
|
|
@@ -149,6 +149,12 @@ var CSS = `
|
|
|
149
149
|
.rte-ie-apply{flex:1;height:26px;background:#1a6fc4;color:#fff;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer;}
|
|
150
150
|
.rte-ie-remove{flex:1;height:26px;background:#fdecea;color:#c0392b;border:1px solid #f5c6c2;border-radius:4px;font-size:11px;cursor:pointer;}
|
|
151
151
|
.rte-handle{position:absolute;width:10px;height:10px;background:#fff;border:2px solid #1a5fb4;border-radius:2px;pointer-events:all;z-index:53;}
|
|
152
|
+
.customeditor {
|
|
153
|
+
max-height: 350px;
|
|
154
|
+
overflow-y: auto;
|
|
155
|
+
position: relative;
|
|
156
|
+
}
|
|
157
|
+
|
|
152
158
|
`;
|
|
153
159
|
|
|
154
160
|
// src/rte/utils.ts
|
|
@@ -1088,7 +1094,7 @@ function ImageEditor({ img, containerRef, onClose, onDelete, onChange }) {
|
|
|
1088
1094
|
|
|
1089
1095
|
// src/RichTextEditor.tsx
|
|
1090
1096
|
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1091
|
-
function RichTextEditor({ value, onChange }) {
|
|
1097
|
+
function RichTextEditor({ value, onChange, toolbar }) {
|
|
1092
1098
|
var _a;
|
|
1093
1099
|
const editorRef = useRef4(null);
|
|
1094
1100
|
const editorAreaRef = useRef4(null);
|
|
@@ -1440,15 +1446,16 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1440
1446
|
const canMerge = selCells.length >= 2;
|
|
1441
1447
|
const canSplit = fmt.cellMerged;
|
|
1442
1448
|
const backdrop = { position: "fixed", inset: 0, zIndex: 199 };
|
|
1449
|
+
const show = (key) => !toolbar || toolbar.includes(key);
|
|
1443
1450
|
return /* @__PURE__ */ jsxs6("div", { style: { padding: "1rem 0" }, children: [
|
|
1444
1451
|
/* @__PURE__ */ jsx7("style", { children: CSS }),
|
|
1445
|
-
/* @__PURE__ */ jsxs6("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
|
|
1452
|
+
/* @__PURE__ */ jsxs6("div", { className: "customeditor", style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
|
|
1446
1453
|
/* @__PURE__ */ jsxs6("div", { className: "rte-toolbar", children: [
|
|
1447
1454
|
!isCode && /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1448
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
|
|
1449
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
|
|
1450
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
|
|
1451
|
-
/* @__PURE__ */ jsxs6("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1455
|
+
show("bold") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
|
|
1456
|
+
show("italic") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
|
|
1457
|
+
show("underline") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
|
|
1458
|
+
show("color") && /* @__PURE__ */ jsxs6("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1452
1459
|
showColor && /* @__PURE__ */ jsx7("div", { style: backdrop, onMouseDown: () => setShowColor(false) }),
|
|
1453
1460
|
/* @__PURE__ */ jsxs6(
|
|
1454
1461
|
Btn,
|
|
@@ -1480,8 +1487,8 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1480
1487
|
}
|
|
1481
1488
|
) })
|
|
1482
1489
|
] }),
|
|
1483
|
-
/* @__PURE__ */ jsx7(Sep, {}),
|
|
1484
|
-
/* @__PURE__ */ jsxs6(
|
|
1490
|
+
(show("bold") || show("italic") || show("underline") || show("color")) && (show("blockFormat") || show("alignLeft") || show("alignCenter") || show("alignRight") || show("alignJustify")) && /* @__PURE__ */ jsx7(Sep, {}),
|
|
1491
|
+
show("blockFormat") && /* @__PURE__ */ jsxs6(
|
|
1485
1492
|
"select",
|
|
1486
1493
|
{
|
|
1487
1494
|
className: "rte-select",
|
|
@@ -1504,20 +1511,20 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1504
1511
|
]
|
|
1505
1512
|
}
|
|
1506
1513
|
),
|
|
1507
|
-
/* @__PURE__ */ jsx7(Sep, {}),
|
|
1508
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL, children: /* @__PURE__ */ jsx7(AlignIco, { t: "left" }) }),
|
|
1509
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC, children: /* @__PURE__ */ jsx7(AlignIco, { t: "center" }) }),
|
|
1510
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR, children: /* @__PURE__ */ jsx7(AlignIco, { t: "right" }) }),
|
|
1511
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ, children: /* @__PURE__ */ jsx7(AlignIco, { t: "justify" }) }),
|
|
1512
|
-
/* @__PURE__ */ jsx7(Sep, {}),
|
|
1513
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul, children: /* @__PURE__ */ jsx7(IcoUL, {}) }),
|
|
1514
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol, children: /* @__PURE__ */ jsx7(IcoOL, {}) }),
|
|
1515
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)", children: /* @__PURE__ */ jsx7(IcoIndent, {}) }),
|
|
1516
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)", children: /* @__PURE__ */ jsx7(IcoOutdent, {}) }),
|
|
1517
|
-
/* @__PURE__ */ jsx7(Sep, {}),
|
|
1518
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ jsx7(IcoLink, {}) }),
|
|
1519
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ jsx7(IcoCode, {}) }),
|
|
1520
|
-
/* @__PURE__ */ jsxs6("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1514
|
+
show("blockFormat") && (show("alignLeft") || show("alignCenter") || show("alignRight") || show("alignJustify")) && /* @__PURE__ */ jsx7(Sep, {}),
|
|
1515
|
+
show("alignLeft") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL, children: /* @__PURE__ */ jsx7(AlignIco, { t: "left" }) }),
|
|
1516
|
+
show("alignCenter") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC, children: /* @__PURE__ */ jsx7(AlignIco, { t: "center" }) }),
|
|
1517
|
+
show("alignRight") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR, children: /* @__PURE__ */ jsx7(AlignIco, { t: "right" }) }),
|
|
1518
|
+
show("alignJustify") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ, children: /* @__PURE__ */ jsx7(AlignIco, { t: "justify" }) }),
|
|
1519
|
+
(show("alignLeft") || show("alignCenter") || show("alignRight") || show("alignJustify")) && (show("bulletList") || show("numberedList") || show("indent") || show("outdent")) && /* @__PURE__ */ jsx7(Sep, {}),
|
|
1520
|
+
show("bulletList") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul, children: /* @__PURE__ */ jsx7(IcoUL, {}) }),
|
|
1521
|
+
show("numberedList") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol, children: /* @__PURE__ */ jsx7(IcoOL, {}) }),
|
|
1522
|
+
show("indent") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)", children: /* @__PURE__ */ jsx7(IcoIndent, {}) }),
|
|
1523
|
+
show("outdent") && /* @__PURE__ */ jsx7(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)", children: /* @__PURE__ */ jsx7(IcoOutdent, {}) }),
|
|
1524
|
+
(show("bulletList") || show("numberedList") || show("indent") || show("outdent")) && (show("link") || show("codeBlock") || show("image") || show("table")) && /* @__PURE__ */ jsx7(Sep, {}),
|
|
1525
|
+
show("link") && /* @__PURE__ */ jsx7(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ jsx7(IcoLink, {}) }),
|
|
1526
|
+
show("codeBlock") && /* @__PURE__ */ jsx7(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ jsx7(IcoCode, {}) }),
|
|
1527
|
+
show("image") && /* @__PURE__ */ jsxs6("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1521
1528
|
showImagePicker && /* @__PURE__ */ jsx7("div", { style: backdrop, onMouseDown: () => setShowImagePicker(false) }),
|
|
1522
1529
|
/* @__PURE__ */ jsx7(Btn, { onClick: () => setShowImagePicker((v) => !v), title: "Insert image", active: showImagePicker, children: /* @__PURE__ */ jsx7(IcoImage, {}) }),
|
|
1523
1530
|
showImagePicker && /* @__PURE__ */ jsx7("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ jsx7(
|
|
@@ -1531,7 +1538,7 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1531
1538
|
}
|
|
1532
1539
|
) })
|
|
1533
1540
|
] }),
|
|
1534
|
-
/* @__PURE__ */ jsxs6("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1541
|
+
show("table") && /* @__PURE__ */ jsxs6("div", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
1535
1542
|
showTable && /* @__PURE__ */ jsx7("div", { style: backdrop, onMouseDown: () => setShowTable(false) }),
|
|
1536
1543
|
/* @__PURE__ */ jsx7(Btn, { onClick: () => setShowTable((v) => !v), title: "Insert table", active: showTable || !!fmt.inTable, children: /* @__PURE__ */ jsx7(IcoTable, {}) }),
|
|
1537
1544
|
showTable && /* @__PURE__ */ jsx7("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ jsx7(
|
|
@@ -1545,19 +1552,10 @@ function RichTextEditor({ value, onChange }) {
|
|
|
1545
1552
|
}
|
|
1546
1553
|
) })
|
|
1547
1554
|
] }),
|
|
1548
|
-
/* @__PURE__ */ jsx7(Sep, {})
|
|
1555
|
+
(show("link") || show("codeBlock") || show("image") || show("table")) && (show("html") || show("copyHtml")) && /* @__PURE__ */ jsx7(Sep, {})
|
|
1549
1556
|
] }),
|
|
1550
|
-
/* @__PURE__ */ jsx7(
|
|
1551
|
-
|
|
1552
|
-
{
|
|
1553
|
-
onClick: isCode ? toVisual : toCode,
|
|
1554
|
-
title: "Toggle HTML",
|
|
1555
|
-
active: isCode,
|
|
1556
|
-
style: { fontSize: 12, fontWeight: 600, letterSpacing: "0.03em", padding: "0 8px" },
|
|
1557
|
-
children: isCode ? "Visual" : "HTML"
|
|
1558
|
-
}
|
|
1559
|
-
),
|
|
1560
|
-
/* @__PURE__ */ jsx7(Btn, { onClick: copyHtml, title: "Copy HTML", children: /* @__PURE__ */ jsx7(IcoCopy, {}) }),
|
|
1557
|
+
show("html") && /* @__PURE__ */ jsx7(Btn, { onClick: isCode ? toVisual : toCode, title: "Toggle HTML", active: isCode, style: { fontSize: 12, fontWeight: 600, letterSpacing: "0.03em", padding: "0 8px" }, children: isCode ? "Visual" : "HTML" }),
|
|
1558
|
+
show("copyHtml") && /* @__PURE__ */ jsx7(Btn, { onClick: copyHtml, title: "Copy HTML", children: /* @__PURE__ */ jsx7(IcoCopy, {}) }),
|
|
1561
1559
|
/* @__PURE__ */ jsx7("div", { style: { flex: 1 } }),
|
|
1562
1560
|
/* @__PURE__ */ jsxs6("span", { style: { fontSize: 12, color: "#aaa" }, children: [
|
|
1563
1561
|
words,
|