mirror of
https://github.com/docusealco/docuseal
synced 2026-11-04 06:42:32 -05:00
adjust dynamic editor menus on mobile
This commit is contained in:
1 parent
ed7109bfc6
commit
57c8be33ce
4 files changed
+102
-88
No files matched your search
@@ -4,56 +4,43 @@
|
|||||||
:style="{ top: coords.top + 'px', left: coords.left + 'px', width: coords.width + 'px', height: coords.height + 'px' }"
|
:style="{ top: coords.top + 'px', left: coords.left + 'px', width: coords.width + 'px', height: coords.height + 'px' }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="absolute left-0 flex items-center gap-0.5 p-1 bg-white border border-neutral-200 rounded-lg shadow-lg pointer-events-auto"
|
ref="toolbar"
|
||||||
:style="{ top: Math.max(coords.top - 40, 0) - coords.top + 'px' }"
|
class="absolute left-0 flex flex-col sm:flex-row gap-0.5 p-1 bg-white border border-neutral-200 rounded-lg shadow-lg pointer-events-auto"
|
||||||
|
:style="{ top: Math.max(coords.top - toolbarHeight - 2, 0) - coords.top + 'px' }"
|
||||||
@mousedown.prevent
|
@mousedown.prevent
|
||||||
>
|
>
|
||||||
<button
|
<div
|
||||||
v-for="(icon, align) in alignIcons"
|
v-for="(row, rowIndex) in actionRows"
|
||||||
:key="align"
|
:key="rowIndex"
|
||||||
class="inline-flex items-center justify-center w-7 h-7 rounded-md hover:bg-neutral-100"
|
class="flex items-center gap-0.5"
|
||||||
:class="{ 'bg-neutral-200': coords.align === align }"
|
|
||||||
:title="t(`align_${align}`)"
|
|
||||||
@click="$emit('command', 'setBlockAlign', align)"
|
|
||||||
>
|
>
|
||||||
<component
|
<template
|
||||||
:is="icon"
|
v-for="(group, groupIndex) in row"
|
||||||
:width="16"
|
:key="groupIndex"
|
||||||
:height="16"
|
|
||||||
:stroke-width="1.6"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<div class="w-px h-5 bg-neutral-200 mx-1" />
|
|
||||||
<template v-if="coords.type === 'table'">
|
|
||||||
<button
|
|
||||||
v-for="action in tableActions"
|
|
||||||
:key="action.title"
|
|
||||||
class="inline-flex items-center justify-center w-7 h-7 rounded-md"
|
|
||||||
:class="[{ 'text-red-600': action.danger }, isDisabled(action) ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-100']"
|
|
||||||
:title="action.structural && !coords.isRegular ? t('irregular_table_structure') : t(action.title)"
|
|
||||||
:disabled="isDisabled(action)"
|
|
||||||
@click="$emit('command', action.command, action.arg)"
|
|
||||||
>
|
>
|
||||||
<component
|
<div
|
||||||
:is="action.icon"
|
v-if="rowIndex > 0 || groupIndex > 0"
|
||||||
:width="16"
|
class="w-px h-5 bg-neutral-200 mx-1"
|
||||||
:height="16"
|
:class="{ 'hidden sm:block': rowIndex > 0 && groupIndex === 0 }"
|
||||||
:stroke-width="1.6"
|
|
||||||
/>
|
/>
|
||||||
</button>
|
<button
|
||||||
</template>
|
v-for="action in group"
|
||||||
<button
|
:key="action.title"
|
||||||
v-else
|
class="inline-flex items-center justify-center w-7 h-7 rounded-md"
|
||||||
class="inline-flex items-center justify-center w-7 h-7 rounded-md hover:bg-neutral-100 text-red-600"
|
:class="[{ 'text-red-600': action.danger, 'bg-neutral-200': action.active }, isDisabled(action) ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-100']"
|
||||||
:title="t('delete_image')"
|
:title="action.structural && !coords.isRegular ? t('irregular_table_structure') : t(action.title)"
|
||||||
@click="$emit('command', 'deleteSelection')"
|
:disabled="isDisabled(action)"
|
||||||
>
|
@click="$emit('command', action.command, action.arg)"
|
||||||
<IconTrash
|
>
|
||||||
:width="16"
|
<component
|
||||||
:height="16"
|
:is="action.icon"
|
||||||
:stroke-width="1.6"
|
:width="16"
|
||||||
/>
|
:height="16"
|
||||||
</button>
|
:stroke-width="1.6"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="size && coords.type === 'table'"
|
v-if="size && coords.type === 'table'"
|
||||||
@@ -62,10 +49,10 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="coords.type === 'table'"
|
v-if="coords.type === 'table'"
|
||||||
class="absolute w-3 h-3 bg-white border-2 border-blue-500 rounded-sm pointer-events-auto cursor-nwse-resize"
|
class="absolute w-3 h-3 bg-white border-2 border-blue-500 rounded-sm pointer-events-auto cursor-nwse-resize touch-none before:absolute before:-inset-3 before:content-['']"
|
||||||
:style="{ left: (size?.width ?? coords.width) - 6 + 'px', top: (size?.height ?? coords.height) - 6 + 'px' }"
|
:style="{ left: (size?.width ?? coords.width) - 6 + 'px', top: (size?.height ?? coords.height) - 6 + 'px' }"
|
||||||
:title="t('resize')"
|
:title="t('resize')"
|
||||||
@mousedown.prevent.stop="startResize"
|
@pointerdown.prevent.stop="startResize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -89,9 +76,6 @@ const IconSplitCell = (props) => h('svg', { xmlns: 'http://www.w3.org/2000/svg',
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DynamicBlockMenu',
|
name: 'DynamicBlockMenu',
|
||||||
components: {
|
|
||||||
IconTrash
|
|
||||||
},
|
|
||||||
inject: ['t'],
|
inject: ['t'],
|
||||||
props: {
|
props: {
|
||||||
coords: {
|
coords: {
|
||||||
@@ -102,34 +86,55 @@ export default {
|
|||||||
emits: ['command', 'resize'],
|
emits: ['command', 'resize'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
size: null
|
size: null,
|
||||||
|
toolbarHeight: 38
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
alignIcons () {
|
actionRows () {
|
||||||
return {
|
const alignActions = [
|
||||||
left: IconAlignLeft,
|
{ command: 'setBlockAlign', arg: 'left', icon: IconAlignLeft, title: 'align_left', active: this.coords.align === 'left' },
|
||||||
center: IconAlignCenter,
|
{ command: 'setBlockAlign', arg: 'center', icon: IconAlignCenter, title: 'align_center', active: this.coords.align === 'center' },
|
||||||
right: IconAlignRight
|
{ command: 'setBlockAlign', arg: 'right', icon: IconAlignRight, title: 'align_right', active: this.coords.align === 'right' }
|
||||||
|
]
|
||||||
|
|
||||||
|
if (this.coords.type !== 'table') {
|
||||||
|
return [[alignActions, [{ command: 'deleteSelection', icon: IconTrash, title: 'delete_image', danger: true }]]]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
tableActions () {
|
|
||||||
return [
|
return [
|
||||||
{ command: 'addTableRow', arg: false, icon: IconRowInsertTop, title: 'insert_row_above', structural: true },
|
[
|
||||||
{ command: 'addTableRow', arg: true, icon: IconRowInsertBottom, title: 'insert_row_below', structural: true },
|
alignActions,
|
||||||
{ command: 'addTableColumn', arg: false, icon: IconColumnInsertLeft, title: 'insert_column_left', structural: true },
|
[
|
||||||
{ command: 'addTableColumn', arg: true, icon: IconColumnInsertRight, title: 'insert_column_right', structural: true },
|
{ command: 'addTableRow', arg: false, icon: IconRowInsertTop, title: 'insert_row_above', structural: true },
|
||||||
{ command: 'mergeTableCells', icon: IconMergeCells, title: 'merge_cells', structural: true, enabled: 'canMerge' },
|
{ command: 'addTableRow', arg: true, icon: IconRowInsertBottom, title: 'insert_row_below', structural: true },
|
||||||
{ command: 'splitTableCell', icon: IconSplitCell, title: 'split_cell', structural: true, enabled: 'canSplit' },
|
{ command: 'addTableColumn', arg: false, icon: IconColumnInsertLeft, title: 'insert_column_left', structural: true },
|
||||||
{ command: 'deleteTableRow', icon: IconRowRemove, title: 'delete_row', danger: true, structural: true },
|
{ command: 'addTableColumn', arg: true, icon: IconColumnInsertRight, title: 'insert_column_right', structural: true }
|
||||||
{ command: 'deleteTableColumn', icon: IconColumnRemove, title: 'delete_column', danger: true, structural: true },
|
]
|
||||||
{ command: 'deleteTable', icon: IconTrash, title: 'delete_table', danger: true }
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{ command: 'mergeTableCells', icon: IconMergeCells, title: 'merge_cells', structural: true, enabled: 'canMerge' },
|
||||||
|
{ command: 'splitTableCell', icon: IconSplitCell, title: 'split_cell', structural: true, enabled: 'canSplit' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ command: 'deleteTableRow', icon: IconRowRemove, title: 'delete_row', danger: true, structural: true },
|
||||||
|
{ command: 'deleteTableColumn', icon: IconColumnRemove, title: 'delete_column', danger: true, structural: true },
|
||||||
|
{ command: 'deleteTable', icon: IconTrash, title: 'delete_table', danger: true }
|
||||||
|
]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
this.toolbarHeight = this.$refs.toolbar.offsetHeight
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
this.toolbarHeight = this.$refs.toolbar.offsetHeight
|
||||||
|
},
|
||||||
beforeUnmount () {
|
beforeUnmount () {
|
||||||
window.removeEventListener('mousemove', this.onResizeMove)
|
window.removeEventListener('pointermove', this.onResizeMove)
|
||||||
window.removeEventListener('mouseup', this.onResizeEnd)
|
window.removeEventListener('pointerup', this.onResizeEnd)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isDisabled (action) {
|
isDisabled (action) {
|
||||||
@@ -139,8 +144,8 @@ export default {
|
|||||||
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 }
|
||||||
this.size = { width: this.coords.width, height: this.coords.height }
|
this.size = { width: this.coords.width, height: this.coords.height }
|
||||||
|
|
||||||
window.addEventListener('mousemove', this.onResizeMove)
|
window.addEventListener('pointermove', this.onResizeMove)
|
||||||
window.addEventListener('mouseup', this.onResizeEnd)
|
window.addEventListener('pointerup', this.onResizeEnd)
|
||||||
},
|
},
|
||||||
onResizeMove (event) {
|
onResizeMove (event) {
|
||||||
const { x, y, width, height } = this.resizeStart
|
const { x, y, width, height } = this.resizeStart
|
||||||
@@ -150,8 +155,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onResizeEnd () {
|
onResizeEnd () {
|
||||||
window.removeEventListener('mousemove', this.onResizeMove)
|
window.removeEventListener('pointermove', this.onResizeMove)
|
||||||
window.removeEventListener('mouseup', this.onResizeEnd)
|
window.removeEventListener('pointerup', this.onResizeEnd)
|
||||||
|
|
||||||
if (this.size.width !== this.coords.width || this.size.height !== this.coords.height) {
|
if (this.size.width !== this.coords.width || this.size.height !== this.coords.height) {
|
||||||
this.$emit('resize', this.size)
|
this.$emit('resize', this.size)
|
||||||
|
|||||||
@@ -104,15 +104,24 @@ img.ProseMirror-separator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-resize-handle] {
|
[data-resize-handle] {
|
||||||
|
--handle-size: calc(10px / var(--zoom, 1));
|
||||||
display: none;
|
display: none;
|
||||||
width: 10px;
|
width: var(--handle-size);
|
||||||
height: 10px;
|
height: var(--handle-size);
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border: 2px solid #3b82f6;
|
border: calc(2px / var(--zoom, 1)) solid #3b82f6;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (pointer: coarse) {
|
||||||
|
[data-resize-handle]::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: calc(-8px / var(--zoom, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ProseMirror-selectednode [data-resize-wrapper] {
|
.ProseMirror-selectednode [data-resize-wrapper] {
|
||||||
outline: 2px solid #3b82f6;
|
outline: 2px solid #3b82f6;
|
||||||
}
|
}
|
||||||
@@ -121,10 +130,10 @@ img.ProseMirror-separator {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-resize-handle="top-left"] { margin: -5px 0 0 -5px; cursor: nwse-resize; }
|
[data-resize-handle="top-left"] { margin: calc(var(--handle-size) / -2) 0 0 calc(var(--handle-size) / -2); cursor: nwse-resize; }
|
||||||
[data-resize-handle="top-right"] { margin: -5px -5px 0 0; cursor: nesw-resize; }
|
[data-resize-handle="top-right"] { margin: calc(var(--handle-size) / -2) calc(var(--handle-size) / -2) 0 0; cursor: nesw-resize; }
|
||||||
[data-resize-handle="bottom-left"] { margin: 0 0 -5px -5px; cursor: nesw-resize; }
|
[data-resize-handle="bottom-left"] { margin: 0 0 calc(var(--handle-size) / -2) calc(var(--handle-size) / -2); cursor: nesw-resize; }
|
||||||
[data-resize-handle="bottom-right"] { margin: 0 -5px -5px 0; cursor: nwse-resize; }
|
[data-resize-handle="bottom-right"] { margin: 0 calc(var(--handle-size) / -2) calc(var(--handle-size) / -2) 0; cursor: nwse-resize; }
|
||||||
|
|
||||||
.ProseMirror .selectedCell {
|
.ProseMirror .selectedCell {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<div class="flex items-center gap-0.5">
|
<div class="flex items-center gap-0.5">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<button
|
<button
|
||||||
class="flex items-center justify-between gap-1 w-36 h-7 px-2 border border-neutral-200 rounded-md hover:bg-neutral-100"
|
class="flex items-center justify-between gap-1 w-24 sm:w-36 h-7 px-2 border border-neutral-200 rounded-md hover:bg-neutral-100"
|
||||||
:title="t('font')"
|
:title="t('font')"
|
||||||
@click="toggleDropdown('font')"
|
@click="toggleDropdown('font')"
|
||||||
>
|
>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<button
|
<button
|
||||||
class="flex items-center justify-between gap-1 w-16 h-7 px-2 border border-neutral-200 rounded-md hover:bg-neutral-100"
|
class="flex items-center justify-between gap-1 w-12 sm:w-16 h-7 px-2 border border-neutral-200 rounded-md hover:bg-neutral-100"
|
||||||
:title="t('font_size')"
|
:title="t('font_size')"
|
||||||
@click="toggleDropdown('size')"
|
@click="toggleDropdown('size')"
|
||||||
>
|
>
|
||||||
@@ -305,9 +305,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="!isCellSelection">
|
<template v-if="!isCellSelection">
|
||||||
<div class="w-px h-5 bg-neutral-200 mx-1" />
|
<div class="w-px h-5 bg-neutral-200 mx-1 sm:mx-0.5" />
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center justify-center text-xs h-7 rounded-md hover:bg-neutral-100"
|
class="inline-flex items-center justify-center text-xs h-7 px-1 rounded-md hover:bg-neutral-100"
|
||||||
:title="t('create_variable')"
|
:title="t('create_variable')"
|
||||||
@click="wrapVariable"
|
@click="wrapVariable"
|
||||||
>
|
>
|
||||||
@@ -316,12 +316,12 @@
|
|||||||
:height="16"
|
:height="16"
|
||||||
:stroke-width="1.6"
|
:stroke-width="1.6"
|
||||||
/>
|
/>
|
||||||
<span class="px-0.5">
|
<span class="hidden sm:inline px-0.5">
|
||||||
{{ t('variable') }}
|
{{ t('variable') }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center justify-center text-xs h-7 rounded-md hover:bg-neutral-100"
|
class="inline-flex items-center justify-center text-xs h-7 px-1 rounded-md hover:bg-neutral-100"
|
||||||
:title="t('create_condition')"
|
:title="t('create_condition')"
|
||||||
@click="wrapCondition"
|
@click="wrapCondition"
|
||||||
>
|
>
|
||||||
@@ -348,7 +348,7 @@
|
|||||||
font-family="ui-sans-serif, system-ui, sans-serif"
|
font-family="ui-sans-serif, system-ui, sans-serif"
|
||||||
>if</text>
|
>if</text>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="px-0.5">
|
<span class="hidden sm:inline px-0.5">
|
||||||
{{ t('condition') }}
|
{{ t('condition') }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
class="absolute pointer-events-none z-10 bg-black"
|
class="absolute pointer-events-none z-10 bg-black"
|
||||||
:style="{ width: '1px', height: cursorHighlightCoords.height + 'px', left: cursorHighlightCoords.x + 'px', top: cursorHighlightCoords.y + 'px' }"
|
:style="{ width: '1px', height: cursorHighlightCoords.height + 'px', left: cursorHighlightCoords.x + 'px', top: cursorHighlightCoords.y + 'px' }"
|
||||||
/>
|
/>
|
||||||
<div :style="{ zoom: containerWidth / sectionWidthPx }">
|
<div :style="{ zoom, '--zoom': zoom }">
|
||||||
<section
|
<section
|
||||||
:id="section.id"
|
:id="section.id"
|
||||||
ref="editorElement"
|
ref="editorElement"
|
||||||
|
|||||||
Reference in new issue
Block a user