@capra/core 1.12.0 → 1.13.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/dist/index.cjs CHANGED
@@ -346,11 +346,50 @@ HelperText.displayName = "HelperText";
346
346
  //#endregion
347
347
  //#region src/components/input-helpers/fieldLayout.module.css
348
348
  var fieldLayout_module_default = {
349
- "field": "capra-fieldLayout-module-GZD11W-field",
350
- "fieldContent": "capra-fieldLayout-module-GZD11W-fieldContent",
351
- "labelRow": "capra-fieldLayout-module-GZD11W-labelRow"
349
+ "field": "capra-fieldLayout-module-0mM4jW-field",
350
+ "fieldContent": "capra-fieldLayout-module-0mM4jW-fieldContent",
351
+ "labelRow": "capra-fieldLayout-module-0mM4jW-labelRow"
352
352
  };
353
353
  //#endregion
354
+ //#region src/components/input-helpers/useAnchorWidth.ts
355
+ /**
356
+ * Element that visually bounds the field (select trigger or text input shell). The popover matches
357
+ * its width and left edge, which differs from the field root in horizontal layouts where the label
358
+ * sits beside the control.
359
+ */
360
+ function getShellElement(anchor) {
361
+ return anchor.querySelector("[data-capra-anchor-width-target]") ?? anchor.querySelector("[data-capra-text-input]") ?? anchor;
362
+ }
363
+ /** Element React Aria anchors the popover to: the select trigger or the combobox input. */
364
+ function getTriggerElement(anchor) {
365
+ return anchor.querySelector("[data-capra-anchor-width-target]") ?? anchor.querySelector("input") ?? anchor;
366
+ }
367
+ function useAnchorWidth(additionalDependencies = []) {
368
+ const anchorRef = react.useRef(null);
369
+ const [width, setWidth] = react.useState(null);
370
+ const [offset, setOffset] = react.useState(0);
371
+ react.useEffect(() => {
372
+ const anchor = anchorRef.current;
373
+ if (!anchor) return;
374
+ const measure = () => {
375
+ const shell = getShellElement(anchor);
376
+ const trigger = getTriggerElement(anchor);
377
+ setWidth(shell.offsetWidth + "px");
378
+ setOffset(shell.getBoundingClientRect().left - trigger.getBoundingClientRect().left);
379
+ };
380
+ const observer = new ResizeObserver(measure);
381
+ observer.observe(anchor);
382
+ const shell = getShellElement(anchor);
383
+ if (shell !== anchor) observer.observe(shell);
384
+ return () => observer.disconnect();
385
+ }, additionalDependencies);
386
+ return [
387
+ anchorRef,
388
+ width,
389
+ offset
390
+ ];
391
+ }
392
+ //#endregion
354
393
  //#region src/components/Autocomplete/AutocompleteField.module.css
355
394
  var AutocompleteField_module_default = {
356
395
  "item": "capra-AutocompleteField-module-MVvKYa-item",
@@ -409,41 +448,6 @@ function AutocompleteFieldWithoutRef(props, ref) {
409
448
  style: { width: shouldAutoSizeDropdown ? width : void 0 }
410
449
  }, /* @__PURE__ */ react.default.createElement(react_aria_components.ListBox, { className: AutocompleteField_module_default.listbox }, children)));
411
450
  }
412
- function useResizeObserver({ ref, onResize, additionalDependencies = [] }) {
413
- react.default.useEffect(() => {
414
- if (!ref.current) return;
415
- const observer = new ResizeObserver((entries) => {
416
- onResize(entries[0]);
417
- });
418
- observer.observe(ref.current);
419
- return () => observer.disconnect();
420
- }, [
421
- ref,
422
- onResize,
423
- ...additionalDependencies
424
- ]);
425
- }
426
- function useAnchorWidth(additionalDependencies = []) {
427
- const anchorRef = react.default.useRef(null);
428
- const [width, setWidth] = (0, react.useState)(null);
429
- const [offset, setOffset] = (0, react.useState)(0);
430
- useResizeObserver({
431
- ref: anchorRef,
432
- onResize: (0, react.useCallback)(() => {
433
- if (anchorRef.current) {
434
- setWidth(anchorRef.current.offsetWidth + "px");
435
- const input = anchorRef.current.querySelector("input");
436
- if (input) setOffset(anchorRef.current.offsetLeft - input.offsetLeft);
437
- }
438
- }, [anchorRef]),
439
- additionalDependencies
440
- });
441
- return [
442
- anchorRef,
443
- width,
444
- offset
445
- ];
446
- }
447
451
  /**
448
452
  * AutocompleteField combines a text field with a suggestions list. Consumers can supply static `AutocompleteField.Item` children or an `items` collection with an optional default item renderer. The field allows custom values that are not in the list.
449
453
  */
@@ -834,7 +838,7 @@ function RadioTileWithoutRef({ children, checked, disabled, description, icon, F
834
838
  const RadioTile = fixedForwardRef(RadioTileWithoutRef);
835
839
  //#endregion
836
840
  //#region src/components/Switch/Switch.module.css
837
- var Switch_module_default = { "switch": "capra-Switch-module-16LYpW-switch" };
841
+ var Switch_module_default = { "switch": "capra-Switch-module-j8Yc-q-switch" };
838
842
  //#endregion
839
843
  //#region src/components/Switch/Switch.tsx
840
844
  const Switch = react.forwardRef((props, ref) => {
@@ -2313,6 +2317,430 @@ const DatePickerFieldWithRef = react.forwardRef(DatePickerFieldInner);
2313
2317
  DatePickerFieldWithRef.displayName = "DatePickerField";
2314
2318
  const DatePickerField = DatePickerFieldWithRef;
2315
2319
  //#endregion
2320
+ //#region src/components/SelectField/SelectField.module.css
2321
+ var SelectField_module_default = {
2322
+ "chevron": "capra-SelectField-module-1X4diq-chevron",
2323
+ "dropdownPanel": "capra-SelectField-module-1X4diq-dropdownPanel",
2324
+ "emptyState": "capra-SelectField-module-1X4diq-emptyState",
2325
+ "errorIndicator": "capra-SelectField-module-1X4diq-errorIndicator",
2326
+ "header": "capra-SelectField-module-1X4diq-header",
2327
+ "item": "capra-SelectField-module-1X4diq-item",
2328
+ "itemCheck": "capra-SelectField-module-1X4diq-itemCheck",
2329
+ "itemContent": "capra-SelectField-module-1X4diq-itemContent",
2330
+ "leadingSlot": "capra-SelectField-module-1X4diq-leadingSlot",
2331
+ "listbox": "capra-SelectField-module-1X4diq-listbox",
2332
+ "multipleOpenButton": "capra-SelectField-module-1X4diq-multipleOpenButton",
2333
+ "multipleTrigger": "capra-SelectField-module-1X4diq-multipleTrigger",
2334
+ "search": "capra-SelectField-module-1X4diq-search",
2335
+ "section": "capra-SelectField-module-1X4diq-section",
2336
+ "singleTrigger": "capra-SelectField-module-1X4diq-singleTrigger",
2337
+ "tag": "capra-SelectField-module-1X4diq-tag",
2338
+ "tagGroup": "capra-SelectField-module-1X4diq-tagGroup",
2339
+ "tagIcon": "capra-SelectField-module-1X4diq-tagIcon",
2340
+ "tagLabel": "capra-SelectField-module-1X4diq-tagLabel",
2341
+ "tagList": "capra-SelectField-module-1X4diq-tagList",
2342
+ "tagRemove": "capra-SelectField-module-1X4diq-tagRemove",
2343
+ "trailingSlot": "capra-SelectField-module-1X4diq-trailingSlot",
2344
+ "triggerShell": "capra-SelectField-module-1X4diq-triggerShell",
2345
+ "value": "capra-SelectField-module-1X4diq-value",
2346
+ "valueIcon": "capra-SelectField-module-1X4diq-valueIcon",
2347
+ "valueText": "capra-SelectField-module-1X4diq-valueText",
2348
+ "warningIndicator": "capra-SelectField-module-1X4diq-warningIndicator"
2349
+ };
2350
+ //#endregion
2351
+ //#region src/components/SelectField/SelectField.tsx
2352
+ /** When true, SelectField.Item indents by default (inside SelectField.Section that contains SelectField.Header). */
2353
+ const SelectFieldSectionIndentContext = react.createContext(false);
2354
+ function normalizeIterable(value) {
2355
+ return value === void 0 ? void 0 : Array.from(value);
2356
+ }
2357
+ function getItemLabelFromNode(node) {
2358
+ if (typeof node === "string") return node;
2359
+ if (typeof node === "number") return String(node);
2360
+ if (react.isValidElement(node)) {
2361
+ if (typeof node.props.textValue === "string") return node.props.textValue;
2362
+ return getItemLabelFromNode(node.props.children);
2363
+ }
2364
+ return "";
2365
+ }
2366
+ function removeKeysFromSelection(selection, keys) {
2367
+ return (selection ?? []).filter((selectedKey) => !keys.has(selectedKey));
2368
+ }
2369
+ function getItemIcon(item) {
2370
+ if (item && typeof item === "object" && "icon" in item) {
2371
+ const icon = item.icon;
2372
+ return icon && typeof icon === "object" && "__brand" in icon ? icon : void 0;
2373
+ }
2374
+ }
2375
+ function isDefaultSection(item) {
2376
+ const children = item.children;
2377
+ return children != null && typeof children !== "string" && typeof children[Symbol.iterator] === "function";
2378
+ }
2379
+ function SelectFieldIndicators(props) {
2380
+ const { appearance } = props;
2381
+ return /* @__PURE__ */ react.createElement(react.Fragment, null, appearance === "warning" ? /* @__PURE__ */ react.createElement("span", {
2382
+ className: SelectField_module_default.warningIndicator,
2383
+ "aria-hidden": true
2384
+ }, /* @__PURE__ */ react.createElement(_capra_icons.WarningOutlined, { size: "sm" })) : null, appearance === "danger" ? /* @__PURE__ */ react.createElement("span", {
2385
+ className: SelectField_module_default.errorIndicator,
2386
+ "aria-hidden": true
2387
+ }, /* @__PURE__ */ react.createElement(_capra_icons.Exclamation, { size: "sm" })) : null, /* @__PURE__ */ react.createElement("span", {
2388
+ className: SelectField_module_default.chevron,
2389
+ "aria-hidden": true
2390
+ }, /* @__PURE__ */ react.createElement(_capra_icons.ChevronDown, { size: "sm" })));
2391
+ }
2392
+ function SelectFieldTriggerButton(props) {
2393
+ const { appearance, buttonRef, children, className, ...rest } = props;
2394
+ const localRef = react.useRef(null);
2395
+ const ref = buttonRef ?? localRef;
2396
+ const [mergedProps, mergedRef] = (0, react_aria_components.useContextProps)({
2397
+ ...rest,
2398
+ className
2399
+ }, ref, react_aria_components.ButtonContext);
2400
+ const { buttonProps, isPressed } = (0, react_aria.useButton)(mergedProps, mergedRef);
2401
+ const { focusProps, isFocused, isFocusVisible } = (0, react_aria.useFocusRing)(mergedProps);
2402
+ return /* @__PURE__ */ react.createElement("button", {
2403
+ ...(0, react_aria.mergeProps)(buttonProps, focusProps),
2404
+ ref: mergedRef,
2405
+ className,
2406
+ "aria-invalid": appearance === "danger" ? true : void 0,
2407
+ "data-pressed": isPressed || void 0,
2408
+ "data-focused": isFocused || void 0,
2409
+ "data-focus-visible": isFocusVisible || void 0,
2410
+ "data-disabled": mergedProps.isDisabled || void 0
2411
+ }, children);
2412
+ }
2413
+ function SelectFieldTrigger(props) {
2414
+ const { appearance, disabled, leadingSlot, placeholder, required, selectedItemsLabel, size, triggerRef } = props;
2415
+ const state = react.useContext(react_aria_components.SelectStateContext);
2416
+ const openButtonRef = react.useRef(null);
2417
+ if (!state) return null;
2418
+ if (!(state.selectionManager.selectionMode === "multiple")) return /* @__PURE__ */ react.createElement(SelectFieldTriggerButton, {
2419
+ appearance,
2420
+ className: clsx(SelectField_module_default.triggerShell, SelectField_module_default.singleTrigger),
2421
+ "data-capra-anchor-width-target": "",
2422
+ "data-size": size,
2423
+ "data-disabled": disabled || void 0,
2424
+ "aria-required": required || void 0,
2425
+ isDisabled: disabled
2426
+ }, leadingSlot ? /* @__PURE__ */ react.createElement("span", { className: SelectField_module_default.leadingSlot }, leadingSlot) : null, /* @__PURE__ */ react.createElement(react_aria_components.SelectValue, { className: SelectField_module_default.value }, ({ isPlaceholder, selectedText, selectedItems }) => {
2427
+ const ValueIcon = getItemIcon(selectedItems[0]);
2428
+ return /* @__PURE__ */ react.createElement(react.Fragment, null, ValueIcon ? /* @__PURE__ */ react.createElement("span", {
2429
+ className: SelectField_module_default.valueIcon,
2430
+ "aria-hidden": true
2431
+ }, /* @__PURE__ */ react.createElement(ValueIcon, { size: "sm" })) : null, /* @__PURE__ */ react.createElement("span", {
2432
+ className: SelectField_module_default.valueText,
2433
+ "data-placeholder": isPlaceholder || !selectedText || void 0
2434
+ }, selectedText || placeholder));
2435
+ }), /* @__PURE__ */ react.createElement("span", { className: SelectField_module_default.trailingSlot }, /* @__PURE__ */ react.createElement(SelectFieldIndicators, { appearance })));
2436
+ const hasSelection = !state.selectionManager.isEmpty;
2437
+ const handleEmptyTriggerPress = (event) => {
2438
+ if (disabled || hasSelection || state.isOpen) return;
2439
+ if (event.target.closest("button")) return;
2440
+ openButtonRef.current?.focus();
2441
+ state.open();
2442
+ };
2443
+ return /* @__PURE__ */ react.createElement(react_aria_components.Group, {
2444
+ ref: triggerRef,
2445
+ className: clsx(SelectField_module_default.triggerShell, SelectField_module_default.multipleTrigger),
2446
+ "data-capra-anchor-width-target": "",
2447
+ "data-size": size,
2448
+ "data-disabled": disabled || void 0,
2449
+ "data-empty": hasSelection ? void 0 : "",
2450
+ onClick: handleEmptyTriggerPress
2451
+ }, leadingSlot ? /* @__PURE__ */ react.createElement("span", { className: SelectField_module_default.leadingSlot }, leadingSlot) : null, /* @__PURE__ */ react.createElement(react_aria_components.ButtonContext.Provider, { value: null }, /* @__PURE__ */ react.createElement(react_aria_components.SelectValue, { className: SelectField_module_default.value }, ({ state: selectState }) => {
2452
+ const tagItems = selectState.selectedItems.filter((item) => item != null).map((item) => ({
2453
+ id: item.key,
2454
+ label: item.textValue || getItemLabelFromNode(item.rendered) || String(item.key),
2455
+ icon: getItemIcon(item.value)
2456
+ }));
2457
+ return /* @__PURE__ */ react.createElement(react_aria_components.TagGroup, {
2458
+ "aria-label": selectedItemsLabel,
2459
+ className: SelectField_module_default.tagGroup,
2460
+ onRemove: disabled ? void 0 : (keys) => {
2461
+ const value = selectState.value;
2462
+ if (Array.isArray(value)) selectState.setValue(removeKeysFromSelection(value, keys));
2463
+ }
2464
+ }, /* @__PURE__ */ react.createElement(react_aria_components.TagList, {
2465
+ className: SelectField_module_default.tagList,
2466
+ items: tagItems,
2467
+ renderEmptyState: () => /* @__PURE__ */ react.createElement("span", {
2468
+ className: SelectField_module_default.valueText,
2469
+ "data-placeholder": placeholder ? true : void 0
2470
+ }, placeholder)
2471
+ }, (item) => /* @__PURE__ */ react.createElement(react_aria_components.Tag, {
2472
+ id: item.id,
2473
+ textValue: item.label,
2474
+ className: SelectField_module_default.tag,
2475
+ "data-size": size === "sm" ? "sm" : "md"
2476
+ }, ({ allowsRemoving }) => /* @__PURE__ */ react.createElement(react.Fragment, null, item.icon ? /* @__PURE__ */ react.createElement("span", {
2477
+ className: SelectField_module_default.tagIcon,
2478
+ "aria-hidden": true
2479
+ }, /* @__PURE__ */ react.createElement(item.icon, { size: "xs" })) : null, /* @__PURE__ */ react.createElement("span", { className: SelectField_module_default.tagLabel }, item.label), allowsRemoving ? /* @__PURE__ */ react.createElement(react_aria_components.Button, {
2480
+ slot: "remove",
2481
+ className: SelectField_module_default.tagRemove,
2482
+ "aria-label": "Delete",
2483
+ excludeFromTabOrder: true
2484
+ }, /* @__PURE__ */ react.createElement(_capra_icons.CloseOutlined, { size: "xs" })) : null))));
2485
+ })), /* @__PURE__ */ react.createElement(SelectFieldTriggerButton, {
2486
+ appearance,
2487
+ buttonRef: openButtonRef,
2488
+ className: SelectField_module_default.multipleOpenButton,
2489
+ isDisabled: disabled,
2490
+ "aria-required": required || void 0
2491
+ }, /* @__PURE__ */ react.createElement("span", { className: SelectField_module_default.trailingSlot }, /* @__PURE__ */ react.createElement(SelectFieldIndicators, { appearance }))));
2492
+ }
2493
+ function SelectFieldItem(props) {
2494
+ const { id, textValue, isDisabled, indent: indentProp, value, children } = props;
2495
+ const indentFromSectionHeader = react.useContext(SelectFieldSectionIndentContext);
2496
+ const indent = indentProp !== void 0 ? indentProp : indentFromSectionHeader;
2497
+ return /* @__PURE__ */ react.createElement(react_aria_components.ListBoxItem, {
2498
+ id,
2499
+ value,
2500
+ textValue: textValue || getItemLabelFromNode(children),
2501
+ isDisabled,
2502
+ className: SelectField_module_default.item,
2503
+ "data-indent": indent || void 0
2504
+ }, ({ isSelected, selectionMode }) => /* @__PURE__ */ react.createElement("div", { className: SelectField_module_default.itemContent }, selectionMode === "multiple" ? /* @__PURE__ */ react.createElement("span", {
2505
+ className: SelectField_module_default.itemCheck,
2506
+ "data-selected": isSelected || void 0,
2507
+ "aria-hidden": true
2508
+ }, isSelected ? /* @__PURE__ */ react.createElement(_capra_icons.CheckOutlined, { size: "sm" }) : null) : null, typeof children === "string" ? /* @__PURE__ */ react.createElement(Text, { slot: "label" }, children) : children));
2509
+ }
2510
+ function SelectFieldHeader(props) {
2511
+ const { id, label = "Header", FORCE__className: classNameOverride, children } = props;
2512
+ return /* @__PURE__ */ react.createElement(react_aria_components.Header, {
2513
+ id,
2514
+ className: clsx(SelectField_module_default.header, classNameOverride)
2515
+ }, children ?? label);
2516
+ }
2517
+ SelectFieldHeader.displayName = "SelectField.Header";
2518
+ function isSelectFieldHeaderElement(child) {
2519
+ return react.isValidElement(child) && child.type === SelectFieldHeader;
2520
+ }
2521
+ function SelectFieldSection(props) {
2522
+ const { children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, FORCE__className: classNameOverride, className, ...rest } = props;
2523
+ const autoHeaderId = react.useId();
2524
+ const childArray = react.Children.toArray(children);
2525
+ let headerIndex = -1;
2526
+ for (let i = 0; i < childArray.length; i++) if (isSelectFieldHeaderElement(childArray[i])) {
2527
+ headerIndex = i;
2528
+ break;
2529
+ }
2530
+ const hasHeader = headerIndex >= 0;
2531
+ if (process.env.NODE_ENV !== "production") {
2532
+ if (!hasHeader && !ariaLabel?.trim() && !ariaLabelledBy?.trim()) console.error("SelectField.Section: provide aria-label or aria-labelledby when the section has no direct SelectField.Header child.");
2533
+ }
2534
+ const labelledBy = hasHeader ? ariaLabelledBy ?? autoHeaderId : ariaLabelledBy;
2535
+ const effectiveAriaLabel = hasHeader ? void 0 : ariaLabel;
2536
+ const resolvedChildren = hasHeader && headerIndex >= 0 ? childArray.map((child, i) => {
2537
+ if (i === headerIndex && isSelectFieldHeaderElement(child)) return react.cloneElement(child, { id: child.props.id ?? autoHeaderId });
2538
+ return child;
2539
+ }) : children;
2540
+ return /* @__PURE__ */ react.createElement(SelectFieldSectionIndentContext.Provider, { value: hasHeader }, /* @__PURE__ */ react.createElement(react_aria_components.ListBoxSection, {
2541
+ "aria-label": effectiveAriaLabel,
2542
+ "aria-labelledby": labelledBy,
2543
+ className: clsx(SelectField_module_default.section, className, classNameOverride),
2544
+ ...rest
2545
+ }, resolvedChildren));
2546
+ }
2547
+ SelectFieldSection.displayName = "SelectField.Section";
2548
+ function defaultSelectFieldItemRenderer(item) {
2549
+ if (isDefaultSection(item)) {
2550
+ const { id, label, "aria-label": ariaLabel, children } = item;
2551
+ return /* @__PURE__ */ react.createElement(SelectFieldSection, {
2552
+ key: String(id),
2553
+ "aria-label": label ? void 0 : ariaLabel
2554
+ }, label ? /* @__PURE__ */ react.createElement(SelectFieldHeader, { label }) : null, /* @__PURE__ */ react.createElement(react_aria_components.Collection, { items: children }, (child) => defaultSelectFieldItemRenderer(child)));
2555
+ }
2556
+ const Icon = item.icon;
2557
+ return /* @__PURE__ */ react.createElement(SelectFieldItem, {
2558
+ id: item.id,
2559
+ textValue: item.label
2560
+ }, Icon ? /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(Icon, { size: "sm" }), /* @__PURE__ */ react.createElement(Text, { slot: "label" }, item.label)) : item.label);
2561
+ }
2562
+ function SelectFieldLabel(props) {
2563
+ const { children, required } = props;
2564
+ const { elementType: _elementType, ...labelContextWithoutElementType } = (0, react_aria_components.useSlottedContext)(react_aria_components.LabelContext) ?? {};
2565
+ return /* @__PURE__ */ react.createElement(react_aria_components.LabelContext.Provider, { value: labelContextWithoutElementType }, /* @__PURE__ */ react.createElement(Label, { required }, children));
2566
+ }
2567
+ function AutocompleteTextInput(props) {
2568
+ const { placeholder, shouldFocus, size } = props;
2569
+ const ref = react.useRef(null);
2570
+ const [mergedProps, mergedRef] = (0, react_aria_components.useContextProps)({
2571
+ "aria-label": props["aria-label"],
2572
+ placeholder,
2573
+ type: "search"
2574
+ }, ref, react_aria_components.FieldInputContext);
2575
+ const { onChange, value, ...inputProps } = mergedProps;
2576
+ const inputRef = mergedRef;
2577
+ react.useLayoutEffect(() => {
2578
+ if (!shouldFocus) return;
2579
+ const input = inputRef.current;
2580
+ if (!input) return;
2581
+ requestAnimationFrame(() => {
2582
+ try {
2583
+ input.focus({ preventScroll: true });
2584
+ } catch {}
2585
+ });
2586
+ }, [inputRef, shouldFocus]);
2587
+ return /* @__PURE__ */ react.createElement(TextInput, {
2588
+ ...inputProps,
2589
+ ref: inputRef,
2590
+ size,
2591
+ value: typeof value === "string" ? value : "",
2592
+ onChange: (event) => {
2593
+ onChange?.(event.target.value);
2594
+ }
2595
+ });
2596
+ }
2597
+ function SelectFieldInner(props, ref) {
2598
+ const { appearance = "default", autoFocus, canSearch, defaultOpen, disabled, disabledKeys, helperText, id, isOpen, items, children, label, layout, leadingSlot, name, onOpenChange, placeholder, required, searchPlaceholder, selectionMode = "single", shouldAutoSizeDropdown = true, size = "md", FORCE__className: classNameOverride, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy } = props;
2599
+ const [searchValue, setSearchValue] = react.useState("");
2600
+ const [popoverWidth, setPopoverWidth] = react.useState();
2601
+ const [uncontrolledOpen, setUncontrolledOpen] = react.useState(Boolean(defaultOpen));
2602
+ const isPopoverOpen = isOpen ?? uncontrolledOpen;
2603
+ const { contains } = (0, react_aria_components.useFilter)({ sensitivity: "base" });
2604
+ const [anchorRef, width, offset] = useAnchorWidth([
2605
+ canSearch,
2606
+ selectionMode,
2607
+ size
2608
+ ]);
2609
+ const multipleTriggerRef = react.useRef(null);
2610
+ const selectedItemsLabel = label ? `Selected ${label}` : ariaLabel ?? "Selected items";
2611
+ react.useImperativeHandle(ref, () => anchorRef.current, [anchorRef]);
2612
+ react.useLayoutEffect(() => {
2613
+ const trigger = multipleTriggerRef.current ?? anchorRef.current;
2614
+ if (selectionMode !== "multiple" || !isPopoverOpen || popoverWidth || !trigger) return;
2615
+ setPopoverWidth(`${trigger.offsetWidth}px`);
2616
+ }, [
2617
+ isPopoverOpen,
2618
+ popoverWidth,
2619
+ selectionMode,
2620
+ width,
2621
+ anchorRef
2622
+ ]);
2623
+ const itemList = react.useMemo(() => items ? Array.from(items) : void 0, [items]);
2624
+ const resolvedChildren = children ?? (itemList ? defaultSelectFieldItemRenderer : void 0);
2625
+ const handleOpenChange = react.useCallback((nextOpen) => {
2626
+ if (isOpen === void 0) setUncontrolledOpen(nextOpen);
2627
+ if (selectionMode === "multiple" && !nextOpen) setPopoverWidth(void 0);
2628
+ if (!nextOpen) setSearchValue("");
2629
+ onOpenChange?.(nextOpen);
2630
+ }, [
2631
+ isOpen,
2632
+ onOpenChange,
2633
+ selectionMode
2634
+ ]);
2635
+ const dropdownWidth = selectionMode === "multiple" ? popoverWidth ?? width ?? void 0 : width ?? void 0;
2636
+ const listBoxSearchProps = canSearch ? {
2637
+ autoFocus: false,
2638
+ renderEmptyState: () => /* @__PURE__ */ react.createElement("span", { className: SelectField_module_default.emptyState }, "No results")
2639
+ } : {};
2640
+ const listBox = /* @__PURE__ */ react.createElement(react_aria_components.ListBox, {
2641
+ ...listBoxSearchProps,
2642
+ className: SelectField_module_default.listbox,
2643
+ "data-selection-mode": selectionMode,
2644
+ items: itemList
2645
+ }, resolvedChildren);
2646
+ const popoverContent = canSearch ? /* @__PURE__ */ react.createElement(react_aria_components.Autocomplete, {
2647
+ filter: contains,
2648
+ inputValue: searchValue,
2649
+ onInputChange: setSearchValue
2650
+ }, /* @__PURE__ */ react.createElement("div", { className: SelectField_module_default.search }, /* @__PURE__ */ react.createElement(AutocompleteTextInput, {
2651
+ "aria-label": searchPlaceholder || "Search",
2652
+ placeholder: searchPlaceholder || "Search",
2653
+ shouldFocus: isPopoverOpen,
2654
+ size
2655
+ })), listBox) : listBox;
2656
+ const selectContent = /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement("div", { className: fieldLayout_module_default.labelRow }, /* @__PURE__ */ react.createElement(SelectFieldLabel, { required }, label)), /* @__PURE__ */ react.createElement("div", { className: fieldLayout_module_default.fieldContent }, /* @__PURE__ */ react.createElement(SelectFieldTrigger, {
2657
+ appearance,
2658
+ disabled,
2659
+ leadingSlot,
2660
+ placeholder,
2661
+ required,
2662
+ selectedItemsLabel,
2663
+ size,
2664
+ triggerRef: selectionMode === "multiple" ? multipleTriggerRef : void 0
2665
+ }), /* @__PURE__ */ react.createElement(HelperText, {
2666
+ appearance,
2667
+ disabled
2668
+ }, helperText)), /* @__PURE__ */ react.createElement(react_aria_components.Popover, {
2669
+ className: SelectField_module_default.dropdownPanel,
2670
+ offset: 2,
2671
+ crossOffset: offset,
2672
+ triggerRef: selectionMode === "multiple" ? multipleTriggerRef : void 0,
2673
+ style: { width: shouldAutoSizeDropdown ? dropdownWidth : void 0 }
2674
+ }, popoverContent));
2675
+ if (props.selectionMode === "multiple") {
2676
+ const handleChange = (nextValue) => {
2677
+ props.onChange?.(new Set(nextValue));
2678
+ };
2679
+ return /* @__PURE__ */ react.createElement(react_aria_components.Select, {
2680
+ id,
2681
+ ref: anchorRef,
2682
+ name,
2683
+ className: clsx(fieldLayout_module_default.field, classNameOverride),
2684
+ "data-capra-field-root": "",
2685
+ "data-layout": layout,
2686
+ isDisabled: disabled,
2687
+ isRequired: required,
2688
+ isInvalid: appearance === "danger",
2689
+ validationBehavior: "aria",
2690
+ placeholder,
2691
+ selectionMode: "multiple",
2692
+ disabledKeys,
2693
+ value: normalizeIterable(props.value),
2694
+ defaultValue: normalizeIterable(props.defaultValue),
2695
+ onChange: handleChange,
2696
+ isOpen,
2697
+ defaultOpen,
2698
+ onOpenChange: handleOpenChange,
2699
+ autoFocus,
2700
+ "aria-label": ariaLabel,
2701
+ "aria-labelledby": ariaLabelledBy,
2702
+ "aria-describedby": ariaDescribedBy
2703
+ }, selectContent);
2704
+ }
2705
+ const handleChange = (nextValue) => {
2706
+ props.onChange?.(nextValue ?? null);
2707
+ };
2708
+ return /* @__PURE__ */ react.createElement(react_aria_components.Select, {
2709
+ id,
2710
+ ref: anchorRef,
2711
+ name,
2712
+ className: clsx(fieldLayout_module_default.field, classNameOverride),
2713
+ "data-capra-field-root": "",
2714
+ "data-layout": layout,
2715
+ isDisabled: disabled,
2716
+ isRequired: required,
2717
+ isInvalid: appearance === "danger",
2718
+ validationBehavior: "aria",
2719
+ placeholder,
2720
+ selectionMode: "single",
2721
+ disabledKeys,
2722
+ value: props.value,
2723
+ defaultValue: props.defaultValue,
2724
+ onChange: handleChange,
2725
+ isOpen,
2726
+ defaultOpen,
2727
+ onOpenChange: handleOpenChange,
2728
+ autoFocus,
2729
+ "aria-label": ariaLabel,
2730
+ "aria-labelledby": ariaLabelledBy,
2731
+ "aria-describedby": ariaDescribedBy
2732
+ }, selectContent);
2733
+ }
2734
+ const SelectFieldRoot = react.forwardRef(SelectFieldInner);
2735
+ /**
2736
+ * Composed select field with Capra label/helper text layout, single and multiple selection, and an optional popover search input.
2737
+ */
2738
+ const SelectField = Object.assign(SelectFieldRoot, {
2739
+ Item: SelectFieldItem,
2740
+ Header: SelectFieldHeader,
2741
+ Section: SelectFieldSection
2742
+ });
2743
+ //#endregion
2316
2744
  //#region src/components/DateRangePickerField/DateRangePickerField.module.css
2317
2745
  var DateRangePickerField_module_default = {
2318
2746
  "cell": "capra-DateRangePickerField-module-QpPhpG-cell",
@@ -5234,6 +5662,7 @@ Object.defineProperty(exports, "RouterProvider", {
5234
5662
  });
5235
5663
  exports.SKELETON_SIZE = SKELETON_SIZE;
5236
5664
  exports.SKELETON_SIZE_TOKENS = SKELETON_SIZE_TOKENS;
5665
+ exports.SelectField = SelectField;
5237
5666
  exports.Skeleton = Skeleton;
5238
5667
  exports.SkeletonGroup = SkeletonGroup;
5239
5668
  exports.Spinner = Spinner;