@atlaskit/react-select 2.2.0 → 2.2.1
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 +8 -0
- package/dist/cjs/emotion/components/containers.js +111 -0
- package/dist/cjs/emotion/components/control.js +110 -0
- package/dist/cjs/emotion/components/group.js +71 -0
- package/dist/cjs/emotion/components/index.js +52 -0
- package/dist/cjs/emotion/components/indicators.js +137 -0
- package/dist/cjs/emotion/components/input.js +94 -0
- package/dist/cjs/emotion/components/internal/a11y-text.js +36 -0
- package/dist/cjs/emotion/components/internal/dummy-input.js +44 -0
- package/dist/cjs/emotion/components/internal/index.js +34 -0
- package/dist/cjs/emotion/components/internal/notify-open-layer-observer.js +21 -0
- package/dist/cjs/emotion/components/internal/required-input.js +43 -0
- package/dist/cjs/emotion/components/internal/scroll-manager.js +57 -0
- package/dist/cjs/emotion/components/internal/use-scroll-capture.js +132 -0
- package/dist/cjs/emotion/components/internal/use-scroll-lock.js +149 -0
- package/dist/cjs/emotion/components/live-region.js +182 -0
- package/dist/cjs/emotion/components/menu.js +456 -0
- package/dist/cjs/emotion/components/multi-value.js +224 -0
- package/dist/cjs/emotion/components/option.js +82 -0
- package/dist/cjs/emotion/components/placeholder.js +34 -0
- package/dist/cjs/emotion/components/single-value.js +40 -0
- package/dist/es2019/emotion/components/containers.js +109 -0
- package/dist/es2019/emotion/components/control.js +107 -0
- package/dist/es2019/emotion/components/group.js +59 -0
- package/dist/es2019/emotion/components/index.js +41 -0
- package/dist/es2019/emotion/components/indicators.js +128 -0
- package/dist/es2019/emotion/components/input.js +86 -0
- package/dist/es2019/emotion/components/internal/a11y-text.js +27 -0
- package/dist/es2019/emotion/components/internal/dummy-input.js +37 -0
- package/dist/es2019/emotion/components/internal/index.js +4 -0
- package/dist/es2019/emotion/components/internal/notify-open-layer-observer.js +16 -0
- package/dist/es2019/emotion/components/internal/required-input.js +35 -0
- package/dist/es2019/emotion/components/internal/scroll-manager.js +49 -0
- package/dist/es2019/emotion/components/internal/use-scroll-capture.js +128 -0
- package/dist/es2019/emotion/components/internal/use-scroll-lock.js +143 -0
- package/dist/es2019/emotion/components/live-region.js +178 -0
- package/dist/es2019/emotion/components/menu.js +450 -0
- package/dist/es2019/emotion/components/multi-value.js +227 -0
- package/dist/es2019/emotion/components/option.js +78 -0
- package/dist/es2019/emotion/components/placeholder.js +28 -0
- package/dist/es2019/emotion/components/single-value.js +34 -0
- package/dist/esm/emotion/components/containers.js +105 -0
- package/dist/esm/emotion/components/control.js +103 -0
- package/dist/esm/emotion/components/group.js +65 -0
- package/dist/esm/emotion/components/index.js +43 -0
- package/dist/esm/emotion/components/indicators.js +132 -0
- package/dist/esm/emotion/components/input.js +89 -0
- package/dist/esm/emotion/components/internal/a11y-text.js +29 -0
- package/dist/esm/emotion/components/internal/dummy-input.js +38 -0
- package/dist/esm/emotion/components/internal/index.js +4 -0
- package/dist/esm/emotion/components/internal/notify-open-layer-observer.js +15 -0
- package/dist/esm/emotion/components/internal/required-input.js +36 -0
- package/dist/esm/emotion/components/internal/scroll-manager.js +49 -0
- package/dist/esm/emotion/components/internal/use-scroll-capture.js +126 -0
- package/dist/esm/emotion/components/internal/use-scroll-lock.js +143 -0
- package/dist/esm/emotion/components/live-region.js +175 -0
- package/dist/esm/emotion/components/menu.js +454 -0
- package/dist/esm/emotion/components/multi-value.js +217 -0
- package/dist/esm/emotion/components/option.js +75 -0
- package/dist/esm/emotion/components/placeholder.js +27 -0
- package/dist/esm/emotion/components/single-value.js +33 -0
- package/dist/types/emotion/components/containers.d.ts +54 -0
- package/dist/types/emotion/components/control.d.ts +42 -0
- package/dist/types/emotion/components/group.d.ts +52 -0
- package/dist/types/emotion/components/index.d.ts +67 -0
- package/dist/types/emotion/components/indicators.d.ts +73 -0
- package/dist/types/emotion/components/input.d.ts +37 -0
- package/dist/types/emotion/components/internal/a11y-text.d.ts +8 -0
- package/dist/types/emotion/components/internal/dummy-input.d.ts +9 -0
- package/dist/types/emotion/components/internal/index.d.ts +4 -0
- package/dist/types/emotion/components/internal/notify-open-layer-observer.d.ts +11 -0
- package/dist/types/emotion/components/internal/required-input.d.ts +10 -0
- package/dist/types/emotion/components/internal/scroll-manager.d.ts +17 -0
- package/dist/types/emotion/components/internal/use-scroll-capture.d.ts +12 -0
- package/dist/types/emotion/components/internal/use-scroll-lock.d.ts +9 -0
- package/dist/types/emotion/components/live-region.d.ts +25 -0
- package/dist/types/emotion/components/menu.d.ts +116 -0
- package/dist/types/emotion/components/multi-value.d.ts +47 -0
- package/dist/types/emotion/components/option.d.ts +49 -0
- package/dist/types/emotion/components/placeholder.d.ts +22 -0
- package/dist/types/emotion/components/single-value.d.ts +28 -0
- package/dist/types-ts4.5/emotion/components/containers.d.ts +54 -0
- package/dist/types-ts4.5/emotion/components/control.d.ts +42 -0
- package/dist/types-ts4.5/emotion/components/group.d.ts +52 -0
- package/dist/types-ts4.5/emotion/components/index.d.ts +67 -0
- package/dist/types-ts4.5/emotion/components/indicators.d.ts +73 -0
- package/dist/types-ts4.5/emotion/components/input.d.ts +37 -0
- package/dist/types-ts4.5/emotion/components/internal/a11y-text.d.ts +8 -0
- package/dist/types-ts4.5/emotion/components/internal/dummy-input.d.ts +9 -0
- package/dist/types-ts4.5/emotion/components/internal/index.d.ts +4 -0
- package/dist/types-ts4.5/emotion/components/internal/notify-open-layer-observer.d.ts +11 -0
- package/dist/types-ts4.5/emotion/components/internal/required-input.d.ts +10 -0
- package/dist/types-ts4.5/emotion/components/internal/scroll-manager.d.ts +17 -0
- package/dist/types-ts4.5/emotion/components/internal/use-scroll-capture.d.ts +12 -0
- package/dist/types-ts4.5/emotion/components/internal/use-scroll-lock.d.ts +9 -0
- package/dist/types-ts4.5/emotion/components/live-region.d.ts +25 -0
- package/dist/types-ts4.5/emotion/components/menu.d.ts +116 -0
- package/dist/types-ts4.5/emotion/components/multi-value.d.ts +47 -0
- package/dist/types-ts4.5/emotion/components/option.d.ts +49 -0
- package/dist/types-ts4.5/emotion/components/placeholder.d.ts +22 -0
- package/dist/types-ts4.5/emotion/components/single-value.d.ts +28 -0
- package/package.json +1 -1
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
/**
|
|
3
|
+
* @jsxRuntime classic
|
|
4
|
+
* @jsx jsx
|
|
5
|
+
*/
|
|
6
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useState } from 'react';
|
|
7
|
+
import { jsx } from '@emotion/react';
|
|
8
|
+
import { autoUpdate } from '@floating-ui/dom';
|
|
9
|
+
import { createPortal } from 'react-dom';
|
|
10
|
+
import useLayoutEffect from 'use-isomorphic-layout-effect';
|
|
11
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
12
|
+
import { Text } from '@atlaskit/primitives';
|
|
13
|
+
import { animatedScrollTo, getBoundingClientObj, getScrollParent, getScrollTop, getStyleProps, normalizedHeight, scrollTo } from '../../utils';
|
|
14
|
+
|
|
15
|
+
// ==============================
|
|
16
|
+
// Menu
|
|
17
|
+
// ==============================
|
|
18
|
+
|
|
19
|
+
// Get Menu Placement
|
|
20
|
+
// ------------------------------
|
|
21
|
+
|
|
22
|
+
function getMenuPlacement({
|
|
23
|
+
maxHeight: preferredMaxHeight,
|
|
24
|
+
menuEl,
|
|
25
|
+
minHeight,
|
|
26
|
+
placement: preferredPlacement,
|
|
27
|
+
shouldScroll,
|
|
28
|
+
isFixedPosition,
|
|
29
|
+
controlHeight
|
|
30
|
+
}) {
|
|
31
|
+
const scrollParent = getScrollParent(menuEl);
|
|
32
|
+
const defaultState = {
|
|
33
|
+
placement: 'bottom',
|
|
34
|
+
maxHeight: preferredMaxHeight
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// something went wrong, return default state
|
|
38
|
+
if (!menuEl || !menuEl.offsetParent) {
|
|
39
|
+
return defaultState;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// we can't trust `scrollParent.scrollHeight` --> it may increase when
|
|
43
|
+
// the menu is rendered
|
|
44
|
+
const {
|
|
45
|
+
height: scrollHeight,
|
|
46
|
+
top: scrollParentTop
|
|
47
|
+
} = scrollParent.getBoundingClientRect();
|
|
48
|
+
const {
|
|
49
|
+
bottom: menuBottom,
|
|
50
|
+
height: menuHeight,
|
|
51
|
+
top: menuTop
|
|
52
|
+
} = menuEl.getBoundingClientRect();
|
|
53
|
+
const {
|
|
54
|
+
top: containerTop
|
|
55
|
+
} = menuEl.offsetParent.getBoundingClientRect();
|
|
56
|
+
const viewHeight = isFixedPosition ? window.innerHeight : normalizedHeight(scrollParent);
|
|
57
|
+
const scrollTop = getScrollTop(scrollParent);
|
|
58
|
+
// use menuTop - scrollParentTop for the actual top space of menu in the scroll container
|
|
59
|
+
const menuTopFromParent = fg('design-system-select-fix-placement') ? menuTop - scrollParentTop : menuTop;
|
|
60
|
+
const marginBottom = parseInt(getComputedStyle(menuEl).marginBottom, 10);
|
|
61
|
+
const marginTop = parseInt(getComputedStyle(menuEl).marginTop, 10);
|
|
62
|
+
const viewSpaceAbove = containerTop - marginTop;
|
|
63
|
+
const viewSpaceBelow = viewHeight - menuTopFromParent;
|
|
64
|
+
const scrollSpaceAbove = viewSpaceAbove + scrollTop;
|
|
65
|
+
const scrollSpaceBelow = scrollHeight - scrollTop - menuTopFromParent;
|
|
66
|
+
const scrollDown = menuBottom - viewHeight + scrollTop + marginBottom;
|
|
67
|
+
const scrollUp = scrollTop + menuTop - marginTop;
|
|
68
|
+
const scrollDuration = 160;
|
|
69
|
+
switch (preferredPlacement) {
|
|
70
|
+
case 'auto':
|
|
71
|
+
case 'bottom':
|
|
72
|
+
// 1: the menu will fit, do nothing
|
|
73
|
+
if (viewSpaceBelow >= menuHeight) {
|
|
74
|
+
return {
|
|
75
|
+
placement: 'bottom',
|
|
76
|
+
maxHeight: preferredMaxHeight
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 2: the menu will fit, if scrolled
|
|
81
|
+
if (scrollSpaceBelow >= menuHeight && !isFixedPosition) {
|
|
82
|
+
if (shouldScroll) {
|
|
83
|
+
animatedScrollTo(scrollParent, scrollDown, scrollDuration);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
placement: 'bottom',
|
|
87
|
+
maxHeight: preferredMaxHeight
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 3: the menu will fit, if constrained
|
|
92
|
+
if (!isFixedPosition && scrollSpaceBelow >= minHeight || isFixedPosition && viewSpaceBelow >= minHeight) {
|
|
93
|
+
if (shouldScroll) {
|
|
94
|
+
animatedScrollTo(scrollParent, scrollDown, scrollDuration);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// we want to provide as much of the menu as possible to the user,
|
|
98
|
+
// so give them whatever is available below rather than the minHeight.
|
|
99
|
+
const constrainedHeight = isFixedPosition ? viewSpaceBelow - marginBottom : scrollSpaceBelow - marginBottom;
|
|
100
|
+
return {
|
|
101
|
+
placement: 'bottom',
|
|
102
|
+
maxHeight: constrainedHeight
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 4. Forked beviour when there isn't enough space below
|
|
107
|
+
|
|
108
|
+
// AUTO: flip the menu, render above
|
|
109
|
+
if (preferredPlacement === 'auto' || isFixedPosition) {
|
|
110
|
+
// may need to be constrained after flipping
|
|
111
|
+
let constrainedHeight = preferredMaxHeight;
|
|
112
|
+
const spaceAbove = isFixedPosition ? viewSpaceAbove : scrollSpaceAbove;
|
|
113
|
+
if (spaceAbove >= minHeight) {
|
|
114
|
+
constrainedHeight = Math.min(spaceAbove - marginBottom - controlHeight, preferredMaxHeight);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
placement: 'top',
|
|
118
|
+
maxHeight: constrainedHeight
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// BOTTOM: allow browser to increase scrollable area and immediately set scroll
|
|
123
|
+
if (preferredPlacement === 'bottom') {
|
|
124
|
+
if (shouldScroll) {
|
|
125
|
+
scrollTo(scrollParent, scrollDown);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
placement: 'bottom',
|
|
129
|
+
maxHeight: preferredMaxHeight
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
case 'top':
|
|
134
|
+
// 1: the menu will fit, do nothing
|
|
135
|
+
if (viewSpaceAbove >= menuHeight) {
|
|
136
|
+
return {
|
|
137
|
+
placement: 'top',
|
|
138
|
+
maxHeight: preferredMaxHeight
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 2: the menu will fit, if scrolled
|
|
143
|
+
if (scrollSpaceAbove >= menuHeight && !isFixedPosition) {
|
|
144
|
+
if (shouldScroll) {
|
|
145
|
+
animatedScrollTo(scrollParent, scrollUp, scrollDuration);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
placement: 'top',
|
|
149
|
+
maxHeight: preferredMaxHeight
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 3: the menu will fit, if constrained
|
|
154
|
+
if (!isFixedPosition && scrollSpaceAbove >= minHeight || isFixedPosition && viewSpaceAbove >= minHeight) {
|
|
155
|
+
let constrainedHeight = preferredMaxHeight;
|
|
156
|
+
|
|
157
|
+
// we want to provide as much of the menu as possible to the user,
|
|
158
|
+
// so give them whatever is available below rather than the minHeight.
|
|
159
|
+
if (!isFixedPosition && scrollSpaceAbove >= minHeight || isFixedPosition && viewSpaceAbove >= minHeight) {
|
|
160
|
+
constrainedHeight = isFixedPosition ? viewSpaceAbove - marginTop : scrollSpaceAbove - marginTop;
|
|
161
|
+
}
|
|
162
|
+
if (shouldScroll) {
|
|
163
|
+
animatedScrollTo(scrollParent, scrollUp, scrollDuration);
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
placement: 'top',
|
|
167
|
+
maxHeight: constrainedHeight
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 4. not enough space, the browser WILL NOT increase scrollable area when
|
|
172
|
+
// absolutely positioned element rendered above the viewport (only below).
|
|
173
|
+
// Flip the menu, render below
|
|
174
|
+
return {
|
|
175
|
+
placement: 'bottom',
|
|
176
|
+
maxHeight: preferredMaxHeight
|
|
177
|
+
};
|
|
178
|
+
default:
|
|
179
|
+
throw new Error(`Invalid placement provided "${preferredPlacement}".`);
|
|
180
|
+
}
|
|
181
|
+
return defaultState;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Menu Component
|
|
185
|
+
// ------------------------------
|
|
186
|
+
|
|
187
|
+
function alignToControl(placement) {
|
|
188
|
+
const placementToCSSProp = {
|
|
189
|
+
bottom: 'top',
|
|
190
|
+
top: 'bottom'
|
|
191
|
+
};
|
|
192
|
+
return placement ? placementToCSSProp[placement] : 'bottom';
|
|
193
|
+
}
|
|
194
|
+
const coercePlacement = p => p === 'auto' ? 'bottom' : p;
|
|
195
|
+
export const menuCSS = ({
|
|
196
|
+
placement
|
|
197
|
+
}) => ({
|
|
198
|
+
label: 'menu',
|
|
199
|
+
[alignToControl(placement)]: '100%',
|
|
200
|
+
position: 'absolute',
|
|
201
|
+
width: '100%',
|
|
202
|
+
zIndex: 1,
|
|
203
|
+
borderRadius: "var(--ds-border-radius, 4px)",
|
|
204
|
+
marginBottom: "var(--ds-space-100, 8px)",
|
|
205
|
+
marginTop: "var(--ds-space-100, 8px)",
|
|
206
|
+
backgroundColor: "var(--ds-surface-overlay, white)",
|
|
207
|
+
boxShadow: "var(--ds-shadow-overlay, 0 0 0 1px hsl(0deg 0% 0% / 10%), 0 4px 11px hsl(0deg 0% 0% / 10%))"
|
|
208
|
+
});
|
|
209
|
+
const PortalPlacementContext = /*#__PURE__*/createContext(null);
|
|
210
|
+
|
|
211
|
+
// NOTE: internal only
|
|
212
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
213
|
+
export const MenuPlacer = props => {
|
|
214
|
+
const {
|
|
215
|
+
children,
|
|
216
|
+
minMenuHeight,
|
|
217
|
+
maxMenuHeight,
|
|
218
|
+
menuPlacement,
|
|
219
|
+
menuPosition,
|
|
220
|
+
menuShouldScrollIntoView
|
|
221
|
+
} = props;
|
|
222
|
+
const {
|
|
223
|
+
setPortalPlacement
|
|
224
|
+
} = useContext(PortalPlacementContext) || {};
|
|
225
|
+
const ref = useRef(null);
|
|
226
|
+
const [maxHeight, setMaxHeight] = useState(maxMenuHeight);
|
|
227
|
+
const [placement, setPlacement] = useState(null);
|
|
228
|
+
// The minimum height of the control
|
|
229
|
+
const controlHeight = 38;
|
|
230
|
+
useLayoutEffect(() => {
|
|
231
|
+
const menuEl = ref.current;
|
|
232
|
+
if (!menuEl) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// DO NOT scroll if position is fixed
|
|
237
|
+
const isFixedPosition = menuPosition === 'fixed';
|
|
238
|
+
const shouldScroll = menuShouldScrollIntoView && !isFixedPosition;
|
|
239
|
+
const state = getMenuPlacement({
|
|
240
|
+
maxHeight: maxMenuHeight,
|
|
241
|
+
menuEl,
|
|
242
|
+
minHeight: minMenuHeight,
|
|
243
|
+
placement: menuPlacement,
|
|
244
|
+
shouldScroll,
|
|
245
|
+
isFixedPosition,
|
|
246
|
+
controlHeight
|
|
247
|
+
});
|
|
248
|
+
setMaxHeight(state.maxHeight);
|
|
249
|
+
setPlacement(state.placement);
|
|
250
|
+
setPortalPlacement === null || setPortalPlacement === void 0 ? void 0 : setPortalPlacement(state.placement);
|
|
251
|
+
}, [maxMenuHeight, menuPlacement, menuPosition, menuShouldScrollIntoView, minMenuHeight, setPortalPlacement, controlHeight]);
|
|
252
|
+
return children({
|
|
253
|
+
ref,
|
|
254
|
+
placerProps: {
|
|
255
|
+
...props,
|
|
256
|
+
placement: placement || coercePlacement(menuPlacement),
|
|
257
|
+
maxHeight
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
const Menu = props => {
|
|
262
|
+
const {
|
|
263
|
+
children,
|
|
264
|
+
innerRef,
|
|
265
|
+
innerProps
|
|
266
|
+
} = props;
|
|
267
|
+
return jsx("div", _extends({}, getStyleProps(props, 'menu', {
|
|
268
|
+
menu: true
|
|
269
|
+
}), {
|
|
270
|
+
ref: innerRef
|
|
271
|
+
}, innerProps), children);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
275
|
+
export default Menu;
|
|
276
|
+
|
|
277
|
+
// ==============================
|
|
278
|
+
// Menu List
|
|
279
|
+
// ==============================
|
|
280
|
+
|
|
281
|
+
export const menuListCSS = ({
|
|
282
|
+
maxHeight
|
|
283
|
+
}) => ({
|
|
284
|
+
maxHeight,
|
|
285
|
+
overflowY: 'auto',
|
|
286
|
+
position: 'relative',
|
|
287
|
+
// required for offset[Height, Top] > keyboard scroll
|
|
288
|
+
WebkitOverflowScrolling: 'touch',
|
|
289
|
+
paddingTop: "var(--ds-space-100, 8px)",
|
|
290
|
+
paddingBottom: "var(--ds-space-100, 8px)"
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
294
|
+
export const MenuList = props => {
|
|
295
|
+
const {
|
|
296
|
+
children,
|
|
297
|
+
innerProps,
|
|
298
|
+
innerRef,
|
|
299
|
+
isMulti
|
|
300
|
+
} = props;
|
|
301
|
+
return jsx("div", _extends({}, getStyleProps(props, 'menuList', {
|
|
302
|
+
'menu-list': true,
|
|
303
|
+
'menu-list--is-multi': isMulti
|
|
304
|
+
}), {
|
|
305
|
+
ref: innerRef
|
|
306
|
+
}, innerProps, {
|
|
307
|
+
tabIndex: -1
|
|
308
|
+
}), children);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// ==============================
|
|
312
|
+
// Menu Notices
|
|
313
|
+
// ==============================
|
|
314
|
+
|
|
315
|
+
const noticeCSS = ({}) => ({
|
|
316
|
+
textAlign: 'center',
|
|
317
|
+
padding: `${"var(--ds-space-100, 8px)"} ${"var(--ds-space-150, 12px)"}`
|
|
318
|
+
});
|
|
319
|
+
export const noOptionsMessageCSS = noticeCSS;
|
|
320
|
+
export const loadingMessageCSS = noticeCSS;
|
|
321
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
322
|
+
export const NoOptionsMessage = ({
|
|
323
|
+
children = 'No options',
|
|
324
|
+
innerProps,
|
|
325
|
+
...restProps
|
|
326
|
+
}) => {
|
|
327
|
+
return jsx("div", _extends({}, getStyleProps({
|
|
328
|
+
...restProps,
|
|
329
|
+
children,
|
|
330
|
+
innerProps
|
|
331
|
+
}, 'noOptionsMessage', {
|
|
332
|
+
'menu-notice': true,
|
|
333
|
+
'menu-notice--no-options': true
|
|
334
|
+
}), {
|
|
335
|
+
// eslint-disable-next-line jsx-a11y/role-has-required-aria-props
|
|
336
|
+
role: "option"
|
|
337
|
+
}, innerProps), jsx(Text, {
|
|
338
|
+
color: "color.text.subtle"
|
|
339
|
+
}, children));
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
343
|
+
export const LoadingMessage = ({
|
|
344
|
+
children = 'Loading...',
|
|
345
|
+
innerProps,
|
|
346
|
+
...restProps
|
|
347
|
+
}) => {
|
|
348
|
+
return jsx("div", _extends({}, getStyleProps({
|
|
349
|
+
...restProps,
|
|
350
|
+
children,
|
|
351
|
+
innerProps
|
|
352
|
+
}, 'loadingMessage', {
|
|
353
|
+
'menu-notice': true,
|
|
354
|
+
'menu-notice--loading': true
|
|
355
|
+
}), innerProps, {
|
|
356
|
+
// eslint-disable-next-line jsx-a11y/role-has-required-aria-props
|
|
357
|
+
role: "option"
|
|
358
|
+
}), jsx(Text, {
|
|
359
|
+
color: "color.text.subtle"
|
|
360
|
+
}, children));
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// ==============================
|
|
364
|
+
// Menu Portal
|
|
365
|
+
// ==============================
|
|
366
|
+
|
|
367
|
+
export const menuPortalCSS = ({
|
|
368
|
+
rect,
|
|
369
|
+
offset,
|
|
370
|
+
position
|
|
371
|
+
}) => ({
|
|
372
|
+
left: rect.left,
|
|
373
|
+
position: position,
|
|
374
|
+
top: offset,
|
|
375
|
+
width: rect.width,
|
|
376
|
+
zIndex: 1
|
|
377
|
+
});
|
|
378
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
379
|
+
export const MenuPortal = props => {
|
|
380
|
+
const {
|
|
381
|
+
appendTo,
|
|
382
|
+
children,
|
|
383
|
+
controlElement,
|
|
384
|
+
innerProps,
|
|
385
|
+
menuPlacement,
|
|
386
|
+
menuPosition
|
|
387
|
+
} = props;
|
|
388
|
+
const menuPortalRef = useRef(null);
|
|
389
|
+
const cleanupRef = useRef(null);
|
|
390
|
+
const [placement, setPortalPlacement] = useState(coercePlacement(menuPlacement));
|
|
391
|
+
const portalPlacementContext = useMemo(() => ({
|
|
392
|
+
setPortalPlacement
|
|
393
|
+
}), []);
|
|
394
|
+
const [computedPosition, setComputedPosition] = useState(null);
|
|
395
|
+
const updateComputedPosition = useCallback(() => {
|
|
396
|
+
if (!controlElement) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const rect = getBoundingClientObj(controlElement);
|
|
400
|
+
const scrollDistance = menuPosition === 'fixed' ? 0 : window.pageYOffset;
|
|
401
|
+
const offset = rect[placement] + scrollDistance;
|
|
402
|
+
if (offset !== (computedPosition === null || computedPosition === void 0 ? void 0 : computedPosition.offset) || rect.left !== (computedPosition === null || computedPosition === void 0 ? void 0 : computedPosition.rect.left) || rect.width !== (computedPosition === null || computedPosition === void 0 ? void 0 : computedPosition.rect.width)) {
|
|
403
|
+
setComputedPosition({
|
|
404
|
+
offset,
|
|
405
|
+
rect
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}, [controlElement, menuPosition, placement, computedPosition === null || computedPosition === void 0 ? void 0 : computedPosition.offset, computedPosition === null || computedPosition === void 0 ? void 0 : computedPosition.rect.left, computedPosition === null || computedPosition === void 0 ? void 0 : computedPosition.rect.width]);
|
|
409
|
+
useLayoutEffect(() => {
|
|
410
|
+
updateComputedPosition();
|
|
411
|
+
}, [updateComputedPosition]);
|
|
412
|
+
const runAutoUpdate = useCallback(() => {
|
|
413
|
+
if (typeof cleanupRef.current === 'function') {
|
|
414
|
+
cleanupRef.current();
|
|
415
|
+
cleanupRef.current = null;
|
|
416
|
+
}
|
|
417
|
+
if (controlElement && menuPortalRef.current) {
|
|
418
|
+
cleanupRef.current = autoUpdate(controlElement, menuPortalRef.current, updateComputedPosition, {
|
|
419
|
+
elementResize: 'ResizeObserver' in window
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}, [controlElement, updateComputedPosition]);
|
|
423
|
+
useLayoutEffect(() => {
|
|
424
|
+
runAutoUpdate();
|
|
425
|
+
}, [runAutoUpdate]);
|
|
426
|
+
const setMenuPortalElement = useCallback(menuPortalElement => {
|
|
427
|
+
menuPortalRef.current = menuPortalElement;
|
|
428
|
+
runAutoUpdate();
|
|
429
|
+
}, [runAutoUpdate]);
|
|
430
|
+
|
|
431
|
+
// bail early if required elements aren't present
|
|
432
|
+
if (!appendTo && menuPosition !== 'fixed' || !computedPosition) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// same wrapper element whether fixed or portalled
|
|
437
|
+
const menuWrapper = jsx("div", _extends({
|
|
438
|
+
ref: setMenuPortalElement
|
|
439
|
+
}, getStyleProps({
|
|
440
|
+
...props,
|
|
441
|
+
offset: computedPosition.offset,
|
|
442
|
+
position: menuPosition,
|
|
443
|
+
rect: computedPosition.rect
|
|
444
|
+
}, 'menuPortal', {
|
|
445
|
+
'menu-portal': true
|
|
446
|
+
}), innerProps), children);
|
|
447
|
+
return jsx(PortalPlacementContext.Provider, {
|
|
448
|
+
value: portalPlacementContext
|
|
449
|
+
}, appendTo ? /*#__PURE__*/createPortal(menuWrapper, appendTo) : menuWrapper);
|
|
450
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jsxRuntime classic
|
|
3
|
+
* @jsx jsx
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { css, jsx } from '@emotion/react';
|
|
7
|
+
import LegacySelectClearIcon from '@atlaskit/icon/glyph/select-clear';
|
|
8
|
+
import CrossIcon from '@atlaskit/icon/utility/cross';
|
|
9
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
10
|
+
import { Inline, xcss } from '@atlaskit/primitives';
|
|
11
|
+
import { getStyleProps } from '../../utils';
|
|
12
|
+
export const multiValueCSS = ({
|
|
13
|
+
isDisabled,
|
|
14
|
+
isFocused
|
|
15
|
+
}) => {
|
|
16
|
+
let backgroundColor;
|
|
17
|
+
let color;
|
|
18
|
+
if (isDisabled) {
|
|
19
|
+
// Use the basic neutral background so it is slightly separate from the
|
|
20
|
+
// field's background
|
|
21
|
+
backgroundColor = "var(--ds-background-neutral, #091E420F)";
|
|
22
|
+
color = "var(--ds-text-disabled, #091E424F)";
|
|
23
|
+
} else if (isFocused) {
|
|
24
|
+
backgroundColor = "var(--ds-background-selected, #E9F2FF)";
|
|
25
|
+
color = "var(--ds-text-selected, hsl(0, 0%, 20%))";
|
|
26
|
+
} else {
|
|
27
|
+
backgroundColor = fg('platform-component-visual-refresh') ? "var(--ds-background-neutral-subtle-hovered, #091E420F)" : "var(--ds-background-neutral, #091E420F)";
|
|
28
|
+
color = "var(--ds-text, hsl(0, 0%, 20%))";
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
label: 'multiValue',
|
|
32
|
+
display: 'flex',
|
|
33
|
+
minWidth: 0,
|
|
34
|
+
// resolves flex/text-overflow bug
|
|
35
|
+
margin: "var(--ds-space-025, 2px)",
|
|
36
|
+
borderRadius: "var(--ds-border-radius-050, 2px)",
|
|
37
|
+
backgroundColor,
|
|
38
|
+
boxShadow: isFocused ? `0 0 0 2px ${"var(--ds-surface, transparent)"}, 0 0 0 4px ${"var(--ds-border-focused, transparent)"}` : 'none',
|
|
39
|
+
maxWidth: '100%',
|
|
40
|
+
'@media screen and (-ms-high-contrast: active)': {
|
|
41
|
+
border: isFocused ? '1px solid transparent' : 'none'
|
|
42
|
+
},
|
|
43
|
+
color,
|
|
44
|
+
...(fg('platform-component-visual-refresh') && {
|
|
45
|
+
borderRadius: "var(--ds-border-radius-100, 4px)",
|
|
46
|
+
// Hardcode this color for visual refresh as there is no token color yet
|
|
47
|
+
borderColor: '#B7B9BE',
|
|
48
|
+
borderWidth: "var(--ds-border-width, 1px)",
|
|
49
|
+
borderStyle: 'solid',
|
|
50
|
+
backgroundColor: "var(--ds-background-input, #FFFFFF)"
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export const multiValueLabelCSS = ({
|
|
55
|
+
cropWithEllipsis,
|
|
56
|
+
isDisabled
|
|
57
|
+
}) => ({
|
|
58
|
+
overflow: 'hidden',
|
|
59
|
+
textOverflow: cropWithEllipsis || cropWithEllipsis === undefined ? 'ellipsis' : undefined,
|
|
60
|
+
whiteSpace: 'nowrap',
|
|
61
|
+
borderRadius: "var(--ds-border-radius-050, 2px)",
|
|
62
|
+
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
63
|
+
fontSize: '85%',
|
|
64
|
+
font: "var(--ds-font-body-UNSAFE_small, normal 400 12px/16px ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
65
|
+
padding: "var(--ds-space-025, 2px)",
|
|
66
|
+
color: isDisabled ? "var(--ds-text-disabled, #091E424F)" : 'inherit',
|
|
67
|
+
paddingLeft: "var(--ds-space-075, 6px)",
|
|
68
|
+
...(fg('platform-component-visual-refresh') && {
|
|
69
|
+
font: "var(--ds-font-body, normal 400 14px/20px ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
70
|
+
paddingTop: 0,
|
|
71
|
+
paddingBottom: 0,
|
|
72
|
+
paddingLeft: "var(--ds-space-050, 4px)"
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
export const multiValueRemoveCSS = ({
|
|
76
|
+
isFocused
|
|
77
|
+
}) => ({
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
display: 'flex',
|
|
80
|
+
backgroundColor: isFocused ? "var(--ds-UNSAFE-transparent, transparent)" : undefined,
|
|
81
|
+
fill: isFocused ? "var(--ds-text-selected, #000)" : "var(--ds-text, #000)",
|
|
82
|
+
paddingLeft: "var(--ds-space-025, 2px)",
|
|
83
|
+
paddingRight: "var(--ds-space-025, 2px)",
|
|
84
|
+
borderRadius: '0px 2px 2px 0px',
|
|
85
|
+
// DSP-6470 we should style like Tag once we have the :has selector
|
|
86
|
+
':hover': {
|
|
87
|
+
backgroundColor: "var(--ds-background-danger-hovered, #FFD5D2)",
|
|
88
|
+
fill: "var(--ds-text-danger, #000)"
|
|
89
|
+
},
|
|
90
|
+
':active': {
|
|
91
|
+
backgroundColor: "var(--ds-background-danger-pressed, #FD9891)",
|
|
92
|
+
fill: "var(--ds-text-danger, #000)"
|
|
93
|
+
},
|
|
94
|
+
...(fg('platform-component-visual-refresh') && {
|
|
95
|
+
backgroundColor: "var(--ds-background-neutral-subtle, #00000000)",
|
|
96
|
+
border: 'none',
|
|
97
|
+
alignItems: 'center',
|
|
98
|
+
justifyContent: 'center',
|
|
99
|
+
alignSelf: 'center',
|
|
100
|
+
appearance: 'none',
|
|
101
|
+
borderRadius: "var(--ds-border-radius, 4px)",
|
|
102
|
+
color: "var(--ds-text, #172B4D)",
|
|
103
|
+
padding: "var(--ds-space-025, 2px)",
|
|
104
|
+
marginRight: "var(--ds-space-025, 2px)",
|
|
105
|
+
':focus-visible': {
|
|
106
|
+
outlineOffset: "var(--ds-space-negative-025, -2px)"
|
|
107
|
+
},
|
|
108
|
+
':hover': {
|
|
109
|
+
backgroundColor: "var(--ds-background-neutral-subtle-hovered, #091E420F)"
|
|
110
|
+
},
|
|
111
|
+
':active': {
|
|
112
|
+
backgroundColor: "var(--ds-background-neutral-subtle-pressed, #091E4224)"
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
});
|
|
116
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
117
|
+
const MultiValueGeneric = ({
|
|
118
|
+
children,
|
|
119
|
+
innerProps
|
|
120
|
+
}) => jsx("div", innerProps, children);
|
|
121
|
+
|
|
122
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
123
|
+
export const MultiValueContainer = MultiValueGeneric;
|
|
124
|
+
|
|
125
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
126
|
+
export const MultiValueLabel = MultiValueGeneric;
|
|
127
|
+
const disabledStyles = css({
|
|
128
|
+
display: 'none'
|
|
129
|
+
});
|
|
130
|
+
const enabledStyles = css({
|
|
131
|
+
display: 'inherit'
|
|
132
|
+
});
|
|
133
|
+
const iconWrapperStyles = xcss({
|
|
134
|
+
padding: 'space.025'
|
|
135
|
+
});
|
|
136
|
+
const renderIcon = () => {
|
|
137
|
+
// eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
|
|
138
|
+
if (fg('platform-component-visual-refresh')) {
|
|
139
|
+
return jsx(CrossIcon, {
|
|
140
|
+
label: "",
|
|
141
|
+
color: "currentColor"
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
|
|
146
|
+
if (fg('platform-visual-refresh-icons-legacy-facade')) {
|
|
147
|
+
return jsx(Inline, {
|
|
148
|
+
xcss: iconWrapperStyles
|
|
149
|
+
}, jsx(CrossIcon, {
|
|
150
|
+
label: "",
|
|
151
|
+
color: "currentColor"
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
return (
|
|
155
|
+
// eslint-disable-next-line @atlaskit/design-system/no-legacy-icons
|
|
156
|
+
jsx(LegacySelectClearIcon, {
|
|
157
|
+
label: "",
|
|
158
|
+
primaryColor: "transparent",
|
|
159
|
+
size: "small",
|
|
160
|
+
secondaryColor: "inherit"
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
export function MultiValueRemove({
|
|
165
|
+
isDisabled,
|
|
166
|
+
innerProps
|
|
167
|
+
}) {
|
|
168
|
+
return (
|
|
169
|
+
// The Remove button is intentionally excluded from the tab order, please avoid assigning a non-negative tabIndex to it. Context: https://hello.atlassian.net/wiki/spaces/A11YKB/pages/3031993460/Clear+Options+on+an+Input+Field
|
|
170
|
+
jsx("div", innerProps, jsx("div", {
|
|
171
|
+
css: isDisabled ? disabledStyles : enabledStyles,
|
|
172
|
+
"data-testid": isDisabled ? 'hide-clear-icon' : 'show-clear-icon'
|
|
173
|
+
}, renderIcon()))
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
const MultiValue = props => {
|
|
177
|
+
const {
|
|
178
|
+
children,
|
|
179
|
+
components,
|
|
180
|
+
data,
|
|
181
|
+
innerProps,
|
|
182
|
+
isDisabled,
|
|
183
|
+
removeProps,
|
|
184
|
+
selectProps
|
|
185
|
+
} = props;
|
|
186
|
+
const {
|
|
187
|
+
Container,
|
|
188
|
+
Label,
|
|
189
|
+
Remove
|
|
190
|
+
} = components;
|
|
191
|
+
const ariaLabel = typeof children === 'string' ? children : data.label;
|
|
192
|
+
return jsx(Container, {
|
|
193
|
+
data: data,
|
|
194
|
+
innerProps: {
|
|
195
|
+
...getStyleProps(props, 'multiValue', {
|
|
196
|
+
'multi-value': true,
|
|
197
|
+
'multi-value--is-disabled': isDisabled
|
|
198
|
+
}),
|
|
199
|
+
...innerProps
|
|
200
|
+
},
|
|
201
|
+
selectProps: selectProps
|
|
202
|
+
}, jsx(Label, {
|
|
203
|
+
data: data,
|
|
204
|
+
innerProps: {
|
|
205
|
+
...getStyleProps(props, 'multiValueLabel', {
|
|
206
|
+
'multi-value__label': true
|
|
207
|
+
})
|
|
208
|
+
},
|
|
209
|
+
selectProps: selectProps
|
|
210
|
+
}, children), jsx(Remove, {
|
|
211
|
+
data: data,
|
|
212
|
+
innerProps: {
|
|
213
|
+
...getStyleProps(props, 'multiValueRemove', {
|
|
214
|
+
'multi-value__remove': true
|
|
215
|
+
}),
|
|
216
|
+
role: 'button',
|
|
217
|
+
tabIndex: -1,
|
|
218
|
+
'aria-label': `${ariaLabel || 'option'}, remove`,
|
|
219
|
+
...removeProps
|
|
220
|
+
},
|
|
221
|
+
isDisabled: isDisabled,
|
|
222
|
+
selectProps: selectProps
|
|
223
|
+
}));
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// eslint-disable-next-line @repo/internal/react/require-jsdoc
|
|
227
|
+
export default MultiValue;
|