@atlaskit/editor-common 78.34.0 → 78.35.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 +6 -0
- package/dist/cjs/monitoring/error.js +1 -1
- package/dist/cjs/portal/PortalBucket.js +44 -0
- package/dist/cjs/portal/PortalManager.js +209 -0
- package/dist/cjs/portal/index.js +26 -0
- package/dist/cjs/portal/usePortalProvider.js +80 -0
- package/dist/cjs/ui/DropList/index.js +1 -1
- package/dist/es2019/monitoring/error.js +1 -1
- package/dist/es2019/portal/PortalBucket.js +29 -0
- package/dist/es2019/portal/PortalManager.js +157 -0
- package/dist/es2019/portal/index.js +3 -0
- package/dist/es2019/portal/usePortalProvider.js +58 -0
- package/dist/es2019/ui/DropList/index.js +1 -1
- package/dist/esm/monitoring/error.js +1 -1
- package/dist/esm/portal/PortalBucket.js +34 -0
- package/dist/esm/portal/PortalManager.js +202 -0
- package/dist/esm/portal/index.js +3 -0
- package/dist/esm/portal/usePortalProvider.js +70 -0
- package/dist/esm/ui/DropList/index.js +1 -1
- package/dist/types/portal/PortalBucket.d.ts +18 -0
- package/dist/types/portal/PortalManager.d.ts +53 -0
- package/dist/types/portal/index.d.ts +3 -0
- package/dist/types/portal/usePortalProvider.d.ts +18 -0
- package/dist/types-ts4.5/portal/PortalBucket.d.ts +18 -0
- package/dist/types-ts4.5/portal/PortalManager.d.ts +53 -0
- package/dist/types-ts4.5/portal/index.d.ts +3 -0
- package/dist/types-ts4.5/portal/usePortalProvider.d.ts +21 -0
- package/package.json +2 -1
- package/portal/package.json +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @atlaskit/editor-common
|
|
2
2
|
|
|
3
|
+
## 78.35.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#93196](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/93196) [`2528bcaa6643`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/2528bcaa6643) - Added new PortalProvider implementation + tests
|
|
8
|
+
|
|
3
9
|
## 78.34.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -16,7 +16,7 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
|
|
|
16
16
|
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 && Object.prototype.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; }
|
|
17
17
|
var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
|
|
18
18
|
var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
|
|
19
|
-
var packageVersion = "78.
|
|
19
|
+
var packageVersion = "78.35.0";
|
|
20
20
|
var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
|
|
21
21
|
// Remove URL as it has UGC
|
|
22
22
|
// TODO: Sanitise the URL instead of just removing it
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof = require("@babel/runtime/helpers/typeof");
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.PortalBucket = PortalBucket;
|
|
9
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
11
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
12
|
+
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 && Object.prototype.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; }
|
|
13
|
+
/**
|
|
14
|
+
* A component for rendering portals managed by a `PortalManager`.
|
|
15
|
+
* It subscribes to a `PortalManager` instance to listen for changes in the portal content
|
|
16
|
+
* and renders the content of its assigned portal bucket.
|
|
17
|
+
*
|
|
18
|
+
* @param {PortalBucketProps} props The component props.
|
|
19
|
+
* @param {number} props.id The ID for the portal bucket. This ID is used by the `PortalManager` to manage the content of this bucket.
|
|
20
|
+
* @param {PortalManager} props.portalManager An instance of `PortalManager` which manages the registration and unregistration of portal buckets and their content.
|
|
21
|
+
* @returns {React.ReactElement} The React element(s) that are currently registered to this portal bucket.
|
|
22
|
+
*/
|
|
23
|
+
function PortalBucket(_ref) {
|
|
24
|
+
var id = _ref.id,
|
|
25
|
+
portalManager = _ref.portalManager;
|
|
26
|
+
// State to hold the current portals for this bucket
|
|
27
|
+
var _useState = (0, _react.useState)({}),
|
|
28
|
+
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
29
|
+
portals = _useState2[0],
|
|
30
|
+
setPortals = _useState2[1];
|
|
31
|
+
// Effect to register/unregister this bucket with the portal manager on mount/unmount
|
|
32
|
+
(0, _react.useLayoutEffect)(function () {
|
|
33
|
+
portalManager.registerBucket(id, setPortals);
|
|
34
|
+
return function () {
|
|
35
|
+
portalManager.unregisterBucket(id);
|
|
36
|
+
};
|
|
37
|
+
}, [id, portalManager]);
|
|
38
|
+
// Memoize the portal elements to avoid unnecessary re-renders
|
|
39
|
+
var portalElements = (0, _react.useMemo)(function () {
|
|
40
|
+
return Object.values(portals);
|
|
41
|
+
}, [portals]);
|
|
42
|
+
// Render the current portal elements
|
|
43
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, portalElements);
|
|
44
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.PortalManager = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
11
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
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
|
+
var DEFAULT_INITIAL_BUCKETS = 50;
|
|
15
|
+
var DEFAULT_MAX_BUCKET_CAPACITY = 50;
|
|
16
|
+
var DEFAULT_SCALE_RATIO = 0.5;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates an empty bucket object with a specified capacity. Each bucket is designed
|
|
20
|
+
* to hold a certain number of React portals and has an associated updater function
|
|
21
|
+
* which can be null initially.
|
|
22
|
+
*
|
|
23
|
+
* @function createEmptyBucket
|
|
24
|
+
* @param {number} capacity - The maximum capacity of the bucket.
|
|
25
|
+
* @returns {PortalBucketType} An object representing an empty bucket with the specified capacity.
|
|
26
|
+
*/
|
|
27
|
+
function createEmptyBucket(capacity) {
|
|
28
|
+
return {
|
|
29
|
+
portals: {},
|
|
30
|
+
capacity: capacity,
|
|
31
|
+
updater: null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A utility class to manage and dynamically scale React portals across multiple buckets.
|
|
37
|
+
* It allows for efficient rendering of large numbers of React portals by distributing them
|
|
38
|
+
* across "buckets" and updating these buckets as necessary to balance load and performance.
|
|
39
|
+
*
|
|
40
|
+
* @class PortalManager
|
|
41
|
+
* @typedef {Object} PortalManager
|
|
42
|
+
*
|
|
43
|
+
* @property {number} maxBucketCapacity - The maximum capacity of each bucket before a new bucket is created.
|
|
44
|
+
* @property {number} scaleRatio - The ratio to determine the number of new buckets to add when scaling up.
|
|
45
|
+
* @property {Array<PortalBucketType>} buckets - An array of bucket objects where each bucket holds a record of React portals.
|
|
46
|
+
* @property {Set<number>} availableBuckets - A set of indices representing buckets that have available capacity.
|
|
47
|
+
* @property {Map<React.Key, number>} portalToBucketMap - A map of React portal keys to their corresponding bucket indices.
|
|
48
|
+
* @property {PortalRendererUpdater|null} portalRendererUpdater - A function to trigger updates to the rendering of portals.
|
|
49
|
+
* @property {number} scaleCapacityThreshold - The threshold at which the buckets are scaled up to accommodate more portals.
|
|
50
|
+
*
|
|
51
|
+
* @param {number} [initialBuckets=DEFAULT_INITIAL_BUCKETS] - The initial number of buckets to create.
|
|
52
|
+
* @param {number} [maxBucketCapacity=DEFAULT_MAX_BUCKET_CAPACITY] - The maximum number of portals a single bucket can hold.
|
|
53
|
+
* @param {number} [scaleRatio=DEFAULT_SCALE_RATIO] - The ratio used to calculate the number of new buckets to add when scaling.
|
|
54
|
+
*/
|
|
55
|
+
var PortalManager = exports.PortalManager = /*#__PURE__*/function () {
|
|
56
|
+
function PortalManager() {
|
|
57
|
+
var initialBuckets = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_INITIAL_BUCKETS;
|
|
58
|
+
var maxBucketCapacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_MAX_BUCKET_CAPACITY;
|
|
59
|
+
var scaleRatio = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_SCALE_RATIO;
|
|
60
|
+
(0, _classCallCheck2.default)(this, PortalManager);
|
|
61
|
+
this.maxBucketCapacity = maxBucketCapacity;
|
|
62
|
+
this.scaleRatio = scaleRatio;
|
|
63
|
+
|
|
64
|
+
// Initialise buckets array by creating an array of length `initialBuckets` containing empty buckets
|
|
65
|
+
this.buckets = Array.from({
|
|
66
|
+
length: initialBuckets
|
|
67
|
+
}, function () {
|
|
68
|
+
return createEmptyBucket(maxBucketCapacity);
|
|
69
|
+
});
|
|
70
|
+
this.portalToBucketMap = new Map();
|
|
71
|
+
this.availableBuckets = new Set(Array.from({
|
|
72
|
+
length: initialBuckets
|
|
73
|
+
}, function (_, i) {
|
|
74
|
+
return i;
|
|
75
|
+
}));
|
|
76
|
+
this.portalRendererUpdater = null;
|
|
77
|
+
this.scaleCapacityThreshold = maxBucketCapacity / 2;
|
|
78
|
+
}
|
|
79
|
+
(0, _createClass2.default)(PortalManager, [{
|
|
80
|
+
key: "getCurrentBucket",
|
|
81
|
+
value: function getCurrentBucket() {
|
|
82
|
+
return this.availableBuckets.values().next().value;
|
|
83
|
+
}
|
|
84
|
+
}, {
|
|
85
|
+
key: "createBucket",
|
|
86
|
+
value: function createBucket() {
|
|
87
|
+
var _this$portalRendererU;
|
|
88
|
+
var currentBucket = this.getCurrentBucket();
|
|
89
|
+
|
|
90
|
+
//If the current bucket has capacity, skip this logic
|
|
91
|
+
if (this.buckets[currentBucket].capacity > 0) {
|
|
92
|
+
return;
|
|
93
|
+
} else {
|
|
94
|
+
// The current bucket is full, delete the bucket from the list of available buckets
|
|
95
|
+
this.availableBuckets.delete(currentBucket);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Skip creating new bucket if there are buckets still available
|
|
99
|
+
if (this.availableBuckets.size > 0) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Scale the buckets up only if there are no available buckets left
|
|
104
|
+
// Calculate how many new buckets need to be added
|
|
105
|
+
var numBucketsToAdd = Math.floor(this.buckets.length * this.scaleRatio);
|
|
106
|
+
this.buckets = (0, _toConsumableArray2.default)(this.buckets);
|
|
107
|
+
for (var i = 0; i < numBucketsToAdd; i++) {
|
|
108
|
+
this.buckets.push(createEmptyBucket(this.maxBucketCapacity));
|
|
109
|
+
this.availableBuckets.add(this.buckets.length - 1);
|
|
110
|
+
}
|
|
111
|
+
(_this$portalRendererU = this.portalRendererUpdater) === null || _this$portalRendererU === void 0 || _this$portalRendererU.call(this, this.buckets);
|
|
112
|
+
}
|
|
113
|
+
}, {
|
|
114
|
+
key: "getBuckets",
|
|
115
|
+
value: function getBuckets() {
|
|
116
|
+
return this.buckets;
|
|
117
|
+
}
|
|
118
|
+
}, {
|
|
119
|
+
key: "registerBucket",
|
|
120
|
+
value: function registerBucket(id, updater) {
|
|
121
|
+
var _this$buckets$id$upda,
|
|
122
|
+
_this$buckets$id,
|
|
123
|
+
_this = this;
|
|
124
|
+
this.buckets[id].updater = updater;
|
|
125
|
+
(_this$buckets$id$upda = (_this$buckets$id = this.buckets[id]).updater) === null || _this$buckets$id$upda === void 0 || _this$buckets$id$upda.call(_this$buckets$id, function () {
|
|
126
|
+
return _objectSpread({}, _this.buckets[id].portals);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}, {
|
|
130
|
+
key: "unregisterBucket",
|
|
131
|
+
value: function unregisterBucket(id) {
|
|
132
|
+
this.buckets[id].updater = null;
|
|
133
|
+
}
|
|
134
|
+
}, {
|
|
135
|
+
key: "updateBuckets",
|
|
136
|
+
value: function updateBuckets(id) {
|
|
137
|
+
var _this$buckets$id$upda2,
|
|
138
|
+
_this$buckets$id2,
|
|
139
|
+
_this2 = this;
|
|
140
|
+
(_this$buckets$id$upda2 = (_this$buckets$id2 = this.buckets[id]).updater) === null || _this$buckets$id$upda2 === void 0 || _this$buckets$id$upda2.call(_this$buckets$id2, function () {
|
|
141
|
+
// new object is required to trigger react updates
|
|
142
|
+
return _objectSpread({}, _this2.buckets[id].portals);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}, {
|
|
146
|
+
key: "registerPortal",
|
|
147
|
+
value: function registerPortal(key, portal) {
|
|
148
|
+
var _this$portalToBucketM,
|
|
149
|
+
_this3 = this;
|
|
150
|
+
this.createBucket();
|
|
151
|
+
this.buckets[this.getCurrentBucket()].capacity -= 1;
|
|
152
|
+
var id = (_this$portalToBucketM = this.portalToBucketMap.get(key)) !== null && _this$portalToBucketM !== void 0 ? _this$portalToBucketM : this.getCurrentBucket();
|
|
153
|
+
this.portalToBucketMap.set(key, id);
|
|
154
|
+
if (this.buckets[id].portals[key] !== portal) {
|
|
155
|
+
this.buckets[id].portals[key] = portal;
|
|
156
|
+
this.updateBuckets(id);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//returns a function to unregister the portal
|
|
160
|
+
return function () {
|
|
161
|
+
delete _this3.buckets[id].portals[key];
|
|
162
|
+
_this3.portalToBucketMap.delete(key);
|
|
163
|
+
_this3.buckets[id].capacity += 1;
|
|
164
|
+
if (_this3.buckets[id].capacity > _this3.scaleCapacityThreshold) {
|
|
165
|
+
_this3.availableBuckets.add(id);
|
|
166
|
+
}
|
|
167
|
+
_this3.updateBuckets(id);
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}, {
|
|
171
|
+
key: "registerPortalRenderer",
|
|
172
|
+
value: function registerPortalRenderer(updater) {
|
|
173
|
+
var _this4 = this;
|
|
174
|
+
if (!this.portalRendererUpdater) {
|
|
175
|
+
updater(function () {
|
|
176
|
+
return _this4.buckets;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
this.portalRendererUpdater = updater;
|
|
180
|
+
}
|
|
181
|
+
}, {
|
|
182
|
+
key: "unregisterPortalRenderer",
|
|
183
|
+
value: function unregisterPortalRenderer() {
|
|
184
|
+
this.portalRendererUpdater = null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Cleans up resources used by the PortalManager. This includes clearing all portals,
|
|
189
|
+
* unregistering all buckets, and resetting internal state.
|
|
190
|
+
*/
|
|
191
|
+
}, {
|
|
192
|
+
key: "destroy",
|
|
193
|
+
value: function destroy() {
|
|
194
|
+
var _this5 = this;
|
|
195
|
+
// Iterate through each bucket and clear its portals and unset the updater function
|
|
196
|
+
this.buckets.forEach(function (bucket, id) {
|
|
197
|
+
bucket.portals = {}; // Clearing all portals from the bucket
|
|
198
|
+
bucket.updater = null; // Unsetting the bucket's updater function
|
|
199
|
+
_this5.availableBuckets.add(id); // Mark all buckets as available
|
|
200
|
+
});
|
|
201
|
+
this.portalToBucketMap.clear();
|
|
202
|
+
this.portalRendererUpdater = null;
|
|
203
|
+
this.availableBuckets = new Set(this.buckets.map(function (_, index) {
|
|
204
|
+
return index;
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
}]);
|
|
208
|
+
return PortalManager;
|
|
209
|
+
}();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "PortalBucket", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _PortalBucket.PortalBucket;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "PortalManager", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function get() {
|
|
15
|
+
return _PortalManager.PortalManager;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "usePortalProvider", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function get() {
|
|
21
|
+
return _usePortalProvider.usePortalProvider;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
var _PortalManager = require("./PortalManager");
|
|
25
|
+
var _PortalBucket = require("./PortalBucket");
|
|
26
|
+
var _usePortalProvider = require("./usePortalProvider");
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof = require("@babel/runtime/helpers/typeof");
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.usePortalProvider = usePortalProvider;
|
|
9
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
11
|
+
var _reactDom = require("react-dom");
|
|
12
|
+
var _PortalBucket = require("./PortalBucket");
|
|
13
|
+
var _PortalManager = require("./PortalManager");
|
|
14
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
15
|
+
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 && Object.prototype.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; }
|
|
16
|
+
function createPortalRendererComponent(portalManager) {
|
|
17
|
+
return function PortalRenderer() {
|
|
18
|
+
var _useState = (0, _react.useState)(portalManager.getBuckets()),
|
|
19
|
+
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
20
|
+
buckets = _useState2[0],
|
|
21
|
+
setBuckets = _useState2[1];
|
|
22
|
+
(0, _react.useLayoutEffect)(function () {
|
|
23
|
+
portalManager.registerPortalRenderer(setBuckets);
|
|
24
|
+
return function () {
|
|
25
|
+
portalManager.unregisterPortalRenderer();
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
var portalsElements = (0, _react.useMemo)(function () {
|
|
29
|
+
return buckets.map(function (_, i) {
|
|
30
|
+
return /*#__PURE__*/_react.default.createElement(_PortalBucket.PortalBucket, {
|
|
31
|
+
key: i,
|
|
32
|
+
id: i,
|
|
33
|
+
portalManager: portalManager
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}, [buckets]);
|
|
37
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, portalsElements);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Initializes PortalManager and creates PortalRendererComponent. Offers an API (portalProviderAPI) for managing portals.
|
|
43
|
+
* @returns {[PortalProviderAPI, PortalRendererComponent]} An array containing two elements:
|
|
44
|
+
* 1. portalProviderAPI: An object providing an API for rendering and removing portals.
|
|
45
|
+
* 2. PortalRenderer: A React component responsible for rendering the portal content.
|
|
46
|
+
*/
|
|
47
|
+
function usePortalProvider() {
|
|
48
|
+
var portalManager = (0, _react.useMemo)(function () {
|
|
49
|
+
return new _PortalManager.PortalManager();
|
|
50
|
+
}, []);
|
|
51
|
+
var PortalRenderer = (0, _react.useMemo)(function () {
|
|
52
|
+
return createPortalRendererComponent(portalManager);
|
|
53
|
+
}, [portalManager]);
|
|
54
|
+
var portalProviderAPI = (0, _react.useMemo)(function () {
|
|
55
|
+
var portalsMap = new Map();
|
|
56
|
+
return {
|
|
57
|
+
render: function render(key, children, container) {
|
|
58
|
+
var portal = /*#__PURE__*/(0, _reactDom.createPortal)(children(), container, key);
|
|
59
|
+
portalsMap.set(key, portalManager.registerPortal(key, portal));
|
|
60
|
+
},
|
|
61
|
+
remove: function remove(key) {
|
|
62
|
+
var _portalsMap$get;
|
|
63
|
+
(_portalsMap$get = portalsMap.get(key)) === null || _portalsMap$get === void 0 || _portalsMap$get();
|
|
64
|
+
portalsMap.delete(key);
|
|
65
|
+
},
|
|
66
|
+
destroy: function destroy() {
|
|
67
|
+
portalsMap.clear();
|
|
68
|
+
portalManager.destroy();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}, [portalManager]);
|
|
72
|
+
|
|
73
|
+
// Cleanup on unmount
|
|
74
|
+
(0, _react.useEffect)(function () {
|
|
75
|
+
return function () {
|
|
76
|
+
portalProviderAPI.destroy();
|
|
77
|
+
};
|
|
78
|
+
}, [portalManager, portalProviderAPI]);
|
|
79
|
+
return [portalProviderAPI, PortalRenderer];
|
|
80
|
+
}
|
|
@@ -20,7 +20,7 @@ var _Layer = _interopRequireDefault(require("../Layer"));
|
|
|
20
20
|
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
|
|
21
21
|
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /** @jsx jsx */
|
|
22
22
|
var packageName = "@atlaskit/editor-common";
|
|
23
|
-
var packageVersion = "78.
|
|
23
|
+
var packageVersion = "78.35.0";
|
|
24
24
|
var halfFocusRing = 1;
|
|
25
25
|
var dropOffset = '0, 8';
|
|
26
26
|
var DropList = /*#__PURE__*/function (_Component) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
|
|
2
2
|
const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
|
|
3
|
-
const packageVersion = "78.
|
|
3
|
+
const packageVersion = "78.35.0";
|
|
4
4
|
const sanitiseSentryEvents = (data, _hint) => {
|
|
5
5
|
// Remove URL as it has UGC
|
|
6
6
|
// TODO: Sanitise the URL instead of just removing it
|
|
@@ -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,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
|
+
}
|
|
@@ -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.
|
|
10
|
+
const packageVersion = "78.35.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.
|
|
9
|
+
var packageVersion = "78.35.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
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
3
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
4
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
5
|
+
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; }
|
|
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
|
+
var DEFAULT_INITIAL_BUCKETS = 50;
|
|
8
|
+
var DEFAULT_MAX_BUCKET_CAPACITY = 50;
|
|
9
|
+
var DEFAULT_SCALE_RATIO = 0.5;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates an empty bucket object with a specified capacity. Each bucket is designed
|
|
13
|
+
* to hold a certain number of React portals and has an associated updater function
|
|
14
|
+
* which can be null initially.
|
|
15
|
+
*
|
|
16
|
+
* @function createEmptyBucket
|
|
17
|
+
* @param {number} capacity - The maximum capacity of the bucket.
|
|
18
|
+
* @returns {PortalBucketType} An object representing an empty bucket with the specified capacity.
|
|
19
|
+
*/
|
|
20
|
+
function createEmptyBucket(capacity) {
|
|
21
|
+
return {
|
|
22
|
+
portals: {},
|
|
23
|
+
capacity: capacity,
|
|
24
|
+
updater: null
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A utility class to manage and dynamically scale React portals across multiple buckets.
|
|
30
|
+
* It allows for efficient rendering of large numbers of React portals by distributing them
|
|
31
|
+
* across "buckets" and updating these buckets as necessary to balance load and performance.
|
|
32
|
+
*
|
|
33
|
+
* @class PortalManager
|
|
34
|
+
* @typedef {Object} PortalManager
|
|
35
|
+
*
|
|
36
|
+
* @property {number} maxBucketCapacity - The maximum capacity of each bucket before a new bucket is created.
|
|
37
|
+
* @property {number} scaleRatio - The ratio to determine the number of new buckets to add when scaling up.
|
|
38
|
+
* @property {Array<PortalBucketType>} buckets - An array of bucket objects where each bucket holds a record of React portals.
|
|
39
|
+
* @property {Set<number>} availableBuckets - A set of indices representing buckets that have available capacity.
|
|
40
|
+
* @property {Map<React.Key, number>} portalToBucketMap - A map of React portal keys to their corresponding bucket indices.
|
|
41
|
+
* @property {PortalRendererUpdater|null} portalRendererUpdater - A function to trigger updates to the rendering of portals.
|
|
42
|
+
* @property {number} scaleCapacityThreshold - The threshold at which the buckets are scaled up to accommodate more portals.
|
|
43
|
+
*
|
|
44
|
+
* @param {number} [initialBuckets=DEFAULT_INITIAL_BUCKETS] - The initial number of buckets to create.
|
|
45
|
+
* @param {number} [maxBucketCapacity=DEFAULT_MAX_BUCKET_CAPACITY] - The maximum number of portals a single bucket can hold.
|
|
46
|
+
* @param {number} [scaleRatio=DEFAULT_SCALE_RATIO] - The ratio used to calculate the number of new buckets to add when scaling.
|
|
47
|
+
*/
|
|
48
|
+
export var PortalManager = /*#__PURE__*/function () {
|
|
49
|
+
function PortalManager() {
|
|
50
|
+
var initialBuckets = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_INITIAL_BUCKETS;
|
|
51
|
+
var maxBucketCapacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_MAX_BUCKET_CAPACITY;
|
|
52
|
+
var scaleRatio = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_SCALE_RATIO;
|
|
53
|
+
_classCallCheck(this, PortalManager);
|
|
54
|
+
this.maxBucketCapacity = maxBucketCapacity;
|
|
55
|
+
this.scaleRatio = scaleRatio;
|
|
56
|
+
|
|
57
|
+
// Initialise buckets array by creating an array of length `initialBuckets` containing empty buckets
|
|
58
|
+
this.buckets = Array.from({
|
|
59
|
+
length: initialBuckets
|
|
60
|
+
}, function () {
|
|
61
|
+
return createEmptyBucket(maxBucketCapacity);
|
|
62
|
+
});
|
|
63
|
+
this.portalToBucketMap = new Map();
|
|
64
|
+
this.availableBuckets = new Set(Array.from({
|
|
65
|
+
length: initialBuckets
|
|
66
|
+
}, function (_, i) {
|
|
67
|
+
return i;
|
|
68
|
+
}));
|
|
69
|
+
this.portalRendererUpdater = null;
|
|
70
|
+
this.scaleCapacityThreshold = maxBucketCapacity / 2;
|
|
71
|
+
}
|
|
72
|
+
_createClass(PortalManager, [{
|
|
73
|
+
key: "getCurrentBucket",
|
|
74
|
+
value: function getCurrentBucket() {
|
|
75
|
+
return this.availableBuckets.values().next().value;
|
|
76
|
+
}
|
|
77
|
+
}, {
|
|
78
|
+
key: "createBucket",
|
|
79
|
+
value: function createBucket() {
|
|
80
|
+
var _this$portalRendererU;
|
|
81
|
+
var currentBucket = this.getCurrentBucket();
|
|
82
|
+
|
|
83
|
+
//If the current bucket has capacity, skip this logic
|
|
84
|
+
if (this.buckets[currentBucket].capacity > 0) {
|
|
85
|
+
return;
|
|
86
|
+
} else {
|
|
87
|
+
// The current bucket is full, delete the bucket from the list of available buckets
|
|
88
|
+
this.availableBuckets.delete(currentBucket);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Skip creating new bucket if there are buckets still available
|
|
92
|
+
if (this.availableBuckets.size > 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Scale the buckets up only if there are no available buckets left
|
|
97
|
+
// Calculate how many new buckets need to be added
|
|
98
|
+
var numBucketsToAdd = Math.floor(this.buckets.length * this.scaleRatio);
|
|
99
|
+
this.buckets = _toConsumableArray(this.buckets);
|
|
100
|
+
for (var i = 0; i < numBucketsToAdd; i++) {
|
|
101
|
+
this.buckets.push(createEmptyBucket(this.maxBucketCapacity));
|
|
102
|
+
this.availableBuckets.add(this.buckets.length - 1);
|
|
103
|
+
}
|
|
104
|
+
(_this$portalRendererU = this.portalRendererUpdater) === null || _this$portalRendererU === void 0 || _this$portalRendererU.call(this, this.buckets);
|
|
105
|
+
}
|
|
106
|
+
}, {
|
|
107
|
+
key: "getBuckets",
|
|
108
|
+
value: function getBuckets() {
|
|
109
|
+
return this.buckets;
|
|
110
|
+
}
|
|
111
|
+
}, {
|
|
112
|
+
key: "registerBucket",
|
|
113
|
+
value: function registerBucket(id, updater) {
|
|
114
|
+
var _this$buckets$id$upda,
|
|
115
|
+
_this$buckets$id,
|
|
116
|
+
_this = this;
|
|
117
|
+
this.buckets[id].updater = updater;
|
|
118
|
+
(_this$buckets$id$upda = (_this$buckets$id = this.buckets[id]).updater) === null || _this$buckets$id$upda === void 0 || _this$buckets$id$upda.call(_this$buckets$id, function () {
|
|
119
|
+
return _objectSpread({}, _this.buckets[id].portals);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}, {
|
|
123
|
+
key: "unregisterBucket",
|
|
124
|
+
value: function unregisterBucket(id) {
|
|
125
|
+
this.buckets[id].updater = null;
|
|
126
|
+
}
|
|
127
|
+
}, {
|
|
128
|
+
key: "updateBuckets",
|
|
129
|
+
value: function updateBuckets(id) {
|
|
130
|
+
var _this$buckets$id$upda2,
|
|
131
|
+
_this$buckets$id2,
|
|
132
|
+
_this2 = this;
|
|
133
|
+
(_this$buckets$id$upda2 = (_this$buckets$id2 = this.buckets[id]).updater) === null || _this$buckets$id$upda2 === void 0 || _this$buckets$id$upda2.call(_this$buckets$id2, function () {
|
|
134
|
+
// new object is required to trigger react updates
|
|
135
|
+
return _objectSpread({}, _this2.buckets[id].portals);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}, {
|
|
139
|
+
key: "registerPortal",
|
|
140
|
+
value: function registerPortal(key, portal) {
|
|
141
|
+
var _this$portalToBucketM,
|
|
142
|
+
_this3 = this;
|
|
143
|
+
this.createBucket();
|
|
144
|
+
this.buckets[this.getCurrentBucket()].capacity -= 1;
|
|
145
|
+
var id = (_this$portalToBucketM = this.portalToBucketMap.get(key)) !== null && _this$portalToBucketM !== void 0 ? _this$portalToBucketM : this.getCurrentBucket();
|
|
146
|
+
this.portalToBucketMap.set(key, id);
|
|
147
|
+
if (this.buckets[id].portals[key] !== portal) {
|
|
148
|
+
this.buckets[id].portals[key] = portal;
|
|
149
|
+
this.updateBuckets(id);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
//returns a function to unregister the portal
|
|
153
|
+
return function () {
|
|
154
|
+
delete _this3.buckets[id].portals[key];
|
|
155
|
+
_this3.portalToBucketMap.delete(key);
|
|
156
|
+
_this3.buckets[id].capacity += 1;
|
|
157
|
+
if (_this3.buckets[id].capacity > _this3.scaleCapacityThreshold) {
|
|
158
|
+
_this3.availableBuckets.add(id);
|
|
159
|
+
}
|
|
160
|
+
_this3.updateBuckets(id);
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}, {
|
|
164
|
+
key: "registerPortalRenderer",
|
|
165
|
+
value: function registerPortalRenderer(updater) {
|
|
166
|
+
var _this4 = this;
|
|
167
|
+
if (!this.portalRendererUpdater) {
|
|
168
|
+
updater(function () {
|
|
169
|
+
return _this4.buckets;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
this.portalRendererUpdater = updater;
|
|
173
|
+
}
|
|
174
|
+
}, {
|
|
175
|
+
key: "unregisterPortalRenderer",
|
|
176
|
+
value: function unregisterPortalRenderer() {
|
|
177
|
+
this.portalRendererUpdater = null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Cleans up resources used by the PortalManager. This includes clearing all portals,
|
|
182
|
+
* unregistering all buckets, and resetting internal state.
|
|
183
|
+
*/
|
|
184
|
+
}, {
|
|
185
|
+
key: "destroy",
|
|
186
|
+
value: function destroy() {
|
|
187
|
+
var _this5 = this;
|
|
188
|
+
// Iterate through each bucket and clear its portals and unset the updater function
|
|
189
|
+
this.buckets.forEach(function (bucket, id) {
|
|
190
|
+
bucket.portals = {}; // Clearing all portals from the bucket
|
|
191
|
+
bucket.updater = null; // Unsetting the bucket's updater function
|
|
192
|
+
_this5.availableBuckets.add(id); // Mark all buckets as available
|
|
193
|
+
});
|
|
194
|
+
this.portalToBucketMap.clear();
|
|
195
|
+
this.portalRendererUpdater = null;
|
|
196
|
+
this.availableBuckets = new Set(this.buckets.map(function (_, index) {
|
|
197
|
+
return index;
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
}]);
|
|
201
|
+
return PortalManager;
|
|
202
|
+
}();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { PortalBucket } from './PortalBucket';
|
|
5
|
+
import { PortalManager } from './PortalManager';
|
|
6
|
+
function createPortalRendererComponent(portalManager) {
|
|
7
|
+
return function PortalRenderer() {
|
|
8
|
+
var _useState = useState(portalManager.getBuckets()),
|
|
9
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
10
|
+
buckets = _useState2[0],
|
|
11
|
+
setBuckets = _useState2[1];
|
|
12
|
+
useLayoutEffect(function () {
|
|
13
|
+
portalManager.registerPortalRenderer(setBuckets);
|
|
14
|
+
return function () {
|
|
15
|
+
portalManager.unregisterPortalRenderer();
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
var portalsElements = useMemo(function () {
|
|
19
|
+
return buckets.map(function (_, i) {
|
|
20
|
+
return /*#__PURE__*/React.createElement(PortalBucket, {
|
|
21
|
+
key: i,
|
|
22
|
+
id: i,
|
|
23
|
+
portalManager: portalManager
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}, [buckets]);
|
|
27
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, portalsElements);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Initializes PortalManager and creates PortalRendererComponent. Offers an API (portalProviderAPI) for managing portals.
|
|
33
|
+
* @returns {[PortalProviderAPI, PortalRendererComponent]} An array containing two elements:
|
|
34
|
+
* 1. portalProviderAPI: An object providing an API for rendering and removing portals.
|
|
35
|
+
* 2. PortalRenderer: A React component responsible for rendering the portal content.
|
|
36
|
+
*/
|
|
37
|
+
export function usePortalProvider() {
|
|
38
|
+
var portalManager = useMemo(function () {
|
|
39
|
+
return new PortalManager();
|
|
40
|
+
}, []);
|
|
41
|
+
var PortalRenderer = useMemo(function () {
|
|
42
|
+
return createPortalRendererComponent(portalManager);
|
|
43
|
+
}, [portalManager]);
|
|
44
|
+
var portalProviderAPI = useMemo(function () {
|
|
45
|
+
var portalsMap = new Map();
|
|
46
|
+
return {
|
|
47
|
+
render: function render(key, children, container) {
|
|
48
|
+
var portal = /*#__PURE__*/createPortal(children(), container, key);
|
|
49
|
+
portalsMap.set(key, portalManager.registerPortal(key, portal));
|
|
50
|
+
},
|
|
51
|
+
remove: function remove(key) {
|
|
52
|
+
var _portalsMap$get;
|
|
53
|
+
(_portalsMap$get = portalsMap.get(key)) === null || _portalsMap$get === void 0 || _portalsMap$get();
|
|
54
|
+
portalsMap.delete(key);
|
|
55
|
+
},
|
|
56
|
+
destroy: function destroy() {
|
|
57
|
+
portalsMap.clear();
|
|
58
|
+
portalManager.destroy();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}, [portalManager]);
|
|
62
|
+
|
|
63
|
+
// Cleanup on unmount
|
|
64
|
+
useEffect(function () {
|
|
65
|
+
return function () {
|
|
66
|
+
portalProviderAPI.destroy();
|
|
67
|
+
};
|
|
68
|
+
}, [portalManager, portalProviderAPI]);
|
|
69
|
+
return [portalProviderAPI, PortalRenderer];
|
|
70
|
+
}
|
|
@@ -15,7 +15,7 @@ import { createAndFireEvent, withAnalyticsContext, withAnalyticsEvents } from '@
|
|
|
15
15
|
import { N0, N50A, N60A, N900 } from '@atlaskit/theme/colors';
|
|
16
16
|
import Layer from '../Layer';
|
|
17
17
|
var packageName = "@atlaskit/editor-common";
|
|
18
|
-
var packageVersion = "78.
|
|
18
|
+
var packageVersion = "78.35.0";
|
|
19
19
|
var halfFocusRing = 1;
|
|
20
20
|
var dropOffset = '0, 8';
|
|
21
21
|
var DropList = /*#__PURE__*/function (_Component) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { PortalManager } from './PortalManager';
|
|
3
|
+
type PortalBucketProps = {
|
|
4
|
+
id: number;
|
|
5
|
+
portalManager: PortalManager;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* A component for rendering portals managed by a `PortalManager`.
|
|
9
|
+
* It subscribes to a `PortalManager` instance to listen for changes in the portal content
|
|
10
|
+
* and renders the content of its assigned portal bucket.
|
|
11
|
+
*
|
|
12
|
+
* @param {PortalBucketProps} props The component props.
|
|
13
|
+
* @param {number} props.id The ID for the portal bucket. This ID is used by the `PortalManager` to manage the content of this bucket.
|
|
14
|
+
* @param {PortalManager} props.portalManager An instance of `PortalManager` which manages the registration and unregistration of portal buckets and their content.
|
|
15
|
+
* @returns {React.ReactElement} The React element(s) that are currently registered to this portal bucket.
|
|
16
|
+
*/
|
|
17
|
+
export declare function PortalBucket({ id, portalManager }: PortalBucketProps): JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
type PortalsBucketUpdater = React.Dispatch<React.SetStateAction<Record<React.Key, React.ReactPortal>>>;
|
|
3
|
+
type PortalRendererUpdater = React.Dispatch<React.SetStateAction<Array<PortalBucketType>>>;
|
|
4
|
+
type PortalBucketType = {
|
|
5
|
+
portals: Record<React.Key, React.ReactPortal>;
|
|
6
|
+
capacity: number;
|
|
7
|
+
updater: PortalsBucketUpdater | null;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A utility class to manage and dynamically scale React portals across multiple buckets.
|
|
11
|
+
* It allows for efficient rendering of large numbers of React portals by distributing them
|
|
12
|
+
* across "buckets" and updating these buckets as necessary to balance load and performance.
|
|
13
|
+
*
|
|
14
|
+
* @class PortalManager
|
|
15
|
+
* @typedef {Object} PortalManager
|
|
16
|
+
*
|
|
17
|
+
* @property {number} maxBucketCapacity - The maximum capacity of each bucket before a new bucket is created.
|
|
18
|
+
* @property {number} scaleRatio - The ratio to determine the number of new buckets to add when scaling up.
|
|
19
|
+
* @property {Array<PortalBucketType>} buckets - An array of bucket objects where each bucket holds a record of React portals.
|
|
20
|
+
* @property {Set<number>} availableBuckets - A set of indices representing buckets that have available capacity.
|
|
21
|
+
* @property {Map<React.Key, number>} portalToBucketMap - A map of React portal keys to their corresponding bucket indices.
|
|
22
|
+
* @property {PortalRendererUpdater|null} portalRendererUpdater - A function to trigger updates to the rendering of portals.
|
|
23
|
+
* @property {number} scaleCapacityThreshold - The threshold at which the buckets are scaled up to accommodate more portals.
|
|
24
|
+
*
|
|
25
|
+
* @param {number} [initialBuckets=DEFAULT_INITIAL_BUCKETS] - The initial number of buckets to create.
|
|
26
|
+
* @param {number} [maxBucketCapacity=DEFAULT_MAX_BUCKET_CAPACITY] - The maximum number of portals a single bucket can hold.
|
|
27
|
+
* @param {number} [scaleRatio=DEFAULT_SCALE_RATIO] - The ratio used to calculate the number of new buckets to add when scaling.
|
|
28
|
+
*/
|
|
29
|
+
export declare class PortalManager {
|
|
30
|
+
private maxBucketCapacity;
|
|
31
|
+
private scaleRatio;
|
|
32
|
+
private buckets;
|
|
33
|
+
private availableBuckets;
|
|
34
|
+
private portalToBucketMap;
|
|
35
|
+
private portalRendererUpdater;
|
|
36
|
+
private scaleCapacityThreshold;
|
|
37
|
+
constructor(initialBuckets?: number, maxBucketCapacity?: number, scaleRatio?: number);
|
|
38
|
+
private getCurrentBucket;
|
|
39
|
+
private createBucket;
|
|
40
|
+
getBuckets(): PortalBucketType[];
|
|
41
|
+
registerBucket(id: number, updater: PortalsBucketUpdater): void;
|
|
42
|
+
unregisterBucket(id: number): void;
|
|
43
|
+
updateBuckets(id: number): void;
|
|
44
|
+
registerPortal(key: React.Key, portal: React.ReactPortal): () => void;
|
|
45
|
+
registerPortalRenderer(updater: PortalRendererUpdater): void;
|
|
46
|
+
unregisterPortalRenderer(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Cleans up resources used by the PortalManager. This includes clearing all portals,
|
|
49
|
+
* unregistering all buckets, and resetting internal state.
|
|
50
|
+
*/
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type RenderFn = (key: string, children: () => React.ReactChild | JSX.Element | null, container: HTMLElement) => void;
|
|
3
|
+
type RemoveFn = (key: string) => void;
|
|
4
|
+
interface PortalProviderAPI {
|
|
5
|
+
render: RenderFn;
|
|
6
|
+
remove: RemoveFn;
|
|
7
|
+
destroy: () => void;
|
|
8
|
+
}
|
|
9
|
+
type PortalRendererComponent = () => JSX.Element;
|
|
10
|
+
type UsePortalProviderReturnType = [PortalProviderAPI, PortalRendererComponent];
|
|
11
|
+
/**
|
|
12
|
+
* Initializes PortalManager and creates PortalRendererComponent. Offers an API (portalProviderAPI) for managing portals.
|
|
13
|
+
* @returns {[PortalProviderAPI, PortalRendererComponent]} An array containing two elements:
|
|
14
|
+
* 1. portalProviderAPI: An object providing an API for rendering and removing portals.
|
|
15
|
+
* 2. PortalRenderer: A React component responsible for rendering the portal content.
|
|
16
|
+
*/
|
|
17
|
+
export declare function usePortalProvider(): UsePortalProviderReturnType;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { PortalManager } from './PortalManager';
|
|
3
|
+
type PortalBucketProps = {
|
|
4
|
+
id: number;
|
|
5
|
+
portalManager: PortalManager;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* A component for rendering portals managed by a `PortalManager`.
|
|
9
|
+
* It subscribes to a `PortalManager` instance to listen for changes in the portal content
|
|
10
|
+
* and renders the content of its assigned portal bucket.
|
|
11
|
+
*
|
|
12
|
+
* @param {PortalBucketProps} props The component props.
|
|
13
|
+
* @param {number} props.id The ID for the portal bucket. This ID is used by the `PortalManager` to manage the content of this bucket.
|
|
14
|
+
* @param {PortalManager} props.portalManager An instance of `PortalManager` which manages the registration and unregistration of portal buckets and their content.
|
|
15
|
+
* @returns {React.ReactElement} The React element(s) that are currently registered to this portal bucket.
|
|
16
|
+
*/
|
|
17
|
+
export declare function PortalBucket({ id, portalManager }: PortalBucketProps): JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
type PortalsBucketUpdater = React.Dispatch<React.SetStateAction<Record<React.Key, React.ReactPortal>>>;
|
|
3
|
+
type PortalRendererUpdater = React.Dispatch<React.SetStateAction<Array<PortalBucketType>>>;
|
|
4
|
+
type PortalBucketType = {
|
|
5
|
+
portals: Record<React.Key, React.ReactPortal>;
|
|
6
|
+
capacity: number;
|
|
7
|
+
updater: PortalsBucketUpdater | null;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A utility class to manage and dynamically scale React portals across multiple buckets.
|
|
11
|
+
* It allows for efficient rendering of large numbers of React portals by distributing them
|
|
12
|
+
* across "buckets" and updating these buckets as necessary to balance load and performance.
|
|
13
|
+
*
|
|
14
|
+
* @class PortalManager
|
|
15
|
+
* @typedef {Object} PortalManager
|
|
16
|
+
*
|
|
17
|
+
* @property {number} maxBucketCapacity - The maximum capacity of each bucket before a new bucket is created.
|
|
18
|
+
* @property {number} scaleRatio - The ratio to determine the number of new buckets to add when scaling up.
|
|
19
|
+
* @property {Array<PortalBucketType>} buckets - An array of bucket objects where each bucket holds a record of React portals.
|
|
20
|
+
* @property {Set<number>} availableBuckets - A set of indices representing buckets that have available capacity.
|
|
21
|
+
* @property {Map<React.Key, number>} portalToBucketMap - A map of React portal keys to their corresponding bucket indices.
|
|
22
|
+
* @property {PortalRendererUpdater|null} portalRendererUpdater - A function to trigger updates to the rendering of portals.
|
|
23
|
+
* @property {number} scaleCapacityThreshold - The threshold at which the buckets are scaled up to accommodate more portals.
|
|
24
|
+
*
|
|
25
|
+
* @param {number} [initialBuckets=DEFAULT_INITIAL_BUCKETS] - The initial number of buckets to create.
|
|
26
|
+
* @param {number} [maxBucketCapacity=DEFAULT_MAX_BUCKET_CAPACITY] - The maximum number of portals a single bucket can hold.
|
|
27
|
+
* @param {number} [scaleRatio=DEFAULT_SCALE_RATIO] - The ratio used to calculate the number of new buckets to add when scaling.
|
|
28
|
+
*/
|
|
29
|
+
export declare class PortalManager {
|
|
30
|
+
private maxBucketCapacity;
|
|
31
|
+
private scaleRatio;
|
|
32
|
+
private buckets;
|
|
33
|
+
private availableBuckets;
|
|
34
|
+
private portalToBucketMap;
|
|
35
|
+
private portalRendererUpdater;
|
|
36
|
+
private scaleCapacityThreshold;
|
|
37
|
+
constructor(initialBuckets?: number, maxBucketCapacity?: number, scaleRatio?: number);
|
|
38
|
+
private getCurrentBucket;
|
|
39
|
+
private createBucket;
|
|
40
|
+
getBuckets(): PortalBucketType[];
|
|
41
|
+
registerBucket(id: number, updater: PortalsBucketUpdater): void;
|
|
42
|
+
unregisterBucket(id: number): void;
|
|
43
|
+
updateBuckets(id: number): void;
|
|
44
|
+
registerPortal(key: React.Key, portal: React.ReactPortal): () => void;
|
|
45
|
+
registerPortalRenderer(updater: PortalRendererUpdater): void;
|
|
46
|
+
unregisterPortalRenderer(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Cleans up resources used by the PortalManager. This includes clearing all portals,
|
|
49
|
+
* unregistering all buckets, and resetting internal state.
|
|
50
|
+
*/
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type RenderFn = (key: string, children: () => React.ReactChild | JSX.Element | null, container: HTMLElement) => void;
|
|
3
|
+
type RemoveFn = (key: string) => void;
|
|
4
|
+
interface PortalProviderAPI {
|
|
5
|
+
render: RenderFn;
|
|
6
|
+
remove: RemoveFn;
|
|
7
|
+
destroy: () => void;
|
|
8
|
+
}
|
|
9
|
+
type PortalRendererComponent = () => JSX.Element;
|
|
10
|
+
type UsePortalProviderReturnType = [
|
|
11
|
+
PortalProviderAPI,
|
|
12
|
+
PortalRendererComponent
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Initializes PortalManager and creates PortalRendererComponent. Offers an API (portalProviderAPI) for managing portals.
|
|
16
|
+
* @returns {[PortalProviderAPI, PortalRendererComponent]} An array containing two elements:
|
|
17
|
+
* 1. portalProviderAPI: An object providing an API for rendering and removing portals.
|
|
18
|
+
* 2. PortalRenderer: A React component responsible for rendering the portal content.
|
|
19
|
+
*/
|
|
20
|
+
export declare function usePortalProvider(): UsePortalProviderReturnType;
|
|
21
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-common",
|
|
3
|
-
"version": "78.
|
|
3
|
+
"version": "78.35.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/"
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"./collab": "./src/collab/index.ts",
|
|
50
50
|
"./emoji": "./src/emoji.ts",
|
|
51
51
|
"./mention": "./src/mention.ts",
|
|
52
|
+
"./portal": "./src/portal/index.ts",
|
|
52
53
|
"./provider-helpers": "./src/provider-helpers/index.ts",
|
|
53
54
|
"./icons": "./src/icons/index.ts",
|
|
54
55
|
"./safe-plugin": "./src/safe-plugin/index.ts",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaskit/editor-common/portal",
|
|
3
|
+
"main": "../dist/cjs/portal/index.js",
|
|
4
|
+
"module": "../dist/esm/portal/index.js",
|
|
5
|
+
"module:es2019": "../dist/es2019/portal/index.js",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"types": "../dist/types/portal/index.d.ts",
|
|
8
|
+
"typesVersions": {
|
|
9
|
+
">=4.5 <5.4": {
|
|
10
|
+
"*": [
|
|
11
|
+
"../dist/types-ts4.5/portal/index.d.ts"
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|