@atlaskit/react-select 4.0.2 → 4.1.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 +26 -0
- package/dist/cjs/components/menu-placer.js +7 -0
- package/dist/cjs/components/menu-portal-top-layer.compiled.css +1 -0
- package/dist/cjs/components/menu-portal-top-layer.js +151 -0
- package/dist/cjs/components/menu-portal.js +22 -2
- package/dist/cjs/internal/menu-portal-close-context.js +13 -0
- package/dist/cjs/select.js +157 -54
- package/dist/es2019/components/menu-placer.js +7 -0
- package/dist/es2019/components/menu-portal-top-layer.compiled.css +1 -0
- package/dist/es2019/components/menu-portal-top-layer.js +143 -0
- package/dist/es2019/components/menu-portal.js +22 -2
- package/dist/es2019/internal/menu-portal-close-context.js +8 -0
- package/dist/es2019/select.js +106 -11
- package/dist/esm/components/menu-placer.js +7 -0
- package/dist/esm/components/menu-portal-top-layer.compiled.css +1 -0
- package/dist/esm/components/menu-portal-top-layer.js +142 -0
- package/dist/esm/components/menu-portal.js +22 -2
- package/dist/esm/internal/menu-portal-close-context.js +8 -0
- package/dist/esm/select.js +157 -54
- package/dist/types/components/menu-portal-top-layer.d.ts +21 -0
- package/dist/types/components/menu-portal.d.ts +6 -1
- package/dist/types/internal/menu-portal-close-context.d.ts +7 -0
- package/dist/types/select.d.ts +34 -0
- package/package.json +13 -2
package/dist/cjs/select.js
CHANGED
|
@@ -20,6 +20,7 @@ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits
|
|
|
20
20
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
21
21
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
22
22
|
var _react = _interopRequireWildcard(require("react"));
|
|
23
|
+
var _bindEventListener = require("bind-event-listener");
|
|
23
24
|
var _deviceCheck = require("@atlaskit/ds-lib/device-check");
|
|
24
25
|
var _isSafari = require("@atlaskit/ds-lib/is-safari");
|
|
25
26
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
@@ -33,6 +34,7 @@ var _filters = require("./filters");
|
|
|
33
34
|
var _classnames = require("./internal/classnames");
|
|
34
35
|
var _cleanValue = require("./internal/clean-value");
|
|
35
36
|
var _isDocumentEl = require("./internal/is-document-el");
|
|
37
|
+
var _menuPortalCloseContext = require("./internal/menu-portal-close-context");
|
|
36
38
|
var _multiValueAsValue = require("./internal/multi-value-as-value");
|
|
37
39
|
var _notifyOpenLayerObserver = require("./internal/notify-open-layer-observer");
|
|
38
40
|
var _requiredInput = _interopRequireDefault(require("./internal/required-input"));
|
|
@@ -336,7 +338,8 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
336
338
|
prevWasFocused: false,
|
|
337
339
|
inputIsHiddenAfterUpdate: undefined,
|
|
338
340
|
prevProps: undefined,
|
|
339
|
-
instancePrefix: ''
|
|
341
|
+
instancePrefix: '',
|
|
342
|
+
controlElement: null
|
|
340
343
|
});
|
|
341
344
|
// Misc. Instance Properties
|
|
342
345
|
// ------------------------------
|
|
@@ -347,11 +350,26 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
347
350
|
(0, _defineProperty2.default)(_this, "initialTouchY", 0);
|
|
348
351
|
(0, _defineProperty2.default)(_this, "openAfterFocus", false);
|
|
349
352
|
(0, _defineProperty2.default)(_this, "scrollToFocusedOptionOnUpdate", false);
|
|
353
|
+
// Cleanup for a pending document `pointerup` listener registered by
|
|
354
|
+
// `openMenuAfterPointerUp`. See that method for the full rationale.
|
|
355
|
+
(0, _defineProperty2.default)(_this, "deferredOpenMenuCleanup", null);
|
|
350
356
|
// Refs
|
|
351
357
|
// ------------------------------
|
|
352
358
|
(0, _defineProperty2.default)(_this, "controlRef", null);
|
|
353
359
|
(0, _defineProperty2.default)(_this, "getControlRef", function (ref) {
|
|
354
360
|
_this.controlRef = ref;
|
|
361
|
+
// Mirror the ref into state on the top-layer path so
|
|
362
|
+
// `MenuPortalTopLayer`'s layout effects react to the anchor attaching.
|
|
363
|
+
// Skip the null-ref (unmount) case: setState during unmount is unsafe
|
|
364
|
+
// and the state is dropped with the instance anyway.
|
|
365
|
+
if (ref === null) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (_this.state.controlElement !== ref && (0, _platformFeatureFlags.fg)('platform-dst-top-layer')) {
|
|
369
|
+
_this.setState({
|
|
370
|
+
controlElement: ref
|
|
371
|
+
});
|
|
372
|
+
}
|
|
355
373
|
});
|
|
356
374
|
(0, _defineProperty2.default)(_this, "focusedOptionRef", null);
|
|
357
375
|
(0, _defineProperty2.default)(_this, "getFocusedOptionRef", function (ref) {
|
|
@@ -562,7 +580,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
562
580
|
_this.focusInput();
|
|
563
581
|
} else if (!_this.props.menuIsOpen) {
|
|
564
582
|
if (openMenuOnClick) {
|
|
565
|
-
_this.
|
|
583
|
+
_this.openMenuAfterPointerUp('first');
|
|
566
584
|
}
|
|
567
585
|
} else {
|
|
568
586
|
if (event.target.tagName !== 'INPUT' && event.target.tagName !== 'TEXTAREA') {
|
|
@@ -591,7 +609,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
591
609
|
});
|
|
592
610
|
_this.onMenuClose();
|
|
593
611
|
} else {
|
|
594
|
-
_this.
|
|
612
|
+
_this.openMenuAfterPointerUp('first');
|
|
595
613
|
}
|
|
596
614
|
event.preventDefault();
|
|
597
615
|
});
|
|
@@ -709,7 +727,14 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
709
727
|
isFocused: true
|
|
710
728
|
});
|
|
711
729
|
if (_this.openAfterFocus || _this.props.openMenuOnFocus) {
|
|
712
|
-
|
|
730
|
+
// `openAfterFocus` always follows a pointer gesture, so defer past
|
|
731
|
+
// pointerup. `openMenuOnFocus` alone can come from a keyboard tab
|
|
732
|
+
// with no pointer gesture in flight, so open synchronously.
|
|
733
|
+
if (_this.openAfterFocus) {
|
|
734
|
+
_this.openMenuAfterPointerUp('first');
|
|
735
|
+
} else {
|
|
736
|
+
_this.openMenu('first');
|
|
737
|
+
}
|
|
713
738
|
}
|
|
714
739
|
_this.openAfterFocus = false;
|
|
715
740
|
});
|
|
@@ -1000,6 +1025,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1000
1025
|
value: function componentWillUnmount() {
|
|
1001
1026
|
this.stopListeningComposition();
|
|
1002
1027
|
this.stopListeningToTouch();
|
|
1028
|
+
this.cancelDeferredOpenMenu();
|
|
1003
1029
|
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
1004
1030
|
document.removeEventListener('scroll', this.onScroll, true);
|
|
1005
1031
|
}
|
|
@@ -1046,10 +1072,72 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1046
1072
|
}
|
|
1047
1073
|
this.inputRef.blur();
|
|
1048
1074
|
}
|
|
1075
|
+
}, {
|
|
1076
|
+
key: "shouldDeferOpenPastPointerUp",
|
|
1077
|
+
value:
|
|
1078
|
+
/**
|
|
1079
|
+
* Whether to defer the menu open past the in-flight pointer gesture.
|
|
1080
|
+
* Any renderer that drives a `popover="auto"` element must, otherwise
|
|
1081
|
+
* the browser's light-dismiss runs on the matching `pointerup` and
|
|
1082
|
+
* closes the menu immediately. Today only the top-layer path needs it.
|
|
1083
|
+
*/
|
|
1084
|
+
function shouldDeferOpenPastPointerUp() {
|
|
1085
|
+
return (0, _platformFeatureFlags.fg)('platform-dst-top-layer');
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Open the menu after the current pointer gesture, instead of
|
|
1090
|
+
* synchronously inside `mousedown`.
|
|
1091
|
+
*
|
|
1092
|
+
* On the top-layer path the menu is a `popover="auto"` element. The
|
|
1093
|
+
* browser captures the pointerdown target before the popover exists, so
|
|
1094
|
+
* opening synchronously gets immediately light-dismissed on pointerup
|
|
1095
|
+
* (and the matching `beforetoggle: closed` is not cancellable). Deferring
|
|
1096
|
+
* to the next `pointerup` avoids that.
|
|
1097
|
+
*
|
|
1098
|
+
* We listen for `pointerup` rather than `click` because `pointerup` is
|
|
1099
|
+
* the exact event the browser uses for light-dismiss (earliest safe
|
|
1100
|
+
* moment), always fires (`click` requires same down/up target), is hard
|
|
1101
|
+
* to lose to upstream `stopPropagation`, and is uniform across input
|
|
1102
|
+
* types. Off the top-layer path we open synchronously as before.
|
|
1103
|
+
*/
|
|
1104
|
+
}, {
|
|
1105
|
+
key: "openMenuAfterPointerUp",
|
|
1106
|
+
value: function openMenuAfterPointerUp(focusOption) {
|
|
1107
|
+
var _this2 = this;
|
|
1108
|
+
if (!this.shouldDeferOpenPastPointerUp()) {
|
|
1109
|
+
this.openMenu(focusOption);
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
// A second pointerdown can land before the queued pointerup if the
|
|
1113
|
+
// user releases and re-clicks very quickly. Replace any pending
|
|
1114
|
+
// deferred open with the latest one so we never stack listeners.
|
|
1115
|
+
this.cancelDeferredOpenMenu();
|
|
1116
|
+
var handlePointerUp = function handlePointerUp() {
|
|
1117
|
+
_this2.deferredOpenMenuCleanup = null;
|
|
1118
|
+
_this2.openMenu(focusOption);
|
|
1119
|
+
};
|
|
1120
|
+
this.deferredOpenMenuCleanup = (0, _bindEventListener.bind)(document, {
|
|
1121
|
+
type: 'pointerup',
|
|
1122
|
+
listener: handlePointerUp,
|
|
1123
|
+
options: {
|
|
1124
|
+
capture: true,
|
|
1125
|
+
once: true
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
}, {
|
|
1130
|
+
key: "cancelDeferredOpenMenu",
|
|
1131
|
+
value: function cancelDeferredOpenMenu() {
|
|
1132
|
+
if (this.deferredOpenMenuCleanup) {
|
|
1133
|
+
this.deferredOpenMenuCleanup();
|
|
1134
|
+
this.deferredOpenMenuCleanup = null;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1049
1137
|
}, {
|
|
1050
1138
|
key: "openMenu",
|
|
1051
1139
|
value: function openMenu(focusOption) {
|
|
1052
|
-
var
|
|
1140
|
+
var _this3 = this;
|
|
1053
1141
|
var _this$state2 = this.state,
|
|
1054
1142
|
selectValue = _this$state2.selectValue,
|
|
1055
1143
|
isFocused = _this$state2.isFocused;
|
|
@@ -1071,25 +1159,25 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1071
1159
|
focusedOption: focusedOption,
|
|
1072
1160
|
focusedOptionId: this.getFocusedOptionId(focusedOption)
|
|
1073
1161
|
}, function () {
|
|
1074
|
-
return
|
|
1162
|
+
return _this3.onMenuOpen();
|
|
1075
1163
|
});
|
|
1076
1164
|
(0, _isSafari.isSafari)() && focusedOption && this.updateInputLabel(this.calculateInputLabel(focusedOption, openAtIndex));
|
|
1077
1165
|
}
|
|
1078
1166
|
}, {
|
|
1079
1167
|
key: "updateInputLabel",
|
|
1080
1168
|
value: function updateInputLabel(inputLabel) {
|
|
1081
|
-
var
|
|
1169
|
+
var _this4 = this;
|
|
1082
1170
|
if (inputLabel) {
|
|
1083
1171
|
var _this$inputRef;
|
|
1084
1172
|
(_this$inputRef = this.inputRef) === null || _this$inputRef === void 0 || _this$inputRef.setAttribute('aria-label', inputLabel);
|
|
1085
1173
|
setTimeout(function () {
|
|
1086
|
-
var normalizedLabel =
|
|
1174
|
+
var normalizedLabel = _this4.props['aria-label'] || _this4.props.label;
|
|
1087
1175
|
if (normalizedLabel) {
|
|
1088
|
-
var
|
|
1089
|
-
(
|
|
1176
|
+
var _this4$inputRef;
|
|
1177
|
+
(_this4$inputRef = _this4.inputRef) === null || _this4$inputRef === void 0 || _this4$inputRef.setAttribute('aria-label', normalizedLabel);
|
|
1090
1178
|
} else {
|
|
1091
|
-
var
|
|
1092
|
-
(
|
|
1179
|
+
var _this4$inputRef2;
|
|
1180
|
+
(_this4$inputRef2 = _this4.inputRef) === null || _this4$inputRef2 === void 0 || _this4$inputRef2.removeAttribute('aria-label');
|
|
1093
1181
|
}
|
|
1094
1182
|
}, 500);
|
|
1095
1183
|
}
|
|
@@ -1097,14 +1185,14 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1097
1185
|
}, {
|
|
1098
1186
|
key: "calculateInputLabel",
|
|
1099
1187
|
value: function calculateInputLabel(focusedOption, optionIndex) {
|
|
1100
|
-
var
|
|
1188
|
+
var _this5 = this;
|
|
1101
1189
|
var options = this.props.options;
|
|
1102
1190
|
var isOptionsGrouped = options === null || options === void 0 ? void 0 : options.every(function (obj) {
|
|
1103
1191
|
return (0, _typeof2.default)(obj) === 'object' && obj !== null && 'options' in obj;
|
|
1104
1192
|
});
|
|
1105
1193
|
var inputLabel = this.getOptionLabel(focusedOption);
|
|
1106
1194
|
var isOptionFocused = function isOptionFocused(option) {
|
|
1107
|
-
return
|
|
1195
|
+
return _this5.getOptionLabel(option) === inputLabel;
|
|
1108
1196
|
};
|
|
1109
1197
|
var groupData = options === null || options === void 0 ? void 0 : options.find(function (option) {
|
|
1110
1198
|
var _groupCandidate$optio, _groupCandidate$optio2;
|
|
@@ -1492,7 +1580,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1492
1580
|
}, {
|
|
1493
1581
|
key: "renderPlaceholderOrValue",
|
|
1494
1582
|
value: function renderPlaceholderOrValue() {
|
|
1495
|
-
var
|
|
1583
|
+
var _this6 = this;
|
|
1496
1584
|
var _this$getComponents2 = this.getComponents(),
|
|
1497
1585
|
MultiValue = _this$getComponents2.MultiValue,
|
|
1498
1586
|
MultiValueContainer = _this$getComponents2.MultiValueContainer,
|
|
@@ -1527,7 +1615,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1527
1615
|
if (isMulti) {
|
|
1528
1616
|
return selectValue.map(function (opt, index) {
|
|
1529
1617
|
var isOptionFocused = opt === focusedValue;
|
|
1530
|
-
var key = "".concat(
|
|
1618
|
+
var key = "".concat(_this6.getOptionLabel(opt), "-").concat(_this6.getOptionValue(opt));
|
|
1531
1619
|
return /*#__PURE__*/_react.default.createElement(MultiValue, (0, _extends2.default)({}, commonProps, {
|
|
1532
1620
|
components: {
|
|
1533
1621
|
Container: MultiValueContainer,
|
|
@@ -1540,10 +1628,10 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1540
1628
|
index: index,
|
|
1541
1629
|
removeProps: _objectSpread(_objectSpread({
|
|
1542
1630
|
onClick: function onClick() {
|
|
1543
|
-
return
|
|
1631
|
+
return _this6.removeValue(opt);
|
|
1544
1632
|
},
|
|
1545
1633
|
onTouchEnd: function onTouchEnd() {
|
|
1546
|
-
return
|
|
1634
|
+
return _this6.removeValue(opt);
|
|
1547
1635
|
},
|
|
1548
1636
|
onMouseDown: function onMouseDown(e) {
|
|
1549
1637
|
e.preventDefault();
|
|
@@ -1551,15 +1639,15 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1551
1639
|
}, testId && {
|
|
1552
1640
|
'data-testid': "".concat(testId, "-select--multivalue-").concat(index, "-remove")
|
|
1553
1641
|
}), {}, {
|
|
1554
|
-
id: "".concat(
|
|
1642
|
+
id: "".concat(_this6.getElementId('selected-value'), "-").concat(index, "-remove")
|
|
1555
1643
|
}),
|
|
1556
1644
|
data: opt,
|
|
1557
1645
|
innerProps: _objectSpread(_objectSpread({}, testId && {
|
|
1558
1646
|
'data-testid': "".concat(testId, "-select--multivalue-").concat(index)
|
|
1559
1647
|
}), {}, {
|
|
1560
|
-
id: "".concat(
|
|
1648
|
+
id: "".concat(_this6.getElementId('selected-value'), "-").concat(index)
|
|
1561
1649
|
})
|
|
1562
|
-
}),
|
|
1650
|
+
}), _this6.formatOptionLabel(opt, 'value'));
|
|
1563
1651
|
});
|
|
1564
1652
|
}
|
|
1565
1653
|
if (inputValue) {
|
|
@@ -1665,7 +1753,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1665
1753
|
}, {
|
|
1666
1754
|
key: "renderMenu",
|
|
1667
1755
|
value: function renderMenu() {
|
|
1668
|
-
var
|
|
1756
|
+
var _this7 = this;
|
|
1669
1757
|
var _this$getComponents6 = this.getComponents(),
|
|
1670
1758
|
Group = _this$getComponents6.Group,
|
|
1671
1759
|
GroupHeading = _this$getComponents6.GroupHeading,
|
|
@@ -1708,19 +1796,19 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1708
1796
|
value = props.value;
|
|
1709
1797
|
var isFocused = focusedOption === data;
|
|
1710
1798
|
var onHover = isDisabled ? undefined : function () {
|
|
1711
|
-
return
|
|
1799
|
+
return _this7.onOptionHover(data);
|
|
1712
1800
|
};
|
|
1713
1801
|
var onSelect = isDisabled ? undefined : function () {
|
|
1714
|
-
return
|
|
1802
|
+
return _this7.selectOption(data);
|
|
1715
1803
|
};
|
|
1716
|
-
var optionId = "".concat(
|
|
1804
|
+
var optionId = "".concat(_this7.getElementId('option'), "-").concat(id);
|
|
1717
1805
|
var innerProps = _objectSpread({
|
|
1718
1806
|
id: optionId,
|
|
1719
1807
|
onClick: onSelect,
|
|
1720
1808
|
onMouseMove: onHover,
|
|
1721
1809
|
onMouseOver: onHover,
|
|
1722
|
-
role:
|
|
1723
|
-
'aria-selected':
|
|
1810
|
+
role: _this7.props['UNSAFE_is_experimental_generic'] ? 'listitem' : 'option',
|
|
1811
|
+
'aria-selected': _this7.props['UNSAFE_is_experimental_generic'] ? undefined : isSelected,
|
|
1724
1812
|
// We don't want aria-disabled if it's false. It's just noisy.
|
|
1725
1813
|
'aria-disabled': !isDisabled ? undefined : isDisabled,
|
|
1726
1814
|
'aria-describedby': headingId
|
|
@@ -1737,8 +1825,8 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1737
1825
|
type: type,
|
|
1738
1826
|
value: value,
|
|
1739
1827
|
isFocused: isFocused,
|
|
1740
|
-
innerRef: isFocused ?
|
|
1741
|
-
}),
|
|
1828
|
+
innerRef: isFocused ? _this7.getFocusedOptionRef : undefined
|
|
1829
|
+
}), _this7.formatOptionLabel(props.data, 'menu'));
|
|
1742
1830
|
};
|
|
1743
1831
|
var menuUI;
|
|
1744
1832
|
if (this.hasOptions()) {
|
|
@@ -1748,7 +1836,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1748
1836
|
var data = item.data,
|
|
1749
1837
|
options = item.options,
|
|
1750
1838
|
groupIndex = item.index;
|
|
1751
|
-
var groupId = "".concat(
|
|
1839
|
+
var groupId = "".concat(_this7.getElementId('group'), "-").concat(groupIndex);
|
|
1752
1840
|
var headingId = "".concat(groupId, "-heading");
|
|
1753
1841
|
return /*#__PURE__*/_react.default.createElement(Group, (0, _extends2.default)({}, commonProps, {
|
|
1754
1842
|
key: groupId,
|
|
@@ -1761,7 +1849,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1761
1849
|
}, testId && {
|
|
1762
1850
|
'data-testid': "".concat(testId, "-select--group-").concat(groupIndex, "-heading")
|
|
1763
1851
|
}),
|
|
1764
|
-
label:
|
|
1852
|
+
label: _this7.formatGroupLabel(item.data)
|
|
1765
1853
|
}), item.options.map(function (option) {
|
|
1766
1854
|
return render(option, "".concat(groupIndex, "-").concat(option.index), headingId);
|
|
1767
1855
|
}));
|
|
@@ -1808,9 +1896,9 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1808
1896
|
return /*#__PURE__*/_react.default.createElement(Menu, (0, _extends2.default)({}, commonProps, menuPlacementProps, {
|
|
1809
1897
|
innerRef: ref,
|
|
1810
1898
|
innerProps: _objectSpread({
|
|
1811
|
-
onMouseDown:
|
|
1812
|
-
onMouseMove:
|
|
1813
|
-
id:
|
|
1899
|
+
onMouseDown: _this7.onMenuMouseDown,
|
|
1900
|
+
onMouseMove: _this7.onMenuMouseMove,
|
|
1901
|
+
id: _this7.props.components.Menu ? _this7.getElementId('listbox') : undefined
|
|
1814
1902
|
}, testId && {
|
|
1815
1903
|
'data-testid': "".concat(testId, "-select--listbox-container")
|
|
1816
1904
|
}),
|
|
@@ -1822,47 +1910,62 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1822
1910
|
onBottomArrive: onMenuScrollToBottom,
|
|
1823
1911
|
lockEnabled: menuShouldBlockScroll
|
|
1824
1912
|
}, function (scrollTargetRef) {
|
|
1825
|
-
var
|
|
1913
|
+
var _this7$inputRef, _this7$inputRef2;
|
|
1826
1914
|
return /*#__PURE__*/_react.default.createElement(MenuList, (0, _extends2.default)({}, commonProps, {
|
|
1827
1915
|
innerRef: function innerRef(instance) {
|
|
1828
|
-
|
|
1916
|
+
_this7.getMenuListRef(instance);
|
|
1829
1917
|
scrollTargetRef(instance);
|
|
1830
1918
|
},
|
|
1831
1919
|
innerProps: _objectSpread(_objectSpread(_objectSpread({
|
|
1832
|
-
role:
|
|
1833
|
-
},
|
|
1834
|
-
'aria-labelledby': ((
|
|
1920
|
+
role: _this7.props['UNSAFE_is_experimental_generic'] ? 'dialog' : 'listbox'
|
|
1921
|
+
}, _this7.props['UNSAFE_is_experimental_generic'] && {
|
|
1922
|
+
'aria-labelledby': ((_this7$inputRef = _this7.inputRef) === null || _this7$inputRef === void 0 ? void 0 : _this7$inputRef.id) || _this7.getElementId('input')
|
|
1835
1923
|
}), {}, {
|
|
1836
|
-
'aria-multiselectable': !commonProps.isMulti ||
|
|
1837
|
-
id:
|
|
1924
|
+
'aria-multiselectable': !commonProps.isMulti || _this7.props['UNSAFE_is_experimental_generic'] ? undefined : commonProps.isMulti,
|
|
1925
|
+
id: _this7.getElementId('listbox')
|
|
1838
1926
|
}, testId && {
|
|
1839
1927
|
'data-testid': "".concat(testId, "-select--listbox")
|
|
1840
|
-
}), (0, _isSafari.isSafari)() && !
|
|
1841
|
-
'aria-describedby': ((
|
|
1928
|
+
}), (0, _isSafari.isSafari)() && !_this7.props['UNSAFE_is_experimental_generic'] && {
|
|
1929
|
+
'aria-describedby': ((_this7$inputRef2 = _this7.inputRef) === null || _this7$inputRef2 === void 0 ? void 0 : _this7$inputRef2.id) || _this7.getElementId('input')
|
|
1842
1930
|
}),
|
|
1843
1931
|
isLoading: isLoading,
|
|
1844
1932
|
maxHeight: maxHeight,
|
|
1845
1933
|
focusedOption: focusedOption
|
|
1846
|
-
}),
|
|
1934
|
+
}), _this7.props['UNSAFE_is_experimental_generic'] ? /*#__PURE__*/_react.default.createElement("div", {
|
|
1847
1935
|
role: "list"
|
|
1848
1936
|
}, menuUI) : menuUI);
|
|
1849
1937
|
}));
|
|
1850
1938
|
});
|
|
1851
1939
|
|
|
1852
|
-
//
|
|
1853
|
-
//
|
|
1854
|
-
//
|
|
1855
|
-
|
|
1940
|
+
// On the top-layer path the menu always portals (into the top layer)
|
|
1941
|
+
// regardless of consumer `menuPortalTarget` / `menuPosition`. Off the
|
|
1942
|
+
// flag we keep the legacy "portal only when needed" behaviour.
|
|
1943
|
+
var shouldPortal = (0, _platformFeatureFlags.fg)('platform-dst-top-layer') || menuPortalTarget || menuPosition === 'fixed';
|
|
1944
|
+
if (!shouldPortal) {
|
|
1945
|
+
return menuElement;
|
|
1946
|
+
}
|
|
1947
|
+
// Top-layer path needs the state mirror so MenuPortalTopLayer re-renders
|
|
1948
|
+
// when the anchor attaches; legacy path keeps the direct ref read.
|
|
1949
|
+
var controlElementForPortal = (0, _platformFeatureFlags.fg)('platform-dst-top-layer') ? this.state.controlElement : this.controlRef;
|
|
1950
|
+
var menuPortal = /*#__PURE__*/_react.default.createElement(MenuPortal, (0, _extends2.default)({}, commonProps, {
|
|
1856
1951
|
appendTo: menuPortalTarget,
|
|
1857
|
-
controlElement:
|
|
1952
|
+
controlElement: controlElementForPortal,
|
|
1858
1953
|
menuPlacement: menuPlacement,
|
|
1859
1954
|
menuPosition: menuPosition
|
|
1860
|
-
}), menuElement)
|
|
1955
|
+
}), menuElement);
|
|
1956
|
+
// The Provider plumbs the close signal to MenuPortalTopLayer; not
|
|
1957
|
+
// needed on the legacy path.
|
|
1958
|
+
if (!(0, _platformFeatureFlags.fg)('platform-dst-top-layer')) {
|
|
1959
|
+
return menuPortal;
|
|
1960
|
+
}
|
|
1961
|
+
return /*#__PURE__*/_react.default.createElement(_menuPortalCloseContext.MenuPortalCloseContext.Provider, {
|
|
1962
|
+
value: this.handleOpenLayerObserverCloseSignal
|
|
1963
|
+
}, menuPortal);
|
|
1861
1964
|
}
|
|
1862
1965
|
}, {
|
|
1863
1966
|
key: "renderFormField",
|
|
1864
1967
|
value: function renderFormField() {
|
|
1865
|
-
var
|
|
1968
|
+
var _this8 = this;
|
|
1866
1969
|
var _this$props12 = this.props,
|
|
1867
1970
|
delimiter = _this$props12.delimiter,
|
|
1868
1971
|
isDisabled = _this$props12.isDisabled,
|
|
@@ -1882,7 +1985,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1882
1985
|
if (isMulti) {
|
|
1883
1986
|
if (delimiter) {
|
|
1884
1987
|
var value = selectValue.map(function (opt) {
|
|
1885
|
-
return
|
|
1988
|
+
return _this8.getOptionValue(opt);
|
|
1886
1989
|
}).join(delimiter);
|
|
1887
1990
|
return /*#__PURE__*/_react.default.createElement("input", {
|
|
1888
1991
|
name: name,
|
|
@@ -1895,7 +1998,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1895
1998
|
key: "i-".concat(i),
|
|
1896
1999
|
name: name,
|
|
1897
2000
|
type: "hidden",
|
|
1898
|
-
value:
|
|
2001
|
+
value: _this8.getOptionValue(opt)
|
|
1899
2002
|
});
|
|
1900
2003
|
}) : /*#__PURE__*/_react.default.createElement("input", {
|
|
1901
2004
|
name: name,
|
|
@@ -2010,7 +2113,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
2010
2113
|
innerProps: _objectSpread({}, testId && {
|
|
2011
2114
|
'data-testid': "".concat(testId, "-select--indicators-container")
|
|
2012
2115
|
})
|
|
2013
|
-
}), this.renderClearIndicator(), this.renderLoadingIndicator(), this.renderDropdownIndicator())), this.renderMenu(), this.renderFormField(), /*#__PURE__*/_react.default.createElement(_notifyOpenLayerObserver.NotifyOpenLayerObserver, {
|
|
2116
|
+
}), this.renderClearIndicator(), this.renderLoadingIndicator(), this.renderDropdownIndicator())), this.renderMenu(), this.renderFormField(), !(0, _platformFeatureFlags.fg)('platform-dst-top-layer') && /*#__PURE__*/_react.default.createElement(_notifyOpenLayerObserver.NotifyOpenLayerObserver, {
|
|
2014
2117
|
isOpen: this.props.menuIsOpen,
|
|
2015
2118
|
onClose: this.handleOpenLayerObserverCloseSignal
|
|
2016
2119
|
})));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useContext, useLayoutEffect, useRef, useState } from 'react';
|
|
2
2
|
import __noop from '@atlaskit/ds-lib/noop';
|
|
3
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
4
|
import { PortalPlacementContext } from '../internal/portal-placement-context';
|
|
4
5
|
const noop = __noop;
|
|
5
6
|
function getScrollParent(element) {
|
|
@@ -255,6 +256,12 @@ const MenuPlacer = props => {
|
|
|
255
256
|
// The minimum height of the control
|
|
256
257
|
const controlHeight = 38;
|
|
257
258
|
useLayoutEffect(() => {
|
|
259
|
+
// When the menu is hosted in the browser top layer, positioning, flipping
|
|
260
|
+
// and viewport-fit are all handled by `@atlaskit/top-layer`. The placer
|
|
261
|
+
// becomes a pass-through that only forwards `maxMenuHeight` as a cap.
|
|
262
|
+
if (fg('platform-dst-top-layer')) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
258
265
|
const menuEl = ref.current;
|
|
259
266
|
if (!menuEl) {
|
|
260
267
|
return;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._ofie1496{max-block-size:100dvh}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* menu-portal-top-layer.tsx generated by @compiled/babel-plugin v0.39.1 */
|
|
2
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
3
|
+
import "./menu-portal-top-layer.compiled.css";
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
6
|
+
import { useCallback, useContext, useRef } from 'react';
|
|
7
|
+
import { cx } from '@compiled/react';
|
|
8
|
+
import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
|
|
9
|
+
import { Popover } from '@atlaskit/top-layer/popover';
|
|
10
|
+
import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
|
|
11
|
+
import { useWidthFromAnchor } from '@atlaskit/top-layer/use-width-from-anchor';
|
|
12
|
+
import { getStyleProps } from '../get-style-props';
|
|
13
|
+
import { MenuPortalCloseContext } from '../internal/menu-portal-close-context';
|
|
14
|
+
// `'auto'` falls through to `'end'`; top-layer's `position-try-fallbacks` flips it if needed.
|
|
15
|
+
function reactSelectEdgeToTopLayerEdge(menuPlacement) {
|
|
16
|
+
return menuPlacement === 'top' ? 'start' : 'end';
|
|
17
|
+
}
|
|
18
|
+
const menuPortalStyles = {
|
|
19
|
+
root: "_ofie1496"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Top-layer host for react-select's menu. Hands positioning, flip, and width
|
|
24
|
+
* to `@atlaskit/top-layer`; ignores `appendTo` / `menuPortalTarget` /
|
|
25
|
+
* `menuPosition`. The browser-dismiss handler is supplied internally by
|
|
26
|
+
* `Select` via `MenuPortalCloseContext`.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <MenuPortalTopLayer controlElement={el} menuPlacement="bottom">
|
|
31
|
+
* <Menu />
|
|
32
|
+
* </MenuPortalTopLayer>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function MenuPortalTopLayer(props) {
|
|
36
|
+
const {
|
|
37
|
+
children,
|
|
38
|
+
controlElement,
|
|
39
|
+
innerProps,
|
|
40
|
+
menuPlacement,
|
|
41
|
+
menuPosition,
|
|
42
|
+
xcss
|
|
43
|
+
} = props;
|
|
44
|
+
// Select's "close the menu" callback, distinct from `Popover.onClose`.
|
|
45
|
+
const closeSelect = useContext(MenuPortalCloseContext);
|
|
46
|
+
const popoverRef = useRef(null);
|
|
47
|
+
|
|
48
|
+
// Top-layer hooks need a RefObject; in-render mutation keeps it in sync
|
|
49
|
+
// with the prop without an effect. The hooks re-read `.current` from
|
|
50
|
+
// `isOpen`-keyed layout effects, so a stable ref identity is fine.
|
|
51
|
+
const anchorRef = useRef(null);
|
|
52
|
+
anchorRef.current = controlElement;
|
|
53
|
+
|
|
54
|
+
// `controlElement` is null on initial mount / SSR until `Select` mirrors
|
|
55
|
+
// its ref into state. While null, every hook below no-ops to avoid DOM
|
|
56
|
+
// reads or registering an unpositioned popover.
|
|
57
|
+
const isAnchored = controlElement !== null;
|
|
58
|
+
|
|
59
|
+
// `gap: 0` matches the legacy MenuPortal (no trigger-to-menu gap; the
|
|
60
|
+
// menu root already declares its own `marginBlockStart`). Without this
|
|
61
|
+
// override `useAnchorPosition`'s default 8px gap would diverge visually
|
|
62
|
+
// from the legacy path.
|
|
63
|
+
useAnchorPosition({
|
|
64
|
+
anchorRef,
|
|
65
|
+
popoverRef,
|
|
66
|
+
placement: {
|
|
67
|
+
axis: 'block',
|
|
68
|
+
edge: reactSelectEdgeToTopLayerEdge(menuPlacement),
|
|
69
|
+
offset: {
|
|
70
|
+
gap: 0
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
isOpen: isAnchored
|
|
74
|
+
});
|
|
75
|
+
useWidthFromAnchor({
|
|
76
|
+
anchorRef,
|
|
77
|
+
popoverRef,
|
|
78
|
+
mode: 'match-anchor',
|
|
79
|
+
isOpen: isAnchored
|
|
80
|
+
});
|
|
81
|
+
const handlePopoverClose = useCallback(() => {
|
|
82
|
+
if (closeSelect) {
|
|
83
|
+
closeSelect();
|
|
84
|
+
}
|
|
85
|
+
}, [closeSelect]);
|
|
86
|
+
|
|
87
|
+
// Explicit observer registration: the outer Popover is intentionally
|
|
88
|
+
// roleless (see Popover comment below), so Popover cannot auto-register
|
|
89
|
+
// from its role. This lets `closeLayers()` (Modal / Drawer) dismiss us.
|
|
90
|
+
useNotifyOpenLayerObserver({
|
|
91
|
+
type: 'popup',
|
|
92
|
+
isOpen: isAnchored,
|
|
93
|
+
onClose: handlePopoverClose
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Top-layer owns positioning; zero offset/rect preserves the consumer
|
|
97
|
+
// styles call shape.
|
|
98
|
+
const {
|
|
99
|
+
className
|
|
100
|
+
} = getStyleProps({
|
|
101
|
+
...props,
|
|
102
|
+
offset: 0,
|
|
103
|
+
position: menuPosition,
|
|
104
|
+
rect: {
|
|
105
|
+
left: 0,
|
|
106
|
+
width: 0
|
|
107
|
+
}
|
|
108
|
+
}, 'menuPortal', {
|
|
109
|
+
'menu-portal': true
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Popover stays mounted and is driven by `isOpen` so its layout-effect
|
|
113
|
+
// teardown (`hidePopover`, observer cleanup, position-hook style reset)
|
|
114
|
+
// runs against a live element. Conditional render would skip that path.
|
|
115
|
+
//
|
|
116
|
+
// `mode="manual"` opts out of native light-dismiss: react-select already
|
|
117
|
+
// owns outside-click and Escape via its own handlers, and the combobox
|
|
118
|
+
// trigger lives in a separate DOM subtree that the spec algorithm cannot
|
|
119
|
+
// see. Matches the pattern in `@atlaskit/datetime-picker`'s MenuTopLayer.
|
|
120
|
+
//
|
|
121
|
+
// The Popover host is intentionally roleless: the inner `MenuList`
|
|
122
|
+
// keeps `role="listbox"` and the id referenced by `aria-controls`.
|
|
123
|
+
// Putting the role on the outer host caused Playwright `toBeVisible`
|
|
124
|
+
// and some SR hit-testing to treat it as hidden (zero bounding rect
|
|
125
|
+
// while `styles.root` opts out of UA `[popover]` positioning).
|
|
126
|
+
//
|
|
127
|
+
// TODO: add open / close animation. Select unmounts MenuPortalTopLayer
|
|
128
|
+
// the moment `menuIsOpen` flips false, so Popover's `animate` exit
|
|
129
|
+
// transition never gets a frame. Needs keeping the portal mounted
|
|
130
|
+
// through the exit (`onExitFinish`) on the Select side.
|
|
131
|
+
return /*#__PURE__*/React.createElement(Popover, {
|
|
132
|
+
ref: popoverRef,
|
|
133
|
+
mode: "manual",
|
|
134
|
+
isOpen: isAnchored,
|
|
135
|
+
onClose: handlePopoverClose
|
|
136
|
+
}, /*#__PURE__*/React.createElement("div", _extends({
|
|
137
|
+
// `className` carries consumer `styles.menuPortal({...})` output,
|
|
138
|
+
// `xcss` carries caller compiled atomic classes, and `-MenuPortal`
|
|
139
|
+
// is a legacy selector hook kept for parity with MenuPortalLegacy.
|
|
140
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop, @atlaskit/ui-styling-standard/local-cx-xcss, @compiled/local-cx-xcss, @typescript-eslint/no-explicit-any
|
|
141
|
+
className: ax([menuPortalStyles.root, cx(className, xcss, '-MenuPortal')])
|
|
142
|
+
}, innerProps), children));
|
|
143
|
+
}
|
|
@@ -7,8 +7,10 @@ import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
|
7
7
|
import { cx } from '@compiled/react';
|
|
8
8
|
import { autoUpdate } from '@floating-ui/dom';
|
|
9
9
|
import { createPortal } from 'react-dom';
|
|
10
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
10
11
|
import { getStyleProps } from '../get-style-props';
|
|
11
12
|
import { PortalPlacementContext } from '../internal/portal-placement-context';
|
|
13
|
+
import { MenuPortalTopLayer } from './menu-portal-top-layer';
|
|
12
14
|
function getBoundingClientObj(element) {
|
|
13
15
|
const rect = element.getBoundingClientRect();
|
|
14
16
|
return {
|
|
@@ -25,7 +27,7 @@ const menuPortalStyles = {
|
|
|
25
27
|
root: "_1pbykb7n _1e02a1vk _kqswcp1v _152t1nmo _1bsb1qxj"
|
|
26
28
|
};
|
|
27
29
|
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
28
|
-
|
|
30
|
+
function MenuPortalLegacy(props) {
|
|
29
31
|
const {
|
|
30
32
|
appendTo,
|
|
31
33
|
children,
|
|
@@ -78,7 +80,10 @@ export const MenuPortal = props => {
|
|
|
78
80
|
runAutoUpdate();
|
|
79
81
|
}, [runAutoUpdate]);
|
|
80
82
|
|
|
81
|
-
//
|
|
83
|
+
// Legacy quirk: `computedPosition` is null until the layout effect runs,
|
|
84
|
+
// so the first render returns null even with `defaultMenuIsOpen` set.
|
|
85
|
+
// Synchronous observers (VR snapshots) see a one-frame "closed" state.
|
|
86
|
+
// Left as-is; the top-layer path supersedes this and positions declaratively.
|
|
82
87
|
if (!appendTo && menuPosition !== 'fixed' || !computedPosition) {
|
|
83
88
|
return null;
|
|
84
89
|
}
|
|
@@ -112,4 +117,19 @@ export const MenuPortal = props => {
|
|
|
112
117
|
return /*#__PURE__*/React.createElement(PortalPlacementContext.Provider, {
|
|
113
118
|
value: portalPlacementContext
|
|
114
119
|
}, appendTo ? /*#__PURE__*/createPortal(menuWrapper, appendTo) : menuWrapper);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Public-facing `MenuPortal` component. Routes between the legacy
|
|
124
|
+
* `createPortal`-based implementation and the top-layer-based
|
|
125
|
+
* `MenuPortalTopLayer` based on the `platform-dst-top-layer` feature flag.
|
|
126
|
+
*/
|
|
127
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
128
|
+
export const MenuPortal = props => {
|
|
129
|
+
if (fg('platform-dst-top-layer')) {
|
|
130
|
+
// eslint-disable-next-line @repo/internal/react/no-unsafe-spread-props
|
|
131
|
+
return /*#__PURE__*/React.createElement(MenuPortalTopLayer, props);
|
|
132
|
+
}
|
|
133
|
+
// eslint-disable-next-line @repo/internal/react/no-unsafe-spread-props
|
|
134
|
+
return /*#__PURE__*/React.createElement(MenuPortalLegacy, props);
|
|
115
135
|
};
|