diff options
Diffstat (limited to 'app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue')
-rw-r--r-- | app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue | 156 |
1 files changed, 84 insertions, 72 deletions
diff --git a/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue b/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue index 5624bae34c2..44f5a2895fd 100644 --- a/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue +++ b/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue @@ -1,22 +1,71 @@ <script> -import { GlDropdown, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui'; +import { GlDisclosureDropdown } from '@gitlab/ui'; import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'; import { selectedRect as getSelectedRect } from '@tiptap/pm/tables'; -import { __ } from '~/locale'; +import { __, n__ } from '~/locale'; const TABLE_CELL_HEADER = 'th'; const TABLE_CELL_BODY = 'td'; +function getDropdownItems({ selectedRect, cellType, rowspan = 1, colspan = 1 }) { + const totalRows = selectedRect?.map.height; + const totalCols = selectedRect?.map.width; + const isTableBodyCell = cellType === TABLE_CELL_BODY; + const selectedRows = selectedRect ? selectedRect.bottom - selectedRect.top : 0; + const selectedCols = selectedRect ? selectedRect.right - selectedRect.left : 0; + const showSplitCellOption = + selectedRows === rowspan && selectedCols === colspan && (rowspan > 1 || colspan > 1); + const showMergeCellsOption = selectedRows !== rowspan || selectedCols !== colspan; + const numCellsToMerge = (selectedRows - rowspan + 1) * (selectedCols - colspan + 1); + const showDeleteRowOption = totalRows > selectedRows + 1 && isTableBodyCell; + const showDeleteColumnOption = totalCols > selectedCols; + + return [ + { + items: [ + { text: __('Insert column before'), value: 'addColumnBefore' }, + { text: __('Insert column after'), value: 'addColumnAfter' }, + isTableBodyCell && { text: __('Insert row before'), value: 'addRowBefore' }, + { text: __('Insert row after'), value: 'addRowAfter' }, + ].filter(Boolean), + }, + { + items: [ + showSplitCellOption && { text: __('Split cell'), value: 'splitCell' }, + showMergeCellsOption && { + text: n__('Merge %d cell', 'Merge %d cells', numCellsToMerge), + value: 'mergeCells', + }, + ].filter(Boolean), + }, + { + items: [ + showDeleteRowOption && { + text: n__('Delete row', 'Delete %d rows', selectedRows), + value: 'deleteRow', + }, + showDeleteColumnOption && { + text: n__('Delete column', 'Delete %d columns', selectedCols), + value: 'deleteColumn', + }, + { text: __('Delete table'), value: 'deleteTable' }, + ].filter(Boolean), + }, + ].filter(({ items }) => items.length); +} + export default { name: 'TableCellBaseWrapper', components: { NodeViewWrapper, NodeViewContent, - GlDropdown, - GlDropdownItem, - GlDropdownDivider, + GlDisclosureDropdown, }, props: { + getPos: { + type: Function, + required: true, + }, cellType: { type: String, validator: (type) => [TABLE_CELL_HEADER, TABLE_CELL_BODY].includes(type), @@ -34,19 +83,17 @@ export default { data() { return { displayActionsDropdown: false, - preventHide: true, selectedRect: null, }; }, computed: { - totalRows() { - return this.selectedRect?.map.height; - }, - totalCols() { - return this.selectedRect?.map.width; - }, - isTableBodyCell() { - return this.cellType === TABLE_CELL_BODY; + dropdownItems() { + return getDropdownItems({ + selectedRect: this.selectedRect, + cellType: this.cellType, + rowspan: this.node.attrs.rowspan, + colspan: this.node.attrs.colspan, + }); }, }, mounted() { @@ -61,6 +108,13 @@ export default { const { state } = this.editor; const { $cursor } = state.selection; + try { + this.selectedRect = getSelectedRect(state); + } catch (e) { + // ignore error if the selection is not in a table + return; + } + if (!$cursor) return; this.displayActionsDropdown = false; @@ -71,54 +125,34 @@ export default { break; } } - - if (this.displayActionsDropdown) { - this.selectedRect = getSelectedRect(state); - } }, - runCommand(command) { - this.editor.chain()[command]().run(); + + runCommand({ value: command }) { this.hideDropdown(); + this.editor.chain()[command]().run(); }, - handleHide($event) { - if (this.preventHide) { - $event.preventDefault(); - } - this.preventHide = true; - }, + hideDropdown() { - this.preventHide = false; - this.$refs.dropdown?.hide(); + this.$refs.dropdown?.close(); }, }, - i18n: { - insertColumnBefore: __('Insert column before'), - insertColumnAfter: __('Insert column after'), - insertRowBefore: __('Insert row before'), - insertRowAfter: __('Insert row after'), - deleteRow: __('Delete row'), - deleteColumn: __('Delete column'), - deleteTable: __('Delete table'), - editTableActions: __('Edit table'), - }, - dropdownPopperOpts: { - positionFixed: true, - }, }; </script> <template> <node-view-wrapper - class="gl-relative gl-padding-5 gl-min-w-10" :as="cellType" + :rowspan="node.attrs.rowspan || 1" + :colspan="node.attrs.colspan || 1" dir="auto" + class="gl-m-0! gl-p-0! gl-relative" @click="hideDropdown" > <span v-if="displayActionsDropdown" contenteditable="false" - class="gl-absolute gl-right-0 gl-top-0" + class="gl-absolute gl-right-0 gl-top-0 gl-pr-1 gl-pt-1" > - <gl-dropdown + <gl-disclosure-dropdown ref="dropdown" dropup icon="chevron-down" @@ -127,34 +161,12 @@ export default { boundary="viewport" no-caret text-sr-only - :text="$options.i18n.editTableActions" - :popper-opts="$options.dropdownPopperOpts" - @hide="handleHide($event)" - > - <gl-dropdown-item @click="runCommand('addColumnBefore')"> - {{ $options.i18n.insertColumnBefore }} - </gl-dropdown-item> - <gl-dropdown-item @click="runCommand('addColumnAfter')"> - {{ $options.i18n.insertColumnAfter }} - </gl-dropdown-item> - <gl-dropdown-item v-if="isTableBodyCell" @click="runCommand('addRowBefore')"> - {{ $options.i18n.insertRowBefore }} - </gl-dropdown-item> - <gl-dropdown-item @click="runCommand('addRowAfter')"> - {{ $options.i18n.insertRowAfter }} - </gl-dropdown-item> - <gl-dropdown-divider /> - <gl-dropdown-item v-if="totalRows > 2 && isTableBodyCell" @click="runCommand('deleteRow')"> - {{ $options.i18n.deleteRow }} - </gl-dropdown-item> - <gl-dropdown-item v-if="totalCols > 1" @click="runCommand('deleteColumn')"> - {{ $options.i18n.deleteColumn }} - </gl-dropdown-item> - <gl-dropdown-item @click="runCommand('deleteTable')"> - {{ $options.i18n.deleteTable }} - </gl-dropdown-item> - </gl-dropdown> + :items="dropdownItems" + :toggle-text="__('Edit table')" + positioning-strategy="fixed" + @action="runCommand" + /> </span> - <node-view-content /> + <node-view-content as="div" class="gl-p-5 gl-min-w-10" /> </node-view-wrapper> </template> |