@atlaskit/editor-plugin-placeholder 6.5.4 → 6.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/placeholderPlugin.js +19 -568
  3. package/dist/cjs/pm-plugins/adf-builders.js +30 -0
  4. package/dist/cjs/pm-plugins/animation.js +58 -0
  5. package/dist/cjs/pm-plugins/constants.js +18 -0
  6. package/dist/cjs/pm-plugins/decorations.js +96 -0
  7. package/dist/cjs/pm-plugins/main.js +168 -0
  8. package/dist/cjs/pm-plugins/placeholderPluginLegacy.js +617 -0
  9. package/dist/cjs/pm-plugins/types.js +5 -0
  10. package/dist/cjs/pm-plugins/utils.js +243 -0
  11. package/dist/es2019/placeholderPlugin.js +8 -548
  12. package/dist/es2019/pm-plugins/adf-builders.js +22 -0
  13. package/dist/es2019/pm-plugins/animation.js +49 -0
  14. package/dist/es2019/pm-plugins/constants.js +12 -0
  15. package/dist/es2019/pm-plugins/decorations.js +87 -0
  16. package/dist/es2019/pm-plugins/main.js +162 -0
  17. package/dist/es2019/pm-plugins/placeholderPluginLegacy.js +598 -0
  18. package/dist/es2019/pm-plugins/types.js +1 -0
  19. package/dist/es2019/pm-plugins/utils.js +234 -0
  20. package/dist/esm/placeholderPlugin.js +17 -563
  21. package/dist/esm/pm-plugins/adf-builders.js +24 -0
  22. package/dist/esm/pm-plugins/animation.js +52 -0
  23. package/dist/esm/pm-plugins/constants.js +12 -0
  24. package/dist/esm/pm-plugins/decorations.js +90 -0
  25. package/dist/esm/pm-plugins/main.js +162 -0
  26. package/dist/esm/pm-plugins/placeholderPluginLegacy.js +607 -0
  27. package/dist/esm/pm-plugins/types.js +1 -0
  28. package/dist/esm/pm-plugins/utils.js +232 -0
  29. package/dist/types/placeholderPlugin.d.ts +0 -8
  30. package/dist/types/pm-plugins/adf-builders.d.ts +4 -0
  31. package/dist/types/pm-plugins/animation.d.ts +1 -0
  32. package/dist/types/pm-plugins/constants.d.ts +9 -0
  33. package/dist/types/pm-plugins/decorations.d.ts +4 -0
  34. package/dist/types/pm-plugins/main.d.ts +6 -0
  35. package/dist/types/pm-plugins/placeholderPluginLegacy.d.ts +13 -0
  36. package/dist/types/pm-plugins/types.d.ts +37 -0
  37. package/dist/types/pm-plugins/utils.d.ts +27 -0
  38. package/dist/types-ts4.5/placeholderPlugin.d.ts +0 -8
  39. package/dist/types-ts4.5/pm-plugins/adf-builders.d.ts +4 -0
  40. package/dist/types-ts4.5/pm-plugins/animation.d.ts +1 -0
  41. package/dist/types-ts4.5/pm-plugins/constants.d.ts +9 -0
  42. package/dist/types-ts4.5/pm-plugins/decorations.d.ts +4 -0
  43. package/dist/types-ts4.5/pm-plugins/main.d.ts +6 -0
  44. package/dist/types-ts4.5/pm-plugins/placeholderPluginLegacy.d.ts +13 -0
  45. package/dist/types-ts4.5/pm-plugins/types.d.ts +37 -0
  46. package/dist/types-ts4.5/pm-plugins/utils.d.ts +27 -0
  47. package/package.json +6 -9
@@ -0,0 +1,52 @@
1
+ import { TYPEWRITER_CYCLE_DELAY, TYPEWRITER_ERASE_DELAY, TYPEWRITER_PAUSE_BEFORE_ERASE, TYPEWRITER_TYPE_DELAY } from './constants';
2
+ export var cycleThroughPlaceholderPrompts = function cycleThroughPlaceholderPrompts(placeholderPrompts, activeTypewriterTimeouts, placeholderNodeWithText) {
3
+ var initialDelayWhenUserTypedAndDeleted = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
4
+ var currentPromptIndex = 0;
5
+ var displayedText = '';
6
+ var animationTimeouts = [];
7
+ var clearAllTimeouts = function clearAllTimeouts() {
8
+ animationTimeouts.forEach(function (timeoutId) {
9
+ return clearTimeout(timeoutId);
10
+ });
11
+ animationTimeouts = [];
12
+ };
13
+ var scheduleTimeout = function scheduleTimeout(callback, delay) {
14
+ var timeoutId = setTimeout(callback, delay);
15
+ animationTimeouts.push(timeoutId);
16
+ return timeoutId;
17
+ };
18
+ var _startAnimationCycle = function startAnimationCycle() {
19
+ var currentPrompt = placeholderPrompts[currentPromptIndex];
20
+ var characterIndex = 0;
21
+ var _typeNextCharacter = function typeNextCharacter() {
22
+ if (characterIndex < currentPrompt.length) {
23
+ displayedText = currentPrompt.substring(0, characterIndex + 1);
24
+ placeholderNodeWithText.textContent = displayedText;
25
+ characterIndex++;
26
+ scheduleTimeout(_typeNextCharacter, TYPEWRITER_TYPE_DELAY);
27
+ } else {
28
+ scheduleTimeout(_eraseLastCharacter, TYPEWRITER_PAUSE_BEFORE_ERASE);
29
+ }
30
+ };
31
+ var _eraseLastCharacter = function eraseLastCharacter() {
32
+ if (displayedText.length > 1) {
33
+ displayedText = displayedText.substring(0, displayedText.length - 1);
34
+ placeholderNodeWithText.textContent = displayedText;
35
+ scheduleTimeout(_eraseLastCharacter, TYPEWRITER_ERASE_DELAY);
36
+ } else {
37
+ displayedText = ' ';
38
+ placeholderNodeWithText.textContent = displayedText;
39
+ currentPromptIndex = (currentPromptIndex + 1) % placeholderPrompts.length;
40
+ scheduleTimeout(_startAnimationCycle, TYPEWRITER_CYCLE_DELAY);
41
+ }
42
+ };
43
+ _typeNextCharacter();
44
+ };
45
+ activeTypewriterTimeouts === null || activeTypewriterTimeouts === void 0 || activeTypewriterTimeouts.push(clearAllTimeouts);
46
+ if (initialDelayWhenUserTypedAndDeleted > 0) {
47
+ placeholderNodeWithText.textContent = ' ';
48
+ scheduleTimeout(_startAnimationCycle, initialDelayWhenUserTypedAndDeleted);
49
+ } else {
50
+ _startAnimationCycle();
51
+ }
52
+ };
@@ -0,0 +1,12 @@
1
+ export var placeholderTestId = 'placeholder-test-id';
2
+
3
+ // Typewriter animation timing constants
4
+ export var TYPEWRITER_TYPE_DELAY = 50; // Delay between typing each character
5
+ export var TYPEWRITER_PAUSE_BEFORE_ERASE = 2000; // Pause before starting to erase text
6
+ export var TYPEWRITER_ERASE_DELAY = 40; // Delay between erasing each character
7
+ export var TYPEWRITER_CYCLE_DELAY = 500; // Delay before starting next cycle
8
+ export var TYPEWRITER_TYPED_AND_DELETED_DELAY = 1500; // Delay before starting animation after user typed and deleted
9
+
10
+ export var nodeTypesWithLongPlaceholderText = ['expand', 'panel'];
11
+ export var nodeTypesWithShortPlaceholderText = ['tableCell', 'tableHeader'];
12
+ export var nodeTypesWithSyncBlockPlaceholderText = ['bodiedSyncBlock'];
@@ -0,0 +1,90 @@
1
+ import { processRawValue } from '@atlaskit/editor-common/process-raw-value';
2
+ import { browser } from '@atlaskit/editor-common/utils';
3
+ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
4
+ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
5
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
+ import { cycleThroughPlaceholderPrompts } from './animation';
7
+ import { placeholderTestId } from './constants';
8
+ export function createPlaceholderDecoration(editorState, placeholderText, placeholderPrompts, activeTypewriterTimeouts) {
9
+ var pos = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
10
+ var initialDelayWhenUserTypedAndDeleted = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;
11
+ var placeholderADF = arguments.length > 6 ? arguments[6] : undefined;
12
+ var placeholderDecoration = document.createElement('span');
13
+ var placeholderNodeWithText = placeholderDecoration;
14
+ placeholderDecoration.setAttribute('data-testid', placeholderTestId);
15
+ placeholderDecoration.className = 'placeholder-decoration';
16
+ placeholderDecoration.setAttribute('aria-hidden', 'true');
17
+
18
+ // PM sets contenteditable to false on Decorations so Firefox doesn't display the flashing cursor
19
+ // So adding an extra span which will contain the placeholder text
20
+ if (browser.gecko) {
21
+ var placeholderNode = document.createElement('span');
22
+ placeholderNode.setAttribute('contenteditable', 'true'); // explicitly overriding the default Decoration behaviour
23
+ placeholderDecoration.appendChild(placeholderNode);
24
+ placeholderNodeWithText = placeholderNode;
25
+ }
26
+ if (placeholderText) {
27
+ placeholderNodeWithText.textContent = placeholderText || ' ';
28
+ } else if (placeholderADF) {
29
+ var serializer = DOMSerializer.fromSchema(editorState.schema);
30
+ // Get a PMNode from docnode
31
+ var docNode = processRawValue(editorState.schema, placeholderADF);
32
+ if (docNode) {
33
+ // Extract only the inline content from paragraphs, avoiding block-level elements
34
+ // that can interfere with cursor rendering
35
+
36
+ docNode.children.forEach(function (node) {
37
+ // For paragraph nodes, serialize their content (inline elements) directly
38
+ // without the wrapping <p> tag
39
+ if (node.type.name === 'paragraph') {
40
+ node.content.forEach(function (inlineNode) {
41
+ var inlineDOM = serializer.serializeNode(inlineNode);
42
+ placeholderNodeWithText.append(inlineDOM);
43
+ });
44
+ } else {
45
+ // For non-paragraph nodes, serialize normally
46
+ var nodeDOM = serializer.serializeNode(node);
47
+ placeholderNodeWithText.append(nodeDOM);
48
+ }
49
+ });
50
+ var markElements = placeholderNodeWithText.querySelectorAll('[data-prosemirror-content-type="mark"]');
51
+ markElements.forEach(function (markEl) {
52
+ if (markEl instanceof HTMLElement) {
53
+ markEl.style.setProperty('color', "var(--ds-text-subtlest, #6B6E76)");
54
+ }
55
+ });
56
+ // Ensure all child elements don't block pointer events or cursor
57
+ var allElements = placeholderNodeWithText.querySelectorAll('*');
58
+ allElements.forEach(function (el) {
59
+ if (el instanceof HTMLElement) {
60
+ el.style.pointerEvents = 'none';
61
+ el.style.userSelect = 'none';
62
+ }
63
+ });
64
+ }
65
+ } else if (placeholderPrompts) {
66
+ cycleThroughPlaceholderPrompts(placeholderPrompts, activeTypewriterTimeouts, placeholderNodeWithText, initialDelayWhenUserTypedAndDeleted);
67
+ }
68
+
69
+ // ME-2289 Tapping on backspace in empty editor hides and displays the keyboard
70
+ // Add a editable buff node as the cursor moving forward is inevitable
71
+ // when backspace in GBoard composition
72
+ if (browser.android && browser.chrome) {
73
+ var buffNode = document.createElement('span');
74
+ buffNode.setAttribute('class', 'placeholder-android');
75
+ buffNode.setAttribute('contenteditable', 'true');
76
+ buffNode.textContent = ' ';
77
+ placeholderDecoration.appendChild(buffNode);
78
+ }
79
+ var isTargetNested = editorState.doc.resolve(pos).depth > 1;
80
+
81
+ // only truncate text for nested nodes, otherwise applying 'overflow: hidden;' to top level nodes
82
+ // creates issues with quick insert button
83
+ if (isTargetNested && editorExperiment('platform_editor_controls', 'variant1')) {
84
+ placeholderDecoration.classList.add('placeholder-decoration-hide-overflow');
85
+ }
86
+ return DecorationSet.create(editorState.doc, [Decoration.widget(pos, placeholderDecoration, {
87
+ side: 0,
88
+ key: "placeholder ".concat(placeholderText)
89
+ })]);
90
+ }
@@ -0,0 +1,162 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import { fg } from '@atlaskit/platform-feature-flags';
3
+ import { pluginKey, EMPTY_PARAGRAPH_TIMEOUT_DELAY } from '../placeholderPlugin';
4
+ import { TYPEWRITER_TYPED_AND_DELETED_DELAY } from './constants';
5
+ import { createPlaceholderDecoration } from './decorations';
6
+ import { calculateUserInteractionState, createPlaceHolderStateFrom, getPlaceholderState } from './utils';
7
+ export default function createPlugin(intl, defaultPlaceholderText, bracketPlaceholderText, emptyLinePlaceholder, placeholderPrompts, withEmptyParagraph, placeholderADF, api) {
8
+ if (!defaultPlaceholderText && !placeholderPrompts && !bracketPlaceholderText && !placeholderADF) {
9
+ return;
10
+ }
11
+ var isDestroyed = false;
12
+ var activeTypewriterTimeouts = [];
13
+ var clearAllTypewriterTimeouts = function clearAllTypewriterTimeouts() {
14
+ activeTypewriterTimeouts.forEach(function (clearFn) {
15
+ return clearFn();
16
+ });
17
+ activeTypewriterTimeouts = [];
18
+ };
19
+ return new SafePlugin({
20
+ key: pluginKey,
21
+ state: {
22
+ init: function init(_, state) {
23
+ var _api$focus, _api$typeAhead;
24
+ return createPlaceHolderStateFrom({
25
+ isInitial: true,
26
+ isEditorFocused: Boolean(api === null || api === void 0 || (_api$focus = api.focus) === null || _api$focus === void 0 || (_api$focus = _api$focus.sharedState.currentState()) === null || _api$focus === void 0 ? void 0 : _api$focus.hasFocus),
27
+ editorState: state,
28
+ isTypeAheadOpen: api === null || api === void 0 || (_api$typeAhead = api.typeAhead) === null || _api$typeAhead === void 0 ? void 0 : _api$typeAhead.actions.isOpen,
29
+ defaultPlaceholderText: defaultPlaceholderText,
30
+ bracketPlaceholderText: bracketPlaceholderText,
31
+ emptyLinePlaceholder: emptyLinePlaceholder,
32
+ placeholderADF: placeholderADF,
33
+ placeholderPrompts: placeholderPrompts,
34
+ typedAndDeleted: false,
35
+ userHadTyped: false,
36
+ intl: intl,
37
+ withEmptyParagraph: withEmptyParagraph
38
+ });
39
+ },
40
+ apply: function apply(tr, placeholderState, _oldEditorState, newEditorState) {
41
+ var _api$focus2, _placeholderState$isP, _api$typeAhead2, _ref, _meta$placeholderText, _ref2, _meta$placeholderProm, _meta$showOnEmptyPara;
42
+ var meta = tr.getMeta(pluginKey);
43
+ var isEditorFocused = Boolean(api === null || api === void 0 || (_api$focus2 = api.focus) === null || _api$focus2 === void 0 || (_api$focus2 = _api$focus2.sharedState.currentState()) === null || _api$focus2 === void 0 ? void 0 : _api$focus2.hasFocus);
44
+ var _calculateUserInterac = calculateUserInteractionState({
45
+ placeholderState: placeholderState,
46
+ oldEditorState: _oldEditorState,
47
+ newEditorState: newEditorState
48
+ }),
49
+ userHadTyped = _calculateUserInterac.userHadTyped,
50
+ typedAndDeleted = _calculateUserInterac.typedAndDeleted;
51
+ var isPlaceholderHidden = (_placeholderState$isP = placeholderState === null || placeholderState === void 0 ? void 0 : placeholderState.isPlaceholderHidden) !== null && _placeholderState$isP !== void 0 ? _placeholderState$isP : false;
52
+ if ((meta === null || meta === void 0 ? void 0 : meta.isPlaceholderHidden) !== undefined && withEmptyParagraph) {
53
+ isPlaceholderHidden = meta.isPlaceholderHidden;
54
+ }
55
+ if ((meta === null || meta === void 0 ? void 0 : meta.placeholderText) !== undefined && withEmptyParagraph) {
56
+ // Only update defaultPlaceholderText from meta if we're not using ADF placeholder
57
+ if (!(fg('platform_editor_ai_aifc_patch_ga') && placeholderADF)) {
58
+ defaultPlaceholderText = meta.placeholderText;
59
+ }
60
+ }
61
+ var newPlaceholderState = createPlaceHolderStateFrom({
62
+ isEditorFocused: isEditorFocused,
63
+ editorState: newEditorState,
64
+ isTypeAheadOpen: api === null || api === void 0 || (_api$typeAhead2 = api.typeAhead) === null || _api$typeAhead2 === void 0 ? void 0 : _api$typeAhead2.actions.isOpen,
65
+ defaultPlaceholderText: withEmptyParagraph ? defaultPlaceholderText : (_ref = (_meta$placeholderText = meta === null || meta === void 0 ? void 0 : meta.placeholderText) !== null && _meta$placeholderText !== void 0 ? _meta$placeholderText : placeholderState === null || placeholderState === void 0 ? void 0 : placeholderState.placeholderText) !== null && _ref !== void 0 ? _ref : defaultPlaceholderText,
66
+ bracketPlaceholderText: bracketPlaceholderText,
67
+ emptyLinePlaceholder: emptyLinePlaceholder,
68
+ placeholderADF: placeholderADF,
69
+ placeholderPrompts: (_ref2 = (_meta$placeholderProm = meta === null || meta === void 0 ? void 0 : meta.placeholderPrompts) !== null && _meta$placeholderProm !== void 0 ? _meta$placeholderProm : placeholderState === null || placeholderState === void 0 ? void 0 : placeholderState.placeholderPrompts) !== null && _ref2 !== void 0 ? _ref2 : placeholderPrompts,
70
+ typedAndDeleted: typedAndDeleted,
71
+ userHadTyped: userHadTyped,
72
+ intl: intl,
73
+ isPlaceholderHidden: isPlaceholderHidden,
74
+ withEmptyParagraph: withEmptyParagraph,
75
+ showOnEmptyParagraph: (_meta$showOnEmptyPara = meta === null || meta === void 0 ? void 0 : meta.showOnEmptyParagraph) !== null && _meta$showOnEmptyPara !== void 0 ? _meta$showOnEmptyPara : placeholderState === null || placeholderState === void 0 ? void 0 : placeholderState.showOnEmptyParagraph
76
+ });
77
+
78
+ // Clear timeouts when hasPlaceholder becomes false
79
+ if (!newPlaceholderState.hasPlaceholder) {
80
+ clearAllTypewriterTimeouts();
81
+ }
82
+ return newPlaceholderState;
83
+ }
84
+ },
85
+ props: {
86
+ decorations: function decorations(editorState) {
87
+ var _api$composition, _api$showDiff;
88
+ var _getPlaceholderState = getPlaceholderState(editorState),
89
+ hasPlaceholder = _getPlaceholderState.hasPlaceholder,
90
+ placeholderText = _getPlaceholderState.placeholderText,
91
+ pos = _getPlaceholderState.pos,
92
+ typedAndDeleted = _getPlaceholderState.typedAndDeleted,
93
+ contextPlaceholderADF = _getPlaceholderState.contextPlaceholderADF;
94
+
95
+ // Decorations is still called after plugin is destroyed
96
+ // So we need to make sure decorations is not called if plugin has been destroyed to prevent the placeholder animations' setTimeouts called infinitely
97
+ if (isDestroyed) {
98
+ return;
99
+ }
100
+ var compositionPluginState = api === null || api === void 0 || (_api$composition = api.composition) === null || _api$composition === void 0 ? void 0 : _api$composition.sharedState.currentState();
101
+ var isShowingDiff = Boolean(api === null || api === void 0 || (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 || (_api$showDiff = _api$showDiff.sharedState.currentState()) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.isDisplayingChanges);
102
+ if (hasPlaceholder && ((placeholderText !== null && placeholderText !== void 0 ? placeholderText : '') || placeholderPrompts || placeholderADF || contextPlaceholderADF) && pos !== undefined && !(compositionPluginState !== null && compositionPluginState !== void 0 && compositionPluginState.isComposing) && !isShowingDiff) {
103
+ var initialDelayWhenUserTypedAndDeleted = typedAndDeleted ? TYPEWRITER_TYPED_AND_DELETED_DELAY : 0;
104
+ // contextPlaceholderADF takes precedence over the global placeholderADF
105
+ var placeholderAdfToUse = contextPlaceholderADF || placeholderADF;
106
+ return createPlaceholderDecoration(editorState, placeholderText !== null && placeholderText !== void 0 ? placeholderText : '', placeholderPrompts, activeTypewriterTimeouts, pos, initialDelayWhenUserTypedAndDeleted, placeholderAdfToUse);
107
+ }
108
+ return;
109
+ }
110
+ },
111
+ view: function view() {
112
+ var timeoutId;
113
+ function startEmptyParagraphTimeout(editorView) {
114
+ if (timeoutId) {
115
+ return;
116
+ }
117
+ timeoutId = setTimeout(function () {
118
+ timeoutId = undefined;
119
+ editorView.dispatch(editorView.state.tr.setMeta(pluginKey, {
120
+ showOnEmptyParagraph: true
121
+ }));
122
+ }, EMPTY_PARAGRAPH_TIMEOUT_DELAY);
123
+ }
124
+ function destroyEmptyParagraphTimeout() {
125
+ if (timeoutId) {
126
+ clearTimeout(timeoutId);
127
+ timeoutId = undefined;
128
+ }
129
+ }
130
+ return {
131
+ update: function update(editorView, prevState) {
132
+ if (withEmptyParagraph) {
133
+ var prevPluginState = getPlaceholderState(prevState);
134
+ var newPluginState = getPlaceholderState(editorView.state);
135
+
136
+ // user start typing after move to an empty paragraph, clear timeout
137
+ if (!newPluginState.canShowOnEmptyParagraph && timeoutId) {
138
+ destroyEmptyParagraphTimeout();
139
+ }
140
+ // user move to an empty paragraph again, reset state to hide placeholder, and restart timeout
141
+ else if (prevPluginState.canShowOnEmptyParagraph && newPluginState.canShowOnEmptyParagraph && newPluginState.pos !== prevPluginState.pos) {
142
+ editorView.dispatch(editorView.state.tr.setMeta(pluginKey, {
143
+ showOnEmptyParagraph: false
144
+ }));
145
+ destroyEmptyParagraphTimeout();
146
+ startEmptyParagraphTimeout(editorView);
147
+ }
148
+ // user move to an empty paragraph (by click enter or move to an empty paragraph), start timeout
149
+ else if (!prevPluginState.canShowOnEmptyParagraph && newPluginState.canShowOnEmptyParagraph && !newPluginState.showOnEmptyParagraph && !timeoutId) {
150
+ startEmptyParagraphTimeout(editorView);
151
+ }
152
+ }
153
+ },
154
+ destroy: function destroy() {
155
+ clearAllTypewriterTimeouts();
156
+ destroyEmptyParagraphTimeout();
157
+ isDestroyed = true;
158
+ }
159
+ };
160
+ }
161
+ });
162
+ }