infinity-ui-elements 1.8.11 → 1.8.13

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.js CHANGED
@@ -3214,7 +3214,7 @@ const defaultFilter = (item, query) => {
3214
3214
  return (item.label.toLowerCase().includes(searchQuery) ||
3215
3215
  (item.description?.toLowerCase().includes(searchQuery) ?? false));
3216
3216
  };
3217
- const SearchableDropdown = React__namespace.forwardRef(({ className, items = [], sectionHeading, isLoading = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText, secondaryButtonText, onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, footerLayout = "horizontal", onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, showAddNew = false, containerClassName, ...textFieldProps }, ref) => {
3217
+ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [], sectionHeading, isLoading = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText, secondaryButtonText, onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, footerLayout = "horizontal", onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, showAddNew = false, onAddNew, containerClassName, ...textFieldProps }, ref) => {
3218
3218
  const [uncontrolledSearchValue, setUncontrolledSearchValue] = React__namespace.useState(defaultSearchValue);
3219
3219
  const [isOpen, setIsOpen] = React__namespace.useState(false);
3220
3220
  const [focusedIndex, setFocusedIndex] = React__namespace.useState(-1);
@@ -3281,16 +3281,21 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3281
3281
  }
3282
3282
  const addNewItem = {
3283
3283
  value: searchValue,
3284
- label: `+ Add ${searchValue}`,
3284
+ label: `+ Add "${searchValue}"`,
3285
3285
  variant: "primary",
3286
3286
  onClick: () => {
3287
- const newItem = {
3288
- value: searchValue,
3289
- label: searchValue,
3290
- };
3291
- onItemSelect?.(newItem);
3292
- if (controlledSearchValue === undefined) {
3293
- setUncontrolledSearchValue(searchValue);
3287
+ if (onAddNew) {
3288
+ onAddNew(searchValue);
3289
+ }
3290
+ else {
3291
+ const newItem = {
3292
+ value: searchValue,
3293
+ label: searchValue,
3294
+ };
3295
+ onItemSelect?.(newItem);
3296
+ if (controlledSearchValue === undefined) {
3297
+ setUncontrolledSearchValue(searchValue);
3298
+ }
3294
3299
  }
3295
3300
  setIsOpen(false);
3296
3301
  inputRef.current?.focus();
@@ -3302,6 +3307,7 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3302
3307
  searchValue,
3303
3308
  filteredItems,
3304
3309
  onItemSelect,
3310
+ onAddNew,
3305
3311
  controlledSearchValue,
3306
3312
  ]);
3307
3313
  // Reset focused index when items change
@@ -3436,6 +3442,208 @@ const Skeleton = React__namespace.forwardRef(({ className, containerClassName, c
3436
3442
  });
3437
3443
  Skeleton.displayName = "Skeleton";
3438
3444
 
3445
+ const selectTriggerVariants = classVarianceAuthority.cva("flex items-center gap-1 transition-all font-display font-size-100 leading-100", {
3446
+ variants: {
3447
+ size: {
3448
+ small: "px-2 text-xs",
3449
+ medium: "px-2 text-sm",
3450
+ large: "px-3 text-base",
3451
+ },
3452
+ validationState: {
3453
+ none: "",
3454
+ positive: "",
3455
+ negative: "",
3456
+ },
3457
+ isDisabled: {
3458
+ true: "opacity-60 cursor-not-allowed",
3459
+ false: "cursor-pointer hover:opacity-80",
3460
+ },
3461
+ },
3462
+ defaultVariants: {
3463
+ size: "medium",
3464
+ validationState: "none",
3465
+ isDisabled: false,
3466
+ },
3467
+ });
3468
+ const SelectTextField = React__namespace.forwardRef(({ textValue: controlledTextValue, defaultTextValue, onTextChange, selectOptions = [], selectValue: controlledSelectValue, defaultSelectValue, onSelectChange, selectPlaceholder = "Select", selectTriggerClassName, selectMenuClassName, selectMenuWidth = "auto", selectSectionHeading, selectEmptyTitle = "No options available", selectEmptyDescription = "There are no options to select from.", selectEmptyIcon, label, helperText, errorText, successText, validationState = "none", isDisabled = false, isRequired = false, isOptional = false, size = "medium", containerClassName, labelClassName, inputClassName, className, ...textFieldProps }, ref) => {
3469
+ const [uncontrolledTextValue, setUncontrolledTextValue] = React__namespace.useState(defaultTextValue || "");
3470
+ const [uncontrolledSelectValue, setUncontrolledSelectValue] = React__namespace.useState(defaultSelectValue);
3471
+ const [isSelectOpen, setIsSelectOpen] = React__namespace.useState(false);
3472
+ const [dropdownPlacement, setDropdownPlacement] = React__namespace.useState("bottom");
3473
+ const selectRef = React__namespace.useRef(null);
3474
+ const dropdownContainerRef = React__namespace.useRef(null);
3475
+ const componentRef = React__namespace.useRef(null);
3476
+ const textValue = controlledTextValue !== undefined
3477
+ ? controlledTextValue
3478
+ : uncontrolledTextValue;
3479
+ const selectValue = controlledSelectValue !== undefined
3480
+ ? controlledSelectValue
3481
+ : uncontrolledSelectValue;
3482
+ // Find the selected option
3483
+ const selectedOption = selectOptions.find((opt) => opt.value === selectValue);
3484
+ // Determine which helper text to show
3485
+ const displayHelperText = errorText || successText || helperText;
3486
+ const currentValidationState = errorText
3487
+ ? "negative"
3488
+ : successText
3489
+ ? "positive"
3490
+ : validationState;
3491
+ const handleTextChange = (e) => {
3492
+ const newValue = e.target.value;
3493
+ if (onTextChange) {
3494
+ onTextChange(newValue);
3495
+ }
3496
+ else {
3497
+ setUncontrolledTextValue(newValue);
3498
+ }
3499
+ };
3500
+ const handleSelectOpenChange = (newOpen) => {
3501
+ if (!isDisabled) {
3502
+ setIsSelectOpen(newOpen);
3503
+ }
3504
+ };
3505
+ const toggleSelectOpen = () => {
3506
+ handleSelectOpenChange(!isSelectOpen);
3507
+ };
3508
+ const handleSelect = (option) => {
3509
+ if (controlledSelectValue === undefined) {
3510
+ setUncontrolledSelectValue(option.value);
3511
+ }
3512
+ onSelectChange?.(option.value, option);
3513
+ setIsSelectOpen(false);
3514
+ };
3515
+ const updateDropdownPlacement = React__namespace.useCallback(() => {
3516
+ if (typeof window === "undefined")
3517
+ return;
3518
+ const trigger = selectRef.current;
3519
+ if (!trigger)
3520
+ return;
3521
+ const triggerRect = trigger.getBoundingClientRect();
3522
+ const spaceBelow = window.innerHeight - triggerRect.bottom;
3523
+ const spaceAbove = triggerRect.top;
3524
+ const dropdownHeight = dropdownContainerRef.current
3525
+ ? dropdownContainerRef.current.offsetHeight
3526
+ : 0;
3527
+ if (dropdownHeight === 0) {
3528
+ setDropdownPlacement(spaceBelow >= spaceAbove ? "bottom" : "top");
3529
+ return;
3530
+ }
3531
+ if (spaceBelow >= dropdownHeight || spaceBelow >= spaceAbove) {
3532
+ setDropdownPlacement("bottom");
3533
+ }
3534
+ else {
3535
+ setDropdownPlacement("top");
3536
+ }
3537
+ }, []);
3538
+ const attachDropdownListeners = React__namespace.useCallback(() => {
3539
+ if (!isSelectOpen)
3540
+ return;
3541
+ if (typeof window === "undefined")
3542
+ return;
3543
+ let rafId = requestAnimationFrame(updateDropdownPlacement);
3544
+ const handleUpdate = () => updateDropdownPlacement();
3545
+ window.addEventListener("resize", handleUpdate);
3546
+ window.addEventListener("scroll", handleUpdate, true);
3547
+ return () => {
3548
+ cancelAnimationFrame(rafId);
3549
+ window.removeEventListener("resize", handleUpdate);
3550
+ window.removeEventListener("scroll", handleUpdate, true);
3551
+ };
3552
+ }, [isSelectOpen, updateDropdownPlacement]);
3553
+ React__namespace.useEffect(() => {
3554
+ const detach = attachDropdownListeners();
3555
+ return () => {
3556
+ detach?.();
3557
+ };
3558
+ }, [attachDropdownListeners]);
3559
+ React__namespace.useEffect(() => {
3560
+ if (isSelectOpen) {
3561
+ updateDropdownPlacement();
3562
+ }
3563
+ }, [isSelectOpen, selectOptions.length, updateDropdownPlacement]);
3564
+ // Close dropdown when clicking outside
3565
+ React__namespace.useEffect(() => {
3566
+ const handleClickOutside = (event) => {
3567
+ const target = event.target;
3568
+ if (selectRef.current &&
3569
+ !selectRef.current.contains(target) &&
3570
+ dropdownContainerRef.current &&
3571
+ !dropdownContainerRef.current.contains(target)) {
3572
+ handleSelectOpenChange(false);
3573
+ }
3574
+ };
3575
+ if (isSelectOpen) {
3576
+ document.addEventListener("mousedown", handleClickOutside);
3577
+ return () => {
3578
+ document.removeEventListener("mousedown", handleClickOutside);
3579
+ };
3580
+ }
3581
+ }, [isSelectOpen]);
3582
+ // Close on escape key
3583
+ React__namespace.useEffect(() => {
3584
+ const handleEscape = (event) => {
3585
+ if (event.key === "Escape") {
3586
+ handleSelectOpenChange(false);
3587
+ }
3588
+ };
3589
+ if (isSelectOpen) {
3590
+ document.addEventListener("keydown", handleEscape);
3591
+ return () => {
3592
+ document.removeEventListener("keydown", handleEscape);
3593
+ };
3594
+ }
3595
+ }, [isSelectOpen]);
3596
+ // Transform options to dropdown menu items
3597
+ const menuItems = selectOptions.map((option) => ({
3598
+ value: option.value,
3599
+ label: option.label ?? String(option.value),
3600
+ description: option.description,
3601
+ leadingIcon: option.leadingIcon,
3602
+ trailingIcon: option.trailingIcon,
3603
+ isDisabled: option.isDisabled,
3604
+ variant: option.variant,
3605
+ onClick: () => handleSelect(option),
3606
+ }));
3607
+ const widthStyle = selectMenuWidth === "full"
3608
+ ? "100%"
3609
+ : selectMenuWidth === "auto"
3610
+ ? "auto"
3611
+ : selectMenuWidth;
3612
+ const sizeConfig = {
3613
+ small: {
3614
+ gap: "gap-2",
3615
+ },
3616
+ medium: {
3617
+ gap: "gap-2",
3618
+ },
3619
+ large: {
3620
+ gap: "gap-3",
3621
+ },
3622
+ };
3623
+ // Create the select suffix component
3624
+ const selectSuffix = (jsxRuntime.jsxs("div", { className: "relative flex items-center h-full", children: [jsxRuntime.jsxs("div", { ref: selectRef, className: cn(selectTriggerVariants({
3625
+ size,
3626
+ validationState: currentValidationState,
3627
+ isDisabled,
3628
+ }), "border-l border-action-outline-neutral-faded pl-2 ml-2 h-full flex items-center", selectTriggerClassName), onClick: !isDisabled ? toggleSelectOpen : undefined, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isSelectOpen, "aria-disabled": isDisabled, children: [jsxRuntime.jsx("span", { className: cn("text-left truncate max-w-[120px] whitespace-nowrap", !selectedOption && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: selectedOption?.label || selectPlaceholder }), jsxRuntime.jsx(lucideReact.ChevronDown, { className: cn("shrink-0 transition-transform", size === "small"
3629
+ ? "w-3 h-3"
3630
+ : size === "medium"
3631
+ ? "w-3.5 h-3.5"
3632
+ : "w-4 h-4", isDisabled
3633
+ ? "text-surface-ink-neutral-disabled"
3634
+ : currentValidationState === "positive"
3635
+ ? "text-feedback-ink-positive-intense"
3636
+ : currentValidationState === "negative"
3637
+ ? "text-feedback-ink-negative-subtle"
3638
+ : "text-surface-ink-neutral-muted", isSelectOpen && "transform rotate-180") })] }), isSelectOpen && !isDisabled && (jsxRuntime.jsx("div", { ref: dropdownContainerRef, className: cn("absolute z-50 right-0", dropdownPlacement === "bottom"
3639
+ ? "top-full mt-1"
3640
+ : "bottom-full mb-1"), children: jsxRuntime.jsx(DropdownMenu, { items: menuItems, sectionHeading: selectSectionHeading, isEmpty: selectOptions.length === 0, emptyTitle: selectEmptyTitle, emptyDescription: selectEmptyDescription, emptyIcon: selectEmptyIcon, disableFooter: true, onClose: () => handleSelectOpenChange(false), className: selectMenuClassName, width: widthStyle }) }))] }));
3641
+ return (jsxRuntime.jsxs("div", { ref: componentRef, className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (jsxRuntime.jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: textFieldProps.infoHeading, infoDescription: textFieldProps.infoDescription, LinkComponent: textFieldProps.LinkComponent, linkText: textFieldProps.linkText, linkHref: textFieldProps.linkHref, onLinkClick: textFieldProps.onLinkClick, htmlFor: textFieldProps.id, className: "mb-2", labelClassName: labelClassName })), jsxRuntime.jsx(TextField, { ref: ref, value: textValue, onChange: handleTextChange, suffix: selectSuffix, size: size, validationState: currentValidationState, isDisabled: isDisabled, isRequired: isRequired, isOptional: isOptional, containerClassName: "gap-0", className: className, inputClassName: inputClassName, ...textFieldProps }), jsxRuntime.jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
3642
+ ? "default"
3643
+ : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
3644
+ });
3645
+ SelectTextField.displayName = "SelectTextField";
3646
+
3439
3647
  const switchVariants = classVarianceAuthority.cva("relative inline-flex items-center shrink-0 cursor-pointer rounded-full transition-all duration-200", {
3440
3648
  variants: {
3441
3649
  size: {
@@ -4054,6 +4262,7 @@ exports.Pagination = Pagination;
4054
4262
  exports.Radio = Radio;
4055
4263
  exports.SearchableDropdown = SearchableDropdown;
4056
4264
  exports.Select = Select;
4265
+ exports.SelectTextField = SelectTextField;
4057
4266
  exports.Skeleton = Skeleton;
4058
4267
  exports.SlotCell = SlotCell;
4059
4268
  exports.SpacerCell = SpacerCell;
@@ -4084,6 +4293,7 @@ exports.linkVariants = linkVariants;
4084
4293
  exports.listItemVariants = listItemVariants;
4085
4294
  exports.paginationVariants = paginationVariants;
4086
4295
  exports.radioVariants = radioVariants;
4296
+ exports.selectTriggerVariants = selectTriggerVariants;
4087
4297
  exports.selectVariants = selectVariants;
4088
4298
  exports.switchVariants = switchVariants;
4089
4299
  exports.tableCellVariants = tableCellVariants;