@atlaskit/editor-common 107.6.0 → 107.7.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,27 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 107.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#182161](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/182161)
8
+ [`dcf2f7bd931a8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/dcf2f7bd931a8) -
9
+ feat: add useSharedStateWithSelector to editor-common
10
+
11
+ ### Patch Changes
12
+
13
+ - [#181781](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/181781)
14
+ [`e0060cc2c2eb7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e0060cc2c2eb7) -
15
+ ED-28417 Offline Editing: Update the type ahead error to clear on new requests, and make error
16
+ more generic.
17
+ - Updated dependencies
18
+
19
+ ## 107.6.1
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies
24
+
3
25
  ## 107.6.0
4
26
 
5
27
  ### Minor Changes
@@ -28,7 +28,14 @@ Object.defineProperty(exports, "useSharedPluginState", {
28
28
  return _useSharedPluginState.useSharedPluginState;
29
29
  }
30
30
  });
31
+ Object.defineProperty(exports, "useSharedPluginStateWithSelector", {
32
+ enumerable: true,
33
+ get: function get() {
34
+ return _useSharedPluginStateWithSelector.useSharedPluginStateWithSelector;
35
+ }
36
+ });
31
37
  var _usePreviousState = _interopRequireDefault(require("./usePreviousState"));
32
38
  var _useConstructor = _interopRequireDefault(require("./useConstructor"));
33
39
  var _useSharedPluginState = require("./useSharedPluginState");
34
- var _sharedPluginStateHookMigratorFactory = require("./sharedPluginStateHookMigratorFactory");
40
+ var _sharedPluginStateHookMigratorFactory = require("./sharedPluginStateHookMigratorFactory");
41
+ var _useSharedPluginStateWithSelector = require("./useSharedPluginStateWithSelector");
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.useSharedPluginStateWithSelector = useSharedPluginStateWithSelector;
8
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
+ var _react = require("react");
12
+ var _debounce = _interopRequireDefault(require("lodash/debounce"));
13
+ 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; }
14
+ 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; }
15
+ // Ignored via go/ees005
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+
18
+ /**
19
+ *
20
+ * Directly map object values
21
+ *
22
+ * @private
23
+ * @param object The object to transform
24
+ * @param mapFunction The function to map an old value to new one
25
+ * @returns Object with the same key but transformed values
26
+ *
27
+ */
28
+ function mapValues(object, mapFunction) {
29
+ return Object.entries(object).reduce(function (acc, _ref) {
30
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
31
+ key = _ref2[0],
32
+ value = _ref2[1];
33
+ return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2.default)({}, key, mapFunction(value)));
34
+ }, {});
35
+ }
36
+
37
+ // When we use the `useSharedPluginStateWithSelector` example: `useSharedPluginStateWithSelector(api, ['width'], selector)`
38
+ // it will re-render every time the component re-renders as the array "['width']" is seen as an update.
39
+ // This hook is used to prevent re-renders due to this.
40
+ function useStaticPlugins(plugins) {
41
+ // eslint-disable-next-line react-hooks/exhaustive-deps
42
+ return (0, _react.useMemo)(function () {
43
+ return plugins;
44
+ }, []);
45
+ }
46
+
47
+ /**
48
+ *
49
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
50
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
51
+ * your React Component will get only the last one.
52
+ *
53
+ * Used to return the current plugin state of input dependencies.
54
+ *
55
+ * @example
56
+ * Example in plugin:
57
+ *
58
+ * ```typescript
59
+ * function selector(states: NamedPluginStatesFromInjectionAPI<API, ['dog']>) {
60
+ * return {
61
+ * title: states.dogState?.title,
62
+ * }
63
+ * }
64
+ *
65
+ * function ExampleContent({ api }: Props) {
66
+ * const { title } = useSharedPluginStateWithSelector(
67
+ * api,
68
+ * ['dog'],
69
+ * selector
70
+ * )
71
+ * return <p>{ title }</p>
72
+ * }
73
+ *
74
+ * ```
75
+ *
76
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
77
+ * @param plugins Plugin names to get the shared plugin state for
78
+ * @param selector A function that takes the shared states of the plugins and returns a subset of a plugin state.
79
+ * @returns A corresponding object, the keys are names of the plugin with `State` appended,
80
+ * the values are the shared state exposed by that plugin.
81
+ */
82
+ function useSharedPluginStateWithSelector(injectionApi, plugins, selector) {
83
+ var pluginNames = useStaticPlugins(plugins);
84
+
85
+ // Create a memoized object containing the named plugins
86
+ var namedExternalPlugins = (0, _react.useMemo)(function () {
87
+ return pluginNames.reduce(function (acc, pluginName) {
88
+ return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2.default)({}, "".concat(String(pluginName), "State"), injectionApi === null || injectionApi === void 0 ? void 0 : injectionApi[pluginName]));
89
+ }, {});
90
+ }, [injectionApi, pluginNames]);
91
+ return useSharedPluginStateInternal(namedExternalPlugins, selector);
92
+ }
93
+ function useSharedPluginStateInternal(externalPlugins, selector) {
94
+ var refStates = (0, _react.useRef)(mapValues(externalPlugins, function (value) {
95
+ return value === null || value === void 0 ? void 0 : value.sharedState.currentState();
96
+ }));
97
+ var _useState = (0, _react.useState)(function () {
98
+ return selector(refStates.current);
99
+ }),
100
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
101
+ pluginStates = _useState2[0],
102
+ setPluginState = _useState2[1];
103
+ var mounted = (0, _react.useRef)(false);
104
+ (0, _react.useLayoutEffect)(function () {
105
+ var debouncedPluginStateUpdate = (0, _debounce.default)(function () {
106
+ setPluginState(function (currentPluginStates) {
107
+ // This is to avoid changing behaviour of the hook when selector is not provided.
108
+ var nextStates = selector(_objectSpread({}, refStates.current));
109
+ if (shallowEqual(nextStates, currentPluginStates)) {
110
+ return currentPluginStates;
111
+ }
112
+ return nextStates;
113
+ });
114
+ });
115
+
116
+ // If we re-render this hook due to a change in the external
117
+ // plugins we need to push a state update to ensure we have
118
+ // the most current state.
119
+ if (mounted.current) {
120
+ refStates.current = mapValues(externalPlugins, function (value) {
121
+ return value === null || value === void 0 ? void 0 : value.sharedState.currentState();
122
+ });
123
+ debouncedPluginStateUpdate();
124
+ }
125
+ var unsubs = Object.entries(externalPlugins).map(function (_ref3) {
126
+ var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
127
+ pluginKey = _ref4[0],
128
+ externalPlugin = _ref4[1];
129
+ return externalPlugin === null || externalPlugin === void 0 ? void 0 : externalPlugin.sharedState.onChange(function (_ref5) {
130
+ var nextSharedState = _ref5.nextSharedState,
131
+ prevSharedState = _ref5.prevSharedState;
132
+ if (prevSharedState === nextSharedState) {
133
+ return;
134
+ }
135
+ refStates.current[pluginKey] = nextSharedState;
136
+ debouncedPluginStateUpdate();
137
+ });
138
+ });
139
+ mounted.current = true;
140
+ return function () {
141
+ refStates.current = {};
142
+ unsubs.forEach(function (cb) {
143
+ return cb === null || cb === void 0 ? void 0 : cb();
144
+ });
145
+ };
146
+ // Do not re-render due to state changes, we only need to check this when
147
+ // setting up the initial subscription.
148
+ // eslint-disable-next-line react-hooks/exhaustive-deps
149
+ }, [externalPlugins, selector]);
150
+ return pluginStates;
151
+ }
152
+ function shallowEqual(objA, objB) {
153
+ if (objA === objB) {
154
+ return true;
155
+ }
156
+ if ((0, _typeof2.default)(objA) !== 'object' || objA === null || (0, _typeof2.default)(objB) !== 'object' || objB === null) {
157
+ return false;
158
+ }
159
+ var keysA = Object.keys(objA);
160
+ var keysB = Object.keys(objB);
161
+ if (keysA.length !== keysB.length) {
162
+ return false;
163
+ }
164
+ for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) {
165
+ var key = _keysA[_i];
166
+ if (objA[key] !== objB[key]) {
167
+ return false;
168
+ }
169
+ }
170
+ return true;
171
+ }
@@ -16,7 +16,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
16
16
  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); }
17
17
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
18
18
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
19
- var packageVersion = "107.5.0";
19
+ var packageVersion = "107.6.1";
20
20
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
21
21
  // Remove URL as it has UGC
22
22
  // Ignored via go/ees007
@@ -81,15 +81,15 @@ var typeAheadListMessages = exports.typeAheadListMessages = (0, _reactIntlNext.d
81
81
  defaultMessage: 'Text shortcut',
82
82
  description: 'Text shortcut'
83
83
  },
84
- offlineErrorFallbackHeading: {
85
- id: 'fabric.editor.offlineErrorFallbackHeading',
84
+ typeAheadErrorFallbackHeading: {
85
+ id: 'fabric.editor.typeAheadErrorFallbackHeading',
86
86
  defaultMessage: 'Something went wrong!',
87
- description: 'heading for offline error fallback when mentions are not available'
87
+ description: 'Error message heading shown when typeahead items fail to load'
88
88
  },
89
- offlineErrorFallbackDesc: {
90
- id: 'fabric.editor.offlineErrorFallbackDescription',
91
- defaultMessage: 'Try reloading the page.',
92
- description: 'description for offline error fallback when mentions are not available'
89
+ typeAheadErrorFallbackDesc: {
90
+ id: 'fabric.editor.typeAheadErrorFallbackDescription',
91
+ defaultMessage: 'Please try again.',
92
+ description: 'Error message description shown when typeahead items fail to load'
93
93
  },
94
94
  viewAllInserts: {
95
95
  id: 'fablric.editor.viewAllInserts',
@@ -23,7 +23,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
23
23
  * @jsx jsx
24
24
  */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
25
25
  var packageName = "@atlaskit/editor-common";
26
- var packageVersion = "107.5.0";
26
+ var packageVersion = "107.6.1";
27
27
  var halfFocusRing = 1;
28
28
  var dropOffset = '0, 8';
29
29
  // Ignored via go/ees005
@@ -4,4 +4,5 @@
4
4
  export { default as usePreviousState } from './usePreviousState';
5
5
  export { default as useConstructor } from './useConstructor';
6
6
  export { useSharedPluginState } from './useSharedPluginState';
7
- export { sharedPluginStateHookMigratorFactory } from './sharedPluginStateHookMigratorFactory';
7
+ export { sharedPluginStateHookMigratorFactory } from './sharedPluginStateHookMigratorFactory';
8
+ export { useSharedPluginStateWithSelector } from './useSharedPluginStateWithSelector';
@@ -0,0 +1,143 @@
1
+ import { useLayoutEffect, useMemo, useRef, useState } from 'react';
2
+ import debounce from 'lodash/debounce';
3
+
4
+ // Ignored via go/ees005
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+
7
+ /**
8
+ *
9
+ * Directly map object values
10
+ *
11
+ * @private
12
+ * @param object The object to transform
13
+ * @param mapFunction The function to map an old value to new one
14
+ * @returns Object with the same key but transformed values
15
+ *
16
+ */
17
+ function mapValues(object, mapFunction) {
18
+ return Object.entries(object).reduce((acc, [key, value]) => ({
19
+ ...acc,
20
+ [key]: mapFunction(value)
21
+ }), {});
22
+ }
23
+
24
+ // When we use the `useSharedPluginStateWithSelector` example: `useSharedPluginStateWithSelector(api, ['width'], selector)`
25
+ // it will re-render every time the component re-renders as the array "['width']" is seen as an update.
26
+ // This hook is used to prevent re-renders due to this.
27
+ function useStaticPlugins(plugins) {
28
+ // eslint-disable-next-line react-hooks/exhaustive-deps
29
+ return useMemo(() => plugins, []);
30
+ }
31
+
32
+ /**
33
+ *
34
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
35
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
36
+ * your React Component will get only the last one.
37
+ *
38
+ * Used to return the current plugin state of input dependencies.
39
+ *
40
+ * @example
41
+ * Example in plugin:
42
+ *
43
+ * ```typescript
44
+ * function selector(states: NamedPluginStatesFromInjectionAPI<API, ['dog']>) {
45
+ * return {
46
+ * title: states.dogState?.title,
47
+ * }
48
+ * }
49
+ *
50
+ * function ExampleContent({ api }: Props) {
51
+ * const { title } = useSharedPluginStateWithSelector(
52
+ * api,
53
+ * ['dog'],
54
+ * selector
55
+ * )
56
+ * return <p>{ title }</p>
57
+ * }
58
+ *
59
+ * ```
60
+ *
61
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
62
+ * @param plugins Plugin names to get the shared plugin state for
63
+ * @param selector A function that takes the shared states of the plugins and returns a subset of a plugin state.
64
+ * @returns A corresponding object, the keys are names of the plugin with `State` appended,
65
+ * the values are the shared state exposed by that plugin.
66
+ */
67
+ export function useSharedPluginStateWithSelector(injectionApi, plugins, selector) {
68
+ const pluginNames = useStaticPlugins(plugins);
69
+
70
+ // Create a memoized object containing the named plugins
71
+ const namedExternalPlugins = useMemo(() => pluginNames.reduce((acc, pluginName) => ({
72
+ ...acc,
73
+ [`${String(pluginName)}State`]: injectionApi === null || injectionApi === void 0 ? void 0 : injectionApi[pluginName]
74
+ }), {}), [injectionApi, pluginNames]);
75
+ return useSharedPluginStateInternal(namedExternalPlugins, selector);
76
+ }
77
+ function useSharedPluginStateInternal(externalPlugins, selector) {
78
+ const refStates = useRef(mapValues(externalPlugins, value => value === null || value === void 0 ? void 0 : value.sharedState.currentState()));
79
+ const [pluginStates, setPluginState] = useState(() => selector(refStates.current));
80
+ const mounted = useRef(false);
81
+ useLayoutEffect(() => {
82
+ const debouncedPluginStateUpdate = debounce(() => {
83
+ setPluginState(currentPluginStates => {
84
+ // This is to avoid changing behaviour of the hook when selector is not provided.
85
+ const nextStates = selector({
86
+ ...refStates.current
87
+ });
88
+ if (shallowEqual(nextStates, currentPluginStates)) {
89
+ return currentPluginStates;
90
+ }
91
+ return nextStates;
92
+ });
93
+ });
94
+
95
+ // If we re-render this hook due to a change in the external
96
+ // plugins we need to push a state update to ensure we have
97
+ // the most current state.
98
+ if (mounted.current) {
99
+ refStates.current = mapValues(externalPlugins, value => value === null || value === void 0 ? void 0 : value.sharedState.currentState());
100
+ debouncedPluginStateUpdate();
101
+ }
102
+ const unsubs = Object.entries(externalPlugins).map(([pluginKey, externalPlugin]) => {
103
+ return externalPlugin === null || externalPlugin === void 0 ? void 0 : externalPlugin.sharedState.onChange(({
104
+ nextSharedState,
105
+ prevSharedState
106
+ }) => {
107
+ if (prevSharedState === nextSharedState) {
108
+ return;
109
+ }
110
+ refStates.current[pluginKey] = nextSharedState;
111
+ debouncedPluginStateUpdate();
112
+ });
113
+ });
114
+ mounted.current = true;
115
+ return () => {
116
+ refStates.current = {};
117
+ unsubs.forEach(cb => cb === null || cb === void 0 ? void 0 : cb());
118
+ };
119
+ // Do not re-render due to state changes, we only need to check this when
120
+ // setting up the initial subscription.
121
+ // eslint-disable-next-line react-hooks/exhaustive-deps
122
+ }, [externalPlugins, selector]);
123
+ return pluginStates;
124
+ }
125
+ function shallowEqual(objA, objB) {
126
+ if (objA === objB) {
127
+ return true;
128
+ }
129
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
130
+ return false;
131
+ }
132
+ const keysA = Object.keys(objA);
133
+ const keysB = Object.keys(objB);
134
+ if (keysA.length !== keysB.length) {
135
+ return false;
136
+ }
137
+ for (const key of keysA) {
138
+ if (objA[key] !== objB[key]) {
139
+ return false;
140
+ }
141
+ }
142
+ return true;
143
+ }
@@ -1,7 +1,7 @@
1
1
  import { isFedRamp } from './environment';
2
2
  const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
3
3
  const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
4
- const packageVersion = "107.5.0";
4
+ const packageVersion = "107.6.1";
5
5
  const sanitiseSentryEvents = (data, _hint) => {
6
6
  // Remove URL as it has UGC
7
7
  // Ignored via go/ees007
@@ -75,15 +75,15 @@ export const typeAheadListMessages = defineMessages({
75
75
  defaultMessage: 'Text shortcut',
76
76
  description: 'Text shortcut'
77
77
  },
78
- offlineErrorFallbackHeading: {
79
- id: 'fabric.editor.offlineErrorFallbackHeading',
78
+ typeAheadErrorFallbackHeading: {
79
+ id: 'fabric.editor.typeAheadErrorFallbackHeading',
80
80
  defaultMessage: 'Something went wrong!',
81
- description: 'heading for offline error fallback when mentions are not available'
81
+ description: 'Error message heading shown when typeahead items fail to load'
82
82
  },
83
- offlineErrorFallbackDesc: {
84
- id: 'fabric.editor.offlineErrorFallbackDescription',
85
- defaultMessage: 'Try reloading the page.',
86
- description: 'description for offline error fallback when mentions are not available'
83
+ typeAheadErrorFallbackDesc: {
84
+ id: 'fabric.editor.typeAheadErrorFallbackDescription',
85
+ defaultMessage: 'Please try again.',
86
+ description: 'Error message description shown when typeahead items fail to load'
87
87
  },
88
88
  viewAllInserts: {
89
89
  id: 'fablric.editor.viewAllInserts',
@@ -13,7 +13,7 @@ import withAnalyticsContext from '@atlaskit/analytics-next/withAnalyticsContext'
13
13
  import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
14
14
  import Layer from '../Layer';
15
15
  const packageName = "@atlaskit/editor-common";
16
- const packageVersion = "107.5.0";
16
+ const packageVersion = "107.6.1";
17
17
  const halfFocusRing = 1;
18
18
  const dropOffset = '0, 8';
19
19
  // Ignored via go/ees005
@@ -4,4 +4,5 @@
4
4
  export { default as usePreviousState } from './usePreviousState';
5
5
  export { default as useConstructor } from './useConstructor';
6
6
  export { useSharedPluginState } from './useSharedPluginState';
7
- export { sharedPluginStateHookMigratorFactory } from './sharedPluginStateHookMigratorFactory';
7
+ export { sharedPluginStateHookMigratorFactory } from './sharedPluginStateHookMigratorFactory';
8
+ export { useSharedPluginStateWithSelector } from './useSharedPluginStateWithSelector';
@@ -0,0 +1,165 @@
1
+ import _typeof from "@babel/runtime/helpers/typeof";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
+ 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; }
5
+ 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; }
6
+ import { useLayoutEffect, useMemo, useRef, useState } from 'react';
7
+ import debounce from 'lodash/debounce';
8
+
9
+ // Ignored via go/ees005
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+
12
+ /**
13
+ *
14
+ * Directly map object values
15
+ *
16
+ * @private
17
+ * @param object The object to transform
18
+ * @param mapFunction The function to map an old value to new one
19
+ * @returns Object with the same key but transformed values
20
+ *
21
+ */
22
+ function mapValues(object, mapFunction) {
23
+ return Object.entries(object).reduce(function (acc, _ref) {
24
+ var _ref2 = _slicedToArray(_ref, 2),
25
+ key = _ref2[0],
26
+ value = _ref2[1];
27
+ return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, key, mapFunction(value)));
28
+ }, {});
29
+ }
30
+
31
+ // When we use the `useSharedPluginStateWithSelector` example: `useSharedPluginStateWithSelector(api, ['width'], selector)`
32
+ // it will re-render every time the component re-renders as the array "['width']" is seen as an update.
33
+ // This hook is used to prevent re-renders due to this.
34
+ function useStaticPlugins(plugins) {
35
+ // eslint-disable-next-line react-hooks/exhaustive-deps
36
+ return useMemo(function () {
37
+ return plugins;
38
+ }, []);
39
+ }
40
+
41
+ /**
42
+ *
43
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
44
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
45
+ * your React Component will get only the last one.
46
+ *
47
+ * Used to return the current plugin state of input dependencies.
48
+ *
49
+ * @example
50
+ * Example in plugin:
51
+ *
52
+ * ```typescript
53
+ * function selector(states: NamedPluginStatesFromInjectionAPI<API, ['dog']>) {
54
+ * return {
55
+ * title: states.dogState?.title,
56
+ * }
57
+ * }
58
+ *
59
+ * function ExampleContent({ api }: Props) {
60
+ * const { title } = useSharedPluginStateWithSelector(
61
+ * api,
62
+ * ['dog'],
63
+ * selector
64
+ * )
65
+ * return <p>{ title }</p>
66
+ * }
67
+ *
68
+ * ```
69
+ *
70
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
71
+ * @param plugins Plugin names to get the shared plugin state for
72
+ * @param selector A function that takes the shared states of the plugins and returns a subset of a plugin state.
73
+ * @returns A corresponding object, the keys are names of the plugin with `State` appended,
74
+ * the values are the shared state exposed by that plugin.
75
+ */
76
+ export function useSharedPluginStateWithSelector(injectionApi, plugins, selector) {
77
+ var pluginNames = useStaticPlugins(plugins);
78
+
79
+ // Create a memoized object containing the named plugins
80
+ var namedExternalPlugins = useMemo(function () {
81
+ return pluginNames.reduce(function (acc, pluginName) {
82
+ return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, "".concat(String(pluginName), "State"), injectionApi === null || injectionApi === void 0 ? void 0 : injectionApi[pluginName]));
83
+ }, {});
84
+ }, [injectionApi, pluginNames]);
85
+ return useSharedPluginStateInternal(namedExternalPlugins, selector);
86
+ }
87
+ function useSharedPluginStateInternal(externalPlugins, selector) {
88
+ var refStates = useRef(mapValues(externalPlugins, function (value) {
89
+ return value === null || value === void 0 ? void 0 : value.sharedState.currentState();
90
+ }));
91
+ var _useState = useState(function () {
92
+ return selector(refStates.current);
93
+ }),
94
+ _useState2 = _slicedToArray(_useState, 2),
95
+ pluginStates = _useState2[0],
96
+ setPluginState = _useState2[1];
97
+ var mounted = useRef(false);
98
+ useLayoutEffect(function () {
99
+ var debouncedPluginStateUpdate = debounce(function () {
100
+ setPluginState(function (currentPluginStates) {
101
+ // This is to avoid changing behaviour of the hook when selector is not provided.
102
+ var nextStates = selector(_objectSpread({}, refStates.current));
103
+ if (shallowEqual(nextStates, currentPluginStates)) {
104
+ return currentPluginStates;
105
+ }
106
+ return nextStates;
107
+ });
108
+ });
109
+
110
+ // If we re-render this hook due to a change in the external
111
+ // plugins we need to push a state update to ensure we have
112
+ // the most current state.
113
+ if (mounted.current) {
114
+ refStates.current = mapValues(externalPlugins, function (value) {
115
+ return value === null || value === void 0 ? void 0 : value.sharedState.currentState();
116
+ });
117
+ debouncedPluginStateUpdate();
118
+ }
119
+ var unsubs = Object.entries(externalPlugins).map(function (_ref3) {
120
+ var _ref4 = _slicedToArray(_ref3, 2),
121
+ pluginKey = _ref4[0],
122
+ externalPlugin = _ref4[1];
123
+ return externalPlugin === null || externalPlugin === void 0 ? void 0 : externalPlugin.sharedState.onChange(function (_ref5) {
124
+ var nextSharedState = _ref5.nextSharedState,
125
+ prevSharedState = _ref5.prevSharedState;
126
+ if (prevSharedState === nextSharedState) {
127
+ return;
128
+ }
129
+ refStates.current[pluginKey] = nextSharedState;
130
+ debouncedPluginStateUpdate();
131
+ });
132
+ });
133
+ mounted.current = true;
134
+ return function () {
135
+ refStates.current = {};
136
+ unsubs.forEach(function (cb) {
137
+ return cb === null || cb === void 0 ? void 0 : cb();
138
+ });
139
+ };
140
+ // Do not re-render due to state changes, we only need to check this when
141
+ // setting up the initial subscription.
142
+ // eslint-disable-next-line react-hooks/exhaustive-deps
143
+ }, [externalPlugins, selector]);
144
+ return pluginStates;
145
+ }
146
+ function shallowEqual(objA, objB) {
147
+ if (objA === objB) {
148
+ return true;
149
+ }
150
+ if (_typeof(objA) !== 'object' || objA === null || _typeof(objB) !== 'object' || objB === null) {
151
+ return false;
152
+ }
153
+ var keysA = Object.keys(objA);
154
+ var keysB = Object.keys(objB);
155
+ if (keysA.length !== keysB.length) {
156
+ return false;
157
+ }
158
+ for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) {
159
+ var key = _keysA[_i];
160
+ if (objA[key] !== objB[key]) {
161
+ return false;
162
+ }
163
+ }
164
+ return true;
165
+ }
@@ -7,7 +7,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
7
7
  import { isFedRamp } from './environment';
8
8
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
9
9
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
10
- var packageVersion = "107.5.0";
10
+ var packageVersion = "107.6.1";
11
11
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
12
12
  // Remove URL as it has UGC
13
13
  // Ignored via go/ees007
@@ -75,15 +75,15 @@ export var typeAheadListMessages = defineMessages({
75
75
  defaultMessage: 'Text shortcut',
76
76
  description: 'Text shortcut'
77
77
  },
78
- offlineErrorFallbackHeading: {
79
- id: 'fabric.editor.offlineErrorFallbackHeading',
78
+ typeAheadErrorFallbackHeading: {
79
+ id: 'fabric.editor.typeAheadErrorFallbackHeading',
80
80
  defaultMessage: 'Something went wrong!',
81
- description: 'heading for offline error fallback when mentions are not available'
81
+ description: 'Error message heading shown when typeahead items fail to load'
82
82
  },
83
- offlineErrorFallbackDesc: {
84
- id: 'fabric.editor.offlineErrorFallbackDescription',
85
- defaultMessage: 'Try reloading the page.',
86
- description: 'description for offline error fallback when mentions are not available'
83
+ typeAheadErrorFallbackDesc: {
84
+ id: 'fabric.editor.typeAheadErrorFallbackDescription',
85
+ defaultMessage: 'Please try again.',
86
+ description: 'Error message description shown when typeahead items fail to load'
87
87
  },
88
88
  viewAllInserts: {
89
89
  id: 'fablric.editor.viewAllInserts',
@@ -20,7 +20,7 @@ import withAnalyticsContext from '@atlaskit/analytics-next/withAnalyticsContext'
20
20
  import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
21
21
  import Layer from '../Layer';
22
22
  var packageName = "@atlaskit/editor-common";
23
- var packageVersion = "107.5.0";
23
+ var packageVersion = "107.6.1";
24
24
  var halfFocusRing = 1;
25
25
  var dropOffset = '0, 8';
26
26
  // Ignored via go/ees005
@@ -2,3 +2,4 @@ export { default as usePreviousState } from './usePreviousState';
2
2
  export { default as useConstructor } from './useConstructor';
3
3
  export { useSharedPluginState } from './useSharedPluginState';
4
4
  export { sharedPluginStateHookMigratorFactory } from './sharedPluginStateHookMigratorFactory';
5
+ export { useSharedPluginStateWithSelector, type NamedPluginStatesFromInjectionAPI, } from './useSharedPluginStateWithSelector';
@@ -0,0 +1,41 @@
1
+ import type { BasePluginDependenciesAPI, EditorInjectionAPI, ExtractInjectionAPI, ExtractPluginSharedState, NextEditorPlugin, NextEditorPluginMetadata } from '../types/next-editor-plugin';
2
+ export type NamedPluginStatesFromInjectionAPI<API extends ExtractInjectionAPI<NextEditorPlugin<any, any>>, PluginNames extends string | number | symbol> = Readonly<{
3
+ [K in PluginNames as `${K extends string ? K : never}State`]: API[K] extends BasePluginDependenciesAPI<any> | undefined ? Exclude<API[K], undefined> extends BasePluginDependenciesAPI<infer Metadata> ? Metadata extends NextEditorPluginMetadata ? ExtractPluginSharedState<Metadata> | undefined : never : never : never;
4
+ }>;
5
+ export type ExtractPluginNames<API extends EditorInjectionAPI<any, any>> = keyof API;
6
+ /**
7
+ *
8
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
9
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
10
+ * your React Component will get only the last one.
11
+ *
12
+ * Used to return the current plugin state of input dependencies.
13
+ *
14
+ * @example
15
+ * Example in plugin:
16
+ *
17
+ * ```typescript
18
+ * function selector(states: NamedPluginStatesFromInjectionAPI<API, ['dog']>) {
19
+ * return {
20
+ * title: states.dogState?.title,
21
+ * }
22
+ * }
23
+ *
24
+ * function ExampleContent({ api }: Props) {
25
+ * const { title } = useSharedPluginStateWithSelector(
26
+ * api,
27
+ * ['dog'],
28
+ * selector
29
+ * )
30
+ * return <p>{ title }</p>
31
+ * }
32
+ *
33
+ * ```
34
+ *
35
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
36
+ * @param plugins Plugin names to get the shared plugin state for
37
+ * @param selector A function that takes the shared states of the plugins and returns a subset of a plugin state.
38
+ * @returns A corresponding object, the keys are names of the plugin with `State` appended,
39
+ * the values are the shared state exposed by that plugin.
40
+ */
41
+ export declare function useSharedPluginStateWithSelector<API extends EditorInjectionAPI<any, any>, PluginNames extends ExtractPluginNames<API>, PluginStates extends NamedPluginStatesFromInjectionAPI<API, PluginNames>, SelectorResult>(injectionApi: API | null | undefined, plugins: PluginNames[], selector: (states: PluginStates) => SelectorResult): SelectorResult;
@@ -74,12 +74,12 @@ export declare const typeAheadListMessages: {
74
74
  defaultMessage: string;
75
75
  description: string;
76
76
  };
77
- offlineErrorFallbackHeading: {
77
+ typeAheadErrorFallbackHeading: {
78
78
  id: string;
79
79
  defaultMessage: string;
80
80
  description: string;
81
81
  };
82
- offlineErrorFallbackDesc: {
82
+ typeAheadErrorFallbackDesc: {
83
83
  id: string;
84
84
  defaultMessage: string;
85
85
  description: string;
@@ -2,3 +2,4 @@ export { default as usePreviousState } from './usePreviousState';
2
2
  export { default as useConstructor } from './useConstructor';
3
3
  export { useSharedPluginState } from './useSharedPluginState';
4
4
  export { sharedPluginStateHookMigratorFactory } from './sharedPluginStateHookMigratorFactory';
5
+ export { useSharedPluginStateWithSelector, type NamedPluginStatesFromInjectionAPI, } from './useSharedPluginStateWithSelector';
@@ -0,0 +1,41 @@
1
+ import type { BasePluginDependenciesAPI, EditorInjectionAPI, ExtractInjectionAPI, ExtractPluginSharedState, NextEditorPlugin, NextEditorPluginMetadata } from '../types/next-editor-plugin';
2
+ export type NamedPluginStatesFromInjectionAPI<API extends ExtractInjectionAPI<NextEditorPlugin<any, any>>, PluginNames extends string | number | symbol> = Readonly<{
3
+ [K in PluginNames as `${K extends string ? K : never}State`]: API[K] extends BasePluginDependenciesAPI<any> | undefined ? Exclude<API[K], undefined> extends BasePluginDependenciesAPI<infer Metadata> ? Metadata extends NextEditorPluginMetadata ? ExtractPluginSharedState<Metadata> | undefined : never : never : never;
4
+ }>;
5
+ export type ExtractPluginNames<API extends EditorInjectionAPI<any, any>> = keyof API;
6
+ /**
7
+ *
8
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
9
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
10
+ * your React Component will get only the last one.
11
+ *
12
+ * Used to return the current plugin state of input dependencies.
13
+ *
14
+ * @example
15
+ * Example in plugin:
16
+ *
17
+ * ```typescript
18
+ * function selector(states: NamedPluginStatesFromInjectionAPI<API, ['dog']>) {
19
+ * return {
20
+ * title: states.dogState?.title,
21
+ * }
22
+ * }
23
+ *
24
+ * function ExampleContent({ api }: Props) {
25
+ * const { title } = useSharedPluginStateWithSelector(
26
+ * api,
27
+ * ['dog'],
28
+ * selector
29
+ * )
30
+ * return <p>{ title }</p>
31
+ * }
32
+ *
33
+ * ```
34
+ *
35
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
36
+ * @param plugins Plugin names to get the shared plugin state for
37
+ * @param selector A function that takes the shared states of the plugins and returns a subset of a plugin state.
38
+ * @returns A corresponding object, the keys are names of the plugin with `State` appended,
39
+ * the values are the shared state exposed by that plugin.
40
+ */
41
+ export declare function useSharedPluginStateWithSelector<API extends EditorInjectionAPI<any, any>, PluginNames extends ExtractPluginNames<API>, PluginStates extends NamedPluginStatesFromInjectionAPI<API, PluginNames>, SelectorResult>(injectionApi: API | null | undefined, plugins: PluginNames[], selector: (states: PluginStates) => SelectorResult): SelectorResult;
@@ -74,12 +74,12 @@ export declare const typeAheadListMessages: {
74
74
  defaultMessage: string;
75
75
  description: string;
76
76
  };
77
- offlineErrorFallbackHeading: {
77
+ typeAheadErrorFallbackHeading: {
78
78
  id: string;
79
79
  defaultMessage: string;
80
80
  description: string;
81
81
  };
82
- offlineErrorFallbackDesc: {
82
+ typeAheadErrorFallbackDesc: {
83
83
  id: string;
84
84
  defaultMessage: string;
85
85
  description: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-common",
3
- "version": "107.6.0",
3
+ "version": "107.7.0",
4
4
  "description": "A package that contains common classes and components for editor and renderer",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -143,7 +143,7 @@
143
143
  "@atlaskit/icon": "^27.2.0",
144
144
  "@atlaskit/icon-object": "^7.1.0",
145
145
  "@atlaskit/link": "^3.2.0",
146
- "@atlaskit/link-datasource": "^4.11.0",
146
+ "@atlaskit/link-datasource": "^4.12.0",
147
147
  "@atlaskit/link-picker": "^3.5.0",
148
148
  "@atlaskit/media-card": "^79.3.0",
149
149
  "@atlaskit/media-client": "^35.0.0",
@@ -163,13 +163,13 @@
163
163
  "@atlaskit/profilecard": "^23.21.0",
164
164
  "@atlaskit/react-ufo": "^3.14.0",
165
165
  "@atlaskit/section-message": "^8.2.0",
166
- "@atlaskit/smart-card": "^38.20.0",
166
+ "@atlaskit/smart-card": "^39.0.0",
167
167
  "@atlaskit/smart-user-picker": "^8.0.0",
168
168
  "@atlaskit/spinner": "^18.0.0",
169
169
  "@atlaskit/task-decision": "^19.2.0",
170
170
  "@atlaskit/textfield": "^8.0.0",
171
171
  "@atlaskit/theme": "^18.0.0",
172
- "@atlaskit/tmp-editor-statsig": "^8.7.0",
172
+ "@atlaskit/tmp-editor-statsig": "^8.8.0",
173
173
  "@atlaskit/tokens": "^5.4.0",
174
174
  "@atlaskit/tooltip": "^20.3.0",
175
175
  "@atlaskit/width-detector": "^5.0.0",