@atlaskit/editor-plugin-placeholder-text 0.1.0

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 (73) hide show
  1. package/.eslintrc.js +14 -0
  2. package/CHANGELOG.md +1 -0
  3. package/LICENSE.md +13 -0
  4. package/README.md +30 -0
  5. package/dist/cjs/actions.js +35 -0
  6. package/dist/cjs/fake-text-cursor/cursor.js +107 -0
  7. package/dist/cjs/index.js +13 -0
  8. package/dist/cjs/placeholder-text-nodeview.js +89 -0
  9. package/dist/cjs/plugin-key.js +8 -0
  10. package/dist/cjs/plugin.js +270 -0
  11. package/dist/cjs/selection-utils.js +14 -0
  12. package/dist/cjs/styles.js +13 -0
  13. package/dist/cjs/types.js +5 -0
  14. package/dist/cjs/ui/FloatingToolbar/index.js +88 -0
  15. package/dist/cjs/ui/FloatingToolbar/styles.js +15 -0
  16. package/dist/cjs/ui/FloatingToolbar/utils.js +55 -0
  17. package/dist/cjs/ui/PlaceholderFloatingToolbar/index.js +90 -0
  18. package/dist/es2019/actions.js +27 -0
  19. package/dist/es2019/fake-text-cursor/cursor.js +77 -0
  20. package/dist/es2019/index.js +1 -0
  21. package/dist/es2019/placeholder-text-nodeview.js +77 -0
  22. package/dist/es2019/plugin-key.js +2 -0
  23. package/dist/es2019/plugin.js +250 -0
  24. package/dist/es2019/selection-utils.js +8 -0
  25. package/dist/es2019/styles.js +52 -0
  26. package/dist/es2019/types.js +1 -0
  27. package/dist/es2019/ui/FloatingToolbar/index.js +49 -0
  28. package/dist/es2019/ui/FloatingToolbar/styles.js +15 -0
  29. package/dist/es2019/ui/FloatingToolbar/utils.js +42 -0
  30. package/dist/es2019/ui/PlaceholderFloatingToolbar/index.js +61 -0
  31. package/dist/esm/actions.js +29 -0
  32. package/dist/esm/fake-text-cursor/cursor.js +100 -0
  33. package/dist/esm/index.js +1 -0
  34. package/dist/esm/placeholder-text-nodeview.js +82 -0
  35. package/dist/esm/plugin-key.js +2 -0
  36. package/dist/esm/plugin.js +262 -0
  37. package/dist/esm/selection-utils.js +8 -0
  38. package/dist/esm/styles.js +6 -0
  39. package/dist/esm/types.js +1 -0
  40. package/dist/esm/ui/FloatingToolbar/index.js +67 -0
  41. package/dist/esm/ui/FloatingToolbar/styles.js +8 -0
  42. package/dist/esm/ui/FloatingToolbar/utils.js +49 -0
  43. package/dist/esm/ui/PlaceholderFloatingToolbar/index.js +80 -0
  44. package/dist/types/actions.d.ts +4 -0
  45. package/dist/types/fake-text-cursor/cursor.d.ts +30 -0
  46. package/dist/types/index.d.ts +2 -0
  47. package/dist/types/placeholder-text-nodeview.d.ts +17 -0
  48. package/dist/types/plugin-key.d.ts +3 -0
  49. package/dist/types/plugin.d.ts +7 -0
  50. package/dist/types/selection-utils.d.ts +2 -0
  51. package/dist/types/styles.d.ts +1 -0
  52. package/dist/types/types.d.ts +22 -0
  53. package/dist/types/ui/FloatingToolbar/index.d.ts +28 -0
  54. package/dist/types/ui/FloatingToolbar/styles.d.ts +1 -0
  55. package/dist/types/ui/FloatingToolbar/utils.d.ts +18 -0
  56. package/dist/types/ui/PlaceholderFloatingToolbar/index.d.ts +25 -0
  57. package/dist/types-ts4.5/actions.d.ts +4 -0
  58. package/dist/types-ts4.5/fake-text-cursor/cursor.d.ts +30 -0
  59. package/dist/types-ts4.5/index.d.ts +2 -0
  60. package/dist/types-ts4.5/placeholder-text-nodeview.d.ts +17 -0
  61. package/dist/types-ts4.5/plugin-key.d.ts +3 -0
  62. package/dist/types-ts4.5/plugin.d.ts +7 -0
  63. package/dist/types-ts4.5/selection-utils.d.ts +2 -0
  64. package/dist/types-ts4.5/styles.d.ts +1 -0
  65. package/dist/types-ts4.5/types.d.ts +22 -0
  66. package/dist/types-ts4.5/ui/FloatingToolbar/index.d.ts +28 -0
  67. package/dist/types-ts4.5/ui/FloatingToolbar/styles.d.ts +1 -0
  68. package/dist/types-ts4.5/ui/FloatingToolbar/utils.d.ts +18 -0
  69. package/dist/types-ts4.5/ui/PlaceholderFloatingToolbar/index.d.ts +25 -0
  70. package/package.json +100 -0
  71. package/report.api.md +80 -0
  72. package/styles/package.json +15 -0
  73. package/tmp/api-report-tmp.d.ts +49 -0
@@ -0,0 +1,250 @@
1
+ import React from 'react';
2
+ import { placeholder } from '@atlaskit/adf-schema';
3
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
5
+ import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
6
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
7
+ import { isNodeEmpty } from '@atlaskit/editor-common/utils';
8
+ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
9
+ import MediaServicesTextIcon from '@atlaskit/icon/glyph/media-services/text';
10
+ import { hidePlaceholderFloatingToolbar, insertPlaceholderTextAtSelection, showPlaceholderFloatingToolbar } from './actions';
11
+ import { drawFakeTextCursor, FakeTextCursorSelection } from './fake-text-cursor/cursor';
12
+ import { PlaceholderTextNodeView } from './placeholder-text-nodeview';
13
+ import { pluginKey } from './plugin-key';
14
+ import { isSelectionAtPlaceholder } from './selection-utils';
15
+ import PlaceholderFloatingToolbar from './ui/PlaceholderFloatingToolbar';
16
+ const getOpenTypeAhead = (trigger, api) => {
17
+ var _api$typeAhead, _api$typeAhead$action, _api$typeAhead2, _api$typeAhead2$actio;
18
+ const typeAheadHandler = api === null || api === void 0 ? void 0 : (_api$typeAhead = api.typeAhead) === null || _api$typeAhead === void 0 ? void 0 : (_api$typeAhead$action = _api$typeAhead.actions) === null || _api$typeAhead$action === void 0 ? void 0 : _api$typeAhead$action.findHandlerByTrigger(trigger);
19
+ if (!typeAheadHandler || !typeAheadHandler.id) {
20
+ return null;
21
+ }
22
+ return api === null || api === void 0 ? void 0 : (_api$typeAhead2 = api.typeAhead) === null || _api$typeAhead2 === void 0 ? void 0 : (_api$typeAhead2$actio = _api$typeAhead2.actions) === null || _api$typeAhead2$actio === void 0 ? void 0 : _api$typeAhead2$actio.openAtTransaction({
23
+ triggerHandler: typeAheadHandler,
24
+ inputMethod: INPUT_METHOD.KEYBOARD
25
+ });
26
+ };
27
+ export function createPlugin(dispatch, options, api) {
28
+ const allowInserting = !!options.allowInserting;
29
+ return new SafePlugin({
30
+ key: pluginKey,
31
+ state: {
32
+ init: () => ({
33
+ showInsertPanelAt: null,
34
+ allowInserting
35
+ }),
36
+ apply: (tr, state) => {
37
+ const meta = tr.getMeta(pluginKey);
38
+ if (meta && meta.showInsertPanelAt !== undefined) {
39
+ const newState = {
40
+ showInsertPanelAt: meta.showInsertPanelAt,
41
+ allowInserting
42
+ };
43
+ dispatch(pluginKey, newState);
44
+ return newState;
45
+ } else if (state.showInsertPanelAt) {
46
+ const newState = {
47
+ showInsertPanelAt: tr.mapping.map(state.showInsertPanelAt),
48
+ allowInserting
49
+ };
50
+ dispatch(pluginKey, newState);
51
+ return newState;
52
+ }
53
+ return state;
54
+ }
55
+ },
56
+ appendTransaction(transactions, oldState, newState) {
57
+ if (transactions.some(txn => txn.docChanged)) {
58
+ const didPlaceholderExistBeforeTxn = oldState.selection.$head.nodeAfter === newState.selection.$head.nodeAfter;
59
+ const adjacentNode = newState.selection.$head.nodeAfter;
60
+ const adjacentNodePos = newState.selection.$head.pos;
61
+ const placeholderNodeType = newState.schema.nodes.placeholder;
62
+ if (adjacentNode && adjacentNode.type === placeholderNodeType && didPlaceholderExistBeforeTxn) {
63
+ var _$newHead$nodeBefore;
64
+ const {
65
+ $head: $newHead
66
+ } = newState.selection;
67
+ const {
68
+ $head: $oldHead
69
+ } = oldState.selection;
70
+ // Check that cursor has moved forward in the document **and** that there is content before the cursor
71
+ const cursorMoved = $oldHead.pos < $newHead.pos;
72
+ const nodeBeforeHasContent = !isNodeEmpty($newHead.nodeBefore);
73
+ const nodeBeforeIsInline = (_$newHead$nodeBefore = $newHead.nodeBefore) === null || _$newHead$nodeBefore === void 0 ? void 0 : _$newHead$nodeBefore.type.isInline;
74
+ if (cursorMoved && (nodeBeforeHasContent || nodeBeforeIsInline)) {
75
+ const {
76
+ $from,
77
+ $to
78
+ } = NodeSelection.create(newState.doc, adjacentNodePos);
79
+ return newState.tr.deleteRange($from.pos, $to.pos);
80
+ }
81
+ }
82
+ }
83
+
84
+ // Handle Fake Text Cursor for Floating Toolbar
85
+ if (!pluginKey.getState(oldState).showInsertPanelAt && pluginKey.getState(newState).showInsertPanelAt) {
86
+ return newState.tr.setSelection(new FakeTextCursorSelection(newState.selection.$from));
87
+ }
88
+ if (pluginKey.getState(oldState).showInsertPanelAt && !pluginKey.getState(newState).showInsertPanelAt) {
89
+ if (newState.selection instanceof FakeTextCursorSelection) {
90
+ return newState.tr.setSelection(new TextSelection(newState.selection.$from));
91
+ }
92
+ }
93
+ return;
94
+ },
95
+ props: {
96
+ decorations: drawFakeTextCursor,
97
+ handleDOMEvents: {
98
+ beforeinput: (view, event) => {
99
+ const {
100
+ state
101
+ } = view;
102
+ if (event instanceof InputEvent && !event.isComposing && event.inputType === 'insertText' && isSelectionAtPlaceholder(view.state.selection)) {
103
+ event.stopPropagation();
104
+ event.preventDefault();
105
+ const startNodePosition = state.selection.from;
106
+ const content = event.data || '';
107
+ const tr = view.state.tr;
108
+ tr.delete(startNodePosition, startNodePosition + 1);
109
+ const openTypeAhead = getOpenTypeAhead(content, api);
110
+ if (openTypeAhead) {
111
+ openTypeAhead(tr);
112
+ } else {
113
+ tr.insertText(content);
114
+ }
115
+ view.dispatch(tr);
116
+ return true;
117
+ }
118
+ return false;
119
+ }
120
+ },
121
+ nodeViews: {
122
+ placeholder: (node, view, getPos) => new PlaceholderTextNodeView(node, view, getPos)
123
+ }
124
+ }
125
+ });
126
+ }
127
+ function ContentComponent({
128
+ editorView,
129
+ dependencyApi,
130
+ popupsMountPoint,
131
+ popupsBoundariesElement
132
+ }) {
133
+ const {
134
+ placeholderTextState
135
+ } = useSharedPluginState(dependencyApi, ['placeholderText']);
136
+ const insertPlaceholderText = value => insertPlaceholderTextAtSelection(value)(editorView.state, editorView.dispatch);
137
+ const hidePlaceholderToolbar = () => hidePlaceholderFloatingToolbar(editorView.state, editorView.dispatch);
138
+ const getNodeFromPos = pos => editorView.domAtPos(pos).node;
139
+ const getFixedCoordinatesFromPos = pos => editorView.coordsAtPos(pos);
140
+ const setFocusInEditor = () => editorView.focus();
141
+ if (placeholderTextState !== null && placeholderTextState !== void 0 && placeholderTextState.showInsertPanelAt) {
142
+ return /*#__PURE__*/React.createElement(PlaceholderFloatingToolbar, {
143
+ editorViewDOM: editorView.dom,
144
+ popupsMountPoint: popupsMountPoint,
145
+ popupsBoundariesElement: popupsBoundariesElement,
146
+ getFixedCoordinatesFromPos: getFixedCoordinatesFromPos,
147
+ getNodeFromPos: getNodeFromPos,
148
+ hidePlaceholderFloatingToolbar: hidePlaceholderToolbar,
149
+ showInsertPanelAt: placeholderTextState.showInsertPanelAt,
150
+ insertPlaceholder: insertPlaceholderText,
151
+ setFocusInEditor: setFocusInEditor
152
+ });
153
+ }
154
+ return null;
155
+ }
156
+ const basePlaceholderTextPlugin = ({
157
+ api,
158
+ config: options
159
+ }) => ({
160
+ name: 'placeholderText',
161
+ nodes() {
162
+ return [{
163
+ name: 'placeholder',
164
+ node: placeholder
165
+ }];
166
+ },
167
+ pmPlugins() {
168
+ return [{
169
+ name: 'placeholderText',
170
+ plugin: ({
171
+ dispatch
172
+ }) => createPlugin(dispatch, options, api)
173
+ }];
174
+ },
175
+ actions: {
176
+ showPlaceholderFloatingToolbar
177
+ },
178
+ getSharedState(editorState) {
179
+ if (!editorState) {
180
+ return undefined;
181
+ }
182
+ const {
183
+ showInsertPanelAt,
184
+ allowInserting
185
+ } = pluginKey.getState(editorState) || {
186
+ showInsertPanelAt: null
187
+ };
188
+ return {
189
+ showInsertPanelAt,
190
+ allowInserting: !!allowInserting
191
+ };
192
+ },
193
+ contentComponent({
194
+ editorView,
195
+ popupsMountPoint,
196
+ popupsBoundariesElement
197
+ }) {
198
+ return /*#__PURE__*/React.createElement(ContentComponent, {
199
+ editorView: editorView,
200
+ popupsMountPoint: popupsMountPoint,
201
+ popupsBoundariesElement: popupsBoundariesElement,
202
+ dependencyApi: api
203
+ });
204
+ }
205
+ });
206
+ const decorateWithPluginOptions = (plugin, options, api) => {
207
+ if (!options.allowInserting) {
208
+ return plugin;
209
+ }
210
+ plugin.pluginsOptions = {
211
+ quickInsert: ({
212
+ formatMessage
213
+ }) => [{
214
+ id: 'placeholderText',
215
+ title: formatMessage(messages.placeholderText),
216
+ description: formatMessage(messages.placeholderTextDescription),
217
+ priority: 1400,
218
+ keywords: ['placeholder'],
219
+ icon: () => /*#__PURE__*/React.createElement(MediaServicesTextIcon, {
220
+ label: ""
221
+ }),
222
+ action(insert, state) {
223
+ var _api$analytics;
224
+ const tr = state.tr;
225
+ tr.setMeta(pluginKey, {
226
+ showInsertPanelAt: tr.selection.anchor
227
+ });
228
+ api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.attachAnalyticsEvent({
229
+ action: ACTION.INSERTED,
230
+ actionSubject: ACTION_SUBJECT.DOCUMENT,
231
+ actionSubjectId: ACTION_SUBJECT_ID.PLACEHOLDER_TEXT,
232
+ attributes: {
233
+ inputMethod: INPUT_METHOD.QUICK_INSERT
234
+ },
235
+ eventType: EVENT_TYPE.TRACK
236
+ })(tr);
237
+ return tr;
238
+ }
239
+ }]
240
+ };
241
+ return plugin;
242
+ };
243
+ const placeholderTextPlugin = ({
244
+ config: options = {},
245
+ api
246
+ }) => decorateWithPluginOptions(basePlaceholderTextPlugin({
247
+ config: options,
248
+ api
249
+ }), options, api);
250
+ export default placeholderTextPlugin;
@@ -0,0 +1,8 @@
1
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
+ export const isSelectionAtPlaceholder = selection => {
3
+ if (!(selection instanceof TextSelection) || !selection.$cursor) {
4
+ return false;
5
+ }
6
+ const node = selection.$cursor.doc.nodeAt(selection.$cursor.pos);
7
+ return (node === null || node === void 0 ? void 0 : node.type.name) === 'placeholder';
8
+ };
@@ -0,0 +1,52 @@
1
+ import { css } from '@emotion/react';
2
+ import { akEditorSelectedNodeClassName, getSelectionStyles, SelectionStyle } from '@atlaskit/editor-shared-styles';
3
+ import { B75, N200 } from '@atlaskit/theme/colors';
4
+ export const placeholderTextStyles = css`
5
+ .ProseMirror span[data-placeholder] {
6
+ color: ${`var(--ds-text-subtlest, ${N200})`};
7
+ display: inline;
8
+ }
9
+
10
+ .ProseMirror span.pm-placeholder {
11
+ display: inline;
12
+ color: ${`var(--ds-text-subtlest, ${N200})`};
13
+ }
14
+ .ProseMirror span.pm-placeholder__text {
15
+ display: inline;
16
+ color: ${`var(--ds-text-subtlest, ${N200})`};
17
+ }
18
+
19
+ .ProseMirror span.pm-placeholder.${akEditorSelectedNodeClassName} {
20
+ ${getSelectionStyles([SelectionStyle.Background])}
21
+ }
22
+
23
+ .ProseMirror span.pm-placeholder__text[data-placeholder]::after {
24
+ color: ${`var(--ds-text-subtlest, ${N200})`};
25
+ cursor: text;
26
+ content: attr(data-placeholder);
27
+ display: inline;
28
+ }
29
+
30
+ .ProseMirror {
31
+ .ProseMirror-fake-text-cursor {
32
+ display: inline;
33
+ pointer-events: none;
34
+ position: relative;
35
+ }
36
+
37
+ .ProseMirror-fake-text-cursor::after {
38
+ content: '';
39
+ display: inline;
40
+ top: 0;
41
+ position: absolute;
42
+ border-right: 1px solid ${"var(--ds-border, rgba(0, 0, 0, 0.4))"};
43
+ }
44
+
45
+ .ProseMirror-fake-text-selection {
46
+ display: inline;
47
+ pointer-events: none;
48
+ position: relative;
49
+ background-color: ${`var(--ds-background-selected, ${B75})`};
50
+ }
51
+ }
52
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ /* eslint-disable @atlaskit/design-system/prefer-primitives */
2
+ /** @jsx jsx */
3
+ import { PureComponent } from 'react';
4
+ import { jsx } from '@emotion/react';
5
+ import { Popup } from '@atlaskit/editor-common/ui';
6
+ import { container } from './styles';
7
+ export { handlePositionCalculatedWith, getOffsetParent, getNearestNonTextNode } from './utils';
8
+
9
+ // eslint-disable-next-line @repo/internal/react/no-class-components
10
+ export default class FloatingToolbar extends PureComponent {
11
+ render() {
12
+ const {
13
+ children,
14
+ target,
15
+ offset,
16
+ fitWidth,
17
+ fitHeight = 40,
18
+ onPositionCalculated,
19
+ popupsMountPoint,
20
+ popupsBoundariesElement,
21
+ className,
22
+ absoluteOffset,
23
+ alignX,
24
+ alignY,
25
+ zIndex
26
+ } = this.props;
27
+ if (!target) {
28
+ return null;
29
+ }
30
+ return jsx(Popup, {
31
+ absoluteOffset: absoluteOffset,
32
+ alignX: alignX,
33
+ alignY: alignY,
34
+ target: target,
35
+ zIndex: zIndex,
36
+ mountTo: popupsMountPoint,
37
+ boundariesElement: popupsBoundariesElement,
38
+ offset: offset,
39
+ fitWidth: fitWidth,
40
+ fitHeight: fitHeight,
41
+ onPositionCalculated: onPositionCalculated
42
+ }, jsx("div", {
43
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
44
+ css: container(fitHeight),
45
+ "data-testid": "popup-container",
46
+ className: className
47
+ }, children));
48
+ }
49
+ }
@@ -0,0 +1,15 @@
1
+ import { css } from '@emotion/react';
2
+ import { N0, N50A, N60A } from '@atlaskit/theme/colors';
3
+ import { borderRadius } from '@atlaskit/theme/constants';
4
+ export const container = height => css`
5
+ border-radius: ${borderRadius()}px;
6
+ box-shadow: ${`var(--ds-shadow-overlay, ${`0 12px 24px -6px ${N50A}, 0 0 1px ${N60A}`})`};
7
+ display: flex;
8
+ align-items: center;
9
+ box-sizing: border-box;
10
+ padding: ${"var(--ds-space-050, 4px)"} ${"var(--ds-space-100, 8px)"};
11
+ background-color: ${`var(--ds-background-input, ${N0})`};
12
+ ${height ? css`
13
+ height: ${height}px;
14
+ ` : ''};
15
+ `;
@@ -0,0 +1,42 @@
1
+ const getCursorHeightFrom = node => parseFloat(window.getComputedStyle(node, undefined).lineHeight || '');
2
+ export const getOffsetParent = (editorViewDom, popupsMountPoint) => popupsMountPoint ? popupsMountPoint.offsetParent : editorViewDom.offsetParent;
3
+ export const getNearestNonTextNode = node => node.nodeType === Node.TEXT_NODE ? node.parentNode : node;
4
+
5
+ /**
6
+ * We need to translate the co-ordinates because `coordsAtPos` returns co-ordinates
7
+ * relative to `window`. And, also need to adjust the cursor container height.
8
+ * (0, 0)
9
+ * +--------------------- [window] ---------------------+
10
+ * | (left, top) +-------- [Offset Parent] --------+ |
11
+ * | {coordsAtPos} | [Cursor] <- cursorHeight | |
12
+ * | | [FloatingToolbar] | |
13
+ */
14
+ const convertFixedCoordinatesToAbsolutePositioning = (coordinates, offsetParent, cursorHeight) => {
15
+ var _coordinates$left, _coordinates$right, _coordinates$top, _coordinates$top2;
16
+ const {
17
+ left: offsetParentLeft,
18
+ top: offsetParentTop,
19
+ height: offsetParentHeight
20
+ } = offsetParent.getBoundingClientRect();
21
+ return {
22
+ left: ((_coordinates$left = coordinates.left) !== null && _coordinates$left !== void 0 ? _coordinates$left : 0) - offsetParentLeft,
23
+ right: ((_coordinates$right = coordinates.right) !== null && _coordinates$right !== void 0 ? _coordinates$right : 0) - offsetParentLeft,
24
+ top: ((_coordinates$top = coordinates.top) !== null && _coordinates$top !== void 0 ? _coordinates$top : 0) - (offsetParentTop - cursorHeight) + offsetParent.scrollTop,
25
+ bottom: offsetParentHeight - (((_coordinates$top2 = coordinates.top) !== null && _coordinates$top2 !== void 0 ? _coordinates$top2 : 0) - (offsetParentTop - cursorHeight) - offsetParent.scrollTop)
26
+ };
27
+ };
28
+ export const handlePositionCalculatedWith = (offsetParent, node, getCurrentFixedCoordinates) => position => {
29
+ if (!offsetParent) {
30
+ return position;
31
+ }
32
+ const target = getNearestNonTextNode(node);
33
+ const cursorHeight = getCursorHeightFrom(target);
34
+ const fixedCoordinates = getCurrentFixedCoordinates();
35
+ const absoluteCoordinates = convertFixedCoordinatesToAbsolutePositioning(fixedCoordinates, offsetParent, cursorHeight);
36
+ return {
37
+ left: position.left ? absoluteCoordinates.left : undefined,
38
+ right: position.right ? absoluteCoordinates.right : undefined,
39
+ top: position.top ? absoluteCoordinates.top : undefined,
40
+ bottom: position.bottom ? absoluteCoordinates.bottom : undefined
41
+ };
42
+ };
@@ -0,0 +1,61 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import React from 'react';
3
+ import { defineMessages, injectIntl } from 'react-intl-next';
4
+ import { PanelTextInput } from '@atlaskit/editor-common/ui';
5
+ import FloatingToolbar, { getNearestNonTextNode, getOffsetParent, handlePositionCalculatedWith } from '../FloatingToolbar';
6
+ export const messages = defineMessages({
7
+ placeholderTextPlaceholder: {
8
+ id: 'fabric.editor.placeholderTextPlaceholder',
9
+ defaultMessage: 'Add placeholder text',
10
+ description: ''
11
+ }
12
+ });
13
+ // eslint-disable-next-line @repo/internal/react/no-class-components
14
+ class PlaceholderFloatingToolbar extends React.Component {
15
+ constructor(...args) {
16
+ super(...args);
17
+ _defineProperty(this, "handleSubmit", value => {
18
+ if (value) {
19
+ this.props.insertPlaceholder(value);
20
+ this.props.setFocusInEditor();
21
+ } else {
22
+ this.props.hidePlaceholderFloatingToolbar();
23
+ }
24
+ });
25
+ _defineProperty(this, "handleBlur", () => {
26
+ this.props.hidePlaceholderFloatingToolbar();
27
+ });
28
+ }
29
+ render() {
30
+ const {
31
+ getNodeFromPos,
32
+ showInsertPanelAt,
33
+ editorViewDOM,
34
+ popupsMountPoint,
35
+ getFixedCoordinatesFromPos,
36
+ popupsBoundariesElement,
37
+ intl: {
38
+ formatMessage
39
+ }
40
+ } = this.props;
41
+ const target = getNodeFromPos(showInsertPanelAt);
42
+ const offsetParent = getOffsetParent(editorViewDOM, popupsMountPoint);
43
+ const getFixedCoordinates = () => getFixedCoordinatesFromPos(showInsertPanelAt);
44
+ const handlePositionCalculated = handlePositionCalculatedWith(offsetParent, target, getFixedCoordinates);
45
+ return /*#__PURE__*/React.createElement(FloatingToolbar, {
46
+ target: getNearestNonTextNode(target),
47
+ onPositionCalculated: handlePositionCalculated,
48
+ popupsMountPoint: popupsMountPoint,
49
+ popupsBoundariesElement: popupsBoundariesElement,
50
+ fitHeight: 32,
51
+ offset: [0, 12]
52
+ }, /*#__PURE__*/React.createElement(PanelTextInput, {
53
+ placeholder: formatMessage(messages.placeholderTextPlaceholder),
54
+ onSubmit: this.handleSubmit,
55
+ onBlur: this.handleBlur,
56
+ autoFocus: true,
57
+ width: 300
58
+ }));
59
+ }
60
+ }
61
+ export default injectIntl(PlaceholderFloatingToolbar);
@@ -0,0 +1,29 @@
1
+ import { pluginKey } from './plugin-key';
2
+ export var showPlaceholderFloatingToolbar = function showPlaceholderFloatingToolbar(state, dispatch) {
3
+ var tr = state.tr;
4
+ if (!state.selection.empty) {
5
+ tr.deleteSelection();
6
+ }
7
+ tr.setMeta(pluginKey, {
8
+ showInsertPanelAt: tr.selection.anchor
9
+ });
10
+ tr.scrollIntoView();
11
+ dispatch(tr);
12
+ return true;
13
+ };
14
+ export var insertPlaceholderTextAtSelection = function insertPlaceholderTextAtSelection(value) {
15
+ return function (state, dispatch) {
16
+ dispatch(state.tr.replaceSelectionWith(state.schema.nodes.placeholder.createChecked({
17
+ text: value
18
+ })).setMeta(pluginKey, {
19
+ showInsertPanelAt: null
20
+ }).scrollIntoView());
21
+ return true;
22
+ };
23
+ };
24
+ export var hidePlaceholderFloatingToolbar = function hidePlaceholderFloatingToolbar(state, dispatch) {
25
+ dispatch(state.tr.setMeta(pluginKey, {
26
+ showInsertPanelAt: null
27
+ }));
28
+ return true;
29
+ };
@@ -0,0 +1,100 @@
1
+ import _inherits from "@babel/runtime/helpers/inherits";
2
+ import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
3
+ import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
4
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
5
+ import _createClass from "@babel/runtime/helpers/createClass";
6
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
7
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
8
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
9
+ import { Slice } from '@atlaskit/editor-prosemirror/model';
10
+ import { Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
11
+ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
12
+ export var FakeTextCursorBookmark = /*#__PURE__*/function () {
13
+ function FakeTextCursorBookmark(pos) {
14
+ _classCallCheck(this, FakeTextCursorBookmark);
15
+ _defineProperty(this, "pos", undefined);
16
+ _defineProperty(this, "visible", false);
17
+ this.pos = pos;
18
+ }
19
+ _createClass(FakeTextCursorBookmark, [{
20
+ key: "map",
21
+ value: function map(mapping) {
22
+ return new FakeTextCursorBookmark(mapping.map(this.pos));
23
+ }
24
+ }, {
25
+ key: "resolve",
26
+ value: function resolve(doc) {
27
+ var $pos = doc.resolve(this.pos);
28
+ return Selection.near($pos);
29
+ }
30
+ }]);
31
+ return FakeTextCursorBookmark;
32
+ }();
33
+ export var FakeTextCursorSelection = /*#__PURE__*/function (_Selection) {
34
+ _inherits(FakeTextCursorSelection, _Selection);
35
+ var _super = _createSuper(FakeTextCursorSelection);
36
+ function FakeTextCursorSelection($pos) {
37
+ _classCallCheck(this, FakeTextCursorSelection);
38
+ return _super.call(this, $pos, $pos);
39
+ }
40
+ _createClass(FakeTextCursorSelection, [{
41
+ key: "map",
42
+ value: function map(doc, mapping) {
43
+ var $pos = doc.resolve(mapping.map(this.$head.pos));
44
+ return new FakeTextCursorSelection($pos);
45
+ }
46
+ }, {
47
+ key: "eq",
48
+ value: function eq(other) {
49
+ return other instanceof FakeTextCursorSelection && other.head === this.head;
50
+ }
51
+ }, {
52
+ key: "toJSON",
53
+ value: function toJSON() {
54
+ return {
55
+ type: 'Cursor',
56
+ pos: this.head
57
+ };
58
+ }
59
+ }, {
60
+ key: "getBookmark",
61
+ value: function getBookmark() {
62
+ return new FakeTextCursorBookmark(this.anchor);
63
+ }
64
+ }], [{
65
+ key: "content",
66
+ value: function content() {
67
+ return Slice.empty;
68
+ }
69
+ }, {
70
+ key: "fromJSON",
71
+ value: function fromJSON(doc, json) {
72
+ return new FakeTextCursorSelection(doc.resolve(json.pos));
73
+ }
74
+ }]);
75
+ return FakeTextCursorSelection;
76
+ }(Selection);
77
+ Selection.jsonID('fake-text-cursor', FakeTextCursorSelection);
78
+ export var addFakeTextCursor = function addFakeTextCursor(state, dispatch) {
79
+ var selection = state.selection;
80
+ if (selection.empty) {
81
+ var $from = state.selection.$from;
82
+ dispatch(state.tr.setSelection(new FakeTextCursorSelection($from)));
83
+ }
84
+ };
85
+ export var removeFakeTextCursor = function removeFakeTextCursor(state, dispatch) {
86
+ if (state.selection instanceof FakeTextCursorSelection) {
87
+ var $from = state.selection.$from;
88
+ dispatch(state.tr.setSelection(new TextSelection($from)));
89
+ }
90
+ };
91
+ export var drawFakeTextCursor = function drawFakeTextCursor(state) {
92
+ if (!(state.selection instanceof FakeTextCursorSelection)) {
93
+ return null;
94
+ }
95
+ var node = document.createElement('div');
96
+ node.className = 'ProseMirror-fake-text-cursor';
97
+ return DecorationSet.create(state.doc, [Decoration.widget(state.selection.head, node, {
98
+ key: 'Cursor'
99
+ })]);
100
+ };
@@ -0,0 +1 @@
1
+ export { default as placeholderTextPlugin } from './plugin';