ui-svelte 0.1.0

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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/dist/charts/ArcChart.svelte +320 -0
  4. package/dist/charts/ArcChart.svelte.d.ts +26 -0
  5. package/dist/charts/AreaChart.svelte +495 -0
  6. package/dist/charts/AreaChart.svelte.d.ts +32 -0
  7. package/dist/charts/BarChart.svelte +504 -0
  8. package/dist/charts/BarChart.svelte.d.ts +38 -0
  9. package/dist/charts/Candlestick.svelte +527 -0
  10. package/dist/charts/Candlestick.svelte.d.ts +38 -0
  11. package/dist/charts/LineChart.svelte +365 -0
  12. package/dist/charts/LineChart.svelte.d.ts +36 -0
  13. package/dist/charts/PieChart.svelte +311 -0
  14. package/dist/charts/PieChart.svelte.d.ts +28 -0
  15. package/dist/charts/css/arc-chart.css +237 -0
  16. package/dist/charts/css/area-chart.css +289 -0
  17. package/dist/charts/css/bar-chart.css +167 -0
  18. package/dist/charts/css/candlestick.css +197 -0
  19. package/dist/charts/css/line-chart.css +202 -0
  20. package/dist/charts/css/pie-chart.css +199 -0
  21. package/dist/control/Audio.svelte +212 -0
  22. package/dist/control/Audio.svelte.d.ts +8 -0
  23. package/dist/control/Button.svelte +116 -0
  24. package/dist/control/Button.svelte.d.ts +22 -0
  25. package/dist/control/IconButton.svelte +104 -0
  26. package/dist/control/IconButton.svelte.d.ts +17 -0
  27. package/dist/control/Record.svelte +430 -0
  28. package/dist/control/Record.svelte.d.ts +11 -0
  29. package/dist/control/ToggleTheme.svelte +21 -0
  30. package/dist/control/ToggleTheme.svelte.d.ts +8 -0
  31. package/dist/control/Video.svelte +222 -0
  32. package/dist/control/Video.svelte.d.ts +10 -0
  33. package/dist/control/css/btn.css +206 -0
  34. package/dist/control/css/media.css +78 -0
  35. package/dist/control/css/video.css +58 -0
  36. package/dist/css/animations.css +27 -0
  37. package/dist/css/base.css +192 -0
  38. package/dist/css/utilities.css +136 -0
  39. package/dist/display/Accordion.svelte +98 -0
  40. package/dist/display/Accordion.svelte.d.ts +20 -0
  41. package/dist/display/Alert.svelte +65 -0
  42. package/dist/display/Alert.svelte.d.ts +15 -0
  43. package/dist/display/Avatar.svelte +80 -0
  44. package/dist/display/Avatar.svelte.d.ts +13 -0
  45. package/dist/display/Badge.svelte +46 -0
  46. package/dist/display/Badge.svelte.d.ts +11 -0
  47. package/dist/display/Card.svelte +94 -0
  48. package/dist/display/Card.svelte.d.ts +21 -0
  49. package/dist/display/Carousel.svelte +359 -0
  50. package/dist/display/Carousel.svelte.d.ts +25 -0
  51. package/dist/display/ChatBox.svelte +249 -0
  52. package/dist/display/ChatBox.svelte.d.ts +18 -0
  53. package/dist/display/Chip.svelte +67 -0
  54. package/dist/display/Chip.svelte.d.ts +17 -0
  55. package/dist/display/Code.svelte +56 -0
  56. package/dist/display/Code.svelte.d.ts +9 -0
  57. package/dist/display/Collapsible.svelte +71 -0
  58. package/dist/display/Collapsible.svelte.d.ts +15 -0
  59. package/dist/display/Divider.svelte +32 -0
  60. package/dist/display/Divider.svelte.d.ts +10 -0
  61. package/dist/display/Empty.svelte +462 -0
  62. package/dist/display/Empty.svelte.d.ts +11 -0
  63. package/dist/display/Icon.svelte +20 -0
  64. package/dist/display/Icon.svelte.d.ts +11 -0
  65. package/dist/display/Item.svelte +119 -0
  66. package/dist/display/Item.svelte.d.ts +24 -0
  67. package/dist/display/Loading.svelte +8 -0
  68. package/dist/display/Loading.svelte.d.ts +26 -0
  69. package/dist/display/Marquee.svelte +164 -0
  70. package/dist/display/Marquee.svelte.d.ts +21 -0
  71. package/dist/display/Section.svelte +63 -0
  72. package/dist/display/Section.svelte.d.ts +16 -0
  73. package/dist/display/Table.svelte +407 -0
  74. package/dist/display/Table.svelte.d.ts +32 -0
  75. package/dist/display/TypeWriter.svelte +23 -0
  76. package/dist/display/TypeWriter.svelte.d.ts +11 -0
  77. package/dist/display/User.svelte +0 -0
  78. package/dist/display/User.svelte.d.ts +26 -0
  79. package/dist/display/css/accordion.css +98 -0
  80. package/dist/display/css/alert.css +51 -0
  81. package/dist/display/css/avatar.css +158 -0
  82. package/dist/display/css/badge.css +47 -0
  83. package/dist/display/css/card.css +231 -0
  84. package/dist/display/css/carousel.css +156 -0
  85. package/dist/display/css/chat-box.css +188 -0
  86. package/dist/display/css/chip.css +91 -0
  87. package/dist/display/css/code.css +19 -0
  88. package/dist/display/css/collapsible.css +86 -0
  89. package/dist/display/css/divider.css +54 -0
  90. package/dist/display/css/empty.css +8 -0
  91. package/dist/display/css/item.css +149 -0
  92. package/dist/display/css/listbox.css +24 -0
  93. package/dist/display/css/marquee.css +138 -0
  94. package/dist/display/css/section.css +85 -0
  95. package/dist/display/css/table.css +361 -0
  96. package/dist/form/Checkbox.svelte +45 -0
  97. package/dist/form/Checkbox.svelte.d.ts +13 -0
  98. package/dist/form/ComboBox.svelte +448 -0
  99. package/dist/form/ComboBox.svelte.d.ts +29 -0
  100. package/dist/form/CsvField.svelte +389 -0
  101. package/dist/form/CsvField.svelte.d.ts +21 -0
  102. package/dist/form/DateField.svelte +292 -0
  103. package/dist/form/DateField.svelte.d.ts +18 -0
  104. package/dist/form/Dropzone.svelte +196 -0
  105. package/dist/form/Dropzone.svelte.d.ts +30 -0
  106. package/dist/form/ImageCropper.svelte +254 -0
  107. package/dist/form/ImageCropper.svelte.d.ts +14 -0
  108. package/dist/form/PasswordField.svelte +170 -0
  109. package/dist/form/PasswordField.svelte.d.ts +28 -0
  110. package/dist/form/PhoneField.svelte +485 -0
  111. package/dist/form/PhoneField.svelte.d.ts +25 -0
  112. package/dist/form/PinField.svelte +139 -0
  113. package/dist/form/PinField.svelte.d.ts +17 -0
  114. package/dist/form/RadioGroup.svelte +70 -0
  115. package/dist/form/RadioGroup.svelte.d.ts +19 -0
  116. package/dist/form/Select.svelte +350 -0
  117. package/dist/form/Select.svelte.d.ts +26 -0
  118. package/dist/form/Slider.svelte +60 -0
  119. package/dist/form/Slider.svelte.d.ts +15 -0
  120. package/dist/form/TextField.svelte +154 -0
  121. package/dist/form/TextField.svelte.d.ts +31 -0
  122. package/dist/form/Textarea.svelte +137 -0
  123. package/dist/form/Textarea.svelte.d.ts +27 -0
  124. package/dist/form/Toggle.svelte +45 -0
  125. package/dist/form/Toggle.svelte.d.ts +13 -0
  126. package/dist/form/css/checkbox.css +46 -0
  127. package/dist/form/css/combo-box.css +69 -0
  128. package/dist/form/css/control.css +177 -0
  129. package/dist/form/css/csv-field.css +0 -0
  130. package/dist/form/css/date.css +56 -0
  131. package/dist/form/css/dropzone.css +133 -0
  132. package/dist/form/css/field.css +17 -0
  133. package/dist/form/css/image-cropper.css +155 -0
  134. package/dist/form/css/password.css +35 -0
  135. package/dist/form/css/radio-group.css +57 -0
  136. package/dist/form/css/select.css +18 -0
  137. package/dist/form/css/slider.css +80 -0
  138. package/dist/form/css/textarea.css +130 -0
  139. package/dist/form/css/toggle.css +27 -0
  140. package/dist/form/js/countries.d.ts +13 -0
  141. package/dist/form/js/countries.js +307 -0
  142. package/dist/form/js/phone-examples.d.ts +248 -0
  143. package/dist/form/js/phone-examples.js +247 -0
  144. package/dist/hooks/use-auth.svelte.d.ts +11 -0
  145. package/dist/hooks/use-auth.svelte.js +59 -0
  146. package/dist/hooks/use-chat.svelte.d.ts +40 -0
  147. package/dist/hooks/use-chat.svelte.js +265 -0
  148. package/dist/hooks/use-clipboard.svelte.d.ts +9 -0
  149. package/dist/hooks/use-clipboard.svelte.js +52 -0
  150. package/dist/hooks/use-fetch.svelte.d.ts +11 -0
  151. package/dist/hooks/use-fetch.svelte.js +38 -0
  152. package/dist/hooks/use-form.svelte.d.ts +31 -0
  153. package/dist/hooks/use-form.svelte.js +110 -0
  154. package/dist/hooks/use-localstorage.svelte.d.ts +3 -0
  155. package/dist/hooks/use-localstorage.svelte.js +26 -0
  156. package/dist/hooks/use-scroll.svelte.d.ts +6 -0
  157. package/dist/hooks/use-scroll.svelte.js +34 -0
  158. package/dist/hooks/use-search.svelte.d.ts +49 -0
  159. package/dist/hooks/use-search.svelte.js +229 -0
  160. package/dist/hooks/use-table.svelte.d.ts +85 -0
  161. package/dist/hooks/use-table.svelte.js +362 -0
  162. package/dist/hooks/use-websocket.svelte.d.ts +18 -0
  163. package/dist/hooks/use-websocket.svelte.js +79 -0
  164. package/dist/icons/index.d.ts +132 -0
  165. package/dist/icons/index.js +132 -0
  166. package/dist/index.css +115 -0
  167. package/dist/index.d.ts +76 -0
  168. package/dist/index.js +76 -0
  169. package/dist/layout/AppBar.svelte +94 -0
  170. package/dist/layout/AppBar.svelte.d.ts +17 -0
  171. package/dist/layout/Footer.svelte +94 -0
  172. package/dist/layout/Footer.svelte.d.ts +17 -0
  173. package/dist/layout/FooterLinks.svelte +28 -0
  174. package/dist/layout/FooterLinks.svelte.d.ts +11 -0
  175. package/dist/layout/Provider.svelte +52 -0
  176. package/dist/layout/Provider.svelte.d.ts +10 -0
  177. package/dist/layout/Scaffold.svelte +46 -0
  178. package/dist/layout/Scaffold.svelte.d.ts +15 -0
  179. package/dist/layout/Sidebar.svelte +40 -0
  180. package/dist/layout/Sidebar.svelte.d.ts +13 -0
  181. package/dist/layout/css/app-bar.css +35 -0
  182. package/dist/layout/css/bottom-bar.css +12 -0
  183. package/dist/layout/css/footer-links.css +17 -0
  184. package/dist/layout/css/footer.css +35 -0
  185. package/dist/layout/css/scaffold.css +15 -0
  186. package/dist/layout/css/sidebar.css +17 -0
  187. package/dist/navigation/BottomNav.svelte +0 -0
  188. package/dist/navigation/BottomNav.svelte.d.ts +26 -0
  189. package/dist/navigation/NavMenu.svelte +254 -0
  190. package/dist/navigation/SideNav.svelte +249 -0
  191. package/dist/navigation/Tabs.svelte +79 -0
  192. package/dist/navigation/Tabs.svelte.d.ts +19 -0
  193. package/dist/navigation/css/bottom-nav.css +0 -0
  194. package/dist/navigation/css/nav-menu.css +168 -0
  195. package/dist/navigation/css/side-nav.css +244 -0
  196. package/dist/navigation/css/tabs.css +118 -0
  197. package/dist/overlay/AlertDialog.svelte +0 -0
  198. package/dist/overlay/AlertDialog.svelte.d.ts +26 -0
  199. package/dist/overlay/Command.svelte +0 -0
  200. package/dist/overlay/Command.svelte.d.ts +26 -0
  201. package/dist/overlay/Drawer.svelte +129 -0
  202. package/dist/overlay/Drawer.svelte.d.ts +20 -0
  203. package/dist/overlay/Dropdown.svelte +140 -0
  204. package/dist/overlay/Modal.svelte +102 -0
  205. package/dist/overlay/Modal.svelte.d.ts +19 -0
  206. package/dist/overlay/PopoverStack.svelte +0 -0
  207. package/dist/overlay/PopoverStack.svelte.d.ts +26 -0
  208. package/dist/overlay/Toast.svelte +83 -0
  209. package/dist/overlay/Toast.svelte.d.ts +9 -0
  210. package/dist/overlay/Tooltip.svelte +140 -0
  211. package/dist/overlay/Tooltip.svelte.d.ts +12 -0
  212. package/dist/overlay/css/drawer.css +75 -0
  213. package/dist/overlay/css/dropdown.css +24 -0
  214. package/dist/overlay/css/hovercard.css +11 -0
  215. package/dist/overlay/css/modal.css +51 -0
  216. package/dist/overlay/css/toast.css +80 -0
  217. package/dist/overlay/css/tooltip.css +89 -0
  218. package/dist/stores/i18n.svelte.d.ts +16 -0
  219. package/dist/stores/i18n.svelte.js +137 -0
  220. package/dist/stores/theme.svelte.d.ts +5 -0
  221. package/dist/stores/theme.svelte.js +55 -0
  222. package/dist/stores/toast.svelte.d.ts +19 -0
  223. package/dist/stores/toast.svelte.js +38 -0
  224. package/dist/types.d.ts +75 -0
  225. package/dist/types.js +1 -0
  226. package/dist/utils/charts.d.ts +27 -0
  227. package/dist/utils/charts.js +140 -0
  228. package/dist/utils/class-names.d.ts +1 -0
  229. package/dist/utils/class-names.js +3 -0
  230. package/dist/utils/click-outside.d.ts +3 -0
  231. package/dist/utils/click-outside.js +9 -0
  232. package/dist/utils/popover.d.ts +3 -0
  233. package/dist/utils/popover.js +17 -0
  234. package/dist/utils/ulid.d.ts +1 -0
  235. package/dist/utils/ulid.js +22 -0
  236. package/dist/utils/validate-schema.d.ts +2 -0
  237. package/dist/utils/validate-schema.js +97 -0
  238. package/package.json +69 -0
@@ -0,0 +1,38 @@
1
+ export const useFetch = (config) => {
2
+ let loading = $state(false);
3
+ const request = async (body) => {
4
+ loading = true;
5
+ try {
6
+ const token = localStorage.getItem('auth_token');
7
+ const response = await fetch(config.url, {
8
+ method: 'GET',
9
+ headers: {
10
+ Authorization: `Bearer ${token}`,
11
+ ...config.headers
12
+ },
13
+ body: body ? JSON.stringify(body) : undefined
14
+ });
15
+ if (!response.ok)
16
+ throw new Error('Network response was not ok');
17
+ const data = await response.json();
18
+ config.onSuccess?.(data);
19
+ }
20
+ catch (err) {
21
+ if (err instanceof Error) {
22
+ config.onError?.(err.message);
23
+ }
24
+ else {
25
+ config.onError?.(String(err));
26
+ }
27
+ }
28
+ finally {
29
+ loading = false;
30
+ }
31
+ };
32
+ return {
33
+ get loading() {
34
+ return loading;
35
+ },
36
+ request
37
+ };
38
+ };
@@ -0,0 +1,31 @@
1
+ export type ValidationRuleType = 'email' | 'required' | `min:${number}` | `max:${number}` | 'url' | 'phone' | `pattern:${string}` | 'alphanumeric' | 'numeric' | 'alpha' | `matches:${string}` | `custom:${string}` | 'strongPassword' | 'creditCard' | 'date' | `minWords:${number}` | `maxWords:${number}`;
2
+ export interface ValidationRule {
3
+ rule: ValidationRuleType;
4
+ message: string;
5
+ }
6
+ export interface ValidationRules {
7
+ [field: string]: ValidationRule[];
8
+ }
9
+ export interface FormConfig {
10
+ validationRules: ValidationRules;
11
+ url: string;
12
+ method: 'POST' | 'PUT' | 'PATCH';
13
+ headers?: Record<string, string>;
14
+ onError?: (errors: Record<string, string>) => void;
15
+ onSuccess?: (response: any) => void;
16
+ transformData?: (data: Record<string, any>) => any;
17
+ showToast?: boolean;
18
+ errorTitle?: string;
19
+ errorDescription?: string;
20
+ errorIcon?: string;
21
+ successTitle?: string;
22
+ successDescription?: string;
23
+ successIcon?: string;
24
+ resetOnSuccess?: boolean;
25
+ }
26
+ export declare const useForm: (config: FormConfig) => {
27
+ state: HTMLFormElement;
28
+ readonly errors: Record<string, string>;
29
+ readonly isSubmitting: boolean;
30
+ reset: () => void;
31
+ };
@@ -0,0 +1,110 @@
1
+ import { toast } from '../stores/toast.svelte.js';
2
+ import { validateSchema } from '../utils/validate-schema.js';
3
+ export const useForm = (config) => {
4
+ let state = $state();
5
+ let errors = $state({});
6
+ let isSubmitting = $state(false);
7
+ const handleSubmit = async (e) => {
8
+ e.preventDefault();
9
+ state = e.target;
10
+ const formData = new FormData(state);
11
+ let data = Object.fromEntries(formData.entries());
12
+ errors = validateSchema(data, config.validationRules);
13
+ if (Object.keys(errors).length > 0) {
14
+ config.onError?.(errors);
15
+ return;
16
+ }
17
+ if (config.transformData) {
18
+ data = config.transformData(data);
19
+ }
20
+ let url = config.url;
21
+ isSubmitting = true;
22
+ try {
23
+ const mergedHeaders = {
24
+ 'Content-Type': 'application/json',
25
+ ...config?.headers
26
+ };
27
+ const response = await fetch(url, {
28
+ method: config.method,
29
+ headers: mergedHeaders,
30
+ body: JSON.stringify(data)
31
+ });
32
+ if (!response.ok) {
33
+ const errorData = await response.json().catch(() => ({}));
34
+ errors = errorData.errors || { general: 'An error occurred' };
35
+ if (config.showToast) {
36
+ toast.show({
37
+ title: config.errorTitle || 'Error',
38
+ description: config.errorDescription || 'An error occurred while submitting the form.',
39
+ status: 'danger',
40
+ duration: 3000
41
+ });
42
+ }
43
+ config.onError?.(errors);
44
+ return;
45
+ }
46
+ const result = await response.json();
47
+ if (config.showToast) {
48
+ toast.show({
49
+ title: config.successTitle || 'Success',
50
+ description: config.successDescription || 'Form submitted successfully.',
51
+ status: 'success',
52
+ duration: 3000
53
+ });
54
+ }
55
+ config.onSuccess?.(result);
56
+ if (config.resetOnSuccess) {
57
+ state.reset();
58
+ }
59
+ }
60
+ catch (error) {
61
+ errors = { general: 'Network error occurred' };
62
+ if (config.showToast) {
63
+ toast.show({
64
+ title: config.errorTitle || 'Error',
65
+ description: config.errorDescription || 'An error occurred while submitting the form.',
66
+ status: 'danger',
67
+ duration: 3000
68
+ });
69
+ }
70
+ config.onError?.(errors);
71
+ }
72
+ finally {
73
+ isSubmitting = false;
74
+ }
75
+ };
76
+ const handleInput = (e) => {
77
+ const input = e.target;
78
+ const fieldName = input.name;
79
+ if (errors[fieldName]) {
80
+ delete errors[fieldName];
81
+ }
82
+ };
83
+ const reset = () => {
84
+ state?.reset();
85
+ errors = {};
86
+ };
87
+ $effect(() => {
88
+ state?.addEventListener('submit', handleSubmit);
89
+ state?.addEventListener('input', handleInput);
90
+ return () => {
91
+ state?.removeEventListener('submit', handleSubmit);
92
+ state?.removeEventListener('input', handleInput);
93
+ };
94
+ });
95
+ return {
96
+ get state() {
97
+ return state;
98
+ },
99
+ set state(value) {
100
+ state = value;
101
+ },
102
+ get errors() {
103
+ return errors;
104
+ },
105
+ get isSubmitting() {
106
+ return isSubmitting;
107
+ },
108
+ reset
109
+ };
110
+ };
@@ -0,0 +1,3 @@
1
+ export declare const useLocalStorage: (key: string, initialValue: any) => {
2
+ value: string;
3
+ };
@@ -0,0 +1,26 @@
1
+ import { onMount } from 'svelte';
2
+ export const useLocalStorage = (key, initialValue) => {
3
+ let value = $state(initialValue);
4
+ onMount(() => {
5
+ const currentValue = localStorage.getItem(key);
6
+ if (currentValue)
7
+ value = JSON.parse(currentValue);
8
+ });
9
+ const save = () => {
10
+ if (value) {
11
+ localStorage.setItem(key, JSON.stringify(value));
12
+ }
13
+ else {
14
+ localStorage.removeItem(key);
15
+ }
16
+ };
17
+ return {
18
+ get value() {
19
+ return value;
20
+ },
21
+ set value(v) {
22
+ value = v;
23
+ save();
24
+ }
25
+ };
26
+ };
@@ -0,0 +1,6 @@
1
+ export declare const useScroll: () => {
2
+ readonly scrollY: number;
3
+ readonly scrollX: number;
4
+ readonly isScrolled: boolean;
5
+ setThreshold: (value: number) => void;
6
+ };
@@ -0,0 +1,34 @@
1
+ export const useScroll = () => {
2
+ let scrollY = $state(0);
3
+ let scrollX = $state(0);
4
+ let isScrolled = $state(false);
5
+ let threshold = $state(100);
6
+ const updateScrollPosition = () => {
7
+ scrollY = window.scrollY;
8
+ scrollX = window.scrollX;
9
+ isScrolled = scrollY > threshold;
10
+ };
11
+ const setThreshold = (value) => {
12
+ threshold = value;
13
+ isScrolled = scrollY > threshold;
14
+ };
15
+ $effect(() => {
16
+ updateScrollPosition();
17
+ window.addEventListener('scroll', updateScrollPosition);
18
+ return () => {
19
+ window.removeEventListener('scroll', updateScrollPosition);
20
+ };
21
+ });
22
+ return {
23
+ get scrollY() {
24
+ return scrollY;
25
+ },
26
+ get scrollX() {
27
+ return scrollX;
28
+ },
29
+ get isScrolled() {
30
+ return isScrolled;
31
+ },
32
+ setThreshold
33
+ };
34
+ };
@@ -0,0 +1,49 @@
1
+ export interface SearchOption {
2
+ id: string | number;
3
+ label: string;
4
+ description?: string;
5
+ icon?: string;
6
+ src?: string;
7
+ disabled?: boolean;
8
+ [key: string]: unknown;
9
+ }
10
+ export interface SearchState {
11
+ options: SearchOption[];
12
+ isLoading: boolean;
13
+ isLoadingMore: boolean;
14
+ hasMore: boolean;
15
+ error: any;
16
+ search: string;
17
+ selectedValue: string | number | null;
18
+ selectedOption: SearchOption | null;
19
+ currentPage: number;
20
+ totalResults: number;
21
+ setSearch: (query: string) => void;
22
+ setSelectedValue: (value: string | number | null) => void;
23
+ refresh: () => void;
24
+ reset: () => void;
25
+ setOptions: (newOptions: SearchOption[]) => void;
26
+ loadMore: () => Promise<void>;
27
+ }
28
+ export interface SearchResponse {
29
+ options: SearchOption[];
30
+ total?: number;
31
+ hasMore?: boolean;
32
+ }
33
+ export interface SearchConfig {
34
+ url?: string;
35
+ options?: SearchOption[];
36
+ initialSearch?: string;
37
+ initialValue?: string | number | null;
38
+ headers?: Record<string, string>;
39
+ transformData?: (data: any, search: string) => SearchResponse;
40
+ onError?: (error: any) => void;
41
+ onSuccess?: (response: SearchResponse) => void;
42
+ debounceSearch?: number;
43
+ clientSide?: boolean;
44
+ minSearchLength?: number;
45
+ buildQueryParams?: (search: string, page: number) => Record<string, string>;
46
+ pageSize?: number;
47
+ enablePagination?: boolean;
48
+ }
49
+ export declare const useSearch: (config: SearchConfig) => SearchState;
@@ -0,0 +1,229 @@
1
+ export const useSearch = (config) => {
2
+ const isManualMode = !config.url || config.clientSide;
3
+ const pageSize = config.pageSize ?? 20;
4
+ const enablePagination = config.enablePagination ?? true;
5
+ let allOptions = $state(config.options ?? []);
6
+ let options = $state([]);
7
+ let search = $state(config.initialSearch ?? '');
8
+ let selectedValue = $state(config.initialValue ?? null);
9
+ let isLoading = $state(false);
10
+ let isLoadingMore = $state(false);
11
+ let hasMore = $state(false);
12
+ let error = $state(null);
13
+ let currentPage = $state(0);
14
+ let totalResults = $state(0);
15
+ let debounceTimeout;
16
+ const buildUrl = (page = 0) => {
17
+ if (!config.url)
18
+ return '';
19
+ const params = new URLSearchParams();
20
+ if (config.buildQueryParams) {
21
+ const customParams = config.buildQueryParams(search, page);
22
+ Object.entries(customParams).forEach(([key, value]) => {
23
+ if (value !== undefined && value !== null && value !== '') {
24
+ params.append(key, value);
25
+ }
26
+ });
27
+ }
28
+ else {
29
+ if (search)
30
+ params.append('search', search);
31
+ if (enablePagination) {
32
+ params.append('limit', String(pageSize));
33
+ params.append('skip', String(page * pageSize));
34
+ }
35
+ }
36
+ return `${config.url}?${params.toString()}`;
37
+ };
38
+ const processClientSideData = () => {
39
+ let processedOptions = [...allOptions];
40
+ if (search) {
41
+ const lowerSearch = search.toLowerCase();
42
+ processedOptions = processedOptions.filter((option) => {
43
+ return (option.label.toLowerCase().includes(lowerSearch) ||
44
+ String(option.id).toLowerCase().includes(lowerSearch) ||
45
+ (option.description && option.description.toLowerCase().includes(lowerSearch)));
46
+ });
47
+ }
48
+ // Client-side pagination
49
+ if (enablePagination) {
50
+ const start = 0;
51
+ const end = (currentPage + 1) * pageSize;
52
+ options = processedOptions.slice(start, end);
53
+ hasMore = end < processedOptions.length;
54
+ totalResults = processedOptions.length;
55
+ }
56
+ else {
57
+ options = processedOptions;
58
+ hasMore = false;
59
+ totalResults = processedOptions.length;
60
+ }
61
+ };
62
+ const fetchData = async (page = 0, append = false) => {
63
+ if (isManualMode) {
64
+ processClientSideData();
65
+ return;
66
+ }
67
+ if (config.minSearchLength && search.length < config.minSearchLength) {
68
+ options = [];
69
+ hasMore = false;
70
+ totalResults = 0;
71
+ return;
72
+ }
73
+ if (append) {
74
+ isLoadingMore = true;
75
+ }
76
+ else {
77
+ isLoading = true;
78
+ }
79
+ error = null;
80
+ try {
81
+ const response = await fetch(buildUrl(page), {
82
+ method: 'GET',
83
+ headers: {
84
+ 'Content-Type': 'application/json',
85
+ ...config.headers
86
+ }
87
+ });
88
+ if (!response.ok) {
89
+ throw new Error(`HTTP error! status: ${response.status}`);
90
+ }
91
+ const result = await response.json();
92
+ const searchResponse = config.transformData ? config.transformData(result, search) : result;
93
+ if (append) {
94
+ options = [...options, ...searchResponse.options];
95
+ }
96
+ else {
97
+ options = searchResponse.options;
98
+ }
99
+ // Determinar si hay más resultados
100
+ if (searchResponse.hasMore !== undefined) {
101
+ hasMore = searchResponse.hasMore;
102
+ }
103
+ else if (searchResponse.total !== undefined) {
104
+ totalResults = searchResponse.total;
105
+ hasMore = options.length < searchResponse.total;
106
+ }
107
+ else {
108
+ // Si no hay info de total, asumimos que hay más si recibimos pageSize resultados
109
+ hasMore = searchResponse.options.length >= pageSize;
110
+ }
111
+ config.onSuccess?.(searchResponse);
112
+ }
113
+ catch (err) {
114
+ error = err;
115
+ config.onError?.(err);
116
+ }
117
+ finally {
118
+ isLoading = false;
119
+ isLoadingMore = false;
120
+ }
121
+ };
122
+ const debouncedFetch = () => {
123
+ if (config.debounceSearch && config.debounceSearch > 0) {
124
+ clearTimeout(debounceTimeout);
125
+ debounceTimeout = setTimeout(() => {
126
+ currentPage = 0;
127
+ fetchData(0, false);
128
+ }, config.debounceSearch);
129
+ }
130
+ else {
131
+ currentPage = 0;
132
+ fetchData(0, false);
133
+ }
134
+ };
135
+ $effect(() => {
136
+ if (isManualMode) {
137
+ search;
138
+ allOptions;
139
+ processClientSideData();
140
+ }
141
+ else {
142
+ search;
143
+ debouncedFetch();
144
+ }
145
+ return () => {
146
+ clearTimeout(debounceTimeout);
147
+ };
148
+ });
149
+ const setSearch = (query) => {
150
+ search = query;
151
+ currentPage = 0;
152
+ };
153
+ const setSelectedValue = (value) => {
154
+ selectedValue = value;
155
+ };
156
+ const setOptions = (newOptions) => {
157
+ allOptions = newOptions;
158
+ currentPage = 0;
159
+ };
160
+ const refresh = () => {
161
+ currentPage = 0;
162
+ if (isManualMode) {
163
+ processClientSideData();
164
+ }
165
+ else {
166
+ fetchData(0, false);
167
+ }
168
+ };
169
+ const reset = () => {
170
+ search = config.initialSearch ?? '';
171
+ selectedValue = config.initialValue ?? null;
172
+ currentPage = 0;
173
+ if (isManualMode) {
174
+ processClientSideData();
175
+ }
176
+ };
177
+ const loadMore = async () => {
178
+ if (!hasMore || isLoadingMore || isLoading)
179
+ return;
180
+ currentPage += 1;
181
+ if (isManualMode) {
182
+ processClientSideData();
183
+ }
184
+ else {
185
+ await fetchData(currentPage, true);
186
+ }
187
+ };
188
+ return {
189
+ get options() {
190
+ return options;
191
+ },
192
+ get search() {
193
+ return search;
194
+ },
195
+ set search(value) {
196
+ search = value;
197
+ },
198
+ get selectedValue() {
199
+ return selectedValue;
200
+ },
201
+ get selectedOption() {
202
+ return options.find((opt) => opt.id === selectedValue) ?? null;
203
+ },
204
+ get isLoading() {
205
+ return isLoading;
206
+ },
207
+ get isLoadingMore() {
208
+ return isLoadingMore;
209
+ },
210
+ get hasMore() {
211
+ return hasMore;
212
+ },
213
+ get error() {
214
+ return error;
215
+ },
216
+ get currentPage() {
217
+ return currentPage;
218
+ },
219
+ get totalResults() {
220
+ return totalResults;
221
+ },
222
+ setSearch,
223
+ setSelectedValue,
224
+ refresh,
225
+ reset,
226
+ setOptions,
227
+ loadMore
228
+ };
229
+ };
@@ -0,0 +1,85 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Column {
3
+ label: string;
4
+ field: string;
5
+ sortable?: boolean;
6
+ width?: string;
7
+ align?: 'left' | 'center' | 'right';
8
+ render?: Snippet<[any]>;
9
+ }
10
+ export interface TableState {
11
+ data: any[];
12
+ isLoading: boolean;
13
+ error: any;
14
+ sortBy?: string;
15
+ sortOrder: 'ASC' | 'DESC';
16
+ setSort: (column: string, order?: 'ASC' | 'DESC') => void;
17
+ page?: number;
18
+ totalPages?: number;
19
+ total?: number;
20
+ hasNextPage?: boolean;
21
+ hasPrevPage?: boolean;
22
+ nextPage?: () => void;
23
+ prevPage?: () => void;
24
+ goToPage?: (page: number) => void;
25
+ columns: Column[];
26
+ setPage: (newPage: number) => void;
27
+ setPageSize: (newPageSize: number) => void;
28
+ setSearch: (query: string) => void;
29
+ refresh: () => void;
30
+ reset: () => void;
31
+ pageSize: number;
32
+ search: string;
33
+ updateData: (newData: any[]) => void;
34
+ selectable: boolean;
35
+ rowKey: string;
36
+ selectedRows: any[];
37
+ selectedKeys: Set<string | number>;
38
+ isAllSelected: boolean;
39
+ isIndeterminate: boolean;
40
+ selectRow: (row: any) => void;
41
+ deselectRow: (row: any) => void;
42
+ toggleRow: (row: any) => void;
43
+ selectAll: () => void;
44
+ deselectAll: () => void;
45
+ toggleAll: () => void;
46
+ isRowSelected: (row: any) => boolean;
47
+ clearSelection: () => void;
48
+ }
49
+ export type TableParams = {
50
+ page?: number;
51
+ pageSize?: number;
52
+ sortBy?: string;
53
+ sortOrder?: 'ASC' | 'DESC';
54
+ search?: string;
55
+ offset?: number;
56
+ limit?: number;
57
+ };
58
+ export interface PaginatedResponse {
59
+ data: any[];
60
+ total: number;
61
+ page: number;
62
+ pageSize: number;
63
+ totalPages: number;
64
+ }
65
+ export type TableConfig = {
66
+ columns: Column[];
67
+ data?: any[];
68
+ url?: string;
69
+ queryParams?: (params: TableParams) => Record<string, string>;
70
+ initialPage?: number;
71
+ initialPageSize?: number;
72
+ initialSortBy?: string;
73
+ initialSortOrder?: 'ASC' | 'DESC';
74
+ initialSearch?: string;
75
+ headers?: Record<string, string>;
76
+ parseResponse?: (data: any, params: TableParams) => PaginatedResponse;
77
+ onError?: (error: any) => void;
78
+ onSuccess?: (response: PaginatedResponse) => void;
79
+ debounceSearch?: number;
80
+ selectable?: boolean;
81
+ rowKey?: string;
82
+ onSelectionChange?: (selectedRows: any[]) => void;
83
+ };
84
+ export declare const useTable: (config: TableConfig) => TableState;
85
+ export {};