@deque/cauldron-react 5.8.0-canary.b09c14c1 → 5.8.0-canary.c7e293cc
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/lib/components/Combobox/Combobox.d.ts +30 -0
- package/lib/components/Combobox/ComboboxContext.d.ts +22 -0
- package/lib/components/Combobox/ComboboxGroup.d.ts +7 -0
- package/lib/components/Combobox/ComboboxOption.d.ts +12 -0
- package/lib/components/Combobox/index.d.ts +3 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +402 -2
- package/lib/utils/useIntersectionRef.d.ts +12 -0
- package/package.json +2 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ContentNode } from '../../types';
|
|
3
|
+
import ComboboxOption from './ComboboxOption';
|
|
4
|
+
import type { ComboboxValue } from './ComboboxOption';
|
|
5
|
+
import type { ListboxOption } from '../Listbox/ListboxContext';
|
|
6
|
+
interface ComboboxOption {
|
|
7
|
+
key?: string;
|
|
8
|
+
label: string;
|
|
9
|
+
value?: ComboboxValue;
|
|
10
|
+
description?: string;
|
|
11
|
+
}
|
|
12
|
+
interface ComboboxProps extends React.InputHTMLAttributes<Omit<HTMLInputElement, 'value' | 'defaultValue'>> {
|
|
13
|
+
label: ContentNode;
|
|
14
|
+
options?: ComboboxOption[];
|
|
15
|
+
value?: ComboboxValue;
|
|
16
|
+
defaultValue?: ComboboxValue;
|
|
17
|
+
requiredText?: React.ReactNode;
|
|
18
|
+
error?: React.ReactNode;
|
|
19
|
+
autocomplete?: 'none' | 'manual' | 'automatic';
|
|
20
|
+
onSelectionChange?: <T extends HTMLElement = HTMLElement>({ target, value, previousValue }: {
|
|
21
|
+
target: T;
|
|
22
|
+
value: ComboboxValue;
|
|
23
|
+
previousValue: ComboboxValue;
|
|
24
|
+
}) => void;
|
|
25
|
+
onActiveChange?: (option: ListboxOption) => void;
|
|
26
|
+
renderNoResults?: (() => JSX.Element) | React.ReactElement;
|
|
27
|
+
portal?: React.RefObject<HTMLElement> | HTMLElement;
|
|
28
|
+
}
|
|
29
|
+
declare const Combobox: React.ForwardRefExoticComponent<ComboboxProps & React.RefAttributes<HTMLInputElement>>;
|
|
30
|
+
export default Combobox;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ComboboxValue } from './ComboboxOption';
|
|
3
|
+
type ComboboxContext = {
|
|
4
|
+
autocomplete: 'none' | 'manual' | 'automatic';
|
|
5
|
+
inputValue: ComboboxValue;
|
|
6
|
+
selectedValue: ComboboxValue;
|
|
7
|
+
matchingOptions: Map<HTMLElement, ComboboxOptionState>;
|
|
8
|
+
setMatchingOptions: React.Dispatch<React.SetStateAction<Map<HTMLElement, ComboboxOptionState>>>;
|
|
9
|
+
matches: (<T extends string = string>(value: T) => boolean) | boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ComboboxOptionState = {
|
|
12
|
+
selected: boolean;
|
|
13
|
+
value: ComboboxValue;
|
|
14
|
+
};
|
|
15
|
+
type ComboboxProvider = {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
matches: ((inputValue: string, value: string) => boolean) | boolean;
|
|
18
|
+
} & Omit<ComboboxContext, 'matches'>;
|
|
19
|
+
declare const ComboboxContext: React.Context<ComboboxContext>;
|
|
20
|
+
declare function ComboboxProvider({ autocomplete, inputValue, selectedValue, matches, matchingOptions, setMatchingOptions, children }: ComboboxProvider): JSX.Element;
|
|
21
|
+
declare function useComboboxContext(): ComboboxContext;
|
|
22
|
+
export { ComboboxProvider, useComboboxContext };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ContentNode } from '../../types';
|
|
3
|
+
interface ComboboxGroupProps extends React.HTMLAttributes<HTMLUListElement> {
|
|
4
|
+
label: ContentNode;
|
|
5
|
+
}
|
|
6
|
+
declare const ComboboxGroup: React.ForwardRefExoticComponent<ComboboxGroupProps & React.RefAttributes<HTMLUListElement>>;
|
|
7
|
+
export default ComboboxGroup;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ListboxValue } from '../Listbox/ListboxOption';
|
|
3
|
+
import type { ContentNode } from '../../types';
|
|
4
|
+
export type ComboboxValue = Exclude<ListboxValue, number>;
|
|
5
|
+
interface ComboboxOptionProps extends React.HTMLAttributes<HTMLLIElement> {
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
value?: ComboboxValue;
|
|
8
|
+
description?: ContentNode;
|
|
9
|
+
children: string;
|
|
10
|
+
}
|
|
11
|
+
declare const ComboboxOption: React.ForwardRefExoticComponent<ComboboxOptionProps & React.RefAttributes<HTMLLIElement>>;
|
|
12
|
+
export default ComboboxOption;
|
package/lib/index.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ export { default as Breadcrumb, BreadcrumbItem, BreadcrumbLink } from './compone
|
|
|
53
53
|
export { default as TwoColumnPanel, ColumnHeader, ColumnGroupHeader, ColumnLeft, ColumnRight, ColumnList } from './components/TwoColumnPanel';
|
|
54
54
|
export { default as Notice } from './components/Notice';
|
|
55
55
|
export { default as Listbox, ListboxOption, ListboxGroup } from './components/Listbox';
|
|
56
|
+
export { default as Combobox, ComboboxOption, ComboboxGroup } from './components/Combobox';
|
|
56
57
|
export { default as Popover } from './components/Popover';
|
|
57
58
|
/**
|
|
58
59
|
* Helpers / Utils
|
package/lib/index.js
CHANGED
|
@@ -961,7 +961,7 @@ var ClickOutsideListener = /** @class */ (function (_super) {
|
|
|
961
961
|
return ClickOutsideListener;
|
|
962
962
|
}(React__default["default"].Component));
|
|
963
963
|
|
|
964
|
-
var _a$
|
|
964
|
+
var _a$2 = tslib.__read([38, 40, 9, 13, 32, 27], 6), up = _a$2[0], down$1 = _a$2[1], tab = _a$2[2], enter = _a$2[3], space = _a$2[4], esc = _a$2[5];
|
|
965
965
|
var OptionsMenuList = /** @class */ (function (_super) {
|
|
966
966
|
tslib.__extends(OptionsMenuList, _super);
|
|
967
967
|
function OptionsMenuList(props) {
|
|
@@ -1096,7 +1096,7 @@ var OptionsMenuList = /** @class */ (function (_super) {
|
|
|
1096
1096
|
return OptionsMenuList;
|
|
1097
1097
|
}(React__default["default"].Component));
|
|
1098
1098
|
|
|
1099
|
-
var _a = tslib.__read([40], 1), down = _a[0];
|
|
1099
|
+
var _a$1 = tslib.__read([40], 1), down = _a$1[0];
|
|
1100
1100
|
var OptionsMenu = /** @class */ (function (_super) {
|
|
1101
1101
|
tslib.__extends(OptionsMenu, _super);
|
|
1102
1102
|
function OptionsMenu(props) {
|
|
@@ -4162,6 +4162,403 @@ var ListboxGroup = React.forwardRef(function (_a, ref) {
|
|
|
4162
4162
|
});
|
|
4163
4163
|
ListboxGroup.displayName = 'ListboxGroup';
|
|
4164
4164
|
|
|
4165
|
+
/* istanbul ignore next */
|
|
4166
|
+
var ComboboxContext = React.createContext({
|
|
4167
|
+
autocomplete: 'manual',
|
|
4168
|
+
inputValue: undefined,
|
|
4169
|
+
selectedValue: undefined,
|
|
4170
|
+
matches: true,
|
|
4171
|
+
matchingOptions: new Map(),
|
|
4172
|
+
setMatchingOptions: function () { return null; }
|
|
4173
|
+
});
|
|
4174
|
+
function ComboboxProvider(_a) {
|
|
4175
|
+
var autocomplete = _a.autocomplete, inputValue = _a.inputValue, selectedValue = _a.selectedValue, matches = _a.matches, matchingOptions = _a.matchingOptions, setMatchingOptions = _a.setMatchingOptions, children = _a.children;
|
|
4176
|
+
var Provider = ComboboxContext.Provider;
|
|
4177
|
+
var contextValue = React.useMemo(function () { return ({
|
|
4178
|
+
autocomplete: autocomplete,
|
|
4179
|
+
inputValue: inputValue,
|
|
4180
|
+
selectedValue: selectedValue,
|
|
4181
|
+
matches: typeof matches === 'function' && !!inputValue
|
|
4182
|
+
? function (value) { return matches(inputValue, value); }
|
|
4183
|
+
: true,
|
|
4184
|
+
matchingOptions: matchingOptions,
|
|
4185
|
+
setMatchingOptions: setMatchingOptions
|
|
4186
|
+
}); }, [
|
|
4187
|
+
autocomplete,
|
|
4188
|
+
inputValue,
|
|
4189
|
+
selectedValue,
|
|
4190
|
+
matches,
|
|
4191
|
+
matchingOptions,
|
|
4192
|
+
setMatchingOptions
|
|
4193
|
+
]);
|
|
4194
|
+
return React__default["default"].createElement(Provider, { value: contextValue }, children);
|
|
4195
|
+
}
|
|
4196
|
+
function useComboboxContext() {
|
|
4197
|
+
return React.useContext(ComboboxContext);
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
/**
|
|
4201
|
+
* When a component needs to track intersection via a ref, useIntersectionRef
|
|
4202
|
+
* will return a ref object containing the results from IntersectionObserver
|
|
4203
|
+
* for the current intersection entry.
|
|
4204
|
+
*
|
|
4205
|
+
* @example
|
|
4206
|
+
* const elementRef = useRef<HTMLElement>()
|
|
4207
|
+
* const intersectionRef = useIntersectionRef<HTMLElement>(elementRef)
|
|
4208
|
+
* return <span ref={elementRef}>...</span>
|
|
4209
|
+
*/
|
|
4210
|
+
function useIntersectionRef(element, intersectionObserverOptions) {
|
|
4211
|
+
if (intersectionObserverOptions === void 0) { intersectionObserverOptions = {
|
|
4212
|
+
root: null,
|
|
4213
|
+
threshold: 1.0
|
|
4214
|
+
}; }
|
|
4215
|
+
var intersectionRef = React.useRef(null);
|
|
4216
|
+
React.useEffect(function () {
|
|
4217
|
+
// istanbul ignore else
|
|
4218
|
+
if ('IntersectionObserver' in globalThis &&
|
|
4219
|
+
typeof IntersectionObserver === 'function') {
|
|
4220
|
+
if (typeof element === 'undefined' || element === null) {
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
if (!(element instanceof HTMLElement) &&
|
|
4224
|
+
!(element.current instanceof HTMLElement)) {
|
|
4225
|
+
console.warn('An element or ref was provided to useIntersectionRef that was not an HTMLElement.');
|
|
4226
|
+
return;
|
|
4227
|
+
}
|
|
4228
|
+
var handleIntersection = function (_a) {
|
|
4229
|
+
var _b = tslib.__read(_a, 1), entry = _b[0];
|
|
4230
|
+
intersectionRef.current = entry;
|
|
4231
|
+
};
|
|
4232
|
+
var observer_1 = new IntersectionObserver(handleIntersection, intersectionObserverOptions);
|
|
4233
|
+
observer_1.observe(element instanceof HTMLElement ? element : element.current);
|
|
4234
|
+
return function () {
|
|
4235
|
+
observer_1.disconnect();
|
|
4236
|
+
};
|
|
4237
|
+
}
|
|
4238
|
+
}, [element]);
|
|
4239
|
+
return intersectionRef;
|
|
4240
|
+
}
|
|
4241
|
+
|
|
4242
|
+
var ComboboxMatch = function (_a) {
|
|
4243
|
+
var text = _a.children;
|
|
4244
|
+
var inputValue = useComboboxContext().inputValue;
|
|
4245
|
+
if (!(inputValue === null || inputValue === void 0 ? void 0 : inputValue.length)) {
|
|
4246
|
+
return React__default["default"].createElement("span", null, text);
|
|
4247
|
+
}
|
|
4248
|
+
var matchStart = text.toLowerCase().indexOf(inputValue === null || inputValue === void 0 ? void 0 : inputValue.toLowerCase());
|
|
4249
|
+
if (matchStart === -1) {
|
|
4250
|
+
return React__default["default"].createElement("span", null, text);
|
|
4251
|
+
}
|
|
4252
|
+
var matchLength = inputValue.length;
|
|
4253
|
+
var matchBefore = text.substring(0, matchStart);
|
|
4254
|
+
var match = text.substring(matchStart, matchLength + matchStart);
|
|
4255
|
+
var matchAfter = text.substring(matchStart + matchLength);
|
|
4256
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
4257
|
+
React__default["default"].createElement("span", null, matchBefore),
|
|
4258
|
+
React__default["default"].createElement("em", { className: "ComboboxOption__match" }, match),
|
|
4259
|
+
React__default["default"].createElement("span", null, matchAfter)));
|
|
4260
|
+
};
|
|
4261
|
+
var ComboboxOption = React.forwardRef(function (_a, ref) {
|
|
4262
|
+
var className = _a.className, children = _a.children, disabled = _a.disabled, propId = _a.id, description = _a.description, propValue = _a.value, props = tslib.__rest(_a, ["className", "children", "disabled", "id", "description", "value"]);
|
|
4263
|
+
var _b = tslib.__read(propId ? [propId] : nextId.useId(1, 'combobox-option'), 1), id = _b[0];
|
|
4264
|
+
var _c = useListboxContext(), selected = _c.selected, active = _c.active;
|
|
4265
|
+
var _d = useComboboxContext(), matches = _d.matches, setMatchingOptions = _d.setMatchingOptions;
|
|
4266
|
+
var comboboxOptionRef = useSharedRef(ref);
|
|
4267
|
+
var intersectionRef = useIntersectionRef(comboboxOptionRef, {
|
|
4268
|
+
root: null,
|
|
4269
|
+
threshold: 1.0
|
|
4270
|
+
});
|
|
4271
|
+
var isActive = !!(active === null || active === void 0 ? void 0 : active.element) && active.element === comboboxOptionRef.current;
|
|
4272
|
+
var isSelected = !!(selected === null || selected === void 0 ? void 0 : selected.element) && selected.element === comboboxOptionRef.current;
|
|
4273
|
+
var isMatching = (typeof matches === 'boolean' && matches) ||
|
|
4274
|
+
(typeof matches === 'function' && matches(children));
|
|
4275
|
+
// istanbul ignore next
|
|
4276
|
+
React.useLayoutEffect(function () {
|
|
4277
|
+
var intersectionEntry = intersectionRef.current;
|
|
4278
|
+
if (!intersectionEntry || !isActive) {
|
|
4279
|
+
return;
|
|
4280
|
+
}
|
|
4281
|
+
var rect = comboboxOptionRef.current.getBoundingClientRect();
|
|
4282
|
+
var isInViewport = rect.top >= 0 &&
|
|
4283
|
+
rect.left >= 0 &&
|
|
4284
|
+
rect.bottom <= window.innerHeight &&
|
|
4285
|
+
rect.right <= window.innerWidth;
|
|
4286
|
+
if (!isInViewport || !intersectionEntry.isIntersecting) {
|
|
4287
|
+
comboboxOptionRef.current.scrollIntoView({
|
|
4288
|
+
inline: 'nearest',
|
|
4289
|
+
block: rect.top <= 0 ? 'end' : 'nearest'
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4292
|
+
}, [isActive]);
|
|
4293
|
+
React.useEffect(function () {
|
|
4294
|
+
if (isMatching) {
|
|
4295
|
+
setMatchingOptions(function (options) {
|
|
4296
|
+
return new Map(options.set(comboboxOptionRef.current, {
|
|
4297
|
+
value: children,
|
|
4298
|
+
selected: isSelected
|
|
4299
|
+
}));
|
|
4300
|
+
});
|
|
4301
|
+
}
|
|
4302
|
+
return function () {
|
|
4303
|
+
setMatchingOptions(function (options) {
|
|
4304
|
+
options.forEach(function (_value, element) {
|
|
4305
|
+
// istanbul ignore else
|
|
4306
|
+
if (!element.isConnected) {
|
|
4307
|
+
options.delete(element);
|
|
4308
|
+
}
|
|
4309
|
+
});
|
|
4310
|
+
return new Map(options);
|
|
4311
|
+
});
|
|
4312
|
+
};
|
|
4313
|
+
}, [isMatching, isSelected]);
|
|
4314
|
+
if (!isMatching) {
|
|
4315
|
+
return null;
|
|
4316
|
+
}
|
|
4317
|
+
return (React__default["default"].createElement(ListboxOption, tslib.__assign({ as: "li", className: classNames__default["default"]('ComboboxOption', className, {
|
|
4318
|
+
'ComboboxOption--disabled': disabled
|
|
4319
|
+
}), activeClass: "ComboboxOption--active", ref: comboboxOptionRef, disabled: disabled, id: id, value: propValue }, props),
|
|
4320
|
+
React__default["default"].createElement("span", null,
|
|
4321
|
+
React__default["default"].createElement(ComboboxMatch, null, children),
|
|
4322
|
+
description && (React__default["default"].createElement("div", { className: "ComboboxOption__description" }, description))),
|
|
4323
|
+
isSelected ? React__default["default"].createElement(Icon, { type: "check-solid" }) : null));
|
|
4324
|
+
});
|
|
4325
|
+
ComboboxOption.displayName = 'ComboboxOption';
|
|
4326
|
+
|
|
4327
|
+
// Event Keys
|
|
4328
|
+
var _a = tslib.__read(['Enter', 'Escape', 'Home', 'End'], 4), Enter = _a[0], Escape = _a[1], Home = _a[2], End = _a[3];
|
|
4329
|
+
var defaultAutoCompleteMatches = function (inputValue, value) {
|
|
4330
|
+
// istanbul ignore if
|
|
4331
|
+
if (!value) {
|
|
4332
|
+
return true;
|
|
4333
|
+
}
|
|
4334
|
+
return value.toLowerCase().includes(inputValue.toLowerCase());
|
|
4335
|
+
};
|
|
4336
|
+
var ComboboxNoResults = function () {
|
|
4337
|
+
return (React__default["default"].createElement("div", { className: "ComboboxListbox__empty", "aria-live": "polite" }, "No results found."));
|
|
4338
|
+
};
|
|
4339
|
+
var Combobox = React.forwardRef(function (_a, ref) {
|
|
4340
|
+
var propId = _a.id, className = _a.className, label = _a.label, children = _a.children, _b = _a.options, options = _b === void 0 ? [] : _b, propValue = _a.value, defaultValue = _a.defaultValue, _c = _a.requiredText, requiredText = _c === void 0 ? 'Required' : _c, error = _a.error, _d = _a.autocomplete, autocomplete = _d === void 0 ? 'manual' : _d, onSelectionChange = _a.onSelectionChange, onActiveChange = _a.onActiveChange, onChange = _a.onChange, onKeyDown = _a.onKeyDown, onFocus = _a.onFocus, onBlur = _a.onBlur, renderNoResults = _a.renderNoResults, portal = _a.portal, props = tslib.__rest(_a, ["id", "className", "label", "children", "options", "value", "defaultValue", "requiredText", "error", "autocomplete", "onSelectionChange", "onActiveChange", "onChange", "onKeyDown", "onFocus", "onBlur", "renderNoResults", "portal"]);
|
|
4341
|
+
var _e = tslib.__read(React.useState(defaultValue || propValue || ''), 2), value = _e[0], setValue = _e[1];
|
|
4342
|
+
var _f = tslib.__read(React.useState(new Map()), 2), matchingOptions = _f[0], setMatchingOptions = _f[1];
|
|
4343
|
+
var _g = tslib.__read(React.useState(value || ''), 2), selectedValue = _g[0], setSelectedValue = _g[1];
|
|
4344
|
+
var _h = tslib.__read(React.useState(false), 2), open = _h[0], setOpen = _h[1];
|
|
4345
|
+
var _j = tslib.__read(React.useState(null), 2), activeDescendant = _j[0], setActiveDescendant = _j[1];
|
|
4346
|
+
var _k = tslib.__read(propId ? [propId] : nextId.useId(1, 'combobox'), 1), id = _k[0];
|
|
4347
|
+
var comboboxRef = useSharedRef(ref);
|
|
4348
|
+
var inputRef = React.useRef(null);
|
|
4349
|
+
var listboxRef = React.useRef(null);
|
|
4350
|
+
var isControlled = typeof propValue !== 'undefined';
|
|
4351
|
+
var isRequired = !!props.required;
|
|
4352
|
+
var isAutoComplete = autocomplete !== 'none';
|
|
4353
|
+
var hasError = !!error;
|
|
4354
|
+
var comboboxOptions = children ||
|
|
4355
|
+
options.map(function (option, index) { return (React__default["default"].createElement(ComboboxOption, { key: option.key || index, id: "".concat(id, "-option-").concat(index + 1), description: option.description }, option.label)); });
|
|
4356
|
+
var triggerListboxKeyDown = React__default["default"].useCallback(function (key) {
|
|
4357
|
+
var _a;
|
|
4358
|
+
(_a = listboxRef.current) === null || _a === void 0 ? void 0 : _a.dispatchEvent(new KeyboardEvent('keydown', {
|
|
4359
|
+
key: key,
|
|
4360
|
+
bubbles: true,
|
|
4361
|
+
cancelable: true
|
|
4362
|
+
}));
|
|
4363
|
+
}, [listboxRef]);
|
|
4364
|
+
React.useEffect(function () {
|
|
4365
|
+
if (!isAutoComplete) {
|
|
4366
|
+
return;
|
|
4367
|
+
}
|
|
4368
|
+
if (!open && selectedValue && value !== selectedValue) {
|
|
4369
|
+
setValue(selectedValue);
|
|
4370
|
+
}
|
|
4371
|
+
if (!open) {
|
|
4372
|
+
setActiveDescendant(null);
|
|
4373
|
+
}
|
|
4374
|
+
if (open && autocomplete === 'automatic' && !selectedValue) {
|
|
4375
|
+
// Fire an Home keydown event on listbox to ensure the first item is selected
|
|
4376
|
+
triggerListboxKeyDown(Home);
|
|
4377
|
+
}
|
|
4378
|
+
}, [open]);
|
|
4379
|
+
React.useEffect(function () {
|
|
4380
|
+
if (autocomplete === 'manual') {
|
|
4381
|
+
setActiveDescendant(null);
|
|
4382
|
+
}
|
|
4383
|
+
else if (autocomplete === 'automatic' &&
|
|
4384
|
+
matchingOptions.size &&
|
|
4385
|
+
!selectedValue) {
|
|
4386
|
+
// Fire a home keydown event on listbox to ensure the first item is selected
|
|
4387
|
+
requestAnimationFrame(function () {
|
|
4388
|
+
triggerListboxKeyDown(Home);
|
|
4389
|
+
});
|
|
4390
|
+
}
|
|
4391
|
+
}, [matchingOptions]);
|
|
4392
|
+
var handleFocus = React.useCallback(function (event) {
|
|
4393
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
|
|
4394
|
+
// istanbul ignore else
|
|
4395
|
+
if (!event.defaultPrevented) {
|
|
4396
|
+
setOpen(true);
|
|
4397
|
+
if (selectedValue && value === selectedValue && isAutoComplete) {
|
|
4398
|
+
setValue('');
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
}, [onFocus, value, selectedValue]);
|
|
4402
|
+
var handleInputClick = React.useCallback(function (event) {
|
|
4403
|
+
var _a;
|
|
4404
|
+
setOpen(true);
|
|
4405
|
+
if (selectedValue && value === selectedValue && isAutoComplete) {
|
|
4406
|
+
setValue('');
|
|
4407
|
+
}
|
|
4408
|
+
if (event.target !== inputRef.current) {
|
|
4409
|
+
// ensure focus is set on the input field
|
|
4410
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4411
|
+
}
|
|
4412
|
+
}, [value, selectedValue]);
|
|
4413
|
+
var handleComboboxOptionMouseDown = React.useCallback(function (event) {
|
|
4414
|
+
// prevent blur from triggering when activating combobox options
|
|
4415
|
+
event.preventDefault();
|
|
4416
|
+
}, []);
|
|
4417
|
+
var handleComboboxOptionClick = React.useCallback(function () {
|
|
4418
|
+
var _a;
|
|
4419
|
+
// maintain focus on the input
|
|
4420
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4421
|
+
}, []);
|
|
4422
|
+
var handleBlur = React.useCallback(function (event) {
|
|
4423
|
+
var _a;
|
|
4424
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
4425
|
+
setOpen(false);
|
|
4426
|
+
if (autocomplete === 'automatic' && activeDescendant) {
|
|
4427
|
+
var stringValue = ((_a = activeDescendant.value) === null || _a === void 0 ? void 0 : _a.toString()) ||
|
|
4428
|
+
/* istanbul ignore next: default value */ '';
|
|
4429
|
+
setValue(stringValue);
|
|
4430
|
+
setSelectedValue(stringValue);
|
|
4431
|
+
}
|
|
4432
|
+
}, [autocomplete, activeDescendant, onBlur]);
|
|
4433
|
+
var handleKeyDown = React.useCallback(function (event) {
|
|
4434
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
4435
|
+
var enterKeypress = event.key === Enter;
|
|
4436
|
+
var escKeypress = event.key === Escape;
|
|
4437
|
+
var arrowKeypress = ['ArrowDown', 'ArrowUp'].includes(event.key);
|
|
4438
|
+
if ([Home, End].includes(event.key)) {
|
|
4439
|
+
// prevent the page from scrolling and allow start/end option activation
|
|
4440
|
+
event.preventDefault();
|
|
4441
|
+
}
|
|
4442
|
+
if (escKeypress) {
|
|
4443
|
+
setOpen(false);
|
|
4444
|
+
return;
|
|
4445
|
+
}
|
|
4446
|
+
// Selection should not open the listbox or be
|
|
4447
|
+
// forwarded to the listbox component when closed
|
|
4448
|
+
if (enterKeypress && !open) {
|
|
4449
|
+
return;
|
|
4450
|
+
}
|
|
4451
|
+
setOpen(true);
|
|
4452
|
+
if (!open && arrowKeypress && selectedValue && isAutoComplete) {
|
|
4453
|
+
// If the user opens the combobox again with a selected value
|
|
4454
|
+
// just clear out the field to restore filtering capabilities
|
|
4455
|
+
setValue('');
|
|
4456
|
+
}
|
|
4457
|
+
// Space should not trigger selection since the user could be typing
|
|
4458
|
+
// a value for autocompletion. Additionally when not open and there's
|
|
4459
|
+
// an active descendent we do not want to forward keydown events.
|
|
4460
|
+
if (event.key === ' ' ||
|
|
4461
|
+
(!open && activeDescendant) ||
|
|
4462
|
+
(enterKeypress && !activeDescendant)) {
|
|
4463
|
+
return;
|
|
4464
|
+
}
|
|
4465
|
+
// forward input events to listbox
|
|
4466
|
+
triggerListboxKeyDown(event.key);
|
|
4467
|
+
// Close combobox with keyboard selections
|
|
4468
|
+
if (enterKeypress && activeDescendant) {
|
|
4469
|
+
setOpen(false);
|
|
4470
|
+
}
|
|
4471
|
+
}, [onKeyDown, isAutoComplete, open, selectedValue, activeDescendant]);
|
|
4472
|
+
React.useEffect(function () {
|
|
4473
|
+
if (typeof propValue !== 'undefined') {
|
|
4474
|
+
setValue(propValue);
|
|
4475
|
+
}
|
|
4476
|
+
}, [propValue]);
|
|
4477
|
+
var handleChange = React.useCallback(function (event) {
|
|
4478
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
4479
|
+
// istanbul ignore else
|
|
4480
|
+
if (!isControlled) {
|
|
4481
|
+
setValue(event.target.value);
|
|
4482
|
+
}
|
|
4483
|
+
}, [isControlled, onChange]);
|
|
4484
|
+
var handleSelectionChange = React.useCallback(function (_a) {
|
|
4485
|
+
var target = _a.target, listboxValue = _a.value, previousValue = _a.previousValue;
|
|
4486
|
+
var stringValue = (listboxValue === null || listboxValue === void 0 ? void 0 : listboxValue.toString()) || /* istanbul ignore next */ '';
|
|
4487
|
+
// istanbul ignore else
|
|
4488
|
+
if (!isControlled) {
|
|
4489
|
+
setValue(stringValue);
|
|
4490
|
+
}
|
|
4491
|
+
setSelectedValue(stringValue);
|
|
4492
|
+
onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange({
|
|
4493
|
+
target: target,
|
|
4494
|
+
value: stringValue,
|
|
4495
|
+
previousValue: previousValue === null || previousValue === void 0 ? void 0 : previousValue.toString()
|
|
4496
|
+
});
|
|
4497
|
+
setOpen(false);
|
|
4498
|
+
}, [isControlled, onSelectionChange]);
|
|
4499
|
+
var handleActiveChange = React.useCallback(function (option) {
|
|
4500
|
+
// istanbul ignore else
|
|
4501
|
+
if (option.element) {
|
|
4502
|
+
setActiveDescendant(option);
|
|
4503
|
+
}
|
|
4504
|
+
onActiveChange === null || onActiveChange === void 0 ? void 0 : onActiveChange(option);
|
|
4505
|
+
}, []);
|
|
4506
|
+
var NoMatchingOptions = React__default["default"].useMemo(function () {
|
|
4507
|
+
return React__default["default"].isValidElement(renderNoResults)
|
|
4508
|
+
? function () { return renderNoResults; }
|
|
4509
|
+
: typeof renderNoResults === 'function'
|
|
4510
|
+
? function () { return renderNoResults(); }
|
|
4511
|
+
: ComboboxNoResults;
|
|
4512
|
+
}, [renderNoResults]);
|
|
4513
|
+
var noMatchingOptions = !!(value === null || value === void 0 ? void 0 : value.length) && !matchingOptions.size && (React__default["default"].createElement(NoMatchingOptions, null));
|
|
4514
|
+
var comboboxListbox = (React__default["default"].createElement(Listbox, { className: classNames__default["default"]('Combobox__listbox', {
|
|
4515
|
+
'Combobox__listbox--open': open
|
|
4516
|
+
}), role: "listbox", "aria-labelledby": "".concat(id, "-label"), id: "".concat(id, "-listbox"), value: selectedValue, onMouseDown: handleComboboxOptionMouseDown, onClick: handleComboboxOptionClick, onSelectionChange: handleSelectionChange, onActiveChange: handleActiveChange, ref: listboxRef, tabIndex: undefined, "aria-activedescendant": "" },
|
|
4517
|
+
comboboxOptions,
|
|
4518
|
+
noMatchingOptions));
|
|
4519
|
+
return (React__default["default"].createElement("div", { id: id, className: classNames__default["default"]('Combobox', className), ref: comboboxRef },
|
|
4520
|
+
React__default["default"].createElement("label", { className: classNames__default["default"]('Field__label', {
|
|
4521
|
+
'Field__label--is-required': isRequired,
|
|
4522
|
+
'Field__label--has-error': hasError
|
|
4523
|
+
}), id: "".concat(id, "-label"), htmlFor: "".concat(id, "-input") },
|
|
4524
|
+
React__default["default"].createElement("span", null, label),
|
|
4525
|
+
isRequired && (React__default["default"].createElement("span", { className: "Field__required-text" }, requiredText))),
|
|
4526
|
+
React__default["default"].createElement("div", { className: classNames__default["default"]('Combobox__input', {
|
|
4527
|
+
'Combobox__input--error': hasError
|
|
4528
|
+
}),
|
|
4529
|
+
// We're handling click here to open the listbox when the wrapping element is clicked,
|
|
4530
|
+
// there's already keyboard handlers to open the listbox on the input element
|
|
4531
|
+
onClick: handleInputClick },
|
|
4532
|
+
React__default["default"].createElement("input", tslib.__assign({ type: "text", id: "".concat(id, "-input"), ref: inputRef, value: value, role: "combobox", "aria-autocomplete": !isAutoComplete ? 'none' : 'list', "aria-controls": "".concat(id, "-listbox"), "aria-expanded": open, "aria-haspopup": "listbox", "aria-activedescendant": open && activeDescendant ? activeDescendant.element.id : undefined }, props, { onChange: handleChange, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur })),
|
|
4533
|
+
React__default["default"].createElement("span", { className: "Combobox__arrow" })),
|
|
4534
|
+
React__default["default"].createElement(ComboboxProvider, { autocomplete: autocomplete, inputValue: value, selectedValue: selectedValue, matches: !isAutoComplete || defaultAutoCompleteMatches, matchingOptions: matchingOptions, setMatchingOptions: setMatchingOptions }, portal
|
|
4535
|
+
? reactDom.createPortal(comboboxListbox, portal instanceof HTMLElement
|
|
4536
|
+
? portal
|
|
4537
|
+
: portal.current ||
|
|
4538
|
+
/* istanbul ignore next: default fallback value */ document.body)
|
|
4539
|
+
: comboboxListbox),
|
|
4540
|
+
hasError && (React__default["default"].createElement("div", { className: "Error", id: "".concat(id, "-error") }, error))));
|
|
4541
|
+
});
|
|
4542
|
+
Combobox.displayName = 'Combobox';
|
|
4543
|
+
|
|
4544
|
+
var ComboboxGroup = React.forwardRef(function (_a, ref) {
|
|
4545
|
+
var className = _a.className, children = _a.children, label = _a.label, props = tslib.__rest(_a, ["className", "children", "label"]);
|
|
4546
|
+
var _b = useComboboxContext(), inputValue = _b.inputValue, autocomplete = _b.autocomplete, matchingOptions = _b.matchingOptions;
|
|
4547
|
+
var comboboxGroupRef = useSharedRef(ref);
|
|
4548
|
+
// istanbul ignore next
|
|
4549
|
+
var showGroup = React.useMemo(function () {
|
|
4550
|
+
if (autocomplete === 'none' || !(inputValue === null || inputValue === void 0 ? void 0 : inputValue.length)) {
|
|
4551
|
+
return true;
|
|
4552
|
+
}
|
|
4553
|
+
var elements = Array.from(matchingOptions.keys());
|
|
4554
|
+
return !!elements.find(function (element) { var _a; return (_a = comboboxGroupRef.current) === null || _a === void 0 ? void 0 : _a.contains(element); });
|
|
4555
|
+
}, [inputValue, autocomplete, matchingOptions]);
|
|
4556
|
+
return (React__default["default"].createElement(ListboxGroup, tslib.__assign({ as: "ul", className: classNames__default["default"]('ComboboxGroup', className, {
|
|
4557
|
+
'ComboboxGroup--hidden': !showGroup
|
|
4558
|
+
}), "aria-hidden": !showGroup, ref: comboboxGroupRef, label: label, groupLabelProps: { className: 'ComboboxGroup__label' } }, props), children));
|
|
4559
|
+
});
|
|
4560
|
+
ComboboxGroup.displayName = 'ComboboxGroup';
|
|
4561
|
+
|
|
4165
4562
|
var PromptPopoverContent = function (_a) {
|
|
4166
4563
|
var onClose = _a.onClose, _b = _a.applyButtonText, applyButtonText = _b === void 0 ? 'Apply' : _b, onApply = _a.onApply, _c = _a.closeButtonText, closeButtonText = _c === void 0 ? 'Close' : _c, infoText = _a.infoText, infoTextId = _a.infoTextId;
|
|
4167
4564
|
return (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
@@ -4377,6 +4774,9 @@ exports.ColumnHeader = ColumnHeader;
|
|
|
4377
4774
|
exports.ColumnLeft = ColumnLeft;
|
|
4378
4775
|
exports.ColumnList = ColumnList;
|
|
4379
4776
|
exports.ColumnRight = ColumnRight;
|
|
4777
|
+
exports.Combobox = Combobox;
|
|
4778
|
+
exports.ComboboxGroup = ComboboxGroup;
|
|
4779
|
+
exports.ComboboxOption = ComboboxOption;
|
|
4380
4780
|
exports.DescriptionDetails = DescriptionDetails;
|
|
4381
4781
|
exports.DescriptionList = DescriptionList;
|
|
4382
4782
|
exports.DescriptionListItem = DescriptionListItem;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MutableRefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* When a component needs to track intersection via a ref, useIntersectionRef
|
|
4
|
+
* will return a ref object containing the results from IntersectionObserver
|
|
5
|
+
* for the current intersection entry.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const elementRef = useRef<HTMLElement>()
|
|
9
|
+
* const intersectionRef = useIntersectionRef<HTMLElement>(elementRef)
|
|
10
|
+
* return <span ref={elementRef}>...</span>
|
|
11
|
+
*/
|
|
12
|
+
export default function useIntersectionRef<T extends HTMLElement>(element: T | MutableRefObject<T>, intersectionObserverOptions?: IntersectionObserverInit): MutableRefObject<IntersectionObserverEntry | null>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deque/cauldron-react",
|
|
3
|
-
"version": "5.8.0-canary.
|
|
3
|
+
"version": "5.8.0-canary.c7e293cc",
|
|
4
4
|
"description": "Fully accessible react components library for Deque Cauldron",
|
|
5
5
|
"homepage": "https://cauldron.dequelabs.com/",
|
|
6
6
|
"publishConfig": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"**/__tests__/demo/**/*.js"
|
|
111
111
|
],
|
|
112
112
|
"collectCoverageFrom": [
|
|
113
|
-
"**/src/**/*.tsx"
|
|
113
|
+
"**/src/**/*.{ts,tsx}"
|
|
114
114
|
],
|
|
115
115
|
"moduleNameMapper": {
|
|
116
116
|
"\\.(css|less)$": "<rootDir>/__tests__/styleMock.js",
|