smartrte-react 0.1.14 → 0.1.15

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.
@@ -1,6 +1,12 @@
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
+ import * as pdfjsLib from 'pdfjs-dist';
5
+ import mammoth from 'mammoth';
6
+ // Initialize PDF.js worker
7
+ if (typeof window !== 'undefined') {
8
+ pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
9
+ }
4
10
  export function ClassicEditor({ value, onChange, placeholder = "Type here…", minHeight = 200, maxHeight = 500, readOnly = false, table = true, media = true, formula = true, mediaManager, fonts = [
5
11
  { name: "Arial", value: "Arial, Helvetica, sans-serif" },
6
12
  { name: "Georgia", value: "Georgia, serif" },
@@ -14,6 +20,12 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
14
20
  const lastEmittedRef = useRef("");
15
21
  const isComposingRef = useRef(false);
16
22
  const fileInputRef = useRef(null);
23
+ const pdfInputRef = useRef(null);
24
+ const docxInputRef = useRef(null);
25
+ const [loadingPdf, setLoadingPdf] = useState(false);
26
+ const [loadingDocx, setLoadingDocx] = useState(false);
27
+ // State for import confirmation
28
+ const [pendingImport, setPendingImport] = useState(null);
17
29
  const replaceTargetRef = useRef(null);
18
30
  const [selectedImage, setSelectedImage] = useState(null);
19
31
  const [imageOverlay, setImageOverlay] = useState(null);
@@ -44,6 +56,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
44
56
  el.innerHTML = value || "";
45
57
  fixNegativeMargins(el);
46
58
  ensureTableWrappers(el);
59
+ addTableResizeHandles();
47
60
  }
48
61
  // Suppress native context menu inside table cells at capture phase
49
62
  const onCtx = (evt) => {
@@ -267,14 +280,15 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
267
280
  if (!table)
268
281
  return;
269
282
  const target = e.target;
270
- if (target.tagName === 'TD' || target.tagName === 'TH') {
271
- const rect = target.getBoundingClientRect();
283
+ const cell = getClosestCell(target);
284
+ if (cell) {
285
+ const rect = cell.getBoundingClientRect();
272
286
  const rightEdge = rect.right;
273
287
  const clickX = e.clientX;
274
288
  if (Math.abs(clickX - rightEdge) < 5) {
275
289
  e.preventDefault();
276
- const tableElem = target.closest('table');
277
- const colIndex = parseInt(target.getAttribute('data-col-index') || '0', 10);
290
+ const tableElem = cell.closest('table');
291
+ const colIndex = parseInt(cell.getAttribute('data-col-index') || '0', 10);
278
292
  if (tableElem) {
279
293
  startColumnResize(tableElem, colIndex, e.clientX);
280
294
  }
@@ -284,8 +298,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
284
298
  const clickY = e.clientY;
285
299
  if (Math.abs(clickY - bottomEdge) < 5) {
286
300
  e.preventDefault();
287
- const tableElem = target.closest('table');
288
- const row = target.closest('tr');
301
+ const tableElem = cell.closest('table');
302
+ const row = cell.closest('tr');
289
303
  if (tableElem && row) {
290
304
  const rowIndex = parseInt(row.getAttribute('data-row-index') || '0', 10);
291
305
  startRowResize(tableElem, rowIndex, e.clientY);
@@ -301,23 +315,26 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
301
315
  }
302
316
  if (!table)
303
317
  return;
318
+ let cursor = '';
304
319
  const target = e.target;
305
- if (target.tagName === 'TD' || target.tagName === 'TH') {
306
- const rect = target.getBoundingClientRect();
320
+ const cell = getClosestCell(target);
321
+ if (cell) {
322
+ const rect = cell.getBoundingClientRect();
307
323
  const clickX = e.clientX;
308
324
  const clickY = e.clientY;
309
325
  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;
326
+ cursor = 'col-resize';
316
327
  }
317
- if (el.style.cursor === 'col-resize' || el.style.cursor === 'row-resize') {
318
- el.style.cursor = '';
328
+ else if (Math.abs(clickY - rect.bottom) < 5) {
329
+ cursor = 'row-resize';
319
330
  }
320
331
  }
332
+ if (cursor) {
333
+ el.style.cursor = cursor;
334
+ }
335
+ else if (el.style.cursor === 'col-resize' || el.style.cursor === 'row-resize') {
336
+ el.style.cursor = '';
337
+ }
321
338
  };
322
339
  const onMouseUp = () => {
323
340
  handleTableResizeEnd();
@@ -326,15 +343,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
326
343
  if (!table)
327
344
  return;
328
345
  const target = e.target;
329
- if (target.tagName === 'TD' || target.tagName === 'TH') {
330
- const rect = target.getBoundingClientRect();
346
+ const cell = getClosestCell(target);
347
+ if (cell) {
348
+ const rect = cell.getBoundingClientRect();
331
349
  const touch = e.touches[0];
332
350
  const clickX = touch.clientX;
333
351
  const clickY = touch.clientY;
334
352
  if (Math.abs(clickX - rect.right) < 15) {
335
353
  e.preventDefault();
336
- const tableElem = target.closest('table');
337
- const colIndex = parseInt(target.getAttribute('data-col-index') || '0', 10);
354
+ const tableElem = cell.closest('table');
355
+ const colIndex = parseInt(cell.getAttribute('data-col-index') || '0', 10);
338
356
  if (tableElem) {
339
357
  startColumnResize(tableElem, colIndex, clickX);
340
358
  }
@@ -342,8 +360,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
342
360
  }
343
361
  if (Math.abs(clickY - rect.bottom) < 15) {
344
362
  e.preventDefault();
345
- const tableElem = target.closest('table');
346
- const row = target.closest('tr');
363
+ const tableElem = cell.closest('table');
364
+ const row = cell.closest('tr');
347
365
  if (tableElem && row) {
348
366
  const rowIndex = parseInt(row.getAttribute('data-row-index') || '0', 10);
349
367
  startRowResize(tableElem, rowIndex, clickY);
@@ -528,6 +546,375 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
528
546
  });
529
547
  }
530
548
  };
549
+ const handlePdfFiles = (files) => {
550
+ if (!files || files.length === 0)
551
+ return;
552
+ const file = files[0];
553
+ if (file.type !== 'application/pdf')
554
+ return;
555
+ // Check if editor has content
556
+ const el = editableRef.current;
557
+ const hasContent = el && el.textContent && el.textContent.trim().length > 0;
558
+ if (hasContent) {
559
+ setPendingImport({ file, type: 'pdf' });
560
+ }
561
+ else {
562
+ processImport(file, 'pdf', 'replace');
563
+ }
564
+ };
565
+ const processImport = async (file, type, mode) => {
566
+ if (type === 'pdf') {
567
+ await processPdf(file, mode);
568
+ }
569
+ else {
570
+ await processDocx(file, mode);
571
+ }
572
+ setPendingImport(null);
573
+ // Reset inputs
574
+ if (pdfInputRef.current)
575
+ pdfInputRef.current.value = "";
576
+ if (docxInputRef.current)
577
+ docxInputRef.current.value = "";
578
+ };
579
+ const processPdf = async (file, mode) => {
580
+ try {
581
+ setLoadingPdf(true);
582
+ const arrayBuffer = await file.arrayBuffer();
583
+ const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
584
+ let fullHtml = '';
585
+ for (let i = 1; i <= pdf.numPages; i++) {
586
+ const page = await pdf.getPage(i);
587
+ const textContent = await page.getTextContent();
588
+ const styles = textContent.styles;
589
+ // 1. Group items into lines
590
+ const items = textContent.items;
591
+ // Calculate base statistics
592
+ const heights = items.map(item => Math.abs(item.transform[3])).filter(h => h > 0);
593
+ heights.sort((a, b) => a - b);
594
+ const medianHeight = heights[Math.floor(heights.length / 2)] || 12;
595
+ // Group by Y (with tolerance)
596
+ const linesMap = new Map();
597
+ for (const item of items) {
598
+ if (!item.str.trim())
599
+ continue;
600
+ // Normalize Y to integer buckets to group roughly
601
+ // PDF Y is bottom-0, so higher Y is higher on page.
602
+ const y = item.transform[5];
603
+ // Find closest existing line
604
+ let foundKey = -1;
605
+ for (const key of linesMap.keys()) {
606
+ if (Math.abs(key - y) < medianHeight * 0.5) {
607
+ foundKey = key;
608
+ break;
609
+ }
610
+ }
611
+ if (foundKey !== -1) {
612
+ linesMap.get(foundKey).items.push(item);
613
+ }
614
+ else {
615
+ linesMap.set(y, { y, items: [item] });
616
+ }
617
+ }
618
+ // Convert map to sorted array (top to bottom)
619
+ const lines = Array.from(linesMap.values()).sort((a, b) => b.y - a.y);
620
+ // Sort items within lines (left to right)
621
+ lines.forEach(line => {
622
+ line.items.sort((a, b) => a.transform[4] - b.transform[4]);
623
+ });
624
+ // 2. Identify and Build Structures
625
+ let html = '';
626
+ let listStack = []; // 'ul' or 'ol'
627
+ let inTable = false;
628
+ let tableColumns = []; // X-coordinates of column starts
629
+ let tableHtml = '';
630
+ const closeList = () => {
631
+ if (listStack.length > 0) {
632
+ html += `</${listStack.pop()}>`;
633
+ }
634
+ };
635
+ const closeTable = () => {
636
+ if (inTable) {
637
+ html += '<div data-table-wrapper="true" style="overflow-x:auto;width:100%;"><table style="border-collapse:collapse;width:100%;" border="1"><tbody>' + tableHtml + '</tbody></table></div>';
638
+ tableHtml = '';
639
+ inTable = false;
640
+ tableColumns = [];
641
+ }
642
+ };
643
+ for (let lIndex = 0; lIndex < lines.length; lIndex++) {
644
+ const line = lines[lIndex];
645
+ // Calculate gaps and text
646
+ let lineText = '';
647
+ let lineHtmlContent = '';
648
+ let lastX = -1;
649
+ let gaps = [];
650
+ let itemXs = []; // Start X of logical items (words or phrases)
651
+ // Reconstruct text with spacing detection
652
+ for (let j = 0; j < line.items.length; j++) {
653
+ const item = line.items[j];
654
+ const x = item.transform[4];
655
+ const width = item.width;
656
+ const fontName = item.fontName;
657
+ const fontObj = styles[fontName];
658
+ const isBold = fontObj?.fontFamily?.toLowerCase().includes('bold') || false;
659
+ // const isItalic = fontObj?.fontFamily?.toLowerCase().includes('italic') || false;
660
+ if (lastX > 0) {
661
+ const gap = x - lastX;
662
+ if (gap > 2) { // Minimal space threshold
663
+ lineText += ' ';
664
+ lineHtmlContent += ' ';
665
+ if (gap > 20) { // Large gap threshold for table detection
666
+ gaps.push(gap);
667
+ }
668
+ }
669
+ }
670
+ else {
671
+ // First item
672
+ }
673
+ // Track "columns" candidates: items separated by big gaps
674
+ if (j === 0 || (x - lastX) > 20) {
675
+ itemXs.push(x);
676
+ }
677
+ // Append text style
678
+ let chunk = item.str;
679
+ if (isBold)
680
+ chunk = `<strong>${chunk}</strong>`;
681
+ // if (isItalic) chunk = `<em>${chunk}</em>`;
682
+ lineText += item.str;
683
+ lineHtmlContent += chunk;
684
+ lastX = x + width;
685
+ }
686
+ // === Structure Detection ===
687
+ // Max Font Size in line
688
+ const maxH = Math.max(...line.items.map((i) => Math.abs(i.transform[3])));
689
+ const isHeader = maxH > medianHeight * 1.2;
690
+ // List Detection
691
+ const isBullet = /^[•\-\*]\s/.test(lineText);
692
+ const isNumber = /^\d+[\.\)]\s/.test(lineText);
693
+ // Table Detection Logic
694
+ // A line starts a table if it has distinct "columns" (multiple items with large gaps)
695
+ // Or if we are already in a table and this line aligns with columns
696
+ let isTableLine = false;
697
+ // If in table, check alignment
698
+ if (inTable) {
699
+ // Check if items align with known columns
700
+ // Simple loose check: do any of the itemXs align with tableColumns?
701
+ // Or is the line just sparsely populated but roughly compatible?
702
+ // We'll continually simple-add rows for now until a Paragraph break (plain text, no gaps) is found.
703
+ // If line looks like normal paragraph (no large gaps, starts at left margin), close table
704
+ const isPlainParagraph = gaps.length === 0 && itemXs[0] < 50 && lineText.length > 50;
705
+ // Allow wrapping text in table cells, which might look like lines with no gaps?
706
+ // Table wrapping usually is indented or aligns with a column > 0.
707
+ const alignsWithColumn = itemXs.some(x => tableColumns.some(cx => Math.abs(x - cx) < 20));
708
+ if (alignsWithColumn || (itemXs[0] > 50)) {
709
+ isTableLine = true;
710
+ }
711
+ else {
712
+ // Maybe a new row starting at col 0?
713
+ // If it aligns with col 0.
714
+ if (Math.abs(itemXs[0] - tableColumns[0]) < 20) {
715
+ isTableLine = true;
716
+ }
717
+ }
718
+ }
719
+ else {
720
+ // Potential start of table: multiple items separated by gaps, AND next line likely follows suit?
721
+ // Or simply: It has > 1 column significantly spaced.
722
+ if (itemXs.length >= 2 && gaps.some(g => g > 30)) {
723
+ isTableLine = true;
724
+ // Establish columns
725
+ tableColumns = [...itemXs];
726
+ }
727
+ }
728
+ // --- Apply Logic ---
729
+ if (isTableLine) {
730
+ closeList();
731
+ if (!inTable) {
732
+ inTable = true;
733
+ // Start table
734
+ }
735
+ // Build Row
736
+ // We need to map items to cells based on tableColumns.
737
+ // Naive approach: Items close to col X go to col X.
738
+ let rowHtml = '<tr>';
739
+ // We assume `tableColumns` defines the start of each cell.
740
+ // We create a cell for each column.
741
+ // Collect content for each bucket.
742
+ const cellContents = new Array(tableColumns.length).fill('');
743
+ let currentItemHtml = '';
744
+ let currentItemStart = -1;
745
+ // process items again to slot them
746
+ let currentLineX = 0;
747
+ for (const item of line.items) {
748
+ const x = item.transform[4];
749
+ const w = item.width;
750
+ const txt = item.str;
751
+ const fontObj = styles[item.fontName];
752
+ const isBold = fontObj?.fontFamily?.toLowerCase().includes('bold');
753
+ const styledTxt = isBold ? `<strong>${txt}</strong>` : txt;
754
+ // Decide which column this belongs to
755
+ // Find closest column to the left (or close enough)
756
+ let colIdx = 0;
757
+ let minDiff = 9999;
758
+ for (let c = 0; c < tableColumns.length; c++) {
759
+ const colX = tableColumns[c];
760
+ // If item starts near colX or after it (but before next col)
761
+ // Actually, just find the "controlling" column (closest start to the left)
762
+ if (x >= colX - 10) {
763
+ colIdx = c;
764
+ }
765
+ }
766
+ // Append space if needed
767
+ if (cellContents[colIdx])
768
+ cellContents[colIdx] += ' ';
769
+ cellContents[colIdx] += styledTxt;
770
+ }
771
+ cellContents.forEach(content => {
772
+ rowHtml += `<td style="border:1px solid #ddd;padding:8px;vertical-align:top;">${content || '&nbsp;'}</td>`;
773
+ });
774
+ rowHtml += '</tr>';
775
+ tableHtml += rowHtml;
776
+ }
777
+ else {
778
+ closeTable();
779
+ if (isBullet || isNumber) {
780
+ const listType = isBullet ? 'ul' : 'ol';
781
+ if (listStack.length === 0 || listStack[listStack.length - 1] !== listType) {
782
+ if (listStack.length > 0)
783
+ closeList(); // Close switch
784
+ html += `<${listType}>`;
785
+ listStack.push(listType);
786
+ }
787
+ // Strip marker
788
+ const content = lineHtmlContent.replace(/^[•\-\*]|\d+[\.\)]/, '').trim();
789
+ html += `<li>${content}</li>`;
790
+ }
791
+ else {
792
+ closeList();
793
+ if (isHeader) {
794
+ const tag = maxH > medianHeight * 1.5 ? 'h2' : 'h3';
795
+ html += `<${tag}>${lineHtmlContent}</${tag}>`;
796
+ }
797
+ else {
798
+ html += `<p>${lineHtmlContent}</p>`;
799
+ }
800
+ }
801
+ }
802
+ }
803
+ closeList();
804
+ closeTable();
805
+ fullHtml += html;
806
+ }
807
+ const el = editableRef.current;
808
+ if (el) {
809
+ el.focus();
810
+ if (mode === 'replace') {
811
+ // Select all and replace
812
+ const range = document.createRange();
813
+ range.selectNodeContents(el);
814
+ const sel = window.getSelection();
815
+ sel?.removeAllRanges();
816
+ sel?.addRange(range);
817
+ exec("delete"); // Clear content safely
818
+ exec("insertHTML", fullHtml);
819
+ }
820
+ else {
821
+ // Append
822
+ const range = document.createRange();
823
+ range.selectNodeContents(el);
824
+ range.collapse(false); // End
825
+ const sel = window.getSelection();
826
+ sel?.removeAllRanges();
827
+ sel?.addRange(range);
828
+ exec("insertHTML", "<br>" + fullHtml);
829
+ }
830
+ }
831
+ }
832
+ catch (error) {
833
+ console.error('Error reading PDF:', error);
834
+ // Optional: show user error
835
+ }
836
+ finally {
837
+ setLoadingPdf(false);
838
+ }
839
+ };
840
+ const handleDocxFiles = (files) => {
841
+ if (!files || files.length === 0)
842
+ return;
843
+ const file = files[0];
844
+ if (!file.name.endsWith('.docx'))
845
+ return;
846
+ // Check if editor has content
847
+ const el = editableRef.current;
848
+ const hasContent = el && el.textContent && el.textContent.trim().length > 0;
849
+ if (hasContent) {
850
+ setPendingImport({ file, type: 'docx' });
851
+ }
852
+ else {
853
+ processImport(file, 'docx', 'replace');
854
+ }
855
+ };
856
+ const processDocx = async (file, mode) => {
857
+ try {
858
+ setLoadingDocx(true);
859
+ const arrayBuffer = await file.arrayBuffer();
860
+ const result = await mammoth.convertToHtml({ arrayBuffer });
861
+ let html = result.value;
862
+ if (html) {
863
+ // Process HTML to ensure tables have borders and structure
864
+ const temp = document.createElement('div');
865
+ temp.innerHTML = html;
866
+ const tables = temp.querySelectorAll('table');
867
+ tables.forEach(tbl => {
868
+ tbl.style.borderCollapse = 'collapse';
869
+ tbl.style.minWidth = '100%';
870
+ // Browser parser auto-adds tbody, but we verify styles
871
+ const cells = tbl.querySelectorAll('td, th');
872
+ cells.forEach(cell => {
873
+ cell.style.border = '1px solid #000';
874
+ cell.style.padding = '8px';
875
+ cell.style.verticalAlign = 'top';
876
+ });
877
+ });
878
+ html = temp.innerHTML;
879
+ const el = editableRef.current;
880
+ if (el) {
881
+ el.focus();
882
+ if (mode === 'replace') {
883
+ const range = document.createRange();
884
+ range.selectNodeContents(el);
885
+ const sel = window.getSelection();
886
+ sel?.removeAllRanges();
887
+ sel?.addRange(range);
888
+ exec("delete");
889
+ exec("insertHTML", html);
890
+ }
891
+ else {
892
+ const range = document.createRange();
893
+ range.selectNodeContents(el);
894
+ range.collapse(false);
895
+ const sel = window.getSelection();
896
+ sel?.removeAllRanges();
897
+ sel?.addRange(range);
898
+ exec("insertHTML", "<br>" + html);
899
+ }
900
+ // Initialize handlers for the new content
901
+ // We use setTimeout to let the DOM settle after execCommand
902
+ setTimeout(() => {
903
+ ensureTableWrappers(el);
904
+ addTableResizeHandles();
905
+ fixNegativeMargins(el);
906
+ handleInput();
907
+ }, 10);
908
+ }
909
+ }
910
+ }
911
+ catch (error) {
912
+ console.error('Error reading DOCX:', error);
913
+ }
914
+ finally {
915
+ setLoadingDocx(false);
916
+ }
917
+ };
531
918
  const fixNegativeMargins = (root) => {
532
919
  try {
533
920
  const nodes = root.querySelectorAll('*');
@@ -582,6 +969,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
582
969
  fixNegativeMargins(el);
583
970
  // Ensure tables are wrapped for horizontal scrolling
584
971
  ensureTableWrappers(el);
972
+ // Add resize handles to tables
973
+ addTableResizeHandles();
585
974
  if (!onChange)
586
975
  return;
587
976
  const html = el.innerHTML;
@@ -1152,7 +1541,13 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1152
1541
  }
1153
1542
  }
1154
1543
  e.currentTarget.value = "";
1155
- } })), _jsxs("select", { defaultValue: "p", onChange: (e) => {
1544
+ } })), _jsx("input", { ref: pdfInputRef, type: "file", accept: "application/pdf", style: { display: "none" }, onChange: (e) => {
1545
+ handlePdfFiles(e.currentTarget.files);
1546
+ e.currentTarget.value = "";
1547
+ } }), _jsx("input", { ref: docxInputRef, type: "file", accept: ".docx", style: { display: "none" }, onChange: (e) => {
1548
+ handleDocxFiles(e.currentTarget.files);
1549
+ e.currentTarget.value = "";
1550
+ } }), _jsxs("select", { defaultValue: "p", onChange: (e) => {
1156
1551
  const val = e.target.value;
1157
1552
  if (val === "p")
1158
1553
  applyFormatBlock("<p>");
@@ -1328,7 +1723,23 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1328
1723
  borderRadius: 6,
1329
1724
  background: "#fff",
1330
1725
  color: "#111",
1331
- }, children: "\uD83D\uDCC1 Media" })), _jsxs("div", { style: {
1726
+ }, children: "\uD83D\uDCC1 Media" })), _jsx("button", { title: "Import PDF", onClick: () => pdfInputRef.current?.click(), disabled: loadingPdf, style: {
1727
+ height: 32,
1728
+ padding: "0 10px",
1729
+ border: "1px solid #e5e7eb",
1730
+ borderRadius: 6,
1731
+ background: "#fff",
1732
+ color: "#111",
1733
+ opacity: loadingPdf ? 0.5 : 1,
1734
+ }, children: loadingPdf ? '⌛ Importing...' : '📄 PDF' }), _jsx("button", { title: "Import DOCX", onClick: () => docxInputRef.current?.click(), disabled: loadingDocx, style: {
1735
+ height: 32,
1736
+ padding: "0 10px",
1737
+ border: "1px solid #e5e7eb",
1738
+ borderRadius: 6,
1739
+ background: "#fff",
1740
+ color: "#111",
1741
+ opacity: loadingDocx ? 0.5 : 1,
1742
+ }, children: loadingDocx ? '⌛ Importing...' : '📝 DOCX' }), _jsxs("div", { style: {
1332
1743
  display: "inline-flex",
1333
1744
  gap: 4,
1334
1745
  alignItems: "center",
@@ -1451,7 +1862,60 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1451
1862
  }, children: [_jsx("button", { onClick: () => setShowTableDialog(false), children: "Cancel" }), _jsx("button", { onClick: () => {
1452
1863
  insertTable();
1453
1864
  setShowTableDialog(false);
1454
- }, children: "Insert" })] })] }) })), showColorPicker && (_jsx("div", { style: {
1865
+ }, children: "Insert" })] })] }) })), pendingImport && (_jsx("div", { style: {
1866
+ position: "fixed",
1867
+ inset: 0,
1868
+ background: "rgba(0,0,0,0.5)",
1869
+ display: "flex",
1870
+ alignItems: "center",
1871
+ justifyContent: "center",
1872
+ zIndex: 100,
1873
+ }, onClick: () => {
1874
+ setPendingImport(null);
1875
+ if (pdfInputRef.current)
1876
+ pdfInputRef.current.value = "";
1877
+ if (docxInputRef.current)
1878
+ docxInputRef.current.value = "";
1879
+ }, children: _jsxs("div", { style: {
1880
+ background: "#fff",
1881
+ padding: 20,
1882
+ borderRadius: 8,
1883
+ maxWidth: 400,
1884
+ width: "90%",
1885
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
1886
+ }, onClick: (e) => e.stopPropagation(), children: [_jsx("h3", { style: { margin: "0 0 12px 0", fontSize: 18, fontWeight: 600 }, children: "Import Content" }), _jsx("p", { style: { margin: "0 0 20px 0", color: "#4b5563", fontSize: 14 }, children: "The editor already contains content. How would you like to handle the imported document?" }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsx("button", { onClick: () => processImport(pendingImport.file, pendingImport.type, 'replace'), style: {
1887
+ padding: "8px 16px",
1888
+ background: "#dc2626",
1889
+ color: "white",
1890
+ border: "none",
1891
+ borderRadius: 6,
1892
+ cursor: "pointer",
1893
+ fontWeight: 500,
1894
+ textAlign: "left"
1895
+ }, children: "Replace Check existing content (Overwrite)" }), _jsx("button", { onClick: () => processImport(pendingImport.file, pendingImport.type, 'append'), style: {
1896
+ padding: "8px 16px",
1897
+ background: "#2563eb",
1898
+ color: "white",
1899
+ border: "none",
1900
+ borderRadius: 6,
1901
+ cursor: "pointer",
1902
+ fontWeight: 500,
1903
+ textAlign: "left"
1904
+ }, children: "Append to bottom" }), _jsx("button", { onClick: () => {
1905
+ setPendingImport(null);
1906
+ if (pdfInputRef.current)
1907
+ pdfInputRef.current.value = "";
1908
+ if (docxInputRef.current)
1909
+ docxInputRef.current.value = "";
1910
+ }, style: {
1911
+ padding: "8px 16px",
1912
+ background: "#f3f4f6",
1913
+ color: "#374151",
1914
+ border: "1px solid #d1d5db",
1915
+ borderRadius: 6,
1916
+ cursor: "pointer",
1917
+ marginTop: 4
1918
+ }, children: "Cancel" })] })] }) })), showColorPicker && (_jsx("div", { style: {
1455
1919
  position: "fixed",
1456
1920
  inset: 0,
1457
1921
  background: "rgba(0,0,0,0.35)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartrte-react",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
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,16 +38,7 @@
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
+ "runkitExampleFilename": "runkit-example.js",
51
42
  "publishConfig": {
52
43
  "access": "public"
53
44
  },
@@ -57,17 +48,31 @@
57
48
  "react-dom": ">=18"
58
49
  },
59
50
  "devDependencies": {
60
- "typescript": "^5.6.2",
61
- "@types/react": "^18.3.4",
62
- "@types/react-dom": "^18.3.0",
51
+ "@playwright/test": "^1.48.2",
52
+ "@storybook/addon-essentials": "^8.6.14",
63
53
  "@storybook/react": "^8.6.14",
64
54
  "@storybook/react-vite": "^8.6.14",
65
- "@storybook/addon-essentials": "^8.6.14",
55
+ "@types/pdfjs-dist": "^2.10.377",
56
+ "@types/react": "^18.3.4",
57
+ "@types/react-dom": "^18.3.0",
58
+ "react": "18.3.1",
59
+ "react-dom": "18.3.1",
66
60
  "storybook": "^8.6.14",
61
+ "typescript": "^5.6.2",
67
62
  "vite": "^5.4.8",
68
- "vitest": "^2.1.4",
69
- "@playwright/test": "^1.48.2",
70
- "react": "18.3.1",
71
- "react-dom": "18.3.1"
63
+ "vitest": "^2.1.4"
64
+ },
65
+ "dependencies": {
66
+ "mammoth": "^1.11.0",
67
+ "pdfjs-dist": "^5.4.530"
68
+ },
69
+ "scripts": {
70
+ "build": "tsc -p tsconfig.json",
71
+ "dev": "pnpm build",
72
+ "lint": "eslint . || true",
73
+ "test": "vitest run || true",
74
+ "storybook": "storybook dev -p 6006",
75
+ "build-storybook": "storybook build",
76
+ "e2e": "playwright test || true"
72
77
  }
73
- }
78
+ }