simplify dynamic editor code

This commit is contained in:
Alex Turchyn authored and Pete Matsyburka committed 2026-10-03 14:05:38 +03:00
1 parent 4f5e72f983
commit a70ac571e3
3 files changed
+34 -57

No files matched your search

@@ -43,7 +43,7 @@
</div> </div>
</div> </div>
<div <div
v-if="size && coords.type === 'table'" v-if="size"
class="absolute top-0 left-0 border border-dashed border-blue-500" class="absolute top-0 left-0 border border-dashed border-blue-500"
:style="{ width: size.width + 'px', height: size.height + 'px' }" :style="{ width: size.width + 'px', height: size.height + 'px' }"
/> />
@@ -114,8 +114,8 @@ export default {
], ],
[ [
[ [
{ command: 'mergeTableCells', icon: IconMergeCells, title: 'merge_cells', structural: true, enabled: 'canMerge' }, { command: 'mergeTableCells', icon: IconMergeCells, title: 'merge_cells', structural: true, disabled: !this.coords.canMerge },
{ command: 'splitTableCell', icon: IconSplitCell, title: 'split_cell', structural: true, enabled: 'canSplit' } { command: 'splitTableCell', icon: IconSplitCell, title: 'split_cell', structural: true, disabled: !this.coords.canSplit }
], ],
[ [
{ command: 'deleteTableRow', icon: IconRowRemove, title: 'delete_row', danger: true, structural: true }, { command: 'deleteTableRow', icon: IconRowRemove, title: 'delete_row', danger: true, structural: true },
@@ -138,7 +138,7 @@ export default {
}, },
methods: { methods: {
isDisabled (action) { isDisabled (action) {
return (action.structural && !this.coords.isRegular) || (action.enabled && !this.coords[action.enabled]) return (action.structural && !this.coords.isRegular) || action.disabled
}, },
startResize (event) { startResize (event) {
this.resizeStart = { x: event.clientX, y: event.clientY, width: this.coords.width, height: this.coords.height } this.resizeStart = { x: event.clientX, y: event.clientY, width: this.coords.width, height: this.coords.height }
@@ -1058,8 +1058,6 @@ function tableCellNodes (table) {
function styleNewCells (tr, existingCells) { function styleNewCells (tr, existingCells) {
const table = findTable(tr.selection.$head) const table = findTable(tr.selection.$head)
if (!table) return
const map = TableMap.get(table.node) const map = TableMap.get(table.node)
const cellPositions = [...new Set(map.map)] const cellPositions = [...new Set(map.map)]
const isNew = (pos) => !existingCells.has(table.node.nodeAt(pos)) const isNew = (pos) => !existingCells.has(table.node.nodeAt(pos))
@@ -1098,15 +1096,7 @@ function styleNewCells (tr, existingCells) {
}) })
} }
function updateTableCols (tr, tablePos, update) { function runTableCommand (command, { state, tr, dispatch }, colsUpdate) {
const table = tr.doc.nodeAt(tablePos)
if (!table.attrs.cols.length) return
tr.setNodeMarkup(tablePos, null, { ...table.attrs, cols: update([...table.attrs.cols]) })
}
function runTableCommand (command, { state, tr, dispatch }, { colsUpdate } = {}) {
const table = findEditableTable(state) const table = findEditableTable(state)
if (!table) return false if (!table) return false
@@ -1122,7 +1112,13 @@ function runTableCommand (command, { state, tr, dispatch }, { colsUpdate } = {})
styleNewCells(tr, existingCells) styleNewCells(tr, existingCells)
if (colsUpdate && colsMatch) { if (colsUpdate && colsMatch) {
updateTableCols(tr, tr.mapping.map(table.pos), (cols) => colsUpdate(cols, rect)) const tablePos = tr.mapping.map(table.pos)
const { attrs } = tr.doc.nodeAt(tablePos)
const cols = [...attrs.cols]
colsUpdate(cols, rect)
tr.setNodeMarkup(tablePos, null, { ...attrs, cols })
} }
return true return true
@@ -1142,9 +1138,10 @@ const TableCommands = Extension.create({
plugin.props.handlePaste = (view, event, slice) => { plugin.props.handlePaste = (view, event, slice) => {
const table = findTable(view.state.selection.$head) const table = findTable(view.state.selection.$head)
if (table && !isRegularTable(table.node)) return view.state.selection instanceof CellSelection if (!table) return false
if (!isRegularTable(table.node)) return view.state.selection instanceof CellSelection
const existingCells = table ? tableCellNodes(table.node) : new Set() const existingCells = tableCellNodes(table.node)
const dispatch = (tr) => { const dispatch = (tr) => {
styleNewCells(tr, existingCells) styleNewCells(tr, existingCells)
@@ -1181,14 +1178,10 @@ const TableCommands = Extension.create({
addCommands () { addCommands () {
return { return {
addTableRow: (after) => (props) => runTableCommand(after ? addRowAfter : addRowBefore, props), addTableRow: (after) => (props) => runTableCommand(after ? addRowAfter : addRowBefore, props),
addTableColumn: (after) => (props) => runTableCommand(after ? addColumnAfter : addColumnBefore, props, { addTableColumn: (after) => (props) => runTableCommand(after ? addColumnAfter : addColumnBefore, props, (cols, rect) => {
colsUpdate: (cols, rect) => { const index = after ? rect.right : rect.left
const index = after ? rect.right : rect.left
cols.splice(index, 0, { ...cols[after ? rect.right - 1 : rect.left] }) cols.splice(index, 0, { ...cols[after ? index - 1 : index] })
return cols
}
}), }),
deleteTableRow: () => (props) => { deleteTableRow: () => (props) => {
const table = findEditableTable(props.state) const table = findEditableTable(props.state)
@@ -1218,13 +1211,7 @@ const TableCommands = Extension.create({
return true return true
} }
return runTableCommand(deleteColumn, props, { return runTableCommand(deleteColumn, props, (cols, { left, right }) => cols.splice(left, right - left))
colsUpdate: (cols, { left, right }) => {
cols.splice(left, right - left)
return cols
}
})
}, },
mergeTableCells: () => (props) => runTableCommand(mergeCells, props), mergeTableCells: () => (props) => runTableCommand(mergeCells, props),
splitTableCell: () => (props) => runTableCommand(splitCell, props), splitTableCell: () => (props) => runTableCommand(splitCell, props),
@@ -1247,7 +1234,7 @@ const TableCommands = Extension.create({
let htmlAttrs = table.node.attrs.htmlAttrs let htmlAttrs = table.node.attrs.htmlAttrs
if (isFloating && align !== 'center') { if (isFloating && align !== 'center') {
const gap = [el.style.marginLeft, el.style.marginRight].find((value) => value && value !== 'auto' && parseFloat(value)) || '0' const gap = [el.style.marginLeft, el.style.marginRight].find((value) => parseFloat(value)) || '0'
margins = align === 'left' ? ['0', gap] : [gap, '0'] margins = align === 'left' ? ['0', gap] : [gap, '0']
htmlAttrs = withStyle(htmlAttrs, 'float', align) htmlAttrs = withStyle(htmlAttrs, 'float', align)
@@ -1593,7 +1580,7 @@ export function buildEditor ({ dynamicAreaProps, attachmentsIndex, renderHtmlFor
const MIN_SIZE_PX = 16 const MIN_SIZE_PX = 16
function findResizeTarget (view, event) { function findResizeTarget (view, event) {
const cellDom = event.target.closest?.('td, th') const cellDom = event.target.closest('td, th')
if (!cellDom) return null if (!cellDom) return null
@@ -1671,7 +1658,7 @@ export function buildEditor ({ dynamicAreaProps, attachmentsIndex, renderHtmlFor
guide.remove() guide.remove()
document.body.style.cursor = '' document.body.style.cursor = ''
const delta = ((isColumn ? e.clientX - event.clientX : e.clientY - event.clientY)) / dynamicAreaProps.getZoom() const delta = (isColumn ? e.clientX - event.clientX : e.clientY - event.clientY) / dynamicAreaProps.getZoom()
if (!delta) return if (!delta) return
@@ -1795,7 +1782,7 @@ export function buildEditor ({ dynamicAreaProps, attachmentsIndex, renderHtmlFor
onUpdate: (updatedNode) => updatedNode.attrs.htmlAttrs === node.attrs.htmlAttrs, onUpdate: (updatedNode) => updatedNode.attrs.htmlAttrs === node.attrs.htmlAttrs,
options: { options: {
directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'], directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
min: { width: 16, height: 16 }, min: { width: MIN_SIZE_PX, height: MIN_SIZE_PX },
preserveAspectRatio: true preserveAspectRatio: true
} }
}) })
@@ -192,13 +192,13 @@
class="absolute z-10 top-full mt-1 w-28 p-1 bg-white border border-neutral-200 rounded-lg shadow-lg" class="absolute z-10 top-full mt-1 w-28 p-1 bg-white border border-neutral-200 rounded-lg shadow-lg"
> >
<button <button
v-for="(label, style) in listStyles" v-for="({ label, marker }, style) in listStyles"
:key="style" :key="style"
class="flex items-center gap-2 w-full text-left px-2 py-1 rounded-md hover:bg-neutral-100" class="flex items-center gap-2 w-full px-2 py-1 rounded-md hover:bg-neutral-100"
:class="{ 'bg-neutral-200': style === listStyle }" :class="{ 'bg-neutral-200': style === listStyle }"
@click="toggleList(style)" @click="toggleList(style)"
> >
<span class="w-4 text-center">{{ listMarkers[style] }}</span> <span class="w-4 text-center">{{ marker }}</span>
{{ label }} {{ label }}
</button> </button>
</div> </div>
@@ -379,10 +379,7 @@ export default {
IconList, IconList,
IconX IconX
}, },
inject: { inject: ['t', 'documentFonts'],
t: 't',
documentFonts: { default: () => [] }
},
props: { props: {
editor: { editor: {
type: Object, type: Object,
@@ -445,16 +442,9 @@ export default {
}, },
listStyles () { listStyles () {
return { return {
disc: this.t('dots'), disc: { label: this.t('dots'), marker: '•' },
dash: this.t('dash'), dash: { label: this.t('dash'), marker: '–' },
decimal: this.t('numbers') decimal: { label: this.t('numbers'), marker: '1.' }
}
},
listMarkers () {
return {
disc: '•',
dash: '–',
decimal: '1.'
} }
}, },
colors () { colors () {
@@ -514,12 +504,12 @@ export default {
}) })
}, },
toggleBold () { toggleBold () {
this.toggleStyledMark('bold', 'font-weight', 'bold', this.isBold, (style) => parseInt(style.fontWeight) >= 600) this.toggleStyledMark('bold', 'font-weight', this.isBold, (style) => parseInt(style.fontWeight) >= 600)
}, },
toggleItalic () { toggleItalic () {
this.toggleStyledMark('italic', 'font-style', 'italic', this.isItalic, (style) => style.fontStyle === 'italic') this.toggleStyledMark('italic', 'font-style', this.isItalic, (style) => style.fontStyle === 'italic')
}, },
toggleStyledMark (markName, property, value, isActive, isStyled) { toggleStyledMark (markName, property, isActive, isStyled) {
const isSegmentStyled = ({ from }) => isStyled(getComputedStyle(this.textElementAt(from))) const isSegmentStyled = ({ from }) => isStyled(getComputedStyle(this.textElementAt(from)))
if (isActive) { if (isActive) {
@@ -540,7 +530,7 @@ export default {
if (tr.docChanged) view.dispatch(tr) if (tr.docChanged) view.dispatch(tr)
this.editor.commands.setListMarkerStyle(property, value) this.editor.commands.setListMarkerStyle(property, markName)
} }
}, },
selectedTextSegments () { selectedTextSegments () {