@atlaskit/editor-core 215.18.2 → 215.19.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @atlaskit/editor-core
2
2
 
3
+ ## 215.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`c7acfc11f076a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c7acfc11f076a) -
8
+ [https://hello.jira.atlassian.cloud/browse/EDITOR-3745](EDITOR-3745) - adopt EditorSSRRenderer to
9
+ ReactEditorView
10
+
11
+ ### Patch Changes
12
+
13
+ - [`d2e5db925723b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d2e5db925723b) -
14
+ EDITOR-2772 Update selection extension types for block menu
15
+ - Updated dependencies
16
+
3
17
  ## 215.18.2
4
18
 
5
19
  ### Patch Changes
@@ -57,6 +57,9 @@
57
57
  {
58
58
  "path": "../../editor-shared-styles/afm-cc/tsconfig.json"
59
59
  },
60
+ {
61
+ "path": "../../editor-ssr-renderer/afm-cc/tsconfig.json"
62
+ },
60
63
  {
61
64
  "path": "../../editor-toolbar/afm-cc/tsconfig.json"
62
65
  },
@@ -57,6 +57,9 @@
57
57
  {
58
58
  "path": "../../editor-shared-styles/afm-products/tsconfig.json"
59
59
  },
60
+ {
61
+ "path": "../../editor-ssr-renderer/afm-products/tsconfig.json"
62
+ },
60
63
  {
61
64
  "path": "../../editor-toolbar/afm-products/tsconfig.json"
62
65
  },
@@ -14,6 +14,7 @@ var _react = _interopRequireWildcard(require("react"));
14
14
  var _reactIntlNext = require("react-intl-next");
15
15
  var _v = _interopRequireDefault(require("uuid/v4"));
16
16
  var _analytics = require("@atlaskit/editor-common/analytics");
17
+ var _coreUtils = require("@atlaskit/editor-common/core-utils");
17
18
  var _eventDispatcher = require("@atlaskit/editor-common/event-dispatcher");
18
19
  var _hooks = require("@atlaskit/editor-common/hooks");
19
20
  var _nodeVisibility = require("@atlaskit/editor-common/node-visibility");
@@ -27,6 +28,7 @@ var _analytics2 = require("@atlaskit/editor-common/utils/analytics");
27
28
  var _document = require("@atlaskit/editor-common/utils/document");
28
29
  var _state2 = require("@atlaskit/editor-prosemirror/state");
29
30
  var _view = require("@atlaskit/editor-prosemirror/view");
31
+ var _editorSsrRenderer = require("@atlaskit/editor-ssr-renderer");
30
32
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
31
33
  var _interactionMetrics = require("@atlaskit/react-ufo/interaction-metrics");
32
34
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
@@ -125,6 +127,19 @@ function ReactEditorView(props) {
125
127
  getEditorView: getEditorView,
126
128
  fireAnalyticsEvent: handleAnalyticsEvent
127
129
  }));
130
+ var parseDoc = (0, _react.useCallback)(function (schema, api, options) {
131
+ if (!options.doc) {
132
+ return undefined;
133
+ }
134
+
135
+ // if the collabEdit API is set, skip this validation due to potential pm validation errors
136
+ // from docs that end up with invalid marks after processing (See #hot-111702 for more details)
137
+ if ((api === null || api === void 0 ? void 0 : api.collabEdit) !== undefined || options.props.editorProps.skipValidation) {
138
+ return (0, _processRawValue.processRawValueWithoutValidation)(schema, options.doc, dispatchAnalyticsEvent);
139
+ } else {
140
+ return (0, _processRawValue.processRawValue)(schema, options.doc, options.props.providerFactory, options.props.editorProps.sanitizePrivateContent, contentTransformer.current, dispatchAnalyticsEvent);
141
+ }
142
+ }, [dispatchAnalyticsEvent]);
128
143
  var createEditorState = (0, _react.useCallback)(function (options) {
129
144
  var _api$editorViewMode;
130
145
  var schema;
@@ -176,16 +191,7 @@ function ReactEditorView(props) {
176
191
  var api = pluginInjectionAPI.current.api();
177
192
 
178
193
  // If we have a doc prop, we need to process it into a PMNode
179
- var doc;
180
- if (options.doc) {
181
- // if the collabEdit API is set, skip this validation due to potential pm validation errors
182
- // from docs that end up with invalid marks after processing (See #hot-111702 for more details)
183
- if ((api === null || api === void 0 ? void 0 : api.collabEdit) !== undefined || options.props.editorProps.skipValidation) {
184
- doc = (0, _processRawValue.processRawValueWithoutValidation)(schema, options.doc, dispatchAnalyticsEvent);
185
- } else {
186
- doc = (0, _processRawValue.processRawValue)(schema, options.doc, options.props.providerFactory, options.props.editorProps.sanitizePrivateContent, contentTransformer.current, dispatchAnalyticsEvent);
187
- }
188
- }
194
+ var doc = parseDoc(schema, api, options);
189
195
  var isViewMode = (api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.currentState().mode) === 'view';
190
196
  var selection;
191
197
  if (doc) {
@@ -210,8 +216,13 @@ function ReactEditorView(props) {
210
216
  doc: doc,
211
217
  selection: patchedSelection
212
218
  });
213
- }, [errorReporter, featureFlags, props.intl, props.portalProviderAPI, props.nodeViewPortalProviderAPI, props.editorProps, dispatchAnalyticsEvent, eventDispatcher, dispatch]);
219
+ }, [errorReporter, featureFlags, parseDoc, props.intl, props.portalProviderAPI, props.nodeViewPortalProviderAPI, props.editorProps, dispatchAnalyticsEvent, eventDispatcher, dispatch]);
214
220
  var initialEditorState = (0, _react.useMemo)(function () {
221
+ if ((0, _coreUtils.isSSR)() && (0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
222
+ // We don't need to create initial state in SSR, it would be done by EditorSSRRenderer,
223
+ // so we can save some CPU time here.
224
+ return undefined;
225
+ }
215
226
  return createEditorState({
216
227
  props: props,
217
228
  doc: defaultValue,
@@ -288,6 +299,10 @@ function ReactEditorView(props) {
288
299
  });
289
300
  });
290
301
  (0, _react.useLayoutEffect)(function () {
302
+ if ((0, _coreUtils.isSSR)() && (0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
303
+ return;
304
+ }
305
+
291
306
  // Transaction dispatching is already enabled by default prior to
292
307
  // mounting, but we reset it here, just in case the editor view
293
308
  // instance is ever recycled (mounted again after unmounting) with
@@ -308,6 +323,10 @@ function ReactEditorView(props) {
308
323
 
309
324
  // Cleanup
310
325
  (0, _react.useLayoutEffect)(function () {
326
+ if ((0, _coreUtils.isSSR)() && (0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
327
+ // No cleanup in SSR should happened because SSR doesn't render a real editor.
328
+ return;
329
+ }
311
330
  return function () {
312
331
  var focusTimeoutIdCurrent = focusTimeoutId.current;
313
332
  if (focusTimeoutIdCurrent) {
@@ -406,8 +425,15 @@ function ReactEditorView(props) {
406
425
  taskDecisionProvider: props.editorProps.taskDecisionProvider
407
426
  });
408
427
  var getDirectEditorProps = (0, _react.useCallback)(function (state) {
428
+ var stateToUse = state !== null && state !== void 0 ? state : getCurrentEditorState();
429
+ if (!stateToUse) {
430
+ // This should not be happened, because initialState is only inavailable in SSR,
431
+ // but in SSR this function should never be called.
432
+ // In SSR we should use EditorSSRRenderer instead usual ProseMirror editor.
433
+ throw new Error('No editor state found');
434
+ }
409
435
  return {
410
- state: state !== null && state !== void 0 ? state : getCurrentEditorState(),
436
+ state: stateToUse,
411
437
  dispatchTransaction: function dispatchTransaction(tr) {
412
438
  // Block stale transactions:
413
439
  // Prevent runtime exceptions from async transactions that would attempt to
@@ -516,6 +542,10 @@ function ReactEditorView(props) {
516
542
  (0, _isFullPage.isFullPage)(props.editorProps.appearance) && originalScrollToRestore.current && originalScrollToRestore.current !== 0;
517
543
  (0, _react.useLayoutEffect)(function () {
518
544
  var _editorView$props$edi, _editorView$props;
545
+ if ((0, _coreUtils.isSSR)() && (0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
546
+ // We don't need to focus anything in SSR.
547
+ return;
548
+ }
519
549
  if (shouldFocus && editorView !== null && editorView !== void 0 && (_editorView$props$edi = (_editorView$props = editorView.props).editable) !== null && _editorView$props$edi !== void 0 && _editorView$props$edi.call(_editorView$props, editorView.state) && (0, _platformFeatureFlags.fg)('platform_editor_react_18_autofocus_fix')) {
520
550
  if (!mitigateScrollJump) {
521
551
  var liveDocWithContent = (__livePage || (0, _expValEquals.expValEquals)('platform_editor_no_cursor_on_edit_page_init', 'isEnabled', true)) && !(0, _document.isEmptyDocument)(editorView.state.doc);
@@ -532,7 +562,11 @@ function ReactEditorView(props) {
532
562
  }, [editorView, shouldFocus, __livePage, mitigateScrollJump, disabled]);
533
563
  var scrollElement = _react.default.useRef();
534
564
  var possibleListeners = _react.default.useRef([]);
535
- _react.default.useEffect(function () {
565
+ (0, _react.useEffect)(function () {
566
+ if ((0, _coreUtils.isSSR)() && (0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
567
+ // No event listeners should be attached to scroll element in SSR.
568
+ return;
569
+ }
536
570
  return function () {
537
571
  if (scrollElement.current) {
538
572
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -702,12 +736,20 @@ function ReactEditorView(props) {
702
736
  }, [handleEditorViewRef, props.intl]);
703
737
  var previousPreset = (0, _hooks.usePreviousState)(preset);
704
738
  (0, _react.useLayoutEffect)(function () {
739
+ if ((0, _coreUtils.isSSR)() && (0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
740
+ // No state reconfiguration is supported in SSR.
741
+ return;
742
+ }
705
743
  if (previousPreset && previousPreset !== preset) {
706
744
  reconfigureState(props);
707
745
  }
708
746
  }, [reconfigureState, previousPreset, preset, props]);
709
747
  var previousDisabledState = (0, _hooks.usePreviousState)(disabled);
710
748
  (0, _react.useLayoutEffect)(function () {
749
+ if ((0, _coreUtils.isSSR)() && (0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
750
+ // We don't need to focus anything in SSR.
751
+ return;
752
+ }
711
753
  if (viewRef.current && previousDisabledState !== disabled) {
712
754
  // Disables the contentEditable attribute of the editor if the editor is disabled
713
755
  viewRef.current.setProps({
@@ -727,13 +769,55 @@ function ReactEditorView(props) {
727
769
  }
728
770
  }, [disabled, shouldFocus, previousDisabledState, __livePage]);
729
771
  (0, _useFireFullWidthEvent.useFireFullWidthEvent)(nextAppearance, dispatchAnalyticsEvent);
772
+
773
+ // This function uses as prop as `<EditorSSRRender>` so, thay should be memoized,
774
+ // to avoid extra rerenders.
775
+ var buildDoc = (0, _react.useCallback)(function (schema) {
776
+ return parseDoc(schema, undefined, {
777
+ // Don't pass all props here, use only what you need to keep hook dependencies more stable.
778
+ // Check what `parseDoc` consumes and pass only needed data.
779
+ props: {
780
+ providerFactory: props.providerFactory,
781
+ editorProps: {
782
+ sanitizePrivateContent: props.editorProps.sanitizePrivateContent
783
+ }
784
+ },
785
+ doc: defaultValue
786
+ });
787
+ }, [defaultValue, parseDoc, props.editorProps.sanitizePrivateContent, props.providerFactory]);
788
+ var ssrEditor = (0, _react.useMemo)(function () {
789
+ if (!(0, _coreUtils.isSSR)() || !(0, _expValEquals.expValEquals)('platform_editor_ssr_renderer', 'isEnabled', true)) {
790
+ return null;
791
+ }
792
+ return /*#__PURE__*/_react.default.createElement(_editorSsrRenderer.EditorSSRRenderer, {
793
+ intl: props.intl,
794
+ preset: preset,
795
+ portalProviderAPI: props.portalProviderAPI,
796
+ pluginInjectionAPI: pluginInjectionAPI.current,
797
+ buildDoc: buildDoc
798
+ // IMPORTANT: Keep next props in sync with div that renders a real ProseMirror editor.
799
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
800
+ ,
801
+ className: "ProseMirror ".concat((0, _getUAPrefix.getUAPrefix)()),
802
+ key: "ProseMirror",
803
+ "aria-label": props.editorProps.assistiveLabel || props.intl.formatMessage(_messages.editorMessages.editorAssistiveLabel),
804
+ id: EDIT_AREA_ID,
805
+ "aria-describedby": props.editorProps.assistiveDescribedBy,
806
+ "data-editor-id": editorId.current
807
+ });
808
+ }, [props.intl, props.portalProviderAPI, props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy, preset, buildDoc]);
730
809
  var editor = (0, _react.useMemo)(function () {
810
+ // SSR editor will be available only in SSR environment,
811
+ // in a browser `ssrEditor` will be `null`, and we will render a normal one ProseMirror.
812
+ if (ssrEditor) {
813
+ return ssrEditor;
814
+ }
731
815
  return createEditor(props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy);
732
816
  },
733
817
  // `createEditor` changes a little too frequently - we don't want to recreate the editor view in this case
734
818
  // We should follow-up
735
819
  // eslint-disable-next-line react-hooks/exhaustive-deps
736
- [props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy]);
820
+ [props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy, ssrEditor]);
737
821
 
738
822
  // Render tracking is firing too many events in Jira so we are disabling them for now. See - https://product-fabric.atlassian.net/browse/ED-25616
739
823
  // Also firing too many events for the legacy content macro, so disabling for now. See - https://product-fabric.atlassian.net/browse/ED-26650
@@ -5,4 +5,4 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.version = exports.name = void 0;
7
7
  var name = exports.name = "@atlaskit/editor-core";
8
- var version = exports.version = "215.18.1";
8
+ var version = exports.version = "0.0.0-development";
@@ -1,8 +1,9 @@
1
- import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useLayoutEffect, useMemo, useRef, useState, useEffect } from 'react';
2
2
  import { injectIntl } from 'react-intl-next';
3
3
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
4
4
  import uuid from 'uuid/v4';
5
5
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE, fireAnalyticsEvent, PLATFORMS } from '@atlaskit/editor-common/analytics';
6
+ import { isSSR } from '@atlaskit/editor-common/core-utils';
6
7
  import { createDispatch, EventDispatcher } from '@atlaskit/editor-common/event-dispatcher';
7
8
  import { useConstructor, usePreviousState } from '@atlaskit/editor-common/hooks';
8
9
  import { nodeVisibilityManager } from '@atlaskit/editor-common/node-visibility';
@@ -16,6 +17,7 @@ import { analyticsEventKey, getAnalyticsEventSeverity } from '@atlaskit/editor-c
16
17
  import { isEmptyDocument } from '@atlaskit/editor-common/utils/document';
17
18
  import { EditorState, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
18
19
  import { EditorView } from '@atlaskit/editor-prosemirror/view';
20
+ import { EditorSSRRenderer } from '@atlaskit/editor-ssr-renderer';
19
21
  import { fg } from '@atlaskit/platform-feature-flags';
20
22
  import { abortAll, getActiveInteraction } from '@atlaskit/react-ufo/interaction-metrics';
21
23
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
@@ -98,6 +100,19 @@ export function ReactEditorView(props) {
98
100
  getEditorView: getEditorView,
99
101
  fireAnalyticsEvent: handleAnalyticsEvent
100
102
  }));
103
+ const parseDoc = useCallback((schema, api, options) => {
104
+ if (!options.doc) {
105
+ return undefined;
106
+ }
107
+
108
+ // if the collabEdit API is set, skip this validation due to potential pm validation errors
109
+ // from docs that end up with invalid marks after processing (See #hot-111702 for more details)
110
+ if ((api === null || api === void 0 ? void 0 : api.collabEdit) !== undefined || options.props.editorProps.skipValidation) {
111
+ return processRawValueWithoutValidation(schema, options.doc, dispatchAnalyticsEvent);
112
+ } else {
113
+ return processRawValue(schema, options.doc, options.props.providerFactory, options.props.editorProps.sanitizePrivateContent, contentTransformer.current, dispatchAnalyticsEvent);
114
+ }
115
+ }, [dispatchAnalyticsEvent]);
101
116
  const createEditorState = useCallback(options => {
102
117
  var _api$editorViewMode;
103
118
  let schema;
@@ -149,16 +164,7 @@ export function ReactEditorView(props) {
149
164
  const api = pluginInjectionAPI.current.api();
150
165
 
151
166
  // If we have a doc prop, we need to process it into a PMNode
152
- let doc;
153
- if (options.doc) {
154
- // if the collabEdit API is set, skip this validation due to potential pm validation errors
155
- // from docs that end up with invalid marks after processing (See #hot-111702 for more details)
156
- if ((api === null || api === void 0 ? void 0 : api.collabEdit) !== undefined || options.props.editorProps.skipValidation) {
157
- doc = processRawValueWithoutValidation(schema, options.doc, dispatchAnalyticsEvent);
158
- } else {
159
- doc = processRawValue(schema, options.doc, options.props.providerFactory, options.props.editorProps.sanitizePrivateContent, contentTransformer.current, dispatchAnalyticsEvent);
160
- }
161
- }
167
+ const doc = parseDoc(schema, api, options);
162
168
  const isViewMode = (api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.currentState().mode) === 'view';
163
169
  let selection;
164
170
  if (doc) {
@@ -183,13 +189,20 @@ export function ReactEditorView(props) {
183
189
  doc,
184
190
  selection: patchedSelection
185
191
  });
186
- }, [errorReporter, featureFlags, props.intl, props.portalProviderAPI, props.nodeViewPortalProviderAPI, props.editorProps, dispatchAnalyticsEvent, eventDispatcher, dispatch]);
187
- const initialEditorState = useMemo(() => createEditorState({
188
- props,
189
- doc: defaultValue,
190
- // ED-4759: Don't set selection at end for full-page editor - should be at start.
191
- selectionAtStart: isFullPage(nextAppearance)
192
- }),
192
+ }, [errorReporter, featureFlags, parseDoc, props.intl, props.portalProviderAPI, props.nodeViewPortalProviderAPI, props.editorProps, dispatchAnalyticsEvent, eventDispatcher, dispatch]);
193
+ const initialEditorState = useMemo(() => {
194
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
195
+ // We don't need to create initial state in SSR, it would be done by EditorSSRRenderer,
196
+ // so we can save some CPU time here.
197
+ return undefined;
198
+ }
199
+ return createEditorState({
200
+ props,
201
+ doc: defaultValue,
202
+ // ED-4759: Don't set selection at end for full-page editor - should be at start.
203
+ selectionAtStart: isFullPage(nextAppearance)
204
+ });
205
+ },
193
206
  // This is only used for the initial state - afterwards we will have `viewRef` available for use
194
207
  // eslint-disable-next-line react-hooks/exhaustive-deps
195
208
  []);
@@ -260,6 +273,10 @@ export function ReactEditorView(props) {
260
273
  });
261
274
  });
262
275
  useLayoutEffect(() => {
276
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
277
+ return;
278
+ }
279
+
263
280
  // Transaction dispatching is already enabled by default prior to
264
281
  // mounting, but we reset it here, just in case the editor view
265
282
  // instance is ever recycled (mounted again after unmounting) with
@@ -280,6 +297,10 @@ export function ReactEditorView(props) {
280
297
 
281
298
  // Cleanup
282
299
  useLayoutEffect(() => {
300
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
301
+ // No cleanup in SSR should happened because SSR doesn't render a real editor.
302
+ return;
303
+ }
283
304
  return () => {
284
305
  const focusTimeoutIdCurrent = focusTimeoutId.current;
285
306
  if (focusTimeoutIdCurrent) {
@@ -378,8 +399,15 @@ export function ReactEditorView(props) {
378
399
  taskDecisionProvider: props.editorProps.taskDecisionProvider
379
400
  });
380
401
  const getDirectEditorProps = useCallback(state => {
402
+ const stateToUse = state !== null && state !== void 0 ? state : getCurrentEditorState();
403
+ if (!stateToUse) {
404
+ // This should not be happened, because initialState is only inavailable in SSR,
405
+ // but in SSR this function should never be called.
406
+ // In SSR we should use EditorSSRRenderer instead usual ProseMirror editor.
407
+ throw new Error('No editor state found');
408
+ }
381
409
  return {
382
- state: state !== null && state !== void 0 ? state : getCurrentEditorState(),
410
+ state: stateToUse,
383
411
  dispatchTransaction: tr => {
384
412
  // Block stale transactions:
385
413
  // Prevent runtime exceptions from async transactions that would attempt to
@@ -483,6 +511,10 @@ export function ReactEditorView(props) {
483
511
  isFullPage(props.editorProps.appearance) && originalScrollToRestore.current && originalScrollToRestore.current !== 0;
484
512
  useLayoutEffect(() => {
485
513
  var _editorView$props$edi, _editorView$props;
514
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
515
+ // We don't need to focus anything in SSR.
516
+ return;
517
+ }
486
518
  if (shouldFocus && editorView !== null && editorView !== void 0 && (_editorView$props$edi = (_editorView$props = editorView.props).editable) !== null && _editorView$props$edi !== void 0 && _editorView$props$edi.call(_editorView$props, editorView.state) && fg('platform_editor_react_18_autofocus_fix')) {
487
519
  if (!mitigateScrollJump) {
488
520
  const liveDocWithContent = (__livePage || expValEquals('platform_editor_no_cursor_on_edit_page_init', 'isEnabled', true)) && !isEmptyDocument(editorView.state.doc);
@@ -499,7 +531,11 @@ export function ReactEditorView(props) {
499
531
  }, [editorView, shouldFocus, __livePage, mitigateScrollJump, disabled]);
500
532
  const scrollElement = React.useRef();
501
533
  const possibleListeners = React.useRef([]);
502
- React.useEffect(() => {
534
+ useEffect(() => {
535
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
536
+ // No event listeners should be attached to scroll element in SSR.
537
+ return;
538
+ }
503
539
  return () => {
504
540
  if (scrollElement.current) {
505
541
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -649,12 +685,20 @@ export function ReactEditorView(props) {
649
685
  }, [handleEditorViewRef, props.intl]);
650
686
  const previousPreset = usePreviousState(preset);
651
687
  useLayoutEffect(() => {
688
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
689
+ // No state reconfiguration is supported in SSR.
690
+ return;
691
+ }
652
692
  if (previousPreset && previousPreset !== preset) {
653
693
  reconfigureState(props);
654
694
  }
655
695
  }, [reconfigureState, previousPreset, preset, props]);
656
696
  const previousDisabledState = usePreviousState(disabled);
657
697
  useLayoutEffect(() => {
698
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
699
+ // We don't need to focus anything in SSR.
700
+ return;
701
+ }
658
702
  if (viewRef.current && previousDisabledState !== disabled) {
659
703
  // Disables the contentEditable attribute of the editor if the editor is disabled
660
704
  viewRef.current.setProps({
@@ -672,11 +716,55 @@ export function ReactEditorView(props) {
672
716
  }
673
717
  }, [disabled, shouldFocus, previousDisabledState, __livePage]);
674
718
  useFireFullWidthEvent(nextAppearance, dispatchAnalyticsEvent);
675
- const editor = useMemo(() => createEditor(props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy),
719
+
720
+ // This function uses as prop as `<EditorSSRRender>` so, thay should be memoized,
721
+ // to avoid extra rerenders.
722
+ const buildDoc = useCallback(schema => {
723
+ return parseDoc(schema, undefined, {
724
+ // Don't pass all props here, use only what you need to keep hook dependencies more stable.
725
+ // Check what `parseDoc` consumes and pass only needed data.
726
+ props: {
727
+ providerFactory: props.providerFactory,
728
+ editorProps: {
729
+ sanitizePrivateContent: props.editorProps.sanitizePrivateContent
730
+ }
731
+ },
732
+ doc: defaultValue
733
+ });
734
+ }, [defaultValue, parseDoc, props.editorProps.sanitizePrivateContent, props.providerFactory]);
735
+ const ssrEditor = useMemo(() => {
736
+ if (!isSSR() || !expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
737
+ return null;
738
+ }
739
+ return /*#__PURE__*/React.createElement(EditorSSRRenderer, {
740
+ intl: props.intl,
741
+ preset: preset,
742
+ portalProviderAPI: props.portalProviderAPI,
743
+ pluginInjectionAPI: pluginInjectionAPI.current,
744
+ buildDoc: buildDoc
745
+ // IMPORTANT: Keep next props in sync with div that renders a real ProseMirror editor.
746
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
747
+ ,
748
+ className: `ProseMirror ${getUAPrefix()}`,
749
+ key: "ProseMirror",
750
+ "aria-label": props.editorProps.assistiveLabel || props.intl.formatMessage(editorMessages.editorAssistiveLabel),
751
+ id: EDIT_AREA_ID,
752
+ "aria-describedby": props.editorProps.assistiveDescribedBy,
753
+ "data-editor-id": editorId.current
754
+ });
755
+ }, [props.intl, props.portalProviderAPI, props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy, preset, buildDoc]);
756
+ const editor = useMemo(() => {
757
+ // SSR editor will be available only in SSR environment,
758
+ // in a browser `ssrEditor` will be `null`, and we will render a normal one ProseMirror.
759
+ if (ssrEditor) {
760
+ return ssrEditor;
761
+ }
762
+ return createEditor(props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy);
763
+ },
676
764
  // `createEditor` changes a little too frequently - we don't want to recreate the editor view in this case
677
765
  // We should follow-up
678
766
  // eslint-disable-next-line react-hooks/exhaustive-deps
679
- [props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy]);
767
+ [props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy, ssrEditor]);
680
768
 
681
769
  // Render tracking is firing too many events in Jira so we are disabling them for now. See - https://product-fabric.atlassian.net/browse/ED-25616
682
770
  // Also firing too many events for the legacy content macro, so disabling for now. See - https://product-fabric.atlassian.net/browse/ED-26650
@@ -1,2 +1,2 @@
1
1
  export const name = "@atlaskit/editor-core";
2
- export const version = "215.18.1";
2
+ export const version = "0.0.0-development";
@@ -6,11 +6,12 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
6
6
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
7
7
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
8
8
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
9
- import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
9
+ import React, { useCallback, useLayoutEffect, useMemo, useRef, useState, useEffect } from 'react';
10
10
  import { injectIntl } from 'react-intl-next';
11
11
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
12
12
  import uuid from 'uuid/v4';
13
13
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE, fireAnalyticsEvent, PLATFORMS } from '@atlaskit/editor-common/analytics';
14
+ import { isSSR } from '@atlaskit/editor-common/core-utils';
14
15
  import { createDispatch, EventDispatcher } from '@atlaskit/editor-common/event-dispatcher';
15
16
  import { useConstructor, usePreviousState } from '@atlaskit/editor-common/hooks';
16
17
  import { nodeVisibilityManager } from '@atlaskit/editor-common/node-visibility';
@@ -24,6 +25,7 @@ import { analyticsEventKey, getAnalyticsEventSeverity } from '@atlaskit/editor-c
24
25
  import { isEmptyDocument } from '@atlaskit/editor-common/utils/document';
25
26
  import { EditorState, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
26
27
  import { EditorView } from '@atlaskit/editor-prosemirror/view';
28
+ import { EditorSSRRenderer } from '@atlaskit/editor-ssr-renderer';
27
29
  import { fg } from '@atlaskit/platform-feature-flags';
28
30
  import { abortAll, getActiveInteraction } from '@atlaskit/react-ufo/interaction-metrics';
29
31
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
@@ -116,6 +118,19 @@ export function ReactEditorView(props) {
116
118
  getEditorView: getEditorView,
117
119
  fireAnalyticsEvent: handleAnalyticsEvent
118
120
  }));
121
+ var parseDoc = useCallback(function (schema, api, options) {
122
+ if (!options.doc) {
123
+ return undefined;
124
+ }
125
+
126
+ // if the collabEdit API is set, skip this validation due to potential pm validation errors
127
+ // from docs that end up with invalid marks after processing (See #hot-111702 for more details)
128
+ if ((api === null || api === void 0 ? void 0 : api.collabEdit) !== undefined || options.props.editorProps.skipValidation) {
129
+ return processRawValueWithoutValidation(schema, options.doc, dispatchAnalyticsEvent);
130
+ } else {
131
+ return processRawValue(schema, options.doc, options.props.providerFactory, options.props.editorProps.sanitizePrivateContent, contentTransformer.current, dispatchAnalyticsEvent);
132
+ }
133
+ }, [dispatchAnalyticsEvent]);
119
134
  var createEditorState = useCallback(function (options) {
120
135
  var _api$editorViewMode;
121
136
  var schema;
@@ -167,16 +182,7 @@ export function ReactEditorView(props) {
167
182
  var api = pluginInjectionAPI.current.api();
168
183
 
169
184
  // If we have a doc prop, we need to process it into a PMNode
170
- var doc;
171
- if (options.doc) {
172
- // if the collabEdit API is set, skip this validation due to potential pm validation errors
173
- // from docs that end up with invalid marks after processing (See #hot-111702 for more details)
174
- if ((api === null || api === void 0 ? void 0 : api.collabEdit) !== undefined || options.props.editorProps.skipValidation) {
175
- doc = processRawValueWithoutValidation(schema, options.doc, dispatchAnalyticsEvent);
176
- } else {
177
- doc = processRawValue(schema, options.doc, options.props.providerFactory, options.props.editorProps.sanitizePrivateContent, contentTransformer.current, dispatchAnalyticsEvent);
178
- }
179
- }
185
+ var doc = parseDoc(schema, api, options);
180
186
  var isViewMode = (api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.currentState().mode) === 'view';
181
187
  var selection;
182
188
  if (doc) {
@@ -201,8 +207,13 @@ export function ReactEditorView(props) {
201
207
  doc: doc,
202
208
  selection: patchedSelection
203
209
  });
204
- }, [errorReporter, featureFlags, props.intl, props.portalProviderAPI, props.nodeViewPortalProviderAPI, props.editorProps, dispatchAnalyticsEvent, eventDispatcher, dispatch]);
210
+ }, [errorReporter, featureFlags, parseDoc, props.intl, props.portalProviderAPI, props.nodeViewPortalProviderAPI, props.editorProps, dispatchAnalyticsEvent, eventDispatcher, dispatch]);
205
211
  var initialEditorState = useMemo(function () {
212
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
213
+ // We don't need to create initial state in SSR, it would be done by EditorSSRRenderer,
214
+ // so we can save some CPU time here.
215
+ return undefined;
216
+ }
206
217
  return createEditorState({
207
218
  props: props,
208
219
  doc: defaultValue,
@@ -279,6 +290,10 @@ export function ReactEditorView(props) {
279
290
  });
280
291
  });
281
292
  useLayoutEffect(function () {
293
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
294
+ return;
295
+ }
296
+
282
297
  // Transaction dispatching is already enabled by default prior to
283
298
  // mounting, but we reset it here, just in case the editor view
284
299
  // instance is ever recycled (mounted again after unmounting) with
@@ -299,6 +314,10 @@ export function ReactEditorView(props) {
299
314
 
300
315
  // Cleanup
301
316
  useLayoutEffect(function () {
317
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
318
+ // No cleanup in SSR should happened because SSR doesn't render a real editor.
319
+ return;
320
+ }
302
321
  return function () {
303
322
  var focusTimeoutIdCurrent = focusTimeoutId.current;
304
323
  if (focusTimeoutIdCurrent) {
@@ -397,8 +416,15 @@ export function ReactEditorView(props) {
397
416
  taskDecisionProvider: props.editorProps.taskDecisionProvider
398
417
  });
399
418
  var getDirectEditorProps = useCallback(function (state) {
419
+ var stateToUse = state !== null && state !== void 0 ? state : getCurrentEditorState();
420
+ if (!stateToUse) {
421
+ // This should not be happened, because initialState is only inavailable in SSR,
422
+ // but in SSR this function should never be called.
423
+ // In SSR we should use EditorSSRRenderer instead usual ProseMirror editor.
424
+ throw new Error('No editor state found');
425
+ }
400
426
  return {
401
- state: state !== null && state !== void 0 ? state : getCurrentEditorState(),
427
+ state: stateToUse,
402
428
  dispatchTransaction: function dispatchTransaction(tr) {
403
429
  // Block stale transactions:
404
430
  // Prevent runtime exceptions from async transactions that would attempt to
@@ -507,6 +533,10 @@ export function ReactEditorView(props) {
507
533
  isFullPage(props.editorProps.appearance) && originalScrollToRestore.current && originalScrollToRestore.current !== 0;
508
534
  useLayoutEffect(function () {
509
535
  var _editorView$props$edi, _editorView$props;
536
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
537
+ // We don't need to focus anything in SSR.
538
+ return;
539
+ }
510
540
  if (shouldFocus && editorView !== null && editorView !== void 0 && (_editorView$props$edi = (_editorView$props = editorView.props).editable) !== null && _editorView$props$edi !== void 0 && _editorView$props$edi.call(_editorView$props, editorView.state) && fg('platform_editor_react_18_autofocus_fix')) {
511
541
  if (!mitigateScrollJump) {
512
542
  var liveDocWithContent = (__livePage || expValEquals('platform_editor_no_cursor_on_edit_page_init', 'isEnabled', true)) && !isEmptyDocument(editorView.state.doc);
@@ -523,7 +553,11 @@ export function ReactEditorView(props) {
523
553
  }, [editorView, shouldFocus, __livePage, mitigateScrollJump, disabled]);
524
554
  var scrollElement = React.useRef();
525
555
  var possibleListeners = React.useRef([]);
526
- React.useEffect(function () {
556
+ useEffect(function () {
557
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
558
+ // No event listeners should be attached to scroll element in SSR.
559
+ return;
560
+ }
527
561
  return function () {
528
562
  if (scrollElement.current) {
529
563
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -693,12 +727,20 @@ export function ReactEditorView(props) {
693
727
  }, [handleEditorViewRef, props.intl]);
694
728
  var previousPreset = usePreviousState(preset);
695
729
  useLayoutEffect(function () {
730
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
731
+ // No state reconfiguration is supported in SSR.
732
+ return;
733
+ }
696
734
  if (previousPreset && previousPreset !== preset) {
697
735
  reconfigureState(props);
698
736
  }
699
737
  }, [reconfigureState, previousPreset, preset, props]);
700
738
  var previousDisabledState = usePreviousState(disabled);
701
739
  useLayoutEffect(function () {
740
+ if (isSSR() && expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
741
+ // We don't need to focus anything in SSR.
742
+ return;
743
+ }
702
744
  if (viewRef.current && previousDisabledState !== disabled) {
703
745
  // Disables the contentEditable attribute of the editor if the editor is disabled
704
746
  viewRef.current.setProps({
@@ -718,13 +760,55 @@ export function ReactEditorView(props) {
718
760
  }
719
761
  }, [disabled, shouldFocus, previousDisabledState, __livePage]);
720
762
  useFireFullWidthEvent(nextAppearance, dispatchAnalyticsEvent);
763
+
764
+ // This function uses as prop as `<EditorSSRRender>` so, thay should be memoized,
765
+ // to avoid extra rerenders.
766
+ var buildDoc = useCallback(function (schema) {
767
+ return parseDoc(schema, undefined, {
768
+ // Don't pass all props here, use only what you need to keep hook dependencies more stable.
769
+ // Check what `parseDoc` consumes and pass only needed data.
770
+ props: {
771
+ providerFactory: props.providerFactory,
772
+ editorProps: {
773
+ sanitizePrivateContent: props.editorProps.sanitizePrivateContent
774
+ }
775
+ },
776
+ doc: defaultValue
777
+ });
778
+ }, [defaultValue, parseDoc, props.editorProps.sanitizePrivateContent, props.providerFactory]);
779
+ var ssrEditor = useMemo(function () {
780
+ if (!isSSR() || !expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
781
+ return null;
782
+ }
783
+ return /*#__PURE__*/React.createElement(EditorSSRRenderer, {
784
+ intl: props.intl,
785
+ preset: preset,
786
+ portalProviderAPI: props.portalProviderAPI,
787
+ pluginInjectionAPI: pluginInjectionAPI.current,
788
+ buildDoc: buildDoc
789
+ // IMPORTANT: Keep next props in sync with div that renders a real ProseMirror editor.
790
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
791
+ ,
792
+ className: "ProseMirror ".concat(getUAPrefix()),
793
+ key: "ProseMirror",
794
+ "aria-label": props.editorProps.assistiveLabel || props.intl.formatMessage(editorMessages.editorAssistiveLabel),
795
+ id: EDIT_AREA_ID,
796
+ "aria-describedby": props.editorProps.assistiveDescribedBy,
797
+ "data-editor-id": editorId.current
798
+ });
799
+ }, [props.intl, props.portalProviderAPI, props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy, preset, buildDoc]);
721
800
  var editor = useMemo(function () {
801
+ // SSR editor will be available only in SSR environment,
802
+ // in a browser `ssrEditor` will be `null`, and we will render a normal one ProseMirror.
803
+ if (ssrEditor) {
804
+ return ssrEditor;
805
+ }
722
806
  return createEditor(props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy);
723
807
  },
724
808
  // `createEditor` changes a little too frequently - we don't want to recreate the editor view in this case
725
809
  // We should follow-up
726
810
  // eslint-disable-next-line react-hooks/exhaustive-deps
727
- [props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy]);
811
+ [props.editorProps.assistiveLabel, props.editorProps.assistiveDescribedBy, ssrEditor]);
728
812
 
729
813
  // Render tracking is firing too many events in Jira so we are disabling them for now. See - https://product-fabric.atlassian.net/browse/ED-25616
730
814
  // Also firing too many events for the legacy content macro, so disabling for now. See - https://product-fabric.atlassian.net/browse/ED-26650
@@ -1,2 +1,2 @@
1
1
  export var name = "@atlaskit/editor-core";
2
- export var version = "215.18.1";
2
+ export var version = "0.0.0-development";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-core",
3
- "version": "215.18.2",
3
+ "version": "215.19.0",
4
4
  "description": "A package contains Atlassian editor core functionality",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -53,6 +53,7 @@
53
53
  "@atlaskit/editor-plugins": "^11.1.0",
54
54
  "@atlaskit/editor-prosemirror": "7.0.0",
55
55
  "@atlaskit/editor-shared-styles": "^3.10.0",
56
+ "@atlaskit/editor-ssr-renderer": "^1.3.0",
56
57
  "@atlaskit/editor-toolbar": "^0.18.0",
57
58
  "@atlaskit/editor-toolbar-model": "^0.2.0",
58
59
  "@atlaskit/emoji": "^69.9.0",
@@ -64,7 +65,7 @@
64
65
  "@atlaskit/platform-feature-flags-react": "^0.4.0",
65
66
  "@atlaskit/react-ufo": "^4.15.0",
66
67
  "@atlaskit/task-decision": "^19.2.0",
67
- "@atlaskit/tmp-editor-statsig": "^14.8.0",
68
+ "@atlaskit/tmp-editor-statsig": "^14.9.0",
68
69
  "@atlaskit/tokens": "^8.4.0",
69
70
  "@atlaskit/tooltip": "^20.11.0",
70
71
  "@atlaskit/width-detector": "^5.0.0",
@@ -81,7 +82,7 @@
81
82
  "uuid": "^3.1.0"
82
83
  },
83
84
  "peerDependencies": {
84
- "@atlaskit/editor-common": "^110.39.0",
85
+ "@atlaskit/editor-common": "^110.40.0",
85
86
  "@atlaskit/link-provider": "^4.0.0",
86
87
  "@atlaskit/media-core": "^37.0.0",
87
88
  "react": "^18.2.0",
@@ -1,17 +0,0 @@
1
- {
2
- "name": "@atlaskit/editor-core/editor-styles-container",
3
- "main": "../dist/cjs/ui/EditorContentContainer/EditorContentContainer.js",
4
- "module": "../dist/esm/ui/EditorContentContainer/EditorContentContainer.js",
5
- "module:es2019": "../dist/es2019/ui/EditorContentContainer/EditorContentContainer.js",
6
- "sideEffects": [
7
- "*.compiled.css"
8
- ],
9
- "types": "../dist/types/ui/EditorContentContainer/EditorContentContainer.d.ts",
10
- "typesVersions": {
11
- ">=4.5 <5.9": {
12
- "*": [
13
- "../dist/types-ts4.5/ui/EditorContentContainer/EditorContentContainer.d.ts"
14
- ]
15
- }
16
- }
17
- }