warqadui 0.0.8 → 0.0.11

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 (170) hide show
  1. package/{packages/ui/dist → dist}/index.d.mts +100 -12
  2. package/{packages/ui/dist → dist}/index.d.ts +100 -12
  3. package/dist/index.js +3276 -0
  4. package/dist/index.mjs +3237 -0
  5. package/dist/styles.js +26 -0
  6. package/dist/styles.mjs +24 -0
  7. package/package.json +60 -10
  8. package/.vscode/settings.json +0 -3
  9. package/apps/dev-app/.env +0 -1
  10. package/apps/dev-app/errors.log +0 -0
  11. package/apps/dev-app/index.html +0 -12
  12. package/apps/dev-app/node_modules/.vite/deps/@tanstack_react-table.js +0 -3254
  13. package/apps/dev-app/node_modules/.vite/deps/@tanstack_react-table.js.map +0 -7
  14. package/apps/dev-app/node_modules/.vite/deps/_metadata.json +0 -166
  15. package/apps/dev-app/node_modules/.vite/deps/antd.js +0 -108982
  16. package/apps/dev-app/node_modules/.vite/deps/antd.js.map +0 -7
  17. package/apps/dev-app/node_modules/.vite/deps/axios.js +0 -2751
  18. package/apps/dev-app/node_modules/.vite/deps/axios.js.map +0 -7
  19. package/apps/dev-app/node_modules/.vite/deps/chunk-5OG7DCD7.js +0 -41
  20. package/apps/dev-app/node_modules/.vite/deps/chunk-5OG7DCD7.js.map +0 -7
  21. package/apps/dev-app/node_modules/.vite/deps/chunk-DC5AMYBS.js +0 -39
  22. package/apps/dev-app/node_modules/.vite/deps/chunk-DC5AMYBS.js.map +0 -7
  23. package/apps/dev-app/node_modules/.vite/deps/chunk-DKXRQMOD.js +0 -135
  24. package/apps/dev-app/node_modules/.vite/deps/chunk-DKXRQMOD.js.map +0 -7
  25. package/apps/dev-app/node_modules/.vite/deps/chunk-EL47BWQR.js +0 -37
  26. package/apps/dev-app/node_modules/.vite/deps/chunk-EL47BWQR.js.map +0 -7
  27. package/apps/dev-app/node_modules/.vite/deps/chunk-HHL3MHGV.js +0 -288
  28. package/apps/dev-app/node_modules/.vite/deps/chunk-HHL3MHGV.js.map +0 -7
  29. package/apps/dev-app/node_modules/.vite/deps/chunk-IGGUWUPT.js +0 -60
  30. package/apps/dev-app/node_modules/.vite/deps/chunk-IGGUWUPT.js.map +0 -7
  31. package/apps/dev-app/node_modules/.vite/deps/chunk-IGXZPJXT.js +0 -928
  32. package/apps/dev-app/node_modules/.vite/deps/chunk-IGXZPJXT.js.map +0 -7
  33. package/apps/dev-app/node_modules/.vite/deps/chunk-L2GCM37S.js +0 -21628
  34. package/apps/dev-app/node_modules/.vite/deps/chunk-L2GCM37S.js.map +0 -7
  35. package/apps/dev-app/node_modules/.vite/deps/chunk-M7DZDBHW.js +0 -14
  36. package/apps/dev-app/node_modules/.vite/deps/chunk-M7DZDBHW.js.map +0 -7
  37. package/apps/dev-app/node_modules/.vite/deps/chunk-S54SBVCU.js +0 -1906
  38. package/apps/dev-app/node_modules/.vite/deps/chunk-S54SBVCU.js.map +0 -7
  39. package/apps/dev-app/node_modules/.vite/deps/chunk-WFNHCR67.js +0 -21
  40. package/apps/dev-app/node_modules/.vite/deps/chunk-WFNHCR67.js.map +0 -7
  41. package/apps/dev-app/node_modules/.vite/deps/clsx.js +0 -10
  42. package/apps/dev-app/node_modules/.vite/deps/clsx.js.map +0 -7
  43. package/apps/dev-app/node_modules/.vite/deps/dayjs.js +0 -6
  44. package/apps/dev-app/node_modules/.vite/deps/dayjs.js.map +0 -7
  45. package/apps/dev-app/node_modules/.vite/deps/dayjs_plugin_customParseFormat.js +0 -6
  46. package/apps/dev-app/node_modules/.vite/deps/dayjs_plugin_customParseFormat.js.map +0 -7
  47. package/apps/dev-app/node_modules/.vite/deps/framer-motion.js +0 -12388
  48. package/apps/dev-app/node_modules/.vite/deps/framer-motion.js.map +0 -7
  49. package/apps/dev-app/node_modules/.vite/deps/html2canvas-pro.js +0 -9713
  50. package/apps/dev-app/node_modules/.vite/deps/html2canvas-pro.js.map +0 -7
  51. package/apps/dev-app/node_modules/.vite/deps/html2canvas.esm-IYRWPPEI.js +0 -7808
  52. package/apps/dev-app/node_modules/.vite/deps/html2canvas.esm-IYRWPPEI.js.map +0 -7
  53. package/apps/dev-app/node_modules/.vite/deps/index.es-3WTXOFZ2.js +0 -10392
  54. package/apps/dev-app/node_modules/.vite/deps/index.es-3WTXOFZ2.js.map +0 -7
  55. package/apps/dev-app/node_modules/.vite/deps/jspdf.js +0 -14806
  56. package/apps/dev-app/node_modules/.vite/deps/jspdf.js.map +0 -7
  57. package/apps/dev-app/node_modules/.vite/deps/lucide-react.js +0 -31586
  58. package/apps/dev-app/node_modules/.vite/deps/lucide-react.js.map +0 -7
  59. package/apps/dev-app/node_modules/.vite/deps/package.json +0 -3
  60. package/apps/dev-app/node_modules/.vite/deps/purify.es-JNLDEIMX.js +0 -1029
  61. package/apps/dev-app/node_modules/.vite/deps/purify.es-JNLDEIMX.js.map +0 -7
  62. package/apps/dev-app/node_modules/.vite/deps/react-dom.js +0 -7
  63. package/apps/dev-app/node_modules/.vite/deps/react-dom.js.map +0 -7
  64. package/apps/dev-app/node_modules/.vite/deps/react-dom_client.js +0 -8
  65. package/apps/dev-app/node_modules/.vite/deps/react-dom_client.js.map +0 -7
  66. package/apps/dev-app/node_modules/.vite/deps/react-hook-form.js +0 -2233
  67. package/apps/dev-app/node_modules/.vite/deps/react-hook-form.js.map +0 -7
  68. package/apps/dev-app/node_modules/.vite/deps/react-phone-number-input.js +0 -9307
  69. package/apps/dev-app/node_modules/.vite/deps/react-phone-number-input.js.map +0 -7
  70. package/apps/dev-app/node_modules/.vite/deps/react-router-dom.js +0 -14234
  71. package/apps/dev-app/node_modules/.vite/deps/react-router-dom.js.map +0 -7
  72. package/apps/dev-app/node_modules/.vite/deps/react.js +0 -6
  73. package/apps/dev-app/node_modules/.vite/deps/react.js.map +0 -7
  74. package/apps/dev-app/node_modules/.vite/deps/react_jsx-dev-runtime.js +0 -913
  75. package/apps/dev-app/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +0 -7
  76. package/apps/dev-app/node_modules/.vite/deps/react_jsx-runtime.js +0 -7
  77. package/apps/dev-app/node_modules/.vite/deps/react_jsx-runtime.js.map +0 -7
  78. package/apps/dev-app/node_modules/.vite/deps/tailwind-merge.js +0 -2534
  79. package/apps/dev-app/node_modules/.vite/deps/tailwind-merge.js.map +0 -7
  80. package/apps/dev-app/node_modules/tailwindcss/LICENSE +0 -21
  81. package/apps/dev-app/node_modules/tailwindcss/README.md +0 -36
  82. package/apps/dev-app/node_modules/tailwindcss/dist/chunk-L5IEUH3R.mjs +0 -38
  83. package/apps/dev-app/node_modules/tailwindcss/dist/chunk-UWKE2Z6N.mjs +0 -1
  84. package/apps/dev-app/node_modules/tailwindcss/dist/chunk-X4GG3EDV.mjs +0 -1
  85. package/apps/dev-app/node_modules/tailwindcss/dist/colors-C__qRT83.d.ts +0 -347
  86. package/apps/dev-app/node_modules/tailwindcss/dist/colors.d.mts +0 -347
  87. package/apps/dev-app/node_modules/tailwindcss/dist/colors.d.ts +0 -5
  88. package/apps/dev-app/node_modules/tailwindcss/dist/colors.js +0 -1
  89. package/apps/dev-app/node_modules/tailwindcss/dist/colors.mjs +0 -1
  90. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.d.mts +0 -1199
  91. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.d.ts +0 -1199
  92. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.js +0 -1
  93. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.mjs +0 -1
  94. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.d.mts +0 -6
  95. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.d.ts +0 -6
  96. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.js +0 -3
  97. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.mjs +0 -1
  98. package/apps/dev-app/node_modules/tailwindcss/dist/lib.d.mts +0 -378
  99. package/apps/dev-app/node_modules/tailwindcss/dist/lib.d.ts +0 -3
  100. package/apps/dev-app/node_modules/tailwindcss/dist/lib.js +0 -38
  101. package/apps/dev-app/node_modules/tailwindcss/dist/lib.mjs +0 -1
  102. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.d.mts +0 -11
  103. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.d.ts +0 -134
  104. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.js +0 -1
  105. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.mjs +0 -1
  106. package/apps/dev-app/node_modules/tailwindcss/dist/resolve-config-B4yBzhca.d.ts +0 -29
  107. package/apps/dev-app/node_modules/tailwindcss/dist/resolve-config-QUZ9b-Gn.d.mts +0 -190
  108. package/apps/dev-app/node_modules/tailwindcss/dist/types-CJYAW1ql.d.mts +0 -128
  109. package/apps/dev-app/node_modules/tailwindcss/index.css +0 -944
  110. package/apps/dev-app/node_modules/tailwindcss/package.json +0 -89
  111. package/apps/dev-app/node_modules/tailwindcss/preflight.css +0 -393
  112. package/apps/dev-app/node_modules/tailwindcss/theme.css +0 -510
  113. package/apps/dev-app/node_modules/tailwindcss/utilities.css +0 -1
  114. package/apps/dev-app/package.json +0 -34
  115. package/apps/dev-app/src/App.tsx +0 -74
  116. package/apps/dev-app/src/index.css +0 -18
  117. package/apps/dev-app/src/main.tsx +0 -18
  118. package/apps/dev-app/src/pages/Buttons.tsx +0 -122
  119. package/apps/dev-app/src/pages/DataTable.tsx +0 -208
  120. package/apps/dev-app/src/pages/Fields.tsx +0 -342
  121. package/apps/dev-app/src/pages/Modals.tsx +0 -151
  122. package/apps/dev-app/src/pages/Spins.tsx +0 -161
  123. package/apps/dev-app/ts_errors.txt +0 -0
  124. package/apps/dev-app/tsconfig.json +0 -25
  125. package/apps/dev-app/tsconfig.node.json +0 -10
  126. package/apps/dev-app/vite.config.ts +0 -11
  127. package/packages/ui/dist/index.js +0 -2296
  128. package/packages/ui/dist/index.mjs +0 -2249
  129. package/packages/ui/dist/styles.js +0 -26
  130. package/packages/ui/dist/styles.mjs +0 -24
  131. package/packages/ui/log.txt +0 -0
  132. package/packages/ui/package.json +0 -70
  133. package/packages/ui/postcss.config.js +0 -6
  134. package/packages/ui/src/components/Button.tsx +0 -85
  135. package/packages/ui/src/components/Card.tsx +0 -97
  136. package/packages/ui/src/components/CodeBlock.tsx +0 -53
  137. package/packages/ui/src/components/DashboardLayout.tsx +0 -442
  138. package/packages/ui/src/components/Fields/Input.tsx +0 -191
  139. package/packages/ui/src/components/Fields/PhoneInput.tsx +0 -134
  140. package/packages/ui/src/components/Fields/date.tsx +0 -165
  141. package/packages/ui/src/components/Fields/index.tsx +0 -17
  142. package/packages/ui/src/components/Fields/searchApi.tsx +0 -479
  143. package/packages/ui/src/components/Fields/select.tsx +0 -131
  144. package/packages/ui/src/components/Fields/textArea.tsx +0 -121
  145. package/packages/ui/src/components/LoadingBox.tsx +0 -11
  146. package/packages/ui/src/components/PageHeader.tsx +0 -34
  147. package/packages/ui/src/components/ThemeToggle.tsx +0 -35
  148. package/packages/ui/src/components/modal/Modal.tsx +0 -81
  149. package/packages/ui/src/components/spins/ClassicSpin.tsx +0 -18
  150. package/packages/ui/src/components/spins/LoadingSpin.tsx +0 -45
  151. package/packages/ui/src/components/spins/OverlaySpin.tsx +0 -10
  152. package/packages/ui/src/components/spins/index.tsx +0 -13
  153. package/packages/ui/src/components/tables/DataTable.tsx +0 -261
  154. package/packages/ui/src/components/tables/index.ts +0 -1
  155. package/packages/ui/src/hooks/Fetches/useApis.tsx +0 -197
  156. package/packages/ui/src/hooks/ThemeContext.tsx +0 -56
  157. package/packages/ui/src/hooks/useModal.tsx +0 -38
  158. package/packages/ui/src/hooks/useTheme.ts +0 -34
  159. package/packages/ui/src/index.ts +0 -24
  160. package/packages/ui/src/providers/WarqadProvider.tsx +0 -69
  161. package/packages/ui/src/styles.css +0 -26
  162. package/packages/ui/src/utils/cn.ts +0 -6
  163. package/packages/ui/src/utils/pdf.ts +0 -171
  164. package/packages/ui/tailwind.config.js +0 -13
  165. package/packages/ui/tsconfig.json +0 -17
  166. package/packages/ui/warqad-ui-0.0.1.tgz +0 -0
  167. package/packages/ui/warqadui-0.0.3.tgz +0 -0
  168. package/warqad-ui-0.0.1.tgz +0 -0
  169. /package/{packages/ui/dist → dist}/styles.d.mts +0 -0
  170. /package/{packages/ui/dist → dist}/styles.d.ts +0 -0
@@ -1,479 +0,0 @@
1
- import React, { useState, useEffect, useRef, forwardRef } from "react";
2
- import { Search, Loader2, ChevronDown, Check, X } from "lucide-react";
3
- import useApi from "../../hooks/Fetches/useApis";
4
- import { useWarqadConfig } from "../../providers/WarqadProvider";
5
- import { Controller } from "react-hook-form";
6
-
7
- export interface SearchApiProps extends Omit<
8
- React.InputHTMLAttributes<HTMLInputElement>,
9
- "onChange" | "value"
10
- > {
11
- label?: string;
12
- apiUrl: string;
13
- placeholder?: string;
14
- value?: any;
15
- onChange?: (value: any) => void;
16
- queryKey?: string;
17
- labelKey?: string;
18
- valueKey?: string;
19
- error?: string;
20
- containerClassName?: string;
21
- name?: any;
22
- form?: any;
23
- obj?: any;
24
- filter?: any[];
25
- v?: number;
26
- }
27
-
28
- interface SearchApiContentProps extends SearchApiProps {
29
- currentValue: any;
30
- onChangeHandler: (value: any) => void;
31
- fieldInternalProps?: {
32
- ref?: React.Ref<any>;
33
- onBlur?: () => void;
34
- };
35
- }
36
-
37
- const SearchApiContent = ({
38
- label,
39
- apiUrl,
40
- placeholder = "Search...",
41
- currentValue,
42
- onChangeHandler,
43
- queryKey = "search",
44
- labelKey = "name",
45
- valueKey = "_id",
46
- error,
47
- containerClassName = "",
48
- className = "",
49
- disabled,
50
- name,
51
- obj,
52
- form,
53
- filter = [],
54
- required,
55
- v = 2,
56
- fieldInternalProps,
57
- ...props
58
- }: SearchApiContentProps) => {
59
- const [searchTerm, setSearchTerm] = useState("");
60
- const { theme } = useWarqadConfig();
61
- const primaryColor = theme?.primaryColor;
62
- const [options, setOptions] = useState<any[]>([]);
63
- const [isOpen, setIsOpen] = useState(false);
64
- const [isFocused, setIsFocused] = useState(false);
65
- const [activeIndex, setActiveIndex] = useState(-1);
66
- const [selectedObject, setSelectedObject] = useState<any>(undefined);
67
- const [hasAttemptedHydration, setHasAttemptedHydration] = useState(false);
68
- const containerRef = useRef<HTMLDivElement>(null);
69
- const inputRef = useRef<HTMLInputElement>(null);
70
- const prevApiUrlRef = useRef(apiUrl);
71
-
72
- const setInputRef = (element: HTMLInputElement | null) => {
73
- (inputRef as any).current = element;
74
- if (fieldInternalProps?.ref) {
75
- if (typeof fieldInternalProps.ref === "function") {
76
- fieldInternalProps.ref(element);
77
- } else {
78
- (fieldInternalProps.ref as any).current = element;
79
- }
80
- }
81
- };
82
-
83
- const { get, isLoading, error: errorMessage } = useApi();
84
-
85
- const fetchOptions = async (query: string) => {
86
- try {
87
- const params: Record<string, any> = {};
88
- if (query) params[queryKey] = query;
89
-
90
- const response = await get({
91
- url: apiUrl,
92
- params,
93
- v: v as any,
94
- });
95
-
96
- if (Array.isArray(response)) {
97
- setOptions(response);
98
- } else if (response?.data && Array.isArray(response.data)) {
99
- setOptions(response.data);
100
- } else if (response?.items && Array.isArray(response.items)) {
101
- setOptions(response.items);
102
- } else {
103
- setOptions([]);
104
- }
105
- } catch (err: any) {
106
- console.error("Failed to fetch options:", err);
107
- if (form && name) {
108
- form.setError(name, {
109
- type: "manual",
110
- message: err.message || "Failed to fetch options",
111
- });
112
- }
113
- setOptions([]);
114
- }
115
- };
116
-
117
- useEffect(() => {
118
- const timer = setTimeout(() => {
119
- if (isOpen) {
120
- fetchOptions(searchTerm);
121
- }
122
- }, 300);
123
- return () => clearTimeout(timer);
124
- }, [searchTerm, apiUrl, isOpen]);
125
-
126
- useEffect(() => {
127
- if (isOpen && options.length === 0) {
128
- fetchOptions("");
129
- }
130
- }, [isOpen]);
131
-
132
- useEffect(() => {
133
- if (currentValue && !selectedObject) {
134
- setHasAttemptedHydration(false);
135
- fetchOptions(currentValue).finally(() => {
136
- setHasAttemptedHydration(true);
137
- });
138
- } else if (!currentValue) {
139
- setHasAttemptedHydration(true);
140
- if (selectedObject) setSelectedObject(undefined);
141
- }
142
- }, [currentValue, selectedObject]);
143
-
144
- const getOptionValue = (option: any) => {
145
- if (
146
- valueKey === "_id" &&
147
- option._id === undefined &&
148
- option.id !== undefined
149
- ) {
150
- return option.id;
151
- }
152
- return option[valueKey];
153
- };
154
-
155
- useEffect(() => {
156
- if (currentValue && !selectedObject && options.length > 0) {
157
- const found = options.find((o) => getOptionValue(o) === currentValue);
158
- if (found) {
159
- setSelectedObject(found);
160
- if (form && obj) {
161
- form.setValue(obj, found);
162
- }
163
- }
164
- }
165
- }, [currentValue, options, selectedObject, form, obj]);
166
-
167
- useEffect(() => {
168
- if (prevApiUrlRef.current !== apiUrl) {
169
- prevApiUrlRef.current = apiUrl;
170
- setSearchTerm("");
171
- setOptions([]);
172
- setSelectedObject(undefined);
173
-
174
- if (form) {
175
- if (name) {
176
- form.setValue(name, undefined, {
177
- shouldValidate: true,
178
- shouldDirty: true,
179
- });
180
- }
181
- if (obj) {
182
- form.setValue(obj, undefined, {
183
- shouldValidate: true,
184
- shouldDirty: true,
185
- });
186
- }
187
- } else {
188
- onChangeHandler(undefined);
189
- }
190
- fetchOptions("");
191
- }
192
- }, [apiUrl, form, name, obj, onChangeHandler]);
193
-
194
- useEffect(() => {
195
- const handleClickOutside = (event: MouseEvent) => {
196
- if (
197
- containerRef.current &&
198
- !containerRef.current.contains(event.target as Node)
199
- ) {
200
- setIsOpen(false);
201
- setIsFocused(false);
202
- setSearchTerm("");
203
- fieldInternalProps?.onBlur?.();
204
- }
205
- };
206
- document.addEventListener("mousedown", handleClickOutside);
207
- return () => document.removeEventListener("mousedown", handleClickOutside);
208
- }, [fieldInternalProps]);
209
-
210
- const getDisplayValue = () => {
211
- if (selectedObject) return selectedObject[labelKey];
212
- if (options.length > 0 && currentValue) {
213
- const found = options.find((o) => getOptionValue(o) === currentValue);
214
- if (found) return found[labelKey];
215
- }
216
- if (isLoading || (currentValue && !hasAttemptedHydration))
217
- return "Loading...";
218
- return currentValue || "";
219
- };
220
-
221
- const handleSelect = (option: any) => {
222
- const value = getOptionValue(option);
223
- setSelectedObject(option);
224
- onChangeHandler(value);
225
- if (form) {
226
- if (obj) form.setValue(obj, option);
227
- if (name) form.clearErrors(name);
228
- }
229
- setIsOpen(false);
230
- setSearchTerm("");
231
- setActiveIndex(-1);
232
- fieldInternalProps?.onBlur?.();
233
- };
234
-
235
- const handleKeyDown = (e: React.KeyboardEvent) => {
236
- if (!isOpen) {
237
- if (e.key === "ArrowDown" || e.key === "Enter") setIsOpen(true);
238
- return;
239
- }
240
- switch (e.key) {
241
- case "ArrowDown":
242
- e.preventDefault();
243
- setActiveIndex((prev) => (prev < options.length - 1 ? prev + 1 : prev));
244
- break;
245
- case "ArrowUp":
246
- e.preventDefault();
247
- setActiveIndex((prev) => (prev > 0 ? prev - 1 : prev));
248
- break;
249
- case "Enter":
250
- e.preventDefault();
251
- if (activeIndex >= 0 && activeIndex < options.length)
252
- handleSelect(options[activeIndex]);
253
- break;
254
- case "Escape":
255
- setIsOpen(false);
256
- break;
257
- }
258
- };
259
-
260
- const clearSelection = (e: React.MouseEvent) => {
261
- e.stopPropagation();
262
- if (form) {
263
- if (name)
264
- form.setValue(name, undefined, {
265
- shouldValidate: true,
266
- shouldDirty: true,
267
- });
268
- if (obj)
269
- form.setValue(obj, undefined, {
270
- shouldValidate: true,
271
- shouldDirty: true,
272
- });
273
- } else {
274
- onChangeHandler(undefined);
275
- }
276
- setSearchTerm("");
277
- };
278
-
279
- const filteredOptions = options.filter(
280
- (option) => !filter.includes(getOptionValue(option)),
281
- );
282
-
283
- useEffect(() => {
284
- if (form && name && !form.watch(name)) {
285
- form.setValue(name, undefined);
286
- if (obj) form.setValue(obj, undefined);
287
- }
288
- }, [form, name, obj]);
289
-
290
- let message = error;
291
- if (form && name) {
292
- const {
293
- formState: { errors },
294
- } = form;
295
- const nameParts = name.split(".");
296
- let currentError = errors;
297
- for (const part of nameParts) {
298
- currentError = currentError?.[part];
299
- }
300
- message = currentError?.message || message;
301
- }
302
-
303
- return (
304
- <div ref={containerRef} className={`space-y-2 group ${containerClassName}`}>
305
- {label && (
306
- <label
307
- className={`block text-xs font-medium transition-colors duration-200`}
308
- style={{ color: isFocused || isOpen ? primaryColor : undefined }}
309
- >
310
- {label}
311
- {required && <span className="text-red-500 ml-1">*</span>}
312
- </label>
313
- )}
314
-
315
- <div className="relative">
316
- <div
317
- className={`flex items-center w-full h-8 rounded-lg border bg-gray-50 dark:bg-zinc-900/50 text-gray-900 dark:text-white transition-all duration-200 cursor-text ${
318
- isFocused || isOpen
319
- ? "ring-2 bg-white dark:bg-zinc-900"
320
- : "border-gray-200 dark:border-zinc-800"
321
- } ${className}`}
322
- style={{
323
- borderColor: isFocused || isOpen ? primaryColor : undefined,
324
- boxShadow:
325
- isFocused || isOpen
326
- ? `${primaryColor}33 0px 0px 0px 2px`
327
- : undefined,
328
- }}
329
- onClick={() => {
330
- if (disabled) return;
331
- inputRef.current?.focus();
332
- setIsOpen(true);
333
- }}
334
- >
335
- <div className="pl-3 text-gray-400">
336
- <Search className="h-4 w-4" />
337
- </div>
338
- <div className="flex-1 relative">
339
- {!isOpen && currentValue && (
340
- <div className="absolute inset-0 flex items-center px-3 truncate select-none">
341
- {getDisplayValue()}
342
- </div>
343
- )}
344
- <input
345
- ref={setInputRef}
346
- type="text"
347
- className={`block w-full bg-transparent border-none focus:ring-0 h-8 py-0 pr-8 placeholder-gray-400 focus:outline-none text-sm ${
348
- !isOpen && currentValue ? "opacity-0" : "opacity-100"
349
- }`}
350
- placeholder={!currentValue ? placeholder : ""}
351
- value={searchTerm}
352
- onChange={(e) => setSearchTerm(e.target.value)}
353
- onFocus={() => {
354
- setIsFocused(true);
355
- setIsOpen(true);
356
- }}
357
- onKeyDown={handleKeyDown}
358
- disabled={disabled}
359
- {...props}
360
- />
361
- </div>
362
- <div className="pr-3 flex items-center gap-1">
363
- {isLoading ? (
364
- <Loader2 className="h-4 w-4 animate-spin text-blue-500" />
365
- ) : (
366
- <>
367
- {currentValue && !disabled && (
368
- <button
369
- onClick={clearSelection}
370
- type="button"
371
- className="p-0.5 hover:bg-gray-200 dark:hover:bg-zinc-700 rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
372
- >
373
- <X className="h-3 w-3" />
374
- </button>
375
- )}
376
- <ChevronDown
377
- className={`h-4 w-4 text-gray-400 transition-transform ${isOpen ? "rotate-180" : ""}`}
378
- />
379
- </>
380
- )}
381
- </div>
382
- </div>
383
-
384
- {isOpen && (
385
- <div className="absolute z-50 w-full mt-1 bg-white dark:bg-zinc-900 rounded-xl border border-gray-200 dark:border-zinc-800 shadow-lg max-h-60 overflow-y-auto overflow-x-hidden">
386
- {isLoading && options.length === 0 ? (
387
- <div className="p-4 text-center text-sm text-gray-500">
388
- <Loader2 className="h-5 w-5 animate-spin mx-auto mb-2" />
389
- Loading options...
390
- </div>
391
- ) : filteredOptions.length > 0 ? (
392
- <div className="py-1">
393
- {filteredOptions.map((option, index) => {
394
- const optValue = getOptionValue(option);
395
- const isSelected = currentValue === optValue;
396
- const isHighlighted = index === activeIndex;
397
- return (
398
- <button
399
- key={optValue || index}
400
- type="button"
401
- className={`w-full text-left px-3 py-2 text-sm flex items-center justify-between transition-colors ${
402
- isSelected
403
- ? "font-medium"
404
- : isHighlighted
405
- ? "bg-gray-100 dark:bg-zinc-800 text-gray-900 dark:text-gray-100"
406
- : "text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-zinc-800"
407
- }`}
408
- style={{
409
- backgroundColor: isSelected
410
- ? `${primaryColor}1A`
411
- : undefined,
412
- color: isSelected ? primaryColor : undefined,
413
- }}
414
- onClick={() => handleSelect(option)}
415
- onMouseEnter={() => setActiveIndex(index)}
416
- >
417
- <span className="truncate">{option[labelKey]}</span>
418
- {isSelected && (
419
- <Check
420
- className="h-4 w-4"
421
- style={{ color: primaryColor }}
422
- />
423
- )}
424
- </button>
425
- );
426
- })}
427
- </div>
428
- ) : (
429
- <div className="p-4 text-center text-sm text-gray-500">
430
- {errorMessage ||
431
- (searchTerm
432
- ? `No results for "${searchTerm}"`
433
- : "No options available")}
434
- </div>
435
- )}
436
- </div>
437
- )}
438
- </div>
439
- {message && (
440
- <p className="text-sm text-red-600 dark:text-red-400">{message}</p>
441
- )}
442
- </div>
443
- );
444
- };
445
-
446
- export const SearchApi = forwardRef<HTMLDivElement, SearchApiProps>(
447
- (props, _) => {
448
- const { form, name, onChange, value } = props;
449
-
450
- if (form && name) {
451
- return (
452
- <Controller
453
- name={name}
454
- control={form.control}
455
- render={({
456
- field: { onChange: formOnChange, value: formValue, ref, onBlur },
457
- }) => (
458
- <SearchApiContent
459
- {...props}
460
- currentValue={formValue}
461
- onChangeHandler={formOnChange}
462
- fieldInternalProps={{ ref, onBlur }}
463
- />
464
- )}
465
- />
466
- );
467
- }
468
-
469
- return (
470
- <SearchApiContent
471
- {...props}
472
- currentValue={value}
473
- onChangeHandler={onChange || (() => {})}
474
- />
475
- );
476
- },
477
- );
478
-
479
- SearchApi.displayName = "SearchApi";
@@ -1,131 +0,0 @@
1
- import { forwardRef, useState } from "react";
2
- import { ChevronDown } from "lucide-react";
3
- import { useWarqadConfig } from "../../providers/WarqadProvider";
4
-
5
- export interface Option {
6
- value: string | number | boolean;
7
- label: string;
8
- disabled?: boolean;
9
- }
10
-
11
- export interface SelectProps extends Omit<
12
- React.SelectHTMLAttributes<HTMLSelectElement>,
13
- "onChange"
14
- > {
15
- label: string;
16
- options: Option[];
17
- icon?: React.ReactNode;
18
- error?: string;
19
- containerClassName?: string;
20
- name?: any;
21
- form?: any;
22
- onChange?: (value: string | number | boolean) => void;
23
- }
24
-
25
- export const Select = forwardRef<HTMLSelectElement, SelectProps>(
26
- (
27
- {
28
- label,
29
- options,
30
- icon,
31
- error,
32
- containerClassName = "",
33
- name,
34
- form,
35
- onChange,
36
- onFocus,
37
- onBlur,
38
- className = "",
39
- ...props
40
- },
41
- ref,
42
- ) => {
43
- const [isFocused, setIsFocused] = useState(false);
44
- const { theme } = useWarqadConfig();
45
- const primaryColor = theme?.primaryColor;
46
-
47
- let message = error;
48
- if (form) {
49
- const {
50
- formState: { errors },
51
- } = form;
52
- const nameI = name.split(".");
53
- const names: string = nameI.length > 1 ? nameI[1] : nameI[0];
54
- message = errors?.[names ? names : name]?.message;
55
- }
56
-
57
- return (
58
- <div className={`space-y-2 group ${containerClassName}`}>
59
- <label
60
- htmlFor={props.id}
61
- className={`block text-xs font-medium transition-colors duration-200`}
62
- style={{ color: isFocused ? primaryColor : undefined }}
63
- >
64
- {label}
65
- {props.required && <span className="text-red-500 ml-1">*</span>}
66
- </label>
67
- <div className="relative">
68
- {icon && (
69
- <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
70
- <div
71
- className={`transition-colors duration-200`}
72
- style={{ color: isFocused ? primaryColor : "#9ca3af" }}
73
- >
74
- {icon}
75
- </div>
76
- </div>
77
- )}
78
- <select
79
- ref={ref}
80
- onFocus={(e) => {
81
- setIsFocused(true);
82
- onFocus?.(e);
83
- }}
84
- {...(form ? form.register(name as any) : {})}
85
- onBlur={(e) => {
86
- setIsFocused(false);
87
- onBlur?.(e);
88
- }}
89
- onChange={(e) => {
90
- onChange?.(e.target.value);
91
- if (form) {
92
- form.setValue(name, e.target.value);
93
- }
94
- }}
95
- className={`block w-full ${
96
- icon ? "pl-10" : "pl-3"
97
- } pr-10 h-8 py-0 rounded-lg border-gray-200 dark:border-zinc-800 bg-gray-50 dark:bg-zinc-900/50 text-gray-900 dark:text-white placeholder-gray-400 focus:bg-white dark:focus:bg-zinc-900 outline-none transition-all duration-200 border appearance-none text-sm ${className}`}
98
- style={{
99
- borderColor: isFocused ? primaryColor : undefined,
100
- boxShadow: isFocused
101
- ? `${primaryColor}33 0px 0px 0px 2px`
102
- : undefined,
103
- }}
104
- {...props}
105
- >
106
- <option value="" disabled>
107
- Select {label}
108
- </option>
109
- {options.map((option) => (
110
- <option
111
- key={String(option.value)}
112
- value={String(option.value)}
113
- disabled={option.disabled}
114
- >
115
- {option.label}
116
- </option>
117
- ))}
118
- </select>
119
- <div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
120
- <ChevronDown className="h-4 w-4 text-gray-400" />
121
- </div>
122
- </div>
123
- {message && (
124
- <p className="text-sm text-red-600 dark:text-red-400">{message}</p>
125
- )}
126
- </div>
127
- );
128
- },
129
- );
130
-
131
- Select.displayName = "Select";