@atlaskit/editor-core 219.7.2 → 219.7.4

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,27 @@
1
1
  # @atlaskit/editor-core
2
2
 
3
+ ## 219.7.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`ab1c30e0721ef`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ab1c30e0721ef) -
8
+ Migrate extensionStyles.ts to Compiled CSS
9
+
10
+ ## 219.7.3
11
+
12
+ ### Patch Changes
13
+
14
+ - [`911ea1f065d0f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/911ea1f065d0f) -
15
+ [EDITOR-6965] Rebuild the schema in `ReactEditorView.reconfigureState` when toggling presets under
16
+ the `cc-markdown-mode` experiment, so each preset declares its honest schema (rather than sharing
17
+ the schema from first mount via `state.reconfigure`). Skips the existing plugin-availability
18
+ filter (which would evaluate against the OLD schema and drop e.g. `extensionPlugin` on a
19
+ markdown→rich toggle), and re-seeds `collabInitialised: true` on the new state because the
20
+ Confluence NCS provider does not implement `getInitPayload` (so the rebind path in
21
+ `editor-plugin-collab-edit/initialize.ts` is skipped). Also makes
22
+ `extensionProviderToQuickInsertProvider.getItems` schema-aware so the `/` menu does not surface
23
+ extension items when the live schema cannot accept them.
24
+
3
25
  ## 219.7.2
4
26
 
5
27
  ### Patch Changes
@@ -28,6 +28,7 @@ var _processRawValue = require("@atlaskit/editor-common/process-raw-value");
28
28
  var _uiReact = require("@atlaskit/editor-common/ui-react");
29
29
  var _analytics2 = require("@atlaskit/editor-common/utils/analytics");
30
30
  var _document = require("@atlaskit/editor-common/utils/document");
31
+ var _model = require("@atlaskit/editor-prosemirror/model");
31
32
  var _state2 = require("@atlaskit/editor-prosemirror/state");
32
33
  var _view = require("@atlaskit/editor-prosemirror/view");
33
34
  var _editorSsrRenderer = require("@atlaskit/editor-ssr-renderer");
@@ -57,14 +58,45 @@ var _handleEditorFocus = require("./ReactEditorView/handleEditorFocus");
57
58
  var _useDispatchTransaction = require("./ReactEditorView/useDispatchTransaction");
58
59
  var _useFireFullWidthEvent = require("./ReactEditorView/useFireFullWidthEvent");
59
60
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
61
+ 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; }
62
+ 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) { (0, _defineProperty2.default)(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; }
60
63
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
61
64
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
62
- 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; }
63
- 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; }
64
- 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) { (0, _defineProperty2.default)(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; } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
65
+ 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; } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
65
66
  var EDIT_AREA_ID = 'ak-editor-textarea';
66
67
  var SSR_TRACE_SEGMENT_NAME = 'reactEditorView';
67
68
  var bootStartTime = (0, _isPerformanceApiAvailable.isPerformanceAPIAvailable)() ? performance.now() : undefined;
69
+ // `markdown↔rich` toggles drop different node/mark sets, so the unique
70
+ // name set is enough to detect when a destructive rebuild is needed.
71
+ function sameNames(a, b) {
72
+ var setA = new Set(a);
73
+ var setB = new Set(b);
74
+ if (setA.size !== setB.size) {
75
+ return false;
76
+ }
77
+ var _iterator = _createForOfIteratorHelper(setA),
78
+ _step;
79
+ try {
80
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
81
+ var name = _step.value;
82
+ if (!setB.has(name)) {
83
+ return false;
84
+ }
85
+ }
86
+ } catch (err) {
87
+ _iterator.e(err);
88
+ } finally {
89
+ _iterator.f();
90
+ }
91
+ return true;
92
+ }
93
+ function schemaShapeChanged(current, next) {
94
+ return !sameNames(Object.keys(current.nodes), next.nodes.map(function (n) {
95
+ return n.name;
96
+ })) || !sameNames(Object.keys(current.marks), next.marks.map(function (m) {
97
+ return m.name;
98
+ }));
99
+ }
68
100
  function ReactEditorView(props) {
69
101
  var _pluginInjectionAPI$c, _media, _linking, _document$querySelect, _props$render, _props$render2;
70
102
  // Should be always the first statement in the component
@@ -364,9 +396,10 @@ function ReactEditorView(props) {
364
396
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
365
397
  bumpConfigVersion = _useState2[1];
366
398
 
367
- // Preset reference last processed by the schema/API reconciliation below.
368
- // Used to skip that work when reconfigure is called with the same preset.
369
- var lastFilteredPresetRef = (0, _react.useRef)(null);
399
+ // Preset reference last processed by reconfigureState. Used to skip the
400
+ // destructive work (plugin filter, schema rebuild) when reconfigure is
401
+ // called with the same preset.
402
+ var lastProcessedPresetRef = (0, _react.useRef)(null);
370
403
  var reconfigureState = (0, _react.useCallback)(function (props) {
371
404
  if (!viewRef.current) {
372
405
  return;
@@ -383,6 +416,35 @@ function ReactEditorView(props) {
383
416
  var previousPluginNames = new Set(pluginInjectionAPI.current.getRegisteredPluginNames());
384
417
  var editorPlugins = (0, _createPluginsList.default)(props.preset, 'allowBlockType' in props.editorProps ? props.editorProps : {}, pluginInjectionAPI.current);
385
418
 
419
+ // Capture once, before either downstream block updates the ref —
420
+ // both the filter and the schema rebuild are destructive and only
421
+ // want to run when the preset has actually changed.
422
+ var presetChanged = lastProcessedPresetRef.current !== props.preset;
423
+
424
+ // Build a candidate config from the *unfiltered* plugin list so we can
425
+ // decide whether the schema rebuild path will run. Both the rebuild
426
+ // decision and the drop-filter decision below depend on this answer,
427
+ // so it has to be computed up-front.
428
+ var buildConfig = function buildConfig(plugins) {
429
+ var c = (0, _createEditor.processPluginsList)(plugins);
430
+ if ((0, _expValEquals.expValEquals)('platform_editor_appearance_shared_state', 'isEnabled', true)) {
431
+ var _c$pmPlugins;
432
+ (_c$pmPlugins = c.pmPlugins).push.apply(_c$pmPlugins, (0, _toConsumableArray2.default)(pluginInjectionAPI.current.getInternalPMPlugins()));
433
+ }
434
+ return c;
435
+ };
436
+ var nextConfig = buildConfig(editorPlugins);
437
+
438
+ // `state.reconfigure` preserves the original schema, so a preset
439
+ // toggle that should change schema (markdown↔rich) needs a fresh
440
+ // `EditorState`. Resets all plugin state including undo history.
441
+ //
442
+ // Compare schema *shape* (node + mark name sets) rather than preset
443
+ // identity: consumers commonly recreate the preset object on every
444
+ // parent re-render, and a destructive rebuild on a no-op identity
445
+ // change tears down all plugin state (e.g. unmounts the AI palette).
446
+ var shouldRebuildSchema = presetChanged && schemaShapeChanged(viewRef.current.state.schema, nextConfig) && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('cc-markdown-mode', 'isEnabled', true);
447
+
386
448
  // `state.reconfigure` keeps the original schema, so switching presets
387
449
  // can leave the editor inconsistent in two ways:
388
450
  // 1. The new preset may add plugins that reference schema nodes or
@@ -391,17 +453,26 @@ function ReactEditorView(props) {
391
453
  // injection API even when the new preset doesn't re-register
392
454
  // them, so listeners still fire against a state that no longer
393
455
  // has their pmPlugin.
394
- if (lastFilteredPresetRef.current !== props.preset && (0, _platformFeatureFlags.fg)('platform_editor_reconfigure_filter_plugins')) {
395
- var _filterPluginsForReco = (0, _filterPluginsForReconfigure.filterPluginsForReconfigure)(editorPlugins, viewRef.current.state.schema, previousPluginNames),
396
- kept = _filterPluginsForReco.kept,
397
- dropped = _filterPluginsForReco.dropped;
398
- editorPlugins = kept;
399
-
400
- // Reconcile the injection API with the post-filter plugin set.
401
- // This evicts both the plugins we just dropped above (re-registered
402
- // by createPluginsList but no longer in editorPlugins) AND any
403
- // plugin from a previous preset that the new preset doesn't
404
- // re-register.
456
+ //
457
+ // When the schema is being rebuilt below, the new schema is built
458
+ // from the *unfiltered* plugin list — so dropping plugins whose
459
+ // nodes/marks the OLD schema lacks would wrongly remove the very
460
+ // plugins the rebuild is meant to admit. Skip the drop step in that
461
+ // case (purpose 1) but always reconcile the injection API
462
+ // (purpose 2). When NOT rebuilding, run both even under the
463
+ // `cc-markdown-mode` experiment, otherwise no-op preset identity
464
+ // changes would silently leave a broken plugin/schema mismatch.
465
+ if (presetChanged && (0, _platformFeatureFlags.fg)('platform_editor_reconfigure_filter_plugins')) {
466
+ var dropped = [];
467
+ if (!shouldRebuildSchema) {
468
+ var _result = (0, _filterPluginsForReconfigure.filterPluginsForReconfigure)(editorPlugins, viewRef.current.state.schema, previousPluginNames);
469
+ if (_result.dropped.length > 0) {
470
+ editorPlugins = _result.kept;
471
+ // Plugin list changed — rebuild candidate config to match.
472
+ nextConfig = buildConfig(editorPlugins);
473
+ }
474
+ dropped = _result.dropped;
475
+ }
405
476
  var keptPluginNames = new Set(editorPlugins.map(function (p) {
406
477
  return p === null || p === void 0 ? void 0 : p.name;
407
478
  }).filter(function (n) {
@@ -415,33 +486,77 @@ function ReactEditorView(props) {
415
486
  evictedFromApi: evictedFromApi
416
487
  });
417
488
  }
418
- lastFilteredPresetRef.current = props.preset;
419
- }
420
- config.current = (0, _createEditor.processPluginsList)(editorPlugins);
421
- if ((0, _expValEquals.expValEquals)('platform_editor_appearance_shared_state', 'isEnabled', true)) {
422
- var _config$current$pmPlu2;
423
- (_config$current$pmPlu2 = config.current.pmPlugins).push.apply(_config$current$pmPlu2, (0, _toConsumableArray2.default)(pluginInjectionAPI.current.getInternalPMPlugins()));
424
489
  }
490
+ config.current = nextConfig;
425
491
  var state = viewRef.current.state;
426
- var plugins = (0, _createEditor.createPMPlugins)({
427
- schema: state.schema,
428
- dispatch: dispatch,
429
- errorReporter: errorReporter,
430
- editorConfig: config.current,
431
- eventDispatcher: eventDispatcher,
432
- providerFactory: props.providerFactory,
433
- portalProviderAPI: props.portalProviderAPI,
434
- nodeViewPortalProviderAPI: props.nodeViewPortalProviderAPI,
435
- dispatchAnalyticsEvent: dispatchAnalyticsEvent,
436
- featureFlags: featureFlags,
437
- getIntl: function getIntl() {
438
- return props.intl;
439
- },
440
- onEditorStateUpdated: pluginInjectionAPI.current.onEditorViewUpdated
441
- });
442
- var newState = state.reconfigure({
443
- plugins: plugins
444
- });
492
+ var newState;
493
+ if (shouldRebuildSchema) {
494
+ var newSchema = (0, _createSchema.createSchema)(config.current);
495
+ var newDoc;
496
+ try {
497
+ newDoc = _model.Node.fromJSON(newSchema, state.doc.toJSON());
498
+ } catch (e) {
499
+ // eslint-disable-next-line no-console
500
+ console.error('[reconfigureState] Failed to migrate doc to new schema; resetting to empty doc', e);
501
+ var empty = newSchema.topNodeType.createAndFill();
502
+ if (!empty) {
503
+ throw new Error('reconfigureState: doc migration failed and new schema cannot create an empty top node');
504
+ }
505
+ newDoc = empty;
506
+ }
507
+ var newSelection;
508
+ try {
509
+ newSelection = _state2.Selection.fromJSON(newDoc, state.selection.toJSON());
510
+ } catch (_unused) {
511
+ // Old selection's positions / node types may not map onto the new schema.
512
+ newSelection = _state2.Selection.atStart(newDoc);
513
+ }
514
+ var plugins = (0, _createEditor.createPMPlugins)({
515
+ schema: newSchema,
516
+ dispatch: dispatch,
517
+ errorReporter: errorReporter,
518
+ editorConfig: config.current,
519
+ eventDispatcher: eventDispatcher,
520
+ providerFactory: props.providerFactory,
521
+ portalProviderAPI: props.portalProviderAPI,
522
+ nodeViewPortalProviderAPI: props.nodeViewPortalProviderAPI,
523
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
524
+ featureFlags: featureFlags,
525
+ getIntl: function getIntl() {
526
+ return props.intl;
527
+ },
528
+ onEditorStateUpdated: pluginInjectionAPI.current.onEditorViewUpdated
529
+ });
530
+ newState = _state2.EditorState.create({
531
+ schema: newSchema,
532
+ doc: newDoc,
533
+ selection: newSelection,
534
+ plugins: plugins
535
+ });
536
+ } else {
537
+ var _plugins = (0, _createEditor.createPMPlugins)({
538
+ schema: state.schema,
539
+ dispatch: dispatch,
540
+ errorReporter: errorReporter,
541
+ editorConfig: config.current,
542
+ eventDispatcher: eventDispatcher,
543
+ providerFactory: props.providerFactory,
544
+ portalProviderAPI: props.portalProviderAPI,
545
+ nodeViewPortalProviderAPI: props.nodeViewPortalProviderAPI,
546
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
547
+ featureFlags: featureFlags,
548
+ getIntl: function getIntl() {
549
+ return props.intl;
550
+ },
551
+ onEditorStateUpdated: pluginInjectionAPI.current.onEditorViewUpdated
552
+ });
553
+ newState = state.reconfigure({
554
+ plugins: _plugins
555
+ });
556
+ }
557
+ if (presetChanged) {
558
+ lastProcessedPresetRef.current = props.preset;
559
+ }
445
560
 
446
561
  // need to update the state first so when the view builds the nodeviews it is
447
562
  // using the latest plugins
@@ -450,9 +565,32 @@ function ReactEditorView(props) {
450
565
  state: newState
451
566
  }));
452
567
 
568
+ // The new collab-edit plugin instance starts with `isReady=false`.
569
+ // The rebind path in editor-plugin-collab-edit's initialize.ts is
570
+ // gated on `provider.getInitPayload`, which the Confluence NCS
571
+ // provider does not implement, so the placeholder spinner would
572
+ // never clear. Re-seeding here is safe: the prior state must have
573
+ // had `isReady=true` for the user to have triggered the toggle.
574
+ //
575
+ // Must run AFTER `view.update({ state: newState })`: that call resets
576
+ // the view's state to the captured `newState` reference, so a
577
+ // dispatch placed before it would advance `view.state` to a value
578
+ // that `update` then silently overwrites — discarding the meta and
579
+ // leaving `isReady=false`.
580
+ if (shouldRebuildSchema) {
581
+ // `state.collabEditPlugin$` is the property PM derives from the
582
+ // collab plugin's PluginKey; cast through `unknown` to read it.
583
+ var collabState = viewRef.current.state.collabEditPlugin$;
584
+ if (collabState && collabState.isReady !== true) {
585
+ viewRef.current.dispatch(viewRef.current.state.tr.setMeta('collabInitialised', true));
586
+ }
587
+ }
588
+
453
589
  // EDITOR-6702: gated until we have a broader gate; reconfigure is a
454
590
  // low-level path so use NoExposure.
455
591
  if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('cc-markdown-mode', 'isEnabled', true)) {
592
+ // Force a render so PluginSlot picks up the new preset's content
593
+ // components against the new state.
456
594
  bumpConfigVersion(function (v) {
457
595
  return v + 1;
458
596
  });
@@ -695,19 +833,19 @@ function ReactEditorView(props) {
695
833
  return function () {
696
834
  if (scrollElement.current) {
697
835
  // eslint-disable-next-line react-hooks/exhaustive-deps
698
- var _iterator = _createForOfIteratorHelper(possibleListeners.current),
699
- _step;
836
+ var _iterator2 = _createForOfIteratorHelper(possibleListeners.current),
837
+ _step2;
700
838
  try {
701
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
839
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
702
840
  var _scrollElement$curren;
703
- var possibleListener = _step.value;
841
+ var possibleListener = _step2.value;
704
842
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
705
843
  (_scrollElement$curren = scrollElement.current) === null || _scrollElement$curren === void 0 || _scrollElement$curren.removeEventListener.apply(_scrollElement$curren, (0, _toConsumableArray2.default)(possibleListener));
706
844
  }
707
845
  } catch (err) {
708
- _iterator.e(err);
846
+ _iterator2.e(err);
709
847
  } finally {
710
- _iterator.f();
848
+ _iterator2.f();
711
849
  }
712
850
  }
713
851
  scrollElement.current = null;
@@ -719,19 +857,19 @@ function ReactEditorView(props) {
719
857
  scrollElement.current = document.querySelector('[data-editor-scroll-container]');
720
858
  var cleanupListeners = function cleanupListeners() {
721
859
  // eslint-disable-next-line react-hooks/exhaustive-deps
722
- var _iterator2 = _createForOfIteratorHelper(possibleListeners.current),
723
- _step2;
860
+ var _iterator3 = _createForOfIteratorHelper(possibleListeners.current),
861
+ _step3;
724
862
  try {
725
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
863
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
726
864
  var _scrollElement$curren2;
727
- var possibleListener = _step2.value;
865
+ var possibleListener = _step3.value;
728
866
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
729
867
  (_scrollElement$curren2 = scrollElement.current) === null || _scrollElement$curren2 === void 0 || _scrollElement$curren2.removeEventListener.apply(_scrollElement$curren2, (0, _toConsumableArray2.default)(possibleListener));
730
868
  }
731
869
  } catch (err) {
732
- _iterator2.e(err);
870
+ _iterator3.e(err);
733
871
  } finally {
734
- _iterator2.f();
872
+ _iterator3.f();
735
873
  }
736
874
  };
737
875
  if (scrollElement.current) {