@bagelink/vue 1.15.515 → 1.15.521
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/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -1
- package/dist/core.css +2 -2
- package/dist/index.cjs +130 -110
- package/dist/index.css +1 -1
- package/dist/index.mjs +9733 -9598
- package/dist/style.css +2 -2
- package/package.json +1 -1
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +8 -5
- package/src/components/form/inputs/RichText/index.vue +277 -66
- package/src/components/form/inputs/RichText/utils/commands.ts +48 -3
- package/src/components/form/inputs/RichText/utils/formatting.ts +5 -1
- package/src/components/form/inputs/Upload/UploadInput.vue +1 -1
- package/src/styles/appearance.css +50 -0
|
@@ -358,10 +358,31 @@ const tableForm = ref({
|
|
|
358
358
|
alternateRowBgColor: '#f9f9f9',
|
|
359
359
|
alternateRowTextColor: '#333333',
|
|
360
360
|
fixedLayout: true, // תאים ברוחב קבוע
|
|
361
|
+
columnWidths: [33.34, 33.33, 33.33] as number[],
|
|
361
362
|
alignment: 'left', // left, center, right
|
|
362
363
|
direction: 'ltr' // ltr, rtl
|
|
363
364
|
})
|
|
364
365
|
|
|
366
|
+
const columnWidthsTotal = computed(() => tableForm.value.columnWidths
|
|
367
|
+
.slice(0, tableForm.value.cols)
|
|
368
|
+
.reduce((total, width) => total + (Number(width) || 0), 0))
|
|
369
|
+
|
|
370
|
+
function equalColumnWidths(cols: number): number[] {
|
|
371
|
+
if (cols <= 0) { return [] }
|
|
372
|
+
const base = Number((100 / cols).toFixed(2))
|
|
373
|
+
const widths = Array.from({ length: cols }, () => base)
|
|
374
|
+
widths[cols - 1] = Number((100 - base * (cols - 1)).toFixed(2))
|
|
375
|
+
return widths
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function syncColumnWidths(cols: number) {
|
|
379
|
+
const current = tableForm.value.columnWidths
|
|
380
|
+
if (current.length === cols) { return }
|
|
381
|
+
tableForm.value.columnWidths = equalColumnWidths(cols)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
watch(() => tableForm.value.cols, syncColumnWidths)
|
|
385
|
+
|
|
365
386
|
// Table context menu state
|
|
366
387
|
const showTableContextMenu = ref(false)
|
|
367
388
|
const contextMenuPosition = ref({ x: 0, y: 0 })
|
|
@@ -388,6 +409,7 @@ const tablePreviewHtml = computed(() => {
|
|
|
388
409
|
// For cell text alignment, we'll apply it to individual cells, not the table
|
|
389
410
|
|
|
390
411
|
let html = `<table style="${tableStyle}" dir="${form.direction}">`
|
|
412
|
+
html += `<colgroup>${form.columnWidths.slice(0, form.cols).map(width => `<col style="width: ${width}%;">`).join('')}</colgroup>`
|
|
391
413
|
|
|
392
414
|
// Add header row if enabled
|
|
393
415
|
if (form.showHeaders) {
|
|
@@ -399,7 +421,7 @@ const tablePreviewHtml = computed(() => {
|
|
|
399
421
|
background-color: ${form.headerBgColor};
|
|
400
422
|
color: ${form.headerTextColor};
|
|
401
423
|
text-align: ${form.alignment};
|
|
402
|
-
${form.fixedLayout ? `width: ${100 / form.cols}%;` : ''}
|
|
424
|
+
${form.fixedLayout ? `width: ${form.columnWidths[j] ?? (100 / form.cols)}%;` : ''}
|
|
403
425
|
`
|
|
404
426
|
html += `<th style="${thStyle}">Header ${j + 1}</th>`
|
|
405
427
|
}
|
|
@@ -415,7 +437,7 @@ const tablePreviewHtml = computed(() => {
|
|
|
415
437
|
padding: ${form.cellPadding}px;
|
|
416
438
|
border: ${form.borderWidth}px solid ${form.borderColor};
|
|
417
439
|
text-align: ${form.alignment};
|
|
418
|
-
${form.fixedLayout ? `width: ${100 / form.cols}%;` : ''}
|
|
440
|
+
${form.fixedLayout ? `width: ${form.columnWidths[j] ?? (100 / form.cols)}%;` : ''}
|
|
419
441
|
${form.alternateRows && i % 2 === 1
|
|
420
442
|
? `background-color: ${form.alternateRowBgColor}; color: ${form.alternateRowTextColor};`
|
|
421
443
|
: `background-color: ${form.cellBgColor}; color: ${form.cellTextColor};`}
|
|
@@ -434,38 +456,57 @@ editor.state.content = props.modelValue
|
|
|
434
456
|
|
|
435
457
|
// Matches Hebrew, Arabic, Syriac, Thaana, Arabic Supplement, Arabic Presentation Forms
|
|
436
458
|
const RTL_REGEX = /[\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\uFB1D-\uFDFD\uFE70-\uFEFC]/
|
|
459
|
+
const LTR_REGEX = /[A-Za-z\u00C0-\u02AF\u0370-\u058F]/
|
|
460
|
+
|
|
461
|
+
function getTextDirection(text: string, fallback: 'ltr' | 'rtl' = 'ltr'): 'ltr' | 'rtl' {
|
|
462
|
+
for (const char of text) {
|
|
463
|
+
if (RTL_REGEX.test(char)) { return 'rtl' }
|
|
464
|
+
if (LTR_REGEX.test(char)) { return 'ltr' }
|
|
465
|
+
}
|
|
466
|
+
return fallback
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function getBlockDirection(element: Element, fallback: 'ltr' | 'rtl', preserveExplicit = true): 'ltr' | 'rtl' {
|
|
470
|
+
const explicitDirection = element.getAttribute('dir')
|
|
471
|
+
if (preserveExplicit && (explicitDirection === 'rtl' || explicitDirection === 'ltr')) { return explicitDirection }
|
|
472
|
+
return getTextDirection(element.textContent || '', fallback)
|
|
473
|
+
}
|
|
437
474
|
|
|
438
475
|
function detectAndSetDirection() {
|
|
439
476
|
const { doc } = editor.state
|
|
440
477
|
if (!doc?.body) { return }
|
|
441
478
|
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if (!p.classList.contains('placeholder')) {
|
|
452
|
-
p.setAttribute('dir', newDirection)
|
|
453
|
-
}
|
|
454
|
-
})
|
|
455
|
-
}
|
|
479
|
+
const fallback = getTextDirection(doc.body.textContent || '', (doc.body.dir || 'ltr') as 'ltr' | 'rtl')
|
|
480
|
+
doc.body.dir = fallback
|
|
481
|
+
doc.body.style.direction = fallback
|
|
482
|
+
|
|
483
|
+
doc.querySelectorAll('p,h1,h2,h3,h4,h5,h6,blockquote,li').forEach((block) => {
|
|
484
|
+
if (!block.classList.contains('placeholder')) {
|
|
485
|
+
block.setAttribute('dir', getBlockDirection(block, fallback, false))
|
|
486
|
+
}
|
|
487
|
+
})
|
|
456
488
|
}
|
|
457
489
|
|
|
458
490
|
function getCurrentDirection() {
|
|
459
491
|
const { doc } = editor.state
|
|
460
492
|
if (!doc?.body) { return 'ltr' }
|
|
461
|
-
const
|
|
462
|
-
|
|
493
|
+
const selection = doc.getSelection()
|
|
494
|
+
const selectedNode = selection?.rangeCount ? selection.getRangeAt(0).startContainer : null
|
|
495
|
+
const selectedElement = selectedNode?.nodeType === Node.ELEMENT_NODE
|
|
496
|
+
? selectedNode as Element
|
|
497
|
+
: selectedNode?.parentElement
|
|
498
|
+
const block = selectedElement?.closest('p,h1,h2,h3,h4,h5,h6,blockquote,li')
|
|
499
|
+
const fallback = getTextDirection(doc.body.textContent || '', (doc.body.dir || 'ltr') as 'ltr' | 'rtl')
|
|
500
|
+
return block ? getBlockDirection(block, fallback) : fallback
|
|
463
501
|
}
|
|
464
502
|
|
|
465
503
|
// Helper function to update content with history tracking
|
|
466
504
|
function updateContentWithHistory(doc: Document, skipHistory = false) {
|
|
467
505
|
if (!doc) { return }
|
|
468
506
|
|
|
507
|
+
if (doc === editor.state.doc) {
|
|
508
|
+
detectAndSetDirection()
|
|
509
|
+
}
|
|
469
510
|
const newContent = doc.body.innerHTML
|
|
470
511
|
if (newContent !== editor.state.content) {
|
|
471
512
|
if (!skipHistory) {
|
|
@@ -479,11 +520,6 @@ function updateContentWithHistory(doc: Document, skipHistory = false) {
|
|
|
479
520
|
}
|
|
480
521
|
}
|
|
481
522
|
editor.state.content = newContent
|
|
482
|
-
|
|
483
|
-
// Check direction after content update
|
|
484
|
-
if (doc === editor.state.doc) {
|
|
485
|
-
detectAndSetDirection()
|
|
486
|
-
}
|
|
487
523
|
}
|
|
488
524
|
}
|
|
489
525
|
|
|
@@ -1372,8 +1408,15 @@ function detectTableAlignment(table: HTMLTableElement): 'left' | 'center' | 'rig
|
|
|
1372
1408
|
}
|
|
1373
1409
|
|
|
1374
1410
|
function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
1411
|
+
console.log('[RichText table editor] open requested', {
|
|
1412
|
+
hasTable: Boolean(existingTable),
|
|
1413
|
+
isFullscreen: editor.state.isFullscreen,
|
|
1414
|
+
})
|
|
1375
1415
|
const { doc } = editor.state
|
|
1376
|
-
if (!doc) {
|
|
1416
|
+
if (!doc) {
|
|
1417
|
+
console.warn('[RichText table editor] aborted: editor document is unavailable')
|
|
1418
|
+
return
|
|
1419
|
+
}
|
|
1377
1420
|
|
|
1378
1421
|
// Get current selection for new tables
|
|
1379
1422
|
if (!existingTable) {
|
|
@@ -1394,6 +1437,8 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1394
1437
|
// Populate form with existing table data
|
|
1395
1438
|
const tbody = existingTable.querySelector('tbody')
|
|
1396
1439
|
const thead = existingTable.querySelector('thead')
|
|
1440
|
+
const editorWindow = existingTable.ownerDocument.defaultView
|
|
1441
|
+
const getEditorStyle = (element: Element) => editorWindow?.getComputedStyle(element) ?? null
|
|
1397
1442
|
|
|
1398
1443
|
// Detect alternating rows by comparing background colors
|
|
1399
1444
|
let hasAlternatingRows = false
|
|
@@ -1408,14 +1453,14 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1408
1453
|
const firstCell = firstRow.cells[0] as HTMLElement
|
|
1409
1454
|
const secondCell = secondRow.cells[0] as HTMLElement
|
|
1410
1455
|
|
|
1411
|
-
const firstBgColor =
|
|
1412
|
-
const secondBgColor =
|
|
1456
|
+
const firstBgColor = getEditorStyle(firstCell)?.backgroundColor || ''
|
|
1457
|
+
const secondBgColor = getEditorStyle(secondCell)?.backgroundColor || ''
|
|
1413
1458
|
|
|
1414
1459
|
// Check if colors are different (indicating alternating rows)
|
|
1415
1460
|
if (firstBgColor !== secondBgColor) {
|
|
1416
1461
|
hasAlternatingRows = true
|
|
1417
1462
|
alternateRowBg = secondBgColor
|
|
1418
|
-
alternateRowTextColor =
|
|
1463
|
+
alternateRowTextColor = getEditorStyle(secondCell)?.color || '#333333'
|
|
1419
1464
|
}
|
|
1420
1465
|
}
|
|
1421
1466
|
}
|
|
@@ -1428,14 +1473,27 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1428
1473
|
borderColor: existingTable.style.borderColor || '#dddddd',
|
|
1429
1474
|
cellPadding: 8, // default, hard to extract
|
|
1430
1475
|
showHeaders: !!thead,
|
|
1431
|
-
headerBgColor: thead ?
|
|
1432
|
-
headerTextColor: thead ?
|
|
1433
|
-
cellBgColor: tbody ?
|
|
1434
|
-
cellTextColor: tbody ?
|
|
1476
|
+
headerBgColor: thead ? getEditorStyle(thead.querySelector('th') || thead)?.backgroundColor || '#f4f4f4' : '#f4f4f4',
|
|
1477
|
+
headerTextColor: thead ? getEditorStyle(thead.querySelector('th') || thead)?.color || '#333333' : '#333333',
|
|
1478
|
+
cellBgColor: tbody ? getEditorStyle(tbody.querySelector('td') || tbody)?.backgroundColor || '#ffffff' : '#ffffff',
|
|
1479
|
+
cellTextColor: tbody ? getEditorStyle(tbody.querySelector('td') || tbody)?.color || '#333333' : '#333333',
|
|
1435
1480
|
alternateRows: hasAlternatingRows,
|
|
1436
1481
|
alternateRowBgColor: alternateRowBg,
|
|
1437
1482
|
alternateRowTextColor,
|
|
1438
1483
|
fixedLayout: existingTable.style.tableLayout === 'fixed' || true, // ברירת מחדל true
|
|
1484
|
+
columnWidths: (() => {
|
|
1485
|
+
const cols = tbody && tbody.rows.length > 0 ? tbody.rows[0].cells.length : 3
|
|
1486
|
+
const columnElements = Array.from(existingTable.querySelectorAll(':scope > colgroup > col')) as HTMLElement[]
|
|
1487
|
+
if (columnElements.length === cols) {
|
|
1488
|
+
return columnElements.map((column, index) =>
|
|
1489
|
+
Number.parseFloat(column.style.width)
|
|
1490
|
+
|| Number.parseFloat((existingTable.rows[0]?.cells[index] as HTMLElement | undefined)?.style.width || '')
|
|
1491
|
+
|| (100 / cols))
|
|
1492
|
+
}
|
|
1493
|
+
return Array.from({ length: cols }, (_, index) =>
|
|
1494
|
+
Number.parseFloat((existingTable.rows[0]?.cells[index] as HTMLElement | undefined)?.style.width || '')
|
|
1495
|
+
|| (100 / cols))
|
|
1496
|
+
})(),
|
|
1439
1497
|
alignment: detectTableAlignment(existingTable),
|
|
1440
1498
|
direction: existingTable.dir || 'ltr'
|
|
1441
1499
|
}
|
|
@@ -1457,10 +1515,24 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1457
1515
|
alternateRowBgColor: '#f9f9f9',
|
|
1458
1516
|
alternateRowTextColor: '#333333',
|
|
1459
1517
|
fixedLayout: true,
|
|
1518
|
+
columnWidths: equalColumnWidths(3),
|
|
1460
1519
|
alignment: 'left',
|
|
1461
1520
|
direction: 'ltr'
|
|
1462
1521
|
}
|
|
1463
|
-
}
|
|
1522
|
+
}
|
|
1523
|
+
showTableEditor.value = true
|
|
1524
|
+
console.log('[RichText table editor] dialog state opened', {
|
|
1525
|
+
open: showTableEditor.value,
|
|
1526
|
+
hasPendingTable: Boolean(pendingTableData?.existingTable),
|
|
1527
|
+
})
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
function openExistingTableAdvancedSettings() {
|
|
1531
|
+
const table = contextMenuCell.value?.closest('table') as HTMLTableElement | null
|
|
1532
|
+
if (!table) { return }
|
|
1533
|
+
|
|
1534
|
+
openTableEditor(table)
|
|
1535
|
+
showTableContextMenu.value = false
|
|
1464
1536
|
}
|
|
1465
1537
|
|
|
1466
1538
|
// Function to submit table changes
|
|
@@ -1487,6 +1559,17 @@ function submitTable() {
|
|
|
1487
1559
|
table.style.tableLayout = 'auto'
|
|
1488
1560
|
}
|
|
1489
1561
|
|
|
1562
|
+
let colgroup = table.querySelector(':scope > colgroup')
|
|
1563
|
+
if (!colgroup) {
|
|
1564
|
+
colgroup = doc.createElement('colgroup')
|
|
1565
|
+
table.insertBefore(colgroup, table.firstChild)
|
|
1566
|
+
}
|
|
1567
|
+
colgroup.replaceChildren(...tableForm.value.columnWidths.slice(0, tableForm.value.cols).map((width) => {
|
|
1568
|
+
const column = doc.createElement('col')
|
|
1569
|
+
column.style.width = `${width}%`
|
|
1570
|
+
return column
|
|
1571
|
+
}))
|
|
1572
|
+
|
|
1490
1573
|
// Don't force text alignment for all cells - let them inherit from table direction
|
|
1491
1574
|
// console.log('Setting cell text alignment:', tableForm.value.alignment)
|
|
1492
1575
|
// const allCells = table.querySelectorAll('td, th')
|
|
@@ -1507,8 +1590,8 @@ function submitTable() {
|
|
|
1507
1590
|
|
|
1508
1591
|
// Set fixed width for fixed layout
|
|
1509
1592
|
if (tableForm.value.fixedLayout) {
|
|
1510
|
-
const
|
|
1511
|
-
cellEl.style.width = `${100 /
|
|
1593
|
+
const cellIndex = (cellEl as HTMLTableCellElement).cellIndex
|
|
1594
|
+
cellEl.style.width = `${tableForm.value.columnWidths[cellIndex] ?? (100 / tableForm.value.cols)}%`
|
|
1512
1595
|
}
|
|
1513
1596
|
})
|
|
1514
1597
|
|
|
@@ -1587,6 +1670,14 @@ function createNewTable(doc: Document) {
|
|
|
1587
1670
|
// Set table direction
|
|
1588
1671
|
table.dir = tableForm.value.direction
|
|
1589
1672
|
|
|
1673
|
+
const colgroup = doc.createElement('colgroup')
|
|
1674
|
+
tableForm.value.columnWidths.slice(0, tableForm.value.cols).forEach((width) => {
|
|
1675
|
+
const column = doc.createElement('col')
|
|
1676
|
+
column.style.width = `${width}%`
|
|
1677
|
+
colgroup.appendChild(column)
|
|
1678
|
+
})
|
|
1679
|
+
table.appendChild(colgroup)
|
|
1680
|
+
|
|
1590
1681
|
// Add header if enabled
|
|
1591
1682
|
if (tableForm.value.showHeaders) {
|
|
1592
1683
|
const thead = doc.createElement('thead')
|
|
@@ -1603,7 +1694,7 @@ function createNewTable(doc: Document) {
|
|
|
1603
1694
|
|
|
1604
1695
|
// Set fixed width for fixed layout
|
|
1605
1696
|
if (tableForm.value.fixedLayout) {
|
|
1606
|
-
th.style.width = `${100 / tableForm.value.cols}%`
|
|
1697
|
+
th.style.width = `${tableForm.value.columnWidths[j] ?? (100 / tableForm.value.cols)}%`
|
|
1607
1698
|
}
|
|
1608
1699
|
|
|
1609
1700
|
headerRow.appendChild(th)
|
|
@@ -1625,7 +1716,7 @@ function createNewTable(doc: Document) {
|
|
|
1625
1716
|
|
|
1626
1717
|
// Set fixed width for fixed layout
|
|
1627
1718
|
if (tableForm.value.fixedLayout) {
|
|
1628
|
-
cell.style.width = `${100 / tableForm.value.cols}%`
|
|
1719
|
+
cell.style.width = `${tableForm.value.columnWidths[j] ?? (100 / tableForm.value.cols)}%`
|
|
1629
1720
|
}
|
|
1630
1721
|
|
|
1631
1722
|
// Apply background and text colors
|
|
@@ -2220,7 +2311,10 @@ function handleTableContextMenu(event: MouseEvent) {
|
|
|
2220
2311
|
|
|
2221
2312
|
// Expose debug methods if debug mode is enabled
|
|
2222
2313
|
const debugMethods = computed(() => editor.state.debug)
|
|
2223
|
-
const hasRTL = computed(() =>
|
|
2314
|
+
const hasRTL = computed(() => getTextDirection(
|
|
2315
|
+
props.modelValue.replace(/<[^>]*>/g, ''),
|
|
2316
|
+
'ltr'
|
|
2317
|
+
) === 'rtl')
|
|
2224
2318
|
|
|
2225
2319
|
// Computed property to handle height prop
|
|
2226
2320
|
const editorHeight = computed(() => {
|
|
@@ -2243,16 +2337,49 @@ onUnmounted(() => {
|
|
|
2243
2337
|
})
|
|
2244
2338
|
|
|
2245
2339
|
function setupTableEditButtons(doc: Document) {
|
|
2340
|
+
const previousEditHandler = (doc as any).__tableEditPointerHandler as ((event: PointerEvent) => void) | undefined
|
|
2341
|
+
if (previousEditHandler) {
|
|
2342
|
+
doc.removeEventListener('pointerdown', previousEditHandler, true)
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
const tableEditPointerHandler = (event: PointerEvent) => {
|
|
2346
|
+
const target = event.target as Element | null
|
|
2347
|
+
const editButton = target?.closest('.table-edit-btn')
|
|
2348
|
+
if (!editButton) { return }
|
|
2349
|
+
|
|
2350
|
+
const table = editButton.closest('.table-wrapper')?.querySelector('table') as HTMLTableElement | null
|
|
2351
|
+
console.log('[RichText table editor] delegated pointerdown received', {
|
|
2352
|
+
pointerType: event.pointerType,
|
|
2353
|
+
hasTable: Boolean(table),
|
|
2354
|
+
})
|
|
2355
|
+
if (!table) { return }
|
|
2356
|
+
|
|
2357
|
+
event.preventDefault()
|
|
2358
|
+
event.stopPropagation()
|
|
2359
|
+
event.stopImmediatePropagation()
|
|
2360
|
+
globalThis.setTimeout(() => {
|
|
2361
|
+
try {
|
|
2362
|
+
openTableEditor(table)
|
|
2363
|
+
} catch (error) {
|
|
2364
|
+
console.error('[RichText table editor] delegated open failed', error)
|
|
2365
|
+
}
|
|
2366
|
+
}, 0)
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
;(doc as any).__tableEditPointerHandler = tableEditPointerHandler
|
|
2370
|
+
doc.addEventListener('pointerdown', tableEditPointerHandler, true)
|
|
2371
|
+
|
|
2246
2372
|
// Simple function to add edit buttons to all tables
|
|
2247
2373
|
function addEditButtonsToTables() {
|
|
2248
2374
|
const tables = doc.querySelectorAll<HTMLTableElement>('table:not([data-edit-button-added])')
|
|
2249
2375
|
|
|
2250
|
-
tables.forEach((table
|
|
2376
|
+
tables.forEach((table) => {
|
|
2251
2377
|
// Create edit button as a span element instead of button
|
|
2252
2378
|
const editBtn = doc.createElement('span')
|
|
2253
2379
|
editBtn.className = 'table-edit-btn'
|
|
2254
|
-
editBtn.textContent = '✎
|
|
2380
|
+
editBtn.textContent = '✎'
|
|
2255
2381
|
editBtn.title = 'Edit Table'
|
|
2382
|
+
editBtn.setAttribute('aria-label', 'Edit Table')
|
|
2256
2383
|
|
|
2257
2384
|
// Prevent any content insertion into button
|
|
2258
2385
|
editBtn.setAttribute('contenteditable', 'false')
|
|
@@ -2261,10 +2388,10 @@ function setupTableEditButtons(doc: Document) {
|
|
|
2261
2388
|
editBtn.style.userSelect = 'none'
|
|
2262
2389
|
|
|
2263
2390
|
editBtn.addEventListener('click', (e) => {
|
|
2391
|
+
console.log('[RichText table editor] edit icon click received')
|
|
2264
2392
|
e.preventDefault()
|
|
2265
2393
|
e.stopPropagation()
|
|
2266
2394
|
e.stopImmediatePropagation()
|
|
2267
|
-
openTableEditor(table)
|
|
2268
2395
|
})
|
|
2269
2396
|
|
|
2270
2397
|
// Prevent focus and content insertion
|
|
@@ -2274,9 +2401,14 @@ function setupTableEditButtons(doc: Document) {
|
|
|
2274
2401
|
editBtn.blur()
|
|
2275
2402
|
})
|
|
2276
2403
|
|
|
2277
|
-
editBtn.addEventListener('
|
|
2404
|
+
editBtn.addEventListener('pointerdown', (e) => {
|
|
2405
|
+
console.log('[RichText table editor] edit icon pointerdown received', {
|
|
2406
|
+
pointerType: e.pointerType,
|
|
2407
|
+
tableConnected: table.isConnected,
|
|
2408
|
+
})
|
|
2278
2409
|
e.preventDefault()
|
|
2279
2410
|
e.stopPropagation()
|
|
2411
|
+
e.stopImmediatePropagation()
|
|
2280
2412
|
})
|
|
2281
2413
|
|
|
2282
2414
|
// Create a wrapper div to contain both table and button
|
|
@@ -2549,7 +2681,10 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2549
2681
|
}
|
|
2550
2682
|
|
|
2551
2683
|
// For RTL text, be more careful about splitting
|
|
2552
|
-
const
|
|
2684
|
+
const paragraphDirection = getTextDirection(
|
|
2685
|
+
paragraph.textContent || '',
|
|
2686
|
+
(doc.body.dir || 'ltr') as 'ltr' | 'rtl'
|
|
2687
|
+
)
|
|
2553
2688
|
|
|
2554
2689
|
// Prevent default and handle manually to avoid browser inconsistencies
|
|
2555
2690
|
e.preventDefault()
|
|
@@ -2565,7 +2700,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2565
2700
|
|
|
2566
2701
|
// Create new paragraph with proper direction
|
|
2567
2702
|
const newParagraph = doc.createElement('p')
|
|
2568
|
-
newParagraph.dir =
|
|
2703
|
+
newParagraph.dir = paragraphDirection
|
|
2569
2704
|
|
|
2570
2705
|
// If we're splitting text, handle it carefully
|
|
2571
2706
|
if (range.startContainer.nodeType === Node.TEXT_NODE && textAfterCursor.trim()) {
|
|
@@ -2857,10 +2992,15 @@ async function initEditor() {
|
|
|
2857
2992
|
background: transparent;
|
|
2858
2993
|
max-width: 860px;
|
|
2859
2994
|
margin: 0 auto;
|
|
2995
|
+
unicode-bidi: plaintext;
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
p, h1, h2, h3, h4, h5, h6, blockquote, li {
|
|
2999
|
+
unicode-bidi: plaintext;
|
|
2860
3000
|
}
|
|
2861
3001
|
|
|
2862
3002
|
/* Ensure all text elements inherit the theme color */
|
|
2863
|
-
p, h1, h2, h3, h4, h5, h6, div, span, li
|
|
3003
|
+
p, h1, h2, h3, h4, h5, h6, div, span, li {
|
|
2864
3004
|
color: inherit !important;
|
|
2865
3005
|
}
|
|
2866
3006
|
table {
|
|
@@ -2885,37 +3025,52 @@ async function initEditor() {
|
|
|
2885
3025
|
position: absolute;
|
|
2886
3026
|
top: -8px;
|
|
2887
3027
|
right: -8px;
|
|
2888
|
-
background: #
|
|
2889
|
-
color:
|
|
2890
|
-
border:
|
|
3028
|
+
background: #000;
|
|
3029
|
+
color: #fff !important;
|
|
3030
|
+
border: 1px solid #fff;
|
|
2891
3031
|
border-radius: 4px;
|
|
2892
3032
|
padding: 4px 8px;
|
|
2893
3033
|
font-size: 12px;
|
|
2894
3034
|
cursor: pointer;
|
|
2895
|
-
opacity:
|
|
3035
|
+
opacity: 0;
|
|
3036
|
+
visibility: hidden;
|
|
2896
3037
|
z-index: 99;
|
|
2897
3038
|
font-family: sans-serif;
|
|
2898
3039
|
pointer-events: auto;
|
|
2899
3040
|
user-select: none;
|
|
2900
3041
|
display: block;
|
|
2901
3042
|
line-height: 1;
|
|
3043
|
+
width: 28px;
|
|
3044
|
+
height: 28px;
|
|
3045
|
+
padding: 0;
|
|
3046
|
+
text-align: center;
|
|
3047
|
+
align-content: center;
|
|
2902
3048
|
/* Prevent content insertion */
|
|
2903
3049
|
contenteditable: false !important;
|
|
2904
3050
|
outline: none !important;
|
|
3051
|
+
transition: opacity 0.15s ease, visibility 0.15s ease, background 0.15s ease;
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
.table-wrapper:hover > .table-edit-btn,
|
|
3055
|
+
.table-wrapper:focus-within > .table-edit-btn,
|
|
3056
|
+
.table-edit-btn:hover,
|
|
3057
|
+
.table-edit-btn:focus {
|
|
3058
|
+
opacity: 1;
|
|
3059
|
+
visibility: visible;
|
|
2905
3060
|
}
|
|
2906
3061
|
|
|
2907
3062
|
.table-edit-btn:hover {
|
|
2908
|
-
background: #
|
|
3063
|
+
background: #1f1f1f;
|
|
2909
3064
|
}
|
|
2910
3065
|
|
|
2911
3066
|
/* Prevent any focus or content insertion */
|
|
2912
3067
|
.table-edit-btn:focus {
|
|
2913
3068
|
outline: none !important;
|
|
2914
|
-
background: #
|
|
3069
|
+
background: #000;
|
|
2915
3070
|
}
|
|
2916
3071
|
|
|
2917
3072
|
.table-edit-btn:active {
|
|
2918
|
-
background: #
|
|
3073
|
+
background: #1f1f1f;
|
|
2919
3074
|
}
|
|
2920
3075
|
.richtext-editor-content blockquote {
|
|
2921
3076
|
padding: 8px !important;
|
|
@@ -3086,6 +3241,16 @@ async function initEditor() {
|
|
|
3086
3241
|
|
|
3087
3242
|
// Set default direction based on content
|
|
3088
3243
|
doc.body.dir = hasRTL.value ? 'rtl' : 'ltr'
|
|
3244
|
+
doc.body.style.direction = doc.body.dir
|
|
3245
|
+
|
|
3246
|
+
// Resolve bidirectional text independently for each block. Using the
|
|
3247
|
+
// first strong character keeps English fragments stable inside Hebrew
|
|
3248
|
+
// text (and vice versa) without forcing one direction on the whole document.
|
|
3249
|
+
doc.body.querySelectorAll('p,h1,h2,h3,h4,h5,h6,blockquote,li').forEach((block) => {
|
|
3250
|
+
if (!block.classList.contains('placeholder')) {
|
|
3251
|
+
block.setAttribute('dir', getBlockDirection(block, doc.body.dir as 'ltr' | 'rtl'))
|
|
3252
|
+
}
|
|
3253
|
+
})
|
|
3089
3254
|
|
|
3090
3255
|
// Apply direction to existing blockquotes and lists
|
|
3091
3256
|
const blockElements = doc.body.querySelectorAll('blockquote, ul, ol')
|
|
@@ -3303,7 +3468,7 @@ function updateIframeColors() {
|
|
|
3303
3468
|
}
|
|
3304
3469
|
|
|
3305
3470
|
/* Ensure all text elements inherit the color and font size */
|
|
3306
|
-
p, h1, h2, h3, h4, h5, h6, div, span, li
|
|
3471
|
+
p, h1, h2, h3, h4, h5, h6, div, span, li {
|
|
3307
3472
|
color: inherit !important;
|
|
3308
3473
|
font-size: inherit !important;
|
|
3309
3474
|
}
|
|
@@ -3348,9 +3513,6 @@ watch(() => props.modelValue, (newValue, oldValue) => {
|
|
|
3348
3513
|
|
|
3349
3514
|
// Always emit clean, vanilla HTML — never the editor's internal DOM format
|
|
3350
3515
|
watch(() => editor.state.content, () => {
|
|
3351
|
-
if (editor.state.doc?.body) {
|
|
3352
|
-
editor.state.doc.body.dir = hasRTL.value ? 'rtl' : 'ltr'
|
|
3353
|
-
}
|
|
3354
3516
|
emit('update:modelValue', getCleanHTML(editor.state.doc ?? null))
|
|
3355
3517
|
})
|
|
3356
3518
|
|
|
@@ -3536,6 +3698,12 @@ const imgTransform = computed({
|
|
|
3536
3698
|
</button>
|
|
3537
3699
|
</div>
|
|
3538
3700
|
<div class="px-025">
|
|
3701
|
+
<Btn
|
|
3702
|
+
full-width align-txt="start" thin icon="settings"
|
|
3703
|
+
class="context-menu-btn context-menu-btn--advanced" value="Advanced Settings"
|
|
3704
|
+
@mousedown.stop @click.stop="openExistingTableAdvancedSettings"
|
|
3705
|
+
/>
|
|
3706
|
+
<div class="context-menu-separator" />
|
|
3539
3707
|
<Btn
|
|
3540
3708
|
v-if="canMergeRight" full-width align-txt="start" thin flat icon="start"
|
|
3541
3709
|
class="context-menu-btn" @click="mergeCellRight(); showTableContextMenu = false"
|
|
@@ -3790,11 +3958,12 @@ const imgTransform = computed({
|
|
|
3790
3958
|
</template>
|
|
3791
3959
|
</Dialog>
|
|
3792
3960
|
|
|
3793
|
-
<!--
|
|
3794
|
-
<
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3961
|
+
<!-- Keep the table editor in the document top layer, including while the rich-text editor is fullscreen. -->
|
|
3962
|
+
<Teleport to="body">
|
|
3963
|
+
<Dialog
|
|
3964
|
+
:open="showTableEditor" :title="pendingTableData?.existingTable ? 'Edit Table' : 'Insert Table'"
|
|
3965
|
+
width="l" @update:open="showTableEditor = $event"
|
|
3966
|
+
>
|
|
3798
3967
|
<template #default>
|
|
3799
3968
|
<div class="grid grid-wrap-4 m_grid-wrap-2 gap-col-1 table-editor testMe1">
|
|
3800
3969
|
<!-- Structure Section -->
|
|
@@ -3807,6 +3976,22 @@ const imgTransform = computed({
|
|
|
3807
3976
|
</div>
|
|
3808
3977
|
<NumberInput v-model="tableForm.rows" :min="1" :max="20" label="Rows" />
|
|
3809
3978
|
<NumberInput v-model="tableForm.cols" :min="1" :max="10" label="Columns" />
|
|
3979
|
+
<div class="grid-span-4 m_grid-span-2">
|
|
3980
|
+
<label class="label">Column Widths (%)</label>
|
|
3981
|
+
<div class="grid grid-wrap-4 m_grid-wrap-2 gap-col-1 mt-025">
|
|
3982
|
+
<NumberInput
|
|
3983
|
+
v-for="(_, index) in tableForm.columnWidths.slice(0, tableForm.cols)"
|
|
3984
|
+
:key="index"
|
|
3985
|
+
v-model="tableForm.columnWidths[index]"
|
|
3986
|
+
:min="1"
|
|
3987
|
+
:max="100"
|
|
3988
|
+
:label="`Column ${index + 1}`"
|
|
3989
|
+
/>
|
|
3990
|
+
</div>
|
|
3991
|
+
<p class="txt12 mt-025 mb-0" :class="{ 'color-red': Math.abs(columnWidthsTotal - 100) > 0.01 }">
|
|
3992
|
+
Total: {{ Number(columnWidthsTotal.toFixed(2)) }}%
|
|
3993
|
+
</p>
|
|
3994
|
+
</div>
|
|
3810
3995
|
<!-- Cell Text Alignment -->
|
|
3811
3996
|
<div class="grid-span-1">
|
|
3812
3997
|
<label class="label">Cell Text Alignment</label>
|
|
@@ -3939,7 +4124,8 @@ const imgTransform = computed({
|
|
|
3939
4124
|
@click="submitTable"
|
|
3940
4125
|
/>
|
|
3941
4126
|
</template>
|
|
3942
|
-
|
|
4127
|
+
</Dialog>
|
|
4128
|
+
</Teleport>
|
|
3943
4129
|
</template>
|
|
3944
4130
|
|
|
3945
4131
|
<style>
|
|
@@ -4250,6 +4436,19 @@ margin: 4px 0;
|
|
|
4250
4436
|
background-color: var(--hover-bg, #f5f5f5) !important;
|
|
4251
4437
|
}
|
|
4252
4438
|
|
|
4439
|
+
.context-menu-btn--advanced,
|
|
4440
|
+
.context-menu-btn--advanced:hover,
|
|
4441
|
+
.context-menu-btn--advanced:focus-visible {
|
|
4442
|
+
background: #000 !important;
|
|
4443
|
+
border: 1px solid #fff !important;
|
|
4444
|
+
color: #fff !important;
|
|
4445
|
+
}
|
|
4446
|
+
|
|
4447
|
+
.context-menu-btn--advanced:hover,
|
|
4448
|
+
.context-menu-btn--advanced:focus-visible {
|
|
4449
|
+
background: #1f1f1f !important;
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4253
4452
|
/* Table Cell Selection */
|
|
4254
4453
|
.table-cell-selected {
|
|
4255
4454
|
background-color: rgba(0, 123, 255, 0.1) !important;
|
|
@@ -4283,10 +4482,14 @@ top: 5px !important;
|
|
|
4283
4482
|
right: 5px !important;
|
|
4284
4483
|
background: rgba(0, 0, 0, 0.7) !important;
|
|
4285
4484
|
color: white !important;
|
|
4286
|
-
|
|
4485
|
+
-webkit-text-fill-color: white !important;
|
|
4486
|
+
padding: 0 !important;
|
|
4487
|
+
border: 1px solid white !important;
|
|
4287
4488
|
border-radius: 4px !important;
|
|
4288
4489
|
font-size: 12px !important;
|
|
4289
4490
|
cursor: pointer !important;
|
|
4491
|
+
opacity: 0 !important;
|
|
4492
|
+
visibility: hidden !important;
|
|
4290
4493
|
z-index: 100 !important;
|
|
4291
4494
|
pointer-events: auto !important;
|
|
4292
4495
|
user-select: none !important;
|
|
@@ -4294,16 +4497,15 @@ user-select: none !important;
|
|
|
4294
4497
|
-moz-user-select: none !important;
|
|
4295
4498
|
-ms-user-select: none !important;
|
|
4296
4499
|
outline: none !important;
|
|
4297
|
-
border: none !important;
|
|
4298
4500
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) !important;
|
|
4299
4501
|
white-space: nowrap !important;
|
|
4300
4502
|
overflow: hidden !important;
|
|
4301
4503
|
text-overflow: clip !important;
|
|
4302
4504
|
min-width: unset !important;
|
|
4303
4505
|
max-width: unset !important;
|
|
4304
|
-
width:
|
|
4305
|
-
height:
|
|
4306
|
-
line-height:
|
|
4506
|
+
width: 28px !important;
|
|
4507
|
+
height: 28px !important;
|
|
4508
|
+
line-height: 26px !important;
|
|
4307
4509
|
font-family: inherit !important;
|
|
4308
4510
|
font-weight: normal !important;
|
|
4309
4511
|
text-align: center !important;
|
|
@@ -4316,6 +4518,15 @@ text-indent: 0 !important;
|
|
|
4316
4518
|
text-shadow: none !important;
|
|
4317
4519
|
direction: ltr !important;
|
|
4318
4520
|
unicode-bidi: normal !important;
|
|
4521
|
+
transition: opacity 0.15s ease, visibility 0.15s ease, background 0.15s ease !important;
|
|
4522
|
+
}
|
|
4523
|
+
|
|
4524
|
+
.table-wrapper:hover > .table-edit-btn,
|
|
4525
|
+
.table-wrapper:focus-within > .table-edit-btn,
|
|
4526
|
+
.table-edit-btn:hover,
|
|
4527
|
+
.table-edit-btn:focus {
|
|
4528
|
+
opacity: 1 !important;
|
|
4529
|
+
visibility: visible !important;
|
|
4319
4530
|
}
|
|
4320
4531
|
|
|
4321
4532
|
.table-edit-btn:hover {
|