@jvs-milkdown/components 1.1.8 → 1.2.0
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jvs-milkdown/components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"milkdown",
|
|
6
6
|
"milkdown plugin"
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@floating-ui/dom": "^1.5.1",
|
|
53
|
-
"@jvs-milkdown/core": "^1.
|
|
54
|
-
"@jvs-milkdown/ctx": "^1.
|
|
55
|
-
"@jvs-milkdown/exception": "^1.
|
|
56
|
-
"@jvs-milkdown/plugin-tooltip": "^1.
|
|
57
|
-
"@jvs-milkdown/preset-commonmark": "^1.
|
|
58
|
-
"@jvs-milkdown/preset-gfm": "^1.
|
|
59
|
-
"@jvs-milkdown/prose": "^1.
|
|
60
|
-
"@jvs-milkdown/transformer": "^1.
|
|
61
|
-
"@jvs-milkdown/utils": "^1.
|
|
53
|
+
"@jvs-milkdown/core": "^1.2.0",
|
|
54
|
+
"@jvs-milkdown/ctx": "^1.2.0",
|
|
55
|
+
"@jvs-milkdown/exception": "^1.2.0",
|
|
56
|
+
"@jvs-milkdown/plugin-tooltip": "^1.2.0",
|
|
57
|
+
"@jvs-milkdown/preset-commonmark": "^1.2.0",
|
|
58
|
+
"@jvs-milkdown/preset-gfm": "^1.2.0",
|
|
59
|
+
"@jvs-milkdown/prose": "^1.2.0",
|
|
60
|
+
"@jvs-milkdown/transformer": "^1.2.0",
|
|
61
|
+
"@jvs-milkdown/utils": "^1.2.0",
|
|
62
62
|
"@types/lodash-es": "^4.17.12",
|
|
63
63
|
"clsx": "^2.0.0",
|
|
64
64
|
"dompurify": "^3.2.5",
|
|
@@ -94,22 +94,66 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
94
94
|
const resizingCol = ref(-1)
|
|
95
95
|
const colWidths = ref<number[]>([])
|
|
96
96
|
|
|
97
|
+
/** Count total columns in the table by expanding colspan across all rows */
|
|
98
|
+
const getColCount = () => {
|
|
99
|
+
let maxCols = 0
|
|
100
|
+
node.value.forEach((row) => {
|
|
101
|
+
let cols = 0
|
|
102
|
+
row.forEach((cell) => {
|
|
103
|
+
cols += (cell.attrs.colspan as number) || 1
|
|
104
|
+
})
|
|
105
|
+
if (cols > maxCols) maxCols = cols
|
|
106
|
+
})
|
|
107
|
+
return maxCols
|
|
108
|
+
}
|
|
109
|
+
|
|
97
110
|
const syncColWidths = () => {
|
|
98
111
|
if (resizingCol.value >= 0) return
|
|
99
|
-
const
|
|
100
|
-
if (!firstRow) return
|
|
101
|
-
const colCount = firstRow.content.childCount
|
|
112
|
+
const colCount = getColCount()
|
|
102
113
|
const widths: number[] = []
|
|
103
114
|
const hasBounds = colBounds.value.length === colCount
|
|
115
|
+
|
|
104
116
|
for (let i = 0; i < colCount; i++) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
// Find a cell that covers this column index
|
|
118
|
+
let found = false
|
|
119
|
+
node.value.forEach((row) => {
|
|
120
|
+
if (found) return
|
|
121
|
+
let colIdx = 0
|
|
122
|
+
row.forEach((cell) => {
|
|
123
|
+
if (found) return
|
|
124
|
+
const colspan = (cell.attrs.colspan as number) || 1
|
|
125
|
+
const colwidth: unknown = cell.attrs.colwidth
|
|
126
|
+
if (colIdx === i) {
|
|
127
|
+
if (colspan > 1) {
|
|
128
|
+
// Merged cell: use colwidth array or fall back to DOM width
|
|
129
|
+
if (Array.isArray(colwidth) && colwidth.length > 0) {
|
|
130
|
+
// colwidth for merged cells is an array of per-column widths
|
|
131
|
+
const totalW = colwidth.reduce(
|
|
132
|
+
(sum: number, w: unknown) => sum + ((w as number) || 0),
|
|
133
|
+
0
|
|
134
|
+
)
|
|
135
|
+
widths.push(Math.round(totalW / colspan))
|
|
136
|
+
} else if (hasBounds) {
|
|
137
|
+
widths.push(Math.round(colBounds.value[i]!.width))
|
|
138
|
+
} else {
|
|
139
|
+
widths.push(0)
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
if (Array.isArray(colwidth) && colwidth[0]) {
|
|
143
|
+
widths.push(colwidth[0] as number)
|
|
144
|
+
} else if (hasBounds) {
|
|
145
|
+
widths.push(Math.round(colBounds.value[i]!.width))
|
|
146
|
+
} else {
|
|
147
|
+
widths.push(0)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
found = true
|
|
151
|
+
}
|
|
152
|
+
colIdx += colspan
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
if (!found) {
|
|
156
|
+
widths.push(hasBounds ? Math.round(colBounds.value[i]!.width) : 0)
|
|
113
157
|
}
|
|
114
158
|
}
|
|
115
159
|
colWidths.value = widths
|
|
@@ -238,17 +282,28 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
238
282
|
const content = contentWrapperRef.value
|
|
239
283
|
if (!content) return
|
|
240
284
|
|
|
241
|
-
const
|
|
285
|
+
const rows = content.querySelectorAll('tr')
|
|
242
286
|
const tableRect = content.getBoundingClientRect()
|
|
287
|
+
const expectedColCount = getColCount()
|
|
288
|
+
|
|
289
|
+
// Find the first row where all cells are unmerged (no colspan),
|
|
290
|
+
// so we can accurately measure individual column positions.
|
|
291
|
+
let bestRow: HTMLTableRowElement | null = null
|
|
292
|
+
rows.forEach((row) => {
|
|
293
|
+
if (bestRow) return
|
|
294
|
+
if (row.children.length === expectedColCount) {
|
|
295
|
+
bestRow = row as HTMLTableRowElement
|
|
296
|
+
}
|
|
297
|
+
})
|
|
243
298
|
|
|
244
|
-
|
|
245
|
-
|
|
299
|
+
const measureRow = bestRow ?? content.querySelector('tr')
|
|
300
|
+
if (measureRow) {
|
|
301
|
+
colBounds.value = Array.from(measureRow.children).map((c) => {
|
|
246
302
|
const rect = c.getBoundingClientRect()
|
|
247
303
|
return { left: rect.left - tableRect.left, width: rect.width }
|
|
248
304
|
})
|
|
249
305
|
}
|
|
250
306
|
|
|
251
|
-
const rows = content.querySelectorAll('tr')
|
|
252
307
|
rowBounds.value = Array.from(rows).map((c) => {
|
|
253
308
|
const rect = c.getBoundingClientRect()
|
|
254
309
|
return { top: rect.top - tableRect.top, height: rect.height }
|
|
@@ -404,6 +459,7 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
404
459
|
|
|
405
460
|
watch(node, () => {
|
|
406
461
|
dispatchListener()
|
|
462
|
+
requestAnimationFrame(updateBounds)
|
|
407
463
|
})
|
|
408
464
|
|
|
409
465
|
onBeforeUnmount(() => {
|
|
@@ -447,7 +503,7 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
447
503
|
contenteditable="false"
|
|
448
504
|
onPointerdown={(e: PointerEvent) => e.stopPropagation()}
|
|
449
505
|
>
|
|
450
|
-
{
|
|
506
|
+
{canMerge.value ? (
|
|
451
507
|
<button
|
|
452
508
|
type="button"
|
|
453
509
|
class="cell-toolbar-btn"
|
|
@@ -455,8 +511,7 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
455
511
|
>
|
|
456
512
|
<Icon icon={config.renderButton('merge_cells')} />
|
|
457
513
|
</button>
|
|
458
|
-
|
|
459
|
-
{
|
|
514
|
+
) : canSplit.value ? (
|
|
460
515
|
<button
|
|
461
516
|
type="button"
|
|
462
517
|
class="cell-toolbar-btn"
|
|
@@ -464,7 +519,7 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
464
519
|
>
|
|
465
520
|
<Icon icon={config.renderButton('split_cell')} />
|
|
466
521
|
</button>
|
|
467
|
-
}
|
|
522
|
+
) : undefined}
|
|
468
523
|
</div>
|
|
469
524
|
|
|
470
525
|
<div class="table-wrapper" ref={tableWrapperRef}>
|
|
@@ -496,6 +551,15 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
496
551
|
data-show={activeColIndex.value === i ? 'true' : 'false'}
|
|
497
552
|
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
498
553
|
>
|
|
554
|
+
{canMerge.value ? (
|
|
555
|
+
<button type="button" onPointerdown={onMergeCells}>
|
|
556
|
+
<Icon icon={config.renderButton('merge_cells')} />
|
|
557
|
+
</button>
|
|
558
|
+
) : canSplit.value ? (
|
|
559
|
+
<button type="button" onPointerdown={onSplitCell}>
|
|
560
|
+
<Icon icon={config.renderButton('split_cell')} />
|
|
561
|
+
</button>
|
|
562
|
+
) : undefined}
|
|
499
563
|
<button
|
|
500
564
|
type="button"
|
|
501
565
|
onPointerdown={(e) => {
|
|
@@ -532,16 +596,6 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
532
596
|
>
|
|
533
597
|
<Icon icon={config.renderButton('delete_col')} />
|
|
534
598
|
</button>
|
|
535
|
-
{canMerge.value && (
|
|
536
|
-
<button type="button" onPointerdown={onMergeCells}>
|
|
537
|
-
<Icon icon={config.renderButton('merge_cells')} />
|
|
538
|
-
</button>
|
|
539
|
-
)}
|
|
540
|
-
{canSplit.value && (
|
|
541
|
-
<button type="button" onPointerdown={onSplitCell}>
|
|
542
|
-
<Icon icon={config.renderButton('split_cell')} />
|
|
543
|
-
</button>
|
|
544
|
-
)}
|
|
545
599
|
</div>
|
|
546
600
|
</div>
|
|
547
601
|
))}
|
|
@@ -612,6 +666,15 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
612
666
|
data-show={activeRowIndex.value === i ? 'true' : 'false'}
|
|
613
667
|
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
614
668
|
>
|
|
669
|
+
{canMerge.value ? (
|
|
670
|
+
<button type="button" onPointerdown={onMergeCells}>
|
|
671
|
+
<Icon icon={config.renderButton('merge_cells')} />
|
|
672
|
+
</button>
|
|
673
|
+
) : canSplit.value ? (
|
|
674
|
+
<button type="button" onPointerdown={onSplitCell}>
|
|
675
|
+
<Icon icon={config.renderButton('split_cell')} />
|
|
676
|
+
</button>
|
|
677
|
+
) : undefined}
|
|
615
678
|
<button
|
|
616
679
|
type="button"
|
|
617
680
|
onPointerdown={(e) => {
|
|
@@ -621,16 +684,6 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
621
684
|
>
|
|
622
685
|
<Icon icon={config.renderButton('delete_row')} />
|
|
623
686
|
</button>
|
|
624
|
-
{canMerge.value && (
|
|
625
|
-
<button type="button" onPointerdown={onMergeCells}>
|
|
626
|
-
<Icon icon={config.renderButton('merge_cells')} />
|
|
627
|
-
</button>
|
|
628
|
-
)}
|
|
629
|
-
{canSplit.value && (
|
|
630
|
-
<button type="button" onPointerdown={onSplitCell}>
|
|
631
|
-
<Icon icon={config.renderButton('split_cell')} />
|
|
632
|
-
</button>
|
|
633
|
-
)}
|
|
634
687
|
</div>
|
|
635
688
|
</div>
|
|
636
689
|
))}
|
|
@@ -86,6 +86,8 @@ export function useOperation(
|
|
|
86
86
|
|
|
87
87
|
if (!ctx.get(editorViewCtx).editable) return
|
|
88
88
|
|
|
89
|
+
if (e.button !== 0) return
|
|
90
|
+
|
|
89
91
|
e.preventDefault()
|
|
90
92
|
e.stopPropagation()
|
|
91
93
|
const commands = ctx.get(commandsCtx)
|
|
@@ -101,6 +103,8 @@ export function useOperation(
|
|
|
101
103
|
|
|
102
104
|
if (!ctx.get(editorViewCtx).editable) return
|
|
103
105
|
|
|
106
|
+
if (e.button !== 0) return
|
|
107
|
+
|
|
104
108
|
e.preventDefault()
|
|
105
109
|
e.stopPropagation()
|
|
106
110
|
const commands = ctx.get(commandsCtx)
|
|
@@ -113,6 +117,7 @@ export function useOperation(
|
|
|
113
117
|
const onMergeCells = (e: PointerEvent) => {
|
|
114
118
|
if (!ctx) return
|
|
115
119
|
if (!ctx.get(editorViewCtx).editable) return
|
|
120
|
+
if (e.button !== 0) return
|
|
116
121
|
e.preventDefault()
|
|
117
122
|
e.stopPropagation()
|
|
118
123
|
const commands = ctx.get(commandsCtx)
|
|
@@ -125,6 +130,7 @@ export function useOperation(
|
|
|
125
130
|
const onSplitCell = (e: PointerEvent) => {
|
|
126
131
|
if (!ctx) return
|
|
127
132
|
if (!ctx.get(editorViewCtx).editable) return
|
|
133
|
+
if (e.button !== 0) return
|
|
128
134
|
e.preventDefault()
|
|
129
135
|
e.stopPropagation()
|
|
130
136
|
const view = ctx.get(editorViewCtx)
|