@atlaskit/editor-common 78.34.0 → 78.36.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 (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/monitoring/error.js +1 -1
  3. package/dist/cjs/portal/PortalBucket.js +44 -0
  4. package/dist/cjs/portal/PortalManager.js +209 -0
  5. package/dist/cjs/portal/index.js +26 -0
  6. package/dist/cjs/portal/usePortalProvider.js +80 -0
  7. package/dist/cjs/table/SortingIcon.js +144 -0
  8. package/dist/cjs/table/consts.js +7 -0
  9. package/dist/cjs/table/index.js +28 -0
  10. package/dist/cjs/table/messages.js +56 -0
  11. package/dist/cjs/ui/DropList/index.js +1 -1
  12. package/dist/es2019/monitoring/error.js +1 -1
  13. package/dist/es2019/portal/PortalBucket.js +29 -0
  14. package/dist/es2019/portal/PortalManager.js +157 -0
  15. package/dist/es2019/portal/index.js +3 -0
  16. package/dist/es2019/portal/usePortalProvider.js +58 -0
  17. package/dist/es2019/table/SortingIcon.js +183 -0
  18. package/dist/es2019/table/consts.js +1 -0
  19. package/dist/es2019/table/index.js +2 -0
  20. package/dist/es2019/table/messages.js +50 -0
  21. package/dist/es2019/ui/DropList/index.js +1 -1
  22. package/dist/esm/monitoring/error.js +1 -1
  23. package/dist/esm/portal/PortalBucket.js +34 -0
  24. package/dist/esm/portal/PortalManager.js +202 -0
  25. package/dist/esm/portal/index.js +3 -0
  26. package/dist/esm/portal/usePortalProvider.js +70 -0
  27. package/dist/esm/table/SortingIcon.js +139 -0
  28. package/dist/esm/table/consts.js +1 -0
  29. package/dist/esm/table/index.js +2 -0
  30. package/dist/esm/table/messages.js +50 -0
  31. package/dist/esm/ui/DropList/index.js +1 -1
  32. package/dist/types/portal/PortalBucket.d.ts +18 -0
  33. package/dist/types/portal/PortalManager.d.ts +53 -0
  34. package/dist/types/portal/index.d.ts +3 -0
  35. package/dist/types/portal/usePortalProvider.d.ts +18 -0
  36. package/dist/types/table/SortingIcon.d.ts +23 -0
  37. package/dist/types/table/consts.d.ts +1 -0
  38. package/dist/types/table/index.d.ts +2 -0
  39. package/dist/types/table/messages.d.ts +49 -0
  40. package/dist/types-ts4.5/portal/PortalBucket.d.ts +18 -0
  41. package/dist/types-ts4.5/portal/PortalManager.d.ts +53 -0
  42. package/dist/types-ts4.5/portal/index.d.ts +3 -0
  43. package/dist/types-ts4.5/portal/usePortalProvider.d.ts +21 -0
  44. package/dist/types-ts4.5/table/SortingIcon.d.ts +23 -0
  45. package/dist/types-ts4.5/table/consts.d.ts +1 -0
  46. package/dist/types-ts4.5/table/index.d.ts +2 -0
  47. package/dist/types-ts4.5/table/messages.d.ts +49 -0
  48. package/package.json +4 -2
  49. package/portal/package.json +15 -0
  50. package/table/package.json +15 -0
@@ -0,0 +1,29 @@
1
+ import React, { useLayoutEffect, useMemo, useState } from 'react';
2
+ /**
3
+ * A component for rendering portals managed by a `PortalManager`.
4
+ * It subscribes to a `PortalManager` instance to listen for changes in the portal content
5
+ * and renders the content of its assigned portal bucket.
6
+ *
7
+ * @param {PortalBucketProps} props The component props.
8
+ * @param {number} props.id The ID for the portal bucket. This ID is used by the `PortalManager` to manage the content of this bucket.
9
+ * @param {PortalManager} props.portalManager An instance of `PortalManager` which manages the registration and unregistration of portal buckets and their content.
10
+ * @returns {React.ReactElement} The React element(s) that are currently registered to this portal bucket.
11
+ */
12
+ export function PortalBucket({
13
+ id,
14
+ portalManager
15
+ }) {
16
+ // State to hold the current portals for this bucket
17
+ const [portals, setPortals] = useState({});
18
+ // Effect to register/unregister this bucket with the portal manager on mount/unmount
19
+ useLayoutEffect(() => {
20
+ portalManager.registerBucket(id, setPortals);
21
+ return () => {
22
+ portalManager.unregisterBucket(id);
23
+ };
24
+ }, [id, portalManager]);
25
+ // Memoize the portal elements to avoid unnecessary re-renders
26
+ const portalElements = useMemo(() => Object.values(portals), [portals]);
27
+ // Render the current portal elements
28
+ return /*#__PURE__*/React.createElement(React.Fragment, null, portalElements);
29
+ }
@@ -0,0 +1,157 @@
1
+ const DEFAULT_INITIAL_BUCKETS = 50;
2
+ const DEFAULT_MAX_BUCKET_CAPACITY = 50;
3
+ const DEFAULT_SCALE_RATIO = 0.5;
4
+
5
+ /**
6
+ * Creates an empty bucket object with a specified capacity. Each bucket is designed
7
+ * to hold a certain number of React portals and has an associated updater function
8
+ * which can be null initially.
9
+ *
10
+ * @function createEmptyBucket
11
+ * @param {number} capacity - The maximum capacity of the bucket.
12
+ * @returns {PortalBucketType} An object representing an empty bucket with the specified capacity.
13
+ */
14
+ function createEmptyBucket(capacity) {
15
+ return {
16
+ portals: {},
17
+ capacity,
18
+ updater: null
19
+ };
20
+ }
21
+
22
+ /**
23
+ * A utility class to manage and dynamically scale React portals across multiple buckets.
24
+ * It allows for efficient rendering of large numbers of React portals by distributing them
25
+ * across "buckets" and updating these buckets as necessary to balance load and performance.
26
+ *
27
+ * @class PortalManager
28
+ * @typedef {Object} PortalManager
29
+ *
30
+ * @property {number} maxBucketCapacity - The maximum capacity of each bucket before a new bucket is created.
31
+ * @property {number} scaleRatio - The ratio to determine the number of new buckets to add when scaling up.
32
+ * @property {Array<PortalBucketType>} buckets - An array of bucket objects where each bucket holds a record of React portals.
33
+ * @property {Set<number>} availableBuckets - A set of indices representing buckets that have available capacity.
34
+ * @property {Map<React.Key, number>} portalToBucketMap - A map of React portal keys to their corresponding bucket indices.
35
+ * @property {PortalRendererUpdater|null} portalRendererUpdater - A function to trigger updates to the rendering of portals.
36
+ * @property {number} scaleCapacityThreshold - The threshold at which the buckets are scaled up to accommodate more portals.
37
+ *
38
+ * @param {number} [initialBuckets=DEFAULT_INITIAL_BUCKETS] - The initial number of buckets to create.
39
+ * @param {number} [maxBucketCapacity=DEFAULT_MAX_BUCKET_CAPACITY] - The maximum number of portals a single bucket can hold.
40
+ * @param {number} [scaleRatio=DEFAULT_SCALE_RATIO] - The ratio used to calculate the number of new buckets to add when scaling.
41
+ */
42
+ export class PortalManager {
43
+ constructor(initialBuckets = DEFAULT_INITIAL_BUCKETS, maxBucketCapacity = DEFAULT_MAX_BUCKET_CAPACITY, scaleRatio = DEFAULT_SCALE_RATIO) {
44
+ this.maxBucketCapacity = maxBucketCapacity;
45
+ this.scaleRatio = scaleRatio;
46
+
47
+ // Initialise buckets array by creating an array of length `initialBuckets` containing empty buckets
48
+ this.buckets = Array.from({
49
+ length: initialBuckets
50
+ }, () => createEmptyBucket(maxBucketCapacity));
51
+ this.portalToBucketMap = new Map();
52
+ this.availableBuckets = new Set(Array.from({
53
+ length: initialBuckets
54
+ }, (_, i) => i));
55
+ this.portalRendererUpdater = null;
56
+ this.scaleCapacityThreshold = maxBucketCapacity / 2;
57
+ }
58
+ getCurrentBucket() {
59
+ return this.availableBuckets.values().next().value;
60
+ }
61
+ createBucket() {
62
+ var _this$portalRendererU;
63
+ const currentBucket = this.getCurrentBucket();
64
+
65
+ //If the current bucket has capacity, skip this logic
66
+ if (this.buckets[currentBucket].capacity > 0) {
67
+ return;
68
+ } else {
69
+ // The current bucket is full, delete the bucket from the list of available buckets
70
+ this.availableBuckets.delete(currentBucket);
71
+ }
72
+
73
+ // Skip creating new bucket if there are buckets still available
74
+ if (this.availableBuckets.size > 0) {
75
+ return;
76
+ }
77
+
78
+ // Scale the buckets up only if there are no available buckets left
79
+ // Calculate how many new buckets need to be added
80
+ const numBucketsToAdd = Math.floor(this.buckets.length * this.scaleRatio);
81
+ this.buckets = [...this.buckets];
82
+ for (let i = 0; i < numBucketsToAdd; i++) {
83
+ this.buckets.push(createEmptyBucket(this.maxBucketCapacity));
84
+ this.availableBuckets.add(this.buckets.length - 1);
85
+ }
86
+ (_this$portalRendererU = this.portalRendererUpdater) === null || _this$portalRendererU === void 0 ? void 0 : _this$portalRendererU.call(this, this.buckets);
87
+ }
88
+ getBuckets() {
89
+ return this.buckets;
90
+ }
91
+ registerBucket(id, updater) {
92
+ var _this$buckets$id$upda, _this$buckets$id;
93
+ this.buckets[id].updater = updater;
94
+ (_this$buckets$id$upda = (_this$buckets$id = this.buckets[id]).updater) === null || _this$buckets$id$upda === void 0 ? void 0 : _this$buckets$id$upda.call(_this$buckets$id, () => ({
95
+ ...this.buckets[id].portals
96
+ }));
97
+ }
98
+ unregisterBucket(id) {
99
+ this.buckets[id].updater = null;
100
+ }
101
+ updateBuckets(id) {
102
+ var _this$buckets$id$upda2, _this$buckets$id2;
103
+ (_this$buckets$id$upda2 = (_this$buckets$id2 = this.buckets[id]).updater) === null || _this$buckets$id$upda2 === void 0 ? void 0 : _this$buckets$id$upda2.call(_this$buckets$id2, () => {
104
+ // new object is required to trigger react updates
105
+ return {
106
+ ...this.buckets[id].portals
107
+ };
108
+ });
109
+ }
110
+ registerPortal(key, portal) {
111
+ var _this$portalToBucketM;
112
+ this.createBucket();
113
+ this.buckets[this.getCurrentBucket()].capacity -= 1;
114
+ const id = (_this$portalToBucketM = this.portalToBucketMap.get(key)) !== null && _this$portalToBucketM !== void 0 ? _this$portalToBucketM : this.getCurrentBucket();
115
+ this.portalToBucketMap.set(key, id);
116
+ if (this.buckets[id].portals[key] !== portal) {
117
+ this.buckets[id].portals[key] = portal;
118
+ this.updateBuckets(id);
119
+ }
120
+
121
+ //returns a function to unregister the portal
122
+ return () => {
123
+ delete this.buckets[id].portals[key];
124
+ this.portalToBucketMap.delete(key);
125
+ this.buckets[id].capacity += 1;
126
+ if (this.buckets[id].capacity > this.scaleCapacityThreshold) {
127
+ this.availableBuckets.add(id);
128
+ }
129
+ this.updateBuckets(id);
130
+ };
131
+ }
132
+ registerPortalRenderer(updater) {
133
+ if (!this.portalRendererUpdater) {
134
+ updater(() => this.buckets);
135
+ }
136
+ this.portalRendererUpdater = updater;
137
+ }
138
+ unregisterPortalRenderer() {
139
+ this.portalRendererUpdater = null;
140
+ }
141
+
142
+ /**
143
+ * Cleans up resources used by the PortalManager. This includes clearing all portals,
144
+ * unregistering all buckets, and resetting internal state.
145
+ */
146
+ destroy() {
147
+ // Iterate through each bucket and clear its portals and unset the updater function
148
+ this.buckets.forEach((bucket, id) => {
149
+ bucket.portals = {}; // Clearing all portals from the bucket
150
+ bucket.updater = null; // Unsetting the bucket's updater function
151
+ this.availableBuckets.add(id); // Mark all buckets as available
152
+ });
153
+ this.portalToBucketMap.clear();
154
+ this.portalRendererUpdater = null;
155
+ this.availableBuckets = new Set(this.buckets.map((_, index) => index));
156
+ }
157
+ }
@@ -0,0 +1,3 @@
1
+ export { PortalManager } from './PortalManager';
2
+ export { PortalBucket } from './PortalBucket';
3
+ export { usePortalProvider } from './usePortalProvider';
@@ -0,0 +1,58 @@
1
+ import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { PortalBucket } from './PortalBucket';
4
+ import { PortalManager } from './PortalManager';
5
+ function createPortalRendererComponent(portalManager) {
6
+ return function PortalRenderer() {
7
+ const [buckets, setBuckets] = useState(portalManager.getBuckets());
8
+ useLayoutEffect(() => {
9
+ portalManager.registerPortalRenderer(setBuckets);
10
+ return () => {
11
+ portalManager.unregisterPortalRenderer();
12
+ };
13
+ }, []);
14
+ const portalsElements = useMemo(() => buckets.map((_, i) => /*#__PURE__*/React.createElement(PortalBucket, {
15
+ key: i,
16
+ id: i,
17
+ portalManager: portalManager
18
+ })), [buckets]);
19
+ return /*#__PURE__*/React.createElement(React.Fragment, null, portalsElements);
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Initializes PortalManager and creates PortalRendererComponent. Offers an API (portalProviderAPI) for managing portals.
25
+ * @returns {[PortalProviderAPI, PortalRendererComponent]} An array containing two elements:
26
+ * 1. portalProviderAPI: An object providing an API for rendering and removing portals.
27
+ * 2. PortalRenderer: A React component responsible for rendering the portal content.
28
+ */
29
+ export function usePortalProvider() {
30
+ const portalManager = useMemo(() => new PortalManager(), []);
31
+ const PortalRenderer = useMemo(() => createPortalRendererComponent(portalManager), [portalManager]);
32
+ const portalProviderAPI = useMemo(() => {
33
+ const portalsMap = new Map();
34
+ return {
35
+ render: (key, children, container) => {
36
+ const portal = /*#__PURE__*/createPortal(children(), container, key);
37
+ portalsMap.set(key, portalManager.registerPortal(key, portal));
38
+ },
39
+ remove: key => {
40
+ var _portalsMap$get;
41
+ (_portalsMap$get = portalsMap.get(key)) === null || _portalsMap$get === void 0 ? void 0 : _portalsMap$get();
42
+ portalsMap.delete(key);
43
+ },
44
+ destroy: () => {
45
+ portalsMap.clear();
46
+ portalManager.destroy();
47
+ }
48
+ };
49
+ }, [portalManager]);
50
+
51
+ // Cleanup on unmount
52
+ useEffect(() => {
53
+ return () => {
54
+ portalProviderAPI.destroy();
55
+ };
56
+ }, [portalManager, portalProviderAPI]);
57
+ return [portalProviderAPI, PortalRenderer];
58
+ }
@@ -0,0 +1,183 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/react';
3
+ import { injectIntl } from 'react-intl-next';
4
+ import { N20, N30 } from '@atlaskit/theme/colors';
5
+ import Tooltip from '@atlaskit/tooltip';
6
+ import { SortOrder } from '../types';
7
+ import { SORTABLE_COLUMN_ICON_CLASSNAME } from './consts';
8
+ import { sortingAriaLabelMessages, sortingIconMessages } from './messages';
9
+ export let StatusClassNames = /*#__PURE__*/function (StatusClassNames) {
10
+ StatusClassNames["ASC"] = "sorting-icon-svg__asc";
11
+ StatusClassNames["DESC"] = "sorting-icon-svg__desc";
12
+ StatusClassNames["NO_ORDER"] = "sorting-icon-svg__no_order";
13
+ StatusClassNames["SORTING_NOT_ALLOWED"] = "sorting-icon-svg__not-allowed";
14
+ return StatusClassNames;
15
+ }({});
16
+
17
+ // eslint-disable-next-line @atlaskit/design-system/no-css-tagged-template-expression -- needs manual remediation
18
+ const buttonStyles = css`
19
+ position: absolute;
20
+ display: flex;
21
+ height: 28px;
22
+ width: 28px;
23
+ margin: ${"var(--ds-space-075, 6px)"};
24
+ right: 0;
25
+ top: 0;
26
+ border: 2px solid ${"var(--ds-border, #fff)"};
27
+ border-radius: ${"var(--ds-border-radius-100, 4px)"};
28
+ background-color: ${`var(--ds-surface-overlay, ${N20})`};
29
+ justify-content: center;
30
+ align-items: center;
31
+ cursor: pointer;
32
+
33
+ &:hover {
34
+ background-color: ${`var(--ds-surface-overlay-hovered, ${N30})`};
35
+ }
36
+
37
+ &:active {
38
+ background-color: ${"var(--ds-surface-overlay-pressed, rgba(179, 212, 255, 0.6))"};
39
+ }
40
+
41
+ &.${SORTABLE_COLUMN_ICON_CLASSNAME}__not-allowed {
42
+ cursor: not-allowed;
43
+ }
44
+ `;
45
+
46
+ // eslint-disable-next-line @atlaskit/design-system/no-css-tagged-template-expression -- needs manual remediation
47
+ const iconWrapperStyles = css`
48
+ width: 8px;
49
+ height: 12px;
50
+ transition: transform 0.3s cubic-bezier(0.15, 1, 0.3, 1);
51
+ transform-origin: 50% 50%;
52
+ display: flex;
53
+ justify-content: center;
54
+
55
+ &.${StatusClassNames.DESC} {
56
+ transform: rotate(-180deg);
57
+ }
58
+
59
+ &.${SORTABLE_COLUMN_ICON_CLASSNAME}-inactive {
60
+ opacity: 0.7;
61
+ }
62
+ `;
63
+
64
+ // The icon is created with CSS due to the following Firefox issue: https://product-fabric.atlassian.net/browse/ED-8001
65
+ // The TL;DR is that svg's in tables mess up how HTML is copied in Firefox. Using a styled div instead solves the problem.
66
+ // For this reason, svg's should be avoided in tables until this issue is fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=1664350
67
+ const iconStyles = css({
68
+ height: '100%',
69
+ width: '2px',
70
+ borderRadius: '50px',
71
+ background: "var(--ds-icon, #42526E)",
72
+ '&::before, &::after': {
73
+ background: "var(--ds-icon, #42526E)",
74
+ content: "''",
75
+ height: '2px',
76
+ width: '6px',
77
+ position: 'absolute',
78
+ borderRadius: '50px'
79
+ },
80
+ '&::before': {
81
+ transform: 'rotate(45deg) translate(3.4px, 8.5px)'
82
+ },
83
+ '&::after': {
84
+ transform: 'rotate(-45deg) translate(-6.3px, 5.7px)'
85
+ }
86
+ });
87
+ const getIconClassName = (isSortingAllowed, sortOrdered) => {
88
+ const activated = sortOrdered !== SortOrder.NO_ORDER;
89
+ const activeStatusClass = `${SORTABLE_COLUMN_ICON_CLASSNAME}-${activated ? 'active' : 'inactive'}`;
90
+ if (!isSortingAllowed) {
91
+ return `${StatusClassNames.SORTING_NOT_ALLOWED} ${activeStatusClass}`;
92
+ }
93
+ switch (sortOrdered) {
94
+ case SortOrder.ASC:
95
+ return `${StatusClassNames.ASC} ${activeStatusClass}`;
96
+ case SortOrder.DESC:
97
+ return `${StatusClassNames.DESC} ${activeStatusClass}`;
98
+ default:
99
+ return `${StatusClassNames.NO_ORDER} ${activeStatusClass}`;
100
+ }
101
+ };
102
+ const getTooltipTitle = (intl, isSortingAllowed, sortOrdered) => {
103
+ const {
104
+ noOrderLabel,
105
+ ascOrderLabel,
106
+ descOrderLabel,
107
+ invalidLabel
108
+ } = sortingIconMessages;
109
+ if (!isSortingAllowed) {
110
+ return intl.formatMessage(invalidLabel);
111
+ }
112
+ switch (sortOrdered) {
113
+ case SortOrder.NO_ORDER:
114
+ return intl.formatMessage(noOrderLabel);
115
+ case SortOrder.ASC:
116
+ return intl.formatMessage(ascOrderLabel);
117
+ case SortOrder.DESC:
118
+ return intl.formatMessage(descOrderLabel);
119
+ }
120
+ return '';
121
+ };
122
+ const getAriaLabel = (intl, isSortingAllowed, sortOrdered) => {
123
+ const {
124
+ noOrderLabel,
125
+ ascOrderLabel,
126
+ descOrderLabel,
127
+ invalidLabel,
128
+ defaultLabel
129
+ } = sortingAriaLabelMessages;
130
+ if (!isSortingAllowed) {
131
+ return intl.formatMessage(invalidLabel);
132
+ }
133
+ switch (sortOrdered) {
134
+ case SortOrder.NO_ORDER:
135
+ return intl.formatMessage(noOrderLabel);
136
+ case SortOrder.ASC:
137
+ return intl.formatMessage(ascOrderLabel);
138
+ case SortOrder.DESC:
139
+ return intl.formatMessage(descOrderLabel);
140
+ }
141
+ return intl.formatMessage(defaultLabel);
142
+ };
143
+ const SortingIcon = ({
144
+ isSortingAllowed,
145
+ sortOrdered,
146
+ intl,
147
+ onClick,
148
+ onKeyDown
149
+ }) => {
150
+ const buttonClassName = `${SORTABLE_COLUMN_ICON_CLASSNAME} ${sortOrdered !== SortOrder.NO_ORDER ? 'is-active' : ''}${isSortingAllowed ? '' : ` ${SORTABLE_COLUMN_ICON_CLASSNAME}__not-allowed `}`;
151
+ const content = getTooltipTitle(intl, isSortingAllowed, sortOrdered);
152
+ const ariaLabel = getAriaLabel(intl, isSortingAllowed, sortOrdered);
153
+ const handleClick = () => {
154
+ if (isSortingAllowed) {
155
+ onClick();
156
+ }
157
+ };
158
+ const handleKeyDown = event => {
159
+ if (isSortingAllowed) {
160
+ onKeyDown(event);
161
+ }
162
+ };
163
+ return jsx(Tooltip, {
164
+ delay: 0,
165
+ content: content,
166
+ position: "top"
167
+ }, jsx("div", {
168
+ css: buttonStyles,
169
+ className: buttonClassName,
170
+ role: "button",
171
+ tabIndex: isSortingAllowed ? 0 : -1,
172
+ "aria-label": ariaLabel,
173
+ "aria-disabled": !isSortingAllowed,
174
+ onClick: handleClick,
175
+ onKeyDown: handleKeyDown
176
+ }, jsx("div", {
177
+ css: iconWrapperStyles,
178
+ className: getIconClassName(isSortingAllowed, sortOrdered)
179
+ }, jsx("div", {
180
+ css: iconStyles
181
+ }))));
182
+ };
183
+ export default injectIntl(SortingIcon);
@@ -0,0 +1 @@
1
+ export const SORTABLE_COLUMN_ICON_CLASSNAME = `ak-renderer-tableHeader-sorting-icon`;
@@ -0,0 +1,2 @@
1
+ export { SORTABLE_COLUMN_ICON_CLASSNAME } from './consts';
2
+ export { default as SortingIcon, StatusClassNames } from './SortingIcon';
@@ -0,0 +1,50 @@
1
+ import { defineMessages } from 'react-intl-next';
2
+ export const sortingIconMessages = defineMessages({
3
+ noOrderLabel: {
4
+ id: 'fabric.editor.headingLink.noOrderLabel',
5
+ defaultMessage: 'Sort column A to Z',
6
+ description: 'Sort the column in ascending order'
7
+ },
8
+ ascOrderLabel: {
9
+ id: 'fabric.editor.headingLink.ascOrderLabel',
10
+ defaultMessage: 'Sort column Z to A',
11
+ description: 'Sort the column in descending order'
12
+ },
13
+ descOrderLabel: {
14
+ id: 'fabric.editor.headingLink.descOrderLabel',
15
+ defaultMessage: 'Clear sorting',
16
+ description: 'clear the sorting from this column'
17
+ },
18
+ invalidLabel: {
19
+ id: 'fabric.editor.headingLink.invalidLabel',
20
+ defaultMessage: `⚠️ You can't sort a table with merged cells`,
21
+ description: 'this sort is invalid for merged cells'
22
+ }
23
+ });
24
+ export const sortingAriaLabelMessages = defineMessages({
25
+ noOrderLabel: {
26
+ id: 'fabric.editor.tableHeader.sorting.no',
27
+ defaultMessage: 'No sort applied to the column',
28
+ description: 'Aria-label for Sort column button when sorting was not applied or the sorting has been cleared'
29
+ },
30
+ ascOrderLabel: {
31
+ id: 'fabric.editor.tableHeader.sorting.asc',
32
+ defaultMessage: 'Ascending sort applied',
33
+ description: 'Aria-label for Sort column button when ascending sorting was applied'
34
+ },
35
+ descOrderLabel: {
36
+ id: 'fabric.editor.tableHeader.sorting.desc',
37
+ defaultMessage: 'Descending sort applied',
38
+ description: 'Aria-label for Sort column button when descending sorting was applied'
39
+ },
40
+ invalidLabel: {
41
+ id: 'fabric.editor.tableHeader.sorting.invalid',
42
+ defaultMessage: `You can't sort a table with merged cells`,
43
+ description: 'Aria-label for Sort column button when sorting is not possible'
44
+ },
45
+ defaultLabel: {
46
+ id: 'fabric.editor.tableHeader.sorting.default',
47
+ defaultMessage: 'Sort the column',
48
+ description: 'Default aria-label for Sort column button'
49
+ }
50
+ });
@@ -7,7 +7,7 @@ import { createAndFireEvent, withAnalyticsContext, withAnalyticsEvents } from '@
7
7
  import { N0, N50A, N60A, N900 } from '@atlaskit/theme/colors';
8
8
  import Layer from '../Layer';
9
9
  const packageName = "@atlaskit/editor-common";
10
- const packageVersion = "78.34.0";
10
+ const packageVersion = "78.36.0";
11
11
  const halfFocusRing = 1;
12
12
  const dropOffset = '0, 8';
13
13
  class DropList extends Component {
@@ -6,7 +6,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
6
6
  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; }
7
7
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
8
8
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
9
- var packageVersion = "78.34.0";
9
+ var packageVersion = "78.36.0";
10
10
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
11
11
  // Remove URL as it has UGC
12
12
  // TODO: Sanitise the URL instead of just removing it
@@ -0,0 +1,34 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useLayoutEffect, useMemo, useState } from 'react';
3
+ /**
4
+ * A component for rendering portals managed by a `PortalManager`.
5
+ * It subscribes to a `PortalManager` instance to listen for changes in the portal content
6
+ * and renders the content of its assigned portal bucket.
7
+ *
8
+ * @param {PortalBucketProps} props The component props.
9
+ * @param {number} props.id The ID for the portal bucket. This ID is used by the `PortalManager` to manage the content of this bucket.
10
+ * @param {PortalManager} props.portalManager An instance of `PortalManager` which manages the registration and unregistration of portal buckets and their content.
11
+ * @returns {React.ReactElement} The React element(s) that are currently registered to this portal bucket.
12
+ */
13
+ export function PortalBucket(_ref) {
14
+ var id = _ref.id,
15
+ portalManager = _ref.portalManager;
16
+ // State to hold the current portals for this bucket
17
+ var _useState = useState({}),
18
+ _useState2 = _slicedToArray(_useState, 2),
19
+ portals = _useState2[0],
20
+ setPortals = _useState2[1];
21
+ // Effect to register/unregister this bucket with the portal manager on mount/unmount
22
+ useLayoutEffect(function () {
23
+ portalManager.registerBucket(id, setPortals);
24
+ return function () {
25
+ portalManager.unregisterBucket(id);
26
+ };
27
+ }, [id, portalManager]);
28
+ // Memoize the portal elements to avoid unnecessary re-renders
29
+ var portalElements = useMemo(function () {
30
+ return Object.values(portals);
31
+ }, [portals]);
32
+ // Render the current portal elements
33
+ return /*#__PURE__*/React.createElement(React.Fragment, null, portalElements);
34
+ }