@neasg/design-system 0.1.3 → 0.2.1
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/README.md +102 -14
- package/dist/animated-icons/arrow-down.d.ts +10 -0
- package/dist/animated-icons/arrow-down.js +54 -0
- package/dist/animated-icons/arrow-up.d.ts +10 -0
- package/dist/animated-icons/arrow-up.js +54 -0
- package/dist/animated-icons/bell.d.ts +10 -0
- package/dist/animated-icons/bell.js +42 -0
- package/dist/animated-icons/bold.d.ts +10 -0
- package/dist/animated-icons/bold.js +45 -0
- package/dist/animated-icons/book-open.d.ts +10 -0
- package/dist/animated-icons/book-open.js +55 -0
- package/dist/animated-icons/building.d.ts +10 -0
- package/dist/animated-icons/building.js +46 -0
- package/dist/animated-icons/check.d.ts +10 -0
- package/dist/animated-icons/check.js +55 -0
- package/dist/animated-icons/chevron-down.d.ts +10 -0
- package/dist/animated-icons/chevron-down.js +42 -0
- package/dist/animated-icons/chevron-left.d.ts +10 -0
- package/dist/animated-icons/chevron-left.js +42 -0
- package/dist/animated-icons/chevron-right.d.ts +10 -0
- package/dist/animated-icons/chevron-right.js +42 -0
- package/dist/animated-icons/chevron-up.d.ts +10 -0
- package/dist/animated-icons/chevron-up.js +42 -0
- package/dist/animated-icons/clipboard-check.d.ts +10 -0
- package/dist/animated-icons/clipboard-check.js +56 -0
- package/dist/animated-icons/download.d.ts +10 -0
- package/dist/animated-icons/download.js +47 -0
- package/dist/animated-icons/external-link.d.ts +10 -0
- package/dist/animated-icons/external-link.js +59 -0
- package/dist/animated-icons/eye.d.ts +10 -0
- package/dist/animated-icons/eye.js +41 -0
- package/dist/animated-icons/file-text.d.ts +11 -0
- package/dist/animated-icons/file-text.js +89 -0
- package/dist/animated-icons/filter.d.ts +10 -0
- package/dist/animated-icons/filter.js +78 -0
- package/dist/animated-icons/folder-open.d.ts +10 -0
- package/dist/animated-icons/folder-open.js +46 -0
- package/dist/animated-icons/form.d.ts +10 -0
- package/dist/animated-icons/form.js +56 -0
- package/dist/animated-icons/git-branch.d.ts +10 -0
- package/dist/animated-icons/git-branch.js +56 -0
- package/dist/animated-icons/grip-horizontal.d.ts +10 -0
- package/dist/animated-icons/grip-horizontal.js +70 -0
- package/dist/animated-icons/grip-vertical.d.ts +10 -0
- package/dist/animated-icons/grip-vertical.js +67 -0
- package/dist/animated-icons/grip.d.ts +10 -0
- package/dist/animated-icons/grip.js +72 -0
- package/dist/animated-icons/highlighter.d.ts +10 -0
- package/dist/animated-icons/highlighter.js +46 -0
- package/dist/animated-icons/history.d.ts +10 -0
- package/dist/animated-icons/history.js +80 -0
- package/dist/animated-icons/index.d.ts +100 -0
- package/dist/animated-icons/index.js +50 -0
- package/dist/animated-icons/italic.d.ts +10 -0
- package/dist/animated-icons/italic.js +45 -0
- package/dist/animated-icons/link.d.ts +10 -0
- package/dist/animated-icons/link.js +51 -0
- package/dist/animated-icons/list-ordered.d.ts +10 -0
- package/dist/animated-icons/list-ordered.js +46 -0
- package/dist/animated-icons/list.d.ts +10 -0
- package/dist/animated-icons/list.js +56 -0
- package/dist/animated-icons/loader-pinwheel.d.ts +10 -0
- package/dist/animated-icons/loader-pinwheel.js +51 -0
- package/dist/animated-icons/logout.d.ts +10 -0
- package/dist/animated-icons/logout.js +44 -0
- package/dist/animated-icons/maximize.d.ts +10 -0
- package/dist/animated-icons/maximize.js +45 -0
- package/dist/animated-icons/message-square.d.ts +10 -0
- package/dist/animated-icons/message-square.js +56 -0
- package/dist/animated-icons/palette.d.ts +10 -0
- package/dist/animated-icons/palette.js +45 -0
- package/dist/animated-icons/paperclip.d.ts +10 -0
- package/dist/animated-icons/paperclip.js +45 -0
- package/dist/animated-icons/pencil.d.ts +10 -0
- package/dist/animated-icons/pencil.js +46 -0
- package/dist/animated-icons/plus.d.ts +10 -0
- package/dist/animated-icons/plus.js +42 -0
- package/dist/animated-icons/search.d.ts +10 -0
- package/dist/animated-icons/search.js +44 -0
- package/dist/animated-icons/send.d.ts +10 -0
- package/dist/animated-icons/send.js +46 -0
- package/dist/animated-icons/settings.d.ts +10 -0
- package/dist/animated-icons/settings.js +42 -0
- package/dist/animated-icons/shield.d.ts +10 -0
- package/dist/animated-icons/shield.js +45 -0
- package/dist/animated-icons/strikethrough.d.ts +10 -0
- package/dist/animated-icons/strikethrough.js +45 -0
- package/dist/animated-icons/timer.d.ts +10 -0
- package/dist/animated-icons/timer.js +70 -0
- package/dist/animated-icons/trash.d.ts +10 -0
- package/dist/animated-icons/trash.js +56 -0
- package/dist/animated-icons/trending-down.d.ts +10 -0
- package/dist/animated-icons/trending-down.js +86 -0
- package/dist/animated-icons/trending-up.d.ts +10 -0
- package/dist/animated-icons/trending-up.js +86 -0
- package/dist/animated-icons/underline.d.ts +10 -0
- package/dist/animated-icons/underline.js +45 -0
- package/dist/animated-icons/upload.d.ts +10 -0
- package/dist/animated-icons/upload.js +47 -0
- package/dist/animated-icons/users.d.ts +10 -0
- package/dist/animated-icons/users.js +45 -0
- package/dist/animated-icons/x.d.ts +10 -0
- package/dist/animated-icons/x.js +45 -0
- package/dist/avatar.d.ts +32 -0
- package/dist/avatar.js +21 -0
- package/dist/badge.d.ts +4 -3
- package/dist/badge.js +7 -12
- package/dist/breadcrumb.d.ts +30 -0
- package/dist/breadcrumb.js +32 -0
- package/dist/button.d.ts +2 -2
- package/dist/button.js +84 -6
- package/dist/command-search.d.ts +44 -0
- package/dist/command-search.js +73 -0
- package/dist/command.d.ts +77 -0
- package/dist/command.js +24 -0
- package/dist/count-badge.d.ts +10 -0
- package/dist/count-badge.js +34 -0
- package/dist/dialog-primitive.d.ts +22 -0
- package/dist/dialog-primitive.js +23 -0
- package/dist/dialog.d.ts +28 -0
- package/dist/dialog.js +60 -0
- package/dist/draggable-tabs.d.ts +27 -0
- package/dist/draggable-tabs.js +61 -0
- package/dist/drawer.d.ts +30 -0
- package/dist/drawer.js +77 -0
- package/dist/editable-table.d.ts +31 -0
- package/dist/editable-table.js +122 -0
- package/dist/email-input.d.ts +16 -0
- package/dist/email-input.js +55 -0
- package/dist/empty-state.d.ts +11 -0
- package/dist/empty-state.js +7 -0
- package/dist/field.d.ts +6 -0
- package/dist/field.js +18 -0
- package/dist/filter-toggle.d.ts +12 -0
- package/dist/filter-toggle.js +23 -0
- package/dist/govt-masthead.d.ts +9 -0
- package/dist/govt-masthead.js +8 -0
- package/dist/index.d.ts +55 -4
- package/dist/index.js +32 -4
- package/dist/input-control.d.ts +4 -0
- package/dist/input-control.js +7 -0
- package/dist/input-validation.d.ts +9 -0
- package/dist/input-validation.js +75 -0
- package/dist/input.d.ts +16 -0
- package/dist/input.js +22 -0
- package/dist/label-value-grid.d.ts +13 -0
- package/dist/label-value-grid.js +61 -0
- package/dist/label-value.d.ts +11 -0
- package/dist/label-value.js +19 -0
- package/dist/layout.d.ts +57 -0
- package/dist/layout.js +166 -0
- package/dist/otp-input.d.ts +21 -0
- package/dist/otp-input.js +60 -0
- package/dist/page-header.d.ts +9 -0
- package/dist/page-header.js +6 -0
- package/dist/pagination.d.ts +12 -0
- package/dist/pagination.js +24 -0
- package/dist/phone-input.d.ts +36 -0
- package/dist/phone-input.js +135 -0
- package/dist/right-drawer.d.ts +2 -0
- package/dist/right-drawer.js +1 -0
- package/dist/search-input-shell.d.ts +8 -0
- package/dist/search-input-shell.js +9 -0
- package/dist/search-input.d.ts +20 -0
- package/dist/search-input.js +73 -0
- package/dist/select-primitive.d.ts +17 -0
- package/dist/select-primitive.js +33 -0
- package/dist/select.d.ts +31 -0
- package/dist/select.js +97 -0
- package/dist/styles.css +111 -1
- package/dist/table-toolbar.d.ts +7 -0
- package/dist/table-toolbar.js +7 -0
- package/dist/table.d.ts +87 -3
- package/dist/table.js +102 -5
- package/dist/tabs.d.ts +24 -7
- package/dist/tabs.js +22 -26
- package/dist/tailwind-preset.d.ts +4 -0
- package/dist/tailwind-preset.js +97 -0
- package/dist/textarea.d.ts +16 -0
- package/dist/textarea.js +23 -0
- package/dist/theme.d.ts +259 -0
- package/dist/theme.js +118 -0
- package/dist/tooltip.d.ts +15 -3
- package/dist/tooltip.js +9 -4
- package/dist/typography.d.ts +11 -0
- package/dist/typography.js +41 -0
- package/dist/use-search-highlight.d.ts +13 -0
- package/dist/use-search-highlight.js +46 -0
- package/package.json +126 -4
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { getCountries, getCountryCallingCode } from "libphonenumber-js";
|
|
4
|
+
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
5
|
+
import { InputControl } from "./input-control";
|
|
6
|
+
import { combineValidators, validatePhoneForCountry, validateRequired, } from "./input-validation";
|
|
7
|
+
import { cn } from "./lib/utils";
|
|
8
|
+
import { SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectSeparator, SelectTrigger, } from "./select-primitive";
|
|
9
|
+
const PINNED_COUNTRY_CODES = [
|
|
10
|
+
"SG",
|
|
11
|
+
"MY",
|
|
12
|
+
"ID",
|
|
13
|
+
"TH",
|
|
14
|
+
"VN",
|
|
15
|
+
"PH",
|
|
16
|
+
"IN",
|
|
17
|
+
"CN",
|
|
18
|
+
"JP",
|
|
19
|
+
"KR",
|
|
20
|
+
"AU",
|
|
21
|
+
"US",
|
|
22
|
+
"GB",
|
|
23
|
+
];
|
|
24
|
+
const pinnedCountryOrder = new Map(PINNED_COUNTRY_CODES.map((code, index) => [code, index]));
|
|
25
|
+
const regionNames = typeof Intl !== "undefined" && typeof Intl.DisplayNames !== "undefined"
|
|
26
|
+
? new Intl.DisplayNames(["en"], { type: "region" })
|
|
27
|
+
: undefined;
|
|
28
|
+
export const DEFAULT_COUNTRY_CODES = getCountries()
|
|
29
|
+
.map((code) => {
|
|
30
|
+
var _a;
|
|
31
|
+
return ({
|
|
32
|
+
code,
|
|
33
|
+
dialCode: `+${getCountryCallingCode(code)}`,
|
|
34
|
+
name: (_a = regionNames === null || regionNames === void 0 ? void 0 : regionNames.of(code)) !== null && _a !== void 0 ? _a : code,
|
|
35
|
+
});
|
|
36
|
+
})
|
|
37
|
+
.sort((left, right) => {
|
|
38
|
+
const leftPinnedIndex = pinnedCountryOrder.get(left.code);
|
|
39
|
+
const rightPinnedIndex = pinnedCountryOrder.get(right.code);
|
|
40
|
+
if (leftPinnedIndex !== undefined && rightPinnedIndex !== undefined) {
|
|
41
|
+
return leftPinnedIndex - rightPinnedIndex;
|
|
42
|
+
}
|
|
43
|
+
if (leftPinnedIndex !== undefined) {
|
|
44
|
+
return -1;
|
|
45
|
+
}
|
|
46
|
+
if (rightPinnedIndex !== undefined) {
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
return left.name.localeCompare(right.name, "en");
|
|
50
|
+
});
|
|
51
|
+
function PhoneInput({ label, value, onValueChange, countryCode, onCountryCodeChange, error: externalError, invalid = false, description, hideLabel = false, onClearError, validators = [], required = false, fieldName = "Mobile number", validateOnChange = false, countryCodes = DEFAULT_COUNTRY_CODES, defaultCountryCode = "SG", className, inputClassName, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, countryCodeAriaLabel = "Country code", id, disabled, onBlur, autoComplete = "tel-national", inputMode = "numeric", placeholder = "1234 5678", "aria-describedby": ariaDescribedBy, ...props }) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
const [internalError, setInternalError] = React.useState();
|
|
54
|
+
const [touched, setTouched] = React.useState(false);
|
|
55
|
+
const [selectedCountry, setSelectedCountry] = React.useState(countryCode !== null && countryCode !== void 0 ? countryCode : defaultCountryCode);
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
setSelectedCountry(countryCode !== null && countryCode !== void 0 ? countryCode : defaultCountryCode);
|
|
58
|
+
}, [countryCode, defaultCountryCode]);
|
|
59
|
+
const fallbackCountry = (_a = countryCodes[0]) !== null && _a !== void 0 ? _a : {
|
|
60
|
+
code: defaultCountryCode,
|
|
61
|
+
dialCode: "",
|
|
62
|
+
name: defaultCountryCode,
|
|
63
|
+
};
|
|
64
|
+
const selectedCountryData = (_b = countryCodes.find((country) => country.code === selectedCountry)) !== null && _b !== void 0 ? _b : fallbackCountry;
|
|
65
|
+
const resolvedCountryCode = selectedCountryData.code;
|
|
66
|
+
const pinnedCountries = countryCodes.filter((country) => pinnedCountryOrder.has(country.code));
|
|
67
|
+
const otherCountries = countryCodes.filter((country) => !pinnedCountryOrder.has(country.code));
|
|
68
|
+
const showCountryGroups = pinnedCountries.length > 0 && otherCountries.length > 0;
|
|
69
|
+
const validate = React.useMemo(() => {
|
|
70
|
+
const rules = [];
|
|
71
|
+
if (required) {
|
|
72
|
+
rules.push(validateRequired(fieldName));
|
|
73
|
+
}
|
|
74
|
+
rules.push(validatePhoneForCountry(resolvedCountryCode, fieldName));
|
|
75
|
+
rules.push(...validators);
|
|
76
|
+
return combineValidators(...rules);
|
|
77
|
+
}, [fieldName, required, resolvedCountryCode, validators]);
|
|
78
|
+
const generatedId = React.useId();
|
|
79
|
+
const phoneId = id !== null && id !== void 0 ? id : `phone-input-${generatedId}`;
|
|
80
|
+
const errorId = externalError || internalError ? `${phoneId}-error` : undefined;
|
|
81
|
+
const displayError = externalError !== null && externalError !== void 0 ? externalError : internalError;
|
|
82
|
+
const showDescription = Boolean(description) && !displayError;
|
|
83
|
+
const descriptionId = showDescription ? `${phoneId}-description` : undefined;
|
|
84
|
+
const describedBy = [ariaDescribedBy, descriptionId, errorId].filter(Boolean).join(" ") ||
|
|
85
|
+
undefined;
|
|
86
|
+
const handlePhoneChange = React.useCallback((event) => {
|
|
87
|
+
const nextValue = event.target.value.replace(/[^\d\s]/g, "");
|
|
88
|
+
onValueChange(nextValue);
|
|
89
|
+
if (externalError) {
|
|
90
|
+
onClearError === null || onClearError === void 0 ? void 0 : onClearError();
|
|
91
|
+
}
|
|
92
|
+
if (validateOnChange && touched) {
|
|
93
|
+
setInternalError(validate(nextValue));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (internalError) {
|
|
97
|
+
setInternalError(undefined);
|
|
98
|
+
}
|
|
99
|
+
}, [
|
|
100
|
+
externalError,
|
|
101
|
+
internalError,
|
|
102
|
+
onClearError,
|
|
103
|
+
onValueChange,
|
|
104
|
+
touched,
|
|
105
|
+
validate,
|
|
106
|
+
validateOnChange,
|
|
107
|
+
]);
|
|
108
|
+
const handleCountryChange = React.useCallback((nextCountryCode) => {
|
|
109
|
+
setSelectedCountry(nextCountryCode);
|
|
110
|
+
onCountryCodeChange === null || onCountryCodeChange === void 0 ? void 0 : onCountryCodeChange(nextCountryCode);
|
|
111
|
+
if (externalError) {
|
|
112
|
+
onClearError === null || onClearError === void 0 ? void 0 : onClearError();
|
|
113
|
+
}
|
|
114
|
+
if (touched) {
|
|
115
|
+
setInternalError(combineValidators(...(required ? [validateRequired(fieldName)] : []), validatePhoneForCountry(nextCountryCode, fieldName), ...validators)(value));
|
|
116
|
+
}
|
|
117
|
+
}, [
|
|
118
|
+
externalError,
|
|
119
|
+
fieldName,
|
|
120
|
+
onClearError,
|
|
121
|
+
onCountryCodeChange,
|
|
122
|
+
required,
|
|
123
|
+
touched,
|
|
124
|
+
validators,
|
|
125
|
+
value,
|
|
126
|
+
]);
|
|
127
|
+
const handleBlur = React.useCallback((event) => {
|
|
128
|
+
setTouched(true);
|
|
129
|
+
setInternalError(validate(value));
|
|
130
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
131
|
+
}, [onBlur, validate, value]);
|
|
132
|
+
const isInvalid = invalid || Boolean(displayError);
|
|
133
|
+
return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: phoneId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(SelectRoot, { value: resolvedCountryCode, onValueChange: handleCountryChange, disabled: disabled, children: [_jsx(SelectTrigger, { "aria-label": countryCodeAriaLabel, "aria-describedby": describedBy, "aria-invalid": isInvalid ? "true" : undefined, className: cn("w-[88px] shrink-0 px-2.5", triggerClassName), children: _jsx("span", { className: "font-medium tabular-nums", children: selectedCountryData.dialCode }) }), _jsxs(SelectContent, { className: cn("min-w-[240px]", contentClassName), children: [pinnedCountries.length > 0 ? (_jsxs(SelectGroup, { children: [showCountryGroups ? _jsx(SelectLabel, { children: "Common" }) : null, pinnedCountries.map((country) => (_jsx(SelectItem, { value: country.code, textValue: `${country.name} ${country.code} ${country.dialCode}`, children: _jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-3", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: country.name }), _jsx("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: country.dialCode })] }) }, country.code)))] })) : null, showCountryGroups ? _jsx(SelectSeparator, {}) : null, otherCountries.length > 0 ? (_jsxs(SelectGroup, { children: [showCountryGroups ? _jsx(SelectLabel, { children: "All Countries" }) : null, otherCountries.map((country) => (_jsx(SelectItem, { value: country.code, textValue: `${country.name} ${country.code} ${country.dialCode}`, children: _jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-3", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: country.name }), _jsx("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: country.dialCode })] }) }, country.code)))] })) : null, countryCodes.length === 0 ? (_jsx(SelectItem, { value: resolvedCountryCode, disabled: true, children: _jsx("span", { className: "text-muted-foreground", children: "No country codes available" }) })) : null] })] }), _jsx(InputControl, { id: phoneId, type: "tel", value: value, onChange: handlePhoneChange, onBlur: handleBlur, placeholder: placeholder, autoComplete: autoComplete, inputMode: inputMode, required: required, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: cn("flex-1", inputClassName), ...props })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: displayError })] }));
|
|
134
|
+
}
|
|
135
|
+
export { PhoneInput };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Drawer as RightDrawer } from "./drawer";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
interface SearchInputShellProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
iconClassName?: string;
|
|
5
|
+
iconSize?: number;
|
|
6
|
+
}
|
|
7
|
+
declare function SearchInputShell({ children, className, iconClassName, iconSize, ...props }: SearchInputShellProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export { SearchInputShell };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "./lib/utils";
|
|
3
|
+
function SearchInputIcon({ className, size = 16, }) {
|
|
4
|
+
return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: className, style: { width: size, height: size }, "aria-hidden": "true", children: [_jsx("circle", { cx: "11", cy: "11", r: "8" }), _jsx("path", { d: "m21 21-4.3-4.3" })] }));
|
|
5
|
+
}
|
|
6
|
+
function SearchInputShell({ children, className, iconClassName, iconSize = 14, ...props }) {
|
|
7
|
+
return (_jsxs("div", { className: cn("relative w-full", className), ...props, children: [_jsx(SearchInputIcon, { className: cn("pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", iconClassName), size: iconSize }), children] }));
|
|
8
|
+
}
|
|
9
|
+
export { SearchInputShell };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { InputControl } from "./input-control";
|
|
3
|
+
export interface SearchInputProps extends Omit<React.ComponentProps<typeof InputControl>, "onChange" | "type" | "value"> {
|
|
4
|
+
value: string;
|
|
5
|
+
onValueChange: (value: string) => void;
|
|
6
|
+
label?: React.ReactNode;
|
|
7
|
+
description?: React.ReactNode;
|
|
8
|
+
error?: React.ReactNode;
|
|
9
|
+
invalid?: boolean;
|
|
10
|
+
containerClassName?: string;
|
|
11
|
+
labelClassName?: string;
|
|
12
|
+
descriptionClassName?: string;
|
|
13
|
+
errorClassName?: string;
|
|
14
|
+
onSearch?: (value: string) => void;
|
|
15
|
+
searchDelay?: number;
|
|
16
|
+
searchOnChange?: boolean;
|
|
17
|
+
searchOnEnter?: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare const SearchInput: React.ForwardRefExoticComponent<Omit<SearchInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
20
|
+
export { SearchInput };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { XIcon } from "./animated-icons/x";
|
|
5
|
+
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
6
|
+
import { InputControl } from "./input-control";
|
|
7
|
+
import { cn } from "./lib/utils";
|
|
8
|
+
import { SearchInputShell } from "./search-input-shell";
|
|
9
|
+
const SearchInput = React.forwardRef(({ value, onValueChange, label = "Search", description, error, invalid = false, placeholder = "Search...", containerClassName, labelClassName, descriptionClassName, errorClassName, onSearch, searchDelay = 300, searchOnChange = true, searchOnEnter = true, className, id, disabled, onKeyDown, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
|
|
10
|
+
const searchTimeoutRef = React.useRef();
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
return () => {
|
|
13
|
+
if (searchTimeoutRef.current) {
|
|
14
|
+
clearTimeout(searchTimeoutRef.current);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
const generatedId = React.useId();
|
|
19
|
+
const inputId = id !== null && id !== void 0 ? id : `search-input-${generatedId}`;
|
|
20
|
+
const errorId = error ? `${inputId}-error` : undefined;
|
|
21
|
+
const showDescription = Boolean(description) && !error;
|
|
22
|
+
const descriptionId = showDescription ? `${inputId}-description` : undefined;
|
|
23
|
+
const isInvalid = invalid ||
|
|
24
|
+
ariaInvalid === true ||
|
|
25
|
+
ariaInvalid === "true" ||
|
|
26
|
+
Boolean(error);
|
|
27
|
+
const describedBy = [ariaDescribedBy, descriptionId, errorId]
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.join(" ") || undefined;
|
|
30
|
+
const runSearch = React.useCallback((nextValue) => {
|
|
31
|
+
if (!onSearch) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (searchTimeoutRef.current) {
|
|
35
|
+
clearTimeout(searchTimeoutRef.current);
|
|
36
|
+
searchTimeoutRef.current = undefined;
|
|
37
|
+
}
|
|
38
|
+
onSearch(nextValue);
|
|
39
|
+
}, [onSearch]);
|
|
40
|
+
const scheduleSearch = React.useCallback((nextValue) => {
|
|
41
|
+
if (!onSearch || !searchOnChange) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (searchTimeoutRef.current) {
|
|
45
|
+
clearTimeout(searchTimeoutRef.current);
|
|
46
|
+
}
|
|
47
|
+
if (searchDelay <= 0) {
|
|
48
|
+
onSearch(nextValue);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
searchTimeoutRef.current = setTimeout(() => {
|
|
52
|
+
onSearch(nextValue);
|
|
53
|
+
searchTimeoutRef.current = undefined;
|
|
54
|
+
}, searchDelay);
|
|
55
|
+
}, [onSearch, searchDelay, searchOnChange]);
|
|
56
|
+
const handleChange = React.useCallback((event) => {
|
|
57
|
+
const nextValue = event.target.value;
|
|
58
|
+
onValueChange(nextValue);
|
|
59
|
+
scheduleSearch(nextValue);
|
|
60
|
+
}, [onValueChange, scheduleSearch]);
|
|
61
|
+
const handleKeyDown = React.useCallback((event) => {
|
|
62
|
+
if (event.key === "Enter" && searchOnEnter) {
|
|
63
|
+
runSearch(event.currentTarget.value);
|
|
64
|
+
}
|
|
65
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
66
|
+
}, [onKeyDown, runSearch, searchOnEnter]);
|
|
67
|
+
return (_jsxs(Field, { className: containerClassName, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn("sr-only", labelClassName), children: label }), _jsxs(SearchInputShell, { children: [_jsx(InputControl, { ref: ref, id: inputId, type: "text", placeholder: placeholder, value: value, onChange: handleChange, onKeyDown: handleKeyDown, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: cn("bg-background pl-9 pr-8", className), ...props }), value ? (_jsx("button", { type: "button", onClick: () => {
|
|
68
|
+
onValueChange("");
|
|
69
|
+
runSearch("");
|
|
70
|
+
}, disabled: disabled, className: "absolute right-2 top-1/2 -translate-y-1/2 rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none", "aria-label": "Clear search", children: _jsx(XIcon, { size: 14 }) })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
71
|
+
});
|
|
72
|
+
SearchInput.displayName = "SearchInput";
|
|
73
|
+
export { SearchInput };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
3
|
+
declare function SelectRoot(props: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function SelectGroup(props: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function SelectValue(props: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Value>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
7
|
+
declare const SelectScrollUpButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollUpButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
declare const SelectScrollDownButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollDownButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
interface SelectContentProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {
|
|
10
|
+
header?: React.ReactNode;
|
|
11
|
+
viewportClassName?: string;
|
|
12
|
+
}
|
|
13
|
+
declare const SelectContent: React.ForwardRefExoticComponent<SelectContentProps & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
declare const SelectLabel: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
15
|
+
declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
16
|
+
declare const SelectSeparator: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
17
|
+
export { SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
5
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
6
|
+
import { cn } from "./lib/utils";
|
|
7
|
+
function SelectRoot(props) {
|
|
8
|
+
return _jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
9
|
+
}
|
|
10
|
+
function SelectGroup(props) {
|
|
11
|
+
return _jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
|
|
12
|
+
}
|
|
13
|
+
function SelectValue(props) {
|
|
14
|
+
return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
15
|
+
}
|
|
16
|
+
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, "data-slot": "select-trigger", className: cn("flex h-10 w-full cursor-pointer items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg]:shrink-0", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" }) })] })));
|
|
17
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
18
|
+
const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollUpButton, { ref: ref, "data-slot": "select-scroll-up-button", className: cn("flex cursor-pointer items-center justify-center py-1", className), ...props, children: _jsx(ChevronUp, { className: "h-4 w-4" }) })));
|
|
19
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
20
|
+
const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollDownButton, { ref: ref, "data-slot": "select-scroll-down-button", className: cn("flex cursor-pointer items-center justify-center py-1", className), ...props, children: _jsx(ChevronDown, { className: "h-4 w-4" }) })));
|
|
21
|
+
SelectScrollDownButton.displayName =
|
|
22
|
+
SelectPrimitive.ScrollDownButton.displayName;
|
|
23
|
+
const SelectContent = React.forwardRef(({ className, children, header, position = "popper", viewportClassName, ...props }, ref) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, "data-slot": "select-content", className: cn("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" &&
|
|
24
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [header ? _jsx("div", { className: "border-b p-1", children: header }) : null, _jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
|
|
25
|
+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", viewportClassName), children: children }), _jsx(SelectScrollDownButton, {})] }) })));
|
|
26
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
27
|
+
const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, "data-slot": "select-label", className: cn("px-2 py-1.5 text-xs text-muted-foreground", className), ...props })));
|
|
28
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
29
|
+
const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsx(SelectPrimitive.ItemText, { className: "break-words", children: children })] })));
|
|
30
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
31
|
+
const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Separator, { ref: ref, "data-slot": "select-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props })));
|
|
32
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
33
|
+
export { SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
|
package/dist/select.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { SelectRoot } from "./select-primitive";
|
|
3
|
+
export interface SelectOption {
|
|
4
|
+
label: React.ReactNode;
|
|
5
|
+
value: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
group?: string;
|
|
8
|
+
textValue?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SelectProps extends Pick<React.ComponentProps<typeof SelectRoot>, "defaultOpen" | "defaultValue" | "disabled" | "name" | "onOpenChange" | "onValueChange" | "open" | "required" | "value"> {
|
|
11
|
+
label: React.ReactNode;
|
|
12
|
+
options: SelectOption[];
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
description?: React.ReactNode;
|
|
15
|
+
error?: React.ReactNode;
|
|
16
|
+
invalid?: boolean;
|
|
17
|
+
hideLabel?: boolean;
|
|
18
|
+
searchable?: boolean;
|
|
19
|
+
searchPlaceholder?: string;
|
|
20
|
+
searchEmptyMessage?: React.ReactNode;
|
|
21
|
+
searchInputAriaLabel?: string;
|
|
22
|
+
className?: string;
|
|
23
|
+
triggerClassName?: string;
|
|
24
|
+
contentClassName?: string;
|
|
25
|
+
labelClassName?: string;
|
|
26
|
+
descriptionClassName?: string;
|
|
27
|
+
errorClassName?: string;
|
|
28
|
+
id?: string;
|
|
29
|
+
}
|
|
30
|
+
declare function Select({ label, options, placeholder, description, error, invalid, hideLabel, searchable, searchPlaceholder, searchEmptyMessage, searchInputAriaLabel, className, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, open, onOpenChange, ...props }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
export { Select };
|
package/dist/select.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
5
|
+
import { InputControl } from "./input-control";
|
|
6
|
+
import { cn } from "./lib/utils";
|
|
7
|
+
import { SearchHighlight } from "./use-search-highlight";
|
|
8
|
+
import { SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectSeparator, SelectTrigger, SelectValue, } from "./select-primitive";
|
|
9
|
+
function groupOptions(options) {
|
|
10
|
+
const groups = [];
|
|
11
|
+
options.forEach((option) => {
|
|
12
|
+
const existingGroup = groups.find((group) => group.group === option.group);
|
|
13
|
+
if (existingGroup) {
|
|
14
|
+
existingGroup.options.push(option);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
groups.push({
|
|
18
|
+
group: option.group,
|
|
19
|
+
options: [option],
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
return groups;
|
|
23
|
+
}
|
|
24
|
+
function getOptionTextValue(option) {
|
|
25
|
+
if (option.textValue) {
|
|
26
|
+
return option.textValue;
|
|
27
|
+
}
|
|
28
|
+
if (typeof option.label === "string" || typeof option.label === "number") {
|
|
29
|
+
return String(option.label);
|
|
30
|
+
}
|
|
31
|
+
return option.value;
|
|
32
|
+
}
|
|
33
|
+
function renderHighlightedLabel(label, query) {
|
|
34
|
+
if (!query.trim()) {
|
|
35
|
+
return label;
|
|
36
|
+
}
|
|
37
|
+
if (typeof label === "string" || typeof label === "number") {
|
|
38
|
+
return _jsx(SearchHighlight, { text: String(label), query: query });
|
|
39
|
+
}
|
|
40
|
+
return label;
|
|
41
|
+
}
|
|
42
|
+
function Select({ label, options, placeholder = "Select an option", description, error, invalid = false, hideLabel = false, searchable = false, searchPlaceholder = "Search options...", searchEmptyMessage = "No options found.", searchInputAriaLabel = "Search options", className, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, open, onOpenChange, ...props }) {
|
|
43
|
+
const generatedId = React.useId();
|
|
44
|
+
const selectId = id !== null && id !== void 0 ? id : `select-${generatedId}`;
|
|
45
|
+
const searchInputRef = React.useRef(null);
|
|
46
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
47
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
48
|
+
const errorId = error ? `${selectId}-error` : undefined;
|
|
49
|
+
const showDescription = Boolean(description) && !error;
|
|
50
|
+
const descriptionId = showDescription ? `${selectId}-description` : undefined;
|
|
51
|
+
const isInvalid = invalid || Boolean(error);
|
|
52
|
+
const isOpen = open !== null && open !== void 0 ? open : uncontrolledOpen;
|
|
53
|
+
const normalizedSearchQuery = searchQuery.trim().toLowerCase();
|
|
54
|
+
const filteredOptions = searchable && normalizedSearchQuery
|
|
55
|
+
? options.filter((option) => [
|
|
56
|
+
getOptionTextValue(option),
|
|
57
|
+
option.value,
|
|
58
|
+
option.group,
|
|
59
|
+
]
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.some((value) => String(value).toLowerCase().includes(normalizedSearchQuery)))
|
|
62
|
+
: options;
|
|
63
|
+
const optionGroups = groupOptions(filteredOptions);
|
|
64
|
+
const hasVisibleOptions = filteredOptions.length > 0;
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
if (!isOpen) {
|
|
67
|
+
setSearchQuery("");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!searchable) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const animationFrame = window.requestAnimationFrame(() => {
|
|
74
|
+
var _a;
|
|
75
|
+
(_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
76
|
+
});
|
|
77
|
+
return () => {
|
|
78
|
+
window.cancelAnimationFrame(animationFrame);
|
|
79
|
+
};
|
|
80
|
+
}, [isOpen, searchable]);
|
|
81
|
+
const handleOpenChange = React.useCallback((nextOpen) => {
|
|
82
|
+
if (open === undefined) {
|
|
83
|
+
setUncontrolledOpen(nextOpen);
|
|
84
|
+
}
|
|
85
|
+
if (!nextOpen) {
|
|
86
|
+
setSearchQuery("");
|
|
87
|
+
}
|
|
88
|
+
onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(nextOpen);
|
|
89
|
+
}, [onOpenChange, open]);
|
|
90
|
+
return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: selectId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs(SelectRoot, { disabled: disabled, open: open, onOpenChange: handleOpenChange, ...props, children: [_jsx(SelectTrigger, { id: selectId, className: triggerClassName, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, header: searchable ? (_jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: searchPlaceholder, "aria-label": searchInputAriaLabel, autoComplete: "off", className: "h-8 border-0 bg-transparent px-2 py-0 text-sm shadow-none focus-visible:border-0 focus-visible:ring-0", onKeyDown: (event) => {
|
|
91
|
+
event.stopPropagation();
|
|
92
|
+
} })) : undefined, viewportClassName: searchable ? "max-h-64" : undefined, children: hasVisibleOptions ? (optionGroups.map((group, groupIndex) => {
|
|
93
|
+
var _a;
|
|
94
|
+
return (_jsxs(React.Fragment, { children: [_jsxs(SelectGroup, { children: [group.group ? (_jsx(SelectLabel, { children: _jsx(SearchHighlight, { text: group.group, query: searchQuery }) })) : null, group.options.map((option) => (_jsx(SelectItem, { value: option.value, disabled: option.disabled, textValue: getOptionTextValue(option), children: renderHighlightedLabel(option.label, searchQuery) }, option.value)))] }), groupIndex < optionGroups.length - 1 ? _jsx(SelectSeparator, {}) : null] }, (_a = group.group) !== null && _a !== void 0 ? _a : `group-${groupIndex}`));
|
|
95
|
+
})) : (_jsx("div", { className: "px-3 py-6 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
96
|
+
}
|
|
97
|
+
export { Select };
|
package/dist/styles.css
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
@layer base {
|
|
6
6
|
:root {
|
|
7
|
+
--space-0: 0rem;
|
|
8
|
+
--space-1: 0.25rem;
|
|
9
|
+
--space-2: 0.5rem;
|
|
10
|
+
--space-3: 0.75rem;
|
|
11
|
+
--space-4: 1rem;
|
|
12
|
+
--space-5: 1.25rem;
|
|
13
|
+
--space-6: 1.5rem;
|
|
14
|
+
--space-8: 2rem;
|
|
15
|
+
--space-10: 2.5rem;
|
|
16
|
+
--space-12: 3rem;
|
|
17
|
+
|
|
7
18
|
--nea-green-50: 140 57% 96%;
|
|
8
19
|
--nea-green-100: 141 50% 90%;
|
|
9
20
|
--nea-green-200: 142 45% 80%;
|
|
@@ -15,6 +26,50 @@
|
|
|
15
26
|
--nea-green-800: 145 100% 21%;
|
|
16
27
|
--nea-green-900: 146 100% 15%;
|
|
17
28
|
|
|
29
|
+
--nea-blue-50: 214 100% 97%;
|
|
30
|
+
--nea-blue-100: 214 95% 93%;
|
|
31
|
+
--nea-blue-200: 213 97% 87%;
|
|
32
|
+
--nea-blue-300: 213 97% 78%;
|
|
33
|
+
--nea-blue-400: 213 94% 68%;
|
|
34
|
+
--nea-blue-500: 217 91% 60%;
|
|
35
|
+
--nea-blue-600: 221 83% 53%;
|
|
36
|
+
--nea-blue-700: 224 76% 48%;
|
|
37
|
+
--nea-blue-800: 226 71% 40%;
|
|
38
|
+
--nea-blue-900: 224 64% 33%;
|
|
39
|
+
|
|
40
|
+
--nea-amber-50: 48 100% 96%;
|
|
41
|
+
--nea-amber-100: 48 96% 89%;
|
|
42
|
+
--nea-amber-200: 48 97% 77%;
|
|
43
|
+
--nea-amber-300: 43 96% 66%;
|
|
44
|
+
--nea-amber-400: 43 96% 56%;
|
|
45
|
+
--nea-amber-500: 38 92% 50%;
|
|
46
|
+
--nea-amber-600: 32 95% 44%;
|
|
47
|
+
--nea-amber-700: 26 90% 37%;
|
|
48
|
+
--nea-amber-800: 22 82% 31%;
|
|
49
|
+
--nea-amber-900: 22 78% 26%;
|
|
50
|
+
|
|
51
|
+
--nea-red-50: 0 86% 97%;
|
|
52
|
+
--nea-red-100: 0 93% 94%;
|
|
53
|
+
--nea-red-200: 0 96% 89%;
|
|
54
|
+
--nea-red-300: 0 94% 82%;
|
|
55
|
+
--nea-red-400: 0 91% 71%;
|
|
56
|
+
--nea-red-500: 0 84% 60%;
|
|
57
|
+
--nea-red-600: 0 72% 51%;
|
|
58
|
+
--nea-red-700: 0 74% 42%;
|
|
59
|
+
--nea-red-800: 0 74% 35%;
|
|
60
|
+
--nea-red-900: 0 63% 31%;
|
|
61
|
+
|
|
62
|
+
--nea-violet-50: 250 100% 98%;
|
|
63
|
+
--nea-violet-100: 250 100% 97%;
|
|
64
|
+
--nea-violet-200: 252 96% 90%;
|
|
65
|
+
--nea-violet-300: 252 95% 85%;
|
|
66
|
+
--nea-violet-400: 255 92% 76%;
|
|
67
|
+
--nea-violet-500: 258 90% 66%;
|
|
68
|
+
--nea-violet-600: 263 89% 60%;
|
|
69
|
+
--nea-violet-700: 263 84% 50%;
|
|
70
|
+
--nea-violet-800: 263 70% 42%;
|
|
71
|
+
--nea-violet-900: 264 69% 35%;
|
|
72
|
+
|
|
18
73
|
--nea-neutral-0: 0 0% 100%;
|
|
19
74
|
--nea-neutral-50: 210 20% 98%;
|
|
20
75
|
--nea-neutral-100: 210 18% 96%;
|
|
@@ -27,6 +82,16 @@
|
|
|
27
82
|
--nea-neutral-800: 222 36% 11%;
|
|
28
83
|
--nea-neutral-900: 222 84% 5%;
|
|
29
84
|
|
|
85
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
86
|
+
"Segoe UI", sans-serif;
|
|
87
|
+
--font-mono: "SFMono-Regular", "SF Mono", ui-monospace, Menlo, Monaco, Consolas,
|
|
88
|
+
"Liberation Mono", "Courier New", monospace;
|
|
89
|
+
--font-size-root: 14px;
|
|
90
|
+
--font-weight-regular: 400;
|
|
91
|
+
--font-weight-medium: 500;
|
|
92
|
+
--font-weight-semibold: 600;
|
|
93
|
+
--font-weight-bold: 700;
|
|
94
|
+
|
|
30
95
|
--background: var(--nea-neutral-0);
|
|
31
96
|
--foreground: var(--nea-neutral-900);
|
|
32
97
|
|
|
@@ -51,11 +116,42 @@
|
|
|
51
116
|
--destructive: 0 84.2% 60.2%;
|
|
52
117
|
--destructive-foreground: 210 40% 98%;
|
|
53
118
|
|
|
119
|
+
--status-success: var(--nea-green-100);
|
|
120
|
+
--status-success-foreground: var(--nea-green-800);
|
|
121
|
+
--status-warning: var(--nea-amber-100);
|
|
122
|
+
--status-warning-foreground: var(--nea-amber-800);
|
|
123
|
+
--status-info: var(--nea-blue-100);
|
|
124
|
+
--status-info-foreground: var(--nea-blue-800);
|
|
125
|
+
--status-accent: var(--nea-violet-100);
|
|
126
|
+
--status-accent-foreground: var(--nea-violet-800);
|
|
127
|
+
--status-destructive: var(--nea-red-100);
|
|
128
|
+
--status-destructive-foreground: var(--nea-red-800);
|
|
129
|
+
|
|
54
130
|
--border: var(--nea-neutral-200);
|
|
55
131
|
--input: var(--nea-neutral-200);
|
|
56
132
|
--ring: var(--nea-green-700);
|
|
57
133
|
|
|
58
134
|
--radius: 0.5rem;
|
|
135
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
136
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
137
|
+
--radius-lg: var(--radius);
|
|
138
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
139
|
+
|
|
140
|
+
--layout-shell-background: 223 30% 95.5%;
|
|
141
|
+
--layout-sidebar-width: 18rem;
|
|
142
|
+
--layout-sidebar-collapsed-width: 4rem;
|
|
143
|
+
--layout-sidebar-padding-inline: var(--space-2);
|
|
144
|
+
--layout-sidebar-padding-top: var(--space-3);
|
|
145
|
+
--layout-content-gap-start: var(--space-2);
|
|
146
|
+
--layout-content-gap-start-md: var(--space-3);
|
|
147
|
+
--layout-content-gap-start-lg: var(--space-4);
|
|
148
|
+
--layout-content-padding: var(--space-4);
|
|
149
|
+
--layout-content-padding-md: var(--space-6);
|
|
150
|
+
--layout-content-padding-lg: var(--space-8);
|
|
151
|
+
--layout-surface-padding: var(--space-4);
|
|
152
|
+
--layout-surface-padding-md: var(--space-6);
|
|
153
|
+
--layout-surface-padding-lg: var(--space-8);
|
|
154
|
+
--layout-surface-gap: var(--space-6);
|
|
59
155
|
}
|
|
60
156
|
|
|
61
157
|
.dark {
|
|
@@ -83,6 +179,17 @@
|
|
|
83
179
|
--destructive: 0 62.8% 30.6%;
|
|
84
180
|
--destructive-foreground: 210 40% 98%;
|
|
85
181
|
|
|
182
|
+
--status-success: var(--nea-green-900);
|
|
183
|
+
--status-success-foreground: var(--nea-green-300);
|
|
184
|
+
--status-warning: var(--nea-amber-900);
|
|
185
|
+
--status-warning-foreground: var(--nea-amber-300);
|
|
186
|
+
--status-info: var(--nea-blue-900);
|
|
187
|
+
--status-info-foreground: var(--nea-blue-300);
|
|
188
|
+
--status-accent: var(--nea-violet-900);
|
|
189
|
+
--status-accent-foreground: var(--nea-violet-300);
|
|
190
|
+
--status-destructive: var(--nea-red-900);
|
|
191
|
+
--status-destructive-foreground: var(--nea-red-300);
|
|
192
|
+
|
|
86
193
|
--border: 217.2 32.6% 17.5%;
|
|
87
194
|
--input: 217.2 32.6% 17.5%;
|
|
88
195
|
--ring: 212.7 26.8% 83.9%;
|
|
@@ -93,10 +200,13 @@
|
|
|
93
200
|
}
|
|
94
201
|
|
|
95
202
|
html {
|
|
96
|
-
font-size:
|
|
203
|
+
font-size: var(--font-size-root);
|
|
204
|
+
font-family: var(--font-sans);
|
|
97
205
|
}
|
|
98
206
|
|
|
99
207
|
body {
|
|
100
208
|
@apply bg-background text-foreground;
|
|
209
|
+
font-family: var(--font-sans);
|
|
210
|
+
font-weight: var(--font-weight-regular);
|
|
101
211
|
}
|
|
102
212
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type SearchInputProps } from "./search-input";
|
|
3
|
+
export interface TableToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
search?: SearchInputProps;
|
|
5
|
+
}
|
|
6
|
+
declare function TableToolbar({ search, className, children, ...props }: TableToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export { TableToolbar };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SearchInput } from "./search-input";
|
|
3
|
+
import { cn } from "./lib/utils";
|
|
4
|
+
function TableToolbar({ search, className, children, ...props }) {
|
|
5
|
+
return (_jsxs("div", { className: cn("flex flex-wrap items-center gap-2", className), ...props, children: [search ? (_jsx(SearchInput, { ...search, containerClassName: cn("min-w-[200px] flex-1 max-w-md", search.containerClassName) })) : null, children] }));
|
|
6
|
+
}
|
|
7
|
+
export { TableToolbar };
|