@jvs-milkdown/components 1.0.0 → 1.1.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/lib/image-block/index.js +47 -8
- package/lib/image-block/index.js.map +1 -1
- package/lib/image-block/view/components/image-viewer.d.ts.map +1 -1
- package/lib/table-block/config.d.ts +1 -1
- package/lib/table-block/config.d.ts.map +1 -1
- package/lib/table-block/index.js +622 -421
- package/lib/table-block/index.js.map +1 -1
- package/lib/table-block/view/component.d.ts.map +1 -1
- package/lib/table-block/view/operation.d.ts +2 -0
- package/lib/table-block/view/operation.d.ts.map +1 -1
- package/lib/table-block/view/view.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +80 -54
- package/src/image-block/view/components/image-viewer.tsx +49 -8
- package/src/table-block/config.ts +6 -0
- package/src/table-block/dnd/create-drag-handler.ts +1 -13
- package/src/table-block/dnd/prepare-dnd-context.ts +0 -8
- package/src/table-block/view/component.tsx +484 -83
- package/src/table-block/view/drag.ts +2 -16
- package/src/table-block/view/operation.ts +108 -49
- package/src/table-block/view/pointer.ts +3 -155
- package/src/table-block/view/types.ts +0 -6
- package/src/table-block/view/utils.ts +2 -81
- package/src/table-block/view/view.ts +19 -58
- package/LICENSE +0 -21
|
@@ -2,12 +2,15 @@ import type { Ctx } from '@jvs-milkdown/ctx'
|
|
|
2
2
|
import type { Node } from '@jvs-milkdown/prose/model'
|
|
3
3
|
import type { EditorView } from '@jvs-milkdown/prose/view'
|
|
4
4
|
|
|
5
|
+
import { computePosition } from '@floating-ui/dom'
|
|
6
|
+
import { CellSelection } from '@jvs-milkdown/prose/tables'
|
|
5
7
|
import {
|
|
6
8
|
defineComponent,
|
|
7
9
|
ref,
|
|
8
10
|
type VNodeRef,
|
|
9
11
|
h,
|
|
10
12
|
onMounted,
|
|
13
|
+
onBeforeUnmount,
|
|
11
14
|
type Ref,
|
|
12
15
|
} from 'vue'
|
|
13
16
|
|
|
@@ -61,33 +64,54 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
61
64
|
},
|
|
62
65
|
setup({ view, node, ctx, getPos, config, onMount }) {
|
|
63
66
|
const contentWrapperRef = ref<HTMLElement>()
|
|
67
|
+
let contentMounted = false
|
|
64
68
|
const contentWrapperFunctionRef: VNodeRef = (div) => {
|
|
65
69
|
if (div == null) return
|
|
66
70
|
if (div instanceof HTMLElement) {
|
|
67
71
|
contentWrapperRef.value = div
|
|
68
|
-
|
|
72
|
+
if (!contentMounted) {
|
|
73
|
+
onMount(div)
|
|
74
|
+
contentMounted = true
|
|
75
|
+
}
|
|
69
76
|
} else {
|
|
70
77
|
contentWrapperRef.value = undefined
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
|
-
const colHandleRef = ref<HTMLDivElement>()
|
|
74
|
-
const rowHandleRef = ref<HTMLDivElement>()
|
|
75
|
-
const xLineHandleRef = ref<HTMLDivElement>()
|
|
76
|
-
const yLineHandleRef = ref<HTMLDivElement>()
|
|
77
80
|
const tableWrapperRef = ref<HTMLDivElement>()
|
|
78
81
|
const dragPreviewRef = ref<HTMLDivElement>()
|
|
79
82
|
const hoverIndex = ref<CellIndex>([0, 0])
|
|
80
83
|
const lineHoverIndex = ref<CellIndex>([-1, -1])
|
|
81
84
|
const dragInfo = ref<DragInfo>()
|
|
82
85
|
|
|
86
|
+
// --- Column resize state ---
|
|
87
|
+
const resizingCol = ref(-1)
|
|
88
|
+
const colWidths = ref<number[]>([])
|
|
89
|
+
|
|
90
|
+
const syncColWidths = () => {
|
|
91
|
+
if (resizingCol.value >= 0) return
|
|
92
|
+
const firstRow = node.value.firstChild
|
|
93
|
+
if (!firstRow) return
|
|
94
|
+
const colCount = firstRow.content.childCount
|
|
95
|
+
const widths: number[] = []
|
|
96
|
+
const hasBounds = colBounds.value.length === colCount
|
|
97
|
+
for (let i = 0; i < colCount; i++) {
|
|
98
|
+
const cell = firstRow.content.child(i)
|
|
99
|
+
const w = cell.attrs.colwidth
|
|
100
|
+
if (Array.isArray(w) && w[0]) {
|
|
101
|
+
widths.push(w[0])
|
|
102
|
+
} else if (hasBounds) {
|
|
103
|
+
widths.push(Math.round(colBounds.value[i]!.width))
|
|
104
|
+
} else {
|
|
105
|
+
widths.push(0)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
colWidths.value = widths
|
|
109
|
+
}
|
|
110
|
+
|
|
83
111
|
const refs: Refs = {
|
|
84
112
|
dragPreviewRef,
|
|
85
113
|
tableWrapperRef,
|
|
86
114
|
contentWrapperRef,
|
|
87
|
-
yLineHandleRef,
|
|
88
|
-
xLineHandleRef,
|
|
89
|
-
colHandleRef,
|
|
90
|
-
rowHandleRef,
|
|
91
115
|
hoverIndex,
|
|
92
116
|
lineHoverIndex,
|
|
93
117
|
dragInfo,
|
|
@@ -98,20 +122,270 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
98
122
|
const {
|
|
99
123
|
onAddRow,
|
|
100
124
|
onAddCol,
|
|
125
|
+
addRowByIndex,
|
|
126
|
+
addColByIndex,
|
|
101
127
|
selectCol,
|
|
102
128
|
selectRow,
|
|
103
129
|
deleteSelected,
|
|
104
130
|
onAlign,
|
|
131
|
+
onMergeCells,
|
|
132
|
+
onSplitCell,
|
|
105
133
|
} = useOperation(refs, ctx, getPos)
|
|
106
134
|
|
|
135
|
+
// --- Floating cell toolbar state ---
|
|
136
|
+
const cellToolbarRef = ref<HTMLDivElement>()
|
|
137
|
+
const showCellToolbar = ref(false)
|
|
138
|
+
const canMerge = ref(false)
|
|
139
|
+
const canSplit = ref(false)
|
|
140
|
+
|
|
141
|
+
const updateCellToolbar = () => {
|
|
142
|
+
const { selection } = view.state
|
|
143
|
+
const tablePos = getPos()
|
|
144
|
+
if (tablePos == null) {
|
|
145
|
+
showCellToolbar.value = false
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
const tableEnd = tablePos + node.value.nodeSize
|
|
149
|
+
if (selection.from < tablePos || selection.from > tableEnd) {
|
|
150
|
+
showCellToolbar.value = false
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (selection instanceof CellSelection) {
|
|
155
|
+
// Compute merge/split availability
|
|
156
|
+
let _cellCount = 0
|
|
157
|
+
let hasMerged = false
|
|
158
|
+
selection.forEachCell((cell) => {
|
|
159
|
+
_cellCount++
|
|
160
|
+
if ((cell.attrs.rowspan ?? 1) > 1 || (cell.attrs.colspan ?? 1) > 1) {
|
|
161
|
+
hasMerged = true
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
canMerge.value = _cellCount > 1
|
|
165
|
+
canSplit.value = hasMerged
|
|
166
|
+
} else {
|
|
167
|
+
// TextSelection: only show toolbar for CellSelection-based actions
|
|
168
|
+
showCellToolbar.value = false
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!canMerge.value && !canSplit.value) {
|
|
173
|
+
showCellToolbar.value = false
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
showCellToolbar.value = true
|
|
178
|
+
|
|
179
|
+
// Position toolbar above the first selected cell using floating-ui
|
|
180
|
+
requestAnimationFrame(() => {
|
|
181
|
+
const toolbar = cellToolbarRef.value
|
|
182
|
+
const content = contentWrapperRef.value
|
|
183
|
+
if (!toolbar || !content) return
|
|
184
|
+
|
|
185
|
+
const selectedCells = content.querySelectorAll('.selectedCell')
|
|
186
|
+
let refEl: Element | null =
|
|
187
|
+
selectedCells.length > 0 ? selectedCells[0]! : null
|
|
188
|
+
|
|
189
|
+
if (!refEl && !(view.state.selection instanceof CellSelection)) {
|
|
190
|
+
const { $from } = view.state.selection
|
|
191
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
192
|
+
const node = $from.node(d)
|
|
193
|
+
if (
|
|
194
|
+
node.type.name === 'table_cell' ||
|
|
195
|
+
node.type.name === 'table_header'
|
|
196
|
+
) {
|
|
197
|
+
const cellPos = $from.before(d)
|
|
198
|
+
const dom = view.nodeDOM(cellPos)
|
|
199
|
+
if (dom instanceof HTMLElement) refEl = dom
|
|
200
|
+
break
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!refEl) return
|
|
206
|
+
|
|
207
|
+
computePosition(refEl, toolbar, { placement: 'top' })
|
|
208
|
+
.then(({ x, y }) => {
|
|
209
|
+
toolbar.style.left = `${x}px`
|
|
210
|
+
toolbar.style.top = `${y}px`
|
|
211
|
+
toolbar.dataset.show = 'true'
|
|
212
|
+
})
|
|
213
|
+
.catch(console.error)
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const colBounds = ref<{ left: number; width: number }[]>([])
|
|
218
|
+
const rowBounds = ref<{ top: number; height: number }[]>([])
|
|
219
|
+
|
|
220
|
+
let ro: ResizeObserver | null = null
|
|
221
|
+
let mo: MutationObserver | null = null
|
|
222
|
+
|
|
223
|
+
const updateBounds = () => {
|
|
224
|
+
const content = contentWrapperRef.value
|
|
225
|
+
if (!content) return
|
|
226
|
+
|
|
227
|
+
const firstRow = content.querySelector('tr')
|
|
228
|
+
const tableRect = content.getBoundingClientRect()
|
|
229
|
+
|
|
230
|
+
if (firstRow) {
|
|
231
|
+
colBounds.value = Array.from(firstRow.children).map((c) => {
|
|
232
|
+
const rect = c.getBoundingClientRect()
|
|
233
|
+
return { left: rect.left - tableRect.left, width: rect.width }
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const rows = content.querySelectorAll('tr')
|
|
238
|
+
rowBounds.value = Array.from(rows).map((c) => {
|
|
239
|
+
const rect = c.getBoundingClientRect()
|
|
240
|
+
return { top: rect.top - tableRect.top, height: rect.height }
|
|
241
|
+
})
|
|
242
|
+
syncColWidths()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const startResize = (e: PointerEvent, colIndex: number) => {
|
|
246
|
+
if (!view.editable) return
|
|
247
|
+
e.preventDefault()
|
|
248
|
+
e.stopPropagation()
|
|
249
|
+
const startX = e.clientX
|
|
250
|
+
const startWidth = colBounds.value[colIndex]?.width ?? 100
|
|
251
|
+
resizingCol.value = colIndex
|
|
252
|
+
|
|
253
|
+
const onMove = (ev: PointerEvent) => {
|
|
254
|
+
const delta = ev.clientX - startX
|
|
255
|
+
const newWidth = Math.max(50, startWidth + delta)
|
|
256
|
+
colWidths.value = colWidths.value.map((w, i) =>
|
|
257
|
+
i === colIndex ? newWidth : w
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const onUp = (ev: PointerEvent) => {
|
|
262
|
+
document.removeEventListener('pointermove', onMove)
|
|
263
|
+
document.removeEventListener('pointerup', onUp)
|
|
264
|
+
|
|
265
|
+
const delta = ev.clientX - startX
|
|
266
|
+
const newWidth = Math.max(50, startWidth + delta)
|
|
267
|
+
|
|
268
|
+
// Commit via ProseMirror transaction
|
|
269
|
+
const tablePos = getPos()
|
|
270
|
+
if (tablePos == null) {
|
|
271
|
+
resizingCol.value = -1
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
const tableNode = node.value
|
|
275
|
+
let tr = view.state.tr
|
|
276
|
+
let pos = tablePos + 1
|
|
277
|
+
|
|
278
|
+
tableNode.forEach((row) => {
|
|
279
|
+
let cellIdx = 0
|
|
280
|
+
row.forEach((cell, _offset, cellNodePos) => {
|
|
281
|
+
if (cellIdx === colIndex) {
|
|
282
|
+
const absPos = tablePos + 1 + cellNodePos
|
|
283
|
+
const colwidth = [Math.round(newWidth)]
|
|
284
|
+
tr = tr.setNodeMarkup(absPos, undefined, {
|
|
285
|
+
...cell.attrs,
|
|
286
|
+
colwidth,
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
cellIdx++
|
|
290
|
+
})
|
|
291
|
+
pos += row.nodeSize
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
resizingCol.value = -1
|
|
295
|
+
if (tr.docChanged) view.dispatch(tr)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
document.addEventListener('pointermove', onMove)
|
|
299
|
+
document.addEventListener('pointerup', onUp)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const activeColIndex = ref(-1)
|
|
303
|
+
const activeRowIndex = ref(-1)
|
|
304
|
+
|
|
305
|
+
// Listen for ProseMirror state updates
|
|
306
|
+
const dispatchListener = () => {
|
|
307
|
+
updateCellToolbar()
|
|
308
|
+
|
|
309
|
+
// Update active handler button groups based on selection
|
|
310
|
+
const { selection } = view.state
|
|
311
|
+
if (selection instanceof CellSelection) {
|
|
312
|
+
if (selection.isColSelection()) {
|
|
313
|
+
const { $head } = selection
|
|
314
|
+
activeColIndex.value = $head.index($head.depth - 1)
|
|
315
|
+
activeRowIndex.value = -1
|
|
316
|
+
} else if (selection.isRowSelection()) {
|
|
317
|
+
activeColIndex.value = -1
|
|
318
|
+
// Simple approach for row index: derive from hoverIndex which is updated by pointer? Or compute.
|
|
319
|
+
// recoveryStateBetweenUpdate updates hoverIndex.
|
|
320
|
+
} else {
|
|
321
|
+
activeColIndex.value = -1
|
|
322
|
+
activeRowIndex.value = -1
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
activeColIndex.value = -1
|
|
326
|
+
activeRowIndex.value = -1
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
107
330
|
onMounted(() => {
|
|
108
331
|
requestAnimationFrame(() => {
|
|
109
|
-
// This is a wordaround to keep the popover open when click the select col/row button
|
|
110
332
|
if (view.editable) recoveryStateBetweenUpdate(refs, view, node.value)
|
|
333
|
+
syncColWidths()
|
|
334
|
+
})
|
|
335
|
+
view.dom.addEventListener('keyup', dispatchListener)
|
|
336
|
+
view.dom.addEventListener('pointerup', dispatchListener)
|
|
337
|
+
updateCellToolbar()
|
|
338
|
+
|
|
339
|
+
ro = new ResizeObserver(() => {
|
|
340
|
+
requestAnimationFrame(updateBounds)
|
|
111
341
|
})
|
|
342
|
+
mo = new MutationObserver((mutations) => {
|
|
343
|
+
let shouldUpdate = false
|
|
344
|
+
for (const mut of mutations) {
|
|
345
|
+
if (mut.type === 'childList') {
|
|
346
|
+
for (const node of mut.addedNodes) {
|
|
347
|
+
if (
|
|
348
|
+
node instanceof HTMLElement &&
|
|
349
|
+
['TR', 'TD', 'TH', 'TBODY'].includes(node.nodeName)
|
|
350
|
+
)
|
|
351
|
+
shouldUpdate = true
|
|
352
|
+
}
|
|
353
|
+
for (const node of mut.removedNodes) {
|
|
354
|
+
if (
|
|
355
|
+
node instanceof HTMLElement &&
|
|
356
|
+
['TR', 'TD', 'TH', 'TBODY'].includes(node.nodeName)
|
|
357
|
+
)
|
|
358
|
+
shouldUpdate = true
|
|
359
|
+
}
|
|
360
|
+
} else if (mut.type === 'attributes') {
|
|
361
|
+
shouldUpdate = true
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (shouldUpdate) {
|
|
365
|
+
requestAnimationFrame(updateBounds)
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
if (contentWrapperRef.value) {
|
|
370
|
+
ro.observe(contentWrapperRef.value)
|
|
371
|
+
mo.observe(contentWrapperRef.value, {
|
|
372
|
+
childList: true,
|
|
373
|
+
subtree: true,
|
|
374
|
+
attributes: true,
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
onBeforeUnmount(() => {
|
|
380
|
+
view.dom.removeEventListener('keyup', dispatchListener)
|
|
381
|
+
view.dom.removeEventListener('pointerup', dispatchListener)
|
|
382
|
+
if (ro) ro.disconnect()
|
|
383
|
+
if (mo) mo.disconnect()
|
|
112
384
|
})
|
|
113
385
|
|
|
114
386
|
return () => {
|
|
387
|
+
updateCellToolbar()
|
|
388
|
+
|
|
115
389
|
return (
|
|
116
390
|
<div
|
|
117
391
|
onDragstart={(e) => e.preventDefault()}
|
|
@@ -119,63 +393,204 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
119
393
|
onDragleave={(e) => e.preventDefault()}
|
|
120
394
|
onPointermove={pointerMove}
|
|
121
395
|
onPointerleave={pointerLeave}
|
|
396
|
+
class="milkdown-table-wrapper-outer"
|
|
122
397
|
>
|
|
398
|
+
{/* Floating cell toolbar (merge/split) */}
|
|
123
399
|
<div
|
|
124
|
-
|
|
400
|
+
ref={cellToolbarRef}
|
|
401
|
+
data-show={showCellToolbar.value ? 'true' : 'false'}
|
|
402
|
+
class="cell-toolbar"
|
|
125
403
|
contenteditable="false"
|
|
126
|
-
draggable="true"
|
|
127
|
-
data-role="col-drag-handle"
|
|
128
|
-
class="handle cell-handle"
|
|
129
|
-
onDragstart={dragCol}
|
|
130
|
-
onClick={selectCol}
|
|
131
404
|
onPointerdown={(e: PointerEvent) => e.stopPropagation()}
|
|
132
|
-
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
133
|
-
ref={colHandleRef}
|
|
134
405
|
>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<Icon icon={config.renderButton('align_col_left')} />
|
|
406
|
+
{canMerge.value && (
|
|
407
|
+
<button
|
|
408
|
+
type="button"
|
|
409
|
+
class="cell-toolbar-btn"
|
|
410
|
+
onPointerdown={onMergeCells}
|
|
411
|
+
>
|
|
412
|
+
<Icon icon={config.renderButton('merge_cells')} />
|
|
143
413
|
</button>
|
|
144
|
-
|
|
145
|
-
|
|
414
|
+
)}
|
|
415
|
+
{canSplit.value && (
|
|
416
|
+
<button
|
|
417
|
+
type="button"
|
|
418
|
+
class="cell-toolbar-btn"
|
|
419
|
+
onPointerdown={onSplitCell}
|
|
420
|
+
>
|
|
421
|
+
<Icon icon={config.renderButton('split_cell')} />
|
|
146
422
|
</button>
|
|
147
|
-
|
|
148
|
-
<Icon icon={config.renderButton('align_col_right')} />
|
|
149
|
-
</button>
|
|
150
|
-
<button type="button" onPointerdown={deleteSelected}>
|
|
151
|
-
<Icon icon={config.renderButton('delete_col')} />
|
|
152
|
-
</button>
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
155
|
-
<div
|
|
156
|
-
data-show="false"
|
|
157
|
-
contenteditable="false"
|
|
158
|
-
draggable="true"
|
|
159
|
-
data-role="row-drag-handle"
|
|
160
|
-
class="handle cell-handle"
|
|
161
|
-
onDragstart={dragRow}
|
|
162
|
-
onClick={selectRow}
|
|
163
|
-
onPointerdown={(e: PointerEvent) => e.stopPropagation()}
|
|
164
|
-
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
165
|
-
ref={rowHandleRef}
|
|
166
|
-
>
|
|
167
|
-
<Icon icon={config.renderButton('row_drag_handle')} />
|
|
168
|
-
<div
|
|
169
|
-
data-show="false"
|
|
170
|
-
class="button-group"
|
|
171
|
-
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
172
|
-
>
|
|
173
|
-
<button type="button" onPointerdown={deleteSelected}>
|
|
174
|
-
<Icon icon={config.renderButton('delete_row')} />
|
|
175
|
-
</button>
|
|
176
|
-
</div>
|
|
423
|
+
)}
|
|
177
424
|
</div>
|
|
425
|
+
|
|
178
426
|
<div class="table-wrapper" ref={tableWrapperRef}>
|
|
427
|
+
{/* Fixed Col Drag Handles */}
|
|
428
|
+
<div contenteditable="false" class="fixed-handles-col">
|
|
429
|
+
{colBounds.value.map((bound, i) => (
|
|
430
|
+
<div
|
|
431
|
+
key={`col-${i}`}
|
|
432
|
+
class="handle cell-handle fixed"
|
|
433
|
+
data-role="col-drag-handle"
|
|
434
|
+
draggable="true"
|
|
435
|
+
style={{ left: `${bound.left}px`, width: `${bound.width}px` }}
|
|
436
|
+
onDragstart={(e) => {
|
|
437
|
+
hoverIndex.value = [0, i]
|
|
438
|
+
dragCol(e)
|
|
439
|
+
}}
|
|
440
|
+
onClick={(e) => {
|
|
441
|
+
hoverIndex.value = [0, i]
|
|
442
|
+
selectCol(e)
|
|
443
|
+
activeColIndex.value = i
|
|
444
|
+
activeRowIndex.value = -1
|
|
445
|
+
}}
|
|
446
|
+
onPointerdown={(e: PointerEvent) => e.stopPropagation()}
|
|
447
|
+
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
448
|
+
>
|
|
449
|
+
<div
|
|
450
|
+
class="button-group"
|
|
451
|
+
data-show={activeColIndex.value === i ? 'true' : 'false'}
|
|
452
|
+
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
453
|
+
>
|
|
454
|
+
<button
|
|
455
|
+
type="button"
|
|
456
|
+
onPointerdown={(e) => {
|
|
457
|
+
hoverIndex.value = [0, i]
|
|
458
|
+
onAlign('left')(e)
|
|
459
|
+
}}
|
|
460
|
+
>
|
|
461
|
+
<Icon icon={config.renderButton('align_col_left')} />
|
|
462
|
+
</button>
|
|
463
|
+
<button
|
|
464
|
+
type="button"
|
|
465
|
+
onPointerdown={(e) => {
|
|
466
|
+
hoverIndex.value = [0, i]
|
|
467
|
+
onAlign('center')(e)
|
|
468
|
+
}}
|
|
469
|
+
>
|
|
470
|
+
<Icon icon={config.renderButton('align_col_center')} />
|
|
471
|
+
</button>
|
|
472
|
+
<button
|
|
473
|
+
type="button"
|
|
474
|
+
onPointerdown={(e) => {
|
|
475
|
+
hoverIndex.value = [0, i]
|
|
476
|
+
onAlign('right')(e)
|
|
477
|
+
}}
|
|
478
|
+
>
|
|
479
|
+
<Icon icon={config.renderButton('align_col_right')} />
|
|
480
|
+
</button>
|
|
481
|
+
<button
|
|
482
|
+
type="button"
|
|
483
|
+
onPointerdown={(e) => {
|
|
484
|
+
hoverIndex.value = [0, i]
|
|
485
|
+
deleteSelected(e)
|
|
486
|
+
}}
|
|
487
|
+
>
|
|
488
|
+
<Icon icon={config.renderButton('delete_col')} />
|
|
489
|
+
</button>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
))}
|
|
493
|
+
</div>
|
|
494
|
+
|
|
495
|
+
{/* Add Col Dots */}
|
|
496
|
+
<div contenteditable="false" class="add-dots-col">
|
|
497
|
+
{[
|
|
498
|
+
...colBounds.value.map((b) => b.left),
|
|
499
|
+
colBounds.value.length
|
|
500
|
+
? colBounds.value[colBounds.value.length - 1].left +
|
|
501
|
+
colBounds.value[colBounds.value.length - 1].width
|
|
502
|
+
: 0,
|
|
503
|
+
].map((left, i) => (
|
|
504
|
+
<div
|
|
505
|
+
key={`add-col-${i}`}
|
|
506
|
+
class="add-dot"
|
|
507
|
+
style={{ left: `${left}px` }}
|
|
508
|
+
onClick={() => addColByIndex(i)}
|
|
509
|
+
onPointerdown={(e: PointerEvent) => e.stopPropagation()}
|
|
510
|
+
>
|
|
511
|
+
<Icon icon={config.renderButton('add_col')} />
|
|
512
|
+
</div>
|
|
513
|
+
))}
|
|
514
|
+
</div>
|
|
515
|
+
|
|
516
|
+
{/* Column Resize Handles */}
|
|
517
|
+
<div contenteditable="false" class="resize-handles-col">
|
|
518
|
+
{colBounds.value.map((bound, i) => (
|
|
519
|
+
<div
|
|
520
|
+
key={`resize-${i}`}
|
|
521
|
+
class={[
|
|
522
|
+
'col-resize-handle',
|
|
523
|
+
resizingCol.value === i ? 'active' : '',
|
|
524
|
+
].join(' ')}
|
|
525
|
+
style={{
|
|
526
|
+
left: `${bound.left + bound.width - 3}px`,
|
|
527
|
+
}}
|
|
528
|
+
onPointerdown={(e: PointerEvent) => startResize(e, i)}
|
|
529
|
+
/>
|
|
530
|
+
))}
|
|
531
|
+
</div>
|
|
532
|
+
|
|
533
|
+
{/* Fixed Row Drag Handles */}
|
|
534
|
+
<div contenteditable="false" class="fixed-handles-row">
|
|
535
|
+
{rowBounds.value.map((bound, i) => (
|
|
536
|
+
<div
|
|
537
|
+
key={`row-${i}`}
|
|
538
|
+
class="handle cell-handle fixed"
|
|
539
|
+
data-role="row-drag-handle"
|
|
540
|
+
draggable="true"
|
|
541
|
+
style={{ top: `${bound.top}px`, height: `${bound.height}px` }}
|
|
542
|
+
onDragstart={(e) => {
|
|
543
|
+
hoverIndex.value = [i, 0]
|
|
544
|
+
dragRow(e)
|
|
545
|
+
}}
|
|
546
|
+
onClick={(e) => {
|
|
547
|
+
hoverIndex.value = [i, 0]
|
|
548
|
+
selectRow(e)
|
|
549
|
+
activeRowIndex.value = i
|
|
550
|
+
activeColIndex.value = -1
|
|
551
|
+
}}
|
|
552
|
+
onPointerdown={(e: PointerEvent) => e.stopPropagation()}
|
|
553
|
+
>
|
|
554
|
+
<div
|
|
555
|
+
class="button-group"
|
|
556
|
+
data-show={activeRowIndex.value === i ? 'true' : 'false'}
|
|
557
|
+
onPointermove={(e: PointerEvent) => e.stopPropagation()}
|
|
558
|
+
>
|
|
559
|
+
<button
|
|
560
|
+
type="button"
|
|
561
|
+
onPointerdown={(e) => {
|
|
562
|
+
hoverIndex.value = [i, 0]
|
|
563
|
+
deleteSelected(e)
|
|
564
|
+
}}
|
|
565
|
+
>
|
|
566
|
+
<Icon icon={config.renderButton('delete_row')} />
|
|
567
|
+
</button>
|
|
568
|
+
</div>
|
|
569
|
+
</div>
|
|
570
|
+
))}
|
|
571
|
+
</div>
|
|
572
|
+
|
|
573
|
+
{/* Add Row Dots */}
|
|
574
|
+
<div contenteditable="false" class="add-dots-row">
|
|
575
|
+
{[
|
|
576
|
+
...rowBounds.value.map((b) => b.top),
|
|
577
|
+
rowBounds.value.length
|
|
578
|
+
? rowBounds.value[rowBounds.value.length - 1].top +
|
|
579
|
+
rowBounds.value[rowBounds.value.length - 1].height
|
|
580
|
+
: 0,
|
|
581
|
+
].map((top, i) => (
|
|
582
|
+
<div
|
|
583
|
+
key={`add-row-${i}`}
|
|
584
|
+
class="add-dot"
|
|
585
|
+
style={{ top: `${top}px` }}
|
|
586
|
+
onClick={() => addRowByIndex(i)}
|
|
587
|
+
onPointerdown={(e: PointerEvent) => e.stopPropagation()}
|
|
588
|
+
>
|
|
589
|
+
<Icon icon={config.renderButton('add_row')} />
|
|
590
|
+
</div>
|
|
591
|
+
))}
|
|
592
|
+
</div>
|
|
593
|
+
|
|
179
594
|
<div
|
|
180
595
|
data-show="false"
|
|
181
596
|
class="drag-preview"
|
|
@@ -186,31 +601,17 @@ export const TableBlock = defineComponent<TableBlockProps>({
|
|
|
186
601
|
<tbody></tbody>
|
|
187
602
|
</table>
|
|
188
603
|
</div>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
</div>
|
|
201
|
-
<div
|
|
202
|
-
data-show="false"
|
|
203
|
-
contenteditable="false"
|
|
204
|
-
data-display-type="tool"
|
|
205
|
-
data-role="y-line-drag-handle"
|
|
206
|
-
class="handle line-handle"
|
|
207
|
-
ref={yLineHandleRef}
|
|
208
|
-
>
|
|
209
|
-
<button type="button" onClick={onAddCol} class="add-button">
|
|
210
|
-
<Icon icon={config.renderButton('add_col')} />
|
|
211
|
-
</button>
|
|
212
|
-
</div>
|
|
213
|
-
<table ref={contentWrapperFunctionRef} class="children"></table>
|
|
604
|
+
|
|
605
|
+
<table ref={contentWrapperFunctionRef} class="children">
|
|
606
|
+
<colgroup>
|
|
607
|
+
{colWidths.value.map((w, i) => (
|
|
608
|
+
<col
|
|
609
|
+
key={i}
|
|
610
|
+
style={w > 0 ? { width: `${w}px` } : undefined}
|
|
611
|
+
/>
|
|
612
|
+
))}
|
|
613
|
+
</colgroup>
|
|
614
|
+
</table>
|
|
214
615
|
</div>
|
|
215
616
|
</div>
|
|
216
617
|
)
|
|
@@ -16,10 +16,6 @@ import {
|
|
|
16
16
|
createDragRowHandler,
|
|
17
17
|
} from '../dnd/create-drag-handler'
|
|
18
18
|
import { createDragOverHandler } from '../dnd/drag-over-handler'
|
|
19
|
-
import {
|
|
20
|
-
computeColHandlePositionByIndex,
|
|
21
|
-
computeRowHandlePositionByIndex,
|
|
22
|
-
} from './utils'
|
|
23
19
|
|
|
24
20
|
export function useDragHandlers(
|
|
25
21
|
refs: Refs,
|
|
@@ -56,10 +52,6 @@ export function useDragHandlers(
|
|
|
56
52
|
if (!info) return
|
|
57
53
|
if (!ctx) return
|
|
58
54
|
if (preview.dataset.show === 'false') return
|
|
59
|
-
const colHandle = refs.colHandleRef.value
|
|
60
|
-
if (!colHandle) return
|
|
61
|
-
const rowHandle = refs.rowHandleRef.value
|
|
62
|
-
if (!rowHandle) return
|
|
63
55
|
|
|
64
56
|
yHandle.dataset.show = 'false'
|
|
65
57
|
xHandle.dataset.show = 'false'
|
|
@@ -79,10 +71,7 @@ export function useDragHandlers(
|
|
|
79
71
|
})
|
|
80
72
|
commands.call(moveColCommand.key, payload)
|
|
81
73
|
const index: CellIndex = [0, info.endIndex]
|
|
82
|
-
|
|
83
|
-
refs,
|
|
84
|
-
index,
|
|
85
|
-
})
|
|
74
|
+
refs.hoverIndex.value = index
|
|
86
75
|
} else {
|
|
87
76
|
commands.call(selectRowCommand.key, {
|
|
88
77
|
pos: payload.pos,
|
|
@@ -90,10 +79,7 @@ export function useDragHandlers(
|
|
|
90
79
|
})
|
|
91
80
|
commands.call(moveRowCommand.key, payload)
|
|
92
81
|
const index: CellIndex = [info.endIndex, 0]
|
|
93
|
-
|
|
94
|
-
refs,
|
|
95
|
-
index,
|
|
96
|
-
})
|
|
82
|
+
refs.hoverIndex.value = index
|
|
97
83
|
}
|
|
98
84
|
|
|
99
85
|
requestAnimationFrame(() => {
|