UNPKG

@atlaskit/editor-plugin-panel

Version:

Panel plugin for @atlaskit/editor-core.

136 lines (131 loc) 5.71 kB
import React from 'react'; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead import uuid from 'uuid/v4'; import { PanelType } from '@atlaskit/adf-schema'; import { Emoji } from '@atlaskit/editor-common/emoji'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { PanelErrorIcon, PanelInfoIcon, PanelNoteIcon, PanelSuccessIcon, PanelWarningIcon } from '@atlaskit/editor-common/icons'; import { PanelSharedCssClassName } from '@atlaskit/editor-common/panel'; import { DOMSerializer } from '@atlaskit/editor-prosemirror/model'; import { akEditorCustomIconSize } from '@atlaskit/editor-shared-styles/consts'; import LightbulbIcon from '@atlaskit/icon/core/lightbulb'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { panelAttrsToDom } from '../pm-plugins/utils/utils'; import { renderPanelIcon } from '../ui/renderPanelIcon'; /* eslint-disable @atlaskit/editor/no-re-export */ // Mapping export export const panelIcons = { info: PanelInfoIcon, success: PanelSuccessIcon, note: PanelNoteIcon, tip: LightbulbIcon, warning: PanelWarningIcon, error: PanelErrorIcon, custom: PanelInfoIcon }; /* eslint-enable @atlaskit/editor/no-re-export */ const selector = states => { return { emojiState: states.emojiState }; }; export const PanelIcon = props => { const { allowCustomPanel, providerFactory, pluginInjectionApi, panelAttributes: { panelType, panelIcon, panelIconId, panelIconText } } = props; const { emojiState } = useSharedPluginStateWithSelector(pluginInjectionApi, ['emoji'], selector); const emojiProvider = emojiState === null || emojiState === void 0 ? void 0 : emojiState.emojiProvider; if (allowCustomPanel && panelIcon && panelType === PanelType.CUSTOM) { return /*#__PURE__*/React.createElement(Emoji, { emojiProvider: emojiProvider, providers: providerFactory, shortName: panelIcon, id: panelIconId, fallback: panelIconText, showTooltip: false, allowTextFallback: false, fitToHeight: akEditorCustomIconSize }); } const Icon = panelIcons[panelType]; return Icon ? /*#__PURE__*/React.createElement(Icon, { label: `${panelType} panel` }) : null; }; class PanelNodeView { constructor(node, view, getPos, pluginOptions, api, nodeViewPortalProviderAPI, providerFactory) { this.nodeViewPortalProviderAPI = nodeViewPortalProviderAPI; this.providerFactory = providerFactory; this.pluginOptions = pluginOptions; this.view = view; this.node = node; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead this.key = uuid(); const { dom, contentDOM } = DOMSerializer.renderSpec(document, panelAttrsToDom(node.attrs, pluginOptions.allowCustomPanel || false)); this.getPos = getPos; // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting this.dom = dom; // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting this.contentDOM = contentDOM; // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting this.icon = this.dom.querySelector(`.${PanelSharedCssClassName.icon}`); if (!this.icon) { return; } // set contentEditable as false to be able to select the custom panels with keyboard this.icon.contentEditable = 'false'; const panelAttrs = node.attrs; // Determine if this is a standard panel type (info, note, success, warning, error) const isStandardPanel = panelAttrs.panelType && [PanelType.INFO, PanelType.NOTE, PanelType.SUCCESS, PanelType.WARNING, PanelType.ERROR].includes(panelAttrs.panelType); // For standard panels (info, note, success, warning, error), render icon directly as native DOM // This avoids Portal rendering delays that cause flickering on SSR and page transitions if (isStandardPanel && expValEquals('platform_editor_vc90_transition_panel_icon', 'isEnabled', true)) { renderPanelIcon(panelAttrs.panelType, this.icon); } else { this.nodeViewPortalProviderAPI.render(() => /*#__PURE__*/React.createElement(PanelIcon, { pluginInjectionApi: api, allowCustomPanel: pluginOptions.allowCustomPanel, panelAttributes: panelAttrs, providerFactory: this.providerFactory }), this.icon, this.key); } } ignoreMutation(mutation) { // ignore mutation if it caused by the icon. if (!this.icon) { return false; } const isIcon = mutation.target === this.icon || mutation.target.parentNode === this.icon; // ignore mutation if it caused by the lazy load emoji inside icon. const isInsideIcon = this.icon.contains(mutation.target); return isIcon || isInsideIcon; } destroy() { const panelAttrs = this.node.attrs; // Determine if this is a standard panel type (info, note, success, warning, error) const isStandardPanel = panelAttrs.panelType && [PanelType.INFO, PanelType.NOTE, PanelType.SUCCESS, PanelType.WARNING, PanelType.ERROR].includes(panelAttrs.panelType); // Only remove Portal if it was used (for custom emoji panels) if (!(isStandardPanel && expValEquals('platform_editor_vc90_transition_panel_icon', 'isEnabled', true))) { this.nodeViewPortalProviderAPI.remove(this.key); } } } export const getPanelNodeView = (pluginOptions, api, nodeViewPortalProviderAPI, providerFactory) => (node, view, getPos) => { return new PanelNodeView(node, view, getPos, pluginOptions, api, nodeViewPortalProviderAPI, providerFactory); };