@atlaskit/editor-common 86.0.0 → 86.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cjs/lazy-node-view/index.js +217 -0
  3. package/dist/cjs/link/ConfigureLinkOverlay/Dropdown.js +19 -4
  4. package/dist/cjs/link/ConfigureLinkOverlay/index.js +2 -1
  5. package/dist/cjs/messages/help-dialog.js +5 -0
  6. package/dist/cjs/monitoring/error.js +1 -1
  7. package/dist/cjs/ui/DropList/index.js +1 -1
  8. package/dist/cjs/ui/WidthProvider/index.js +18 -2
  9. package/dist/cjs/ui-menu/DropdownMenu/index.js +4 -4
  10. package/dist/cjs/ui-menu/ToolbarButton/index.js +7 -1
  11. package/dist/cjs/utils/index.js +7 -0
  12. package/dist/cjs/utils/page-element-counts.js +44 -0
  13. package/dist/es2019/lazy-node-view/index.js +212 -0
  14. package/dist/es2019/link/ConfigureLinkOverlay/Dropdown.js +17 -4
  15. package/dist/es2019/link/ConfigureLinkOverlay/index.js +2 -1
  16. package/dist/es2019/messages/help-dialog.js +5 -0
  17. package/dist/es2019/monitoring/error.js +1 -1
  18. package/dist/es2019/ui/DropList/index.js +1 -1
  19. package/dist/es2019/ui/WidthProvider/index.js +18 -2
  20. package/dist/es2019/ui-menu/DropdownMenu/index.js +4 -4
  21. package/dist/es2019/ui-menu/ToolbarButton/index.js +7 -1
  22. package/dist/es2019/utils/index.js +2 -1
  23. package/dist/es2019/utils/page-element-counts.js +38 -0
  24. package/dist/esm/lazy-node-view/index.js +211 -0
  25. package/dist/esm/link/ConfigureLinkOverlay/Dropdown.js +19 -4
  26. package/dist/esm/link/ConfigureLinkOverlay/index.js +2 -1
  27. package/dist/esm/messages/help-dialog.js +5 -0
  28. package/dist/esm/monitoring/error.js +1 -1
  29. package/dist/esm/ui/DropList/index.js +1 -1
  30. package/dist/esm/ui/WidthProvider/index.js +18 -2
  31. package/dist/esm/ui-menu/DropdownMenu/index.js +4 -4
  32. package/dist/esm/ui-menu/ToolbarButton/index.js +7 -1
  33. package/dist/esm/utils/index.js +2 -1
  34. package/dist/esm/utils/page-element-counts.js +38 -0
  35. package/dist/types/lazy-node-view/index.d.ts +109 -0
  36. package/dist/types/link/ConfigureLinkOverlay/Dropdown.d.ts +2 -0
  37. package/dist/types/messages/help-dialog.d.ts +5 -0
  38. package/dist/types/ui-menu/ToolbarButton/index.d.ts +3 -0
  39. package/dist/types/utils/index.d.ts +1 -0
  40. package/dist/types/utils/page-element-counts.d.ts +17 -0
  41. package/dist/types-ts4.5/lazy-node-view/index.d.ts +109 -0
  42. package/dist/types-ts4.5/link/ConfigureLinkOverlay/Dropdown.d.ts +2 -0
  43. package/dist/types-ts4.5/messages/help-dialog.d.ts +5 -0
  44. package/dist/types-ts4.5/ui-menu/ToolbarButton/index.d.ts +3 -0
  45. package/dist/types-ts4.5/utils/index.d.ts +1 -0
  46. package/dist/types-ts4.5/utils/page-element-counts.d.ts +17 -0
  47. package/lazy-node-view/package.json +15 -0
  48. package/package.json +4 -3
@@ -19,6 +19,7 @@ var SMALL_LINK_TOOLBAR_ANALYTICS_SOURCE = 'smallLinkToolbar';
19
19
  var Dropdown = function Dropdown(_ref) {
20
20
  var onConfigureClickCallback = _ref.onConfigureClick,
21
21
  onDropdownChange = _ref.onDropdownChange,
22
+ editorView = _ref.editorView,
22
23
  testId = _ref.testId;
23
24
  var _useIntl = useIntl(),
24
25
  formatMessage = _useIntl.formatMessage;
@@ -28,20 +29,34 @@ var Dropdown = function Dropdown(_ref) {
28
29
  fireActionClickEvent = _useLinkOverlayAnalyt.fireActionClickEvent,
29
30
  fireLinkClickEvent = _useLinkOverlayAnalyt.fireLinkClickEvent,
30
31
  fireToolbarViewEvent = _useLinkOverlayAnalyt.fireToolbarViewEvent;
32
+ var focusEditor = useCallback(function () {
33
+ // Fix dropdown giving focus back to the trigger async which is then unmounted and losing focus
34
+ // this is happening deep within atlaskit dropdown as a result of this code: https://github.com/focus-trap/focus-trap/blob/master/index.js#L987
35
+ // use setTimeout to run this async after that call
36
+ setTimeout(function () {
37
+ return editorView.focus();
38
+ }, 0);
39
+ }, [editorView]);
31
40
  var onOpenChange = useCallback(function (_ref2) {
32
- var isOpen = _ref2.isOpen;
41
+ var isOpen = _ref2.isOpen,
42
+ event = _ref2.event;
33
43
  onDropdownChange === null || onDropdownChange === void 0 || onDropdownChange(isOpen);
34
44
  if (isOpen) {
35
45
  fireToolbarViewEvent();
36
46
  }
37
- }, [fireToolbarViewEvent, onDropdownChange]);
47
+ if (!isOpen && event instanceof KeyboardEvent) {
48
+ focusEditor();
49
+ }
50
+ }, [fireToolbarViewEvent, focusEditor, onDropdownChange]);
38
51
  var onGoToLinkClick = useCallback(function () {
39
52
  fireActionClickEvent('goToLink');
40
- }, [fireActionClickEvent]);
53
+ focusEditor();
54
+ }, [fireActionClickEvent, focusEditor]);
41
55
  var onConfigureClick = useCallback(function () {
42
56
  fireActionClickEvent('configureLink');
43
57
  onConfigureClickCallback === null || onConfigureClickCallback === void 0 || onConfigureClickCallback();
44
- }, [fireActionClickEvent, onConfigureClickCallback]);
58
+ focusEditor();
59
+ }, [fireActionClickEvent, focusEditor, onConfigureClickCallback]);
45
60
  return jsx(DropdownMenu, {
46
61
  trigger: function trigger(_ref3) {
47
62
  var _onClick = _ref3.onClick,
@@ -87,7 +87,8 @@ export var OverlayButton = withAnalyticsContext()(function (_ref) {
87
87
  }, showDropdown ? jsx(Dropdown, {
88
88
  testId: testId,
89
89
  onConfigureClick: handleConfigureClick,
90
- onDropdownChange: onDropdownChange
90
+ onDropdownChange: onDropdownChange,
91
+ editorView: editorView
91
92
  }) : jsx(Tooltip, {
92
93
  content: configureLinkLabel,
93
94
  hideTooltipOnClick: true,
@@ -70,6 +70,11 @@ export var helpDialogMessages = defineMessages({
70
70
  defaultMessage: 'Decrease table or media size',
71
71
  description: 'The text is shown as an shortcut description in help dialog modal, when the user uses the described shortcut, he is able to decrease the width of the selected element. Optimal characters less than 21.'
72
72
  },
73
+ openCellOptions: {
74
+ id: 'fabric.editor.openCellOptions',
75
+ defaultMessage: 'Open cell options',
76
+ description: 'Keyboard shortcut to open cell options.'
77
+ },
73
78
  focusTableResizeHandle: {
74
79
  id: 'fabric.editor.focusTableResizeHandle',
75
80
  defaultMessage: 'Focus table resize handle',
@@ -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 = "86.0.0";
10
+ var packageVersion = "86.2.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
@@ -17,7 +17,7 @@ import { createAndFireEvent, withAnalyticsContext, withAnalyticsEvents } from '@
17
17
  import { N0, N50A, N60A, N900 } from '@atlaskit/theme/colors';
18
18
  import Layer from '../Layer';
19
19
  var packageName = "@atlaskit/editor-common";
20
- var packageVersion = "86.0.0";
20
+ var packageVersion = "86.2.0";
21
21
  var halfFocusRing = 1;
22
22
  var dropOffset = '0, 8';
23
23
  var DropList = /*#__PURE__*/function (_Component) {
@@ -4,6 +4,7 @@ import React, { Fragment } from 'react';
4
4
 
5
5
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
6
6
  import { css, jsx } from '@emotion/react';
7
+ import memoizeOne from 'memoize-one';
7
8
  import rafSchedule from 'raf-schd';
8
9
  import { WidthObserver } from '@atlaskit/width-detector';
9
10
  var styles = css({
@@ -32,13 +33,28 @@ export function createWidthContext() {
32
33
  export var WidthContext = /*#__PURE__*/React.createContext(createWidthContext());
33
34
  var Provider = WidthContext.Provider,
34
35
  Consumer = WidthContext.Consumer;
35
- export var WidthProvider = function WidthProvider(_ref) {
36
+ /**
37
+ * 🧱 Internal function: Editor FE Platform
38
+ *
39
+ * Returns the width of the document body.
40
+ *
41
+ * This function is memoized to avoid forcing a layout reflow multiple times.
42
+ * It uses `document.body.offsetWidth` as the source of the width, which can lead to
43
+ * a layout reflow if accessed repeatedly. To mitigate performance issues, the result
44
+ * is cached using `memoizeOne`.
45
+ *
46
+ * @returns {number} The width of the document body or 0 if the document is undefined.
47
+ */
48
+ var getBodyWidth = memoizeOne(function () {
36
49
  var _document$body$offset, _document$body;
50
+ return typeof document !== 'undefined' ? (_document$body$offset = (_document$body = document.body) === null || _document$body === void 0 ? void 0 : _document$body.offsetWidth) !== null && _document$body$offset !== void 0 ? _document$body$offset : 0 : 0;
51
+ });
52
+ export var WidthProvider = function WidthProvider(_ref) {
37
53
  var className = _ref.className,
38
54
  shouldCheckExistingValue = _ref.shouldCheckExistingValue,
39
55
  children = _ref.children;
40
56
  var existingContextValue = React.useContext(WidthContext);
41
- var _React$useState = React.useState(typeof document !== 'undefined' ? (_document$body$offset = (_document$body = document.body) === null || _document$body === void 0 ? void 0 : _document$body.offsetWidth) !== null && _document$body$offset !== void 0 ? _document$body$offset : 0 : 0),
57
+ var _React$useState = React.useState(getBodyWidth),
42
58
  _React$useState2 = _slicedToArray(_React$useState, 2),
43
59
  width = _React$useState2[0],
44
60
  setWidth = _React$useState2[1];
@@ -94,7 +94,7 @@ var DropdownMenuWrapper = /*#__PURE__*/function (_PureComponent) {
94
94
  _defineProperty(_assertThisInitialized(_this), "handleCloseAndFocus", function (event) {
95
95
  var _this$state$target;
96
96
  (_this$state$target = _this.state.target) === null || _this$state$target === void 0 || (_this$state$target = _this$state$target.querySelector('button')) === null || _this$state$target === void 0 || _this$state$target.focus();
97
- if (fg('platform.editor.a11y-table-context-menu_y4c9c')) {
97
+ if (fg('platform_editor_a11y_table_context_menu')) {
98
98
  _this.handleClose(event);
99
99
  } else {
100
100
  _this.handleClose();
@@ -103,7 +103,7 @@ var DropdownMenuWrapper = /*#__PURE__*/function (_PureComponent) {
103
103
  _defineProperty(_assertThisInitialized(_this), "handleClose", function (event) {
104
104
  var onOpenChange = _this.props.onOpenChange;
105
105
  if (onOpenChange) {
106
- if (fg('platform.editor.a11y-table-context-menu_y4c9c')) {
106
+ if (fg('platform_editor_a11y_table_context_menu')) {
107
107
  onOpenChange({
108
108
  isOpen: false,
109
109
  event: event
@@ -182,7 +182,7 @@ var DropdownMenuWrapper = /*#__PURE__*/function (_PureComponent) {
182
182
  handleClickOutside: this.handleClose,
183
183
  handleEscapeKeydown: fg('platform-editor-a11y-image-border-options-dropdown') ? handleEscapeKeydown || this.handleCloseAndFocus : this.handleCloseAndFocus,
184
184
  handleEnterKeydown: function handleEnterKeydown(e) {
185
- if (fg('platform.editor.a11y-table-context-menu_y4c9c') || fg('platform-editor-a11y-image-border-options-dropdown')) {
185
+ if (fg('platform_editor_a11y_table_context_menu') || fg('platform-editor-a11y-image-border-options-dropdown')) {
186
186
  if (!allowEnterDefaultBehavior) {
187
187
  e.preventDefault();
188
188
  }
@@ -355,7 +355,7 @@ export function DropdownMenuItem(_ref) {
355
355
  item: item
356
356
  });
357
357
  },
358
- "aria-expanded": fg('platform.editor.a11y-table-context-menu_y4c9c') ? item['aria-expanded'] : undefined
358
+ "aria-expanded": fg('platform_editor_a11y_table_context_menu') ? item['aria-expanded'] : undefined
359
359
  }, item.content));
360
360
  if (item.tooltipDescription) {
361
361
  var _item$key3;
@@ -9,8 +9,10 @@ import React, { useCallback } from 'react';
9
9
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
10
10
  import { css, jsx } from '@emotion/react';
11
11
  import { FabricChannel } from '@atlaskit/analytics-listeners';
12
+ import { fg } from '@atlaskit/platform-feature-flags';
12
13
  import Tooltip from '@atlaskit/tooltip';
13
14
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE, TOOLBAR_ACTION_SUBJECT_ID } from '../../analytics';
15
+ import { ToolTipContent } from '../../keymaps';
14
16
  import Button from './styles';
15
17
  export var TOOLBAR_BUTTON = TOOLBAR_ACTION_SUBJECT_ID;
16
18
  var buttonWrapper = css({
@@ -32,6 +34,7 @@ var ToolbarButton = /*#__PURE__*/React.forwardRef(function (props, ref) {
32
34
  children = props.children,
33
35
  hideTooltip = props.hideTooltip,
34
36
  title = props.title,
37
+ keymap = props.keymap,
35
38
  _props$titlePosition = props.titlePosition,
36
39
  titlePosition = _props$titlePosition === void 0 ? 'top' : _props$titlePosition,
37
40
  item = props.item,
@@ -92,7 +95,10 @@ var ToolbarButton = /*#__PURE__*/React.forwardRef(function (props, ref) {
92
95
  if (!title) {
93
96
  return button;
94
97
  }
95
- var tooltipContent = !hideTooltip ? title : null;
98
+ var tooltipContent = hideTooltip ? null : fg('platform_editor_a11y_table_context_menu') ? jsx(ToolTipContent, {
99
+ description: title,
100
+ keymap: keymap
101
+ }) : title;
96
102
  return jsx(Tooltip, {
97
103
  content: tooltipContent,
98
104
  hideTooltipOnClick: true,
@@ -224,4 +224,5 @@ export { transformNodeIntoListItem } from './insert-node-into-ordered-list';
224
224
  export { wrapSelectionIn } from './wrap-selection-in';
225
225
  export { toJSON, nodeToJSON } from './nodes';
226
226
  export { calculateToolbarPositionAboveSelection, calculateToolbarPositionTrackHead } from './calculate-toolbar-position';
227
- export { findNodePosByLocalIds } from './nodes-by-localIds';
227
+ export { findNodePosByLocalIds } from './nodes-by-localIds';
228
+ export { getPageElementCounts } from './page-element-counts';
@@ -0,0 +1,38 @@
1
+ import { reduce } from '@atlaskit/adf-utils/traverse';
2
+ /**
3
+ * Traverses a JSON document and counts the number of unique elements, text formatting and macros.
4
+ *
5
+ **/
6
+ export var getPageElementCounts = function getPageElementCounts(doc) {
7
+ var pageElementCounts = {
8
+ elements: {},
9
+ textFormats: {},
10
+ macros: {}
11
+ };
12
+ reduce(doc, function (acc, node) {
13
+ if (node.type === 'text') {
14
+ var _acc$elements$node$ty;
15
+ if (node.marks) {
16
+ node.marks.forEach(function (mark) {
17
+ var _acc$textFormats$mark;
18
+ var markType = mark.type;
19
+ acc.textFormats[markType] = ((_acc$textFormats$mark = acc.textFormats[markType]) !== null && _acc$textFormats$mark !== void 0 ? _acc$textFormats$mark : 0) + 1;
20
+ });
21
+ }
22
+ acc.elements[node.type] = ((_acc$elements$node$ty = acc.elements[node.type]) !== null && _acc$elements$node$ty !== void 0 ? _acc$elements$node$ty : 0) + 1;
23
+ }
24
+ // If the node is a 'macro'or extension
25
+ else if (node.type === 'extension' || node.type === 'inlineExtension' || node.type === 'bodiedExtension' || node.type === 'multiBodiedExtension' || node.type === 'extensionFrame') {
26
+ if ('attrs' in node && node.attrs && 'extensionKey' in node.attrs && node.attrs.extensionKey) {
27
+ var _acc$macros$extension;
28
+ var extensionKey = node.attrs.extensionKey;
29
+ acc.macros[extensionKey] = ((_acc$macros$extension = acc.macros[extensionKey]) !== null && _acc$macros$extension !== void 0 ? _acc$macros$extension : 0) + 1;
30
+ }
31
+ } else {
32
+ var _acc$elements$node$ty2;
33
+ acc.elements[node.type] = ((_acc$elements$node$ty2 = acc.elements[node.type]) !== null && _acc$elements$node$ty2 !== void 0 ? _acc$elements$node$ty2 : 0) + 1;
34
+ }
35
+ return acc;
36
+ }, pageElementCounts);
37
+ return pageElementCounts;
38
+ };
@@ -0,0 +1,109 @@
1
+ /// <reference types="lodash" />
2
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ import type { Decoration, DecorationSource, EditorView, NodeView } from '@atlaskit/editor-prosemirror/view';
4
+ import type { DispatchAnalyticsEvent } from '../analytics';
5
+ /**
6
+ * 📢 Public Type
7
+ *
8
+ * @see {withLazyLoading}
9
+ */
10
+ export type CreateReactNodeViewProps<NodeViewOptions> = (node: PMNode, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], getNodeViewOptions: () => NodeViewOptions) => NodeView;
11
+ /**
12
+ * 📢 Public Type
13
+ *
14
+ * @see {withLazyLoading}
15
+ */
16
+ export type LazyLoadingProps<NodeViewOptions> = {
17
+ nodeName: string;
18
+ loader: () => Promise<CreateReactNodeViewProps<NodeViewOptions>>;
19
+ getNodeViewOptions: () => NodeViewOptions;
20
+ dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
21
+ };
22
+ /**
23
+ * 🧱 Internal: Editor FE Platform
24
+ */
25
+ export type NodeViewConstructor = (node: PMNode, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
26
+ /**
27
+ * 🧱 Internal: Editor FE Platform
28
+ */
29
+ type LoadedReactNodeViews = Record<string, NodeViewConstructor>;
30
+ /**
31
+ * 🧱 Internal: Editor FE Platform
32
+ */
33
+ type CacheType = WeakMap<EditorView, LoadedReactNodeViews>;
34
+ /**
35
+ * 🧱 Internal: Editor FE Platform
36
+ *
37
+ * Debounces and replaces the node views in a ProseMirror editor with lazy-loaded node views.
38
+ *
39
+ * This function is used to update the `nodeViews` property of the `EditorView` after lazy-loaded
40
+ * node views have been loaded. It uses a debounced approach to ensure that the replacement does
41
+ * not happen too frequently, which can be performance-intensive.
42
+ *
43
+ * The function checks if there are any loaded node views in the cache associated with the given
44
+ * `EditorView`. If there are, it replaces the current `nodeViews` in the `EditorView` with the
45
+ * loaded node views. The replacement is scheduled using `requestIdleCallback` or
46
+ * `requestAnimationFrame` to avoid blocking the main thread, especially in Firefox where
47
+ * `requestIdleCallback` may not be supported.
48
+ *
49
+ * @param {WeakMap<EditorView, Record<string, NodeViewConstructor>>} cache - A WeakMap that stores
50
+ * the loaded node views for each `EditorView`. The key is the `EditorView`, and the value
51
+ * is a record of node type names to their corresponding `NodeViewConstructor`.
52
+ * @param {EditorView} view - The ProseMirror `EditorView` instance whose `nodeViews` property
53
+ * needs to be updated.
54
+ *
55
+ * @example
56
+ * const cache = new WeakMap();
57
+ * const view = new EditorView(...);
58
+ *
59
+ * // Assume some node views have been loaded and stored in the cache
60
+ * cache.set(view, {
61
+ * 'table': TableViewConstructor,
62
+ * 'tableCell': TableCellViewConstructor,
63
+ * });
64
+ *
65
+ * debouncedReplaceNodeviews(cache, view);
66
+ */
67
+ export declare const debouncedReplaceNodeviews: import("lodash").DebouncedFunc<(cache: CacheType, view: EditorView) => void>;
68
+ /**
69
+ * 📢 Public: Any EditorPlugin can use this function
70
+ *
71
+ * Wraps a NodeView constructor with laziness, allowing the NodeView to be loaded only when required.
72
+ *
73
+ * This higher-order function is designed to optimize the loading and rendering performance
74
+ * of ProseMirror editor nodes by deferring the loading of their associated NodeViews until they are actually needed.
75
+ * This is particularly useful for complex or heavy NodeViews, such as tables, table cells, rows, and headers within
76
+ * the ProseMirror editor. By using dynamic imports (with promises), the initial load time of the editor can be significantly
77
+ * reduced, leading to a smoother and faster user experience.
78
+ *
79
+ * The function accepts configuration parameters including the node name, a loader function that dynamically imports
80
+ * the NodeView, and a function to retrieve NodeView options. It returns a NodeViewConstructor that ProseMirror
81
+ * can use when rendering nodes of the specified type.
82
+ *
83
+ * @template NodeViewOptions - The type parameter that describes the shape of the options object for the NodeView.
84
+ * @param {LazyLoadingProps<NodeViewOptions>} params - Configuration parameters for lazy loading.
85
+ * @param {string} params.nodeName - The name of the node (e.g., 'table', 'tableCell', 'tableHeader', 'tableRow') for which the lazy-loaded NodeView is intended.
86
+ * @param {() => Promise<CreateReactNodeViewProps<NodeViewOptions>>} params.loader - A function that, when called, returns a promise that resolves to the actual NodeView constructor. This function typically uses dynamic `import()` to load the NodeView code.
87
+ * @param {() => NodeViewOptions} params.getNodeViewOptions - A function that returns the options to be passed to the NodeView constructor. These options can include dependencies like `portalProviderAPI`, `eventDispatcher`, and others, which are necessary for the NodeView's operation.
88
+ * @param {DispatchAnalyticsEvent} [params.dispatchAnalyticsEvent] - An optional function for dispatching analytics events, which can be used to monitor the performance and usage of the lazy-loaded NodeViews.
89
+ * @returns {NodeViewConstructor} A constructor function for creating a NodeView that ProseMirror can instantiate when it encounters a node of the specified type. This constructor is a lightweight placeholder until the actual NodeView is loaded.
90
+ *
91
+ * @example
92
+ * // Lazy load a table NodeView with specific options
93
+ * const lazyTableView = withLazyLoading({
94
+ * nodeName: 'table',
95
+ * loader: () => import('./table').then(module => module.createTableView),
96
+ * getNodeViewOptions: () => ({
97
+ * portalProviderAPI,
98
+ * eventDispatcher,
99
+ * getEditorContainerWidth,
100
+ * getEditorFeatureFlags,
101
+ * dispatchAnalyticsEvent,
102
+ * pluginInjectionApi,
103
+ * }),
104
+ * });
105
+ *
106
+ * // Then, use `lazyTableView` in ProseMirror editor setup to enhance 'table' nodes with lazy loading
107
+ */
108
+ export declare const withLazyLoading: <Options>({ nodeName, loader, getNodeViewOptions, dispatchAnalyticsEvent, }: LazyLoadingProps<Options>) => NodeViewConstructor;
109
+ export {};
@@ -1,10 +1,12 @@
1
1
  /// <reference types="react" />
2
+ import { type EditorView } from '@atlaskit/editor-prosemirror/view';
2
3
  export type OnDropdownChange = (isOpen: boolean) => void;
3
4
  export type DropdownProps = {
4
5
  /** Callback fired when the Configure dropdown item is clicked */
5
6
  onConfigureClick: () => void;
6
7
  /** Callback fired when the dropdown is open or close */
7
8
  onDropdownChange?: OnDropdownChange;
9
+ editorView: EditorView;
8
10
  testId: string;
9
11
  };
10
12
  declare const _default: import("react").ForwardRefExoticComponent<DropdownProps & import("@atlaskit/analytics-next").WithContextProps & import("react").RefAttributes<any>>;
@@ -69,6 +69,11 @@ export declare const helpDialogMessages: {
69
69
  defaultMessage: string;
70
70
  description: string;
71
71
  };
72
+ openCellOptions: {
73
+ id: string;
74
+ defaultMessage: string;
75
+ description: string;
76
+ };
72
77
  focusTableResizeHandle: {
73
78
  id: string;
74
79
  defaultMessage: string;
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import type { ButtonProps } from '@atlaskit/button/types';
4
4
  import type { PositionType } from '@atlaskit/tooltip';
5
5
  import { TOOLBAR_ACTION_SUBJECT_ID } from '../../analytics';
6
+ import { type Keymap } from '../../keymaps';
6
7
  import type { MenuItem } from '../DropdownMenu';
7
8
  export declare const TOOLBAR_BUTTON: typeof TOOLBAR_ACTION_SUBJECT_ID;
8
9
  export type Props = {
@@ -23,6 +24,7 @@ export type Props = {
23
24
  titlePosition?: PositionType;
24
25
  item?: MenuItem;
25
26
  testId?: string;
27
+ keymap?: Keymap;
26
28
  'aria-label'?: React.AriaAttributes['aria-label'];
27
29
  'aria-expanded'?: React.AriaAttributes['aria-expanded'];
28
30
  'aria-haspopup'?: React.AriaAttributes['aria-haspopup'];
@@ -48,6 +50,7 @@ declare const ToolbarButton: React.ForwardRefExoticComponent<{
48
50
  titlePosition?: PositionType | undefined;
49
51
  item?: MenuItem | undefined;
50
52
  testId?: string | undefined;
53
+ keymap?: Keymap | undefined;
51
54
  'aria-label'?: React.AriaAttributes['aria-label'];
52
55
  'aria-expanded'?: React.AriaAttributes['aria-expanded'];
53
56
  'aria-haspopup'?: React.AriaAttributes['aria-haspopup'];
@@ -91,3 +91,4 @@ export { wrapSelectionIn } from './wrap-selection-in';
91
91
  export { toJSON, nodeToJSON } from './nodes';
92
92
  export { calculateToolbarPositionAboveSelection, calculateToolbarPositionTrackHead, } from './calculate-toolbar-position';
93
93
  export { findNodePosByLocalIds } from './nodes-by-localIds';
94
+ export { getPageElementCounts } from './page-element-counts';
@@ -0,0 +1,17 @@
1
+ import type { JSONDocNode } from '@atlaskit/editor-json-transformer';
2
+ export type PageElementCounts = {
3
+ elements: {
4
+ [key: string]: number;
5
+ };
6
+ textFormats: {
7
+ [key: string]: number;
8
+ };
9
+ macros: {
10
+ [key: string]: number;
11
+ };
12
+ };
13
+ /**
14
+ * Traverses a JSON document and counts the number of unique elements, text formatting and macros.
15
+ *
16
+ **/
17
+ export declare const getPageElementCounts: (doc: JSONDocNode) => PageElementCounts;
@@ -0,0 +1,109 @@
1
+ /// <reference types="lodash" />
2
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ import type { Decoration, DecorationSource, EditorView, NodeView } from '@atlaskit/editor-prosemirror/view';
4
+ import type { DispatchAnalyticsEvent } from '../analytics';
5
+ /**
6
+ * 📢 Public Type
7
+ *
8
+ * @see {withLazyLoading}
9
+ */
10
+ export type CreateReactNodeViewProps<NodeViewOptions> = (node: PMNode, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], getNodeViewOptions: () => NodeViewOptions) => NodeView;
11
+ /**
12
+ * 📢 Public Type
13
+ *
14
+ * @see {withLazyLoading}
15
+ */
16
+ export type LazyLoadingProps<NodeViewOptions> = {
17
+ nodeName: string;
18
+ loader: () => Promise<CreateReactNodeViewProps<NodeViewOptions>>;
19
+ getNodeViewOptions: () => NodeViewOptions;
20
+ dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
21
+ };
22
+ /**
23
+ * 🧱 Internal: Editor FE Platform
24
+ */
25
+ export type NodeViewConstructor = (node: PMNode, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
26
+ /**
27
+ * 🧱 Internal: Editor FE Platform
28
+ */
29
+ type LoadedReactNodeViews = Record<string, NodeViewConstructor>;
30
+ /**
31
+ * 🧱 Internal: Editor FE Platform
32
+ */
33
+ type CacheType = WeakMap<EditorView, LoadedReactNodeViews>;
34
+ /**
35
+ * 🧱 Internal: Editor FE Platform
36
+ *
37
+ * Debounces and replaces the node views in a ProseMirror editor with lazy-loaded node views.
38
+ *
39
+ * This function is used to update the `nodeViews` property of the `EditorView` after lazy-loaded
40
+ * node views have been loaded. It uses a debounced approach to ensure that the replacement does
41
+ * not happen too frequently, which can be performance-intensive.
42
+ *
43
+ * The function checks if there are any loaded node views in the cache associated with the given
44
+ * `EditorView`. If there are, it replaces the current `nodeViews` in the `EditorView` with the
45
+ * loaded node views. The replacement is scheduled using `requestIdleCallback` or
46
+ * `requestAnimationFrame` to avoid blocking the main thread, especially in Firefox where
47
+ * `requestIdleCallback` may not be supported.
48
+ *
49
+ * @param {WeakMap<EditorView, Record<string, NodeViewConstructor>>} cache - A WeakMap that stores
50
+ * the loaded node views for each `EditorView`. The key is the `EditorView`, and the value
51
+ * is a record of node type names to their corresponding `NodeViewConstructor`.
52
+ * @param {EditorView} view - The ProseMirror `EditorView` instance whose `nodeViews` property
53
+ * needs to be updated.
54
+ *
55
+ * @example
56
+ * const cache = new WeakMap();
57
+ * const view = new EditorView(...);
58
+ *
59
+ * // Assume some node views have been loaded and stored in the cache
60
+ * cache.set(view, {
61
+ * 'table': TableViewConstructor,
62
+ * 'tableCell': TableCellViewConstructor,
63
+ * });
64
+ *
65
+ * debouncedReplaceNodeviews(cache, view);
66
+ */
67
+ export declare const debouncedReplaceNodeviews: import("lodash").DebouncedFunc<(cache: CacheType, view: EditorView) => void>;
68
+ /**
69
+ * 📢 Public: Any EditorPlugin can use this function
70
+ *
71
+ * Wraps a NodeView constructor with laziness, allowing the NodeView to be loaded only when required.
72
+ *
73
+ * This higher-order function is designed to optimize the loading and rendering performance
74
+ * of ProseMirror editor nodes by deferring the loading of their associated NodeViews until they are actually needed.
75
+ * This is particularly useful for complex or heavy NodeViews, such as tables, table cells, rows, and headers within
76
+ * the ProseMirror editor. By using dynamic imports (with promises), the initial load time of the editor can be significantly
77
+ * reduced, leading to a smoother and faster user experience.
78
+ *
79
+ * The function accepts configuration parameters including the node name, a loader function that dynamically imports
80
+ * the NodeView, and a function to retrieve NodeView options. It returns a NodeViewConstructor that ProseMirror
81
+ * can use when rendering nodes of the specified type.
82
+ *
83
+ * @template NodeViewOptions - The type parameter that describes the shape of the options object for the NodeView.
84
+ * @param {LazyLoadingProps<NodeViewOptions>} params - Configuration parameters for lazy loading.
85
+ * @param {string} params.nodeName - The name of the node (e.g., 'table', 'tableCell', 'tableHeader', 'tableRow') for which the lazy-loaded NodeView is intended.
86
+ * @param {() => Promise<CreateReactNodeViewProps<NodeViewOptions>>} params.loader - A function that, when called, returns a promise that resolves to the actual NodeView constructor. This function typically uses dynamic `import()` to load the NodeView code.
87
+ * @param {() => NodeViewOptions} params.getNodeViewOptions - A function that returns the options to be passed to the NodeView constructor. These options can include dependencies like `portalProviderAPI`, `eventDispatcher`, and others, which are necessary for the NodeView's operation.
88
+ * @param {DispatchAnalyticsEvent} [params.dispatchAnalyticsEvent] - An optional function for dispatching analytics events, which can be used to monitor the performance and usage of the lazy-loaded NodeViews.
89
+ * @returns {NodeViewConstructor} A constructor function for creating a NodeView that ProseMirror can instantiate when it encounters a node of the specified type. This constructor is a lightweight placeholder until the actual NodeView is loaded.
90
+ *
91
+ * @example
92
+ * // Lazy load a table NodeView with specific options
93
+ * const lazyTableView = withLazyLoading({
94
+ * nodeName: 'table',
95
+ * loader: () => import('./table').then(module => module.createTableView),
96
+ * getNodeViewOptions: () => ({
97
+ * portalProviderAPI,
98
+ * eventDispatcher,
99
+ * getEditorContainerWidth,
100
+ * getEditorFeatureFlags,
101
+ * dispatchAnalyticsEvent,
102
+ * pluginInjectionApi,
103
+ * }),
104
+ * });
105
+ *
106
+ * // Then, use `lazyTableView` in ProseMirror editor setup to enhance 'table' nodes with lazy loading
107
+ */
108
+ export declare const withLazyLoading: <Options>({ nodeName, loader, getNodeViewOptions, dispatchAnalyticsEvent, }: LazyLoadingProps<Options>) => NodeViewConstructor;
109
+ export {};
@@ -1,10 +1,12 @@
1
1
  /// <reference types="react" />
2
+ import { type EditorView } from '@atlaskit/editor-prosemirror/view';
2
3
  export type OnDropdownChange = (isOpen: boolean) => void;
3
4
  export type DropdownProps = {
4
5
  /** Callback fired when the Configure dropdown item is clicked */
5
6
  onConfigureClick: () => void;
6
7
  /** Callback fired when the dropdown is open or close */
7
8
  onDropdownChange?: OnDropdownChange;
9
+ editorView: EditorView;
8
10
  testId: string;
9
11
  };
10
12
  declare const _default: import("react").ForwardRefExoticComponent<DropdownProps & import("@atlaskit/analytics-next").WithContextProps & import("react").RefAttributes<any>>;
@@ -69,6 +69,11 @@ export declare const helpDialogMessages: {
69
69
  defaultMessage: string;
70
70
  description: string;
71
71
  };
72
+ openCellOptions: {
73
+ id: string;
74
+ defaultMessage: string;
75
+ description: string;
76
+ };
72
77
  focusTableResizeHandle: {
73
78
  id: string;
74
79
  defaultMessage: string;
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import type { ButtonProps } from '@atlaskit/button/types';
4
4
  import type { PositionType } from '@atlaskit/tooltip';
5
5
  import { TOOLBAR_ACTION_SUBJECT_ID } from '../../analytics';
6
+ import { type Keymap } from '../../keymaps';
6
7
  import type { MenuItem } from '../DropdownMenu';
7
8
  export declare const TOOLBAR_BUTTON: typeof TOOLBAR_ACTION_SUBJECT_ID;
8
9
  export type Props = {
@@ -23,6 +24,7 @@ export type Props = {
23
24
  titlePosition?: PositionType;
24
25
  item?: MenuItem;
25
26
  testId?: string;
27
+ keymap?: Keymap;
26
28
  'aria-label'?: React.AriaAttributes['aria-label'];
27
29
  'aria-expanded'?: React.AriaAttributes['aria-expanded'];
28
30
  'aria-haspopup'?: React.AriaAttributes['aria-haspopup'];
@@ -48,6 +50,7 @@ declare const ToolbarButton: React.ForwardRefExoticComponent<{
48
50
  titlePosition?: PositionType | undefined;
49
51
  item?: MenuItem | undefined;
50
52
  testId?: string | undefined;
53
+ keymap?: Keymap | undefined;
51
54
  'aria-label'?: React.AriaAttributes['aria-label'];
52
55
  'aria-expanded'?: React.AriaAttributes['aria-expanded'];
53
56
  'aria-haspopup'?: React.AriaAttributes['aria-haspopup'];
@@ -91,3 +91,4 @@ export { wrapSelectionIn } from './wrap-selection-in';
91
91
  export { toJSON, nodeToJSON } from './nodes';
92
92
  export { calculateToolbarPositionAboveSelection, calculateToolbarPositionTrackHead, } from './calculate-toolbar-position';
93
93
  export { findNodePosByLocalIds } from './nodes-by-localIds';
94
+ export { getPageElementCounts } from './page-element-counts';
@@ -0,0 +1,17 @@
1
+ import type { JSONDocNode } from '@atlaskit/editor-json-transformer';
2
+ export type PageElementCounts = {
3
+ elements: {
4
+ [key: string]: number;
5
+ };
6
+ textFormats: {
7
+ [key: string]: number;
8
+ };
9
+ macros: {
10
+ [key: string]: number;
11
+ };
12
+ };
13
+ /**
14
+ * Traverses a JSON document and counts the number of unique elements, text formatting and macros.
15
+ *
16
+ **/
17
+ export declare const getPageElementCounts: (doc: JSONDocNode) => PageElementCounts;
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@atlaskit/editor-common/lazy-node-view",
3
+ "main": "../dist/cjs/lazy-node-view/index.js",
4
+ "module": "../dist/esm/lazy-node-view/index.js",
5
+ "module:es2019": "../dist/es2019/lazy-node-view/index.js",
6
+ "sideEffects": false,
7
+ "types": "../dist/types/lazy-node-view/index.d.ts",
8
+ "typesVersions": {
9
+ ">=4.5 <5.4": {
10
+ "*": [
11
+ "../dist/types-ts4.5/lazy-node-view/index.d.ts"
12
+ ]
13
+ }
14
+ }
15
+ }