infinity-ui-elements 1.4.1-beta.2 → 1.4.1-beta.4
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/Dropdown/Dropdown.d.ts +99 -0
- package/dist/components/Dropdown/Dropdown.d.ts.map +1 -0
- package/dist/components/Dropdown/Dropdown.stories.d.ts +342 -0
- package/dist/components/Dropdown/Dropdown.stories.d.ts.map +1 -0
- package/dist/components/Dropdown/DropdownMenu.d.ts +90 -0
- package/dist/components/Dropdown/DropdownMenu.d.ts.map +1 -0
- package/dist/components/Dropdown/index.d.ts +3 -0
- package/dist/components/Dropdown/index.d.ts.map +1 -0
- package/dist/components/Link/Link.d.ts.map +1 -1
- package/dist/components/ListItem/ListItem.stories.d.ts.map +1 -1
- package/dist/components/SearchableDropdown/SearchableDropdown.d.ts +103 -0
- package/dist/components/SearchableDropdown/SearchableDropdown.d.ts.map +1 -0
- package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts +407 -0
- package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts.map +1 -0
- package/dist/components/SearchableDropdown/index.d.ts +2 -0
- package/dist/components/SearchableDropdown/index.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +510 -317
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +512 -315
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -5,7 +5,7 @@ import { clsx } from 'clsx';
|
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { Slot } from '@radix-ui/react-slot';
|
|
7
7
|
import { PulseLoader, ClipLoader } from 'react-spinners';
|
|
8
|
-
import { ExternalLink } from 'lucide-react';
|
|
8
|
+
import { ExternalLink, Loader2, Search } from 'lucide-react';
|
|
9
9
|
|
|
10
10
|
// Define patterns for custom classes that should be preserved
|
|
11
11
|
// This approach is more scalable than hardcoding individual class names
|
|
@@ -990,6 +990,303 @@ const Divider = React.forwardRef(({ className, orientation = "horizontal", thick
|
|
|
990
990
|
});
|
|
991
991
|
Divider.displayName = "Divider";
|
|
992
992
|
|
|
993
|
+
const listItemVariants = cva("flex items-start gap-3 p-3 rounded-medium transition-colors cursor-pointer", {
|
|
994
|
+
variants: {
|
|
995
|
+
variant: {
|
|
996
|
+
default: `hover:bg-action-fill-neutral-faded
|
|
997
|
+
focus:bg-action-fill-neutral-faded
|
|
998
|
+
focus:ring-2
|
|
999
|
+
ring-action-outline-primary-faded-hover
|
|
1000
|
+
border border-transparent
|
|
1001
|
+
`,
|
|
1002
|
+
bordered: "border border-action-outline-primary-faded hover:bg-surface-fill-primary-subtle",
|
|
1003
|
+
primary: `hover:bg-action-fill-neutral-faded
|
|
1004
|
+
focus:bg-action-fill-neutral-faded
|
|
1005
|
+
focus:ring-2
|
|
1006
|
+
ring-action-outline-primary-faded-hover
|
|
1007
|
+
border border-transparent
|
|
1008
|
+
`,
|
|
1009
|
+
negative: `hover:bg-action-fill-negative-faded
|
|
1010
|
+
focus:bg-action-fill-negative-faded
|
|
1011
|
+
focus:ring-2 ring-action-outline-negative-faded-hover
|
|
1012
|
+
border border-transparent
|
|
1013
|
+
`,
|
|
1014
|
+
},
|
|
1015
|
+
isDisabled: {
|
|
1016
|
+
true: "cursor-not-allowed opacity-60",
|
|
1017
|
+
false: "",
|
|
1018
|
+
},
|
|
1019
|
+
isSelected: {
|
|
1020
|
+
true: "bg-action-fill-primary-faded border-action-outline-primary-faded",
|
|
1021
|
+
false: "",
|
|
1022
|
+
},
|
|
1023
|
+
},
|
|
1024
|
+
defaultVariants: {
|
|
1025
|
+
variant: "default",
|
|
1026
|
+
isDisabled: false,
|
|
1027
|
+
isSelected: false,
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
const ChevronRightIcon = ({ className }) => (jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, children: jsx("path", { d: "M7.5 15L12.5 10L7.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
1031
|
+
const ListItem = React.forwardRef(({ className, type = "single", leadingIcon, title, description, trailingIcon, showChevron = true, variant = "default", isDisabled = false, isSelected = false, onSelectionChange, checkboxSize = "small", containerClassName, contentClassName, onClick, ...props }, ref) => {
|
|
1032
|
+
const [internalSelected, setInternalSelected] = React.useState(isSelected);
|
|
1033
|
+
// Sync internal state with prop
|
|
1034
|
+
React.useEffect(() => {
|
|
1035
|
+
setInternalSelected(isSelected);
|
|
1036
|
+
}, [isSelected]);
|
|
1037
|
+
const handleClick = (e) => {
|
|
1038
|
+
if (isDisabled)
|
|
1039
|
+
return;
|
|
1040
|
+
if (type === "multiple") {
|
|
1041
|
+
const newSelected = !internalSelected;
|
|
1042
|
+
setInternalSelected(newSelected);
|
|
1043
|
+
onSelectionChange?.(newSelected);
|
|
1044
|
+
}
|
|
1045
|
+
onClick?.(e);
|
|
1046
|
+
};
|
|
1047
|
+
const handleCheckboxChange = (e) => {
|
|
1048
|
+
e.stopPropagation();
|
|
1049
|
+
if (isDisabled)
|
|
1050
|
+
return;
|
|
1051
|
+
const newSelected = e.target.checked;
|
|
1052
|
+
setInternalSelected(newSelected);
|
|
1053
|
+
onSelectionChange?.(newSelected);
|
|
1054
|
+
};
|
|
1055
|
+
return (jsxs("div", { ref: ref, className: cn(listItemVariants({
|
|
1056
|
+
variant,
|
|
1057
|
+
isDisabled,
|
|
1058
|
+
isSelected: type === "multiple" ? internalSelected : false,
|
|
1059
|
+
}), containerClassName), onClick: handleClick, role: type === "multiple" ? "checkbox" : "button", "aria-checked": type === "multiple" ? internalSelected : undefined, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, ...props, children: [type === "multiple" && (jsx(Checkbox, { checked: internalSelected, onChange: handleCheckboxChange, isDisabled: isDisabled, size: checkboxSize, className: "shrink-0 mt-0.5" })), leadingIcon && (jsx("div", { className: cn(`shrink-0 flex items-center justify-center mt-0.5`, variant === "primary"
|
|
1060
|
+
? "text-action-ink-primary-normal"
|
|
1061
|
+
: variant === "negative"
|
|
1062
|
+
? "text-action-ink-negative-normal"
|
|
1063
|
+
: "text-action-ink-neutral-subtle", isDisabled && "text-surface-ink-neutral-disabled"), children: leadingIcon })), jsxs("div", { className: cn("flex-1 min-w-0 flex flex-col justify-center", contentClassName), children: [jsx("div", { className: cn("text-body-medium-regular truncate", variant === "primary"
|
|
1064
|
+
? "text-action-ink-primary-normal"
|
|
1065
|
+
: variant === "negative"
|
|
1066
|
+
? "text-action-ink-negative-normal"
|
|
1067
|
+
: "text-action-ink-neutral-normal", isDisabled && "text-surface-ink-neutral-disabled"), children: title }), description && (jsx("div", { className: cn("text-body-small-regular text-surface-ink-neutral-muted mt-0.5 line-clamp-2", isDisabled && "text-surface-ink-neutral-disabled"), children: description }))] }), (trailingIcon || showChevron) && (jsx("div", { className: "shrink-0 self-center text-action-ink-neutral-subtle", children: trailingIcon || jsx(ChevronRightIcon, {}) }))] }));
|
|
1068
|
+
});
|
|
1069
|
+
ListItem.displayName = "ListItem";
|
|
1070
|
+
|
|
1071
|
+
const linkVariants = cva("inline-flex items-center gap-1 whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none decoration-1 underline-offset-4", {
|
|
1072
|
+
variants: {
|
|
1073
|
+
type: {
|
|
1074
|
+
anchor: "hover:underline",
|
|
1075
|
+
action: "no-underline cursor-pointer",
|
|
1076
|
+
},
|
|
1077
|
+
color: {
|
|
1078
|
+
primary: "",
|
|
1079
|
+
positive: "",
|
|
1080
|
+
negative: "",
|
|
1081
|
+
notice: "",
|
|
1082
|
+
info: "",
|
|
1083
|
+
neutral: "",
|
|
1084
|
+
},
|
|
1085
|
+
size: {
|
|
1086
|
+
xsmall: "text-body-xsmall-medium gap-1",
|
|
1087
|
+
small: "text-body-small-medium gap-1",
|
|
1088
|
+
medium: "text-body-medium-medium gap-1.5",
|
|
1089
|
+
large: "text-body-large-medium gap-1.5",
|
|
1090
|
+
},
|
|
1091
|
+
isIconOnly: {
|
|
1092
|
+
true: "no-underline",
|
|
1093
|
+
false: "",
|
|
1094
|
+
},
|
|
1095
|
+
isDisabled: {
|
|
1096
|
+
true: "cursor-not-allowed opacity-50",
|
|
1097
|
+
false: "cursor-pointer",
|
|
1098
|
+
},
|
|
1099
|
+
},
|
|
1100
|
+
compoundVariants: [
|
|
1101
|
+
// Primary color variants
|
|
1102
|
+
{
|
|
1103
|
+
color: "primary",
|
|
1104
|
+
class: `text-action-ink-primary-normal
|
|
1105
|
+
hover:text-action-ink-primary-subtle
|
|
1106
|
+
hover:decoration-action-outline-primary-hover
|
|
1107
|
+
disabled:text-action-ink-primary-disabled
|
|
1108
|
+
focus:text-action-ink-primary-hover
|
|
1109
|
+
`,
|
|
1110
|
+
},
|
|
1111
|
+
// Positive color variants
|
|
1112
|
+
{
|
|
1113
|
+
color: "positive",
|
|
1114
|
+
class: `text-action-ink-positive-normal
|
|
1115
|
+
hover:text-action-ink-positive-subtle
|
|
1116
|
+
hover:decoration-action-outline-positive-hover
|
|
1117
|
+
hover:text-action-ink-positive-hover
|
|
1118
|
+
disabled:text-action-ink-positive-disabled
|
|
1119
|
+
focus:text-action-ink-positive-hover
|
|
1120
|
+
`,
|
|
1121
|
+
},
|
|
1122
|
+
// Negative color variants
|
|
1123
|
+
{
|
|
1124
|
+
color: "negative",
|
|
1125
|
+
class: `text-action-ink-negative-normal
|
|
1126
|
+
hover:text-action-ink-negative-subtle
|
|
1127
|
+
hover:decoration-action-outline-negative-hover
|
|
1128
|
+
hover:text-action-ink-negative-hover
|
|
1129
|
+
disabled:text-action-ink-negative-disabled
|
|
1130
|
+
focus:text-action-ink-negative-hover
|
|
1131
|
+
`,
|
|
1132
|
+
},
|
|
1133
|
+
// Notice color variants
|
|
1134
|
+
{
|
|
1135
|
+
color: "notice",
|
|
1136
|
+
class: `text-action-ink-notice-normal
|
|
1137
|
+
hover:text-action-ink-notice-subtle
|
|
1138
|
+
hover:decoration-action-outline-notice-hover
|
|
1139
|
+
hover:text-action-ink-notice-hover
|
|
1140
|
+
disabled:text-action-ink-notice-disabled
|
|
1141
|
+
focus:text-action-ink-notice-hover
|
|
1142
|
+
`,
|
|
1143
|
+
},
|
|
1144
|
+
// Info color variants
|
|
1145
|
+
{
|
|
1146
|
+
color: "info",
|
|
1147
|
+
class: `text-action-ink-info-normal
|
|
1148
|
+
hover:text-action-ink-info-subtle
|
|
1149
|
+
hover:decoration-action-outline-info-hover
|
|
1150
|
+
hover:text-action-ink-info-hover
|
|
1151
|
+
disabled:text-action-ink-info-disabled
|
|
1152
|
+
focus:text-action-ink-info-hover
|
|
1153
|
+
`,
|
|
1154
|
+
},
|
|
1155
|
+
// Neutral color variants
|
|
1156
|
+
{
|
|
1157
|
+
color: "neutral",
|
|
1158
|
+
class: `text-action-ink-neutral-normal
|
|
1159
|
+
hover:text-action-ink-neutral-subtle
|
|
1160
|
+
hover:decoration-action-outline-neutral-hover
|
|
1161
|
+
hover:text-action-ink-neutral-hover
|
|
1162
|
+
disabled:text-action-ink-neutral-disabled
|
|
1163
|
+
focus:text-action-ink-neutral-hover
|
|
1164
|
+
`,
|
|
1165
|
+
},
|
|
1166
|
+
],
|
|
1167
|
+
defaultVariants: {
|
|
1168
|
+
type: "anchor",
|
|
1169
|
+
color: "primary",
|
|
1170
|
+
size: "medium",
|
|
1171
|
+
isIconOnly: false,
|
|
1172
|
+
isDisabled: false,
|
|
1173
|
+
},
|
|
1174
|
+
});
|
|
1175
|
+
const Link = React.forwardRef(({ className, type = "anchor", color = "primary", size = "medium", isIconOnly = false, isDisabled = false, asChild = false, showIcon = false, icon, leadingIcon, trailingIcon, children, onClick, ...props }, ref) => {
|
|
1176
|
+
const Comp = asChild ? Slot : "a";
|
|
1177
|
+
const handleClick = (e) => {
|
|
1178
|
+
if (isDisabled) {
|
|
1179
|
+
e.preventDefault();
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
onClick?.(e);
|
|
1183
|
+
};
|
|
1184
|
+
// Icon size based on link size
|
|
1185
|
+
const iconSize = {
|
|
1186
|
+
xsmall: 12,
|
|
1187
|
+
small: 14,
|
|
1188
|
+
medium: 16,
|
|
1189
|
+
large: 18,
|
|
1190
|
+
}[size];
|
|
1191
|
+
// Determine what to show as trailing icon
|
|
1192
|
+
// Priority: trailingIcon > (showIcon && icon) > (showIcon && default ExternalLink)
|
|
1193
|
+
const finalTrailingIcon = trailingIcon || (showIcon && (icon || jsx(ExternalLink, { size: iconSize })));
|
|
1194
|
+
const linkContent = (jsxs(Fragment, { children: [leadingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: leadingIcon })), !isIconOnly && children, isIconOnly && children, finalTrailingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: finalTrailingIcon })), isIconOnly &&
|
|
1195
|
+
(leadingIcon || finalTrailingIcon || (jsx(ExternalLink, { size: iconSize })))] }));
|
|
1196
|
+
return (jsx(Comp, { className: cn(linkVariants({
|
|
1197
|
+
type,
|
|
1198
|
+
color,
|
|
1199
|
+
size,
|
|
1200
|
+
isIconOnly,
|
|
1201
|
+
isDisabled,
|
|
1202
|
+
}), className), ref: ref, onClick: handleClick, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : undefined, ...props, children: linkContent }));
|
|
1203
|
+
});
|
|
1204
|
+
Link.displayName = "Link";
|
|
1205
|
+
|
|
1206
|
+
const DropdownMenu = React.forwardRef(({ items = [], sectionHeading, isLoading = false, isEmpty = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, showChevron = false, emptyIcon, disableFooter = false, onClose, focusedIndex = -1, className, width = "auto", }, ref) => {
|
|
1207
|
+
const renderContent = () => {
|
|
1208
|
+
if (isLoading) {
|
|
1209
|
+
return (jsx("div", { className: "flex flex-col items-center justify-center py-12 px-6", children: jsx(Loader2, { className: "w-12 h-12 text-action-ink-primary-normal mb-4 animate-spin" }) }));
|
|
1210
|
+
}
|
|
1211
|
+
if (isEmpty || items.length === 0) {
|
|
1212
|
+
return (jsxs("div", { className: "flex flex-col items-center justify-center py-8 px-6 text-center", children: [emptyIcon || (jsx(Search, { className: "w-12 h-12 text-surface-ink-neutral-muted mb-4" })), jsx(Text, { as: "h3", variant: "body", size: "small", weight: "semibold", className: "text-surface-ink-neutral-normal mb-2", children: emptyTitle }), jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", className: "text-surface-ink-neutral-muted mb-3", children: emptyDescription }), emptyLinkText && (jsx(Link, { type: "anchor", color: "primary", size: "small", onClick: onEmptyLinkClick, children: emptyLinkText }))] }));
|
|
1213
|
+
}
|
|
1214
|
+
return (jsxs("div", { className: "py-3 px-3 max-h-[400px] overflow-y-auto", children: [sectionHeading && (jsx(Text, { as: "div", variant: "body", size: "small", weight: "medium", className: "text-surface-ink-neutral-muted px-3 py-2 mb-1", children: sectionHeading })), jsx("div", { className: "flex flex-col gap-1", children: items.map((item, index) => (jsx(ListItem, { title: item.title, description: item.description, leadingIcon: item.leadingIcon, trailingIcon: item.trailingIcon, showChevron: showChevron, isDisabled: item.isDisabled, isSelected: index === focusedIndex, onClick: () => {
|
|
1215
|
+
item.onClick?.();
|
|
1216
|
+
onClose?.();
|
|
1217
|
+
}, containerClassName: cn(index === focusedIndex && "bg-action-fill-primary-faded") }, item.id))) })] }));
|
|
1218
|
+
};
|
|
1219
|
+
const widthClass = width === "full" ? "w-full" : width === "auto" ? "w-auto" : "";
|
|
1220
|
+
return (jsxs("div", { ref: ref, className: cn("bg-surface-fill-primary-normal rounded-large overflow-hidden", widthClass, className), style: {
|
|
1221
|
+
boxShadow: "0 1px 2px rgba(25, 25, 30, 0.1), 0 2px 6px rgba(25, 25, 30, 0.06)",
|
|
1222
|
+
...(width !== "full" && width !== "auto" ? { width } : {}),
|
|
1223
|
+
}, children: [renderContent(), !disableFooter && (jsxs("div", { className: "flex flex-col", children: [jsx(Divider, { thickness: "thin", variant: "muted" }), jsxs("div", { className: "flex items-center gap-3 p-4", children: [jsx(Button, { variant: "secondary", color: "primary", size: "medium", isFullWidth: true, onClick: onSecondaryClick, children: secondaryButtonText }), jsx(Button, { variant: "primary", color: "primary", size: "medium", isFullWidth: true, onClick: onPrimaryClick, children: primaryButtonText })] })] }))] }));
|
|
1224
|
+
});
|
|
1225
|
+
DropdownMenu.displayName = "DropdownMenu";
|
|
1226
|
+
|
|
1227
|
+
const dropdownVariants = cva("bg-surface-fill-primary-normal border border-surface-outline-neutral-subtle rounded-large", {
|
|
1228
|
+
variants: {
|
|
1229
|
+
size: {
|
|
1230
|
+
small: "w-64",
|
|
1231
|
+
medium: "w-80",
|
|
1232
|
+
large: "w-96",
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
defaultVariants: {
|
|
1236
|
+
size: "medium",
|
|
1237
|
+
},
|
|
1238
|
+
});
|
|
1239
|
+
const Dropdown = React.forwardRef(({ className, trigger, items = [], sectionHeading, isLoading = false, isEmpty = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, size = "medium", open: controlledOpen, defaultOpen = false, onOpenChange, containerClassName, menuClassName, showChevron = false, emptyIcon, disableFooter = false, ...props }, ref) => {
|
|
1240
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
|
|
1241
|
+
const isOpen = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen;
|
|
1242
|
+
const dropdownRef = React.useRef(null);
|
|
1243
|
+
const handleOpenChange = (newOpen) => {
|
|
1244
|
+
if (controlledOpen === undefined) {
|
|
1245
|
+
setUncontrolledOpen(newOpen);
|
|
1246
|
+
}
|
|
1247
|
+
onOpenChange?.(newOpen);
|
|
1248
|
+
};
|
|
1249
|
+
const toggleOpen = () => {
|
|
1250
|
+
handleOpenChange(!isOpen);
|
|
1251
|
+
};
|
|
1252
|
+
// Close dropdown when clicking outside
|
|
1253
|
+
React.useEffect(() => {
|
|
1254
|
+
const handleClickOutside = (event) => {
|
|
1255
|
+
if (dropdownRef.current &&
|
|
1256
|
+
!dropdownRef.current.contains(event.target)) {
|
|
1257
|
+
handleOpenChange(false);
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
if (isOpen) {
|
|
1261
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1262
|
+
return () => {
|
|
1263
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
}, [isOpen]);
|
|
1267
|
+
// Close on escape key
|
|
1268
|
+
React.useEffect(() => {
|
|
1269
|
+
const handleEscape = (event) => {
|
|
1270
|
+
if (event.key === "Escape") {
|
|
1271
|
+
handleOpenChange(false);
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
if (isOpen) {
|
|
1275
|
+
document.addEventListener("keydown", handleEscape);
|
|
1276
|
+
return () => {
|
|
1277
|
+
document.removeEventListener("keydown", handleEscape);
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
}, [isOpen]);
|
|
1281
|
+
const sizeMap = {
|
|
1282
|
+
small: "w-64",
|
|
1283
|
+
medium: "w-80",
|
|
1284
|
+
large: "w-96",
|
|
1285
|
+
};
|
|
1286
|
+
return (jsxs("div", { ref: dropdownRef, className: cn("relative inline-block", containerClassName), ...props, children: [trigger && (jsx("div", { onClick: toggleOpen, className: "cursor-pointer", children: trigger })), isOpen && (jsx(DropdownMenu, { ref: ref, items: items, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: isEmpty, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, onClose: () => handleOpenChange(false), className: cn("absolute z-50 mt-2", menuClassName, className), width: sizeMap[size] }))] }));
|
|
1287
|
+
});
|
|
1288
|
+
Dropdown.displayName = "Dropdown";
|
|
1289
|
+
|
|
993
1290
|
const tooltipVariants = cva("fixed z-50 bg-popup-fill-intense text-action-ink-on-primary-normal rounded-medium border border-popup-outline-subtle flex flex-col p-4 rounded-xlarge min-w-[200px] max-w-[300px] transition-opacity duration-200", {
|
|
994
1291
|
variants: {
|
|
995
1292
|
isVisible: {
|
|
@@ -1239,218 +1536,10 @@ const FormHeader = React.forwardRef(({ label, size = "medium", isOptional = fals
|
|
|
1239
1536
|
gap: "gap-2.5",
|
|
1240
1537
|
},
|
|
1241
1538
|
};
|
|
1242
|
-
const config = sizeConfig[size];
|
|
1243
|
-
return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between px-1", config.gap, className), children: [jsxs("div", { className: cn("flex items-center", config.gap), children: [jsxs("label", { htmlFor: htmlFor, className: cn("flex items-center", labelClassName), children: [jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", color: "subtle", children: label }), isRequired && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", className: "text-feedback-ink-negative-subtle ml-0.5", children: "*" })), isOptional && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "regular", className: "text-surface-ink-neutral-muted ml-1", children: "(optional)" }))] }), infoDescription && (jsx(Tooltip, { description: infoDescription, heading: infoHeading, children: jsxs("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-surface-ink-neutral-muted cursor-help", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 6V10M7 4.5V4", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] }) }))] }), linkText && (jsx("a", { href: linkHref, onClick: onLinkClick, className: cn("text-surface-ink-primary-normal hover:text-surface-ink-primary-hover transition-colors cursor-pointer font-display font-semibold leading-tight shrink-0", size === "small" && "text-xs", size === "medium" && "text-xs", size === "large" && "text-sm", linkClassName), children: linkText }))] }));
|
|
1244
|
-
});
|
|
1245
|
-
FormHeader.displayName = "FormHeader";
|
|
1246
|
-
|
|
1247
|
-
const linkVariants = cva("inline-flex items-center gap-1 whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none underline decoration-1 underline-offset-4", {
|
|
1248
|
-
variants: {
|
|
1249
|
-
type: {
|
|
1250
|
-
anchor: "",
|
|
1251
|
-
action: "cursor-pointer",
|
|
1252
|
-
},
|
|
1253
|
-
color: {
|
|
1254
|
-
primary: "",
|
|
1255
|
-
positive: "",
|
|
1256
|
-
negative: "",
|
|
1257
|
-
notice: "",
|
|
1258
|
-
info: "",
|
|
1259
|
-
neutral: "",
|
|
1260
|
-
},
|
|
1261
|
-
size: {
|
|
1262
|
-
xsmall: "text-body-xsmall-medium gap-1",
|
|
1263
|
-
small: "text-body-small-medium gap-1",
|
|
1264
|
-
medium: "text-body-medium-medium gap-1.5",
|
|
1265
|
-
large: "text-body-large-medium gap-1.5",
|
|
1266
|
-
},
|
|
1267
|
-
isIconOnly: {
|
|
1268
|
-
true: "no-underline",
|
|
1269
|
-
false: "",
|
|
1270
|
-
},
|
|
1271
|
-
isDisabled: {
|
|
1272
|
-
true: "cursor-not-allowed opacity-50",
|
|
1273
|
-
false: "cursor-pointer",
|
|
1274
|
-
},
|
|
1275
|
-
},
|
|
1276
|
-
compoundVariants: [
|
|
1277
|
-
// Primary color variants
|
|
1278
|
-
{
|
|
1279
|
-
color: "primary",
|
|
1280
|
-
class: `text-action-ink-primary-normal
|
|
1281
|
-
hover:text-action-ink-primary-hover
|
|
1282
|
-
disabled:text-action-ink-primary-disabled
|
|
1283
|
-
active:text-action-ink-primary-activated
|
|
1284
|
-
focus:text-action-ink-primary-hover
|
|
1285
|
-
`,
|
|
1286
|
-
},
|
|
1287
|
-
// Positive color variants
|
|
1288
|
-
{
|
|
1289
|
-
color: "positive",
|
|
1290
|
-
class: `text-action-ink-positive-normal
|
|
1291
|
-
hover:text-action-ink-positive-hover
|
|
1292
|
-
disabled:text-action-ink-positive-disabled
|
|
1293
|
-
active:text-action-ink-positive-activated
|
|
1294
|
-
focus:text-action-ink-positive-hover
|
|
1295
|
-
`,
|
|
1296
|
-
},
|
|
1297
|
-
// Negative color variants
|
|
1298
|
-
{
|
|
1299
|
-
color: "negative",
|
|
1300
|
-
class: `text-action-ink-negative-normal
|
|
1301
|
-
hover:text-action-ink-negative-hover
|
|
1302
|
-
disabled:text-action-ink-negative-disabled
|
|
1303
|
-
active:text-action-ink-negative-activated
|
|
1304
|
-
focus:text-action-ink-negative-hover
|
|
1305
|
-
`,
|
|
1306
|
-
},
|
|
1307
|
-
// Notice color variants
|
|
1308
|
-
{
|
|
1309
|
-
color: "notice",
|
|
1310
|
-
class: `text-action-ink-notice-normal
|
|
1311
|
-
hover:text-action-ink-notice-hover
|
|
1312
|
-
disabled:text-action-ink-notice-disabled
|
|
1313
|
-
active:text-action-ink-notice-activated
|
|
1314
|
-
focus:text-action-ink-notice-hover
|
|
1315
|
-
`,
|
|
1316
|
-
},
|
|
1317
|
-
// Info color variants
|
|
1318
|
-
{
|
|
1319
|
-
color: "info",
|
|
1320
|
-
class: `text-action-ink-info-normal
|
|
1321
|
-
hover:text-action-ink-info-hover
|
|
1322
|
-
disabled:text-action-ink-info-disabled
|
|
1323
|
-
active:text-action-ink-info-activated
|
|
1324
|
-
focus:text-action-ink-info-hover
|
|
1325
|
-
`,
|
|
1326
|
-
},
|
|
1327
|
-
// Neutral color variants
|
|
1328
|
-
{
|
|
1329
|
-
color: "neutral",
|
|
1330
|
-
class: `text-action-ink-neutral-normal
|
|
1331
|
-
hover:text-action-ink-neutral-hover
|
|
1332
|
-
disabled:text-action-ink-neutral-disabled
|
|
1333
|
-
active:text-action-ink-neutral-activated
|
|
1334
|
-
focus:text-action-ink-neutral-hover
|
|
1335
|
-
`,
|
|
1336
|
-
},
|
|
1337
|
-
],
|
|
1338
|
-
defaultVariants: {
|
|
1339
|
-
type: "anchor",
|
|
1340
|
-
color: "primary",
|
|
1341
|
-
size: "medium",
|
|
1342
|
-
isIconOnly: false,
|
|
1343
|
-
isDisabled: false,
|
|
1344
|
-
},
|
|
1345
|
-
});
|
|
1346
|
-
const Link = React.forwardRef(({ className, type = "anchor", color = "primary", size = "medium", isIconOnly = false, isDisabled = false, asChild = false, showIcon = false, icon, leadingIcon, trailingIcon, children, onClick, ...props }, ref) => {
|
|
1347
|
-
const Comp = asChild ? Slot : "a";
|
|
1348
|
-
const handleClick = (e) => {
|
|
1349
|
-
if (isDisabled) {
|
|
1350
|
-
e.preventDefault();
|
|
1351
|
-
return;
|
|
1352
|
-
}
|
|
1353
|
-
onClick?.(e);
|
|
1354
|
-
};
|
|
1355
|
-
// Icon size based on link size
|
|
1356
|
-
const iconSize = {
|
|
1357
|
-
xsmall: 12,
|
|
1358
|
-
small: 14,
|
|
1359
|
-
medium: 16,
|
|
1360
|
-
large: 18,
|
|
1361
|
-
}[size];
|
|
1362
|
-
// Determine what to show as trailing icon
|
|
1363
|
-
// Priority: trailingIcon > (showIcon && icon) > (showIcon && default ExternalLink)
|
|
1364
|
-
const finalTrailingIcon = trailingIcon || (showIcon && (icon || jsx(ExternalLink, { size: iconSize })));
|
|
1365
|
-
const linkContent = (jsxs(Fragment, { children: [leadingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: leadingIcon })), !isIconOnly && children, isIconOnly && children, finalTrailingIcon && !isIconOnly && (jsx("span", { className: "inline-flex items-center", children: finalTrailingIcon })), isIconOnly &&
|
|
1366
|
-
(leadingIcon || finalTrailingIcon || (jsx(ExternalLink, { size: iconSize })))] }));
|
|
1367
|
-
return (jsx(Comp, { className: cn(linkVariants({
|
|
1368
|
-
type,
|
|
1369
|
-
color,
|
|
1370
|
-
size,
|
|
1371
|
-
isIconOnly,
|
|
1372
|
-
isDisabled,
|
|
1373
|
-
}), className), ref: ref, onClick: handleClick, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : undefined, ...props, children: linkContent }));
|
|
1374
|
-
});
|
|
1375
|
-
Link.displayName = "Link";
|
|
1376
|
-
|
|
1377
|
-
const listItemVariants = cva("flex items-start gap-3 p-3 rounded-medium transition-colors cursor-pointer", {
|
|
1378
|
-
variants: {
|
|
1379
|
-
variant: {
|
|
1380
|
-
default: `hover:bg-action-fill-neutral-faded
|
|
1381
|
-
focus:bg-action-fill-neutral-faded
|
|
1382
|
-
focus:ring-2
|
|
1383
|
-
ring-action-outline-primary-faded-hover
|
|
1384
|
-
border border-transparent
|
|
1385
|
-
`,
|
|
1386
|
-
bordered: "border border-action-outline-primary-faded hover:bg-surface-fill-primary-subtle",
|
|
1387
|
-
primary: `hover:bg-action-fill-neutral-faded
|
|
1388
|
-
focus:bg-action-fill-neutral-faded
|
|
1389
|
-
focus:ring-2
|
|
1390
|
-
ring-action-outline-primary-faded-hover
|
|
1391
|
-
border border-transparent
|
|
1392
|
-
`,
|
|
1393
|
-
negative: `hover:bg-action-fill-negative-faded
|
|
1394
|
-
focus:bg-action-fill-negative-faded
|
|
1395
|
-
focus:ring-2 ring-action-outline-negative-faded-hover
|
|
1396
|
-
border border-transparent
|
|
1397
|
-
`,
|
|
1398
|
-
},
|
|
1399
|
-
isDisabled: {
|
|
1400
|
-
true: "cursor-not-allowed opacity-60",
|
|
1401
|
-
false: "",
|
|
1402
|
-
},
|
|
1403
|
-
isSelected: {
|
|
1404
|
-
true: "bg-action-fill-primary-faded border-action-outline-primary-faded",
|
|
1405
|
-
false: "",
|
|
1406
|
-
},
|
|
1407
|
-
},
|
|
1408
|
-
defaultVariants: {
|
|
1409
|
-
variant: "default",
|
|
1410
|
-
isDisabled: false,
|
|
1411
|
-
isSelected: false,
|
|
1412
|
-
},
|
|
1413
|
-
});
|
|
1414
|
-
const ChevronRightIcon = ({ className }) => (jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, children: jsx("path", { d: "M7.5 15L12.5 10L7.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
1415
|
-
const ListItem = React.forwardRef(({ className, type = "single", leadingIcon, title, description, trailingIcon, showChevron = true, variant = "default", isDisabled = false, isSelected = false, onSelectionChange, checkboxSize = "small", containerClassName, contentClassName, onClick, ...props }, ref) => {
|
|
1416
|
-
const [internalSelected, setInternalSelected] = React.useState(isSelected);
|
|
1417
|
-
// Sync internal state with prop
|
|
1418
|
-
React.useEffect(() => {
|
|
1419
|
-
setInternalSelected(isSelected);
|
|
1420
|
-
}, [isSelected]);
|
|
1421
|
-
const handleClick = (e) => {
|
|
1422
|
-
if (isDisabled)
|
|
1423
|
-
return;
|
|
1424
|
-
if (type === "multiple") {
|
|
1425
|
-
const newSelected = !internalSelected;
|
|
1426
|
-
setInternalSelected(newSelected);
|
|
1427
|
-
onSelectionChange?.(newSelected);
|
|
1428
|
-
}
|
|
1429
|
-
onClick?.(e);
|
|
1430
|
-
};
|
|
1431
|
-
const handleCheckboxChange = (e) => {
|
|
1432
|
-
e.stopPropagation();
|
|
1433
|
-
if (isDisabled)
|
|
1434
|
-
return;
|
|
1435
|
-
const newSelected = e.target.checked;
|
|
1436
|
-
setInternalSelected(newSelected);
|
|
1437
|
-
onSelectionChange?.(newSelected);
|
|
1438
|
-
};
|
|
1439
|
-
return (jsxs("div", { ref: ref, className: cn(listItemVariants({
|
|
1440
|
-
variant,
|
|
1441
|
-
isDisabled,
|
|
1442
|
-
isSelected: type === "multiple" ? internalSelected : false,
|
|
1443
|
-
}), containerClassName), onClick: handleClick, role: type === "multiple" ? "checkbox" : "button", "aria-checked": type === "multiple" ? internalSelected : undefined, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, ...props, children: [type === "multiple" && (jsx(Checkbox, { checked: internalSelected, onChange: handleCheckboxChange, isDisabled: isDisabled, size: checkboxSize, className: "shrink-0 mt-0.5" })), leadingIcon && (jsx("div", { className: cn(`shrink-0 flex items-center justify-center mt-0.5`, variant === "primary"
|
|
1444
|
-
? "text-action-ink-primary-normal"
|
|
1445
|
-
: variant === "negative"
|
|
1446
|
-
? "text-action-ink-negative-normal"
|
|
1447
|
-
: "text-action-ink-neutral-subtle", isDisabled && "text-surface-ink-neutral-disabled"), children: leadingIcon })), jsxs("div", { className: cn("flex-1 min-w-0 flex flex-col justify-center", contentClassName), children: [jsx("div", { className: cn("text-body-medium-regular truncate", variant === "primary"
|
|
1448
|
-
? "text-action-ink-primary-normal"
|
|
1449
|
-
: variant === "negative"
|
|
1450
|
-
? "text-action-ink-negative-normal"
|
|
1451
|
-
: "text-action-ink-neutral-normal", isDisabled && "text-surface-ink-neutral-disabled"), children: title }), description && (jsx("div", { className: cn("text-body-small-regular text-surface-ink-neutral-muted mt-0.5 line-clamp-2", isDisabled && "text-surface-ink-neutral-disabled"), children: description }))] }), (trailingIcon || showChevron) && (jsx("div", { className: "shrink-0 self-center text-action-ink-neutral-subtle", children: trailingIcon || jsx(ChevronRightIcon, {}) }))] }));
|
|
1539
|
+
const config = sizeConfig[size];
|
|
1540
|
+
return (jsxs("div", { ref: ref, className: cn("flex items-center justify-between px-1", config.gap, className), children: [jsxs("div", { className: cn("flex items-center", config.gap), children: [jsxs("label", { htmlFor: htmlFor, className: cn("flex items-center", labelClassName), children: [jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", color: "subtle", children: label }), isRequired && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "semibold", className: "text-feedback-ink-negative-subtle ml-0.5", children: "*" })), isOptional && (jsx(Text, { as: "span", variant: "body", size: config.textSize, weight: "regular", className: "text-surface-ink-neutral-muted ml-1", children: "(optional)" }))] }), infoDescription && (jsx(Tooltip, { description: infoDescription, heading: infoHeading, children: jsxs("svg", { width: config.iconSize, height: config.iconSize, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-surface-ink-neutral-muted cursor-help", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1" }), jsx("path", { d: "M7 6V10M7 4.5V4", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round" })] }) }))] }), linkText && (jsx("a", { href: linkHref, onClick: onLinkClick, className: cn("text-surface-ink-primary-normal hover:text-surface-ink-primary-hover transition-colors cursor-pointer font-display font-semibold leading-tight shrink-0", size === "small" && "text-xs", size === "medium" && "text-xs", size === "large" && "text-sm", linkClassName), children: linkText }))] }));
|
|
1452
1541
|
});
|
|
1453
|
-
|
|
1542
|
+
FormHeader.displayName = "FormHeader";
|
|
1454
1543
|
|
|
1455
1544
|
const radioVariants = cva("relative inline-flex items-center justify-center shrink-0 border transition-all cursor-pointer rounded-full", {
|
|
1456
1545
|
variants: {
|
|
@@ -1617,6 +1706,214 @@ const Radio = React.forwardRef(({ label, errorText, size = "medium", validationS
|
|
|
1617
1706
|
});
|
|
1618
1707
|
Radio.displayName = "Radio";
|
|
1619
1708
|
|
|
1709
|
+
const textFieldVariants = cva("relative flex items-center gap-2 border rounded-medium transition-all font-display font-size-100 leading-100", {
|
|
1710
|
+
variants: {
|
|
1711
|
+
size: {
|
|
1712
|
+
small: "h-[28px] px-3 text-xs gap-2",
|
|
1713
|
+
medium: "h-[36px] px-4 text-sm gap-2",
|
|
1714
|
+
large: "h-[44px] px-5 text-base gap-3",
|
|
1715
|
+
},
|
|
1716
|
+
validationState: {
|
|
1717
|
+
none: `
|
|
1718
|
+
border-action-outline-neutral-faded
|
|
1719
|
+
hover:border-action-outline-primary-hover
|
|
1720
|
+
focus-within:border-action-outline-primary-hover
|
|
1721
|
+
focus-within:ring-2
|
|
1722
|
+
ring-action-outline-primary-faded-hover`,
|
|
1723
|
+
positive: `
|
|
1724
|
+
border-action-outline-positive-default
|
|
1725
|
+
focus-within:border-action-outline-positive-hover
|
|
1726
|
+
focus-within:ring-2
|
|
1727
|
+
ring-action-outline-positive-faded-hover`,
|
|
1728
|
+
negative: `border-action-outline-negative-default
|
|
1729
|
+
focus-within:border-action-outline-negative-hover
|
|
1730
|
+
focus-within:ring-2
|
|
1731
|
+
ring-action-outline-negative-faded-hover`,
|
|
1732
|
+
},
|
|
1733
|
+
isDisabled: {
|
|
1734
|
+
true: `
|
|
1735
|
+
border-[var(--border-width-thinner)]
|
|
1736
|
+
hover:border-action-outline-neutral-disabled
|
|
1737
|
+
border-action-outline-neutral-disabled
|
|
1738
|
+
bg-surface-fill-neutral-intense cursor-not-allowed opacity-60`,
|
|
1739
|
+
false: "bg-surface-fill-neutral-intense",
|
|
1740
|
+
},
|
|
1741
|
+
},
|
|
1742
|
+
defaultVariants: {
|
|
1743
|
+
size: "medium",
|
|
1744
|
+
validationState: "none",
|
|
1745
|
+
isDisabled: false,
|
|
1746
|
+
},
|
|
1747
|
+
});
|
|
1748
|
+
const TextField = React.forwardRef(({ label, helperText, errorText, successText, size = "medium", validationState = "none", isDisabled = false, isRequired = false, isOptional = false, prefix, suffix, showClearButton = false, infoDescription, infoHeading, linkText, linkHref, onLinkClick, onClear, containerClassName, labelClassName, inputClassName, className, value, onChange, ...props }, ref) => {
|
|
1749
|
+
const [internalValue, setInternalValue] = React.useState("");
|
|
1750
|
+
const inputValue = value !== undefined ? value : internalValue;
|
|
1751
|
+
const hasValue = inputValue && String(inputValue).length > 0;
|
|
1752
|
+
const handleChange = (e) => {
|
|
1753
|
+
if (onChange) {
|
|
1754
|
+
onChange(e);
|
|
1755
|
+
}
|
|
1756
|
+
else {
|
|
1757
|
+
setInternalValue(e.target.value);
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
const handleClear = () => {
|
|
1761
|
+
if (onClear) {
|
|
1762
|
+
onClear();
|
|
1763
|
+
}
|
|
1764
|
+
else {
|
|
1765
|
+
setInternalValue("");
|
|
1766
|
+
}
|
|
1767
|
+
// Focus the input after clearing
|
|
1768
|
+
const input = document.getElementById(props.id || "");
|
|
1769
|
+
if (input) {
|
|
1770
|
+
input.focus();
|
|
1771
|
+
}
|
|
1772
|
+
};
|
|
1773
|
+
// Determine which helper text to show
|
|
1774
|
+
const displayHelperText = errorText || successText || helperText;
|
|
1775
|
+
const currentValidationState = errorText
|
|
1776
|
+
? "negative"
|
|
1777
|
+
: successText
|
|
1778
|
+
? "positive"
|
|
1779
|
+
: validationState;
|
|
1780
|
+
const sizeConfig = {
|
|
1781
|
+
small: {
|
|
1782
|
+
gap: "gap-2",
|
|
1783
|
+
},
|
|
1784
|
+
medium: {
|
|
1785
|
+
gap: "gap-2",
|
|
1786
|
+
},
|
|
1787
|
+
large: {
|
|
1788
|
+
gap: "gap-3",
|
|
1789
|
+
},
|
|
1790
|
+
};
|
|
1791
|
+
return (jsxs("div", { className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: props.id, className: "mb-2", labelClassName: labelClassName })), jsxs("div", { className: cn(textFieldVariants({
|
|
1792
|
+
size,
|
|
1793
|
+
validationState: currentValidationState,
|
|
1794
|
+
isDisabled,
|
|
1795
|
+
}), className), children: [prefix && (jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
|
|
1796
|
+
? "text-surface-ink-neutral-disabled"
|
|
1797
|
+
: currentValidationState === "positive"
|
|
1798
|
+
? "text-feedback-ink-positive-intense"
|
|
1799
|
+
: currentValidationState === "negative"
|
|
1800
|
+
? "text-feedback-ink-negative-subtle"
|
|
1801
|
+
: "text-surface-ink-neutral-muted"), children: prefix })), jsx("input", { ref: ref, value: inputValue, onChange: handleChange, disabled: isDisabled, required: isRequired, className: cn("flex-1 bg-transparent border-none outline-none text-surface-ink-neutral-normal placeholder:text-surface-ink-neutral-muted disabled:cursor-not-allowed disabled:text-surface-ink-neutral-disabled font-display", inputClassName), ...props }), showClearButton && hasValue && !isDisabled && (jsx("button", { type: "button", onClick: handleClear, className: "shrink-0 flex items-center justify-center text-surface-ink-neutral-muted hover:text-surface-ink-neutral-normal transition-colors", tabIndex: -1, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })), suffix && (jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
|
|
1802
|
+
? "text-surface-ink-neutral-disabled"
|
|
1803
|
+
: currentValidationState === "positive"
|
|
1804
|
+
? "text-feedback-ink-positive-intense"
|
|
1805
|
+
: currentValidationState === "negative"
|
|
1806
|
+
? "text-feedback-ink-negative-subtle"
|
|
1807
|
+
: "text-surface-ink-neutral-muted"), children: suffix }))] }), jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
|
|
1808
|
+
? "default"
|
|
1809
|
+
: currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
|
|
1810
|
+
});
|
|
1811
|
+
TextField.displayName = "TextField";
|
|
1812
|
+
|
|
1813
|
+
const defaultFilter = (item, query) => {
|
|
1814
|
+
const searchQuery = query.toLowerCase();
|
|
1815
|
+
return (item.title.toLowerCase().includes(searchQuery) ||
|
|
1816
|
+
(item.description?.toLowerCase().includes(searchQuery) ?? false));
|
|
1817
|
+
};
|
|
1818
|
+
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 = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, containerClassName, ...textFieldProps }, ref) => {
|
|
1819
|
+
const [uncontrolledSearchValue, setUncontrolledSearchValue] = React.useState(defaultSearchValue);
|
|
1820
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
1821
|
+
const [focusedIndex, setFocusedIndex] = React.useState(-1);
|
|
1822
|
+
const dropdownRef = React.useRef(null);
|
|
1823
|
+
const inputRef = React.useRef(null);
|
|
1824
|
+
React.useImperativeHandle(ref, () => inputRef.current);
|
|
1825
|
+
const searchValue = controlledSearchValue !== undefined
|
|
1826
|
+
? controlledSearchValue
|
|
1827
|
+
: uncontrolledSearchValue;
|
|
1828
|
+
const handleSearchChange = (e) => {
|
|
1829
|
+
const newValue = e.target.value;
|
|
1830
|
+
if (controlledSearchValue === undefined) {
|
|
1831
|
+
setUncontrolledSearchValue(newValue);
|
|
1832
|
+
}
|
|
1833
|
+
onSearchChange?.(newValue);
|
|
1834
|
+
// Show dropdown if minimum search length is met
|
|
1835
|
+
if (newValue.length >= minSearchLength) {
|
|
1836
|
+
setIsOpen(true);
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
setIsOpen(false);
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
const handleFocus = () => {
|
|
1843
|
+
if (showOnFocus && searchValue.length >= minSearchLength) {
|
|
1844
|
+
setIsOpen(true);
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
const handleItemSelect = (item) => {
|
|
1848
|
+
onItemSelect?.(item);
|
|
1849
|
+
if (controlledSearchValue === undefined) {
|
|
1850
|
+
setUncontrolledSearchValue(item.value || item.title);
|
|
1851
|
+
}
|
|
1852
|
+
setIsOpen(false);
|
|
1853
|
+
inputRef.current?.focus();
|
|
1854
|
+
};
|
|
1855
|
+
// Filter items based on search
|
|
1856
|
+
const filteredItems = React.useMemo(() => {
|
|
1857
|
+
if (!searchValue)
|
|
1858
|
+
return items;
|
|
1859
|
+
return items.filter((item) => filterFunction(item, searchValue));
|
|
1860
|
+
}, [items, searchValue, filterFunction]);
|
|
1861
|
+
// Close dropdown when clicking outside
|
|
1862
|
+
React.useEffect(() => {
|
|
1863
|
+
const handleClickOutside = (event) => {
|
|
1864
|
+
if (dropdownRef.current &&
|
|
1865
|
+
!dropdownRef.current.contains(event.target)) {
|
|
1866
|
+
setIsOpen(false);
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
if (isOpen) {
|
|
1870
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1871
|
+
return () => {
|
|
1872
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
}, [isOpen]);
|
|
1876
|
+
// Handle keyboard navigation
|
|
1877
|
+
const handleKeyDown = (e) => {
|
|
1878
|
+
if (!isOpen) {
|
|
1879
|
+
if (e.key === "ArrowDown" || e.key === "Enter") {
|
|
1880
|
+
setIsOpen(true);
|
|
1881
|
+
e.preventDefault();
|
|
1882
|
+
}
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
switch (e.key) {
|
|
1886
|
+
case "ArrowDown":
|
|
1887
|
+
e.preventDefault();
|
|
1888
|
+
setFocusedIndex((prev) => prev < filteredItems.length - 1 ? prev + 1 : prev);
|
|
1889
|
+
break;
|
|
1890
|
+
case "ArrowUp":
|
|
1891
|
+
e.preventDefault();
|
|
1892
|
+
setFocusedIndex((prev) => (prev > 0 ? prev - 1 : -1));
|
|
1893
|
+
break;
|
|
1894
|
+
case "Enter":
|
|
1895
|
+
e.preventDefault();
|
|
1896
|
+
if (focusedIndex >= 0 && filteredItems[focusedIndex]) {
|
|
1897
|
+
handleItemSelect(filteredItems[focusedIndex]);
|
|
1898
|
+
}
|
|
1899
|
+
break;
|
|
1900
|
+
case "Escape":
|
|
1901
|
+
e.preventDefault();
|
|
1902
|
+
setIsOpen(false);
|
|
1903
|
+
setFocusedIndex(-1);
|
|
1904
|
+
break;
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
// Update items with onClick handlers that call handleItemSelect
|
|
1908
|
+
const itemsWithHandlers = filteredItems.map((item) => ({
|
|
1909
|
+
...item,
|
|
1910
|
+
onClick: () => handleItemSelect(item),
|
|
1911
|
+
}));
|
|
1912
|
+
const showDropdown = isOpen && searchValue.length >= minSearchLength;
|
|
1913
|
+
return (jsxs("div", { ref: dropdownRef, className: cn("relative", containerClassName), children: [jsx(TextField, { ref: inputRef, value: searchValue, onChange: handleSearchChange, onFocus: handleFocus, onKeyDown: handleKeyDown, containerClassName: "mb-0", ...textFieldProps }), showDropdown && (jsx(DropdownMenu, { items: itemsWithHandlers, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: filteredItems.length === 0, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: cn("absolute z-50 mt-2", dropdownClassName), width: dropdownWidth === "full" ? "full" : "auto" }))] }));
|
|
1914
|
+
});
|
|
1915
|
+
SearchableDropdown.displayName = "SearchableDropdown";
|
|
1916
|
+
|
|
1620
1917
|
const switchVariants = cva("relative inline-flex items-center shrink-0 cursor-pointer rounded-full transition-all duration-200", {
|
|
1621
1918
|
variants: {
|
|
1622
1919
|
size: {
|
|
@@ -1959,109 +2256,5 @@ const TextArea = React.forwardRef(({ label, helperText, errorText, successText,
|
|
|
1959
2256
|
});
|
|
1960
2257
|
TextArea.displayName = "TextArea";
|
|
1961
2258
|
|
|
1962
|
-
|
|
1963
|
-
variants: {
|
|
1964
|
-
size: {
|
|
1965
|
-
small: "h-[28px] px-3 text-xs gap-2",
|
|
1966
|
-
medium: "h-[36px] px-4 text-sm gap-2",
|
|
1967
|
-
large: "h-[44px] px-5 text-base gap-3",
|
|
1968
|
-
},
|
|
1969
|
-
validationState: {
|
|
1970
|
-
none: `
|
|
1971
|
-
border-action-outline-neutral-faded
|
|
1972
|
-
hover:border-action-outline-primary-hover
|
|
1973
|
-
focus-within:border-action-outline-primary-hover
|
|
1974
|
-
focus-within:ring-2
|
|
1975
|
-
ring-action-outline-primary-faded-hover`,
|
|
1976
|
-
positive: `
|
|
1977
|
-
border-action-outline-positive-default
|
|
1978
|
-
focus-within:border-action-outline-positive-hover
|
|
1979
|
-
focus-within:ring-2
|
|
1980
|
-
ring-action-outline-positive-faded-hover`,
|
|
1981
|
-
negative: `border-action-outline-negative-default
|
|
1982
|
-
focus-within:border-action-outline-negative-hover
|
|
1983
|
-
focus-within:ring-2
|
|
1984
|
-
ring-action-outline-negative-faded-hover`,
|
|
1985
|
-
},
|
|
1986
|
-
isDisabled: {
|
|
1987
|
-
true: `
|
|
1988
|
-
border-[var(--border-width-thinner)]
|
|
1989
|
-
hover:border-action-outline-neutral-disabled
|
|
1990
|
-
border-action-outline-neutral-disabled
|
|
1991
|
-
bg-surface-fill-neutral-intense cursor-not-allowed opacity-60`,
|
|
1992
|
-
false: "bg-surface-fill-neutral-intense",
|
|
1993
|
-
},
|
|
1994
|
-
},
|
|
1995
|
-
defaultVariants: {
|
|
1996
|
-
size: "medium",
|
|
1997
|
-
validationState: "none",
|
|
1998
|
-
isDisabled: false,
|
|
1999
|
-
},
|
|
2000
|
-
});
|
|
2001
|
-
const TextField = React.forwardRef(({ label, helperText, errorText, successText, size = "medium", validationState = "none", isDisabled = false, isRequired = false, isOptional = false, prefix, suffix, showClearButton = false, infoDescription, infoHeading, linkText, linkHref, onLinkClick, onClear, containerClassName, labelClassName, inputClassName, className, value, onChange, ...props }, ref) => {
|
|
2002
|
-
const [internalValue, setInternalValue] = React.useState("");
|
|
2003
|
-
const inputValue = value !== undefined ? value : internalValue;
|
|
2004
|
-
const hasValue = inputValue && String(inputValue).length > 0;
|
|
2005
|
-
const handleChange = (e) => {
|
|
2006
|
-
if (onChange) {
|
|
2007
|
-
onChange(e);
|
|
2008
|
-
}
|
|
2009
|
-
else {
|
|
2010
|
-
setInternalValue(e.target.value);
|
|
2011
|
-
}
|
|
2012
|
-
};
|
|
2013
|
-
const handleClear = () => {
|
|
2014
|
-
if (onClear) {
|
|
2015
|
-
onClear();
|
|
2016
|
-
}
|
|
2017
|
-
else {
|
|
2018
|
-
setInternalValue("");
|
|
2019
|
-
}
|
|
2020
|
-
// Focus the input after clearing
|
|
2021
|
-
const input = document.getElementById(props.id || "");
|
|
2022
|
-
if (input) {
|
|
2023
|
-
input.focus();
|
|
2024
|
-
}
|
|
2025
|
-
};
|
|
2026
|
-
// Determine which helper text to show
|
|
2027
|
-
const displayHelperText = errorText || successText || helperText;
|
|
2028
|
-
const currentValidationState = errorText
|
|
2029
|
-
? "negative"
|
|
2030
|
-
: successText
|
|
2031
|
-
? "positive"
|
|
2032
|
-
: validationState;
|
|
2033
|
-
const sizeConfig = {
|
|
2034
|
-
small: {
|
|
2035
|
-
gap: "gap-2",
|
|
2036
|
-
},
|
|
2037
|
-
medium: {
|
|
2038
|
-
gap: "gap-2",
|
|
2039
|
-
},
|
|
2040
|
-
large: {
|
|
2041
|
-
gap: "gap-3",
|
|
2042
|
-
},
|
|
2043
|
-
};
|
|
2044
|
-
return (jsxs("div", { className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: props.id, className: "mb-2", labelClassName: labelClassName })), jsxs("div", { className: cn(textFieldVariants({
|
|
2045
|
-
size,
|
|
2046
|
-
validationState: currentValidationState,
|
|
2047
|
-
isDisabled,
|
|
2048
|
-
}), className), children: [prefix && (jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
|
|
2049
|
-
? "text-surface-ink-neutral-disabled"
|
|
2050
|
-
: currentValidationState === "positive"
|
|
2051
|
-
? "text-feedback-ink-positive-intense"
|
|
2052
|
-
: currentValidationState === "negative"
|
|
2053
|
-
? "text-feedback-ink-negative-subtle"
|
|
2054
|
-
: "text-surface-ink-neutral-muted"), children: prefix })), jsx("input", { ref: ref, value: inputValue, onChange: handleChange, disabled: isDisabled, required: isRequired, className: cn("flex-1 bg-transparent border-none outline-none text-surface-ink-neutral-normal placeholder:text-surface-ink-neutral-muted disabled:cursor-not-allowed disabled:text-surface-ink-neutral-disabled font-display", inputClassName), ...props }), showClearButton && hasValue && !isDisabled && (jsx("button", { type: "button", onClick: handleClear, className: "shrink-0 flex items-center justify-center text-surface-ink-neutral-muted hover:text-surface-ink-neutral-normal transition-colors", tabIndex: -1, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })), suffix && (jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
|
|
2055
|
-
? "text-surface-ink-neutral-disabled"
|
|
2056
|
-
: currentValidationState === "positive"
|
|
2057
|
-
? "text-feedback-ink-positive-intense"
|
|
2058
|
-
: currentValidationState === "negative"
|
|
2059
|
-
? "text-feedback-ink-negative-subtle"
|
|
2060
|
-
: "text-surface-ink-neutral-muted"), children: suffix }))] }), jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
|
|
2061
|
-
? "default"
|
|
2062
|
-
: currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
|
|
2063
|
-
});
|
|
2064
|
-
TextField.displayName = "TextField";
|
|
2065
|
-
|
|
2066
|
-
export { Badge, Button, Checkbox, Counter, Divider, FormFooter, FormHeader, Icon, Link, ListItem, Radio, Switch, TabItem, Tabs, Text, TextArea, TextField, Tooltip, badgeVariants, buttonVariants, checkboxVariants, cn, counterVariants, getAvailableIcons, hasIcon, iconRegistry, linkVariants, listItemVariants, radioVariants, switchVariants, textAreaVariants, textFieldVariants, tooltipVariants };
|
|
2259
|
+
export { Badge, Button, Checkbox, Counter, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, Link, ListItem, Radio, SearchableDropdown, Switch, TabItem, Tabs, Text, TextArea, TextField, Tooltip, badgeVariants, buttonVariants, checkboxVariants, cn, counterVariants, dropdownVariants, getAvailableIcons, hasIcon, iconRegistry, linkVariants, listItemVariants, radioVariants, switchVariants, textAreaVariants, textFieldVariants, tooltipVariants };
|
|
2067
2260
|
//# sourceMappingURL=index.esm.js.map
|