@basic-ui/core 0.0.43 → 0.0.46
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/build/cjs/index.js +92 -47
- package/build/cjs/index.js.map +1 -1
- package/build/esm/ComboBox/hooks.js +51 -15
- package/build/esm/ComboBox/hooks.js.map +1 -1
- package/build/esm/Menu/MenuItem.js +0 -1
- package/build/esm/Menu/MenuItem.js.map +1 -1
- package/build/esm/Menu/MenuList.js +18 -4
- package/build/esm/Menu/MenuList.js.map +1 -1
- package/build/esm/Menu/fixtures/countryList.d.ts +1 -0
- package/build/esm/Menu/fixtures/countryList.js +2 -0
- package/build/esm/Menu/fixtures/countryList.js.map +1 -0
- package/build/esm/Slider/Slider.d.ts +0 -21
- package/build/esm/Slider/Slider.js +27 -10
- package/build/esm/Slider/Slider.js.map +1 -1
- package/build/esm/hooks/useControlledState.js +3 -1
- package/build/esm/hooks/useControlledState.js.map +1 -1
- package/build/esm/utils/polymorphic.d.ts +7 -0
- package/build/esm/utils/polymorphic.js.map +1 -1
- package/build/tsconfig-build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/ComboBox/ComboBox.story.tsx +1 -1
- package/src/ComboBox/hooks.tsx +40 -15
- package/src/ComboBox/styles.css +2 -0
- package/src/Menu/MenuComplex.story.tsx +58 -0
- package/src/Menu/MenuItem.tsx +0 -1
- package/src/Menu/MenuList.tsx +25 -4
- package/src/Menu/fixtures/countryList.ts +198 -0
- package/src/Slider/Slider.tsx +18 -4
- package/src/hooks/useControlledState.ts +7 -1
- package/src/utils/polymorphic.ts +13 -7
package/build/cjs/index.js
CHANGED
|
@@ -216,11 +216,13 @@ function wrapEvent(theirHandler, ourHandler) {
|
|
|
216
216
|
function useControlledState(valueProp, onChangeProp, defaultValue, defaultOnChange) {
|
|
217
217
|
const isControlled = valueProp !== undefined;
|
|
218
218
|
const wasControlled = react.useRef(isControlled);
|
|
219
|
+
const hasWarned = react.useRef(false);
|
|
219
220
|
const [valueState, setValueState] = react.useState(defaultValue);
|
|
220
221
|
|
|
221
222
|
if (isControlled) {
|
|
222
|
-
if (wasControlled.current && process.env.NODE_ENV !== 'production') {
|
|
223
|
+
if (wasControlled.current && process.env.NODE_ENV !== 'production' && !hasWarned.current) {
|
|
223
224
|
console.warn('Trying to change from controlled to uncontrolled.');
|
|
225
|
+
hasWarned.current = true;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
return [// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -1153,30 +1155,53 @@ function useFocusManagement(lastActionType, inputRef) {
|
|
|
1153
1155
|
});
|
|
1154
1156
|
}
|
|
1155
1157
|
|
|
1156
|
-
function getNextItem(currentItem,
|
|
1158
|
+
function getNextItem(currentItem, key, optionsItems, autocomplete) {
|
|
1159
|
+
const jumpToStartOrEnd = key === 'Home' || key === 'End';
|
|
1160
|
+
let incr = 1;
|
|
1161
|
+
|
|
1162
|
+
switch (key) {
|
|
1163
|
+
case 'PageUp':
|
|
1164
|
+
incr = -10;
|
|
1165
|
+
break;
|
|
1166
|
+
|
|
1167
|
+
case 'PageDown':
|
|
1168
|
+
incr = 10;
|
|
1169
|
+
break;
|
|
1170
|
+
|
|
1171
|
+
case 'End':
|
|
1172
|
+
case 'ArrowUp':
|
|
1173
|
+
incr = -1;
|
|
1174
|
+
break;
|
|
1175
|
+
|
|
1176
|
+
case 'Home':
|
|
1177
|
+
case 'ArrowDown':
|
|
1178
|
+
incr = 1;
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1157
1182
|
const index = currentItem === '' ? -1 : optionsItems.findIndex(n => String(n.id) === currentItem);
|
|
1158
1183
|
const optionsLen = optionsItems.length; // Nothing selected, either go to start, or end
|
|
1159
1184
|
|
|
1160
|
-
if (index < 0) {
|
|
1185
|
+
if (index < 0 || jumpToStartOrEnd) {
|
|
1161
1186
|
if (incr > 0) {
|
|
1162
1187
|
// Go to start
|
|
1163
|
-
return optionsItems[0]
|
|
1188
|
+
return optionsItems[0];
|
|
1164
1189
|
} else {
|
|
1165
1190
|
// Go to end
|
|
1166
|
-
return optionsItems[optionsLen - 1]
|
|
1191
|
+
return optionsItems[optionsLen - 1];
|
|
1167
1192
|
}
|
|
1168
1193
|
} else if (autocomplete) {
|
|
1169
1194
|
const nextIndex = index + incr;
|
|
1170
1195
|
|
|
1171
1196
|
if (nextIndex < 0 || nextIndex >= optionsLen) {
|
|
1172
1197
|
// Next is outside the bounds of list, return nothing selected
|
|
1173
|
-
return
|
|
1198
|
+
return null;
|
|
1174
1199
|
}
|
|
1175
1200
|
} // I'm sure it won't be null, we already check optionsLen above
|
|
1176
1201
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1177
1202
|
|
|
1178
1203
|
|
|
1179
|
-
return optionsItems[getCircularIndex(index + incr, optionsLen)]
|
|
1204
|
+
return optionsItems[getCircularIndex(index + incr, optionsLen)];
|
|
1180
1205
|
} // We want the same events when the input or the popup have focus (HOW COOL ARE
|
|
1181
1206
|
// HOOKS BTW?) This is probably the hairiest piece but it's not bad.
|
|
1182
1207
|
|
|
@@ -1201,6 +1226,10 @@ function useKeyDown() {
|
|
|
1201
1226
|
const optionNodes = listScope.current.queryAllNodes(scopeQuery$1);
|
|
1202
1227
|
|
|
1203
1228
|
switch (event.key) {
|
|
1229
|
+
case 'Home':
|
|
1230
|
+
case 'End':
|
|
1231
|
+
case 'PageUp':
|
|
1232
|
+
case 'PageDown':
|
|
1204
1233
|
case 'ArrowUp':
|
|
1205
1234
|
case 'ArrowDown':
|
|
1206
1235
|
{
|
|
@@ -1220,17 +1249,26 @@ function useKeyDown() {
|
|
|
1220
1249
|
persistSelection: persistSelectionRef.current
|
|
1221
1250
|
});
|
|
1222
1251
|
} else {
|
|
1223
|
-
//
|
|
1224
|
-
const incr = event.key === 'ArrowUp' ? -1 : 1; // When autocompletting, we'll not cycle through the list directly
|
|
1225
|
-
|
|
1252
|
+
// When autocompletting, we'll not cycle through the list directly
|
|
1226
1253
|
const autocomplete = autocompletePropRef.current; // Get next selected item
|
|
1227
1254
|
|
|
1228
|
-
const nextItem = getNextItem(navigationItem,
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1255
|
+
const nextItem = getNextItem(navigationItem, event.key, optionNodes, autocomplete);
|
|
1256
|
+
|
|
1257
|
+
if (nextItem !== null) {
|
|
1258
|
+
nextItem.scrollIntoView({
|
|
1259
|
+
behavior: 'auto',
|
|
1260
|
+
block: 'nearest'
|
|
1261
|
+
});
|
|
1262
|
+
transition(NAVIGATE, {
|
|
1263
|
+
value: optionsRef.current[nextItem.id].text,
|
|
1264
|
+
item: nextItem.id
|
|
1265
|
+
});
|
|
1266
|
+
} else {
|
|
1267
|
+
transition(NAVIGATE, {
|
|
1268
|
+
value: null,
|
|
1269
|
+
item: ''
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1234
1272
|
}
|
|
1235
1273
|
|
|
1236
1274
|
break;
|
|
@@ -1999,7 +2037,6 @@ const MenuItem = /*#__PURE__*/react.forwardRef(function MenuItem(props, forwarde
|
|
|
1999
2037
|
const handleKeyDown = e => {
|
|
2000
2038
|
switch (e.key) {
|
|
2001
2039
|
case 'Enter':
|
|
2002
|
-
case ' ':
|
|
2003
2040
|
if (!disabled) {
|
|
2004
2041
|
handleSelect(e);
|
|
2005
2042
|
}
|
|
@@ -2037,6 +2074,8 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
|
|
|
2037
2074
|
defaultActiveItemValue,
|
|
2038
2075
|
...otherProps
|
|
2039
2076
|
} = props;
|
|
2077
|
+
const itemSearchStr = react.useRef('');
|
|
2078
|
+
const itemSearchClearTimeout = react.useRef();
|
|
2040
2079
|
const {
|
|
2041
2080
|
menuListIdRef,
|
|
2042
2081
|
buttonRef,
|
|
@@ -2093,6 +2132,7 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
|
|
|
2093
2132
|
onChange && onChange(e, false);
|
|
2094
2133
|
e.preventDefault(); // prevents focusing on next element, because we will be handling it
|
|
2095
2134
|
|
|
2135
|
+
itemSearchStr.current = '';
|
|
2096
2136
|
buttonRef.current?.focus();
|
|
2097
2137
|
break;
|
|
2098
2138
|
}
|
|
@@ -2102,6 +2142,7 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
|
|
|
2102
2142
|
case 'ArrowDown':
|
|
2103
2143
|
case 'ArrowUp':
|
|
2104
2144
|
e.preventDefault();
|
|
2145
|
+
itemSearchStr.current = '';
|
|
2105
2146
|
const allItems = scope ? scope.current.queryAllNodes(queryScope) : [];
|
|
2106
2147
|
const currentIndex = allItems.findIndex(e => e === navigationItem);
|
|
2107
2148
|
|
|
@@ -2137,21 +2178,30 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
|
|
|
2137
2178
|
|
|
2138
2179
|
default:
|
|
2139
2180
|
{
|
|
2140
|
-
if (e.key.length === 1) {
|
|
2181
|
+
if (e.key.length === 1 && !e.ctrlKey && !e.altKey) {
|
|
2141
2182
|
// A-Z
|
|
2142
2183
|
e.preventDefault();
|
|
2184
|
+
|
|
2185
|
+
if (itemSearchStr.current.length === 0 || itemSearchStr.current.slice(-1) !== e.key) {
|
|
2186
|
+
itemSearchStr.current = itemSearchStr.current + e.key;
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
clearTimeout(itemSearchClearTimeout.current);
|
|
2190
|
+
itemSearchClearTimeout.current = setTimeout(() => {
|
|
2191
|
+
itemSearchStr.current = '';
|
|
2192
|
+
}, 500);
|
|
2143
2193
|
const allItems = scope ? scope.current.queryAllNodes(queryScope) : [];
|
|
2144
2194
|
const currentIndex = allItems.findIndex(e => e === navigationItem);
|
|
2145
|
-
const
|
|
2195
|
+
const searchStr = itemSearchStr.current;
|
|
2146
2196
|
let nextIndex = -1;
|
|
2147
2197
|
|
|
2148
|
-
for (let i = 1; i < allItems.length; i++) {
|
|
2198
|
+
for (let i = searchStr.length === 1 ? 1 : 0; i < allItems.length; i++) {
|
|
2149
2199
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2150
2200
|
const idx = getCircularIndex(currentIndex + i, allItems.length);
|
|
2151
2201
|
const dom = allItems[idx];
|
|
2152
2202
|
const domText = dom.innerText.toLowerCase();
|
|
2153
2203
|
|
|
2154
|
-
if (domText.length > 0 && domText.
|
|
2204
|
+
if (domText.length > 0 && domText.startsWith(searchStr)) {
|
|
2155
2205
|
nextIndex = idx;
|
|
2156
2206
|
break;
|
|
2157
2207
|
}
|
|
@@ -2478,27 +2528,7 @@ const RadioGroup = /*#__PURE__*/react.forwardRef(function RadioGroup(props, forw
|
|
|
2478
2528
|
});
|
|
2479
2529
|
});
|
|
2480
2530
|
|
|
2481
|
-
|
|
2482
|
-
* Welcome to @reach/slider!
|
|
2483
|
-
*
|
|
2484
|
-
* A UI input component where the user selects a value from within a given
|
|
2485
|
-
* range. A Slider has a handle that can be moved along a track to change its
|
|
2486
|
-
* value. When the user's mouse or focus is on the Slider's handle, the value
|
|
2487
|
-
* can be incremented with keyboard controls.
|
|
2488
|
-
*
|
|
2489
|
-
* Random thoughts/notes:
|
|
2490
|
-
* - Currently testing this against the behavior of the native input range
|
|
2491
|
-
* element to get our slider on par. We'll explore animated and multi-handle
|
|
2492
|
-
* sliders next.
|
|
2493
|
-
* - We may want to research some use cases for reversed sliders in RTL
|
|
2494
|
-
* languages if that's a thing
|
|
2495
|
-
*
|
|
2496
|
-
* @see Docs https://reach.tech/slider
|
|
2497
|
-
* @see Source https://github.com/reach/reach-ui/tree/main/packages/slider
|
|
2498
|
-
* @see WAI-ARIA https://www.w3.org/TR/wai-aria-practices-1.2/#slider
|
|
2499
|
-
* @see Example https://github.com/Stanko/aria-progress-range-slider
|
|
2500
|
-
* @see Example http://www.oaa-accessibility.org/examplep/slider1/
|
|
2501
|
-
*/
|
|
2531
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2502
2532
|
|
|
2503
2533
|
const noop = () => {
|
|
2504
2534
|
/* noop */
|
|
@@ -2544,6 +2574,7 @@ const SliderInput = /*#__PURE__*/react.forwardRef(function SliderInput({
|
|
|
2544
2574
|
'aria-labelledby': ariaLabelledBy,
|
|
2545
2575
|
'aria-valuetext': ariaValueTextProp,
|
|
2546
2576
|
as: Comp = 'div',
|
|
2577
|
+
innerAs,
|
|
2547
2578
|
defaultValue,
|
|
2548
2579
|
disabled = false,
|
|
2549
2580
|
value: controlledValue,
|
|
@@ -2866,6 +2897,8 @@ const SliderInput = /*#__PURE__*/react.forwardRef(function SliderInput({
|
|
|
2866
2897
|
rangeStyle: rangeStyle,
|
|
2867
2898
|
updateValue: onChange,
|
|
2868
2899
|
children: /*#__PURE__*/jsxRuntime.jsxs(Comp, { ...rest,
|
|
2900
|
+
// @ts-ignore
|
|
2901
|
+
as: innerAs,
|
|
2869
2902
|
ref: assignMultipleRefs(sliderRef, forwardedRef),
|
|
2870
2903
|
"data-reach-slider-input": "",
|
|
2871
2904
|
"data-disabled": disabled ? '' : undefined,
|
|
@@ -2906,6 +2939,7 @@ const SliderInput = /*#__PURE__*/react.forwardRef(function SliderInput({
|
|
|
2906
2939
|
*/
|
|
2907
2940
|
const SliderTrackImpl = /*#__PURE__*/react.forwardRef(function SliderTrack({
|
|
2908
2941
|
as: Comp = 'div',
|
|
2942
|
+
innerAs,
|
|
2909
2943
|
children,
|
|
2910
2944
|
style = {},
|
|
2911
2945
|
...props
|
|
@@ -2916,7 +2950,9 @@ const SliderTrackImpl = /*#__PURE__*/react.forwardRef(function SliderTrack({
|
|
|
2916
2950
|
trackRef
|
|
2917
2951
|
} = useSliderContext('SliderTrack');
|
|
2918
2952
|
return /*#__PURE__*/jsxRuntime.jsx(Comp, {
|
|
2919
|
-
ref: assignMultipleRefs(trackRef, forwardedRef)
|
|
2953
|
+
ref: assignMultipleRefs(trackRef, forwardedRef) // @ts-ignore
|
|
2954
|
+
,
|
|
2955
|
+
as: innerAs,
|
|
2920
2956
|
style: { ...style,
|
|
2921
2957
|
position: 'relative'
|
|
2922
2958
|
},
|
|
@@ -2944,6 +2980,7 @@ const SliderTrack = /*#__PURE__*/react.memo(SliderTrackImpl);
|
|
|
2944
2980
|
*/
|
|
2945
2981
|
const SliderRangeImpl = /*#__PURE__*/react.forwardRef(function SliderRange({
|
|
2946
2982
|
as: Comp = 'div',
|
|
2983
|
+
innerAs,
|
|
2947
2984
|
children,
|
|
2948
2985
|
style = {},
|
|
2949
2986
|
...props
|
|
@@ -2954,7 +2991,9 @@ const SliderRangeImpl = /*#__PURE__*/react.forwardRef(function SliderRange({
|
|
|
2954
2991
|
rangeStyle
|
|
2955
2992
|
} = useSliderContext('SliderRange');
|
|
2956
2993
|
return /*#__PURE__*/jsxRuntime.jsx(Comp, {
|
|
2957
|
-
ref: forwardedRef
|
|
2994
|
+
ref: forwardedRef // @ts-ignore
|
|
2995
|
+
,
|
|
2996
|
+
as: innerAs,
|
|
2958
2997
|
style: {
|
|
2959
2998
|
position: 'absolute',
|
|
2960
2999
|
...rangeStyle,
|
|
@@ -2988,6 +3027,7 @@ const SliderHandleImpl = /*#__PURE__*/react.forwardRef(function SliderHandle({
|
|
|
2988
3027
|
// min,
|
|
2989
3028
|
// max,
|
|
2990
3029
|
as: Comp = 'div',
|
|
3030
|
+
innerAs,
|
|
2991
3031
|
onBlur,
|
|
2992
3032
|
onFocus,
|
|
2993
3033
|
style = {},
|
|
@@ -3009,7 +3049,9 @@ const SliderHandleImpl = /*#__PURE__*/react.forwardRef(function SliderHandle({
|
|
|
3009
3049
|
sliderMax,
|
|
3010
3050
|
value
|
|
3011
3051
|
} = useSliderContext('SliderHandle');
|
|
3012
|
-
return /*#__PURE__*/jsxRuntime.jsx(Comp
|
|
3052
|
+
return /*#__PURE__*/jsxRuntime.jsx(Comp // @ts-ignore
|
|
3053
|
+
, {
|
|
3054
|
+
as: innerAs,
|
|
3013
3055
|
"aria-disabled": disabled || undefined // If the slider has a visible label, it is referenced by
|
|
3014
3056
|
// `aria-labelledby` on the slider element. Otherwise, the slider
|
|
3015
3057
|
// element has a label provided by `aria-label`.
|
|
@@ -3086,6 +3128,7 @@ const SliderHandle = /*#__PURE__*/react.memo(SliderHandleImpl);
|
|
|
3086
3128
|
*/
|
|
3087
3129
|
const SliderMarkerImpl = /*#__PURE__*/react.forwardRef(function SliderMarker({
|
|
3088
3130
|
as: Comp = 'div',
|
|
3131
|
+
innerAs,
|
|
3089
3132
|
children,
|
|
3090
3133
|
style = {},
|
|
3091
3134
|
value,
|
|
@@ -3099,11 +3142,13 @@ const SliderMarkerImpl = /*#__PURE__*/react.forwardRef(function SliderMarker({
|
|
|
3099
3142
|
sliderMax,
|
|
3100
3143
|
value: sliderValue
|
|
3101
3144
|
} = useSliderContext('SliderMarker');
|
|
3102
|
-
const inRange =
|
|
3145
|
+
const inRange = value >= sliderMin && value <= sliderMax;
|
|
3103
3146
|
const absoluteStartPosition = `${valueToPercent(value, sliderMin, sliderMax)}%`;
|
|
3104
3147
|
const state = value < sliderValue ? 'under-value' : value === sliderValue ? 'at-value' : 'over-value';
|
|
3105
3148
|
return inRange ? /*#__PURE__*/jsxRuntime.jsx(Comp, {
|
|
3106
|
-
ref: forwardedRef
|
|
3149
|
+
ref: forwardedRef // @ts-ignore
|
|
3150
|
+
,
|
|
3151
|
+
as: innerAs,
|
|
3107
3152
|
style: {
|
|
3108
3153
|
position: 'absolute',
|
|
3109
3154
|
...(isVertical ? {
|