@atlaskit/editor-common 76.34.0 → 76.35.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 (99) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/cjs/collab/index.js +27 -2
  3. package/dist/cjs/core-utils/document-logger.js +171 -0
  4. package/dist/cjs/core-utils/index.js +8 -1
  5. package/dist/cjs/extensibility/Extension/Extension/index.js +151 -0
  6. package/dist/cjs/extensibility/Extension/Extension/styles.js +17 -0
  7. package/dist/cjs/extensibility/Extension/InlineExtension/index.js +50 -0
  8. package/dist/cjs/extensibility/Extension/InlineExtension/styles.js +12 -0
  9. package/dist/cjs/extensibility/Extension/Lozenge.js +95 -0
  10. package/dist/cjs/extensibility/Extension/styles.js +30 -0
  11. package/dist/cjs/extensibility/Extension.js +79 -0
  12. package/dist/cjs/extensibility/ExtensionComponent.js +252 -0
  13. package/dist/cjs/extensibility/ExtensionNodeWrapper.js +21 -0
  14. package/dist/cjs/extensibility/MultiBodiedExtension/index.js +264 -0
  15. package/dist/cjs/extensibility/extensionNodeView.js +91 -0
  16. package/dist/cjs/extensibility/index.js +42 -0
  17. package/dist/cjs/extensibility/types.js +5 -0
  18. package/dist/cjs/monitoring/error.js +3 -2
  19. package/dist/cjs/ui/DropList/index.js +1 -1
  20. package/dist/es2019/collab/index.js +54 -1
  21. package/dist/es2019/core-utils/document-logger.js +161 -0
  22. package/dist/es2019/core-utils/index.js +2 -1
  23. package/dist/es2019/extensibility/Extension/Extension/index.js +155 -0
  24. package/dist/es2019/extensibility/Extension/Extension/styles.js +42 -0
  25. package/dist/es2019/extensibility/Extension/InlineExtension/index.js +26 -0
  26. package/dist/es2019/extensibility/Extension/InlineExtension/styles.js +26 -0
  27. package/dist/es2019/extensibility/Extension/Lozenge.js +71 -0
  28. package/dist/es2019/extensibility/Extension/styles.js +62 -0
  29. package/dist/es2019/extensibility/Extension.js +52 -0
  30. package/dist/es2019/extensibility/ExtensionComponent.js +204 -0
  31. package/dist/es2019/extensibility/ExtensionNodeWrapper.js +13 -0
  32. package/dist/es2019/extensibility/MultiBodiedExtension/index.js +283 -0
  33. package/dist/es2019/extensibility/extensionNodeView.js +62 -0
  34. package/dist/es2019/extensibility/index.js +4 -0
  35. package/dist/es2019/extensibility/types.js +1 -0
  36. package/dist/es2019/monitoring/error.js +3 -2
  37. package/dist/es2019/ui/DropList/index.js +1 -1
  38. package/dist/esm/collab/index.js +23 -1
  39. package/dist/esm/core-utils/document-logger.js +165 -0
  40. package/dist/esm/core-utils/index.js +2 -1
  41. package/dist/esm/extensibility/Extension/Extension/index.js +146 -0
  42. package/dist/esm/extensibility/Extension/Extension/styles.js +10 -0
  43. package/dist/esm/extensibility/Extension/InlineExtension/index.js +43 -0
  44. package/dist/esm/extensibility/Extension/InlineExtension/styles.js +5 -0
  45. package/dist/esm/extensibility/Extension/Lozenge.js +90 -0
  46. package/dist/esm/extensibility/Extension/styles.js +11 -0
  47. package/dist/esm/extensibility/Extension.js +69 -0
  48. package/dist/esm/extensibility/ExtensionComponent.js +243 -0
  49. package/dist/esm/extensibility/ExtensionNodeWrapper.js +14 -0
  50. package/dist/esm/extensibility/MultiBodiedExtension/index.js +257 -0
  51. package/dist/esm/extensibility/extensionNodeView.js +83 -0
  52. package/dist/esm/extensibility/index.js +4 -0
  53. package/dist/esm/extensibility/types.js +1 -0
  54. package/dist/esm/monitoring/error.js +3 -2
  55. package/dist/esm/ui/DropList/index.js +1 -1
  56. package/dist/types/collab/index.d.ts +7 -0
  57. package/dist/types/core-utils/document-logger.d.ts +6 -0
  58. package/dist/types/core-utils/index.d.ts +1 -0
  59. package/dist/types/extensibility/Extension/Extension/index.d.ts +108 -0
  60. package/dist/types/extensibility/Extension/Extension/styles.d.ts +5 -0
  61. package/dist/types/extensibility/Extension/InlineExtension/index.d.ts +12 -0
  62. package/dist/types/extensibility/Extension/InlineExtension/styles.d.ts +1 -0
  63. package/dist/types/extensibility/Extension/Lozenge.d.ts +14 -0
  64. package/dist/types/extensibility/Extension/styles.d.ts +7 -0
  65. package/dist/types/extensibility/Extension.d.ts +29 -0
  66. package/dist/types/extensibility/ExtensionComponent.d.ts +53 -0
  67. package/dist/types/extensibility/ExtensionNodeWrapper.d.ts +14 -0
  68. package/dist/types/extensibility/MultiBodiedExtension/index.d.ts +26 -0
  69. package/dist/types/extensibility/extensionNodeView.d.ts +31 -0
  70. package/dist/types/extensibility/index.d.ts +4 -0
  71. package/dist/types/extensibility/types.d.ts +6 -0
  72. package/dist/types/types/index.d.ts +1 -1
  73. package/dist/types/types/next-editor-plugin.d.ts +0 -1
  74. package/dist/types/ui/DropList/index.d.ts +1 -1
  75. package/dist/types/ui-menu/ColorPickerButton/index.d.ts +1 -1
  76. package/dist/types/ui-menu/ToolbarButton/index.d.ts +1 -1
  77. package/dist/types-ts4.5/collab/index.d.ts +7 -0
  78. package/dist/types-ts4.5/core-utils/document-logger.d.ts +6 -0
  79. package/dist/types-ts4.5/core-utils/index.d.ts +1 -0
  80. package/dist/types-ts4.5/extensibility/Extension/Extension/index.d.ts +108 -0
  81. package/dist/types-ts4.5/extensibility/Extension/Extension/styles.d.ts +5 -0
  82. package/dist/types-ts4.5/extensibility/Extension/InlineExtension/index.d.ts +12 -0
  83. package/dist/types-ts4.5/extensibility/Extension/InlineExtension/styles.d.ts +1 -0
  84. package/dist/types-ts4.5/extensibility/Extension/Lozenge.d.ts +14 -0
  85. package/dist/types-ts4.5/extensibility/Extension/styles.d.ts +7 -0
  86. package/dist/types-ts4.5/extensibility/Extension.d.ts +29 -0
  87. package/dist/types-ts4.5/extensibility/ExtensionComponent.d.ts +53 -0
  88. package/dist/types-ts4.5/extensibility/ExtensionNodeWrapper.d.ts +14 -0
  89. package/dist/types-ts4.5/extensibility/MultiBodiedExtension/index.d.ts +26 -0
  90. package/dist/types-ts4.5/extensibility/extensionNodeView.d.ts +31 -0
  91. package/dist/types-ts4.5/extensibility/index.d.ts +4 -0
  92. package/dist/types-ts4.5/extensibility/types.d.ts +8 -0
  93. package/dist/types-ts4.5/types/index.d.ts +1 -1
  94. package/dist/types-ts4.5/types/next-editor-plugin.d.ts +0 -3
  95. package/dist/types-ts4.5/ui/DropList/index.d.ts +1 -1
  96. package/dist/types-ts4.5/ui-menu/ColorPickerButton/index.d.ts +1 -1
  97. package/dist/types-ts4.5/ui-menu/ToolbarButton/index.d.ts +1 -1
  98. package/extensibility/package.json +15 -0
  99. package/package.json +5 -4
@@ -0,0 +1,71 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ /** @jsx jsx */
3
+ import { Component } from 'react';
4
+ import { jsx } from '@emotion/react';
5
+ import EditorFileIcon from '@atlaskit/icon/glyph/editor/file';
6
+ import { getExtensionLozengeData } from '../../utils';
7
+ import { placeholderFallback, placeholderFallbackParams, styledImage } from './styles';
8
+ export const capitalizeFirstLetter = str => {
9
+ return str.charAt(0).toUpperCase() + str.slice(1);
10
+ };
11
+ export const ICON_SIZE = 24;
12
+ export default class ExtensionLozenge extends Component {
13
+ render() {
14
+ const {
15
+ node
16
+ } = this.props;
17
+ const imageData = getExtensionLozengeData({
18
+ node,
19
+ type: 'image'
20
+ });
21
+ if (imageData && node.type.name !== 'extension') {
22
+ return this.renderImage(imageData);
23
+ }
24
+ const iconData = getExtensionLozengeData({
25
+ node,
26
+ type: 'icon'
27
+ });
28
+ return this.renderFallback(iconData);
29
+ }
30
+ renderImage(lozengeData) {
31
+ const {
32
+ extensionKey
33
+ } = this.props.node.attrs;
34
+ const {
35
+ url,
36
+ ...rest
37
+ } = lozengeData;
38
+ return jsx("img", _extends({
39
+ css: styledImage,
40
+ src: url
41
+ }, rest, {
42
+ alt: extensionKey
43
+ }));
44
+ }
45
+ renderFallback(lozengeData) {
46
+ const {
47
+ parameters,
48
+ extensionKey
49
+ } = this.props.node.attrs;
50
+ const {
51
+ name
52
+ } = this.props.node.type;
53
+ const params = parameters && parameters.macroParams;
54
+ const title = parameters && parameters.extensionTitle || parameters && parameters.macroMetadata && parameters.macroMetadata.title || extensionKey;
55
+ const isBlockExtension = name === 'extension';
56
+ return jsx("div", {
57
+ "data-testid": "lozenge-fallback",
58
+ css: placeholderFallback
59
+ }, lozengeData && !isBlockExtension ? this.renderImage({
60
+ height: ICON_SIZE,
61
+ width: ICON_SIZE,
62
+ ...lozengeData
63
+ }) : jsx(EditorFileIcon, {
64
+ label: title
65
+ }), jsx("span", {
66
+ className: "extension-title"
67
+ }, capitalizeFirstLetter(title)), params && !isBlockExtension && jsx("span", {
68
+ css: placeholderFallbackParams
69
+ }, Object.keys(params).map(key => key && ` | ${key} = ${params[key].value}`)));
70
+ }
71
+ }
@@ -0,0 +1,62 @@
1
+ import { css } from '@emotion/react';
2
+ import { B200, N20, N20A, N70 } from '@atlaskit/theme/colors';
3
+ import { BODIED_EXT_PADDING, EXTENSION_PADDING } from '../../styles';
4
+ export { EXTENSION_PADDING as padding, BODIED_EXT_PADDING };
5
+ export const wrapperDefault = css`
6
+ background: ${`var(--ds-background-neutral, ${N20})`};
7
+ border-radius: ${"var(--ds-border-radius, 3px)"};
8
+ position: relative;
9
+ vertical-align: middle;
10
+
11
+ .ProseMirror-selectednode > span > & > .extension-overlay {
12
+ box-shadow: inset 0px 0px 0px 2px ${`var(--ds-border-selected, ${B200})`};
13
+ opacity: 1;
14
+ }
15
+
16
+ &.with-overlay {
17
+ .extension-overlay {
18
+ background: ${`var(--ds-background-neutral-hovered, ${N20A})`};
19
+ color: transparent;
20
+ }
21
+
22
+ &:hover .extension-overlay {
23
+ opacity: 1;
24
+ }
25
+ }
26
+ `;
27
+ export const overlay = css`
28
+ border-radius: ${"var(--ds-border-radius, 3px)"};
29
+ position: absolute;
30
+ width: 100%;
31
+ height: 100%;
32
+ opacity: 0;
33
+ pointer-events: none;
34
+ transition: opacity 0.3s;
35
+ `;
36
+ export const placeholderFallback = css`
37
+ display: inline-flex;
38
+ align-items: center;
39
+
40
+ & > img {
41
+ margin: 0 ${"var(--ds-space-050, 4px)"};
42
+ }
43
+ /* TODO: fix in develop: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E */
44
+ /* stylelint-disable-next-line */
45
+ label: placeholder-fallback;
46
+ `;
47
+ export const placeholderFallbackParams = css`
48
+ display: inline-block;
49
+ max-width: 200px;
50
+ margin-left: 5px;
51
+ color: ${`var(--ds-text-subtlest, ${N70})`};
52
+ text-overflow: ellipsis;
53
+ white-space: nowrap;
54
+ overflow: hidden;
55
+ `;
56
+ export const styledImage = css`
57
+ max-height: 16px;
58
+ max-width: 16px;
59
+ /* TODO: fix in develop: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E */
60
+ /* stylelint-disable-next-line */
61
+ label: lozenge-image;
62
+ `;
@@ -0,0 +1,52 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import React, { Component } from 'react';
3
+ import { ProviderFactory, WithProviders } from '../provider-factory';
4
+ import { ExtensionComponent } from './ExtensionComponent';
5
+ export class Extension extends Component {
6
+ constructor(props) {
7
+ super(props);
8
+ _defineProperty(this, "renderWithProvider", ({
9
+ extensionProvider
10
+ }) => {
11
+ const {
12
+ node,
13
+ getPos,
14
+ editorView,
15
+ handleContentDOMRef,
16
+ extensionHandlers,
17
+ references,
18
+ editorAppearance,
19
+ pluginInjectionApi,
20
+ eventDispatcher
21
+ } = this.props;
22
+ return /*#__PURE__*/React.createElement(ExtensionComponent, {
23
+ editorView: editorView,
24
+ node: node,
25
+ getPos: getPos,
26
+ references: references,
27
+ extensionProvider: extensionProvider,
28
+ handleContentDOMRef: handleContentDOMRef,
29
+ extensionHandlers: extensionHandlers,
30
+ editorAppearance: editorAppearance,
31
+ pluginInjectionApi: pluginInjectionApi,
32
+ eventDispatcher: eventDispatcher
33
+ });
34
+ });
35
+ this.providerFactory = props.providerFactory || new ProviderFactory();
36
+ }
37
+ componentWillUnmount() {
38
+ if (!this.props.providerFactory) {
39
+ // new ProviderFactory is created if no `providers` has been set
40
+ // in this case when component is unmounted it's safe to destroy this providerFactory
41
+ this.providerFactory.destroy();
42
+ }
43
+ }
44
+ render() {
45
+ return /*#__PURE__*/React.createElement(WithProviders, {
46
+ providers: ['extensionProvider'],
47
+ providerFactory: this.providerFactory,
48
+ renderNode: this.renderWithProvider
49
+ });
50
+ }
51
+ }
52
+ _defineProperty(Extension, "displayName", 'Extension');
@@ -0,0 +1,204 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import React from 'react';
3
+ import { Component } from 'react';
4
+ import memoizeOne from 'memoize-one';
5
+ import { getExtensionModuleNodePrivateProps, getNodeRenderer } from '../extensions';
6
+ import { getExtensionRenderer } from '../utils';
7
+ import Extension from './Extension/Extension';
8
+ import InlineExtension from './Extension/InlineExtension';
9
+ import MultiBodiedExtension from './MultiBodiedExtension';
10
+ export class ExtensionComponent extends Component {
11
+ constructor(...args) {
12
+ super(...args);
13
+ _defineProperty(this, "privatePropsParsed", false);
14
+ _defineProperty(this, "state", {});
15
+ _defineProperty(this, "mounted", false);
16
+ // memoized to avoid rerender on extension state changes
17
+ _defineProperty(this, "getNodeRenderer", memoizeOne(getNodeRenderer));
18
+ _defineProperty(this, "getExtensionModuleNodePrivateProps", memoizeOne(getExtensionModuleNodePrivateProps));
19
+ _defineProperty(this, "setStateFromPromise", (stateKey, promise) => {
20
+ promise && promise.then(p => {
21
+ if (!this.mounted) {
22
+ return;
23
+ }
24
+ this.setState({
25
+ [stateKey]: p
26
+ });
27
+ });
28
+ });
29
+ /**
30
+ * Parses any private nodes once an extension provider is available.
31
+ *
32
+ * We do this separately from resolving a node renderer component since the
33
+ * private props come from extension provider, rather than an extension
34
+ * handler which only handles `render`/component concerns.
35
+ */
36
+ _defineProperty(this, "parsePrivateNodePropsIfNeeded", async () => {
37
+ if (this.privatePropsParsed || !this.state.extensionProvider) {
38
+ return;
39
+ }
40
+ this.privatePropsParsed = true;
41
+ const {
42
+ extensionType,
43
+ extensionKey
44
+ } = this.props.node.attrs;
45
+
46
+ /**
47
+ * getExtensionModuleNodePrivateProps can throw if there are issues in the
48
+ * manifest
49
+ */
50
+ try {
51
+ const privateProps = await this.getExtensionModuleNodePrivateProps(this.state.extensionProvider, extensionType, extensionKey);
52
+ this.setState({
53
+ _privateProps: privateProps
54
+ });
55
+ } catch (e) {
56
+ // eslint-disable-next-line no-console
57
+ console.error('Provided extension handler has thrown an error\n', e);
58
+ /** We don't want this error to block renderer */
59
+ /** We keep rendering the default content */
60
+ }
61
+ });
62
+ _defineProperty(this, "handleExtension", (pmNode, actions) => {
63
+ var _pmNode$marks, _pmNode$marks$find, _pmNode$marks$find$at;
64
+ const {
65
+ extensionHandlers,
66
+ editorView
67
+ } = this.props;
68
+ const {
69
+ extensionType,
70
+ extensionKey,
71
+ parameters,
72
+ text
73
+ } = pmNode.attrs;
74
+ const isBodiedExtension = pmNode.type.name === 'bodiedExtension';
75
+ if (isBodiedExtension) {
76
+ return;
77
+ }
78
+ const fragmentLocalId = pmNode === null || pmNode === void 0 ? void 0 : (_pmNode$marks = pmNode.marks) === null || _pmNode$marks === void 0 ? void 0 : (_pmNode$marks$find = _pmNode$marks.find(m => m.type.name === 'fragment')) === null || _pmNode$marks$find === void 0 ? void 0 : (_pmNode$marks$find$at = _pmNode$marks$find.attrs) === null || _pmNode$marks$find$at === void 0 ? void 0 : _pmNode$marks$find$at.localId;
79
+ const node = {
80
+ type: pmNode.type.name,
81
+ extensionType,
82
+ extensionKey,
83
+ parameters,
84
+ content: text,
85
+ localId: pmNode.attrs.localId,
86
+ fragmentLocalId
87
+ };
88
+ let result;
89
+ if (extensionHandlers && extensionHandlers[extensionType]) {
90
+ const render = getExtensionRenderer(extensionHandlers[extensionType]);
91
+ result = render(node, editorView.state.doc, actions);
92
+ }
93
+ if (!result) {
94
+ const extensionHandlerFromProvider = this.state.extensionProvider && this.getNodeRenderer(this.state.extensionProvider, extensionType, extensionKey);
95
+ if (extensionHandlerFromProvider) {
96
+ const NodeRenderer = extensionHandlerFromProvider;
97
+ if (node.type === 'multiBodiedExtension') {
98
+ return /*#__PURE__*/React.createElement(NodeRenderer, {
99
+ node: node,
100
+ references: this.props.references,
101
+ actions: actions
102
+ });
103
+ } else {
104
+ return /*#__PURE__*/React.createElement(NodeRenderer, {
105
+ node: node,
106
+ references: this.props.references
107
+ });
108
+ }
109
+ }
110
+ }
111
+ return result;
112
+ });
113
+ }
114
+ UNSAFE_componentWillMount() {
115
+ this.mounted = true;
116
+ }
117
+ componentDidMount() {
118
+ const {
119
+ extensionProvider
120
+ } = this.props;
121
+ if (extensionProvider) {
122
+ this.setStateFromPromise('extensionProvider', extensionProvider);
123
+ }
124
+ }
125
+ componentDidUpdate() {
126
+ this.parsePrivateNodePropsIfNeeded();
127
+ }
128
+ componentWillUnmount() {
129
+ this.mounted = false;
130
+ }
131
+ UNSAFE_componentWillReceiveProps(nextProps) {
132
+ const {
133
+ extensionProvider
134
+ } = nextProps;
135
+ if (extensionProvider && this.props.extensionProvider !== extensionProvider) {
136
+ this.setStateFromPromise('extensionProvider', extensionProvider);
137
+ }
138
+ }
139
+ render() {
140
+ var _this$state$_privateP;
141
+ const {
142
+ node,
143
+ handleContentDOMRef,
144
+ editorView,
145
+ references,
146
+ editorAppearance,
147
+ pluginInjectionApi,
148
+ getPos,
149
+ eventDispatcher
150
+ } = this.props;
151
+ if (node.type.name === 'multiBodiedExtension') {
152
+ return /*#__PURE__*/React.createElement(MultiBodiedExtension, {
153
+ node: node,
154
+ editorView: editorView,
155
+ getPos: getPos,
156
+ handleContentDOMRef: handleContentDOMRef,
157
+ tryExtensionHandler: this.tryExtensionHandler.bind(this),
158
+ eventDispatcher: eventDispatcher,
159
+ pluginInjectionApi: pluginInjectionApi,
160
+ editorAppearance: editorAppearance
161
+ });
162
+ }
163
+ const extensionHandlerResult = this.tryExtensionHandler(undefined);
164
+ switch (node.type.name) {
165
+ case 'extension':
166
+ case 'bodiedExtension':
167
+ return /*#__PURE__*/React.createElement(Extension, {
168
+ node: node,
169
+ getPos: this.props.getPos,
170
+ references: references,
171
+ extensionProvider: this.state.extensionProvider,
172
+ handleContentDOMRef: handleContentDOMRef,
173
+ view: editorView,
174
+ editorAppearance: editorAppearance,
175
+ hideFrame: (_this$state$_privateP = this.state._privateProps) === null || _this$state$_privateP === void 0 ? void 0 : _this$state$_privateP.__hideFrame,
176
+ pluginInjectionApi: pluginInjectionApi
177
+ }, extensionHandlerResult);
178
+ case 'inlineExtension':
179
+ return /*#__PURE__*/React.createElement(InlineExtension, {
180
+ node: node
181
+ }, extensionHandlerResult);
182
+ default:
183
+ return null;
184
+ }
185
+ }
186
+ tryExtensionHandler(actions) {
187
+ const {
188
+ node
189
+ } = this.props;
190
+ try {
191
+ const extensionContent = this.handleExtension(node, actions);
192
+ if (extensionContent && /*#__PURE__*/React.isValidElement(extensionContent)) {
193
+ return extensionContent;
194
+ }
195
+ } catch (e) {
196
+ // eslint-disable-next-line no-console
197
+ console.error('Provided extension handler has thrown an error\n', e);
198
+ /** We don't want this error to block renderer */
199
+ /** We keep rendering the default content */
200
+ }
201
+
202
+ return null;
203
+ }
204
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { ZERO_WIDTH_SPACE } from '../utils';
3
+ /**
4
+ * If inlineExtension, add zero width space to the end of the nodes and wrap with span;
5
+ * else wrap with a div (for multi bodied extensions)
6
+ *
7
+ * @param param0
8
+ * @returns
9
+ */
10
+ export const ExtensionNodeWrapper = ({
11
+ children,
12
+ nodeType
13
+ }) => /*#__PURE__*/React.createElement("span", null, children, nodeType === 'inlineExtension' && ZERO_WIDTH_SPACE);
@@ -0,0 +1,283 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ /** @jsx jsx */
3
+
4
+ import React, { useState } from 'react';
5
+ import { css, jsx } from '@emotion/react';
6
+ import { N30, N40, N50 } from '@atlaskit/theme/colors';
7
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '../../analytics';
8
+ import { createDispatch } from '../../event-dispatcher';
9
+ import { useSharedPluginState } from '../../hooks';
10
+ import { analyticsEventKey, calculateBreakoutStyles } from '../../utils';
11
+ import { WithPluginState } from '../../with-plugin-state';
12
+ const useMultiBodiedExtensionActions = ({
13
+ updateActiveChild,
14
+ editorView,
15
+ getPos,
16
+ node,
17
+ eventDispatcher
18
+ }) => {
19
+ const actions = React.useMemo(() => {
20
+ return {
21
+ changeActive(index) {
22
+ const updateActiveChildResult = updateActiveChild(index);
23
+ if (eventDispatcher) {
24
+ sendMBEAnalyticsEvent(ACTION.CHANGE_ACTIVE, node, eventDispatcher);
25
+ }
26
+ return updateActiveChildResult;
27
+ },
28
+ addChild() {
29
+ const {
30
+ state,
31
+ dispatch
32
+ } = editorView;
33
+ if (node.content.childCount >= node.attrs.maxFrames) {
34
+ throw new Error(`Cannot add more than ${node.attrs.maxFrames} frames`);
35
+ }
36
+ const p = state.schema.nodes.paragraph.createAndFill({});
37
+ if (!p) {
38
+ throw new Error('Could not create paragraph');
39
+ }
40
+ const frame = state.schema.nodes.extensionFrame.createAndFill({}, [p]);
41
+ const pos = getPos();
42
+ if (typeof pos !== 'number' || !frame) {
43
+ throw new Error('Could not create frame or position not valid');
44
+ }
45
+ const insertAt = Math.min((pos || 1) + node.content.size, state.doc.content.size);
46
+ dispatch(state.tr.insert(insertAt, frame));
47
+ if (eventDispatcher) {
48
+ sendMBEAnalyticsEvent(ACTION.ADD_CHILD, node, eventDispatcher);
49
+ }
50
+ return true;
51
+ },
52
+ getChildrenCount() {
53
+ return node.content.childCount;
54
+ },
55
+ removeChild(index) {
56
+ const pos = getPos();
57
+ // TODO: Add child index validation here, don't trust this data
58
+ if (typeof pos !== 'number' || typeof index !== 'number') {
59
+ throw new Error('Position or index not valid');
60
+ }
61
+ const {
62
+ state,
63
+ dispatch
64
+ } = editorView;
65
+ if (node.content.childCount === 1) {
66
+ const tr = state.tr;
67
+ tr.deleteRange(pos, pos + node.content.size);
68
+ dispatch(tr);
69
+ return true;
70
+ }
71
+ const $pos = state.doc.resolve(pos);
72
+ const $startNodePos = state.doc.resolve($pos.start($pos.depth + 1));
73
+ const startFramePosition = $startNodePos.posAtIndex(index);
74
+ const maybeFrameNode = state.doc.nodeAt(startFramePosition);
75
+ if (!maybeFrameNode) {
76
+ throw new Error('Could not find frame node');
77
+ }
78
+ const endFramePosition = maybeFrameNode.content.size + startFramePosition;
79
+ const tr = state.tr;
80
+ tr.deleteRange(startFramePosition, endFramePosition);
81
+ dispatch(tr);
82
+ if (eventDispatcher) {
83
+ sendMBEAnalyticsEvent(ACTION.REMOVE_CHILD, node, eventDispatcher);
84
+ }
85
+ return true;
86
+ },
87
+ updateParameters(parameters) {
88
+ const {
89
+ state,
90
+ dispatch
91
+ } = editorView;
92
+ const pos = getPos();
93
+ if (typeof pos !== 'number') {
94
+ throw new Error('Position not valid');
95
+ }
96
+ const tr = state.tr.setNodeMarkup(pos, undefined, {
97
+ ...node.attrs,
98
+ parameters: {
99
+ ...node.attrs.parameters,
100
+ macroParams: parameters
101
+ }
102
+ });
103
+ dispatch(tr);
104
+ if (eventDispatcher) {
105
+ sendMBEAnalyticsEvent(ACTION.UPDATE_PARAMETERS, node, eventDispatcher);
106
+ }
107
+ return true;
108
+ },
109
+ getChildren() {
110
+ var _state$doc$nodeAt;
111
+ const {
112
+ state
113
+ } = editorView;
114
+ const pos = getPos();
115
+ if (typeof pos !== 'number') {
116
+ return [];
117
+ }
118
+ const children = (_state$doc$nodeAt = state.doc.nodeAt(pos)) === null || _state$doc$nodeAt === void 0 ? void 0 : _state$doc$nodeAt.content;
119
+ if (eventDispatcher) {
120
+ sendMBEAnalyticsEvent(ACTION.GET_CHILDERN, node, eventDispatcher);
121
+ }
122
+ return children ? children.toJSON() : [];
123
+ }
124
+ };
125
+ }, [node, editorView, getPos, updateActiveChild, eventDispatcher]);
126
+ return actions;
127
+ };
128
+ const navigationCSS = css`
129
+ // make sure the user can't see a range selection inside the navigation
130
+ // This is really important to keep the navigation working properly
131
+ user-select: none;
132
+ -webkit-user-modify: read-only;
133
+ border: 1px solid ${`var(--ds-border, ${N40})`};
134
+ `;
135
+ const MultiBodiedExtensionWithWidth = ({
136
+ node,
137
+ handleContentDOMRef,
138
+ getPos,
139
+ tryExtensionHandler,
140
+ editorView,
141
+ eventDispatcher,
142
+ widthState,
143
+ editorAppearance
144
+ }) => {
145
+ const [activeChildIndex, setActiveChildIndex] = useState(0);
146
+ // Adding to avoid aliasing `this` for the callbacks
147
+ const updateActiveChild = React.useCallback(index => {
148
+ if (typeof index !== 'number') {
149
+ setActiveChildIndex(0);
150
+ throw new Error('Index is not valid');
151
+ }
152
+ setActiveChildIndex(index);
153
+ return true;
154
+ }, [setActiveChildIndex]);
155
+ const actions = useMultiBodiedExtensionActions({
156
+ updateActiveChild,
157
+ editorView,
158
+ getPos,
159
+ eventDispatcher,
160
+ node
161
+ });
162
+ const extensionHandlerResult = React.useMemo(() => {
163
+ return tryExtensionHandler(actions);
164
+ }, [tryExtensionHandler, actions]);
165
+ const articleRef = React.useCallback(node => {
166
+ return handleContentDOMRef(node);
167
+ }, [handleContentDOMRef]);
168
+ const containerCSS = css`
169
+ border: 1px solid ${`var(--ds-border, ${N30})`};
170
+ min-height: 100px;
171
+ .multiBodiedExtension-content-dom-wrapper > [data-extension-frame='true'] {
172
+ display: none;
173
+ }
174
+
175
+ .multiBodiedExtension-content-dom-wrapper
176
+ > [data-extension-frame='true']:nth-of-type(${activeChildIndex + 1}) {
177
+ border: 1px solid ${`var(--ds-border, ${N50})`};
178
+ display: block;
179
+ min-height: 100px;
180
+ }
181
+ `;
182
+ const shouldBreakout =
183
+ // Extension should breakout when the layout is set to 'full-width' or 'wide'.
184
+ ['full-width', 'wide'].includes(node.attrs.layout) &&
185
+ // Extension breakout state should not be respected when the editor appearance is full-width mode
186
+ editorAppearance !== 'full-width';
187
+ let mbeContainerStyles = {};
188
+ if (shouldBreakout) {
189
+ const {
190
+ ...breakoutStyles
191
+ } = calculateBreakoutStyles({
192
+ mode: node.attrs.layout,
193
+ widthStateLineLength: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength,
194
+ widthStateWidth: widthState === null || widthState === void 0 ? void 0 : widthState.width
195
+ });
196
+ mbeContainerStyles = breakoutStyles;
197
+ }
198
+ return jsx("section", {
199
+ className: "multiBodiedExtension--container",
200
+ css: containerCSS,
201
+ "data-testid": "multiBodiedExtension--container",
202
+ "data-active-child-index": activeChildIndex,
203
+ style: mbeContainerStyles
204
+ }, jsx("nav", {
205
+ className: "multiBodiedExtension-navigation",
206
+ css: navigationCSS,
207
+ "data-testid": "multiBodiedExtension-navigation"
208
+ }, extensionHandlerResult), jsx("article", {
209
+ className: "multiBodiedExtension--frames",
210
+ "data-testid": "multiBodiedExtension--frames",
211
+ ref: articleRef
212
+ }));
213
+ };
214
+ const sendMBEAnalyticsEvent = (action, node, eventDispatcher) => {
215
+ const analyticsDispatch = createDispatch(eventDispatcher);
216
+ analyticsDispatch(analyticsEventKey, {
217
+ payload: {
218
+ action,
219
+ actionSubject: ACTION_SUBJECT.MULTI_BODIED_EXTENSION,
220
+ eventType: EVENT_TYPE.TRACK,
221
+ attributes: {
222
+ extensionType: node.attrs.extensionType,
223
+ extensionKey: node.attrs.extensionKey,
224
+ localId: node.attrs.localId,
225
+ maxFramesCount: node.attrs.maxFrames,
226
+ currentFramesCount: node.content.childCount
227
+ }
228
+ }
229
+ });
230
+ };
231
+ const MultiBodiedExtensionWithSharedState = props => {
232
+ const {
233
+ pluginInjectionApi
234
+ } = props;
235
+ const {
236
+ widthState
237
+ } = useSharedPluginState(pluginInjectionApi, ['width']);
238
+ return jsx(MultiBodiedExtensionWithWidth, _extends({
239
+ widthState: widthState
240
+ }, props));
241
+ };
242
+
243
+ // Workaround taken from platform/packages/editor/editor-core/src/plugins/extension/ui/Extension/Extension/index.tsx
244
+ const MultiBodiedExtension = props => {
245
+ // TODO: ED-17836 This code is here because confluence injects
246
+ // the `editor-referentiality` plugin via `dangerouslyAppendPlugins`
247
+ // which cannot access the `pluginInjectionApi`. When we move
248
+ // Confluence to using presets we can remove this workaround.
249
+ const {
250
+ pluginInjectionApi
251
+ } = props;
252
+ return pluginInjectionApi === undefined ? jsx(MultiBodiedExtensionDeprecated, props) : jsx(MultiBodiedExtensionWithSharedState, props);
253
+ };
254
+
255
+ // TODO: ED-17836 This code is here because Confluence injects
256
+ // the `editor-referentiality` plugin via `dangerouslyAppendPlugins`
257
+ // which cannot access the `pluginInjectionApi`. When we move
258
+ // Confluence to using presets we can remove this workaround.
259
+ // @ts-ignore
260
+ const widthPluginKey = {
261
+ key: 'widthPlugin$',
262
+ getState: state => {
263
+ return state['widthPlugin$'];
264
+ }
265
+ };
266
+ const MultiBodiedExtensionDeprecated = props => {
267
+ return jsx(WithPluginState, {
268
+ editorView: props.editorView,
269
+ plugins: {
270
+ widthState: widthPluginKey
271
+ },
272
+ render: ({
273
+ widthState
274
+ }) => jsx(MultiBodiedExtensionWithWidth, _extends({
275
+ widthState: widthState
276
+ }, props))
277
+ });
278
+ };
279
+ /**
280
+ * End workaround
281
+ */
282
+
283
+ export default MultiBodiedExtension;