@atlaskit/editor-common 94.20.0 → 94.21.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,18 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 94.21.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#162829](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/162829)
8
+ [`5bf267e7de21e`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/5bf267e7de21e) -
9
+ Introduce new usePluginStateEffect which can be used to run effects on plugin state change
10
+ (similar to useSharedPluginState but without re-renders)
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
3
16
  ## 94.20.0
4
17
 
5
18
  ### Minor Changes
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.usePluginStateEffect = usePluginStateEffect;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _react = require("react");
11
+ var _debounce = _interopRequireDefault(require("lodash/debounce"));
12
+ 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; }
13
+ 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; }
14
+ /**
15
+ *
16
+ * Directly map object values
17
+ *
18
+ * @param object The object to transform
19
+ * @param mapFunction The function to map an old value to new one
20
+ * @returns Object with the same key but transformed values
21
+ *
22
+ */
23
+ function mapValues(object, mapFunction) {
24
+ return Object.entries(object).reduce(function (acc, _ref) {
25
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
26
+ key = _ref2[0],
27
+ value = _ref2[1];
28
+ return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2.default)({}, key, mapFunction(value)));
29
+ }, {});
30
+ }
31
+
32
+ // When we use the `useSharedPluginState` example: `useSharedPluginState(api, ['width'])`
33
+ // it will re-render every time the component re-renders as the array "['width']" is seen as an update.
34
+ // This hook is used to prevent re-renders due to this.
35
+ function useStaticPlugins(plugins) {
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ return (0, _react.useMemo)(function () {
38
+ return plugins;
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 run effects on changes in editor state - similar to `useSharedPluginState` except this
48
+ * is for reacting to state changes so does not cause re-renders. The effect callback passed will be called
49
+ * on initialisation and when the state changes (with the latest callback we are provided).
50
+ *
51
+ * Example in plugin:
52
+ *
53
+ * ```typescript
54
+ * function ExampleContent({ api }: Props) {
55
+ * const pluginStateCallback = useCallback(( { dogState, exampleState } ) => {
56
+ * // Use as necessary ie. fire analytics or network requests
57
+ * console.log(dogState, exampleState)
58
+ * }, [])
59
+ * usePluginStateEffect(
60
+ * api,
61
+ * ['dog', 'example'],
62
+ * pluginStateCallback
63
+ * )
64
+ * return <p>Content</p>
65
+ * }
66
+ *
67
+ * const examplePlugin: NextEditorPlugin<'example', { dependencies: [typeof pluginDog] }> = ({ api }) => {
68
+ * return {
69
+ * name: 'example',
70
+ * contentComponent: () =>
71
+ * <ExampleContent
72
+ * api={api}
73
+ * />
74
+ * }
75
+ * }
76
+ * ```
77
+ *
78
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
79
+ * @param plugins Plugin names to get the shared plugin state for
80
+ * @param effect A callback, the parameter is a corresponding object, the keys are names of the plugin with `State` appended,
81
+ * the values are the shared state exposed by that plugin. This effect fires when the state changes and runs the most recent callback passed.
82
+ * If the callback changes the effect is not re-run - it is still recommended however to wrap your effect in `useCallback`,
83
+ * You can return a function from your effect to call any cleanup activities which will be called on unmount and when `editorApi` changes.
84
+ */
85
+ function usePluginStateEffect(injectionApi, plugins, effect) {
86
+ var pluginNames = useStaticPlugins(plugins);
87
+
88
+ // Create a memoized object containing the named plugins
89
+ var namedExternalPlugins = (0, _react.useMemo)(function () {
90
+ return pluginNames.reduce(function (acc, pluginName) {
91
+ return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2.default)({}, "".concat(String(pluginName), "State"), injectionApi === null || injectionApi === void 0 ? void 0 : injectionApi[pluginName]));
92
+ }, {});
93
+ }, [injectionApi, pluginNames]);
94
+ usePluginStateEffectInternal(namedExternalPlugins, effect);
95
+ }
96
+ function usePluginStateEffectInternal(externalPlugins, effect) {
97
+ var refStates = (0, _react.useRef)();
98
+ var cleanup = (0, _react.useRef)();
99
+ var latestEffect = (0, _react.useRef)(effect);
100
+
101
+ // We should store the latest effect in a reference so it is more intuitive to the user
102
+ // and we are not causing a memory leak by having references to old state.
103
+ (0, _react.useEffect)(function () {
104
+ latestEffect.current = (0, _debounce.default)(effect);
105
+ return function () {
106
+ latestEffect.current = undefined;
107
+ };
108
+ }, [effect]);
109
+ (0, _react.useLayoutEffect)(function () {
110
+ var _latestEffect$current;
111
+ // Update the reference for this plugin and activate the effect
112
+ refStates.current = mapValues(externalPlugins, function (value) {
113
+ return value === null || value === void 0 ? void 0 : value.sharedState.currentState();
114
+ });
115
+ cleanup.current = (_latestEffect$current = latestEffect.current) === null || _latestEffect$current === void 0 ? void 0 : _latestEffect$current.call(latestEffect, refStates.current);
116
+ var unsubs = Object.entries(externalPlugins).map(function (_ref3) {
117
+ var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
118
+ pluginKey = _ref4[0],
119
+ externalPlugin = _ref4[1];
120
+ return externalPlugin === null || externalPlugin === void 0 ? void 0 : externalPlugin.sharedState.onChange(function (_ref5) {
121
+ var nextSharedState = _ref5.nextSharedState,
122
+ prevSharedState = _ref5.prevSharedState;
123
+ if (prevSharedState !== nextSharedState && refStates.current) {
124
+ var _latestEffect$current2;
125
+ // Update the reference for this plugin and activate the effect
126
+ refStates.current[pluginKey] = nextSharedState;
127
+ cleanup.current = (_latestEffect$current2 = latestEffect.current) === null || _latestEffect$current2 === void 0 ? void 0 : _latestEffect$current2.call(latestEffect, refStates.current);
128
+ }
129
+ });
130
+ });
131
+ return function () {
132
+ var _cleanup$current;
133
+ refStates.current = undefined;
134
+ unsubs.forEach(function (cb) {
135
+ return cb === null || cb === void 0 ? void 0 : cb();
136
+ });
137
+ (_cleanup$current = cleanup.current) === null || _cleanup$current === void 0 || _cleanup$current.call(cleanup);
138
+ };
139
+ // Do not re-run if the `effect` changes - this is not expected with `useEffect` or similar hooks
140
+ // eslint-disable-next-line react-hooks/exhaustive-deps
141
+ }, [externalPlugins]);
142
+ }
@@ -17,7 +17,7 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
17
17
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
18
18
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
19
19
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
20
- var packageVersion = "94.20.0";
20
+ var packageVersion = "94.21.0";
21
21
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
22
22
  // Remove URL as it has UGC
23
23
  // TODO: Sanitise the URL instead of just removing it
@@ -24,7 +24,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
24
24
  * @jsx jsx
25
25
  */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
26
26
  var packageName = "@atlaskit/editor-common";
27
- var packageVersion = "94.20.0";
27
+ var packageVersion = "94.21.0";
28
28
  var halfFocusRing = 1;
29
29
  var dropOffset = '0, 8';
30
30
  var DropList = /*#__PURE__*/function (_Component) {
@@ -0,0 +1,118 @@
1
+ import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
2
+ import debounce from 'lodash/debounce';
3
+ /**
4
+ *
5
+ * Directly map object values
6
+ *
7
+ * @param object The object to transform
8
+ * @param mapFunction The function to map an old value to new one
9
+ * @returns Object with the same key but transformed values
10
+ *
11
+ */
12
+ function mapValues(object, mapFunction) {
13
+ return Object.entries(object).reduce((acc, [key, value]) => ({
14
+ ...acc,
15
+ [key]: mapFunction(value)
16
+ }), {});
17
+ }
18
+
19
+ // When we use the `useSharedPluginState` example: `useSharedPluginState(api, ['width'])`
20
+ // it will re-render every time the component re-renders as the array "['width']" is seen as an update.
21
+ // This hook is used to prevent re-renders due to this.
22
+ function useStaticPlugins(plugins) {
23
+ // eslint-disable-next-line react-hooks/exhaustive-deps
24
+ return useMemo(() => plugins, []);
25
+ }
26
+ /**
27
+ *
28
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
29
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
30
+ * your React Component will get only the last one.
31
+ *
32
+ * Used to run effects on changes in editor state - similar to `useSharedPluginState` except this
33
+ * is for reacting to state changes so does not cause re-renders. The effect callback passed will be called
34
+ * on initialisation and when the state changes (with the latest callback we are provided).
35
+ *
36
+ * Example in plugin:
37
+ *
38
+ * ```typescript
39
+ * function ExampleContent({ api }: Props) {
40
+ * const pluginStateCallback = useCallback(( { dogState, exampleState } ) => {
41
+ * // Use as necessary ie. fire analytics or network requests
42
+ * console.log(dogState, exampleState)
43
+ * }, [])
44
+ * usePluginStateEffect(
45
+ * api,
46
+ * ['dog', 'example'],
47
+ * pluginStateCallback
48
+ * )
49
+ * return <p>Content</p>
50
+ * }
51
+ *
52
+ * const examplePlugin: NextEditorPlugin<'example', { dependencies: [typeof pluginDog] }> = ({ api }) => {
53
+ * return {
54
+ * name: 'example',
55
+ * contentComponent: () =>
56
+ * <ExampleContent
57
+ * api={api}
58
+ * />
59
+ * }
60
+ * }
61
+ * ```
62
+ *
63
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
64
+ * @param plugins Plugin names to get the shared plugin state for
65
+ * @param effect A callback, the parameter is a corresponding object, the keys are names of the plugin with `State` appended,
66
+ * the values are the shared state exposed by that plugin. This effect fires when the state changes and runs the most recent callback passed.
67
+ * If the callback changes the effect is not re-run - it is still recommended however to wrap your effect in `useCallback`,
68
+ * You can return a function from your effect to call any cleanup activities which will be called on unmount and when `editorApi` changes.
69
+ */
70
+ export function usePluginStateEffect(injectionApi, plugins, effect) {
71
+ const pluginNames = useStaticPlugins(plugins);
72
+
73
+ // Create a memoized object containing the named plugins
74
+ const namedExternalPlugins = useMemo(() => pluginNames.reduce((acc, pluginName) => ({
75
+ ...acc,
76
+ [`${String(pluginName)}State`]: injectionApi === null || injectionApi === void 0 ? void 0 : injectionApi[pluginName]
77
+ }), {}), [injectionApi, pluginNames]);
78
+ usePluginStateEffectInternal(namedExternalPlugins, effect);
79
+ }
80
+ function usePluginStateEffectInternal(externalPlugins, effect) {
81
+ const refStates = useRef();
82
+ const cleanup = useRef();
83
+ const latestEffect = useRef(effect);
84
+
85
+ // We should store the latest effect in a reference so it is more intuitive to the user
86
+ // and we are not causing a memory leak by having references to old state.
87
+ useEffect(() => {
88
+ latestEffect.current = debounce(effect);
89
+ return () => {
90
+ latestEffect.current = undefined;
91
+ };
92
+ }, [effect]);
93
+ useLayoutEffect(() => {
94
+ var _latestEffect$current;
95
+ // Update the reference for this plugin and activate the effect
96
+ refStates.current = mapValues(externalPlugins, value => value === null || value === void 0 ? void 0 : value.sharedState.currentState());
97
+ cleanup.current = (_latestEffect$current = latestEffect.current) === null || _latestEffect$current === void 0 ? void 0 : _latestEffect$current.call(latestEffect, refStates.current);
98
+ const unsubs = Object.entries(externalPlugins).map(([pluginKey, externalPlugin]) => externalPlugin === null || externalPlugin === void 0 ? void 0 : externalPlugin.sharedState.onChange(({
99
+ nextSharedState,
100
+ prevSharedState
101
+ }) => {
102
+ if (prevSharedState !== nextSharedState && refStates.current) {
103
+ var _latestEffect$current2;
104
+ // Update the reference for this plugin and activate the effect
105
+ refStates.current[pluginKey] = nextSharedState;
106
+ cleanup.current = (_latestEffect$current2 = latestEffect.current) === null || _latestEffect$current2 === void 0 ? void 0 : _latestEffect$current2.call(latestEffect, refStates.current);
107
+ }
108
+ }));
109
+ return () => {
110
+ var _cleanup$current;
111
+ refStates.current = undefined;
112
+ unsubs.forEach(cb => cb === null || cb === void 0 ? void 0 : cb());
113
+ (_cleanup$current = cleanup.current) === null || _cleanup$current === void 0 ? void 0 : _cleanup$current.call(cleanup);
114
+ };
115
+ // Do not re-run if the `effect` changes - this is not expected with `useEffect` or similar hooks
116
+ // eslint-disable-next-line react-hooks/exhaustive-deps
117
+ }, [externalPlugins]);
118
+ }
@@ -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 = "94.20.0";
4
+ const packageVersion = "94.21.0";
5
5
  const sanitiseSentryEvents = (data, _hint) => {
6
6
  // Remove URL as it has UGC
7
7
  // TODO: Sanitise the URL instead of just removing it
@@ -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 = "94.20.0";
16
+ const packageVersion = "94.21.0";
17
17
  const halfFocusRing = 1;
18
18
  const dropOffset = '0, 8';
19
19
  class DropList extends Component {
@@ -0,0 +1,135 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ 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; }
4
+ 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; }
5
+ import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
6
+ import debounce from 'lodash/debounce';
7
+ /**
8
+ *
9
+ * Directly map object values
10
+ *
11
+ * @param object The object to transform
12
+ * @param mapFunction The function to map an old value to new one
13
+ * @returns Object with the same key but transformed values
14
+ *
15
+ */
16
+ function mapValues(object, mapFunction) {
17
+ return Object.entries(object).reduce(function (acc, _ref) {
18
+ var _ref2 = _slicedToArray(_ref, 2),
19
+ key = _ref2[0],
20
+ value = _ref2[1];
21
+ return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, key, mapFunction(value)));
22
+ }, {});
23
+ }
24
+
25
+ // When we use the `useSharedPluginState` example: `useSharedPluginState(api, ['width'])`
26
+ // it will re-render every time the component re-renders as the array "['width']" is seen as an update.
27
+ // This hook is used to prevent re-renders due to this.
28
+ function useStaticPlugins(plugins) {
29
+ // eslint-disable-next-line react-hooks/exhaustive-deps
30
+ return useMemo(function () {
31
+ return plugins;
32
+ }, []);
33
+ }
34
+ /**
35
+ *
36
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
37
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
38
+ * your React Component will get only the last one.
39
+ *
40
+ * Used to run effects on changes in editor state - similar to `useSharedPluginState` except this
41
+ * is for reacting to state changes so does not cause re-renders. The effect callback passed will be called
42
+ * on initialisation and when the state changes (with the latest callback we are provided).
43
+ *
44
+ * Example in plugin:
45
+ *
46
+ * ```typescript
47
+ * function ExampleContent({ api }: Props) {
48
+ * const pluginStateCallback = useCallback(( { dogState, exampleState } ) => {
49
+ * // Use as necessary ie. fire analytics or network requests
50
+ * console.log(dogState, exampleState)
51
+ * }, [])
52
+ * usePluginStateEffect(
53
+ * api,
54
+ * ['dog', 'example'],
55
+ * pluginStateCallback
56
+ * )
57
+ * return <p>Content</p>
58
+ * }
59
+ *
60
+ * const examplePlugin: NextEditorPlugin<'example', { dependencies: [typeof pluginDog] }> = ({ api }) => {
61
+ * return {
62
+ * name: 'example',
63
+ * contentComponent: () =>
64
+ * <ExampleContent
65
+ * api={api}
66
+ * />
67
+ * }
68
+ * }
69
+ * ```
70
+ *
71
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
72
+ * @param plugins Plugin names to get the shared plugin state for
73
+ * @param effect A callback, the parameter is a corresponding object, the keys are names of the plugin with `State` appended,
74
+ * the values are the shared state exposed by that plugin. This effect fires when the state changes and runs the most recent callback passed.
75
+ * If the callback changes the effect is not re-run - it is still recommended however to wrap your effect in `useCallback`,
76
+ * You can return a function from your effect to call any cleanup activities which will be called on unmount and when `editorApi` changes.
77
+ */
78
+ export function usePluginStateEffect(injectionApi, plugins, effect) {
79
+ var pluginNames = useStaticPlugins(plugins);
80
+
81
+ // Create a memoized object containing the named plugins
82
+ var namedExternalPlugins = useMemo(function () {
83
+ return pluginNames.reduce(function (acc, pluginName) {
84
+ return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, "".concat(String(pluginName), "State"), injectionApi === null || injectionApi === void 0 ? void 0 : injectionApi[pluginName]));
85
+ }, {});
86
+ }, [injectionApi, pluginNames]);
87
+ usePluginStateEffectInternal(namedExternalPlugins, effect);
88
+ }
89
+ function usePluginStateEffectInternal(externalPlugins, effect) {
90
+ var refStates = useRef();
91
+ var cleanup = useRef();
92
+ var latestEffect = useRef(effect);
93
+
94
+ // We should store the latest effect in a reference so it is more intuitive to the user
95
+ // and we are not causing a memory leak by having references to old state.
96
+ useEffect(function () {
97
+ latestEffect.current = debounce(effect);
98
+ return function () {
99
+ latestEffect.current = undefined;
100
+ };
101
+ }, [effect]);
102
+ useLayoutEffect(function () {
103
+ var _latestEffect$current;
104
+ // Update the reference for this plugin and activate the effect
105
+ refStates.current = mapValues(externalPlugins, function (value) {
106
+ return value === null || value === void 0 ? void 0 : value.sharedState.currentState();
107
+ });
108
+ cleanup.current = (_latestEffect$current = latestEffect.current) === null || _latestEffect$current === void 0 ? void 0 : _latestEffect$current.call(latestEffect, refStates.current);
109
+ var unsubs = Object.entries(externalPlugins).map(function (_ref3) {
110
+ var _ref4 = _slicedToArray(_ref3, 2),
111
+ pluginKey = _ref4[0],
112
+ externalPlugin = _ref4[1];
113
+ return externalPlugin === null || externalPlugin === void 0 ? void 0 : externalPlugin.sharedState.onChange(function (_ref5) {
114
+ var nextSharedState = _ref5.nextSharedState,
115
+ prevSharedState = _ref5.prevSharedState;
116
+ if (prevSharedState !== nextSharedState && refStates.current) {
117
+ var _latestEffect$current2;
118
+ // Update the reference for this plugin and activate the effect
119
+ refStates.current[pluginKey] = nextSharedState;
120
+ cleanup.current = (_latestEffect$current2 = latestEffect.current) === null || _latestEffect$current2 === void 0 ? void 0 : _latestEffect$current2.call(latestEffect, refStates.current);
121
+ }
122
+ });
123
+ });
124
+ return function () {
125
+ var _cleanup$current;
126
+ refStates.current = undefined;
127
+ unsubs.forEach(function (cb) {
128
+ return cb === null || cb === void 0 ? void 0 : cb();
129
+ });
130
+ (_cleanup$current = cleanup.current) === null || _cleanup$current === void 0 || _cleanup$current.call(cleanup);
131
+ };
132
+ // Do not re-run if the `effect` changes - this is not expected with `useEffect` or similar hooks
133
+ // eslint-disable-next-line react-hooks/exhaustive-deps
134
+ }, [externalPlugins]);
135
+ }
@@ -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 = "94.20.0";
10
+ var packageVersion = "94.21.0";
11
11
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
12
12
  // Remove URL as it has UGC
13
13
  // TODO: Sanitise the URL instead of just removing it
@@ -21,7 +21,7 @@ import withAnalyticsContext from '@atlaskit/analytics-next/withAnalyticsContext'
21
21
  import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
22
22
  import Layer from '../Layer';
23
23
  var packageName = "@atlaskit/editor-common";
24
- var packageVersion = "94.20.0";
24
+ var packageVersion = "94.21.0";
25
25
  var halfFocusRing = 1;
26
26
  var dropOffset = '0, 8';
27
27
  var DropList = /*#__PURE__*/function (_Component) {
@@ -0,0 +1,52 @@
1
+ import type { BasePluginDependenciesAPI, EditorInjectionAPI, ExtractInjectionAPI, NextEditorPlugin } from '../types/next-editor-plugin';
2
+ 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 ? ReturnType<API[K]['sharedState']['currentState']> : never;
4
+ }>;
5
+ type ExtractPluginNames<API extends EditorInjectionAPI<any, any>> = keyof API;
6
+ type Cleanup = () => void;
7
+ /**
8
+ *
9
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
10
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
11
+ * your React Component will get only the last one.
12
+ *
13
+ * Used to run effects on changes in editor state - similar to `useSharedPluginState` except this
14
+ * is for reacting to state changes so does not cause re-renders. The effect callback passed will be called
15
+ * on initialisation and when the state changes (with the latest callback we are provided).
16
+ *
17
+ * Example in plugin:
18
+ *
19
+ * ```typescript
20
+ * function ExampleContent({ api }: Props) {
21
+ * const pluginStateCallback = useCallback(( { dogState, exampleState } ) => {
22
+ * // Use as necessary ie. fire analytics or network requests
23
+ * console.log(dogState, exampleState)
24
+ * }, [])
25
+ * usePluginStateEffect(
26
+ * api,
27
+ * ['dog', 'example'],
28
+ * pluginStateCallback
29
+ * )
30
+ * return <p>Content</p>
31
+ * }
32
+ *
33
+ * const examplePlugin: NextEditorPlugin<'example', { dependencies: [typeof pluginDog] }> = ({ api }) => {
34
+ * return {
35
+ * name: 'example',
36
+ * contentComponent: () =>
37
+ * <ExampleContent
38
+ * api={api}
39
+ * />
40
+ * }
41
+ * }
42
+ * ```
43
+ *
44
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
45
+ * @param plugins Plugin names to get the shared plugin state for
46
+ * @param effect A callback, the parameter is a corresponding object, the keys are names of the plugin with `State` appended,
47
+ * the values are the shared state exposed by that plugin. This effect fires when the state changes and runs the most recent callback passed.
48
+ * If the callback changes the effect is not re-run - it is still recommended however to wrap your effect in `useCallback`,
49
+ * You can return a function from your effect to call any cleanup activities which will be called on unmount and when `editorApi` changes.
50
+ */
51
+ export declare function usePluginStateEffect<API extends EditorInjectionAPI<any, any>, PluginNames extends ExtractPluginNames<API>>(injectionApi: API | null | undefined, plugins: PluginNames[], effect: (states: NamedPluginStatesFromInjectionAPI<API, PluginNames>) => Cleanup | void): void;
52
+ export {};
@@ -0,0 +1,52 @@
1
+ import type { BasePluginDependenciesAPI, EditorInjectionAPI, ExtractInjectionAPI, NextEditorPlugin } from '../types/next-editor-plugin';
2
+ 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 ? ReturnType<API[K]['sharedState']['currentState']> : never;
4
+ }>;
5
+ type ExtractPluginNames<API extends EditorInjectionAPI<any, any>> = keyof API;
6
+ type Cleanup = () => void;
7
+ /**
8
+ *
9
+ * ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
10
+ * If the plugins you are listening to generate multiple shared states while the user is typing,
11
+ * your React Component will get only the last one.
12
+ *
13
+ * Used to run effects on changes in editor state - similar to `useSharedPluginState` except this
14
+ * is for reacting to state changes so does not cause re-renders. The effect callback passed will be called
15
+ * on initialisation and when the state changes (with the latest callback we are provided).
16
+ *
17
+ * Example in plugin:
18
+ *
19
+ * ```typescript
20
+ * function ExampleContent({ api }: Props) {
21
+ * const pluginStateCallback = useCallback(( { dogState, exampleState } ) => {
22
+ * // Use as necessary ie. fire analytics or network requests
23
+ * console.log(dogState, exampleState)
24
+ * }, [])
25
+ * usePluginStateEffect(
26
+ * api,
27
+ * ['dog', 'example'],
28
+ * pluginStateCallback
29
+ * )
30
+ * return <p>Content</p>
31
+ * }
32
+ *
33
+ * const examplePlugin: NextEditorPlugin<'example', { dependencies: [typeof pluginDog] }> = ({ api }) => {
34
+ * return {
35
+ * name: 'example',
36
+ * contentComponent: () =>
37
+ * <ExampleContent
38
+ * api={api}
39
+ * />
40
+ * }
41
+ * }
42
+ * ```
43
+ *
44
+ * @param injectionApi Plugin injection API from `NextEditorPlugin`
45
+ * @param plugins Plugin names to get the shared plugin state for
46
+ * @param effect A callback, the parameter is a corresponding object, the keys are names of the plugin with `State` appended,
47
+ * the values are the shared state exposed by that plugin. This effect fires when the state changes and runs the most recent callback passed.
48
+ * If the callback changes the effect is not re-run - it is still recommended however to wrap your effect in `useCallback`,
49
+ * You can return a function from your effect to call any cleanup activities which will be called on unmount and when `editorApi` changes.
50
+ */
51
+ export declare function usePluginStateEffect<API extends EditorInjectionAPI<any, any>, PluginNames extends ExtractPluginNames<API>>(injectionApi: API | null | undefined, plugins: PluginNames[], effect: (states: NamedPluginStatesFromInjectionAPI<API, PluginNames>) => Cleanup | void): void;
52
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-common",
3
- "version": "94.20.0",
3
+ "version": "94.21.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/"
@@ -104,6 +104,7 @@
104
104
  "./intl-error-boundary": "./src/ui/IntlErrorBoundary/index.tsx",
105
105
  "./code-block": "./src/code-block/index.ts",
106
106
  "./table": "./src/table/index.ts",
107
+ "./use-plugin-state-effect": "./src/hooks/usePluginStateEffect.ts",
107
108
  "./lazy-node-view": "./src/lazy-node-view/index.ts",
108
109
  "./nesting": "./src/nesting/utilities.ts",
109
110
  "./UNSAFE_do_not_use_editor_context": "./src/ui/EditorContext/index.ts"
@@ -129,30 +130,30 @@
129
130
  "@atlaskit/emoji": "^67.9.0",
130
131
  "@atlaskit/icon": "^22.24.0",
131
132
  "@atlaskit/icon-object": "^6.7.0",
132
- "@atlaskit/link-datasource": "^3.8.0",
133
+ "@atlaskit/link-datasource": "^3.9.0",
133
134
  "@atlaskit/link-picker": "^1.47.0",
134
- "@atlaskit/media-card": "^78.13.0",
135
+ "@atlaskit/media-card": "^78.14.0",
135
136
  "@atlaskit/media-client": "^28.3.0",
136
137
  "@atlaskit/media-client-react": "^2.3.0",
137
138
  "@atlaskit/media-common": "^11.7.0",
138
139
  "@atlaskit/media-file-preview": "^0.9.0",
139
140
  "@atlaskit/media-picker": "^67.0.0",
140
141
  "@atlaskit/media-ui": "^26.2.0",
141
- "@atlaskit/media-viewer": "49.4.0",
142
+ "@atlaskit/media-viewer": "49.4.1",
142
143
  "@atlaskit/mention": "^23.3.0",
143
144
  "@atlaskit/menu": "^2.13.0",
144
145
  "@atlaskit/onboarding": "^12.1.0",
145
146
  "@atlaskit/platform-feature-flags": "^0.3.0",
146
147
  "@atlaskit/popper": "^6.3.0",
147
- "@atlaskit/primitives": "^13.1.0",
148
- "@atlaskit/profilecard": "^20.11.0",
148
+ "@atlaskit/primitives": "^13.2.0",
149
+ "@atlaskit/profilecard": "^20.12.0",
149
150
  "@atlaskit/section-message": "^6.6.0",
150
151
  "@atlaskit/smart-card": "^30.2.0",
151
152
  "@atlaskit/smart-user-picker": "^6.11.0",
152
153
  "@atlaskit/spinner": "^16.3.0",
153
154
  "@atlaskit/task-decision": "^17.11.0",
154
155
  "@atlaskit/textfield": "^6.5.0",
155
- "@atlaskit/tmp-editor-statsig": "^2.14.0",
156
+ "@atlaskit/tmp-editor-statsig": "^2.15.0",
156
157
  "@atlaskit/tokens": "^2.2.0",
157
158
  "@atlaskit/tooltip": "^18.9.0",
158
159
  "@atlaskit/width-detector": "^4.3.0",
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@atlaskit/editor-common/use-plugin-state-effect",
3
+ "main": "../dist/cjs/hooks/usePluginStateEffect.js",
4
+ "module": "../dist/esm/hooks/usePluginStateEffect.js",
5
+ "module:es2019": "../dist/es2019/hooks/usePluginStateEffect.js",
6
+ "sideEffects": false,
7
+ "types": "../dist/types/hooks/usePluginStateEffect.d.ts",
8
+ "typesVersions": {
9
+ ">=4.5 <5.4": {
10
+ "*": [
11
+ "../dist/types-ts4.5/hooks/usePluginStateEffect.d.ts"
12
+ ]
13
+ }
14
+ }
15
+ }