nucleus-core-ts 0.8.7 → 0.8.8

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 (203) hide show
  1. package/dist/client.js +1 -1
  2. package/dist/fe/components/AbstractAnimatedBackground/index.js +437 -0
  3. package/dist/fe/components/AuthorizationPage/components/AuthorizationPage.js +841 -0
  4. package/dist/fe/components/AuthorizationPage/components/ClaimList.js +100 -0
  5. package/dist/fe/components/AuthorizationPage/components/RoleClaimEditor.js +232 -0
  6. package/dist/fe/components/AuthorizationPage/components/RoleList.js +115 -0
  7. package/dist/fe/components/AuthorizationPage/index.js +6 -0
  8. package/dist/fe/components/AuthorizationPage/store/index.js +117 -0
  9. package/dist/fe/components/AuthorizationPage/theme/index.js +137 -0
  10. package/dist/fe/components/AuthorizationPage/types/index.js +1 -0
  11. package/dist/fe/components/Button/components/Button.js +158 -0
  12. package/dist/fe/components/Button/components/ButtonSpinner.js +52 -0
  13. package/dist/fe/components/Button/index.js +3 -0
  14. package/dist/fe/components/Button/theme/index.js +186 -0
  15. package/dist/fe/components/Button/types/index.js +1 -0
  16. package/dist/fe/components/Button/utils/cn.js +5 -0
  17. package/dist/fe/components/Captcha/components/Captcha.js +311 -0
  18. package/dist/fe/components/Captcha/index.js +2 -0
  19. package/dist/fe/components/Captcha/theme.js +52 -0
  20. package/dist/fe/components/Captcha/types.js +1 -0
  21. package/dist/fe/components/ChangePasswordPage/components/ChangePasswordForm.js +242 -0
  22. package/dist/fe/components/ChangePasswordPage/components/ChangePasswordHeader.js +39 -0
  23. package/dist/fe/components/ChangePasswordPage/components/ChangePasswordPage.js +60 -0
  24. package/dist/fe/components/ChangePasswordPage/index.js +5 -0
  25. package/dist/fe/components/ChangePasswordPage/store/index.js +44 -0
  26. package/dist/fe/components/ChangePasswordPage/theme/index.js +87 -0
  27. package/dist/fe/components/ChangePasswordPage/types/index.js +1 -0
  28. package/dist/fe/components/Checkbox/components/Checkbox.js +115 -0
  29. package/dist/fe/components/Checkbox/components/CheckboxIcon.js +119 -0
  30. package/dist/fe/components/Checkbox/components/SwitchTrack.js +178 -0
  31. package/dist/fe/components/Checkbox/index.js +4 -0
  32. package/dist/fe/components/Checkbox/theme/index.js +221 -0
  33. package/dist/fe/components/Checkbox/types/index.js +1 -0
  34. package/dist/fe/components/Checkbox/utils/cn.js +5 -0
  35. package/dist/fe/components/DataTable/DataTable.js +225 -0
  36. package/dist/fe/components/DataTable/components/ActionCell.js +26 -0
  37. package/dist/fe/components/DataTable/components/DataCell.js +76 -0
  38. package/dist/fe/components/DataTable/components/EditableCell.js +221 -0
  39. package/dist/fe/components/DataTable/components/EmptyState.js +29 -0
  40. package/dist/fe/components/DataTable/components/HeaderCell.js +64 -0
  41. package/dist/fe/components/DataTable/components/InfiniteScrollTrigger.js +66 -0
  42. package/dist/fe/components/DataTable/components/LoadingSpinner.js +19 -0
  43. package/dist/fe/components/DataTable/components/ResizeHandle.js +41 -0
  44. package/dist/fe/components/DataTable/components/SelectionCell.js +105 -0
  45. package/dist/fe/components/DataTable/components/SkeletonRow.js +56 -0
  46. package/dist/fe/components/DataTable/components/SkeletonTable.js +83 -0
  47. package/dist/fe/components/DataTable/components/SortIcon.js +39 -0
  48. package/dist/fe/components/DataTable/components/TableHeader.js +49 -0
  49. package/dist/fe/components/DataTable/components/TableRow.js +118 -0
  50. package/dist/fe/components/DataTable/components/index.js +14 -0
  51. package/dist/fe/components/DataTable/hooks/index.js +2 -0
  52. package/dist/fe/components/DataTable/hooks/useAutoFitColumn.js +23 -0
  53. package/dist/fe/components/DataTable/hooks/useResizeColumn.js +122 -0
  54. package/dist/fe/components/DataTable/index.js +3 -0
  55. package/dist/fe/components/DataTable/store/index.js +97 -0
  56. package/dist/fe/components/DataTable/theme/index.js +144 -0
  57. package/dist/fe/components/DataTable/types/index.js +1 -0
  58. package/dist/fe/components/DataTable/utils/cn.js +5 -0
  59. package/dist/fe/components/DatePicker/components/CalendarGrid.js +95 -0
  60. package/dist/fe/components/DatePicker/components/CalendarHeader.js +152 -0
  61. package/dist/fe/components/DatePicker/components/DatePicker.js +381 -0
  62. package/dist/fe/components/DatePicker/components/MonthYearSelector.js +93 -0
  63. package/dist/fe/components/DatePicker/index.js +7 -0
  64. package/dist/fe/components/DatePicker/locales/index.js +1113 -0
  65. package/dist/fe/components/DatePicker/theme/index.js +315 -0
  66. package/dist/fe/components/DatePicker/types/index.js +1 -0
  67. package/dist/fe/components/DatePicker/utils/cn.js +5 -0
  68. package/dist/fe/components/DatePicker/utils/date.js +132 -0
  69. package/dist/fe/components/DevicesPage/components/DeviceCard.js +251 -0
  70. package/dist/fe/components/DevicesPage/components/DevicesHeader.js +42 -0
  71. package/dist/fe/components/DevicesPage/components/DevicesPage.js +450 -0
  72. package/dist/fe/components/DevicesPage/index.js +5 -0
  73. package/dist/fe/components/DevicesPage/store/index.js +55 -0
  74. package/dist/fe/components/DevicesPage/theme/index.js +131 -0
  75. package/dist/fe/components/DevicesPage/types/index.js +1 -0
  76. package/dist/fe/components/ForgotPasswordPage/components/ForgotPasswordForm.js +214 -0
  77. package/dist/fe/components/ForgotPasswordPage/components/ForgotPasswordHeader.js +42 -0
  78. package/dist/fe/components/ForgotPasswordPage/components/ForgotPasswordPage.js +59 -0
  79. package/dist/fe/components/ForgotPasswordPage/index.js +5 -0
  80. package/dist/fe/components/ForgotPasswordPage/store/index.js +28 -0
  81. package/dist/fe/components/ForgotPasswordPage/theme/index.js +87 -0
  82. package/dist/fe/components/ForgotPasswordPage/types/index.js +1 -0
  83. package/dist/fe/components/FormBuilder/components/FormBuilder.js +156 -0
  84. package/dist/fe/components/FormBuilder/components/FormField.js +218 -0
  85. package/dist/fe/components/FormBuilder/hooks/useFormBuilder.js +152 -0
  86. package/dist/fe/components/FormBuilder/index.js +4 -0
  87. package/dist/fe/components/FormBuilder/theme/index.js +134 -0
  88. package/dist/fe/components/FormBuilder/types/index.js +1 -0
  89. package/dist/fe/components/FormBuilder/utils/cn.js +5 -0
  90. package/dist/fe/components/FormBuilder/utils/fieldMapping.js +216 -0
  91. package/dist/fe/components/FormBuilder/utils/validation.js +78 -0
  92. package/dist/fe/components/LoginPage/components/LoginForm.js +214 -0
  93. package/dist/fe/components/LoginPage/components/LoginHeader.js +24 -0
  94. package/dist/fe/components/LoginPage/components/LoginPage.js +138 -0
  95. package/dist/fe/components/LoginPage/index.js +5 -0
  96. package/dist/fe/components/LoginPage/store/index.js +59 -0
  97. package/dist/fe/components/LoginPage/theme/index.js +98 -0
  98. package/dist/fe/components/LoginPage/types/index.js +1 -0
  99. package/dist/fe/components/MagicLinkVerifyPage/components/MagicLinkVerifyPage.js +200 -0
  100. package/dist/fe/components/MagicLinkVerifyPage/index.js +3 -0
  101. package/dist/fe/components/MagicLinkVerifyPage/store.js +20 -0
  102. package/dist/fe/components/MagicLinkVerifyPage/theme.js +36 -0
  103. package/dist/fe/components/MagicLinkVerifyPage/types.js +1 -0
  104. package/dist/fe/components/NucleusEntityShowcase.js +1409 -0
  105. package/dist/fe/components/NucleusTextInput/components/FloatingLabel.js +56 -0
  106. package/dist/fe/components/NucleusTextInput/components/InputIcons.js +258 -0
  107. package/dist/fe/components/NucleusTextInput/components/NucleusTextInput.js +321 -0
  108. package/dist/fe/components/NucleusTextInput/components/PasswordStrengthIndicator.js +104 -0
  109. package/dist/fe/components/NucleusTextInput/components/TypewriterText.js +56 -0
  110. package/dist/fe/components/NucleusTextInput/index.js +7 -0
  111. package/dist/fe/components/NucleusTextInput/theme/index.js +121 -0
  112. package/dist/fe/components/NucleusTextInput/types/index.js +1 -0
  113. package/dist/fe/components/NucleusTextInput/utils/cn.js +5 -0
  114. package/dist/fe/components/NucleusTextInput/utils/format.js +62 -0
  115. package/dist/fe/components/NucleusTextInput/utils/validation.js +191 -0
  116. package/dist/fe/components/ProfilePage/components/AddressCard.js +196 -0
  117. package/dist/fe/components/ProfilePage/components/PhoneCard.js +206 -0
  118. package/dist/fe/components/ProfilePage/components/ProfileHeader.js +150 -0
  119. package/dist/fe/components/ProfilePage/components/ProfilePage.js +1336 -0
  120. package/dist/fe/components/ProfilePage/index.js +6 -0
  121. package/dist/fe/components/ProfilePage/store/index.js +115 -0
  122. package/dist/fe/components/ProfilePage/theme/index.js +168 -0
  123. package/dist/fe/components/ProfilePage/types/index.js +1 -0
  124. package/dist/fe/components/RangePicker/components/RangePicker.js +338 -0
  125. package/dist/fe/components/RangePicker/components/RangeThumb.js +68 -0
  126. package/dist/fe/components/RangePicker/components/RangeTooltip.js +45 -0
  127. package/dist/fe/components/RangePicker/components/RangeTrack.js +32 -0
  128. package/dist/fe/components/RangePicker/index.js +5 -0
  129. package/dist/fe/components/RangePicker/theme/index.js +88 -0
  130. package/dist/fe/components/RangePicker/types/index.js +1 -0
  131. package/dist/fe/components/RangePicker/utils/cn.js +3 -0
  132. package/dist/fe/components/RegisterPage/components/PasswordStrengthIndicator.js +107 -0
  133. package/dist/fe/components/RegisterPage/components/RegisterForm.js +322 -0
  134. package/dist/fe/components/RegisterPage/components/RegisterHeader.js +23 -0
  135. package/dist/fe/components/RegisterPage/components/RegisterPage.js +85 -0
  136. package/dist/fe/components/RegisterPage/index.js +6 -0
  137. package/dist/fe/components/RegisterPage/store/index.js +106 -0
  138. package/dist/fe/components/RegisterPage/theme/index.js +128 -0
  139. package/dist/fe/components/RegisterPage/types/index.js +1 -0
  140. package/dist/fe/components/ResetPasswordPage/components/ResetPasswordForm.js +347 -0
  141. package/dist/fe/components/ResetPasswordPage/components/ResetPasswordHeader.js +42 -0
  142. package/dist/fe/components/ResetPasswordPage/components/ResetPasswordPage.js +61 -0
  143. package/dist/fe/components/ResetPasswordPage/index.js +5 -0
  144. package/dist/fe/components/ResetPasswordPage/store/index.js +36 -0
  145. package/dist/fe/components/ResetPasswordPage/theme/index.js +99 -0
  146. package/dist/fe/components/ResetPasswordPage/types/index.js +1 -0
  147. package/dist/fe/components/SearchBox/components/SearchBox.js +271 -0
  148. package/dist/fe/components/SearchBox/components/SearchBoxDropdown.js +87 -0
  149. package/dist/fe/components/SearchBox/index.js +5 -0
  150. package/dist/fe/components/SearchBox/theme/index.js +184 -0
  151. package/dist/fe/components/SearchBox/types/index.js +1 -0
  152. package/dist/fe/components/SearchBox/utils/cn.js +5 -0
  153. package/dist/fe/components/SearchBox/utils/debounce.js +22 -0
  154. package/dist/fe/components/SearchBox/utils/sanitize.js +48 -0
  155. package/dist/fe/components/SelectBox/components/SelectBox.js +364 -0
  156. package/dist/fe/components/SelectBox/components/SelectDropdown.js +92 -0
  157. package/dist/fe/components/SelectBox/components/SelectOptionItem.js +43 -0
  158. package/dist/fe/components/SelectBox/components/SelectTrigger.js +22 -0
  159. package/dist/fe/components/SelectBox/index.js +5 -0
  160. package/dist/fe/components/SelectBox/theme/index.js +98 -0
  161. package/dist/fe/components/SelectBox/types/index.js +1 -0
  162. package/dist/fe/components/SelectBox/utils/cn.js +3 -0
  163. package/dist/fe/components/SetPasswordPage/components/PasswordStrengthIndicator.js +107 -0
  164. package/dist/fe/components/SetPasswordPage/components/SetPasswordForm.js +142 -0
  165. package/dist/fe/components/SetPasswordPage/components/SetPasswordHeader.js +23 -0
  166. package/dist/fe/components/SetPasswordPage/components/SetPasswordPage.js +263 -0
  167. package/dist/fe/components/SetPasswordPage/index.js +7 -0
  168. package/dist/fe/components/SetPasswordPage/store/index.js +79 -0
  169. package/dist/fe/components/SetPasswordPage/theme/index.js +98 -0
  170. package/dist/fe/components/SetPasswordPage/types/index.js +12 -0
  171. package/dist/fe/components/UsersPage/components/InviteUserModal.js +262 -0
  172. package/dist/fe/components/UsersPage/components/Pagination.js +147 -0
  173. package/dist/fe/components/UsersPage/components/RoleAssignmentModal.js +186 -0
  174. package/dist/fe/components/UsersPage/components/StatsCards.js +124 -0
  175. package/dist/fe/components/UsersPage/components/UserDetailDrawer.js +444 -0
  176. package/dist/fe/components/UsersPage/components/UserFilters.js +142 -0
  177. package/dist/fe/components/UsersPage/components/UserListItem.js +125 -0
  178. package/dist/fe/components/UsersPage/components/UserListSkeleton.js +40 -0
  179. package/dist/fe/components/UsersPage/components/UsersPage.js +556 -0
  180. package/dist/fe/components/UsersPage/index.js +10 -0
  181. package/dist/fe/components/UsersPage/store/index.js +151 -0
  182. package/dist/fe/components/UsersPage/theme/index.js +231 -0
  183. package/dist/fe/components/UsersPage/types/index.js +1 -0
  184. package/dist/fe/components/VerifyEmailPage/components/VerifyEmailPage.js +290 -0
  185. package/dist/fe/components/VerifyEmailPage/index.js +3 -0
  186. package/dist/fe/components/VerifyEmailPage/store/index.js +45 -0
  187. package/dist/fe/components/VerifyEmailPage/theme/index.js +52 -0
  188. package/dist/fe/components/VerifyEmailPage/types/index.js +1 -0
  189. package/dist/fe/hooks/useNucleusEntity.js +247 -0
  190. package/dist/fe/index.js +28 -157
  191. package/dist/fe/types/index.js +1 -0
  192. package/dist/fe/utils/cn.js +5 -0
  193. package/dist/fe/utils/columnUtils.js +189 -0
  194. package/dist/fe/utils/endpointKeys.js +44 -0
  195. package/dist/index.js +1 -1
  196. package/dist/src/Client/Proxy/httpProxy.js +1 -0
  197. package/dist/src/Client/Proxy/index.js +1 -1
  198. package/dist/src/Client/Proxy/server.js +1 -0
  199. package/dist/src/Client/Proxy/types.js +1 -0
  200. package/dist/src/Client/Proxy/utils.js +1 -0
  201. package/dist/src/Client/Proxy/wsProxy.js +1 -0
  202. package/package.json +1 -1
  203. package/scripts/build.ts +32 -16
@@ -0,0 +1,364 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useGSAP } from "@gsap/react";
4
+ import gsap from "gsap";
5
+ import { useEffect, useRef, useState } from "react";
6
+ import { selectBoxTheme } from "../theme";
7
+ import { cn } from "../utils/cn";
8
+ import { SelectDropdown } from "./SelectDropdown";
9
+ import { SelectOptionItem } from "./SelectOptionItem";
10
+ import { SelectTrigger } from "./SelectTrigger";
11
+ gsap.registerPlugin(useGSAP);
12
+ export function SelectBox({ options, value: controlledValue, defaultValue, placeholder = "Select...", disabled = false, readOnly = false, loading = false, clearable = false, searchable = false, size = "md", variant = "default", position = "auto", maxHeight = 240, emptyText = "No options", loadingText = "Loading...", searchPlaceholder = "Search...", onChange, onSearch, onOpen, onClose, renderOption, renderValue, label, error, helperText, required = false, name, ariaLabel, className, ...props }) {
13
+ const theme = selectBoxTheme;
14
+ const containerRef = useRef(null);
15
+ const triggerRef = useRef(null);
16
+ const dropdownRef = useRef(null);
17
+ const searchInputRef = useRef(null);
18
+ const [isOpen, setIsOpen] = useState(false);
19
+ const [internalValue, setInternalValue] = useState(defaultValue);
20
+ const [searchQuery, setSearchQuery] = useState("");
21
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
22
+ const [dropdownPosition, setDropdownPosition] = useState("bottom");
23
+ const value = controlledValue !== undefined ? controlledValue : internalValue;
24
+ const isControlled = controlledValue !== undefined;
25
+ const selectedOption = options.find((opt)=>opt.value === value);
26
+ const filteredOptions = searchable && searchQuery ? options.filter((opt)=>opt.label.toLowerCase().includes(searchQuery.toLowerCase())) : options;
27
+ const calculatePosition = ()=>{
28
+ if (position !== "auto") {
29
+ setDropdownPosition(position);
30
+ return;
31
+ }
32
+ if (!triggerRef.current) return;
33
+ const rect = triggerRef.current.getBoundingClientRect();
34
+ const spaceBelow = window.innerHeight - rect.bottom;
35
+ const spaceAbove = rect.top;
36
+ const dropdownHeight = Math.min(maxHeight + 50, 300);
37
+ if (spaceBelow < dropdownHeight && spaceAbove > spaceBelow) {
38
+ setDropdownPosition("top");
39
+ } else {
40
+ setDropdownPosition("bottom");
41
+ }
42
+ };
43
+ const openDropdown = ()=>{
44
+ if (disabled || readOnly) return;
45
+ calculatePosition();
46
+ setIsOpen(true);
47
+ setHighlightedIndex(-1);
48
+ onOpen?.();
49
+ };
50
+ const closeDropdown = ()=>{
51
+ setIsOpen(false);
52
+ setSearchQuery("");
53
+ setHighlightedIndex(-1);
54
+ onClose?.();
55
+ };
56
+ const handleSelect = (option)=>{
57
+ if (option.disabled) return;
58
+ if (!isControlled) {
59
+ setInternalValue(option.value);
60
+ }
61
+ onChange?.(option.value);
62
+ closeDropdown();
63
+ triggerRef.current?.focus();
64
+ };
65
+ const handleClear = (e)=>{
66
+ e.stopPropagation();
67
+ if (!isControlled) {
68
+ setInternalValue(undefined);
69
+ }
70
+ onChange?.(undefined);
71
+ };
72
+ const handleSearchChange = (e)=>{
73
+ const query = e.target.value;
74
+ setSearchQuery(query);
75
+ setHighlightedIndex(-1);
76
+ onSearch?.(query);
77
+ };
78
+ const handleTriggerKeyDown = (e)=>{
79
+ switch(e.key){
80
+ case "Enter":
81
+ case " ":
82
+ e.preventDefault();
83
+ if (isOpen) {
84
+ if (highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
85
+ handleSelect(filteredOptions[highlightedIndex]);
86
+ }
87
+ } else {
88
+ openDropdown();
89
+ }
90
+ break;
91
+ case "ArrowDown":
92
+ e.preventDefault();
93
+ if (!isOpen) {
94
+ openDropdown();
95
+ } else {
96
+ setHighlightedIndex((prev)=>prev < filteredOptions.length - 1 ? prev + 1 : 0);
97
+ }
98
+ break;
99
+ case "ArrowUp":
100
+ e.preventDefault();
101
+ if (!isOpen) {
102
+ openDropdown();
103
+ } else {
104
+ setHighlightedIndex((prev)=>prev > 0 ? prev - 1 : filteredOptions.length - 1);
105
+ }
106
+ break;
107
+ case "Escape":
108
+ if (isOpen) {
109
+ e.preventDefault();
110
+ closeDropdown();
111
+ }
112
+ break;
113
+ case "Tab":
114
+ if (isOpen) {
115
+ closeDropdown();
116
+ }
117
+ break;
118
+ case "Home":
119
+ if (isOpen) {
120
+ e.preventDefault();
121
+ setHighlightedIndex(0);
122
+ }
123
+ break;
124
+ case "End":
125
+ if (isOpen) {
126
+ e.preventDefault();
127
+ setHighlightedIndex(filteredOptions.length - 1);
128
+ }
129
+ break;
130
+ }
131
+ };
132
+ useEffect(()=>{
133
+ if (!isOpen) return;
134
+ const handleClickOutside = (e)=>{
135
+ const target = e.target;
136
+ const isInsideContainer = containerRef.current?.contains(target);
137
+ const isInsideDropdown = dropdownRef.current?.contains(target);
138
+ if (!isInsideContainer && !isInsideDropdown) {
139
+ closeDropdown();
140
+ }
141
+ };
142
+ const handleEscape = (e)=>{
143
+ if (e.key === "Escape") {
144
+ closeDropdown();
145
+ }
146
+ };
147
+ document.addEventListener("mousedown", handleClickOutside);
148
+ document.addEventListener("keydown", handleEscape);
149
+ return ()=>{
150
+ document.removeEventListener("mousedown", handleClickOutside);
151
+ document.removeEventListener("keydown", handleEscape);
152
+ };
153
+ }, [
154
+ isOpen
155
+ ]);
156
+ useEffect(()=>{
157
+ if (isOpen && searchable && searchInputRef.current) {
158
+ searchInputRef.current.focus();
159
+ }
160
+ }, [
161
+ isOpen,
162
+ searchable
163
+ ]);
164
+ useEffect(()=>{
165
+ if (isOpen && highlightedIndex >= 0 && dropdownRef.current) {
166
+ const highlightedEl = dropdownRef.current.querySelector(`[data-index="${highlightedIndex}"]`);
167
+ if (highlightedEl) {
168
+ highlightedEl.scrollIntoView({
169
+ block: "nearest"
170
+ });
171
+ }
172
+ }
173
+ }, [
174
+ highlightedIndex,
175
+ isOpen
176
+ ]);
177
+ const iconRef = useRef(null);
178
+ useGSAP(()=>{
179
+ if (iconRef.current) {
180
+ gsap.to(iconRef.current, {
181
+ rotation: isOpen ? 180 : 0,
182
+ duration: 0.2,
183
+ ease: "power2.out"
184
+ });
185
+ }
186
+ }, [
187
+ isOpen
188
+ ]);
189
+ return /*#__PURE__*/ _jsxs("div", {
190
+ ref: containerRef,
191
+ className: cn(theme.container.base, className),
192
+ ...props,
193
+ children: [
194
+ label && /*#__PURE__*/ _jsx("span", {
195
+ className: cn(theme.label.base, theme.label.size[size], required && theme.label.required, error && theme.label.error),
196
+ id: `${props.id || "select"}-label`,
197
+ children: label
198
+ }),
199
+ /*#__PURE__*/ _jsxs(SelectTrigger, {
200
+ ref: triggerRef,
201
+ isOpen: isOpen,
202
+ disabled: disabled,
203
+ loading: loading,
204
+ size: size,
205
+ variant: variant,
206
+ hasValue: !!selectedOption,
207
+ hasError: !!error,
208
+ onClick: ()=>isOpen ? closeDropdown() : openDropdown(),
209
+ onKeyDown: handleTriggerKeyDown,
210
+ children: [
211
+ /*#__PURE__*/ _jsx("span", {
212
+ className: selectedOption ? theme.value.base : theme.placeholder.base,
213
+ children: selectedOption ? renderValue ? renderValue(selectedOption) : selectedOption.icon ? /*#__PURE__*/ _jsxs(_Fragment, {
214
+ children: [
215
+ selectedOption.icon,
216
+ selectedOption.label
217
+ ]
218
+ }) : selectedOption.label : placeholder
219
+ }),
220
+ /*#__PURE__*/ _jsxs("div", {
221
+ className: "flex items-center gap-1",
222
+ children: [
223
+ clearable && value !== undefined && value !== "" && !disabled && // biome-ignore lint/a11y/useSemanticElements: Cannot use button inside button
224
+ /*#__PURE__*/ _jsx("span", {
225
+ role: "button",
226
+ tabIndex: 0,
227
+ onClick: handleClear,
228
+ onKeyDown: (e)=>{
229
+ if (e.key === "Enter" || e.key === " ") {
230
+ e.preventDefault();
231
+ handleClear(e);
232
+ }
233
+ },
234
+ className: cn(theme.clearButton.base, theme.clearButton.hover),
235
+ "aria-label": "Clear selection",
236
+ children: /*#__PURE__*/ _jsx("svg", {
237
+ className: "w-4 h-4",
238
+ viewBox: "0 0 20 20",
239
+ fill: "currentColor",
240
+ "aria-hidden": "true",
241
+ children: /*#__PURE__*/ _jsx("path", {
242
+ d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
243
+ })
244
+ })
245
+ }),
246
+ loading ? /*#__PURE__*/ _jsxs("svg", {
247
+ className: cn(theme.loading.spinner, theme.icon.size[size]),
248
+ viewBox: "0 0 24 24",
249
+ fill: "none",
250
+ "aria-hidden": "true",
251
+ children: [
252
+ /*#__PURE__*/ _jsx("circle", {
253
+ className: "opacity-25",
254
+ cx: "12",
255
+ cy: "12",
256
+ r: "10",
257
+ stroke: "currentColor",
258
+ strokeWidth: "4"
259
+ }),
260
+ /*#__PURE__*/ _jsx("path", {
261
+ className: "opacity-75",
262
+ fill: "currentColor",
263
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
264
+ })
265
+ ]
266
+ }) : /*#__PURE__*/ _jsx("svg", {
267
+ ref: iconRef,
268
+ className: cn(theme.icon.base, theme.icon.size[size]),
269
+ viewBox: "0 0 20 20",
270
+ fill: "currentColor",
271
+ "aria-hidden": "true",
272
+ children: /*#__PURE__*/ _jsx("path", {
273
+ fillRule: "evenodd",
274
+ d: "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z",
275
+ clipRule: "evenodd"
276
+ })
277
+ })
278
+ ]
279
+ })
280
+ ]
281
+ }),
282
+ name && /*#__PURE__*/ _jsx("input", {
283
+ type: "hidden",
284
+ name: name,
285
+ value: String(value ?? "")
286
+ }),
287
+ /*#__PURE__*/ _jsxs(SelectDropdown, {
288
+ ref: dropdownRef,
289
+ isOpen: isOpen,
290
+ position: dropdownPosition,
291
+ size: size,
292
+ maxHeight: maxHeight,
293
+ triggerRef: triggerRef,
294
+ children: [
295
+ searchable && /*#__PURE__*/ _jsx("div", {
296
+ className: theme.search.container,
297
+ children: /*#__PURE__*/ _jsx("input", {
298
+ ref: searchInputRef,
299
+ type: "text",
300
+ value: searchQuery,
301
+ onChange: handleSearchChange,
302
+ placeholder: searchPlaceholder,
303
+ className: theme.search.input,
304
+ onClick: (e)=>e.stopPropagation()
305
+ })
306
+ }),
307
+ loading ? /*#__PURE__*/ _jsxs("div", {
308
+ className: theme.loading.base,
309
+ children: [
310
+ /*#__PURE__*/ _jsxs("svg", {
311
+ className: theme.loading.spinner,
312
+ viewBox: "0 0 24 24",
313
+ fill: "none",
314
+ "aria-hidden": "true",
315
+ children: [
316
+ /*#__PURE__*/ _jsx("circle", {
317
+ className: "opacity-25",
318
+ cx: "12",
319
+ cy: "12",
320
+ r: "10",
321
+ stroke: "currentColor",
322
+ strokeWidth: "4"
323
+ }),
324
+ /*#__PURE__*/ _jsx("path", {
325
+ className: "opacity-75",
326
+ fill: "currentColor",
327
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
328
+ })
329
+ ]
330
+ }),
331
+ loadingText
332
+ ]
333
+ }) : filteredOptions.length === 0 ? /*#__PURE__*/ _jsx("div", {
334
+ className: theme.empty.base,
335
+ children: emptyText
336
+ }) : filteredOptions.map((option, index)=>/*#__PURE__*/ _jsx("div", {
337
+ "data-index": index,
338
+ children: renderOption ? /*#__PURE__*/ _jsx("div", {
339
+ onClick: ()=>handleSelect(option),
340
+ onKeyDown: (e)=>{
341
+ if (e.key === "Enter") handleSelect(option);
342
+ },
343
+ role: "option",
344
+ tabIndex: 0,
345
+ "aria-selected": option.value === value,
346
+ children: renderOption(option, option.value === value)
347
+ }) : /*#__PURE__*/ _jsx(SelectOptionItem, {
348
+ option: option,
349
+ isSelected: option.value === value,
350
+ isHighlighted: index === highlightedIndex,
351
+ size: size,
352
+ onClick: ()=>handleSelect(option),
353
+ onMouseEnter: ()=>setHighlightedIndex(index)
354
+ })
355
+ }, String(option.value)))
356
+ ]
357
+ }),
358
+ (helperText || error) && /*#__PURE__*/ _jsx("p", {
359
+ className: cn(theme.helper.base, error && theme.helper.error),
360
+ children: error || helperText
361
+ })
362
+ ]
363
+ });
364
+ }
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useGSAP } from "@gsap/react";
4
+ import gsap from "gsap";
5
+ import { forwardRef, useEffect, useRef, useState } from "react";
6
+ import { createPortal } from "react-dom";
7
+ import { selectBoxTheme } from "../theme";
8
+ import { cn } from "../utils/cn";
9
+ gsap.registerPlugin(useGSAP);
10
+ export const SelectDropdown = /*#__PURE__*/ forwardRef(({ isOpen, position, size, maxHeight, children, triggerRef }, ref)=>{
11
+ const theme = selectBoxTheme;
12
+ const dropdownRef = useRef(null);
13
+ const [mounted, setMounted] = useState(false);
14
+ const [dropdownStyle, setDropdownStyle] = useState({});
15
+ useEffect(()=>{
16
+ setMounted(true);
17
+ }, []);
18
+ useEffect(()=>{
19
+ if (!isOpen || !triggerRef?.current) return;
20
+ const updatePosition = ()=>{
21
+ const triggerRect = triggerRef.current?.getBoundingClientRect();
22
+ if (!triggerRect) return;
23
+ setDropdownStyle({
24
+ position: "fixed",
25
+ left: triggerRect.left,
26
+ width: triggerRect.width,
27
+ maxHeight,
28
+ overflowY: "auto",
29
+ zIndex: 9999,
30
+ ...position === "top" ? {
31
+ bottom: window.innerHeight - triggerRect.top + 4
32
+ } : {
33
+ top: triggerRect.bottom + 4
34
+ }
35
+ });
36
+ };
37
+ updatePosition();
38
+ window.addEventListener("scroll", updatePosition, true);
39
+ window.addEventListener("resize", updatePosition);
40
+ return ()=>{
41
+ window.removeEventListener("scroll", updatePosition, true);
42
+ window.removeEventListener("resize", updatePosition);
43
+ };
44
+ }, [
45
+ isOpen,
46
+ position,
47
+ maxHeight,
48
+ triggerRef
49
+ ]);
50
+ useGSAP(()=>{
51
+ if (!dropdownRef.current) return;
52
+ if (isOpen) {
53
+ gsap.fromTo(dropdownRef.current, {
54
+ opacity: 0,
55
+ y: position === "bottom" ? -8 : 8,
56
+ scale: 0.96
57
+ }, {
58
+ opacity: 1,
59
+ y: 0,
60
+ scale: 1,
61
+ duration: 0.2,
62
+ ease: "power2.out"
63
+ });
64
+ }
65
+ }, [
66
+ isOpen,
67
+ position
68
+ ]);
69
+ if (!isOpen || !mounted) return null;
70
+ const dropdownContent = /*#__PURE__*/ _jsx("div", {
71
+ ref: (node)=>{
72
+ dropdownRef.current = node;
73
+ if (typeof ref === "function") {
74
+ ref(node);
75
+ } else if (ref) {
76
+ ref.current = node;
77
+ }
78
+ },
79
+ role: "listbox",
80
+ className: cn(theme.dropdown.base, theme.dropdown.size[size], !triggerRef && theme.dropdown.position[position]),
81
+ style: triggerRef ? dropdownStyle : {
82
+ maxHeight,
83
+ overflowY: "auto"
84
+ },
85
+ children: children
86
+ });
87
+ if (triggerRef) {
88
+ return /*#__PURE__*/ createPortal(dropdownContent, document.body);
89
+ }
90
+ return dropdownContent;
91
+ });
92
+ SelectDropdown.displayName = "SelectDropdown";
@@ -0,0 +1,43 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { selectBoxTheme } from "../theme";
4
+ import { cn } from "../utils/cn";
5
+ export function SelectOptionItem({ option, isSelected, isHighlighted, size, onClick, onMouseEnter }) {
6
+ const theme = selectBoxTheme;
7
+ return /*#__PURE__*/ _jsxs("div", {
8
+ role: "option",
9
+ tabIndex: option.disabled ? -1 : 0,
10
+ "aria-selected": isSelected,
11
+ "aria-disabled": option.disabled,
12
+ onClick: option.disabled ? undefined : onClick,
13
+ onKeyDown: (e)=>{
14
+ if (e.key === "Enter" || e.key === " ") {
15
+ e.preventDefault();
16
+ if (!option.disabled) onClick();
17
+ }
18
+ },
19
+ onMouseEnter: onMouseEnter,
20
+ className: cn(theme.option.base, theme.option.size[size], !option.disabled && theme.option.states.hover, isSelected && theme.option.states.selected, isHighlighted && !isSelected && theme.option.states.highlighted, option.disabled && theme.option.states.disabled),
21
+ children: [
22
+ option.icon && /*#__PURE__*/ _jsx("span", {
23
+ className: theme.option.icon,
24
+ children: option.icon
25
+ }),
26
+ /*#__PURE__*/ _jsx("span", {
27
+ className: "truncate",
28
+ children: option.label
29
+ }),
30
+ isSelected && /*#__PURE__*/ _jsx("svg", {
31
+ className: theme.option.checkmark,
32
+ viewBox: "0 0 20 20",
33
+ fill: "currentColor",
34
+ "aria-hidden": "true",
35
+ children: /*#__PURE__*/ _jsx("path", {
36
+ fillRule: "evenodd",
37
+ d: "M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z",
38
+ clipRule: "evenodd"
39
+ })
40
+ })
41
+ ]
42
+ });
43
+ }
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { forwardRef } from "react";
4
+ import { selectBoxTheme } from "../theme";
5
+ import { cn } from "../utils/cn";
6
+ export const SelectTrigger = /*#__PURE__*/ forwardRef(({ isOpen, disabled, loading, size, variant, hasValue, hasError, onClick, onKeyDown, children }, ref)=>{
7
+ const theme = selectBoxTheme;
8
+ return /*#__PURE__*/ _jsx("button", {
9
+ ref: ref,
10
+ type: "button",
11
+ role: "combobox",
12
+ "aria-expanded": isOpen,
13
+ "aria-haspopup": "listbox",
14
+ "aria-disabled": disabled,
15
+ disabled: disabled,
16
+ onClick: onClick,
17
+ onKeyDown: onKeyDown,
18
+ className: cn(theme.trigger.base, theme.trigger.size[size], theme.trigger.variant[variant], !disabled && theme.trigger.states.hover, theme.trigger.states.focus, disabled && theme.trigger.states.disabled, hasError && theme.trigger.states.error, isOpen && theme.trigger.states.open),
19
+ children: children
20
+ });
21
+ });
22
+ SelectTrigger.displayName = "SelectTrigger";
@@ -0,0 +1,5 @@
1
+ export { SelectBox } from "./components/SelectBox";
2
+ export { SelectDropdown } from "./components/SelectDropdown";
3
+ export { SelectOptionItem } from "./components/SelectOptionItem";
4
+ export { SelectTrigger } from "./components/SelectTrigger";
5
+ export { selectBoxTheme } from "./theme";
@@ -0,0 +1,98 @@
1
+ export const selectBoxTheme = {
2
+ container: {
3
+ base: "relative w-full"
4
+ },
5
+ label: {
6
+ base: "block font-medium text-zinc-700 dark:text-zinc-300 mb-1.5",
7
+ size: {
8
+ sm: "text-xs",
9
+ md: "text-sm",
10
+ lg: "text-base"
11
+ },
12
+ required: 'after:content-["*"] after:ml-0.5 after:text-red-500',
13
+ error: "text-red-600 dark:text-red-400"
14
+ },
15
+ trigger: {
16
+ base: "relative w-full flex items-center justify-between gap-2 rounded-lg border bg-white dark:bg-zinc-900 text-left cursor-pointer transition-all duration-150",
17
+ size: {
18
+ sm: "px-2.5 py-1.5 text-sm min-h-[32px]",
19
+ md: "px-3 py-2 text-sm min-h-[40px]",
20
+ lg: "px-4 py-2.5 text-base min-h-[48px]"
21
+ },
22
+ variant: {
23
+ default: "border-zinc-300 dark:border-zinc-600",
24
+ filled: "border-transparent bg-zinc-100 dark:bg-zinc-800",
25
+ ghost: "border-transparent bg-transparent"
26
+ },
27
+ states: {
28
+ hover: "hover:border-zinc-400 dark:hover:border-zinc-500",
29
+ focus: "focus:outline-none focus:ring-2 focus:ring-zinc-400/50 dark:focus:ring-zinc-500/50 focus:border-zinc-400 dark:focus:border-zinc-500",
30
+ disabled: "opacity-50 cursor-not-allowed pointer-events-none",
31
+ error: "border-red-500 dark:border-red-400 focus:ring-red-400/50",
32
+ open: "ring-2 ring-zinc-400/50 dark:ring-zinc-500/50 border-zinc-400 dark:border-zinc-500"
33
+ }
34
+ },
35
+ placeholder: {
36
+ base: "text-zinc-400 dark:text-zinc-500 truncate"
37
+ },
38
+ value: {
39
+ base: "text-zinc-900 dark:text-white truncate flex items-center gap-2"
40
+ },
41
+ icon: {
42
+ base: "flex-shrink-0 text-zinc-400 dark:text-zinc-500 transition-transform duration-200",
43
+ size: {
44
+ sm: "w-4 h-4",
45
+ md: "w-4 h-4",
46
+ lg: "w-5 h-5"
47
+ },
48
+ open: "rotate-180"
49
+ },
50
+ clearButton: {
51
+ base: "flex-shrink-0 p-0.5 rounded text-zinc-400 dark:text-zinc-500 transition-colors",
52
+ hover: "hover:text-zinc-600 dark:hover:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800"
53
+ },
54
+ dropdown: {
55
+ base: "absolute left-0 right-0 z-50 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded-lg shadow-lg overflow-hidden",
56
+ position: {
57
+ top: "bottom-full mb-1",
58
+ bottom: "top-full mt-1"
59
+ },
60
+ size: {
61
+ sm: "py-1",
62
+ md: "py-1.5",
63
+ lg: "py-2"
64
+ }
65
+ },
66
+ search: {
67
+ container: "px-2 pb-1.5 border-b border-zinc-100 dark:border-zinc-800",
68
+ input: "w-full px-2.5 py-1.5 text-sm bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-md outline-none focus:ring-1 focus:ring-zinc-400/50 dark:focus:ring-zinc-500/50 text-zinc-900 dark:text-white placeholder:text-zinc-400 dark:placeholder:text-zinc-500",
69
+ icon: "absolute left-4.5 top-1/2 -translate-y-1/2 w-4 h-4 text-zinc-400"
70
+ },
71
+ option: {
72
+ base: "relative flex items-center gap-2 cursor-pointer transition-colors duration-100",
73
+ size: {
74
+ sm: "px-2.5 py-1.5 text-sm",
75
+ md: "px-3 py-2 text-sm",
76
+ lg: "px-4 py-2.5 text-base"
77
+ },
78
+ states: {
79
+ hover: "bg-zinc-50 dark:bg-zinc-800",
80
+ selected: "bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-white",
81
+ highlighted: "bg-zinc-100 dark:bg-zinc-800",
82
+ disabled: "opacity-50 cursor-not-allowed pointer-events-none"
83
+ },
84
+ icon: "flex-shrink-0 w-4 h-4",
85
+ checkmark: "ml-auto flex-shrink-0 w-4 h-4 text-zinc-900 dark:text-white"
86
+ },
87
+ empty: {
88
+ base: "px-3 py-6 text-center text-sm text-zinc-400 dark:text-zinc-500"
89
+ },
90
+ loading: {
91
+ base: "px-3 py-6 flex items-center justify-center gap-2 text-sm text-zinc-400 dark:text-zinc-500",
92
+ spinner: "w-4 h-4 animate-spin"
93
+ },
94
+ helper: {
95
+ base: "mt-1.5 text-xs text-zinc-500 dark:text-zinc-400",
96
+ error: "text-red-600 dark:text-red-400"
97
+ }
98
+ };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,3 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ export const cn = (...inputs)=>twMerge(clsx(inputs));