@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 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&#818; | 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 | Action |
116
- | --------------- | ---------------------------------- |
117
- | `Ctrl + B` | Bold |
118
- | `Ctrl + I` | Italic |
119
- | `Ctrl + U` | Underline |
120
- | `Tab` | Indent list item / move to next table cell |
121
- | `Shift + Tab` | Outdent list item / move to previous table cell |
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 | Options |
133
- | ----------- | ------- |
134
- | Display | Block (full row) · Inline (flows with text) |
135
- | Alignment | Left · Center · Right |
136
- | Width | 25% · 50% · 75% · 100% · Original size · Custom (e.g. `300px`) |
137
- | Alt text | Free text input |
138
- | Delete | Removes the image from the editor |
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
- Btn,
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
- Btn,
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malaya_jeeva/rich-text-editor",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Custom React Rich Text Editor",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",