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/components/SearchableDropdown/SearchableDropdown.d.ts +5 -0
- package/dist/components/SearchableDropdown/SearchableDropdown.d.ts.map +1 -1
- package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts +1 -0
- package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts.map +1 -1
- package/dist/components/SelectTextField/SelectTextField.d.ts +73 -0
- package/dist/components/SelectTextField/SelectTextField.d.ts.map +1 -0
- package/dist/components/SelectTextField/SelectTextField.stories.d.ts +21 -0
- package/dist/components/SelectTextField/SelectTextField.stories.d.ts.map +1 -0
- package/dist/components/SelectTextField/index.d.ts +3 -0
- package/dist/components/SelectTextField/index.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +218 -10
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +219 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -3193,7 +3193,7 @@ const defaultFilter = (item, query) => {
|
|
|
3193
3193
|
return (item.label.toLowerCase().includes(searchQuery) ||
|
|
3194
3194
|
(item.description?.toLowerCase().includes(searchQuery) ?? false));
|
|
3195
3195
|
};
|
|
3196
|
-
const SearchableDropdown = React.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) => {
|
|
3196
|
+
const SearchableDropdown = React.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) => {
|
|
3197
3197
|
const [uncontrolledSearchValue, setUncontrolledSearchValue] = React.useState(defaultSearchValue);
|
|
3198
3198
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
3199
3199
|
const [focusedIndex, setFocusedIndex] = React.useState(-1);
|
|
@@ -3260,16 +3260,21 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
|
|
|
3260
3260
|
}
|
|
3261
3261
|
const addNewItem = {
|
|
3262
3262
|
value: searchValue,
|
|
3263
|
-
label: `+ Add ${searchValue}`,
|
|
3263
|
+
label: `+ Add "${searchValue}"`,
|
|
3264
3264
|
variant: "primary",
|
|
3265
3265
|
onClick: () => {
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3266
|
+
if (onAddNew) {
|
|
3267
|
+
onAddNew(searchValue);
|
|
3268
|
+
}
|
|
3269
|
+
else {
|
|
3270
|
+
const newItem = {
|
|
3271
|
+
value: searchValue,
|
|
3272
|
+
label: searchValue,
|
|
3273
|
+
};
|
|
3274
|
+
onItemSelect?.(newItem);
|
|
3275
|
+
if (controlledSearchValue === undefined) {
|
|
3276
|
+
setUncontrolledSearchValue(searchValue);
|
|
3277
|
+
}
|
|
3273
3278
|
}
|
|
3274
3279
|
setIsOpen(false);
|
|
3275
3280
|
inputRef.current?.focus();
|
|
@@ -3281,6 +3286,7 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
|
|
|
3281
3286
|
searchValue,
|
|
3282
3287
|
filteredItems,
|
|
3283
3288
|
onItemSelect,
|
|
3289
|
+
onAddNew,
|
|
3284
3290
|
controlledSearchValue,
|
|
3285
3291
|
]);
|
|
3286
3292
|
// Reset focused index when items change
|
|
@@ -3415,6 +3421,208 @@ const Skeleton = React.forwardRef(({ className, containerClassName, containerSty
|
|
|
3415
3421
|
});
|
|
3416
3422
|
Skeleton.displayName = "Skeleton";
|
|
3417
3423
|
|
|
3424
|
+
const selectTriggerVariants = cva("flex items-center gap-1 transition-all font-display font-size-100 leading-100", {
|
|
3425
|
+
variants: {
|
|
3426
|
+
size: {
|
|
3427
|
+
small: "px-2 text-xs",
|
|
3428
|
+
medium: "px-2 text-sm",
|
|
3429
|
+
large: "px-3 text-base",
|
|
3430
|
+
},
|
|
3431
|
+
validationState: {
|
|
3432
|
+
none: "",
|
|
3433
|
+
positive: "",
|
|
3434
|
+
negative: "",
|
|
3435
|
+
},
|
|
3436
|
+
isDisabled: {
|
|
3437
|
+
true: "opacity-60 cursor-not-allowed",
|
|
3438
|
+
false: "cursor-pointer hover:opacity-80",
|
|
3439
|
+
},
|
|
3440
|
+
},
|
|
3441
|
+
defaultVariants: {
|
|
3442
|
+
size: "medium",
|
|
3443
|
+
validationState: "none",
|
|
3444
|
+
isDisabled: false,
|
|
3445
|
+
},
|
|
3446
|
+
});
|
|
3447
|
+
const SelectTextField = React.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) => {
|
|
3448
|
+
const [uncontrolledTextValue, setUncontrolledTextValue] = React.useState(defaultTextValue || "");
|
|
3449
|
+
const [uncontrolledSelectValue, setUncontrolledSelectValue] = React.useState(defaultSelectValue);
|
|
3450
|
+
const [isSelectOpen, setIsSelectOpen] = React.useState(false);
|
|
3451
|
+
const [dropdownPlacement, setDropdownPlacement] = React.useState("bottom");
|
|
3452
|
+
const selectRef = React.useRef(null);
|
|
3453
|
+
const dropdownContainerRef = React.useRef(null);
|
|
3454
|
+
const componentRef = React.useRef(null);
|
|
3455
|
+
const textValue = controlledTextValue !== undefined
|
|
3456
|
+
? controlledTextValue
|
|
3457
|
+
: uncontrolledTextValue;
|
|
3458
|
+
const selectValue = controlledSelectValue !== undefined
|
|
3459
|
+
? controlledSelectValue
|
|
3460
|
+
: uncontrolledSelectValue;
|
|
3461
|
+
// Find the selected option
|
|
3462
|
+
const selectedOption = selectOptions.find((opt) => opt.value === selectValue);
|
|
3463
|
+
// Determine which helper text to show
|
|
3464
|
+
const displayHelperText = errorText || successText || helperText;
|
|
3465
|
+
const currentValidationState = errorText
|
|
3466
|
+
? "negative"
|
|
3467
|
+
: successText
|
|
3468
|
+
? "positive"
|
|
3469
|
+
: validationState;
|
|
3470
|
+
const handleTextChange = (e) => {
|
|
3471
|
+
const newValue = e.target.value;
|
|
3472
|
+
if (onTextChange) {
|
|
3473
|
+
onTextChange(newValue);
|
|
3474
|
+
}
|
|
3475
|
+
else {
|
|
3476
|
+
setUncontrolledTextValue(newValue);
|
|
3477
|
+
}
|
|
3478
|
+
};
|
|
3479
|
+
const handleSelectOpenChange = (newOpen) => {
|
|
3480
|
+
if (!isDisabled) {
|
|
3481
|
+
setIsSelectOpen(newOpen);
|
|
3482
|
+
}
|
|
3483
|
+
};
|
|
3484
|
+
const toggleSelectOpen = () => {
|
|
3485
|
+
handleSelectOpenChange(!isSelectOpen);
|
|
3486
|
+
};
|
|
3487
|
+
const handleSelect = (option) => {
|
|
3488
|
+
if (controlledSelectValue === undefined) {
|
|
3489
|
+
setUncontrolledSelectValue(option.value);
|
|
3490
|
+
}
|
|
3491
|
+
onSelectChange?.(option.value, option);
|
|
3492
|
+
setIsSelectOpen(false);
|
|
3493
|
+
};
|
|
3494
|
+
const updateDropdownPlacement = React.useCallback(() => {
|
|
3495
|
+
if (typeof window === "undefined")
|
|
3496
|
+
return;
|
|
3497
|
+
const trigger = selectRef.current;
|
|
3498
|
+
if (!trigger)
|
|
3499
|
+
return;
|
|
3500
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
3501
|
+
const spaceBelow = window.innerHeight - triggerRect.bottom;
|
|
3502
|
+
const spaceAbove = triggerRect.top;
|
|
3503
|
+
const dropdownHeight = dropdownContainerRef.current
|
|
3504
|
+
? dropdownContainerRef.current.offsetHeight
|
|
3505
|
+
: 0;
|
|
3506
|
+
if (dropdownHeight === 0) {
|
|
3507
|
+
setDropdownPlacement(spaceBelow >= spaceAbove ? "bottom" : "top");
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
if (spaceBelow >= dropdownHeight || spaceBelow >= spaceAbove) {
|
|
3511
|
+
setDropdownPlacement("bottom");
|
|
3512
|
+
}
|
|
3513
|
+
else {
|
|
3514
|
+
setDropdownPlacement("top");
|
|
3515
|
+
}
|
|
3516
|
+
}, []);
|
|
3517
|
+
const attachDropdownListeners = React.useCallback(() => {
|
|
3518
|
+
if (!isSelectOpen)
|
|
3519
|
+
return;
|
|
3520
|
+
if (typeof window === "undefined")
|
|
3521
|
+
return;
|
|
3522
|
+
let rafId = requestAnimationFrame(updateDropdownPlacement);
|
|
3523
|
+
const handleUpdate = () => updateDropdownPlacement();
|
|
3524
|
+
window.addEventListener("resize", handleUpdate);
|
|
3525
|
+
window.addEventListener("scroll", handleUpdate, true);
|
|
3526
|
+
return () => {
|
|
3527
|
+
cancelAnimationFrame(rafId);
|
|
3528
|
+
window.removeEventListener("resize", handleUpdate);
|
|
3529
|
+
window.removeEventListener("scroll", handleUpdate, true);
|
|
3530
|
+
};
|
|
3531
|
+
}, [isSelectOpen, updateDropdownPlacement]);
|
|
3532
|
+
React.useEffect(() => {
|
|
3533
|
+
const detach = attachDropdownListeners();
|
|
3534
|
+
return () => {
|
|
3535
|
+
detach?.();
|
|
3536
|
+
};
|
|
3537
|
+
}, [attachDropdownListeners]);
|
|
3538
|
+
React.useEffect(() => {
|
|
3539
|
+
if (isSelectOpen) {
|
|
3540
|
+
updateDropdownPlacement();
|
|
3541
|
+
}
|
|
3542
|
+
}, [isSelectOpen, selectOptions.length, updateDropdownPlacement]);
|
|
3543
|
+
// Close dropdown when clicking outside
|
|
3544
|
+
React.useEffect(() => {
|
|
3545
|
+
const handleClickOutside = (event) => {
|
|
3546
|
+
const target = event.target;
|
|
3547
|
+
if (selectRef.current &&
|
|
3548
|
+
!selectRef.current.contains(target) &&
|
|
3549
|
+
dropdownContainerRef.current &&
|
|
3550
|
+
!dropdownContainerRef.current.contains(target)) {
|
|
3551
|
+
handleSelectOpenChange(false);
|
|
3552
|
+
}
|
|
3553
|
+
};
|
|
3554
|
+
if (isSelectOpen) {
|
|
3555
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
3556
|
+
return () => {
|
|
3557
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3560
|
+
}, [isSelectOpen]);
|
|
3561
|
+
// Close on escape key
|
|
3562
|
+
React.useEffect(() => {
|
|
3563
|
+
const handleEscape = (event) => {
|
|
3564
|
+
if (event.key === "Escape") {
|
|
3565
|
+
handleSelectOpenChange(false);
|
|
3566
|
+
}
|
|
3567
|
+
};
|
|
3568
|
+
if (isSelectOpen) {
|
|
3569
|
+
document.addEventListener("keydown", handleEscape);
|
|
3570
|
+
return () => {
|
|
3571
|
+
document.removeEventListener("keydown", handleEscape);
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
}, [isSelectOpen]);
|
|
3575
|
+
// Transform options to dropdown menu items
|
|
3576
|
+
const menuItems = selectOptions.map((option) => ({
|
|
3577
|
+
value: option.value,
|
|
3578
|
+
label: option.label ?? String(option.value),
|
|
3579
|
+
description: option.description,
|
|
3580
|
+
leadingIcon: option.leadingIcon,
|
|
3581
|
+
trailingIcon: option.trailingIcon,
|
|
3582
|
+
isDisabled: option.isDisabled,
|
|
3583
|
+
variant: option.variant,
|
|
3584
|
+
onClick: () => handleSelect(option),
|
|
3585
|
+
}));
|
|
3586
|
+
const widthStyle = selectMenuWidth === "full"
|
|
3587
|
+
? "100%"
|
|
3588
|
+
: selectMenuWidth === "auto"
|
|
3589
|
+
? "auto"
|
|
3590
|
+
: selectMenuWidth;
|
|
3591
|
+
const sizeConfig = {
|
|
3592
|
+
small: {
|
|
3593
|
+
gap: "gap-2",
|
|
3594
|
+
},
|
|
3595
|
+
medium: {
|
|
3596
|
+
gap: "gap-2",
|
|
3597
|
+
},
|
|
3598
|
+
large: {
|
|
3599
|
+
gap: "gap-3",
|
|
3600
|
+
},
|
|
3601
|
+
};
|
|
3602
|
+
// Create the select suffix component
|
|
3603
|
+
const selectSuffix = (jsxs("div", { className: "relative flex items-center h-full", children: [jsxs("div", { ref: selectRef, className: cn(selectTriggerVariants({
|
|
3604
|
+
size,
|
|
3605
|
+
validationState: currentValidationState,
|
|
3606
|
+
isDisabled,
|
|
3607
|
+
}), "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: [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 }), jsx(ChevronDown, { className: cn("shrink-0 transition-transform", size === "small"
|
|
3608
|
+
? "w-3 h-3"
|
|
3609
|
+
: size === "medium"
|
|
3610
|
+
? "w-3.5 h-3.5"
|
|
3611
|
+
: "w-4 h-4", isDisabled
|
|
3612
|
+
? "text-surface-ink-neutral-disabled"
|
|
3613
|
+
: currentValidationState === "positive"
|
|
3614
|
+
? "text-feedback-ink-positive-intense"
|
|
3615
|
+
: currentValidationState === "negative"
|
|
3616
|
+
? "text-feedback-ink-negative-subtle"
|
|
3617
|
+
: "text-surface-ink-neutral-muted", isSelectOpen && "transform rotate-180") })] }), isSelectOpen && !isDisabled && (jsx("div", { ref: dropdownContainerRef, className: cn("absolute z-50 right-0", dropdownPlacement === "bottom"
|
|
3618
|
+
? "top-full mt-1"
|
|
3619
|
+
: "bottom-full mb-1"), children: 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 }) }))] }));
|
|
3620
|
+
return (jsxs("div", { ref: componentRef, className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (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 })), 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 }), jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
|
|
3621
|
+
? "default"
|
|
3622
|
+
: currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
|
|
3623
|
+
});
|
|
3624
|
+
SelectTextField.displayName = "SelectTextField";
|
|
3625
|
+
|
|
3418
3626
|
const switchVariants = cva("relative inline-flex items-center shrink-0 cursor-pointer rounded-full transition-all duration-200", {
|
|
3419
3627
|
variants: {
|
|
3420
3628
|
size: {
|
|
@@ -4007,5 +4215,5 @@ const TextArea = React.forwardRef(({ label, helperText, errorText, successText,
|
|
|
4007
4215
|
});
|
|
4008
4216
|
TextArea.displayName = "TextArea";
|
|
4009
4217
|
|
|
4010
|
-
export { Alert, Amount, Avatar, AvatarCell, Badge, Button, ButtonGroup, Checkbox, Counter, DatePicker, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, IconButton, IconCell, Link, ListItem, Modal, NumberCell, Pagination, Radio, SearchableDropdown, Select, Skeleton, SlotCell, SpacerCell, Switch, TabItem, Table, TableDetailPanel, Tabs, Text, TextArea, TextField, Tooltip, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, checkboxVariants, cn, counterVariants, datePickerVariants, dropdownVariants, getAvailableIcons, hasIcon, iconButtonVariants, iconRegistry, linkVariants, listItemVariants, paginationVariants, radioVariants, selectVariants, switchVariants, tableCellVariants, tableHeaderVariants, tableVariants, textAreaVariants, textFieldVariants, tooltipVariants };
|
|
4218
|
+
export { Alert, Amount, Avatar, AvatarCell, Badge, Button, ButtonGroup, Checkbox, Counter, DatePicker, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, IconButton, IconCell, Link, ListItem, Modal, NumberCell, Pagination, Radio, SearchableDropdown, Select, SelectTextField, Skeleton, SlotCell, SpacerCell, Switch, TabItem, Table, TableDetailPanel, Tabs, Text, TextArea, TextField, Tooltip, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, checkboxVariants, cn, counterVariants, datePickerVariants, dropdownVariants, getAvailableIcons, hasIcon, iconButtonVariants, iconRegistry, linkVariants, listItemVariants, paginationVariants, radioVariants, selectTriggerVariants, selectVariants, switchVariants, tableCellVariants, tableHeaderVariants, tableVariants, textAreaVariants, textFieldVariants, tooltipVariants };
|
|
4011
4219
|
//# sourceMappingURL=index.esm.js.map
|