@malaya_jeeva/rich-text-editor 1.0.12 → 1.0.14

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
@@ -26,7 +26,13 @@ export default function Page() {
26
26
  const [value, setValue] = useState('')
27
27
 
28
28
  return (
29
- <RichTextEditor value={value} onChange={setValue} />
29
+ <RichTextEditor
30
+ value={value}
31
+ onChange={setValue}
32
+ placeholder="Write something..."
33
+ height={400}
34
+ theme="light"
35
+ />
30
36
  )
31
37
  }
32
38
  ```
@@ -45,11 +51,14 @@ export default function Page() {
45
51
 
46
52
  ## ⚙️ Props
47
53
 
48
- | Prop | Type | Required | Description |
49
- | ---------- | ------------------------- | -------- | ---------------------------------------- |
50
- | `value` | `string` | No | Initial HTML content (read on first mount only) |
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 |
54
+ | Prop | Type | Required | Default | Description |
55
+ | ------------- | ----------------------------- | -------- | -------------------- | --------------------------------------------------------------------------- |
56
+ | `value` | `string` | No | — | Initial HTML content (read on first mount only) |
57
+ | `onChange` | `(value: string) => void` | No | — | Fires with full `innerHTML` on every change |
58
+ | `toolbar` | `ToolbarItem[]` | No | all items | Controls which toolbar buttons are shown. If omitted, all buttons are shown |
59
+ | `placeholder` | `string` | No | `"Start typing..."` | Placeholder text shown when the editor is empty |
60
+ | `height` | `number \| string` | No | auto (max `350px`) | Editor content area height — `500`, `"400px"`, `"60vh"` etc. |
61
+ | `theme` | `"light" \| "dark" \| "auto"` | No | `"auto"` | Color theme. `"auto"` follows the OS `prefers-color-scheme` setting |
53
62
 
54
63
  ### Type export
55
64
 
@@ -59,6 +68,44 @@ import type { RichTextEditorProps, ToolbarItem } from '@malaya_jeeva/rich-text-e
59
68
 
60
69
  ---
61
70
 
71
+ ## 📐 Height
72
+
73
+ Control the height of the editor content area via the `height` prop. Accepts a number (pixels) or any valid CSS string.
74
+
75
+ ```tsx
76
+ <RichTextEditor height={500} /> // 500px fixed
77
+ <RichTextEditor height="60vh" /> // viewport-relative
78
+ <RichTextEditor height="400px" /> // explicit px string
79
+ <RichTextEditor /> // default — grows with content, caps at 350px
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 💬 Placeholder
85
+
86
+ Set the placeholder text shown when the editor is empty.
87
+
88
+ ```tsx
89
+ <RichTextEditor placeholder="Write something..." />
90
+ // omit it — defaults to "Start typing..."
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 🌗 Theme
96
+
97
+ Three options: `"light"`, `"dark"`, or `"auto"` (default).
98
+
99
+ ```tsx
100
+ <RichTextEditor theme="light" /> // always light
101
+ <RichTextEditor theme="dark" /> // always dark
102
+ <RichTextEditor /> // auto — follows OS prefers-color-scheme
103
+ ```
104
+
105
+ > When `theme="auto"` (or the prop is omitted) the editor automatically switches between light and dark based on the user's OS setting — no extra code needed.
106
+
107
+ ---
108
+
62
109
  ## 🛠️ Toolbar Customization
63
110
 
64
111
  Pass a `toolbar` array to control exactly which buttons appear. If the prop is omitted, **all buttons are shown** — fully backward compatible.
package/dist/index.d.mts CHANGED
@@ -6,8 +6,10 @@ type RichTextEditorProps = {
6
6
  onChange?: (value: string) => void;
7
7
  toolbar?: ToolbarItem[];
8
8
  theme?: "light" | "dark";
9
+ height?: number | string;
10
+ placeholder?: string;
9
11
  };
10
12
 
11
- declare function RichTextEditor({ value, onChange, toolbar, theme }: RichTextEditorProps): ReactElement;
13
+ declare function RichTextEditor({ value, onChange, toolbar, theme, height, placeholder }: RichTextEditorProps): ReactElement;
12
14
 
13
15
  export { type RichTextEditorProps, RichTextEditor as default };
package/dist/index.d.ts CHANGED
@@ -6,8 +6,10 @@ type RichTextEditorProps = {
6
6
  onChange?: (value: string) => void;
7
7
  toolbar?: ToolbarItem[];
8
8
  theme?: "light" | "dark";
9
+ height?: number | string;
10
+ placeholder?: string;
9
11
  };
10
12
 
11
- declare function RichTextEditor({ value, onChange, toolbar, theme }: RichTextEditorProps): ReactElement;
13
+ declare function RichTextEditor({ value, onChange, toolbar, theme, height, placeholder }: RichTextEditorProps): ReactElement;
12
14
 
13
15
  export { type RichTextEditorProps, RichTextEditor as default };
package/dist/index.js CHANGED
@@ -2,7 +2,21 @@
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
6
20
  var __export = (target, all) => {
7
21
  for (var name in all)
8
22
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -92,7 +106,7 @@ var CSS = `
92
106
 
93
107
  /* \u2500\u2500\u2500 Theme tokens \u2500\u2500\u2500 */
94
108
  .customeditor{
95
- max-height:350px;overflow-y:auto;position:relative;
109
+ position:relative;
96
110
  /* light (default) */
97
111
  --rte-toolbar-bg:#f8f8f8;--rte-toolbar-border:#e0e0e0;
98
112
  --rte-btn:#444;--rte-btn-hover-bg:#e8e8e8;--rte-btn-active-bg:#d0e4ff;--rte-btn-active:#1a5fb4;
@@ -164,7 +178,7 @@ var CSS = `
164
178
  .rte-linkbar{min-width:340px;}
165
179
  .rte-linkbar input{flex:1;height:26px;border:1px solid var(--rte-linkinput-border);border-radius:3px;padding:0 8px;font-size:13px;outline:none;font-family:var(--font-sans);background:var(--rte-linkinput-bg);color:var(--rte-linkinput);min-width:0;}
166
180
  .rte-area{position:relative;background:var(--rte-area-bg);}
167
- .rte-body{min-height:280px;outline:none;font-size:15px;line-height:1.75;color:var(--rte-body);caret-color:var(--rte-body);padding:18px 20px;}
181
+ .rte-body{height:200px;overflow-y: auto;outline:none;font-size:15px;line-height:1.75;color:var(--rte-body);caret-color:var(--rte-body);padding:18px 20px;}
168
182
  .rte-body h1{font-size:26px;font-weight:600;margin:0.6em 0 0.2em;}
169
183
  .rte-body h2{font-size:20px;font-weight:600;margin:0.6em 0 0.2em;}
170
184
  .rte-body h3{font-size:16px;font-weight:600;margin:0.6em 0 0.2em;}
@@ -208,12 +222,16 @@ var CSS = `
208
222
  .rte-ie-remove{flex:1;height:26px;background:#fdecea;color:#c0392b;border:1px solid #f5c6c2;border-radius:4px;font-size:11px;cursor:pointer;}
209
223
  .rte-handle{position:absolute;width:10px;height:10px;background:#fff;border:2px solid #1a5fb4;border-radius:2px;pointer-events:all;z-index:53;}
210
224
  .rte-body blockquote{background-color: rgb(245, 243, 255);
211
- border-left: 4px solid rgb(100, 80, 220);
225
+ border-left: 4px solid rgb(100, 80, 220);
212
226
  margin: 8px 0;
213
227
  padding: 10px 16px;
214
228
  font-style: italic;
215
229
  color: rgb(75, 85, 99);
216
230
  border-radius: 0 4px 4px 0;}
231
+ .resizable {
232
+ overflow: auto;
233
+ resize: vertical;
234
+ }
217
235
  `;
218
236
 
219
237
  // src/rte/utils.ts
@@ -1188,13 +1206,14 @@ function ImageEditor({ img, containerRef, onClose, onDelete, onChange }) {
1188
1206
 
1189
1207
  // src/RichTextEditor.tsx
1190
1208
  var import_jsx_runtime7 = require("react/jsx-runtime");
1191
- function RichTextEditor({ value, onChange, toolbar, theme }) {
1209
+ function RichTextEditor({ value, onChange, toolbar, theme, height, placeholder = "Start typing..." }) {
1192
1210
  var _a;
1193
1211
  const editorRef = (0, import_react5.useRef)(null);
1194
1212
  const editorAreaRef = (0, import_react5.useRef)(null);
1195
1213
  const savedRangeRef = (0, import_react5.useRef)(null);
1196
1214
  const dragStartCell = (0, import_react5.useRef)(null);
1197
1215
  const isDragging = (0, import_react5.useRef)(false);
1216
+ const isMounting = (0, import_react5.useRef)(true);
1198
1217
  const [isCode, setIsCode] = (0, import_react5.useState)(false);
1199
1218
  const [codeVal, setCodeVal] = (0, import_react5.useState)("");
1200
1219
  const [fmt, setFmt] = (0, import_react5.useState)({ block: "p" });
@@ -1281,7 +1300,9 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1281
1300
  else setLinkInfoFP(null);
1282
1301
  const txt = (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerText) != null ? _b : "";
1283
1302
  setWords(txt.trim() ? txt.trim().split(/\s+/).length : 0);
1284
- onChange == null ? void 0 : onChange(sanitizeScriptTags((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : ""));
1303
+ if (!isMounting.current) {
1304
+ onChange == null ? void 0 : onChange(sanitizeScriptTags((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : ""));
1305
+ }
1285
1306
  }, [onChange, calcFloat, linkBar, sanitizeScriptTags]);
1286
1307
  const fixListInParagraph = (0, import_react5.useCallback)(() => {
1287
1308
  var _a2;
@@ -1719,8 +1740,9 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1719
1740
  (0, import_react5.useEffect)(() => {
1720
1741
  if (editorRef.current) {
1721
1742
  document.execCommand("defaultParagraphSeparator", false, "p");
1722
- editorRef.current.innerHTML = sanitizeScriptTags(value != null ? value : "<p>Start writing here...</p>");
1743
+ editorRef.current.innerHTML = sanitizeScriptTags(value != null ? value : "");
1723
1744
  refresh();
1745
+ isMounting.current = false;
1724
1746
  }
1725
1747
  }, []);
1726
1748
  const canMerge = selCells.length >= 2;
@@ -1851,17 +1873,17 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1851
1873
  value: codeVal,
1852
1874
  onChange: (e) => setCodeVal(e.target.value),
1853
1875
  spellCheck: false,
1854
- style: { display: isCode ? "block" : "none" }
1876
+ style: __spreadValues({ display: isCode ? "block" : "none" }, height != null && { height, minHeight: 0 })
1855
1877
  }
1856
1878
  ),
1857
1879
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1858
1880
  "div",
1859
1881
  {
1860
1882
  ref: editorRef,
1861
- className: "rte-body",
1883
+ className: "rte-body resizable",
1862
1884
  contentEditable: true,
1863
1885
  suppressContentEditableWarning: true,
1864
- "data-ph": "Start typing...",
1886
+ "data-ph": placeholder,
1865
1887
  onMouseDown: handleEditorMouseDown,
1866
1888
  onMouseMove: handleEditorMouseMove,
1867
1889
  onMouseUp: () => {
@@ -1882,7 +1904,7 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1882
1904
  onDragOver: (e) => {
1883
1905
  if (e.dataTransfer.types.includes("Files")) e.preventDefault();
1884
1906
  },
1885
- style: { display: isCode ? "none" : "block" }
1907
+ style: __spreadValues({ display: isCode ? "none" : "block" }, height != null && { height, minHeight: 0 })
1886
1908
  }
1887
1909
  ),
1888
1910
  !isCode && selectedImg && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
package/dist/index.mjs CHANGED
@@ -1,3 +1,20 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+
1
18
  // src/RichTextEditor.tsx
2
19
  import {
3
20
  useState as useState5,
@@ -71,7 +88,7 @@ var CSS = `
71
88
 
72
89
  /* \u2500\u2500\u2500 Theme tokens \u2500\u2500\u2500 */
73
90
  .customeditor{
74
- max-height:350px;overflow-y:auto;position:relative;
91
+ position:relative;
75
92
  /* light (default) */
76
93
  --rte-toolbar-bg:#f8f8f8;--rte-toolbar-border:#e0e0e0;
77
94
  --rte-btn:#444;--rte-btn-hover-bg:#e8e8e8;--rte-btn-active-bg:#d0e4ff;--rte-btn-active:#1a5fb4;
@@ -143,7 +160,7 @@ var CSS = `
143
160
  .rte-linkbar{min-width:340px;}
144
161
  .rte-linkbar input{flex:1;height:26px;border:1px solid var(--rte-linkinput-border);border-radius:3px;padding:0 8px;font-size:13px;outline:none;font-family:var(--font-sans);background:var(--rte-linkinput-bg);color:var(--rte-linkinput);min-width:0;}
145
162
  .rte-area{position:relative;background:var(--rte-area-bg);}
146
- .rte-body{min-height:280px;outline:none;font-size:15px;line-height:1.75;color:var(--rte-body);caret-color:var(--rte-body);padding:18px 20px;}
163
+ .rte-body{height:200px;overflow-y: auto;outline:none;font-size:15px;line-height:1.75;color:var(--rte-body);caret-color:var(--rte-body);padding:18px 20px;}
147
164
  .rte-body h1{font-size:26px;font-weight:600;margin:0.6em 0 0.2em;}
148
165
  .rte-body h2{font-size:20px;font-weight:600;margin:0.6em 0 0.2em;}
149
166
  .rte-body h3{font-size:16px;font-weight:600;margin:0.6em 0 0.2em;}
@@ -187,12 +204,16 @@ var CSS = `
187
204
  .rte-ie-remove{flex:1;height:26px;background:#fdecea;color:#c0392b;border:1px solid #f5c6c2;border-radius:4px;font-size:11px;cursor:pointer;}
188
205
  .rte-handle{position:absolute;width:10px;height:10px;background:#fff;border:2px solid #1a5fb4;border-radius:2px;pointer-events:all;z-index:53;}
189
206
  .rte-body blockquote{background-color: rgb(245, 243, 255);
190
- border-left: 4px solid rgb(100, 80, 220);
207
+ border-left: 4px solid rgb(100, 80, 220);
191
208
  margin: 8px 0;
192
209
  padding: 10px 16px;
193
210
  font-style: italic;
194
211
  color: rgb(75, 85, 99);
195
212
  border-radius: 0 4px 4px 0;}
213
+ .resizable {
214
+ overflow: auto;
215
+ resize: vertical;
216
+ }
196
217
  `;
197
218
 
198
219
  // src/rte/utils.ts
@@ -1167,13 +1188,14 @@ function ImageEditor({ img, containerRef, onClose, onDelete, onChange }) {
1167
1188
 
1168
1189
  // src/RichTextEditor.tsx
1169
1190
  import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1170
- function RichTextEditor({ value, onChange, toolbar, theme }) {
1191
+ function RichTextEditor({ value, onChange, toolbar, theme, height, placeholder = "Start typing..." }) {
1171
1192
  var _a;
1172
1193
  const editorRef = useRef4(null);
1173
1194
  const editorAreaRef = useRef4(null);
1174
1195
  const savedRangeRef = useRef4(null);
1175
1196
  const dragStartCell = useRef4(null);
1176
1197
  const isDragging = useRef4(false);
1198
+ const isMounting = useRef4(true);
1177
1199
  const [isCode, setIsCode] = useState5(false);
1178
1200
  const [codeVal, setCodeVal] = useState5("");
1179
1201
  const [fmt, setFmt] = useState5({ block: "p" });
@@ -1260,7 +1282,9 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1260
1282
  else setLinkInfoFP(null);
1261
1283
  const txt = (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerText) != null ? _b : "";
1262
1284
  setWords(txt.trim() ? txt.trim().split(/\s+/).length : 0);
1263
- onChange == null ? void 0 : onChange(sanitizeScriptTags((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : ""));
1285
+ if (!isMounting.current) {
1286
+ onChange == null ? void 0 : onChange(sanitizeScriptTags((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : ""));
1287
+ }
1264
1288
  }, [onChange, calcFloat, linkBar, sanitizeScriptTags]);
1265
1289
  const fixListInParagraph = useCallback3(() => {
1266
1290
  var _a2;
@@ -1698,8 +1722,9 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1698
1722
  useEffect3(() => {
1699
1723
  if (editorRef.current) {
1700
1724
  document.execCommand("defaultParagraphSeparator", false, "p");
1701
- editorRef.current.innerHTML = sanitizeScriptTags(value != null ? value : "<p>Start writing here...</p>");
1725
+ editorRef.current.innerHTML = sanitizeScriptTags(value != null ? value : "");
1702
1726
  refresh();
1727
+ isMounting.current = false;
1703
1728
  }
1704
1729
  }, []);
1705
1730
  const canMerge = selCells.length >= 2;
@@ -1830,17 +1855,17 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1830
1855
  value: codeVal,
1831
1856
  onChange: (e) => setCodeVal(e.target.value),
1832
1857
  spellCheck: false,
1833
- style: { display: isCode ? "block" : "none" }
1858
+ style: __spreadValues({ display: isCode ? "block" : "none" }, height != null && { height, minHeight: 0 })
1834
1859
  }
1835
1860
  ),
1836
1861
  /* @__PURE__ */ jsx7(
1837
1862
  "div",
1838
1863
  {
1839
1864
  ref: editorRef,
1840
- className: "rte-body",
1865
+ className: "rte-body resizable",
1841
1866
  contentEditable: true,
1842
1867
  suppressContentEditableWarning: true,
1843
- "data-ph": "Start typing...",
1868
+ "data-ph": placeholder,
1844
1869
  onMouseDown: handleEditorMouseDown,
1845
1870
  onMouseMove: handleEditorMouseMove,
1846
1871
  onMouseUp: () => {
@@ -1861,7 +1886,7 @@ function RichTextEditor({ value, onChange, toolbar, theme }) {
1861
1886
  onDragOver: (e) => {
1862
1887
  if (e.dataTransfer.types.includes("Files")) e.preventDefault();
1863
1888
  },
1864
- style: { display: isCode ? "none" : "block" }
1889
+ style: __spreadValues({ display: isCode ? "none" : "block" }, height != null && { height, minHeight: 0 })
1865
1890
  }
1866
1891
  ),
1867
1892
  !isCode && selectedImg && /* @__PURE__ */ jsx7(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malaya_jeeva/rich-text-editor",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Custom React Rich Text Editor",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",