import React from 'react'; import { tableEditing } from '@atlaskit/editor-tables/pm-plugins'; import { createTable } from '@atlaskit/editor-tables/utils'; import { table, tableWithLocalId, tableCell, tableHeader, tableRow, } from '@atlaskit/adf-schema'; import { toggleTable, tooltip } from '../../keymaps'; import { EditorPlugin } from '../../types'; import WithPluginState from '../../ui/WithPluginState'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, addAnalytics, EVENT_TYPE, INPUT_METHOD, } from '../analytics'; import { messages } from '../insert-block/ui/ToolbarInsertBlock/messages'; import { IconTable } from '../quick-insert/assets'; import { pluginConfig } from './create-plugin-config'; import { createPlugin as createTableLocalIdPlugin } from './pm-plugins/table-local-id'; import { createPlugin as createDecorationsPlugin } from './pm-plugins/decorations/plugin'; import { keymapPlugin } from './pm-plugins/keymap'; import { tableSelectionKeymapPlugin } from './pm-plugins/table-selection-keymap'; import { createPlugin } from './pm-plugins/main'; import { pluginKey } from './pm-plugins/plugin-factory'; import { createPlugin as createStickyHeadersPlugin, findStickyHeaderForTable, pluginKey as stickyHeadersPluginKey, } from './pm-plugins/sticky-headers'; import { createPlugin as createFlexiResizingPlugin, pluginKey as tableResizingPluginKey, } from './pm-plugins/table-resizing'; import { getToolbarConfig } from './toolbar'; import { ColumnResizingPluginState, PluginConfig } from './types'; import FloatingContextualButton from './ui/FloatingContextualButton'; import FloatingContextualMenu from './ui/FloatingContextualMenu'; import FloatingDeleteButton from './ui/FloatingDeleteButton'; import FloatingInsertButton from './ui/FloatingInsertButton'; import LayoutButton from './ui/LayoutButton'; import { isLayoutSupported } from './utils'; import { getFeatureFlags } from '../feature-flags-context'; import { ErrorBoundary } from '../../ui/ErrorBoundary'; interface TablePluginOptions { tableOptions: PluginConfig; dynamicSizingEnabled?: boolean; breakoutEnabled?: boolean; allowContextualMenu?: boolean; // TODO these two need to be rethought fullWidthEnabled?: boolean; wasFullWidthEnabled?: boolean; allowLocalIdGeneration?: boolean; } const tablesPlugin = (options?: TablePluginOptions): EditorPlugin => ({ name: 'table', nodes() { return [ { name: 'table', node: options?.allowLocalIdGeneration ? tableWithLocalId : table, }, { name: 'tableHeader', node: tableHeader }, { name: 'tableRow', node: tableRow }, { name: 'tableCell', node: tableCell }, ]; }, pmPlugins() { return [ { name: 'table', plugin: ({ dispatch, portalProviderAPI, eventDispatcher }) => { const { dynamicSizingEnabled, fullWidthEnabled, wasFullWidthEnabled, breakoutEnabled, tableOptions, allowLocalIdGeneration, } = options || ({} as TablePluginOptions); return createPlugin( dispatch, portalProviderAPI, eventDispatcher, pluginConfig(tableOptions), breakoutEnabled && dynamicSizingEnabled, breakoutEnabled, fullWidthEnabled, wasFullWidthEnabled, allowLocalIdGeneration, ); }, }, { name: 'tablePMColResizing', plugin: ({ dispatch }) => { const { dynamicSizingEnabled, fullWidthEnabled, tableOptions } = options || ({} as TablePluginOptions); const { allowColumnResizing } = pluginConfig(tableOptions); return allowColumnResizing ? createFlexiResizingPlugin(dispatch, { dynamicTextSizing: dynamicSizingEnabled && !fullWidthEnabled, lastColumnResizable: !fullWidthEnabled, } as ColumnResizingPluginState) : undefined; }, }, { name: 'tableEditing', plugin: () => createDecorationsPlugin() }, // Needs to be lower priority than editor-tables.tableEditing // plugin as it is currently swallowing backspace events inside tables { name: 'tableKeymap', plugin: () => keymapPlugin(options?.allowLocalIdGeneration), }, { name: 'tableSelectionKeymap', plugin: () => tableSelectionKeymapPlugin(), }, { name: 'tableEditing', plugin: () => tableEditing() }, { name: 'tableStickyHeaders', plugin: ({ dispatch, eventDispatcher }) => options && options.tableOptions.stickyHeaders ? createStickyHeadersPlugin(dispatch, eventDispatcher) : undefined, }, { name: 'tableLocalId', plugin: ({ dispatch }) => options && options?.allowLocalIdGeneration ? createTableLocalIdPlugin(dispatch) : undefined, }, ]; }, contentComponent({ editorView, popupsMountPoint, popupsBoundariesElement, popupsScrollableElement, dispatchAnalyticsEvent, }) { return ( { const { state } = editorView; const isDragging = resizingPluginState?.dragging; const { tableNode, tablePos, targetCellPosition, isContextualMenuOpen, layout, tableRef, pluginConfig, insertColumnButtonIndex, insertRowButtonIndex, isHeaderColumnEnabled, isHeaderRowEnabled, tableWrapperTarget, tableWidth, } = tablePluginState!; const { allowControls } = pluginConfig; const { tableRenderOptimization } = getFeatureFlags(state) || {}; const stickyHeader = stickyHeadersState ? findStickyHeaderForTable(stickyHeadersState, tablePos) : undefined; return ( <> {targetCellPosition && tableRef && !isDragging && options && options.allowContextualMenu && ( )} {allowControls && ( )} {allowControls && ( )} {isLayoutSupported(state) && options && options.breakoutEnabled && ( )} ); }} /> ); }, pluginsOptions: { quickInsert: ({ formatMessage }) => [ { id: 'table', title: formatMessage(messages.table), description: formatMessage(messages.tableDescription), keywords: ['cell'], priority: 600, keyshortcut: tooltip(toggleTable), icon: () => , action(insert, state) { const tr = insert( createTable({ schema: state.schema, allowLocalId: options?.allowLocalIdGeneration, }), ); return addAnalytics(state, tr, { action: ACTION.INSERTED, actionSubject: ACTION_SUBJECT.DOCUMENT, actionSubjectId: ACTION_SUBJECT_ID.TABLE, attributes: { inputMethod: INPUT_METHOD.QUICK_INSERT }, eventType: EVENT_TYPE.TRACK, }); }, }, ], floatingToolbar: getToolbarConfig(pluginConfig(options?.tableOptions)), }, }); export default tablesPlugin;