@sqrzro/ui 4.0.0-alpha.0 → 4.0.0-alpha.2

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 (219) hide show
  1. package/dist/components/buttons/ActionButton/index.d.ts +7 -0
  2. package/dist/components/buttons/ActionButton/index.js +33 -0
  3. package/dist/components/buttons/Button/index.d.ts +65 -0
  4. package/dist/components/buttons/Button/index.js +50 -0
  5. package/dist/components/buttons/ConfirmableButton/index.d.ts +7 -0
  6. package/dist/components/buttons/ConfirmableButton/index.js +13 -0
  7. package/dist/components/buttons/TextButton/index.d.ts +5 -0
  8. package/dist/components/buttons/TextButton/index.js +6 -0
  9. package/dist/components/collections/Collection/index.d.ts +15 -0
  10. package/dist/components/collections/Collection/index.js +45 -0
  11. package/dist/components/collections/EmptyMessage/index.d.ts +26 -0
  12. package/dist/components/collections/EmptyMessage/index.js +17 -0
  13. package/dist/components/collections/EmptyMessageAction/index.d.ts +6 -0
  14. package/dist/components/collections/EmptyMessageAction/index.js +19 -0
  15. package/dist/components/collections/List/index.d.ts +6 -0
  16. package/dist/components/collections/List/index.js +13 -0
  17. package/dist/components/collections/ListClientComponent/index.d.ts +7 -0
  18. package/dist/components/collections/ListClientComponent/index.js +9 -0
  19. package/dist/components/collections/ListItem/index.d.ts +14 -0
  20. package/dist/components/collections/ListItem/index.js +28 -0
  21. package/dist/components/collections/ListItemMeta/index.d.ts +11 -0
  22. package/dist/components/collections/ListItemMeta/index.js +22 -0
  23. package/dist/components/collections/ListItemSecondary/index.d.ts +6 -0
  24. package/dist/components/collections/ListItemSecondary/index.js +16 -0
  25. package/dist/components/collections/Pagination/index.d.ts +19 -0
  26. package/dist/components/collections/Pagination/index.js +17 -0
  27. package/dist/components/collections/Table/index.d.ts +9 -0
  28. package/dist/components/collections/Table/index.js +13 -0
  29. package/dist/components/collections/TableClientComponent/index.d.ts +14 -0
  30. package/dist/components/collections/TableClientComponent/index.js +30 -0
  31. package/dist/components/collections/interfaces.d.ts +57 -0
  32. package/dist/components/collections/interfaces.js +1 -0
  33. package/dist/components/collections/lang.d.ts +4 -0
  34. package/dist/components/collections/lang.js +4 -0
  35. package/dist/components/collections/utility/filter-columns.d.ts +3 -0
  36. package/dist/components/collections/utility/filter-columns.js +8 -0
  37. package/dist/components/collections/utility/get-selected-from-search-params.d.ts +2 -0
  38. package/dist/components/collections/utility/get-selected-from-search-params.js +5 -0
  39. package/dist/components/collections/utility/is-paginated.d.ts +3 -0
  40. package/dist/components/collections/utility/is-paginated.js +4 -0
  41. package/dist/components/collections/utility/set-selected-to-search-params.d.ts +2 -0
  42. package/dist/components/collections/utility/set-selected-to-search-params.js +4 -0
  43. package/dist/components/index.d.ts +16 -0
  44. package/dist/components/index.js +9 -0
  45. package/dist/components/modals/ConfirmModal/index.d.ts +7 -0
  46. package/dist/components/modals/ConfirmModal/index.js +23 -0
  47. package/dist/components/modals/Modal/index.d.ts +19 -0
  48. package/dist/components/modals/Modal/index.js +17 -0
  49. package/dist/components/modals/ModalActions/index.d.ts +9 -0
  50. package/dist/components/modals/ModalActions/index.js +8 -0
  51. package/dist/components/modals/ModalLauncher/index.d.ts +5 -0
  52. package/dist/components/modals/ModalLauncher/index.js +14 -0
  53. package/dist/components/utility/ActionList/index.d.ts +12 -0
  54. package/dist/components/utility/ActionList/index.js +9 -0
  55. package/dist/components/utility/Assistive/index.d.ts +8 -0
  56. package/dist/components/utility/Assistive/index.js +8 -0
  57. package/dist/components/utility/ClassNames/index.d.ts +6 -0
  58. package/dist/components/utility/ClassNames/index.js +7 -0
  59. package/dist/components/utility/Container/index.d.ts +9 -0
  60. package/dist/components/utility/Container/index.js +8 -0
  61. package/dist/components/utility/Link/index.d.ts +19 -0
  62. package/dist/components/utility/Link/index.js +33 -0
  63. package/dist/components/utility/Loader/index.d.ts +8 -0
  64. package/dist/components/utility/Loader/index.js +9 -0
  65. package/dist/components/utility/Page/index.d.ts +18 -0
  66. package/dist/components/utility/Page/index.js +34 -0
  67. package/dist/components/utility/Popover/index.d.ts +14 -0
  68. package/dist/components/utility/Popover/index.js +71 -0
  69. package/dist/components/utility/RootLayout/index.d.ts +12 -0
  70. package/dist/components/utility/RootLayout/index.js +14 -0
  71. package/dist/components/utility/Toast/index.d.ts +11 -0
  72. package/dist/components/utility/Toast/index.js +11 -0
  73. package/dist/components/utility/Toaster/index.d.ts +8 -0
  74. package/dist/components/utility/Toaster/index.js +57 -0
  75. package/dist/filters/components/FilterBar/index.d.ts +9 -0
  76. package/dist/filters/components/FilterBar/index.js +9 -0
  77. package/dist/filters/components/FilterBarClientComponent/index.d.ts +12 -0
  78. package/dist/filters/components/FilterBarClientComponent/index.js +68 -0
  79. package/dist/filters/components/FilterClearButton/index.d.ts +5 -0
  80. package/dist/filters/components/FilterClearButton/index.js +6 -0
  81. package/dist/filters/components/FilterControl/index.d.ts +12 -0
  82. package/dist/filters/components/FilterControl/index.js +10 -0
  83. package/dist/filters/components/FilterItem/index.d.ts +14 -0
  84. package/dist/filters/components/FilterItem/index.js +43 -0
  85. package/dist/filters/components/FilterPanel/index.d.ts +19 -0
  86. package/dist/filters/components/FilterPanel/index.js +36 -0
  87. package/dist/filters/filters/BooleanFilter/index.d.ts +3 -0
  88. package/dist/filters/filters/BooleanFilter/index.js +10 -0
  89. package/dist/filters/filters/CalendarFilter/index.d.ts +3 -0
  90. package/dist/filters/filters/CalendarFilter/index.js +10 -0
  91. package/dist/filters/filters/DateFilter/index.d.ts +3 -0
  92. package/dist/filters/filters/DateFilter/index.js +21 -0
  93. package/dist/filters/filters/DropdownFilter/index.d.ts +3 -0
  94. package/dist/filters/filters/DropdownFilter/index.js +10 -0
  95. package/dist/filters/filters/Filter/index.d.ts +16 -0
  96. package/dist/filters/filters/Filter/index.js +13 -0
  97. package/dist/filters/filters/MultiFilter/index.d.ts +3 -0
  98. package/dist/filters/filters/MultiFilter/index.js +9 -0
  99. package/dist/filters/filters/SearchFilter/index.d.ts +4 -0
  100. package/dist/filters/filters/SearchFilter/index.js +30 -0
  101. package/dist/filters/filters/interfaces.d.ts +10 -0
  102. package/dist/filters/filters/interfaces.js +1 -0
  103. package/dist/filters/hooks/useFilters.d.ts +5 -0
  104. package/dist/filters/hooks/useFilters.js +25 -0
  105. package/dist/filters/index.d.ts +2 -0
  106. package/dist/filters/index.js +2 -0
  107. package/dist/filters/interfaces.d.ts +28 -0
  108. package/dist/filters/interfaces.js +1 -0
  109. package/dist/filters/lang.d.ts +1 -0
  110. package/dist/filters/lang.js +1 -0
  111. package/dist/filters/utility/check-has-filters.d.ts +2 -0
  112. package/dist/filters/utility/check-has-filters.js +8 -0
  113. package/dist/filters/utility/create-client-filter-map.d.ts +3 -0
  114. package/dist/filters/utility/create-client-filter-map.js +14 -0
  115. package/dist/filters/utility/filter.d.ts +16 -0
  116. package/dist/filters/utility/filter.js +120 -0
  117. package/dist/filters/utility/get-quick-dates.d.ts +3 -0
  118. package/dist/filters/utility/get-quick-dates.js +90 -0
  119. package/dist/filters/utility/parse-filters.d.ts +3 -0
  120. package/dist/filters/utility/parse-filters.js +16 -0
  121. package/dist/filters/utility/parse-page.d.ts +2 -0
  122. package/dist/filters/utility/parse-page.js +8 -0
  123. package/dist/filters/utility/render-value.d.ts +3 -0
  124. package/dist/filters/utility/render-value.js +57 -0
  125. package/dist/filters/utility/transform-boolean.d.ts +2 -0
  126. package/dist/filters/utility/transform-boolean.js +10 -0
  127. package/dist/filters/utility/transform-date.d.ts +2 -0
  128. package/dist/filters/utility/transform-date.js +29 -0
  129. package/dist/filters/utility/transform-multi.d.ts +2 -0
  130. package/dist/filters/utility/transform-multi.js +9 -0
  131. package/dist/forms/components/Dropdown/index.d.ts +22 -0
  132. package/dist/forms/components/Dropdown/index.js +41 -0
  133. package/dist/forms/components/DropdownList/index.d.ts +8 -0
  134. package/dist/forms/components/DropdownList/index.js +20 -0
  135. package/dist/forms/components/EditableForm/index.d.ts +24 -0
  136. package/dist/forms/components/EditableForm/index.js +23 -0
  137. package/dist/forms/components/EditableFormField/index.d.ts +14 -0
  138. package/dist/forms/components/EditableFormField/index.js +37 -0
  139. package/dist/forms/components/EditableFormFields/index.d.ts +7 -0
  140. package/dist/forms/components/EditableFormFields/index.js +20 -0
  141. package/dist/forms/components/Form/index.d.ts +12 -0
  142. package/dist/forms/components/Form/index.js +9 -0
  143. package/dist/forms/components/FormError/index.d.ts +9 -0
  144. package/dist/forms/components/FormError/index.js +8 -0
  145. package/dist/forms/components/FormField/index.d.ts +25 -0
  146. package/dist/forms/components/FormField/index.js +37 -0
  147. package/dist/forms/components/FormFields/index.d.ts +10 -0
  148. package/dist/forms/components/FormFields/index.js +22 -0
  149. package/dist/forms/components/FormLabel/index.d.ts +10 -0
  150. package/dist/forms/components/FormLabel/index.js +8 -0
  151. package/dist/forms/components/FormSubmit/index.d.ts +4 -0
  152. package/dist/forms/components/FormSubmit/index.js +9 -0
  153. package/dist/forms/components/ModalForm/index.d.ts +13 -0
  154. package/dist/forms/components/ModalForm/index.js +25 -0
  155. package/dist/forms/components/PasswordInput/index.d.ts +11 -0
  156. package/dist/forms/components/PasswordInput/index.js +17 -0
  157. package/dist/forms/components/StaticTextInput/index.d.ts +22 -0
  158. package/dist/forms/components/StaticTextInput/index.js +22 -0
  159. package/dist/forms/components/Switch/index.d.ts +11 -0
  160. package/dist/forms/components/Switch/index.js +17 -0
  161. package/dist/forms/components/TextInput/index.d.ts +34 -0
  162. package/dist/forms/components/TextInput/index.js +30 -0
  163. package/dist/forms/hooks/useDropdown.d.ts +10 -0
  164. package/dist/forms/hooks/useDropdown.js +14 -0
  165. package/dist/forms/hooks/useEditableForm.d.ts +24 -0
  166. package/dist/forms/hooks/useEditableForm.js +34 -0
  167. package/dist/forms/hooks/useForm.d.ts +136 -0
  168. package/dist/forms/hooks/useForm.js +209 -0
  169. package/dist/forms/hooks/useModalForm.d.ts +14 -0
  170. package/dist/forms/hooks/useModalForm.js +29 -0
  171. package/dist/forms/index.d.ts +27 -0
  172. package/dist/forms/index.js +17 -0
  173. package/dist/forms/interfaces.d.ts +32 -0
  174. package/dist/forms/interfaces.js +1 -0
  175. package/dist/forms/utility/extract-editable-input-props.d.ts +8 -0
  176. package/dist/forms/utility/extract-editable-input-props.js +19 -0
  177. package/dist/forms/utility/extract-input-props.d.ts +9 -0
  178. package/dist/forms/utility/extract-input-props.js +37 -0
  179. package/dist/hooks/index.d.ts +0 -0
  180. package/dist/hooks/index.js +1 -0
  181. package/dist/hooks/useClickOutside.d.ts +93 -0
  182. package/dist/hooks/useClickOutside.js +124 -0
  183. package/dist/hooks/usePagination.d.ts +17 -0
  184. package/dist/hooks/usePagination.js +46 -0
  185. package/dist/hooks/useSearchParamsHref.d.ts +6 -0
  186. package/dist/hooks/useSearchParamsHref.js +33 -0
  187. package/dist/hooks/useToast.d.ts +18 -0
  188. package/dist/hooks/useToast.js +25 -0
  189. package/dist/navigation/components/AppNavigation/index.d.ts +13 -0
  190. package/dist/navigation/components/AppNavigation/index.js +15 -0
  191. package/dist/navigation/components/AppNavigationItem/index.d.ts +6 -0
  192. package/dist/navigation/components/AppNavigationItem/index.js +9 -0
  193. package/dist/navigation/components/Tabs/index.d.ts +7 -0
  194. package/dist/navigation/components/Tabs/index.js +9 -0
  195. package/dist/navigation/hooks/useNavigation.d.ts +10 -0
  196. package/dist/navigation/hooks/useNavigation.js +38 -0
  197. package/dist/navigation/index.d.ts +5 -0
  198. package/dist/navigation/index.js +3 -0
  199. package/dist/navigation/interfaces.d.ts +7 -0
  200. package/dist/navigation/interfaces.js +1 -0
  201. package/dist/styles/config.d.ts +64 -0
  202. package/dist/styles/config.js +43 -0
  203. package/dist/styles/icons.d.ts +15 -0
  204. package/dist/styles/icons.js +7 -0
  205. package/dist/styles/index.d.ts +2 -0
  206. package/dist/styles/index.js +1 -0
  207. package/dist/styles/interfaces.d.ts +41 -0
  208. package/dist/styles/interfaces.js +1 -0
  209. package/dist/styles/tw.d.ts +9 -0
  210. package/dist/styles/tw.js +15 -0
  211. package/dist/utility/index.d.ts +1 -0
  212. package/dist/utility/index.js +1 -0
  213. package/dist/utility/interfaces.d.ts +35 -0
  214. package/dist/utility/interfaces.js +1 -1
  215. package/package.json +55 -8
  216. package/dist/index.d.ts +0 -2
  217. package/dist/index.js +0 -1
  218. package/dist/lists/List/index.d.ts +0 -5
  219. package/dist/lists/List/index.js +0 -6
@@ -0,0 +1,209 @@
1
+ 'use client';
2
+ import { useRef, useState, useTransition } from 'react';
3
+ import { formatTitle } from '@sqrzro/utility';
4
+ import { useRouter } from 'next/navigation';
5
+ import useDeepCompareEffect from 'use-deep-compare-effect';
6
+ import useToast from '../../hooks/useToast';
7
+ const DEFAULT_TOAST_MESSAGES = {
8
+ server: 'There was a problem submitting your request. Please try again later.',
9
+ success: 'Your request was submitted successfully.',
10
+ validation: 'There was a problem with your submission. Please check the form and try again.',
11
+ };
12
+ function getErrorsForField(errors, name) {
13
+ if (!errors || Object.keys(errors).length === 0) {
14
+ return null;
15
+ }
16
+ const fieldErrors = Object.entries(errors)
17
+ .filter(([key]) => key === name || key.startsWith(`${name}.`) || key.startsWith(`${name}[`))
18
+ .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
19
+ return Object.keys(fieldErrors).length ? fieldErrors : null;
20
+ }
21
+ function getLabel(name, label) {
22
+ return label || formatTitle(name);
23
+ }
24
+ function getToastMessage(key, toasts) {
25
+ if (toasts === false || toasts?.[key] === false) {
26
+ return '';
27
+ }
28
+ return toasts?.[key] || DEFAULT_TOAST_MESSAGES[key];
29
+ }
30
+ /**
31
+ * ## Overview
32
+ *
33
+ * `useForm` is a custom React hook that facilitates handling server-side form submission,
34
+ * including state management, error handling, and form data processing. It's designed to work
35
+ * with forms where the data is submitted to and validated by a server.
36
+ *
37
+ * ## Type Parameters
38
+ *
39
+ * - `Request extends object`: The shape of the request data.
40
+ * - `Response = Request`: The shape of the response data, defaulting to the same type as `Request`.
41
+ *
42
+ * ## Arguments
43
+ *
44
+ * `useForm` takes an object with the following properties:
45
+ *
46
+ * - `defaults`: Initial default values for the form data.
47
+ * - `hiddenFields`: Fields that should be included in the form submission but not displayed.
48
+ * - `onError`: Callback function for handling errors during form submission.
49
+ * - `onSubmit`: Function to handle the form submission. Expected to return a promise.
50
+ * - `onSuccess`: Callback function to be called upon a successful submission.
51
+ * - `onValidationError`: Callback function for handling validation errors.
52
+ * - `toasts`: Configuration for displaying success or error toasts.
53
+ *
54
+ * ## Returns
55
+ *
56
+ * `useForm` returns an object with the following properties:
57
+ *
58
+ * - `errors`: An object containing any form errors.
59
+ * - `fieldProps`: Function to generate props for a form field.
60
+ * - `formData`: Current form data.
61
+ * - `formProps`: Props for the form element including the submit handler.
62
+ * - `isLoading`: Boolean indicating if the form submission is in progress.
63
+ * - `resetForm`: Function to reset the form to its initial state.
64
+ * - `setFormData`: Function to update a specific piece of form data.
65
+ *
66
+ * ## Functionality
67
+ *
68
+ * - **Form Data Management:**
69
+ * - Manages form data state and provides handleChange and setFormData functions to update form
70
+ * data.
71
+ *
72
+ * - **Form Submission:**
73
+ * - Handles form submission, including displaying loading state, submitting data to the provided
74
+ * onSubmit function, and handling server-side validation errors or other errors.
75
+ *
76
+ * - **Error Handling:**
77
+ * - Manages form error state and handles displaying error messages.
78
+ *
79
+ * - **Toasts:**
80
+ * - Optionally displays success or error toasts based on the form submission outcome.
81
+ *
82
+ * - **Reset Form:**
83
+ * - Provides a function to reset the form to its default state.
84
+ *
85
+ * ## Example
86
+ *
87
+ * ```jsx
88
+ * import React from 'react';
89
+ * import { useForm } from './path-to-useForm';
90
+ *
91
+ * const MyFormComponent = () => {
92
+ * const { fieldProps, formProps, isLoading, resetForm } = useForm<MyFormData>({
93
+ * defaults: { name: '', email: '' },
94
+ * onSubmit: async (formData) => {
95
+ * // Submit form data to the server
96
+ * },
97
+ * });
98
+ *
99
+ * return (
100
+ * <form {...formProps}>
101
+ * <input {...fieldProps('name', 'Name')} />
102
+ * <input {...fieldProps('email', 'Email')} />
103
+ * <button type="submit" disabled={isLoading}>Submit</button>
104
+ * <button type="button" onClick={resetForm}>Reset</button>
105
+ * </form>
106
+ * );
107
+ * };
108
+ *
109
+ * export default MyFormComponent;
110
+ * ```
111
+ *
112
+ * In this example, `MyFormComponent` uses the `useForm` hook to manage form state, handle
113
+ * submissions, display errors, and reset the form.
114
+ *
115
+ * ## Notes
116
+ *
117
+ * - Ensure proper types are passed to the useForm hook to align with the expected structure
118
+ * of your form data and server responses.
119
+ * - The hook's functionality can be extended or customized based on specific use cases, such as
120
+ * handling additional form events or integrating with different server APIs.
121
+ */
122
+ function useForm({ defaults = {}, onError, onSubmit, onSuccess, onValidation, redirectOnSuccess, refreshOnSuccess, toasts, }) {
123
+ const innerRef = useRef(null);
124
+ const router = useRouter();
125
+ const [isLoading, startTransition] = useTransition();
126
+ const [errors, setErrors] = useState(null);
127
+ const [data, setData] = useState(defaults);
128
+ const { toastError, toastSuccess } = useToast();
129
+ function setFormData(key, value) {
130
+ setData((prev) => ({ ...prev, [key]: value }));
131
+ }
132
+ function handleChange(event) {
133
+ setData((prev) => ({ ...prev, [event.target.name]: event.target.value }));
134
+ }
135
+ async function handleSuccess(response) {
136
+ await onSuccess?.(response);
137
+ if (redirectOnSuccess) {
138
+ const redirect = typeof redirectOnSuccess === 'function'
139
+ ? redirectOnSuccess(response)
140
+ : redirectOnSuccess;
141
+ if (redirect) {
142
+ router.push(redirect);
143
+ }
144
+ }
145
+ if (refreshOnSuccess) {
146
+ router.refresh();
147
+ }
148
+ toastSuccess(getToastMessage('success', toasts));
149
+ }
150
+ function handleError(message) {
151
+ onError?.(message);
152
+ toastError(getToastMessage('server', toasts));
153
+ }
154
+ function handleValidation(messages) {
155
+ onValidation?.(messages);
156
+ setErrors(messages);
157
+ toastError(getToastMessage('validation', toasts));
158
+ }
159
+ async function handleSubmit() {
160
+ if (!onSubmit) {
161
+ return;
162
+ }
163
+ setErrors(null);
164
+ const fnReturn = await onSubmit(data);
165
+ if (fnReturn.error) {
166
+ return handleError(fnReturn.error);
167
+ }
168
+ if (fnReturn.validation) {
169
+ return handleValidation(fnReturn.validation);
170
+ }
171
+ if (fnReturn.data) {
172
+ return handleSuccess(fnReturn.data);
173
+ }
174
+ }
175
+ function fieldProps(name, label) {
176
+ return {
177
+ error: getErrorsForField(errors, name),
178
+ label: getLabel(name, label),
179
+ name: name,
180
+ onChange: handleChange,
181
+ value: data[name],
182
+ };
183
+ }
184
+ function resetForm() {
185
+ setTimeout(() => {
186
+ setData((defaults || {}));
187
+ setErrors(null);
188
+ }, 1);
189
+ }
190
+ useDeepCompareEffect(() => {
191
+ setData({ ...data, ...defaults });
192
+ }, [defaults]);
193
+ return {
194
+ errors,
195
+ fieldProps,
196
+ formData: data,
197
+ formProps: {
198
+ action: handleSubmit,
199
+ },
200
+ isLoading,
201
+ resetForm,
202
+ setErrors,
203
+ setFormData,
204
+ submitForm: () => {
205
+ setTimeout(() => innerRef.current?.requestSubmit(), 1);
206
+ },
207
+ };
208
+ }
209
+ export default useForm;
@@ -0,0 +1,14 @@
1
+ import type { Action } from '../../utility/interfaces';
2
+ import type { ModalFormProps } from '../components/ModalForm';
3
+ import type { UseFormArgs, UseFormReturn } from './useForm';
4
+ export interface UseModalFormArgs<Request, Response> extends UseFormArgs<Request, Response> {
5
+ actions?: Action[];
6
+ icon?: React.ReactNode;
7
+ submitLabel?: string;
8
+ title?: string;
9
+ }
10
+ export interface UseModalFormReturn<Request> extends Omit<UseFormReturn<Request>, 'formProps'> {
11
+ formProps: Omit<ModalFormProps, 'children'>;
12
+ }
13
+ declare function useModalForm<Request extends object, Response = Request>({ actions, icon, submitLabel, title, ...useFormArgs }: UseModalFormArgs<Request, Response>): UseModalFormReturn<Request>;
14
+ export default useModalForm;
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+ import { useRouter } from 'next/navigation';
3
+ import useSearchParamsHref from '../../hooks/useSearchParamsHref';
4
+ import useForm from './useForm';
5
+ function useModalForm({ actions, icon, submitLabel, title, ...useFormArgs }) {
6
+ const { setSearchParamsHref } = useSearchParamsHref();
7
+ const router = useRouter();
8
+ function handleSuccess(response) {
9
+ useFormArgs.onSuccess?.(response);
10
+ setSearchParamsHref('action', null);
11
+ setTimeout(() => {
12
+ router.refresh();
13
+ }, 1);
14
+ }
15
+ const useFormReturn = useForm({
16
+ ...useFormArgs,
17
+ onSuccess: handleSuccess,
18
+ toasts: false,
19
+ });
20
+ return {
21
+ ...useFormReturn,
22
+ formProps: {
23
+ formProps: useFormReturn.formProps,
24
+ modalProps: { actions, icon, title },
25
+ submitLabel,
26
+ },
27
+ };
28
+ }
29
+ export default useModalForm;
@@ -0,0 +1,27 @@
1
+ export * from './interfaces';
2
+ export type { DropdownClassNames, DropdownProps } from './components/Dropdown';
3
+ export { default as Dropdown } from './components/Dropdown';
4
+ export type { EditableFormClassNames, EditableFormProps } from './components/EditableForm';
5
+ export { default as EditableForm } from './components/EditableForm';
6
+ export type { EditableFormFieldProps } from './components/EditableFormField';
7
+ export { default as EditableFormField } from './components/EditableFormField';
8
+ export type { FormClassNames, FormProps } from './components/Form';
9
+ export { default as Form } from './components/Form';
10
+ export type { FormFieldClassNames, FormFieldProps } from './components/FormField';
11
+ export { default as FormField } from './components/FormField';
12
+ export type { FormSubmitProps } from './components/FormSubmit';
13
+ export { default as FormSubmit } from './components/FormSubmit';
14
+ export type { ModalFormProps } from './components/ModalForm';
15
+ export { default as ModalForm } from './components/ModalForm';
16
+ export type { PasswordInputClassNames, PasswordInputProps } from './components/PasswordInput';
17
+ export { default as PasswordInput } from './components/PasswordInput';
18
+ export type { TextInputClassNames, TextInputProps } from './components/TextInput';
19
+ export { default as TextInput } from './components/TextInput';
20
+ export type { EditableDropdownFormFieldProps, EditableTextFormFieldProps, } from './components/EditableFormFields';
21
+ export { EditableDropdownFormField, EditableTextFormField } from './components/EditableFormFields';
22
+ export type { DropdownFormFieldProps, PasswordFormFieldProps, TextFormFieldProps, } from './components/FormFields';
23
+ export { DropdownFormField, PasswordFormField, TextFormField } from './components/FormFields';
24
+ export { default as useDropdown } from './hooks/useDropdown';
25
+ export { default as useEditableForm } from './hooks/useEditableForm';
26
+ export { default as useForm } from './hooks/useForm';
27
+ export { default as useModalForm } from './hooks/useModalForm';
@@ -0,0 +1,17 @@
1
+ export * from './interfaces';
2
+ export { default as Dropdown } from './components/Dropdown';
3
+ export { default as EditableForm } from './components/EditableForm';
4
+ export { default as EditableFormField } from './components/EditableFormField';
5
+ export { default as Form } from './components/Form';
6
+ export { default as FormField } from './components/FormField';
7
+ export { default as FormSubmit } from './components/FormSubmit';
8
+ export { default as ModalForm } from './components/ModalForm';
9
+ export { default as PasswordInput } from './components/PasswordInput';
10
+ export { default as TextInput } from './components/TextInput';
11
+ export { EditableDropdownFormField, EditableTextFormField } from './components/EditableFormFields';
12
+ export { DropdownFormField, PasswordFormField, TextFormField } from './components/FormFields';
13
+ // Hooks
14
+ export { default as useDropdown } from './hooks/useDropdown';
15
+ export { default as useEditableForm } from './hooks/useEditableForm';
16
+ export { default as useForm } from './hooks/useForm';
17
+ export { default as useModalForm } from './hooks/useModalForm';
@@ -0,0 +1,32 @@
1
+ import type { EditableFormFieldProps } from './components/EditableFormField';
2
+ import type { FormFieldProps } from './components/FormField';
3
+ export type Default<T> = {
4
+ [K in keyof T]?: T[K] | null;
5
+ };
6
+ export interface DropdownObject<T = string> {
7
+ $data?: Record<string, string>;
8
+ details?: string | null;
9
+ isDisabled?: boolean;
10
+ label: string;
11
+ value: T;
12
+ }
13
+ export type EditingStatus = 'CANCELLED' | 'EDITING' | 'SAVED';
14
+ export type EditableFormFieldComponentProps<T, V extends T = T> = Omit<EditableFormFieldProps<T, V>, 'render'>;
15
+ export type FormFieldComponentProps<T, V extends T = T> = Omit<FormFieldProps<T, V>, 'render'>;
16
+ export interface InputEventTarget<T> {
17
+ name: string;
18
+ value?: T;
19
+ }
20
+ export interface InputEvent<T = unknown> {
21
+ $data?: Record<string, string>;
22
+ target: InputEventTarget<T>;
23
+ }
24
+ export interface InputProps<V, T extends V = V> {
25
+ hasError?: boolean;
26
+ id?: string;
27
+ isDisabled?: boolean;
28
+ name: string;
29
+ onChange?: (event: InputEvent<T>) => void;
30
+ onKeyDown?: React.KeyboardEventHandler;
31
+ value?: V;
32
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { EditableFormFieldComponentProps } from '../interfaces';
2
+ type InferFieldValue<U> = U extends EditableFormFieldComponentProps<infer T> ? T : never;
3
+ export declare function getEditableFieldPropKeys<T>(): (keyof EditableFormFieldComponentProps<T>)[];
4
+ declare function extractEditableInputProps<U extends EditableFormFieldComponentProps<InferFieldValue<U>>>(data: U): {
5
+ fieldProps: EditableFormFieldComponentProps<InferFieldValue<U>>;
6
+ inputProps: Omit<U, keyof EditableFormFieldComponentProps<InferFieldValue<U>>>;
7
+ };
8
+ export default extractEditableInputProps;
@@ -0,0 +1,19 @@
1
+ import { getFieldPropKeys, hasKey } from './extract-input-props';
2
+ export function getEditableFieldPropKeys() {
3
+ return [...getFieldPropKeys(), 'renderValue', 'status'];
4
+ }
5
+ function extractEditableInputProps(data) {
6
+ const fieldPropKeys = getEditableFieldPropKeys();
7
+ const fieldProps = {};
8
+ const inputProps = {};
9
+ for (const key in data) {
10
+ if (hasKey(fieldPropKeys, key)) {
11
+ fieldProps[key] = data[key];
12
+ }
13
+ else {
14
+ inputProps[key] = data[key];
15
+ }
16
+ }
17
+ return { fieldProps, inputProps };
18
+ }
19
+ export default extractEditableInputProps;
@@ -0,0 +1,9 @@
1
+ import type { FormFieldComponentProps } from '../interfaces';
2
+ type InferFieldValue<U> = U extends FormFieldComponentProps<infer T> ? T : never;
3
+ export declare function getFieldPropKeys<T>(): (keyof FormFieldComponentProps<T>)[];
4
+ export declare function hasKey<K extends PropertyKey>(obj: readonly K[], key: PropertyKey): key is K;
5
+ declare function extractInputProps<U extends FormFieldComponentProps<InferFieldValue<U>>>(data: U): {
6
+ fieldProps: FormFieldComponentProps<InferFieldValue<U>>;
7
+ inputProps: Omit<U, keyof FormFieldComponentProps<InferFieldValue<U>>>;
8
+ };
9
+ export default extractInputProps;
@@ -0,0 +1,37 @@
1
+ export function getFieldPropKeys() {
2
+ return [
3
+ 'action',
4
+ 'details',
5
+ 'error',
6
+ 'hasAssistiveError',
7
+ 'hasAssistiveLabel',
8
+ 'hasError',
9
+ 'id',
10
+ 'isContentOnly',
11
+ 'isDisabled',
12
+ 'isOptional',
13
+ 'label',
14
+ 'name',
15
+ 'onChange',
16
+ 'onKeyDown',
17
+ 'value',
18
+ ];
19
+ }
20
+ export function hasKey(obj, key) {
21
+ return obj.includes(key);
22
+ }
23
+ function extractInputProps(data) {
24
+ const fieldPropKeys = getFieldPropKeys();
25
+ const fieldProps = {};
26
+ const inputProps = {};
27
+ for (const key in data) {
28
+ if (hasKey(fieldPropKeys, key)) {
29
+ fieldProps[key] = data[key];
30
+ }
31
+ else {
32
+ inputProps[key] = data[key];
33
+ }
34
+ }
35
+ return { fieldProps, inputProps };
36
+ }
37
+ export default extractInputProps;
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,93 @@
1
+ import type { RefObject } from 'react';
2
+ export interface UseClickOutsideOptions {
3
+ shouldHandleEnterKey?: boolean;
4
+ }
5
+ type UseClickOutsideReturn<T> = [boolean, (bool: boolean) => void, RefObject<T | null>];
6
+ /**
7
+ * # useClickOutside
8
+ *
9
+ * ## Overview
10
+ *
11
+ * `useClickOutside` is a custom React hook that provides functionality to detect and respond to
12
+ * clicks outside a specified element (`ref`). It's commonly used to close modal windows, dropdown
13
+ * menus, or any other UI elements that should be hidden when the user interacts with the rest of
14
+ * the application. Additionally, this hook can handle specific keyboard events.
15
+ *
16
+ * ## Usage
17
+ *
18
+ * This hook is ideal for components that need to react to clicks outside of them or require
19
+ * specific keyboard navigation like closing a modal on pressing the Escape key.
20
+ *
21
+ * ## Type Parameters
22
+ *
23
+ * - `T extends HTMLElement = HTMLDivElement`: The generic type `T` is used for the ref's HTML
24
+ * element type, defaulting to `HTMLDivElement`.
25
+ *
26
+ * ## Arguments
27
+ *
28
+ * `useClickOutside` takes an optional `options` object with the following property:
29
+ *
30
+ * - `shouldHandleEnterKey` (optional): A boolean that indicates whether the hook should also handle
31
+ * the 'Enter' key, in addition to the 'Escape' key, for closing the component.
32
+ *
33
+ * ## Returns
34
+ *
35
+ * `useClickOutside` returns a tuple (array with three elements) with the following types:
36
+ *
37
+ * 1. `isOpen`: A boolean state indicating whether the component is open.
38
+ * 2. `setIsOpen`: A function to set the `isOpen` state.
39
+ * 3. `node`: A ref object to be attached to the component.
40
+ *
41
+ * ## Functionality
42
+ *
43
+ * - **Click Outside Detection:**
44
+ * - The `handleClickOutside` function checks if the click event target is outside the `node` (the
45
+ * element the ref is attached to). If the click is outside, it sets the `isOpen` state to `false`
46
+ * after a delay.
47
+ *
48
+ * - **Keyboard Event Handling:**
49
+ * - The `handleKeyDown` function listens for keyboard events and closes the component (sets
50
+ * `isOpen` to `false`) when the 'Escape' key is pressed, or the 'Enter' key if
51
+ * `shouldHandleEnterKey` is true.
52
+ *
53
+ * - **Lifecycle Management:**
54
+ * - The `mounted` ref tracks if the component is still mounted to prevent state updates on
55
+ * unmounted components.
56
+ * - Event listeners for clicks and keyboard events are added on component mount and removed on
57
+ * component unmount.
58
+ *
59
+ * ## Example
60
+ *
61
+ * ```jsx
62
+ * import React, { useRef } from 'react';
63
+ * import useClickOutside from './path-to-useClickOutside';
64
+ *
65
+ * function MyComponent() {
66
+ * const [isOpen, setIsOpen, node] = useClickOutside<HTMLDivElement>();
67
+ *
68
+ * return (
69
+ * <div ref={node}>
70
+ * {isOpen && <div>Content that should close</div>}
71
+ * <button onClick={() => setIsOpen(true)}>Open</button>
72
+ * </div>
73
+ * );
74
+ * }
75
+ *
76
+ * export default MyComponent;
77
+ * ```
78
+ *
79
+ * In this example, `MyComponent` uses the `useClickOutside` hook to control the visibility of its
80
+ * content. The content is hidden when a click occurs outside the component or when the 'Escape' key
81
+ * is pressed.
82
+ *
83
+ * ## Notes
84
+ *
85
+ * - Ensure that the `node` ref is properly attached to the DOM element you want to monitor for
86
+ * outside clicks.
87
+ * - This hook manages an `isOpen` state internally but exposes it along with its setter function
88
+ * for external control.
89
+ * - The hook adds and removes event listeners for click and keyboard events to avoid memory leaks
90
+ * and unintended behavior in single-page applications.
91
+ */
92
+ declare function useClickOutside<T extends HTMLElement = HTMLDivElement>(options?: UseClickOutsideOptions): UseClickOutsideReturn<T>;
93
+ export default useClickOutside;
@@ -0,0 +1,124 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ /**
3
+ * # useClickOutside
4
+ *
5
+ * ## Overview
6
+ *
7
+ * `useClickOutside` is a custom React hook that provides functionality to detect and respond to
8
+ * clicks outside a specified element (`ref`). It's commonly used to close modal windows, dropdown
9
+ * menus, or any other UI elements that should be hidden when the user interacts with the rest of
10
+ * the application. Additionally, this hook can handle specific keyboard events.
11
+ *
12
+ * ## Usage
13
+ *
14
+ * This hook is ideal for components that need to react to clicks outside of them or require
15
+ * specific keyboard navigation like closing a modal on pressing the Escape key.
16
+ *
17
+ * ## Type Parameters
18
+ *
19
+ * - `T extends HTMLElement = HTMLDivElement`: The generic type `T` is used for the ref's HTML
20
+ * element type, defaulting to `HTMLDivElement`.
21
+ *
22
+ * ## Arguments
23
+ *
24
+ * `useClickOutside` takes an optional `options` object with the following property:
25
+ *
26
+ * - `shouldHandleEnterKey` (optional): A boolean that indicates whether the hook should also handle
27
+ * the 'Enter' key, in addition to the 'Escape' key, for closing the component.
28
+ *
29
+ * ## Returns
30
+ *
31
+ * `useClickOutside` returns a tuple (array with three elements) with the following types:
32
+ *
33
+ * 1. `isOpen`: A boolean state indicating whether the component is open.
34
+ * 2. `setIsOpen`: A function to set the `isOpen` state.
35
+ * 3. `node`: A ref object to be attached to the component.
36
+ *
37
+ * ## Functionality
38
+ *
39
+ * - **Click Outside Detection:**
40
+ * - The `handleClickOutside` function checks if the click event target is outside the `node` (the
41
+ * element the ref is attached to). If the click is outside, it sets the `isOpen` state to `false`
42
+ * after a delay.
43
+ *
44
+ * - **Keyboard Event Handling:**
45
+ * - The `handleKeyDown` function listens for keyboard events and closes the component (sets
46
+ * `isOpen` to `false`) when the 'Escape' key is pressed, or the 'Enter' key if
47
+ * `shouldHandleEnterKey` is true.
48
+ *
49
+ * - **Lifecycle Management:**
50
+ * - The `mounted` ref tracks if the component is still mounted to prevent state updates on
51
+ * unmounted components.
52
+ * - Event listeners for clicks and keyboard events are added on component mount and removed on
53
+ * component unmount.
54
+ *
55
+ * ## Example
56
+ *
57
+ * ```jsx
58
+ * import React, { useRef } from 'react';
59
+ * import useClickOutside from './path-to-useClickOutside';
60
+ *
61
+ * function MyComponent() {
62
+ * const [isOpen, setIsOpen, node] = useClickOutside<HTMLDivElement>();
63
+ *
64
+ * return (
65
+ * <div ref={node}>
66
+ * {isOpen && <div>Content that should close</div>}
67
+ * <button onClick={() => setIsOpen(true)}>Open</button>
68
+ * </div>
69
+ * );
70
+ * }
71
+ *
72
+ * export default MyComponent;
73
+ * ```
74
+ *
75
+ * In this example, `MyComponent` uses the `useClickOutside` hook to control the visibility of its
76
+ * content. The content is hidden when a click occurs outside the component or when the 'Escape' key
77
+ * is pressed.
78
+ *
79
+ * ## Notes
80
+ *
81
+ * - Ensure that the `node` ref is properly attached to the DOM element you want to monitor for
82
+ * outside clicks.
83
+ * - This hook manages an `isOpen` state internally but exposes it along with its setter function
84
+ * for external control.
85
+ * - The hook adds and removes event listeners for click and keyboard events to avoid memory leaks
86
+ * and unintended behavior in single-page applications.
87
+ */
88
+ function useClickOutside(options) {
89
+ const mounted = useRef(true);
90
+ const [isOpen, setIsOpen] = useState(false);
91
+ const node = useRef(null);
92
+ function handleClickOutside(event) {
93
+ if (node.current?.contains(event.target)) {
94
+ return;
95
+ }
96
+ setTimeout(() => {
97
+ if (mounted) {
98
+ setIsOpen(false);
99
+ }
100
+ });
101
+ }
102
+ useEffect(() => {
103
+ mounted.current = true;
104
+ function handleKeyDown(event) {
105
+ const keys = options?.shouldHandleEnterKey ? ['Escape', 'Enter'] : ['Escape'];
106
+ if (keys.includes(event.key)) {
107
+ setTimeout(() => {
108
+ if (mounted) {
109
+ setIsOpen(false);
110
+ }
111
+ });
112
+ }
113
+ }
114
+ document.addEventListener('click', handleClickOutside);
115
+ document.addEventListener('keydown', handleKeyDown);
116
+ return () => {
117
+ mounted.current = false;
118
+ document.removeEventListener('click', handleClickOutside);
119
+ document.removeEventListener('keydown', handleKeyDown);
120
+ };
121
+ }, [options?.shouldHandleEnterKey]);
122
+ return [isOpen, (bool) => setIsOpen(bool), node];
123
+ }
124
+ export default useClickOutside;
@@ -0,0 +1,17 @@
1
+ export interface UsePaginationArgs {
2
+ limit: number;
3
+ page: number;
4
+ total: number;
5
+ }
6
+ export interface UsePaginationReturn {
7
+ links: ({
8
+ href: string;
9
+ label: string;
10
+ } | null)[];
11
+ nextLink?: string | null;
12
+ pageCount: number;
13
+ previousLink?: string | null;
14
+ }
15
+ export declare function getLink(pathname: string, searchParams: URLSearchParams, page: number): string;
16
+ declare function usePagination({ limit, page, total }: Readonly<UsePaginationArgs>): UsePaginationReturn;
17
+ export default usePagination;