UNPKG

@atlaskit/editor-plugin-card

Version:

Card plugin for @atlaskit/editor-core

253 lines (248 loc) 10.8 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; /** * @jsxRuntime classic * @jsx jsx */ import React from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports import { jsx } from '@emotion/react'; import ReactNodeView from '@atlaskit/editor-common/react-node-view'; import { DATASOURCE_INNER_CONTAINER_CLASSNAME, SmartCardSharedCssClassName } from '@atlaskit/editor-common/styles'; import { UnsupportedInline } from '@atlaskit/editor-common/ui'; import { calcBreakoutWidth } from '@atlaskit/editor-common/utils'; import { DatasourceTableView } from '@atlaskit/link-datasource'; import { EditorSmartCardProvider, EditorSmartCardProviderValueGuard } from '@atlaskit/link-provider'; import { DATASOURCE_DEFAULT_LAYOUT } from '@atlaskit/linking-common'; import { DatasourceErrorBoundary } from '../ui/datasourceErrorBoundary'; import { EditorAnalyticsContext } from '../ui/EditorAnalyticsContext'; const getPosSafely = pos => { if (!pos || typeof pos === 'boolean') { return; } try { return pos(); } catch (e) { // Can blow up in rare cases, when node has been removed. } }; // eslint-disable-next-line @repo/internal/react/no-class-components export class DatasourceComponent extends React.PureComponent { constructor(props) { super(props); _defineProperty(this, "getDatasource", () => this.props.node.attrs.datasource); _defineProperty(this, "getTableView", () => { const views = this.getDatasource().views; return views.find(view => view.type === 'table') || undefined; }); _defineProperty(this, "handleColumnChange", columnKeys => { const { wrappedColumnKeys = [], columnCustomSizes = {} } = this.getColumnsInfo(); this.updateTableProperties(columnKeys, columnCustomSizes, wrappedColumnKeys); }); _defineProperty(this, "handleColumnResize", (key, width) => { const { wrappedColumnKeys = [], columnCustomSizes = {}, visibleColumnKeys = [] } = this.getColumnsInfo(); const newColumnCustomSizes = { ...columnCustomSizes, [key]: width }; this.updateTableProperties(visibleColumnKeys, newColumnCustomSizes, wrappedColumnKeys); }); _defineProperty(this, "handleWrappedColumnChange", (key, shouldWrap) => { const { wrappedColumnKeys = [], columnCustomSizes = {}, visibleColumnKeys = [] } = this.getColumnsInfo(); const wrappedColumnKeysSet = new Set(wrappedColumnKeys); if (shouldWrap) { wrappedColumnKeysSet.add(key); } else { wrappedColumnKeysSet.delete(key); } this.updateTableProperties(visibleColumnKeys, columnCustomSizes, Array.from(wrappedColumnKeysSet)); }); _defineProperty(this, "onError", ({ err }) => { if (err) { throw err; } }); } updateTableProperties(columnKeysArg, columnCustomSizes, wrappedColumnKeys) { const { state, dispatch } = this.props.view; const pos = getPosSafely(this.props.getPos); if (pos === undefined) { return; } // In case for some reason there are no visible keys stored in ADF, we take them // from incoming sets of attributes like column sizes and wrapped column keys // columnKeys are needed to update ADF ( // since attributes (like custom width and wrapped state) only make sense for a visible column ) // So this part effectively adds a visible column if it wasn't there but attributes were given. const columnKeys = columnKeysArg.length > 0 ? columnKeysArg : Array.from(new Set([...Object.keys(columnCustomSizes), ...wrappedColumnKeys])); const views = [{ type: 'table', properties: { columns: columnKeys.map(key => { const width = columnCustomSizes[key]; const isWrapped = wrappedColumnKeys.includes(key); return { key, ...(width ? { width } : {}), ...(isWrapped ? { isWrapped } : {}) }; }) } }]; const attrs = this.props.node.attrs; const tr = state.tr.setNodeMarkup(pos, undefined, { ...attrs, datasource: { ...attrs.datasource, views } }); // Ensures dispatch does not contribute to undo history (otherwise user requires three undo's to revert table) tr.setMeta('addToHistory', false); tr.setMeta('scrollIntoView', false); dispatch(tr); } getColumnsInfo() { var _tableView$properties; const tableView = this.getTableView(); const columnsProp = tableView === null || tableView === void 0 ? void 0 : (_tableView$properties = tableView.properties) === null || _tableView$properties === void 0 ? void 0 : _tableView$properties.columns; const visibleColumnKeys = columnsProp === null || columnsProp === void 0 ? void 0 : columnsProp.map(({ key }) => key); let columnCustomSizes; const columnsWithWidth = columnsProp === null || columnsProp === void 0 ? void 0 : columnsProp.filter(c => !!c.width); if (columnsWithWidth) { const keyWidthPairs = columnsWithWidth.map(c => [c.key, c.width]); columnCustomSizes = Object.fromEntries(keyWidthPairs); } const wrappedColumnKeys = columnsProp === null || columnsProp === void 0 ? void 0 : columnsProp.filter(c => c.isWrapped).map(c => c.key); return { visibleColumnKeys, columnCustomSizes, wrappedColumnKeys }; } render() { const datasource = this.getDatasource(); const attrs = this.props.node.attrs; const tableView = this.getTableView(); if (tableView) { const { visibleColumnKeys, columnCustomSizes, wrappedColumnKeys } = this.getColumnsInfo(); return jsx(EditorSmartCardProviderValueGuard, null, jsx(EditorAnalyticsContext, { editorView: this.props.view }, jsx(EditorSmartCardProvider, null, jsx(DatasourceTableView, { datasourceId: datasource.id, parameters: datasource.parameters, visibleColumnKeys: visibleColumnKeys, onVisibleColumnKeysChange: this.handleColumnChange, url: attrs === null || attrs === void 0 ? void 0 : attrs.url, onColumnResize: this.handleColumnResize, columnCustomSizes: columnCustomSizes, onWrappedColumnChange: this.handleWrappedColumnChange, wrappedColumnKeys: wrappedColumnKeys })))); } return null; } } export class Datasource extends ReactNodeView { constructor(props) { var _props$pluginInjectio, _props$pluginInjectio2, _sharedState$currentS; super(props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props); const sharedState = props === null || props === void 0 ? void 0 : (_props$pluginInjectio = props.pluginInjectionApi) === null || _props$pluginInjectio === void 0 ? void 0 : (_props$pluginInjectio2 = _props$pluginInjectio.width) === null || _props$pluginInjectio2 === void 0 ? void 0 : _props$pluginInjectio2.sharedState; this.tableWidth = sharedState === null || sharedState === void 0 ? void 0 : (_sharedState$currentS = sharedState.currentState()) === null || _sharedState$currentS === void 0 ? void 0 : _sharedState$currentS.width; this.isNodeNested = props.isNodeNested; sharedState === null || sharedState === void 0 ? void 0 : sharedState.onChange(({ nextSharedState }) => { if (nextSharedState !== null && nextSharedState !== void 0 && nextSharedState.width && this.tableWidth !== (nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.width)) { this.tableWidth = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.width; this.update(this.node, []); // required to update the width when page is resized. } }); } // Need this function to check if the datasource attribute was added or not to a blockCard. // If not, we return false so we can get the node to re-render properly as a block node instead. // Otherwise, the node view will still consider the node as a Datasource and render a such. validUpdate(_, newNode) { var _newNode$attrs; return !!((_newNode$attrs = newNode.attrs) !== null && _newNode$attrs !== void 0 && _newNode$attrs.datasource); } update(node, decorations, _innerDecorations) { return super.update(node, decorations, _innerDecorations, this.validUpdate); } createDomRef() { const domRef = document.createElement('div'); domRef.setAttribute('contenteditable', 'true'); domRef.classList.add(SmartCardSharedCssClassName.DATASOURCE_CONTAINER); return domRef; } /** * Node views may render interactive elements that should not have their events reach editor * * We should the stop editor from handling events from following elements: * - typing in input * - activating buttons via spacebar * * Can be used to prevent the editor view from trying to handle some or all DOM events that bubble up from the node view. * Events for which this returns true are not handled by the editor. * @see {@link https://prosemirror.net/docs/ref/#view.NodeView.stopEvent} */ stopEvent(event) { const isFormElement = [HTMLButtonElement, HTMLInputElement].some(element => event.target instanceof element); if (isFormElement) { return true; } return false; } render() { var _this$domRef, _attrs$datasource; const { attrs } = this.node; // EDM-10607: Workaround to remove datasource table draggable attribute // @ts-ignore TS2341: Property domRef is private (_this$domRef = this.domRef) === null || _this$domRef === void 0 ? void 0 : _this$domRef.setAttribute('draggable', 'false'); return jsx(DatasourceErrorBoundary, { unsupportedComponent: UnsupportedInline, view: this.view, url: attrs.url, datasourceId: attrs === null || attrs === void 0 ? void 0 : (_attrs$datasource = attrs.datasource) === null || _attrs$datasource === void 0 ? void 0 : _attrs$datasource.id }, jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: DATASOURCE_INNER_CONTAINER_CLASSNAME, style: { minWidth: this.isNodeNested ? '100%' : // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 calcBreakoutWidth(attrs.layout || DATASOURCE_DEFAULT_LAYOUT, this.tableWidth) } }, jsx(DatasourceComponent, { node: this.node, view: this.view, getPos: this.getPos }))); } }