summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue
diff options
context:
space:
mode:
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.vue142
1 files changed, 142 insertions, 0 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
new file mode 100644
index 00000000000..c44e8145982
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/wrappers/table_cell_base.vue
@@ -0,0 +1,142 @@
+<script>
+import { GlDropdown, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
+import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2';
+import { selectedRect as getSelectedRect } from 'prosemirror-tables';
+import { __ } from '~/locale';
+
+const TABLE_CELL_HEADER = 'th';
+const TABLE_CELL_BODY = 'td';
+
+export default {
+ name: 'TableCellBaseWrapper',
+ components: {
+ NodeViewWrapper,
+ NodeViewContent,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownDivider,
+ },
+ props: {
+ cellType: {
+ type: String,
+ validator: (type) => [TABLE_CELL_HEADER, TABLE_CELL_BODY].includes(type),
+ required: true,
+ },
+ editor: {
+ type: Object,
+ required: true,
+ },
+ getPos: {
+ type: Function,
+ required: true,
+ },
+ },
+ 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;
+ },
+ },
+ mounted() {
+ this.editor.on('selectionUpdate', this.handleSelectionUpdate);
+ this.handleSelectionUpdate();
+ },
+ beforeDestroy() {
+ this.editor.off('selectionUpdate', this.handleSelectionUpdate);
+ },
+ methods: {
+ handleSelectionUpdate() {
+ const { state } = this.editor;
+ const { $cursor } = state.selection;
+
+ this.displayActionsDropdown = $cursor?.pos - $cursor?.parentOffset - 1 === this.getPos();
+ if (this.displayActionsDropdown) {
+ this.selectedRect = getSelectedRect(state);
+ }
+ },
+ runCommand(command) {
+ this.editor.chain()[command]().run();
+ this.hideDropdown();
+ },
+ handleHide($event) {
+ if (this.preventHide) {
+ $event.preventDefault();
+ }
+ this.preventHide = true;
+ },
+ hideDropdown() {
+ this.preventHide = false;
+ this.$refs.dropdown?.hide();
+ },
+ },
+ 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'),
+ },
+};
+</script>
+<template>
+ <node-view-wrapper
+ class="gl-relative gl-padding-5 gl-min-w-10"
+ :as="cellType"
+ @click="hideDropdown"
+ >
+ <span v-if="displayActionsDropdown" class="gl-absolute gl-right-0 gl-top-0">
+ <gl-dropdown
+ ref="dropdown"
+ dropup
+ icon="chevron-down"
+ size="small"
+ category="tertiary"
+ boundary="viewport"
+ no-caret
+ text-sr-only
+ :text="$options.i18n.editTableActions"
+ :popper-opts="{ positionFixed: true }"
+ @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>
+ </span>
+ <node-view-content />
+ </node-view-wrapper>
+</template>