smartrte-react 0.1.10 → 0.1.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
@@ -330,15 +330,13 @@ The editor comes with built-in styles. You can customize the appearance by wrapp
330
330
 
331
331
  - Node.js 18+
332
332
  - pnpm 9.10.0+
333
- - Rust (for WASM compilation)
334
- - wasm-pack
335
333
 
336
334
  ### Setting Up Development Environment
337
335
 
338
336
  1. **Clone the repository**
339
337
 
340
338
  ```bash
341
- git clone https://github.com/yourusername/smart-rte.git
339
+ git clone https://github.com/ayush1852017/smart-rte.git
342
340
  cd smart-rte
343
341
  ```
344
342
 
@@ -351,13 +349,7 @@ pnpm install
351
349
  3. **Build the project**
352
350
 
353
351
  ```bash
354
- # Build WASM core
355
- pnpm build:wasm
356
-
357
352
  # Build TypeScript packages
358
- pnpm build:ts
359
-
360
- # Or build everything
361
353
  pnpm build
362
354
  ```
363
355
 
@@ -376,19 +368,17 @@ The playground will be available at `http://localhost:5173`
376
368
  ```
377
369
  smart-rte/
378
370
  ├── packages/
379
- ├── react/ # Main React package (smartrte-react)
380
- ├── src/
381
- ├── components/
382
- │ │ ├── ClassicEditor.tsx # Main editor component
383
- │ │ └── MediaManager.tsx # Media management component
384
- └── index.ts
385
- ├── playground/ # Development playground
386
- └── package.json
387
- ├── core-wasm/ # WASM bindings
388
- └── classic-embed/ # Standalone bundle
389
- ├── rust/ # Rust core
390
- │ └── smart_rte_core/
391
- ├── apps/ # Example applications
371
+ └── react/ # Main React package (smartrte-react)
372
+ ├── src/
373
+ │ ├── components/
374
+ │ │ ├── ClassicEditor.tsx # Main editor component
375
+ │ │ └── MediaManager.tsx # Media management component
376
+ │ └── index.ts
377
+ ├── playground/ # Development playground
378
+ └── package.json
379
+ ├── dart/ # Flutter/Dart packages
380
+ ├── smartrte_flutter/ # Flutter WebView integration
381
+ │ └── example_app/ # Flutter example
392
382
  └── package.json
393
383
  ```
394
384
 
@@ -455,7 +445,7 @@ We welcome contributions! Here's how you can help:
455
445
 
456
446
  ### Reporting Bugs
457
447
 
458
- 1. Check if the bug has already been reported in [Issues](https://github.com/yourusername/smart-rte/issues)
448
+ 1. Check if the bug has already been reported in [Issues](https://github.com/ayush1852017/smart-rte/issues)
459
449
  2. If not, create a new issue with:
460
450
  - Clear title and description
461
451
  - Steps to reproduce
@@ -465,7 +455,7 @@ We welcome contributions! Here's how you can help:
465
455
 
466
456
  ### Suggesting Features
467
457
 
468
- 1. Check [existing feature requests](https://github.com/yourusername/smart-rte/issues?q=is%3Aissue+label%3Aenhancement)
458
+ 1. Check [existing feature requests](https://github.com/ayush1852017/smart-rte/issues?q=is%3Aissue+label%3Aenhancement)
469
459
  2. Create a new issue with:
470
460
  - Clear description of the feature
471
461
  - Use cases
@@ -611,20 +601,20 @@ SOFTWARE.
611
601
 
612
602
  - **Smart RTE Team** - Initial work and maintenance
613
603
 
614
- See the list of [contributors](https://github.com/yourusername/smart-rte/contributors) who participated in this project.
604
+ See the list of [contributors](https://github.com/ayush1852017/smart-rte/contributors) who participated in this project.
615
605
 
616
606
  ## 🙏 Acknowledgments
617
607
 
618
608
  - [KaTeX](https://katex.org/) - For mathematical formula rendering
619
609
  - [React](https://reactjs.org/) - The UI library
620
610
  - [Vite](https://vitejs.dev/) - Build tool
621
- - All our amazing [contributors](https://github.com/yourusername/smart-rte/contributors)
611
+ - All our amazing [contributors](https://github.com/ayush1852017/smart-rte/contributors)
622
612
 
623
613
  ## 📞 Support
624
614
 
625
615
  - **Documentation:** You're reading it! 📖
626
- - **Issues:** [GitHub Issues](https://github.com/yourusername/smart-rte/issues)
627
- - **Discussions:** [GitHub Discussions](https://github.com/yourusername/smart-rte/discussions)
616
+ - **Issues:** [GitHub Issues](https://github.com/ayush1852017/smart-rte/issues)
617
+ - **Discussions:** [GitHub Discussions](https://github.com/ayush1852017/smart-rte/discussions)
628
618
  - **Twitter:** [@smartrte](https://twitter.com/smartrte) (if applicable)
629
619
 
630
620
  ## 🗺️ Roadmap
@@ -658,9 +648,7 @@ See the list of [contributors](https://github.com/yourusername/smart-rte/contrib
658
648
 
659
649
  ## 🔗 Related Packages
660
650
 
661
- - **@smartrte/classic-embed** - Standalone script-tag bundle
662
- - **smartrte-flutter** - Flutter/Dart implementation
663
- - **smart-rte-core** - Rust core library
651
+ - **smartrte-flutter** - Flutter/Dart WebView implementation
664
652
 
665
653
  ## 💡 Tips & Best Practices
666
654
 
@@ -696,4 +684,4 @@ See the list of [contributors](https://github.com/yourusername/smart-rte/contrib
696
684
 
697
685
  **Happy Editing! 🎉**
698
686
 
699
- If you find this package useful, please consider giving it a ⭐ on [GitHub](https://github.com/yourusername/smart-rte)!
687
+ If you find this package useful, please consider giving it a ⭐ on [GitHub](https://github.com/ayush1852017/smart-rte)!
@@ -10,6 +10,21 @@ type ClassicEditorProps = {
10
10
  media?: boolean;
11
11
  formula?: boolean;
12
12
  mediaManager?: MediaManagerAdapter;
13
+ /**
14
+ * Optional custom list of fonts to display in the toolbar.
15
+ * If not provided, a default set of web-safe fonts will be used.
16
+ * Example: [{ name: 'Robto', value: 'Roboto, sans-serif' }]
17
+ */
18
+ fonts?: {
19
+ name: string;
20
+ value: string;
21
+ }[];
22
+ /**
23
+ * The default font family to apply to the editor content.
24
+ * This sets the font-family style of the editable area.
25
+ * Example: "Arial, sans-serif"
26
+ */
27
+ defaultFont?: string;
13
28
  };
14
- export declare function ClassicEditor({ value, onChange, placeholder, minHeight, maxHeight, readOnly, table, media, formula, mediaManager, }: ClassicEditorProps): import("react/jsx-runtime").JSX.Element;
29
+ export declare function ClassicEditor({ value, onChange, placeholder, minHeight, maxHeight, readOnly, table, media, formula, mediaManager, fonts, defaultFont, }: ClassicEditorProps): import("react/jsx-runtime").JSX.Element;
15
30
  export {};
@@ -1,7 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { MediaManager } from "./MediaManager";
4
- export function ClassicEditor({ value, onChange, placeholder = "Type here…", minHeight = 200, maxHeight = 500, readOnly = false, table = true, media = true, formula = true, mediaManager, }) {
4
+ export function ClassicEditor({ value, onChange, placeholder = "Type here…", minHeight = 200, maxHeight = 500, readOnly = false, table = true, media = true, formula = true, mediaManager, fonts = [
5
+ { name: "Arial", value: "Arial, Helvetica, sans-serif" },
6
+ { name: "Georgia", value: "Georgia, serif" },
7
+ { name: "Impact", value: "Impact, Charcoal, sans-serif" },
8
+ { name: "Tahoma", value: "Tahoma, Geneva, sans-serif" },
9
+ { name: "Times New Roman", value: "'Times New Roman', Times, serif" },
10
+ { name: "Verdana", value: "Verdana, Geneva, sans-serif" },
11
+ { name: "Courier New", value: "'Courier New', Courier, monospace" },
12
+ ], defaultFont, }) {
5
13
  const editableRef = useRef(null);
6
14
  const lastEmittedRef = useRef("");
7
15
  const isComposingRef = useRef(false);
@@ -11,6 +19,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
11
19
  const [imageOverlay, setImageOverlay] = useState(null);
12
20
  const resizingRef = useRef(null);
13
21
  const draggedImageRef = useRef(null);
22
+ const tableResizeRef = useRef(null);
14
23
  const [showTableDialog, setShowTableDialog] = useState(false);
15
24
  const [tableRows, setTableRows] = useState(3);
16
25
  const [tableCols, setTableCols] = useState(3);
@@ -24,7 +33,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
24
33
  const [showColorPicker, setShowColorPicker] = useState(false);
25
34
  const [colorPickerType, setColorPickerType] = useState('text');
26
35
  const savedRangeRef = useRef(null);
27
- const [currentFontSize, setCurrentFontSize] = useState("11");
36
+ const [currentFontSize, setCurrentFontSize] = useState("");
37
+ const [currentFont, setCurrentFont] = useState("");
28
38
  useEffect(() => {
29
39
  const el = editableRef.current;
30
40
  if (!el)
@@ -32,6 +42,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
32
42
  // Initialize with provided HTML only when externally controlled value changes
33
43
  if (typeof value === "string" && value !== el.innerHTML) {
34
44
  el.innerHTML = value || "";
45
+ fixNegativeMargins(el);
46
+ ensureTableWrappers(el);
35
47
  }
36
48
  // Suppress native context menu inside table cells at capture phase
37
49
  const onCtx = (evt) => {
@@ -151,6 +163,57 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
151
163
  console.error('Error applying font size:', error);
152
164
  }
153
165
  };
166
+ const applyFontFamily = (font) => {
167
+ try {
168
+ setCurrentFont(font);
169
+ const editor = editableRef.current;
170
+ if (!editor)
171
+ return;
172
+ editor.focus();
173
+ let range = null;
174
+ const sel = window.getSelection();
175
+ if (sel && sel.rangeCount > 0) {
176
+ const currentRange = sel.getRangeAt(0);
177
+ if (editor.contains(currentRange.commonAncestorContainer)) {
178
+ range = currentRange;
179
+ }
180
+ }
181
+ if (!range && savedRangeRef.current) {
182
+ range = savedRangeRef.current.cloneRange();
183
+ }
184
+ if (!range)
185
+ return;
186
+ if (range.collapsed) {
187
+ const span = document.createElement('span');
188
+ span.style.fontFamily = font;
189
+ span.textContent = '\u200B';
190
+ range.insertNode(span);
191
+ const newRange = document.createRange();
192
+ newRange.setStart(span.firstChild, 1);
193
+ newRange.collapse(true);
194
+ if (sel) {
195
+ sel.removeAllRanges();
196
+ sel.addRange(newRange);
197
+ }
198
+ handleInput();
199
+ return;
200
+ }
201
+ const span = document.createElement('span');
202
+ span.style.fontFamily = font;
203
+ const fragment = range.extractContents();
204
+ span.appendChild(fragment);
205
+ range.insertNode(span);
206
+ if (sel) {
207
+ range.selectNodeContents(span);
208
+ sel.removeAllRanges();
209
+ sel.addRange(range);
210
+ }
211
+ handleInput();
212
+ }
213
+ catch (error) {
214
+ console.error('Error applying font family:', error);
215
+ }
216
+ };
154
217
  const applyTextColor = (color) => {
155
218
  exec("foreColor", color);
156
219
  };
@@ -194,6 +257,124 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
194
257
  window.removeEventListener("resize", onResize);
195
258
  };
196
259
  }, []);
260
+ // Table resize event listeners
261
+ useEffect(() => {
262
+ const el = editableRef.current;
263
+ if (!el)
264
+ return;
265
+ addTableResizeHandles();
266
+ const onMouseDown = (e) => {
267
+ if (!table)
268
+ return;
269
+ const target = e.target;
270
+ if (target.tagName === 'TD' || target.tagName === 'TH') {
271
+ const rect = target.getBoundingClientRect();
272
+ const rightEdge = rect.right;
273
+ const clickX = e.clientX;
274
+ if (Math.abs(clickX - rightEdge) < 5) {
275
+ e.preventDefault();
276
+ const tableElem = target.closest('table');
277
+ const colIndex = parseInt(target.getAttribute('data-col-index') || '0', 10);
278
+ if (tableElem) {
279
+ startColumnResize(tableElem, colIndex, e.clientX);
280
+ }
281
+ return;
282
+ }
283
+ const bottomEdge = rect.bottom;
284
+ const clickY = e.clientY;
285
+ if (Math.abs(clickY - bottomEdge) < 5) {
286
+ e.preventDefault();
287
+ const tableElem = target.closest('table');
288
+ const row = target.closest('tr');
289
+ if (tableElem && row) {
290
+ const rowIndex = parseInt(row.getAttribute('data-row-index') || '0', 10);
291
+ startRowResize(tableElem, rowIndex, e.clientY);
292
+ }
293
+ return;
294
+ }
295
+ }
296
+ };
297
+ const onMouseMove = (e) => {
298
+ if (tableResizeRef.current) {
299
+ handleTableResizeMove(e);
300
+ return;
301
+ }
302
+ if (!table)
303
+ return;
304
+ const target = e.target;
305
+ if (target.tagName === 'TD' || target.tagName === 'TH') {
306
+ const rect = target.getBoundingClientRect();
307
+ const clickX = e.clientX;
308
+ const clickY = e.clientY;
309
+ if (Math.abs(clickX - rect.right) < 5) {
310
+ el.style.cursor = 'col-resize';
311
+ return;
312
+ }
313
+ if (Math.abs(clickY - rect.bottom) < 5) {
314
+ el.style.cursor = 'row-resize';
315
+ return;
316
+ }
317
+ if (el.style.cursor === 'col-resize' || el.style.cursor === 'row-resize') {
318
+ el.style.cursor = '';
319
+ }
320
+ }
321
+ };
322
+ const onMouseUp = () => {
323
+ handleTableResizeEnd();
324
+ };
325
+ const onTouchStart = (e) => {
326
+ if (!table)
327
+ return;
328
+ const target = e.target;
329
+ if (target.tagName === 'TD' || target.tagName === 'TH') {
330
+ const rect = target.getBoundingClientRect();
331
+ const touch = e.touches[0];
332
+ const clickX = touch.clientX;
333
+ const clickY = touch.clientY;
334
+ if (Math.abs(clickX - rect.right) < 15) {
335
+ e.preventDefault();
336
+ const tableElem = target.closest('table');
337
+ const colIndex = parseInt(target.getAttribute('data-col-index') || '0', 10);
338
+ if (tableElem) {
339
+ startColumnResize(tableElem, colIndex, clickX);
340
+ }
341
+ return;
342
+ }
343
+ if (Math.abs(clickY - rect.bottom) < 15) {
344
+ e.preventDefault();
345
+ const tableElem = target.closest('table');
346
+ const row = target.closest('tr');
347
+ if (tableElem && row) {
348
+ const rowIndex = parseInt(row.getAttribute('data-row-index') || '0', 10);
349
+ startRowResize(tableElem, rowIndex, clickY);
350
+ }
351
+ return;
352
+ }
353
+ }
354
+ };
355
+ const onTouchMove = (e) => {
356
+ if (tableResizeRef.current) {
357
+ handleTableResizeMove(e);
358
+ }
359
+ };
360
+ const onTouchEnd = () => {
361
+ handleTableResizeEnd();
362
+ };
363
+ el.addEventListener('mousedown', onMouseDown);
364
+ window.addEventListener('mousemove', onMouseMove);
365
+ window.addEventListener('mouseup', onMouseUp);
366
+ el.addEventListener('touchstart', onTouchStart, { passive: false });
367
+ window.addEventListener('touchmove', onTouchMove, { passive: false });
368
+ window.addEventListener('touchend', onTouchEnd);
369
+ return () => {
370
+ el.removeEventListener('mousedown', onMouseDown);
371
+ window.removeEventListener('mousemove', onMouseMove);
372
+ window.removeEventListener('mouseup', onMouseUp);
373
+ el.removeEventListener('touchstart', onTouchStart);
374
+ window.removeEventListener('touchmove', onTouchMove);
375
+ window.removeEventListener('touchend', onTouchEnd);
376
+ };
377
+ }, [table]);
197
378
  const insertImageAtSelection = (src) => {
198
379
  try {
199
380
  const host = editableRef.current;
@@ -347,11 +528,61 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
347
528
  });
348
529
  }
349
530
  };
531
+ const fixNegativeMargins = (root) => {
532
+ try {
533
+ const nodes = root.querySelectorAll('*');
534
+ for (let i = 0; i < nodes.length; i++) {
535
+ const node = nodes[i];
536
+ if (node.style && node.style.marginLeft && node.style.marginLeft.trim().startsWith('-')) {
537
+ node.style.marginLeft = '0px';
538
+ }
539
+ }
540
+ }
541
+ catch { }
542
+ };
543
+ const ensureTableWrappers = (root) => {
544
+ try {
545
+ const tables = root.querySelectorAll('table');
546
+ tables.forEach((table) => {
547
+ const parent = table.parentElement;
548
+ if (parent && parent.getAttribute('data-table-wrapper') !== 'true') {
549
+ const wrapper = document.createElement('div');
550
+ wrapper.setAttribute('data-table-wrapper', 'true');
551
+ wrapper.style.overflowX = 'auto';
552
+ wrapper.style.webkitOverflowScrolling = 'touch';
553
+ wrapper.style.width = '100%';
554
+ wrapper.style.maxWidth = '100%';
555
+ wrapper.style.display = 'block';
556
+ // Use insertBefore + appendChild to move element without losing too much state
557
+ // simpler than replaceChild for wrapping
558
+ parent.insertBefore(wrapper, table);
559
+ wrapper.appendChild(table);
560
+ }
561
+ // Always ensure table takes full width
562
+ if (table.style.width !== '100%') {
563
+ table.style.width = '100%';
564
+ }
565
+ // Ensure min-width is set
566
+ if (!table.style.minWidth || table.style.minWidth === '0px') {
567
+ table.style.minWidth = '100%';
568
+ }
569
+ });
570
+ }
571
+ catch (e) {
572
+ console.error("Error wrapping tables", e);
573
+ }
574
+ };
350
575
  const handleInput = () => {
351
576
  if (isComposingRef.current)
352
577
  return;
353
578
  const el = editableRef.current;
354
- if (!el || !onChange)
579
+ if (!el)
580
+ return;
581
+ // Auto-fix negative margins that might cause visibility issues
582
+ fixNegativeMargins(el);
583
+ // Ensure tables are wrapped for horizontal scrolling
584
+ ensureTableWrappers(el);
585
+ if (!onChange)
355
586
  return;
356
587
  const html = el.innerHTML;
357
588
  if (html !== lastEmittedRef.current) {
@@ -362,7 +593,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
362
593
  const buildTableHTML = (rows, cols) => {
363
594
  const safeRows = Math.max(1, Math.min(50, Math.floor(rows) || 1));
364
595
  const safeCols = Math.max(1, Math.min(20, Math.floor(cols) || 1));
365
- let html = '<table style="border-collapse:collapse;width:100%;"><tbody>';
596
+ let html = '<div data-table-wrapper="true" style="overflow-x:auto;-webkit-overflow-scrolling:touch;width:100%;max-width:100%;display:block;"><table style="border-collapse:collapse;min-width:100%;"><tbody>';
366
597
  for (let r = 0; r < safeRows; r++) {
367
598
  html += "<tr>";
368
599
  for (let c = 0; c < safeCols; c++) {
@@ -371,7 +602,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
371
602
  }
372
603
  html += "</tr>";
373
604
  }
374
- html += "</tbody></table>";
605
+ html += "</tbody></table></div>";
375
606
  return html;
376
607
  };
377
608
  const insertTable = () => {
@@ -400,6 +631,20 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
400
631
  if (!node || !range)
401
632
  return;
402
633
  range.insertNode(node);
634
+ // Add resize handles to the new table
635
+ if (node instanceof HTMLTableElement) {
636
+ const tbody = node.querySelector('tbody');
637
+ if (tbody) {
638
+ const rows = Array.from(tbody.querySelectorAll('tr'));
639
+ rows.forEach((row, index) => {
640
+ row.setAttribute('data-row-index', String(index));
641
+ const cells = cellsOfRow(row);
642
+ cells.forEach((cell, cellIndex) => {
643
+ cell.setAttribute('data-col-index', String(cellIndex));
644
+ });
645
+ });
646
+ }
647
+ }
403
648
  // Move caret into first cell
404
649
  const firstCell = node.querySelector("td,th");
405
650
  if (firstCell)
@@ -741,14 +986,143 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
741
986
  applyToggle(fallbackCell);
742
987
  }
743
988
  };
744
- return (_jsxs("div", { style: { border: "1px solid #ddd", borderRadius: 6 }, children: [_jsxs("div", { style: {
989
+ // Table column and row resizing functions
990
+ const getColumnCells = (table, colIndex) => {
991
+ const tbody = table.querySelector('tbody');
992
+ if (!tbody)
993
+ return [];
994
+ const cells = [];
995
+ const rows = Array.from(tbody.querySelectorAll('tr'));
996
+ rows.forEach(row => {
997
+ const rowCells = cellsOfRow(row);
998
+ if (rowCells[colIndex]) {
999
+ cells.push(rowCells[colIndex]);
1000
+ }
1001
+ });
1002
+ return cells;
1003
+ };
1004
+ const startColumnResize = (table, colIndex, clientX) => {
1005
+ const cells = getColumnCells(table, colIndex);
1006
+ if (cells.length === 0)
1007
+ return;
1008
+ const firstCell = cells[0];
1009
+ const currentWidth = firstCell.offsetWidth;
1010
+ // Unlock table width so it can grow
1011
+ table.style.width = "max-content";
1012
+ table.style.minWidth = "100%";
1013
+ tableResizeRef.current = {
1014
+ type: 'column',
1015
+ table,
1016
+ index: colIndex,
1017
+ startPos: clientX,
1018
+ startSize: currentWidth,
1019
+ cells,
1020
+ };
1021
+ document.body.style.cursor = 'col-resize';
1022
+ document.body.style.userSelect = 'none';
1023
+ };
1024
+ const startRowResize = (table, rowIndex, clientY) => {
1025
+ const tbody = table.querySelector('tbody');
1026
+ if (!tbody)
1027
+ return;
1028
+ const rows = Array.from(tbody.querySelectorAll('tr'));
1029
+ const row = rows[rowIndex];
1030
+ if (!row)
1031
+ return;
1032
+ const cells = cellsOfRow(row);
1033
+ const currentHeight = row.offsetHeight;
1034
+ tableResizeRef.current = {
1035
+ type: 'row',
1036
+ table,
1037
+ index: rowIndex,
1038
+ startPos: clientY,
1039
+ startSize: currentHeight,
1040
+ cells,
1041
+ };
1042
+ document.body.style.cursor = 'row-resize';
1043
+ document.body.style.userSelect = 'none';
1044
+ };
1045
+ const handleTableResizeMove = (e) => {
1046
+ const resize = tableResizeRef.current;
1047
+ if (!resize)
1048
+ return;
1049
+ e.preventDefault();
1050
+ const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
1051
+ const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
1052
+ if (resize.type === 'column') {
1053
+ const delta = clientX - resize.startPos;
1054
+ const newWidth = Math.max(60, resize.startSize + delta);
1055
+ resize.cells.forEach(cell => {
1056
+ cell.style.width = `${newWidth}px`;
1057
+ cell.style.minWidth = `${newWidth}px`;
1058
+ cell.style.maxWidth = `${newWidth}px`;
1059
+ });
1060
+ }
1061
+ else if (resize.type === 'row') {
1062
+ const delta = clientY - resize.startPos;
1063
+ const newHeight = Math.max(30, resize.startSize + delta);
1064
+ const tbody = resize.table.querySelector('tbody');
1065
+ if (tbody) {
1066
+ const rows = Array.from(tbody.querySelectorAll('tr'));
1067
+ const row = rows[resize.index];
1068
+ if (row) {
1069
+ row.style.height = `${newHeight}px`;
1070
+ resize.cells.forEach(cell => {
1071
+ cell.style.height = `${newHeight}px`;
1072
+ });
1073
+ }
1074
+ }
1075
+ }
1076
+ };
1077
+ const handleTableResizeEnd = () => {
1078
+ if (tableResizeRef.current) {
1079
+ tableResizeRef.current = null;
1080
+ document.body.style.cursor = '';
1081
+ document.body.style.userSelect = '';
1082
+ handleInput();
1083
+ }
1084
+ };
1085
+ const addTableResizeHandles = () => {
1086
+ if (!table)
1087
+ return;
1088
+ const el = editableRef.current;
1089
+ if (!el)
1090
+ return;
1091
+ const tables = el.querySelectorAll('table');
1092
+ tables.forEach(tableElem => {
1093
+ const tbody = tableElem.querySelector('tbody');
1094
+ if (!tbody)
1095
+ return;
1096
+ const firstRow = tbody.querySelector('tr');
1097
+ if (firstRow) {
1098
+ const cells = cellsOfRow(firstRow);
1099
+ cells.forEach((cell, index) => {
1100
+ cell.setAttribute('data-col-index', String(index));
1101
+ });
1102
+ }
1103
+ const rows = Array.from(tbody.querySelectorAll('tr'));
1104
+ rows.forEach((row, index) => {
1105
+ row.setAttribute('data-row-index', String(index));
1106
+ });
1107
+ });
1108
+ };
1109
+ return (_jsxs("div", { style: {
1110
+ border: "1px solid #ddd",
1111
+ borderRadius: 6,
1112
+ width: "100%",
1113
+ maxWidth: "100vw",
1114
+ overflow: "hidden",
1115
+ display: "flex",
1116
+ flexDirection: "column",
1117
+ background: "#fff",
1118
+ boxSizing: "border-box"
1119
+ }, children: [_jsxs("div", { style: {
745
1120
  display: "flex",
746
1121
  flexWrap: "wrap",
1122
+ maxWidth: "100%",
747
1123
  gap: 8,
748
1124
  padding: 8,
749
1125
  borderBottom: "1px solid #eee",
750
- background: "#fff",
751
- color: "#111",
752
1126
  position: "sticky",
753
1127
  top: 0,
754
1128
  zIndex: 1,
@@ -847,7 +1221,24 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
847
1221
  borderRadius: 6,
848
1222
  background: "#fff",
849
1223
  color: "#111",
850
- }, children: [_jsx("option", { value: "8", children: "8" }), _jsx("option", { value: "9", children: "9" }), _jsx("option", { value: "10", children: "10" }), _jsx("option", { value: "11", children: "11" }), _jsx("option", { value: "12", children: "12" }), _jsx("option", { value: "14", children: "14" }), _jsx("option", { value: "18", children: "18" }), _jsx("option", { value: "24", children: "24" }), _jsx("option", { value: "30", children: "30" }), _jsx("option", { value: "36", children: "36" }), _jsx("option", { value: "48", children: "48" }), _jsx("option", { value: "60", children: "60" }), _jsx("option", { value: "72", children: "72" }), _jsx("option", { value: "96", children: "96" })] }), _jsxs("button", { title: "Text Color", onClick: () => {
1224
+ }, children: [_jsx("option", { value: "", disabled: true, children: "Size" }), _jsx("option", { value: "8", children: "8" }), _jsx("option", { value: "9", children: "9" }), _jsx("option", { value: "10", children: "10" }), _jsx("option", { value: "11", children: "11" }), _jsx("option", { value: "12", children: "12" }), _jsx("option", { value: "14", children: "14" }), _jsx("option", { value: "18", children: "18" }), _jsx("option", { value: "24", children: "24" }), _jsx("option", { value: "30", children: "30" }), _jsx("option", { value: "36", children: "36" }), _jsx("option", { value: "48", children: "48" }), _jsx("option", { value: "60", children: "60" }), _jsx("option", { value: "72", children: "72" }), _jsx("option", { value: "96", children: "96" })] }), _jsxs("select", { value: currentFont, onMouseDown: () => {
1225
+ const sel = window.getSelection();
1226
+ if (sel && sel.rangeCount > 0) {
1227
+ const range = sel.getRangeAt(0);
1228
+ const editor = editableRef.current;
1229
+ if (editor && editor.contains(range.commonAncestorContainer) && !range.collapsed) {
1230
+ savedRangeRef.current = range.cloneRange();
1231
+ }
1232
+ }
1233
+ }, onChange: (e) => applyFontFamily(e.target.value), title: "Font Family", style: {
1234
+ height: 32,
1235
+ padding: "0 8px",
1236
+ border: "1px solid #e5e7eb",
1237
+ borderRadius: 6,
1238
+ background: "#fff",
1239
+ color: "#111",
1240
+ maxWidth: 100,
1241
+ }, children: [_jsx("option", { value: "", disabled: true, children: "Font" }), fonts.map((f) => (_jsx("option", { value: f.value, children: f.name }, f.value)))] }), _jsx("button", { title: "Text Color", onClick: () => {
851
1242
  setColorPickerType('text');
852
1243
  setShowColorPicker(true);
853
1244
  }, style: {
@@ -859,16 +1250,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
859
1250
  background: "#fff",
860
1251
  color: "#111",
861
1252
  position: "relative",
862
- }, children: [_jsx("span", { style: { fontWeight: 700 }, children: "A" }), _jsx("div", { style: {
863
- position: "absolute",
864
- bottom: 4,
865
- left: "50%",
866
- transform: "translateX(-50%)",
867
- width: 16,
868
- height: 3,
869
- background: "#000",
870
- borderRadius: 1,
871
- } })] }), _jsx("button", { title: "Background Color", onClick: () => {
1253
+ }, children: _jsx("span", { style: { fontWeight: 700 }, children: "A" }) }), _jsx("button", { title: "Background Color", onClick: () => {
872
1254
  setColorPickerType('background');
873
1255
  setShowColorPicker(true);
874
1256
  }, style: {
@@ -879,7 +1261,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
879
1261
  borderRadius: 6,
880
1262
  background: "#fff",
881
1263
  color: "#111",
882
- }, children: _jsx("span", { style: { fontWeight: 700, background: "#ffeb3b", padding: "2px 4px" }, children: "A" }) }), _jsx("button", { title: "Bulleted list", onClick: () => exec("insertUnorderedList"), style: {
1264
+ }, children: _jsx("span", { style: { fontWeight: 700, padding: "2px 4px" }, children: "A" }) }), _jsx("button", { title: "Bulleted list", onClick: () => exec("insertUnorderedList"), style: {
883
1265
  height: 32,
884
1266
  padding: "0 10px",
885
1267
  border: "1px solid #e5e7eb",
@@ -1082,6 +1464,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1082
1464
  padding: 16,
1083
1465
  borderRadius: 8,
1084
1466
  minWidth: 320,
1467
+ maxWidth: "90vw",
1085
1468
  color: "#000",
1086
1469
  }, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 12 }, children: colorPickerType === 'text' ? 'Select Text Color' : 'Select Background Color' }), _jsx("div", { style: {
1087
1470
  display: "grid",
@@ -1235,273 +1618,285 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1235
1618
  borderRadius: 4,
1236
1619
  background: "#fff",
1237
1620
  color: "#000",
1238
- }, title: sym, children: sym }, i)))] })] }) })), _jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
1239
- isComposingRef.current = false;
1240
- handleInput();
1241
- }, onPaste: (e) => {
1242
- const items = e.clipboardData?.files;
1243
- if (media && items && items.length) {
1244
- const hasImage = Array.from(items).some((f) => f.type.startsWith("image/"));
1245
- if (hasImage) {
1621
+ }, title: sym, children: sym }, i)))] })] }) })), _jsx("div", { style: {
1622
+ width: "100%",
1623
+ maxWidth: "100%",
1624
+ flex: "1 1 auto",
1625
+ minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
1626
+ maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight,
1627
+ overflowY: "auto",
1628
+ overflowX: "hidden",
1629
+ boxSizing: "border-box",
1630
+ position: "relative",
1631
+ }, children: _jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
1632
+ isComposingRef.current = false;
1633
+ handleInput();
1634
+ }, onPaste: (e) => {
1635
+ const items = e.clipboardData?.files;
1636
+ if (media && items && items.length) {
1637
+ const hasImage = Array.from(items).some((f) => f.type.startsWith("image/"));
1638
+ if (hasImage) {
1639
+ e.preventDefault();
1640
+ handleLocalImageFiles(items);
1641
+ }
1642
+ }
1643
+ }, onDragOver: (e) => {
1644
+ // Allow dragging images within editor and file drops
1645
+ if (draggedImageRef.current ||
1646
+ e.dataTransfer?.types?.includes("Files")) {
1246
1647
  e.preventDefault();
1247
- handleLocalImageFiles(items);
1248
1648
  }
1249
- }
1250
- }, onDragOver: (e) => {
1251
- // Allow dragging images within editor and file drops
1252
- if (draggedImageRef.current ||
1253
- e.dataTransfer?.types?.includes("Files")) {
1254
- e.preventDefault();
1255
- }
1256
- }, onDrop: (e) => {
1257
- // Move existing dragged image inside editor
1258
- if (draggedImageRef.current) {
1259
- e.preventDefault();
1260
- const x = e.clientX;
1261
- const y = e.clientY;
1262
- let range = null;
1263
- // @ts-ignore
1264
- if (document.caretRangeFromPoint) {
1649
+ }, onDrop: (e) => {
1650
+ // Move existing dragged image inside editor
1651
+ if (draggedImageRef.current) {
1652
+ e.preventDefault();
1653
+ const x = e.clientX;
1654
+ const y = e.clientY;
1655
+ let range = null;
1265
1656
  // @ts-ignore
1266
- range = document.caretRangeFromPoint(x, y);
1267
- }
1268
- else if (document.caretPositionFromPoint) {
1269
- const pos = document.caretPositionFromPoint(x, y);
1270
- if (pos) {
1271
- range = document.createRange();
1272
- range.setStart(pos.offsetNode, pos.offset);
1657
+ if (document.caretRangeFromPoint) {
1658
+ // @ts-ignore
1659
+ range = document.caretRangeFromPoint(x, y);
1273
1660
  }
1274
- }
1275
- const img = draggedImageRef.current;
1276
- draggedImageRef.current = null;
1277
- if (range &&
1278
- img &&
1279
- editableRef.current?.contains(range.commonAncestorContainer)) {
1280
- // Avoid inserting inside the image itself
1281
- if (range.startContainer === img || range.endContainer === img)
1282
- return;
1283
- // If dropping inside a link, insert right after the link element
1284
- let container = range.commonAncestorContainer;
1285
- let linkAncestor = null;
1286
- let el = container;
1287
- while (el && el !== editableRef.current) {
1288
- if (el.tagName === "A") {
1289
- linkAncestor = el;
1290
- break;
1661
+ else if (document.caretPositionFromPoint) {
1662
+ const pos = document.caretPositionFromPoint(x, y);
1663
+ if (pos) {
1664
+ range = document.createRange();
1665
+ range.setStart(pos.offsetNode, pos.offset);
1291
1666
  }
1292
- el = el.parentElement;
1293
- }
1294
- if (linkAncestor) {
1295
- linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
1296
1667
  }
1297
- else {
1298
- range.insertNode(img);
1668
+ const img = draggedImageRef.current;
1669
+ draggedImageRef.current = null;
1670
+ if (range &&
1671
+ img &&
1672
+ editableRef.current?.contains(range.commonAncestorContainer)) {
1673
+ // Avoid inserting inside the image itself
1674
+ if (range.startContainer === img || range.endContainer === img)
1675
+ return;
1676
+ // If dropping inside a link, insert right after the link element
1677
+ let container = range.commonAncestorContainer;
1678
+ let linkAncestor = null;
1679
+ let el = container;
1680
+ while (el && el !== editableRef.current) {
1681
+ if (el.tagName === "A") {
1682
+ linkAncestor = el;
1683
+ break;
1684
+ }
1685
+ el = el.parentElement;
1686
+ }
1687
+ if (linkAncestor) {
1688
+ linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
1689
+ }
1690
+ else {
1691
+ range.insertNode(img);
1692
+ }
1693
+ const r = document.createRange();
1694
+ r.setStartAfter(img);
1695
+ r.collapse(true);
1696
+ safeSelectRange(r);
1697
+ setSelectedImage(img);
1698
+ scheduleImageOverlay();
1699
+ handleInput();
1299
1700
  }
1300
- const r = document.createRange();
1301
- r.setStartAfter(img);
1302
- r.collapse(true);
1303
- safeSelectRange(r);
1304
- setSelectedImage(img);
1305
- scheduleImageOverlay();
1306
- handleInput();
1701
+ return;
1307
1702
  }
1308
- return;
1309
- }
1310
- if (media && e.dataTransfer?.files?.length) {
1311
- e.preventDefault();
1312
- // Try to move caret to drop point
1313
- const x = e.clientX;
1314
- const y = e.clientY;
1315
- let range = null;
1316
- // @ts-ignore
1317
- if (document.caretRangeFromPoint) {
1703
+ if (media && e.dataTransfer?.files?.length) {
1704
+ e.preventDefault();
1705
+ // Try to move caret to drop point
1706
+ const x = e.clientX;
1707
+ const y = e.clientY;
1708
+ let range = null;
1318
1709
  // @ts-ignore
1319
- range = document.caretRangeFromPoint(x, y);
1320
- }
1321
- else if (document.caretPositionFromPoint) {
1322
- const pos = document.caretPositionFromPoint(x, y);
1323
- if (pos) {
1324
- range = document.createRange();
1325
- range.setStart(pos.offsetNode, pos.offset);
1710
+ if (document.caretRangeFromPoint) {
1711
+ // @ts-ignore
1712
+ range = document.caretRangeFromPoint(x, y);
1326
1713
  }
1714
+ else if (document.caretPositionFromPoint) {
1715
+ const pos = document.caretPositionFromPoint(x, y);
1716
+ if (pos) {
1717
+ range = document.createRange();
1718
+ range.setStart(pos.offsetNode, pos.offset);
1719
+ }
1720
+ }
1721
+ if (range) {
1722
+ const sel = window.getSelection();
1723
+ sel?.removeAllRanges();
1724
+ sel?.addRange(range);
1725
+ }
1726
+ handleLocalImageFiles(e.dataTransfer.files);
1327
1727
  }
1328
- if (range) {
1329
- const sel = window.getSelection();
1330
- sel?.removeAllRanges();
1331
- sel?.addRange(range);
1728
+ }, onClick: (e) => {
1729
+ const t = e.target;
1730
+ if (t && t.tagName === "IMG") {
1731
+ setSelectedImage(t);
1732
+ scheduleImageOverlay();
1332
1733
  }
1333
- handleLocalImageFiles(e.dataTransfer.files);
1334
- }
1335
- }, onClick: (e) => {
1336
- const t = e.target;
1337
- if (t && t.tagName === "IMG") {
1338
- setSelectedImage(t);
1339
- scheduleImageOverlay();
1340
- }
1341
- else {
1342
- setSelectedImage(null);
1343
- setImageOverlay(null);
1344
- }
1345
- }, onDragStart: (e) => {
1346
- const t = e.target;
1347
- if (t && t.tagName === "IMG") {
1348
- draggedImageRef.current = t;
1349
- try {
1350
- e.dataTransfer?.setData("text/plain", "moving-image");
1351
- e.dataTransfer.effectAllowed = "move";
1352
- // Provide a subtle drag image
1353
- const dt = e.dataTransfer;
1354
- if (dt && typeof dt.setDragImage === "function") {
1355
- const ghost = new Image();
1356
- ghost.src = t.src;
1357
- ghost.width = Math.min(120, t.width);
1358
- ghost.height = Math.min(120, t.height);
1359
- dt.setDragImage(ghost, 10, 10);
1734
+ else {
1735
+ setSelectedImage(null);
1736
+ setImageOverlay(null);
1737
+ }
1738
+ }, onDragStart: (e) => {
1739
+ const t = e.target;
1740
+ if (t && t.tagName === "IMG") {
1741
+ draggedImageRef.current = t;
1742
+ try {
1743
+ e.dataTransfer?.setData("text/plain", "moving-image");
1744
+ e.dataTransfer.effectAllowed = "move";
1745
+ // Provide a subtle drag image
1746
+ const dt = e.dataTransfer;
1747
+ if (dt && typeof dt.setDragImage === "function") {
1748
+ const ghost = new Image();
1749
+ ghost.src = t.src;
1750
+ ghost.width = Math.min(120, t.width);
1751
+ ghost.height = Math.min(120, t.height);
1752
+ dt.setDragImage(ghost, 10, 10);
1753
+ }
1360
1754
  }
1755
+ catch { }
1361
1756
  }
1362
- catch { }
1363
- }
1364
- else {
1757
+ else {
1758
+ draggedImageRef.current = null;
1759
+ }
1760
+ }, onDragEnd: () => {
1365
1761
  draggedImageRef.current = null;
1366
- }
1367
- }, onDragEnd: () => {
1368
- draggedImageRef.current = null;
1369
- }, style: {
1370
- padding: 12,
1371
- minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
1372
- maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight,
1373
- overflowY: "auto",
1374
- outline: "none",
1375
- lineHeight: 1.6,
1376
- }, "data-placeholder": placeholder, onFocus: (e) => {
1377
- // Ensure the editor has at least one paragraph to type into
1378
- const el = e.currentTarget;
1379
- if (!el.innerHTML || el.innerHTML === "<br>") {
1380
- el.innerHTML = "<p><br></p>";
1381
- }
1382
- }, onKeyDown: (e) => {
1383
- if (formula &&
1384
- (e.metaKey || e.ctrlKey) &&
1385
- String(e.key).toLowerCase() === "m") {
1386
- e.preventDefault();
1387
- setShowFormulaDialog(true);
1388
- return;
1389
- }
1390
- // Keep Tab for indentation in lists; otherwise insert 2 spaces
1391
- if (e.key === "Tab") {
1392
- e.preventDefault();
1393
- if (document.queryCommandState("insertUnorderedList") ||
1394
- document.queryCommandState("insertOrderedList")) {
1395
- exec(e.shiftKey ? "outdent" : "indent");
1762
+ }, style: {
1763
+ minHeight: "100%",
1764
+ maxWidth: "100%",
1765
+ overflowX: "hidden",
1766
+ padding: "16px",
1767
+ outline: "none",
1768
+ lineHeight: 1.6,
1769
+ boxSizing: "border-box",
1770
+ fontFamily: defaultFont || "inherit",
1771
+ }, "data-placeholder": placeholder, onFocus: (e) => {
1772
+ // Ensure the editor has at least one paragraph to type into
1773
+ const el = e.currentTarget;
1774
+ if (!el.innerHTML || el.innerHTML === "<br>") {
1775
+ el.innerHTML = "<p><br></p>";
1396
1776
  }
1397
- else {
1398
- document.execCommand("insertText", false, " ");
1777
+ }, onKeyDown: (e) => {
1778
+ if (formula &&
1779
+ (e.metaKey || e.ctrlKey) &&
1780
+ String(e.key).toLowerCase() === "m") {
1781
+ e.preventDefault();
1782
+ setShowFormulaDialog(true);
1783
+ return;
1399
1784
  }
1400
- }
1401
- // Table navigation with arrows inside cells
1402
- if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
1403
- const sel = window.getSelection();
1404
- const cell = getClosestCell(sel?.anchorNode || null);
1405
- if (table &&
1406
- cell &&
1407
- cell.parentElement &&
1408
- cell.parentElement.parentElement) {
1409
- const row = cell.parentElement;
1410
- const tbody = row.parentElement;
1411
- const cells = Array.from(row.children).filter((c) => c.tagName === "TD" ||
1412
- c.tagName === "TH");
1413
- const rows = Array.from(tbody.children);
1414
- const rIdx = rows.indexOf(row);
1415
- const cIdx = cells.indexOf(cell);
1416
- const atStart = (sel?.anchorOffset || 0) === 0;
1417
- const cellTextLen = (cell.textContent || "").length;
1418
- const atEnd = (sel?.anchorOffset || 0) >= cellTextLen;
1419
- let target = null;
1420
- if (e.key === "ArrowLeft" && atStart && cIdx > 0) {
1421
- target = row.children[cIdx - 1];
1422
- }
1423
- else if (e.key === "ArrowRight" &&
1424
- atEnd &&
1425
- cIdx < row.children.length - 1) {
1426
- target = row.children[cIdx + 1];
1427
- }
1428
- else if (e.key === "ArrowUp" && rIdx > 0 && atStart) {
1429
- target = rows[rIdx - 1].children[cIdx];
1785
+ // Keep Tab for indentation in lists; otherwise insert 2 spaces
1786
+ if (e.key === "Tab") {
1787
+ e.preventDefault();
1788
+ if (document.queryCommandState("insertUnorderedList") ||
1789
+ document.queryCommandState("insertOrderedList")) {
1790
+ exec(e.shiftKey ? "outdent" : "indent");
1430
1791
  }
1431
- else if (e.key === "ArrowDown" &&
1432
- rIdx < rows.length - 1 &&
1433
- atEnd) {
1434
- target = rows[rIdx + 1].children[cIdx];
1792
+ else {
1793
+ document.execCommand("insertText", false, " ");
1435
1794
  }
1436
- if (target) {
1437
- e.preventDefault();
1438
- moveCaretToCell(target, e.key === "ArrowRight" || e.key === "ArrowDown");
1795
+ }
1796
+ // Table navigation with arrows inside cells
1797
+ if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
1798
+ const sel = window.getSelection();
1799
+ const cell = getClosestCell(sel?.anchorNode || null);
1800
+ if (table &&
1801
+ cell &&
1802
+ cell.parentElement &&
1803
+ cell.parentElement.parentElement) {
1804
+ const row = cell.parentElement;
1805
+ const tbody = row.parentElement;
1806
+ const cells = Array.from(row.children).filter((c) => c.tagName === "TD" ||
1807
+ c.tagName === "TH");
1808
+ const rows = Array.from(tbody.children);
1809
+ const rIdx = rows.indexOf(row);
1810
+ const cIdx = cells.indexOf(cell);
1811
+ const atStart = (sel?.anchorOffset || 0) === 0;
1812
+ const cellTextLen = (cell.textContent || "").length;
1813
+ const atEnd = (sel?.anchorOffset || 0) >= cellTextLen;
1814
+ let target = null;
1815
+ if (e.key === "ArrowLeft" && atStart && cIdx > 0) {
1816
+ target = row.children[cIdx - 1];
1817
+ }
1818
+ else if (e.key === "ArrowRight" &&
1819
+ atEnd &&
1820
+ cIdx < row.children.length - 1) {
1821
+ target = row.children[cIdx + 1];
1822
+ }
1823
+ else if (e.key === "ArrowUp" && rIdx > 0 && atStart) {
1824
+ target = rows[rIdx - 1].children[cIdx];
1825
+ }
1826
+ else if (e.key === "ArrowDown" &&
1827
+ rIdx < rows.length - 1 &&
1828
+ atEnd) {
1829
+ target = rows[rIdx + 1].children[cIdx];
1830
+ }
1831
+ if (target) {
1832
+ e.preventDefault();
1833
+ moveCaretToCell(target, e.key === "ArrowRight" || e.key === "ArrowDown");
1834
+ }
1439
1835
  }
1440
1836
  }
1441
- }
1442
- }, onMouseDown: (e) => {
1443
- const cell = getClosestCell(e.target);
1444
- if (!cell) {
1445
- clearSelectionDecor();
1446
- return;
1447
- }
1448
- const pos = getCellPosition(cell);
1449
- if (!pos)
1450
- return;
1451
- selectingRef.current = { tbody: pos.tbody, start: cell };
1452
- const onMove = (ev) => {
1453
- const under = document.elementFromPoint(ev.clientX, ev.clientY);
1454
- const overCell = getClosestCell(under);
1455
- const startInfo = selectingRef.current;
1456
- if (!overCell || !startInfo)
1837
+ }, onMouseDown: (e) => {
1838
+ const cell = getClosestCell(e.target);
1839
+ if (!cell) {
1840
+ clearSelectionDecor();
1457
1841
  return;
1458
- const a = getCellPosition(startInfo.start);
1459
- const b = getCellPosition(overCell);
1460
- if (!a || !b || a.tbody !== b.tbody)
1842
+ }
1843
+ const pos = getCellPosition(cell);
1844
+ if (!pos)
1461
1845
  return;
1462
- const sr = Math.min(a.rIdx, b.rIdx);
1463
- const sc = Math.min(a.cIdx, b.cIdx);
1464
- const er = Math.max(a.rIdx, b.rIdx);
1465
- const ec = Math.max(a.cIdx, b.cIdx);
1466
- updateSelectionDecor(a.tbody, sr, sc, er, ec);
1467
- };
1468
- const onUp = () => {
1469
- window.removeEventListener("mousemove", onMove);
1470
- window.removeEventListener("mouseup", onUp);
1471
- selectingRef.current = null;
1472
- };
1473
- window.addEventListener("mousemove", onMove);
1474
- window.addEventListener("mouseup", onUp);
1475
- }, onContextMenu: (e) => {
1476
- const target = e.target;
1477
- if (target && target.tagName === "IMG") {
1478
- e.preventDefault();
1479
- const vw = window.innerWidth;
1480
- const vh = window.innerHeight;
1481
- const menuW = 220;
1482
- const menuH = 200;
1483
- const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
1484
- const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
1485
- setImageMenu({ x, y, img: target });
1486
- setTableMenu(null);
1487
- return;
1488
- }
1489
- const cell = getClosestCell(e.target);
1490
- if (cell) {
1491
- e.preventDefault();
1492
- const vw = window.innerWidth;
1493
- const vh = window.innerHeight;
1494
- const menuW = 220;
1495
- const menuH = 300;
1496
- const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
1497
- const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
1498
- setTableMenu({ x, y, cell });
1499
- }
1500
- else {
1501
- setTableMenu(null);
1502
- setImageMenu(null);
1503
- }
1504
- } }), selectedImage && imageOverlay && (_jsxs("div", { style: {
1846
+ selectingRef.current = { tbody: pos.tbody, start: cell };
1847
+ const onMove = (ev) => {
1848
+ const under = document.elementFromPoint(ev.clientX, ev.clientY);
1849
+ const overCell = getClosestCell(under);
1850
+ const startInfo = selectingRef.current;
1851
+ if (!overCell || !startInfo)
1852
+ return;
1853
+ const a = getCellPosition(startInfo.start);
1854
+ const b = getCellPosition(overCell);
1855
+ if (!a || !b || a.tbody !== b.tbody)
1856
+ return;
1857
+ const sr = Math.min(a.rIdx, b.rIdx);
1858
+ const sc = Math.min(a.cIdx, b.cIdx);
1859
+ const er = Math.max(a.rIdx, b.rIdx);
1860
+ const ec = Math.max(a.cIdx, b.cIdx);
1861
+ updateSelectionDecor(a.tbody, sr, sc, er, ec);
1862
+ };
1863
+ const onUp = () => {
1864
+ window.removeEventListener("mousemove", onMove);
1865
+ window.removeEventListener("mouseup", onUp);
1866
+ selectingRef.current = null;
1867
+ };
1868
+ window.addEventListener("mousemove", onMove);
1869
+ window.addEventListener("mouseup", onUp);
1870
+ }, onContextMenu: (e) => {
1871
+ const target = e.target;
1872
+ if (target && target.tagName === "IMG") {
1873
+ e.preventDefault();
1874
+ const vw = window.innerWidth;
1875
+ const vh = window.innerHeight;
1876
+ const menuW = 220;
1877
+ const menuH = 200;
1878
+ const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
1879
+ const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
1880
+ setImageMenu({ x, y, img: target });
1881
+ setTableMenu(null);
1882
+ return;
1883
+ }
1884
+ const cell = getClosestCell(e.target);
1885
+ if (cell) {
1886
+ e.preventDefault();
1887
+ const vw = window.innerWidth;
1888
+ const vh = window.innerHeight;
1889
+ const menuW = 220;
1890
+ const menuH = 300;
1891
+ const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
1892
+ const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
1893
+ setTableMenu({ x, y, cell });
1894
+ }
1895
+ else {
1896
+ setTableMenu(null);
1897
+ setImageMenu(null);
1898
+ }
1899
+ } }) }), selectedImage && imageOverlay && (_jsxs("div", { style: {
1505
1900
  position: "fixed",
1506
1901
  left: imageOverlay.left,
1507
1902
  top: imageOverlay.top,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartrte-react",
3
- "version": "0.1.10",
3
+ "version": "0.1.14",
4
4
  "description": "A powerful, feature-rich Rich Text Editor for React with support for tables, mathematical formulas (LaTeX/KaTeX), and media management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -38,6 +38,16 @@
38
38
  },
39
39
  "author": "Smart RTE Contributors",
40
40
  "license": "MIT",
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.json",
43
+ "prepublishOnly": "pnpm run build",
44
+ "dev": "pnpm build",
45
+ "lint": "eslint . || true",
46
+ "test": "vitest run || true",
47
+ "storybook": "storybook dev -p 6006",
48
+ "build-storybook": "storybook build",
49
+ "e2e": "playwright test || true"
50
+ },
41
51
  "publishConfig": {
42
52
  "access": "public"
43
53
  },
@@ -59,16 +69,5 @@
59
69
  "@playwright/test": "^1.48.2",
60
70
  "react": "18.3.1",
61
71
  "react-dom": "18.3.1"
62
- },
63
- "scripts": {
64
- "build": "tsc -p tsconfig.json",
65
- "build:embed": "vite build",
66
- "build:all": "pnpm run build && pnpm run build:embed",
67
- "dev": "pnpm build",
68
- "lint": "eslint . || true",
69
- "test": "vitest run || true",
70
- "storybook": "storybook dev -p 6006",
71
- "build-storybook": "storybook build",
72
- "e2e": "playwright test || true"
73
72
  }
74
- }
73
+ }