warqadui 0.0.6 → 0.0.10

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 (173) hide show
  1. package/package.json +62 -10
  2. package/.vscode/settings.json +0 -3
  3. package/apps/dev-app/.env +0 -1
  4. package/apps/dev-app/errors.log +0 -0
  5. package/apps/dev-app/index.html +0 -12
  6. package/apps/dev-app/node_modules/.vite/deps/@tanstack_react-table.js +0 -3254
  7. package/apps/dev-app/node_modules/.vite/deps/@tanstack_react-table.js.map +0 -7
  8. package/apps/dev-app/node_modules/.vite/deps/_metadata.json +0 -178
  9. package/apps/dev-app/node_modules/.vite/deps/antd.js +0 -108982
  10. package/apps/dev-app/node_modules/.vite/deps/antd.js.map +0 -7
  11. package/apps/dev-app/node_modules/.vite/deps/axios.js +0 -2751
  12. package/apps/dev-app/node_modules/.vite/deps/axios.js.map +0 -7
  13. package/apps/dev-app/node_modules/.vite/deps/chunk-5OG7DCD7.js +0 -41
  14. package/apps/dev-app/node_modules/.vite/deps/chunk-5OG7DCD7.js.map +0 -7
  15. package/apps/dev-app/node_modules/.vite/deps/chunk-7YRZYZRE.js +0 -7807
  16. package/apps/dev-app/node_modules/.vite/deps/chunk-7YRZYZRE.js.map +0 -7
  17. package/apps/dev-app/node_modules/.vite/deps/chunk-DC5AMYBS.js +0 -39
  18. package/apps/dev-app/node_modules/.vite/deps/chunk-DC5AMYBS.js.map +0 -7
  19. package/apps/dev-app/node_modules/.vite/deps/chunk-DKXRQMOD.js +0 -135
  20. package/apps/dev-app/node_modules/.vite/deps/chunk-DKXRQMOD.js.map +0 -7
  21. package/apps/dev-app/node_modules/.vite/deps/chunk-EL47BWQR.js +0 -37
  22. package/apps/dev-app/node_modules/.vite/deps/chunk-EL47BWQR.js.map +0 -7
  23. package/apps/dev-app/node_modules/.vite/deps/chunk-HHL3MHGV.js +0 -288
  24. package/apps/dev-app/node_modules/.vite/deps/chunk-HHL3MHGV.js.map +0 -7
  25. package/apps/dev-app/node_modules/.vite/deps/chunk-IGGUWUPT.js +0 -60
  26. package/apps/dev-app/node_modules/.vite/deps/chunk-IGGUWUPT.js.map +0 -7
  27. package/apps/dev-app/node_modules/.vite/deps/chunk-IGXZPJXT.js +0 -928
  28. package/apps/dev-app/node_modules/.vite/deps/chunk-IGXZPJXT.js.map +0 -7
  29. package/apps/dev-app/node_modules/.vite/deps/chunk-L2GCM37S.js +0 -21628
  30. package/apps/dev-app/node_modules/.vite/deps/chunk-L2GCM37S.js.map +0 -7
  31. package/apps/dev-app/node_modules/.vite/deps/chunk-LDRT62EN.js +0 -14806
  32. package/apps/dev-app/node_modules/.vite/deps/chunk-LDRT62EN.js.map +0 -7
  33. package/apps/dev-app/node_modules/.vite/deps/chunk-M7DZDBHW.js +0 -14
  34. package/apps/dev-app/node_modules/.vite/deps/chunk-M7DZDBHW.js.map +0 -7
  35. package/apps/dev-app/node_modules/.vite/deps/chunk-S54SBVCU.js +0 -1906
  36. package/apps/dev-app/node_modules/.vite/deps/chunk-S54SBVCU.js.map +0 -7
  37. package/apps/dev-app/node_modules/.vite/deps/chunk-WFNHCR67.js +0 -21
  38. package/apps/dev-app/node_modules/.vite/deps/chunk-WFNHCR67.js.map +0 -7
  39. package/apps/dev-app/node_modules/.vite/deps/clsx.js +0 -10
  40. package/apps/dev-app/node_modules/.vite/deps/clsx.js.map +0 -7
  41. package/apps/dev-app/node_modules/.vite/deps/dayjs.js +0 -6
  42. package/apps/dev-app/node_modules/.vite/deps/dayjs.js.map +0 -7
  43. package/apps/dev-app/node_modules/.vite/deps/dayjs_plugin_customParseFormat.js +0 -6
  44. package/apps/dev-app/node_modules/.vite/deps/dayjs_plugin_customParseFormat.js.map +0 -7
  45. package/apps/dev-app/node_modules/.vite/deps/framer-motion.js +0 -12388
  46. package/apps/dev-app/node_modules/.vite/deps/framer-motion.js.map +0 -7
  47. package/apps/dev-app/node_modules/.vite/deps/html2canvas-pro.js +0 -9713
  48. package/apps/dev-app/node_modules/.vite/deps/html2canvas-pro.js.map +0 -7
  49. package/apps/dev-app/node_modules/.vite/deps/html2canvas.esm-VL7GM4AH.js +0 -8
  50. package/apps/dev-app/node_modules/.vite/deps/html2canvas.esm-VL7GM4AH.js.map +0 -7
  51. package/apps/dev-app/node_modules/.vite/deps/index.es-3WTXOFZ2.js +0 -10392
  52. package/apps/dev-app/node_modules/.vite/deps/index.es-3WTXOFZ2.js.map +0 -7
  53. package/apps/dev-app/node_modules/.vite/deps/jspdf.js +0 -41
  54. package/apps/dev-app/node_modules/.vite/deps/jspdf.js.map +0 -7
  55. package/apps/dev-app/node_modules/.vite/deps/lucide-react.js +0 -31586
  56. package/apps/dev-app/node_modules/.vite/deps/lucide-react.js.map +0 -7
  57. package/apps/dev-app/node_modules/.vite/deps/package.json +0 -3
  58. package/apps/dev-app/node_modules/.vite/deps/purify.es-JNLDEIMX.js +0 -1029
  59. package/apps/dev-app/node_modules/.vite/deps/purify.es-JNLDEIMX.js.map +0 -7
  60. package/apps/dev-app/node_modules/.vite/deps/react-dom.js +0 -7
  61. package/apps/dev-app/node_modules/.vite/deps/react-dom.js.map +0 -7
  62. package/apps/dev-app/node_modules/.vite/deps/react-dom_client.js +0 -8
  63. package/apps/dev-app/node_modules/.vite/deps/react-dom_client.js.map +0 -7
  64. package/apps/dev-app/node_modules/.vite/deps/react-hook-form.js +0 -2233
  65. package/apps/dev-app/node_modules/.vite/deps/react-hook-form.js.map +0 -7
  66. package/apps/dev-app/node_modules/.vite/deps/react-phone-number-input.js +0 -9307
  67. package/apps/dev-app/node_modules/.vite/deps/react-phone-number-input.js.map +0 -7
  68. package/apps/dev-app/node_modules/.vite/deps/react-router-dom.js +0 -14234
  69. package/apps/dev-app/node_modules/.vite/deps/react-router-dom.js.map +0 -7
  70. package/apps/dev-app/node_modules/.vite/deps/react-to-pdf.js +0 -268
  71. package/apps/dev-app/node_modules/.vite/deps/react-to-pdf.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 -35
  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.css +0 -3
  128. package/packages/ui/log.txt +0 -0
  129. package/packages/ui/package.json +0 -68
  130. package/packages/ui/postcss.config.js +0 -6
  131. package/packages/ui/src/components/Button.tsx +0 -85
  132. package/packages/ui/src/components/Card.tsx +0 -97
  133. package/packages/ui/src/components/CodeBlock.tsx +0 -53
  134. package/packages/ui/src/components/DashboardLayout.tsx +0 -442
  135. package/packages/ui/src/components/Fields/Input.tsx +0 -191
  136. package/packages/ui/src/components/Fields/PhoneInput.tsx +0 -134
  137. package/packages/ui/src/components/Fields/date.tsx +0 -165
  138. package/packages/ui/src/components/Fields/index.tsx +0 -17
  139. package/packages/ui/src/components/Fields/searchApi.tsx +0 -479
  140. package/packages/ui/src/components/Fields/select.tsx +0 -131
  141. package/packages/ui/src/components/Fields/textArea.tsx +0 -121
  142. package/packages/ui/src/components/LoadingBox.tsx +0 -11
  143. package/packages/ui/src/components/PageHeader.tsx +0 -34
  144. package/packages/ui/src/components/ThemeToggle.tsx +0 -35
  145. package/packages/ui/src/components/modal/Modal.tsx +0 -81
  146. package/packages/ui/src/components/spins/ClassicSpin.tsx +0 -18
  147. package/packages/ui/src/components/spins/LoadingSpin.tsx +0 -45
  148. package/packages/ui/src/components/spins/OverlaySpin.tsx +0 -10
  149. package/packages/ui/src/components/spins/index.tsx +0 -13
  150. package/packages/ui/src/components/tables/DataTable.tsx +0 -261
  151. package/packages/ui/src/components/tables/index.ts +0 -1
  152. package/packages/ui/src/hooks/Fetches/useApis.tsx +0 -197
  153. package/packages/ui/src/hooks/ThemeContext.tsx +0 -56
  154. package/packages/ui/src/hooks/useModal.tsx +0 -38
  155. package/packages/ui/src/hooks/useTheme.ts +0 -34
  156. package/packages/ui/src/index.ts +0 -24
  157. package/packages/ui/src/providers/WarqadProvider.tsx +0 -69
  158. package/packages/ui/src/styles.css +0 -26
  159. package/packages/ui/src/utils/cn.ts +0 -6
  160. package/packages/ui/src/utils/pdf.ts +0 -171
  161. package/packages/ui/tailwind.config.js +0 -13
  162. package/packages/ui/tsconfig.json +0 -17
  163. package/packages/ui/warqad-ui-0.0.1.tgz +0 -0
  164. package/packages/ui/warqadui-0.0.3.tgz +0 -0
  165. package/warqad-ui-0.0.1.tgz +0 -0
  166. /package/{packages/ui/dist → dist}/index.d.mts +0 -0
  167. /package/{packages/ui/dist → dist}/index.d.ts +0 -0
  168. /package/{packages/ui/dist → dist}/index.js +0 -0
  169. /package/{packages/ui/dist → dist}/index.mjs +0 -0
  170. /package/{packages/ui/dist → dist}/styles.d.mts +0 -0
  171. /package/{packages/ui/dist → dist}/styles.d.ts +0 -0
  172. /package/{packages/ui/dist → dist}/styles.js +0 -0
  173. /package/{packages/ui/dist → dist}/styles.mjs +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";