@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.
Files changed (189) hide show
  1. package/README.md +102 -14
  2. package/dist/animated-icons/arrow-down.d.ts +10 -0
  3. package/dist/animated-icons/arrow-down.js +54 -0
  4. package/dist/animated-icons/arrow-up.d.ts +10 -0
  5. package/dist/animated-icons/arrow-up.js +54 -0
  6. package/dist/animated-icons/bell.d.ts +10 -0
  7. package/dist/animated-icons/bell.js +42 -0
  8. package/dist/animated-icons/bold.d.ts +10 -0
  9. package/dist/animated-icons/bold.js +45 -0
  10. package/dist/animated-icons/book-open.d.ts +10 -0
  11. package/dist/animated-icons/book-open.js +55 -0
  12. package/dist/animated-icons/building.d.ts +10 -0
  13. package/dist/animated-icons/building.js +46 -0
  14. package/dist/animated-icons/check.d.ts +10 -0
  15. package/dist/animated-icons/check.js +55 -0
  16. package/dist/animated-icons/chevron-down.d.ts +10 -0
  17. package/dist/animated-icons/chevron-down.js +42 -0
  18. package/dist/animated-icons/chevron-left.d.ts +10 -0
  19. package/dist/animated-icons/chevron-left.js +42 -0
  20. package/dist/animated-icons/chevron-right.d.ts +10 -0
  21. package/dist/animated-icons/chevron-right.js +42 -0
  22. package/dist/animated-icons/chevron-up.d.ts +10 -0
  23. package/dist/animated-icons/chevron-up.js +42 -0
  24. package/dist/animated-icons/clipboard-check.d.ts +10 -0
  25. package/dist/animated-icons/clipboard-check.js +56 -0
  26. package/dist/animated-icons/download.d.ts +10 -0
  27. package/dist/animated-icons/download.js +47 -0
  28. package/dist/animated-icons/external-link.d.ts +10 -0
  29. package/dist/animated-icons/external-link.js +59 -0
  30. package/dist/animated-icons/eye.d.ts +10 -0
  31. package/dist/animated-icons/eye.js +41 -0
  32. package/dist/animated-icons/file-text.d.ts +11 -0
  33. package/dist/animated-icons/file-text.js +89 -0
  34. package/dist/animated-icons/filter.d.ts +10 -0
  35. package/dist/animated-icons/filter.js +78 -0
  36. package/dist/animated-icons/folder-open.d.ts +10 -0
  37. package/dist/animated-icons/folder-open.js +46 -0
  38. package/dist/animated-icons/form.d.ts +10 -0
  39. package/dist/animated-icons/form.js +56 -0
  40. package/dist/animated-icons/git-branch.d.ts +10 -0
  41. package/dist/animated-icons/git-branch.js +56 -0
  42. package/dist/animated-icons/grip-horizontal.d.ts +10 -0
  43. package/dist/animated-icons/grip-horizontal.js +70 -0
  44. package/dist/animated-icons/grip-vertical.d.ts +10 -0
  45. package/dist/animated-icons/grip-vertical.js +67 -0
  46. package/dist/animated-icons/grip.d.ts +10 -0
  47. package/dist/animated-icons/grip.js +72 -0
  48. package/dist/animated-icons/highlighter.d.ts +10 -0
  49. package/dist/animated-icons/highlighter.js +46 -0
  50. package/dist/animated-icons/history.d.ts +10 -0
  51. package/dist/animated-icons/history.js +80 -0
  52. package/dist/animated-icons/index.d.ts +100 -0
  53. package/dist/animated-icons/index.js +50 -0
  54. package/dist/animated-icons/italic.d.ts +10 -0
  55. package/dist/animated-icons/italic.js +45 -0
  56. package/dist/animated-icons/link.d.ts +10 -0
  57. package/dist/animated-icons/link.js +51 -0
  58. package/dist/animated-icons/list-ordered.d.ts +10 -0
  59. package/dist/animated-icons/list-ordered.js +46 -0
  60. package/dist/animated-icons/list.d.ts +10 -0
  61. package/dist/animated-icons/list.js +56 -0
  62. package/dist/animated-icons/loader-pinwheel.d.ts +10 -0
  63. package/dist/animated-icons/loader-pinwheel.js +51 -0
  64. package/dist/animated-icons/logout.d.ts +10 -0
  65. package/dist/animated-icons/logout.js +44 -0
  66. package/dist/animated-icons/maximize.d.ts +10 -0
  67. package/dist/animated-icons/maximize.js +45 -0
  68. package/dist/animated-icons/message-square.d.ts +10 -0
  69. package/dist/animated-icons/message-square.js +56 -0
  70. package/dist/animated-icons/palette.d.ts +10 -0
  71. package/dist/animated-icons/palette.js +45 -0
  72. package/dist/animated-icons/paperclip.d.ts +10 -0
  73. package/dist/animated-icons/paperclip.js +45 -0
  74. package/dist/animated-icons/pencil.d.ts +10 -0
  75. package/dist/animated-icons/pencil.js +46 -0
  76. package/dist/animated-icons/plus.d.ts +10 -0
  77. package/dist/animated-icons/plus.js +42 -0
  78. package/dist/animated-icons/search.d.ts +10 -0
  79. package/dist/animated-icons/search.js +44 -0
  80. package/dist/animated-icons/send.d.ts +10 -0
  81. package/dist/animated-icons/send.js +46 -0
  82. package/dist/animated-icons/settings.d.ts +10 -0
  83. package/dist/animated-icons/settings.js +42 -0
  84. package/dist/animated-icons/shield.d.ts +10 -0
  85. package/dist/animated-icons/shield.js +45 -0
  86. package/dist/animated-icons/strikethrough.d.ts +10 -0
  87. package/dist/animated-icons/strikethrough.js +45 -0
  88. package/dist/animated-icons/timer.d.ts +10 -0
  89. package/dist/animated-icons/timer.js +70 -0
  90. package/dist/animated-icons/trash.d.ts +10 -0
  91. package/dist/animated-icons/trash.js +56 -0
  92. package/dist/animated-icons/trending-down.d.ts +10 -0
  93. package/dist/animated-icons/trending-down.js +86 -0
  94. package/dist/animated-icons/trending-up.d.ts +10 -0
  95. package/dist/animated-icons/trending-up.js +86 -0
  96. package/dist/animated-icons/underline.d.ts +10 -0
  97. package/dist/animated-icons/underline.js +45 -0
  98. package/dist/animated-icons/upload.d.ts +10 -0
  99. package/dist/animated-icons/upload.js +47 -0
  100. package/dist/animated-icons/users.d.ts +10 -0
  101. package/dist/animated-icons/users.js +45 -0
  102. package/dist/animated-icons/x.d.ts +10 -0
  103. package/dist/animated-icons/x.js +45 -0
  104. package/dist/avatar.d.ts +32 -0
  105. package/dist/avatar.js +21 -0
  106. package/dist/badge.d.ts +4 -3
  107. package/dist/badge.js +7 -12
  108. package/dist/breadcrumb.d.ts +30 -0
  109. package/dist/breadcrumb.js +32 -0
  110. package/dist/button.d.ts +2 -2
  111. package/dist/button.js +84 -6
  112. package/dist/command-search.d.ts +44 -0
  113. package/dist/command-search.js +73 -0
  114. package/dist/command.d.ts +77 -0
  115. package/dist/command.js +24 -0
  116. package/dist/count-badge.d.ts +10 -0
  117. package/dist/count-badge.js +34 -0
  118. package/dist/dialog-primitive.d.ts +22 -0
  119. package/dist/dialog-primitive.js +23 -0
  120. package/dist/dialog.d.ts +28 -0
  121. package/dist/dialog.js +60 -0
  122. package/dist/draggable-tabs.d.ts +27 -0
  123. package/dist/draggable-tabs.js +61 -0
  124. package/dist/drawer.d.ts +30 -0
  125. package/dist/drawer.js +77 -0
  126. package/dist/editable-table.d.ts +31 -0
  127. package/dist/editable-table.js +122 -0
  128. package/dist/email-input.d.ts +16 -0
  129. package/dist/email-input.js +55 -0
  130. package/dist/empty-state.d.ts +11 -0
  131. package/dist/empty-state.js +7 -0
  132. package/dist/field.d.ts +6 -0
  133. package/dist/field.js +18 -0
  134. package/dist/filter-toggle.d.ts +12 -0
  135. package/dist/filter-toggle.js +23 -0
  136. package/dist/govt-masthead.d.ts +9 -0
  137. package/dist/govt-masthead.js +8 -0
  138. package/dist/index.d.ts +55 -4
  139. package/dist/index.js +32 -4
  140. package/dist/input-control.d.ts +4 -0
  141. package/dist/input-control.js +7 -0
  142. package/dist/input-validation.d.ts +9 -0
  143. package/dist/input-validation.js +75 -0
  144. package/dist/input.d.ts +16 -0
  145. package/dist/input.js +22 -0
  146. package/dist/label-value-grid.d.ts +13 -0
  147. package/dist/label-value-grid.js +61 -0
  148. package/dist/label-value.d.ts +11 -0
  149. package/dist/label-value.js +19 -0
  150. package/dist/layout.d.ts +57 -0
  151. package/dist/layout.js +166 -0
  152. package/dist/otp-input.d.ts +21 -0
  153. package/dist/otp-input.js +60 -0
  154. package/dist/page-header.d.ts +9 -0
  155. package/dist/page-header.js +6 -0
  156. package/dist/pagination.d.ts +12 -0
  157. package/dist/pagination.js +24 -0
  158. package/dist/phone-input.d.ts +36 -0
  159. package/dist/phone-input.js +135 -0
  160. package/dist/right-drawer.d.ts +2 -0
  161. package/dist/right-drawer.js +1 -0
  162. package/dist/search-input-shell.d.ts +8 -0
  163. package/dist/search-input-shell.js +9 -0
  164. package/dist/search-input.d.ts +20 -0
  165. package/dist/search-input.js +73 -0
  166. package/dist/select-primitive.d.ts +17 -0
  167. package/dist/select-primitive.js +33 -0
  168. package/dist/select.d.ts +31 -0
  169. package/dist/select.js +97 -0
  170. package/dist/styles.css +111 -1
  171. package/dist/table-toolbar.d.ts +7 -0
  172. package/dist/table-toolbar.js +7 -0
  173. package/dist/table.d.ts +87 -3
  174. package/dist/table.js +102 -5
  175. package/dist/tabs.d.ts +24 -7
  176. package/dist/tabs.js +22 -26
  177. package/dist/tailwind-preset.d.ts +4 -0
  178. package/dist/tailwind-preset.js +97 -0
  179. package/dist/textarea.d.ts +16 -0
  180. package/dist/textarea.js +23 -0
  181. package/dist/theme.d.ts +259 -0
  182. package/dist/theme.js +118 -0
  183. package/dist/tooltip.d.ts +15 -3
  184. package/dist/tooltip.js +9 -4
  185. package/dist/typography.d.ts +11 -0
  186. package/dist/typography.js +41 -0
  187. package/dist/use-search-highlight.d.ts +13 -0
  188. package/dist/use-search-highlight.js +46 -0
  189. 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,2 @@
1
+ export { Drawer as RightDrawer } from "./drawer";
2
+ export type { DrawerAction as RightDrawerAction, DrawerProps as RightDrawerProps, } from "./drawer";
@@ -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, };
@@ -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: 14px;
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 };