UNPKG

@atlaskit/editor-plugin-placeholder

Version:

Placeholder plugin for @atlaskit/editor-core.

243 lines 8.49 kB
import { placeholderTextMessages as messages } from '@atlaskit/editor-common/messages'; import { bracketTyped, hasDocAsParent, isEmptyDocument, isEmptyParagraph } from '@atlaskit/editor-common/utils'; import { findParentNode } from '@atlaskit/editor-prosemirror/utils'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { pluginKey } from '../placeholderPlugin'; import { createLongEmptyNodePlaceholderADF, createShortEmptyNodePlaceholderADF } from './adf-builders'; import { nodeTypesWithLongPlaceholderText, nodeTypesWithShortPlaceholderText, nodeTypesWithSyncBlockPlaceholderText } from './constants'; export function getPlaceholderState(editorState) { return pluginKey.getState(editorState); } export function setPlaceHolderState({ placeholderText, pos, placeholderPrompts, typedAndDeleted, userHadTyped, canShowOnEmptyParagraph, showOnEmptyParagraph, contextPlaceholderADF }) { return { hasPlaceholder: true, placeholderText, placeholderPrompts, contextPlaceholderADF, pos: pos ? pos : 1, typedAndDeleted, userHadTyped, canShowOnEmptyParagraph, showOnEmptyParagraph }; } export const emptyPlaceholder = ({ placeholderText, placeholderPrompts, userHadTyped, pos, canShowOnEmptyParagraph, showOnEmptyParagraph }) => ({ hasPlaceholder: false, placeholderText, placeholderPrompts, userHadTyped, typedAndDeleted: false, canShowOnEmptyParagraph, showOnEmptyParagraph, pos }); export function createPlaceHolderStateFrom({ isInitial, isEditorFocused, editorState, isTypeAheadOpen, defaultPlaceholderText, intl, bracketPlaceholderText, emptyLinePlaceholder, placeholderADF, placeholderPrompts, typedAndDeleted, userHadTyped, isPlaceholderHidden, withEmptyParagraph, showOnEmptyParagraph }) { const shouldHidePlaceholder = isPlaceholderHidden; if (shouldHidePlaceholder) { return { ...emptyPlaceholder({ placeholderText: defaultPlaceholderText, placeholderPrompts, userHadTyped }), isPlaceholderHidden }; } if (isTypeAheadOpen !== null && isTypeAheadOpen !== void 0 && isTypeAheadOpen(editorState)) { return emptyPlaceholder({ placeholderText: defaultPlaceholderText, placeholderPrompts, userHadTyped }); } if ((defaultPlaceholderText || placeholderPrompts || placeholderADF) && isEmptyDocument(editorState.doc)) { return setPlaceHolderState({ placeholderText: defaultPlaceholderText, pos: 1, placeholderPrompts, typedAndDeleted, userHadTyped }); } if (withEmptyParagraph) { const { from, to, $to } = editorState.selection; const isOnEmptyParagraphInNonEmptyDoc = (defaultPlaceholderText || placeholderADF) && withEmptyParagraph && !isInitial && !isEmptyDocument(editorState.doc) && from === to && isEmptyParagraph($to.parent) && hasDocAsParent($to); if (isOnEmptyParagraphInNonEmptyDoc) { // If placeholder was already shown, keep it visible even without focus // This prevents the placeholder from disappearing when switching browser tabs if (showOnEmptyParagraph) { return setPlaceHolderState({ placeholderText: defaultPlaceholderText, pos: to, placeholderPrompts, typedAndDeleted, userHadTyped, canShowOnEmptyParagraph: true, showOnEmptyParagraph: true }); } // Focus is required to start the timeout for showing placeholder if (isEditorFocused) { return emptyPlaceholder({ placeholderText: defaultPlaceholderText, placeholderPrompts, userHadTyped, canShowOnEmptyParagraph: true, showOnEmptyParagraph: false, pos: to }); } } } if (isEditorFocused && editorExperiment('platform_editor_controls', 'variant1')) { var _parentNode$firstChil, _parentNode$firstChil2; const { $from, $to } = editorState.selection; if ($from.pos !== $to.pos) { return emptyPlaceholder({ placeholderText: defaultPlaceholderText, placeholderPrompts, userHadTyped }); } const parentNode = $from.node($from.depth - 1); const parentType = parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name; if (emptyLinePlaceholder && parentType === 'doc') { const isEmptyLine = isEmptyParagraph($from.parent); if (isEmptyLine) { return setPlaceHolderState({ placeholderText: emptyLinePlaceholder, pos: $from.pos, placeholderPrompts, typedAndDeleted, userHadTyped }); } } const isEmptyNode = (parentNode === null || parentNode === void 0 ? void 0 : parentNode.childCount) === 1 && ((_parentNode$firstChil = parentNode.firstChild) === null || _parentNode$firstChil === void 0 ? void 0 : _parentNode$firstChil.content.size) === 0 && ((_parentNode$firstChil2 = parentNode.firstChild) === null || _parentNode$firstChil2 === void 0 ? void 0 : _parentNode$firstChil2.type.name) === 'paragraph'; if (nodeTypesWithShortPlaceholderText.includes(parentType) && isEmptyNode) { var _table$node$firstChil; const table = findParentNode(node => node.type === editorState.schema.nodes.table)(editorState.selection); if (!table) { return emptyPlaceholder({ placeholderText: defaultPlaceholderText, placeholderPrompts, userHadTyped }); } const isFirstCell = (table === null || table === void 0 ? void 0 : (_table$node$firstChil = table.node.firstChild) === null || _table$node$firstChil === void 0 ? void 0 : _table$node$firstChil.content.firstChild) === parentNode; if (isFirstCell) { return setPlaceHolderState({ placeholderText: undefined, contextPlaceholderADF: createShortEmptyNodePlaceholderADF(intl), pos: $from.pos, placeholderPrompts, typedAndDeleted, userHadTyped }); } } if (nodeTypesWithLongPlaceholderText.includes(parentType) && isEmptyNode) { return setPlaceHolderState({ placeholderText: undefined, contextPlaceholderADF: createLongEmptyNodePlaceholderADF(intl), pos: $from.pos, placeholderPrompts, typedAndDeleted, userHadTyped }); } if (nodeTypesWithSyncBlockPlaceholderText.includes(parentType) && isEmptyNode && editorExperiment('platform_synced_block', true)) { return setPlaceHolderState({ placeholderText: intl.formatMessage(messages.sourceSyncBlockPlaceholderText), pos: $from.pos, placeholderPrompts, typedAndDeleted, userHadTyped }); } return emptyPlaceholder({ placeholderText: defaultPlaceholderText, placeholderPrompts, userHadTyped }); } if (bracketPlaceholderText && bracketTyped(editorState) && isEditorFocused) { const { $from } = editorState.selection; // Space is to account for positioning of the bracket const bracketHint = ' ' + bracketPlaceholderText; return setPlaceHolderState({ placeholderText: bracketHint, pos: $from.pos - 1, placeholderPrompts, typedAndDeleted, userHadTyped }); } return emptyPlaceholder({ placeholderText: defaultPlaceholderText, placeholderPrompts, userHadTyped }); } export function calculateUserInteractionState({ placeholderState, oldEditorState, newEditorState }) { const wasEmpty = oldEditorState ? isEmptyDocument(oldEditorState.doc) : true; const isEmpty = isEmptyDocument(newEditorState.doc); const hasEverTyped = Boolean(placeholderState === null || placeholderState === void 0 ? void 0 : placeholderState.userHadTyped) || // Previously typed !wasEmpty || // Had content before wasEmpty && !isEmpty; // Just added content const justDeletedAll = hasEverTyped && isEmpty && !wasEmpty; const isInTypedAndDeletedState = justDeletedAll || Boolean(placeholderState === null || placeholderState === void 0 ? void 0 : placeholderState.typedAndDeleted) && isEmpty; // Only reset user interaction tracking when editor is cleanly empty const shouldResetInteraction = isEmpty && !isInTypedAndDeletedState; return { userHadTyped: shouldResetInteraction ? false : hasEverTyped, typedAndDeleted: isInTypedAndDeletedState }; }