@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.1.8",
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.1.8",
54
- "@jvs-milkdown/ctx": "^1.1.8",
55
- "@jvs-milkdown/exception": "^1.1.8",
56
- "@jvs-milkdown/plugin-tooltip": "^1.1.8",
57
- "@jvs-milkdown/preset-commonmark": "^1.1.8",
58
- "@jvs-milkdown/preset-gfm": "^1.1.8",
59
- "@jvs-milkdown/prose": "^1.1.8",
60
- "@jvs-milkdown/transformer": "^1.1.8",
61
- "@jvs-milkdown/utils": "^1.1.8",
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 firstRow = node.value.firstChild
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
- const cell = firstRow.content.child(i)
106
- const w = cell.attrs.colwidth
107
- if (Array.isArray(w) && w[0]) {
108
- widths.push(w[0])
109
- } else if (hasBounds) {
110
- widths.push(Math.round(colBounds.value[i]!.width))
111
- } else {
112
- widths.push(0)
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 firstRow = content.querySelector('tr')
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
- if (firstRow) {
245
- colBounds.value = Array.from(firstRow.children).map((c) => {
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)