@void-snippets/react 0.2.1 → 0.3.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.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { ResourceAdapters, TQueryParams, TPagination, ResourceListResult } from '@void-snippets/core';
2
+ import { VSAdapters, VSQueryParams, VSPagination, VSListResult } from '@void-snippets/core';
3
+ import { ReactNode } from 'react';
3
4
 
4
- type CapitalizeStr<S extends string> = S extends `${infer F}${infer Rest}` ? `${Uppercase<F>}${Rest}` : S;
5
5
  interface WithResourceTypes {
6
6
  readonly __types: {
7
7
  id: unknown;
@@ -20,21 +20,23 @@ type Create<S extends WithResourceTypes> = S["__types"]["create"];
20
20
  type Update<S extends WithResourceTypes> = S["__types"]["update"];
21
21
  type ListRaw<S extends WithResourceTypes> = S["__types"]["listRaw"];
22
22
  type SingleRaw<S extends WithResourceTypes> = S["__types"]["singleRaw"];
23
- type UseListReturn<K extends string, TBase> = {
24
- [P in K]: TBase[];
25
- } & {
26
- pagination: TPagination;
27
- } & {
28
- [P in `is${CapitalizeStr<K>}Loading`]: boolean;
29
- } & {
30
- [P in `${K}Error`]: Error | null;
31
- } & {
32
- [P in `invalidate${CapitalizeStr<K>}`]: () => void;
33
- };
34
- interface CreateResourceHooksOptions<TListRaw, TBase, TSingleRaw, TDetail> {
23
+ interface VSUseListReturn<TBase> {
24
+ list: TBase[];
25
+ pagination: VSPagination;
26
+ isLoading: boolean;
27
+ error: Error | null;
28
+ invalidate: () => void;
29
+ }
30
+ interface VSUseGetReturn<TDetail> {
31
+ item: TDetail | undefined;
32
+ isLoading: boolean;
33
+ error: Error | null;
34
+ refetch: () => void;
35
+ }
36
+ interface VSResourceHooksOptions<TListRaw, TBase, TSingleRaw, TDetail> {
35
37
  /**
36
- * Adapters map your API's raw response shapes to the library's internal
37
- * format. Omit this entirely if your API matches the default shape:
38
+ * Adapters map your API's raw response to the library's internal format.
39
+ * Omit if your API matches the default shape:
38
40
  * List: { data: { items, page, limit, totalPages, totalDocuments } }
39
41
  * Single: { data: <item> }
40
42
  *
@@ -54,54 +56,186 @@ interface CreateResourceHooksOptions<TListRaw, TBase, TSingleRaw, TDetail> {
54
56
  * },
55
57
  * })
56
58
  */
57
- adapters?: ResourceAdapters<TListRaw, TBase, TSingleRaw, TDetail>;
59
+ adapters?: VSAdapters<TListRaw, TBase, TSingleRaw, TDetail>;
58
60
  /**
59
61
  * Default params passed to useList and useInfinite when none are provided.
60
62
  * @default { page: 1, limit: 10 }
61
63
  */
62
- defaultParams?: TQueryParams;
64
+ defaultParams?: VSQueryParams;
63
65
  }
64
66
  /**
65
67
  * Creates a set of TanStack Query hooks for a resource.
66
- * All types are fully inferred from the `apiService` instance — no generics
67
- * need to be passed manually.
68
+ * All types are fully inferred from the `apiService` instance — no generics needed.
68
69
  *
69
- * @param queryKeyPrefix - TanStack Query cache key prefix and the base name
70
- * for the returned hook properties.
71
- * e.g. "contacts" → { contacts, isContactsLoading, ... }
70
+ * @param queryKeyPrefix - TanStack Query cache key. Used to scope the cache
71
+ * and for auto-invalidation. e.g. "contacts"
72
72
  * @param apiService - An instance of ResourceService (or a subclass).
73
73
  * @param options - Optional adapters and default params.
74
74
  *
75
75
  * @example
76
- * // contacts.hooks.ts
77
- * import { createResourceHooks } from '@void-snippets/react';
78
- * import { ContactsApis } from './contacts.api';
79
- *
80
- * // No generics needed — all types are inferred from ContactsApis
81
76
  * export const contactHooks = createResourceHooks('contacts', ContactsApis);
82
77
  *
83
- * // In a component:
84
- * const { contacts, isContactsLoading } = contactHooks.useList();
85
- * const { data } = contactHooks.useGet(id); // data: Contact.WithCreatedBy
86
- * const { create, update, delete: remove } = contactHooks.useMutations();
78
+ * // useList generic fixed shape
79
+ * const { list, isLoading, pagination, error, invalidate } = contactHooks.useList();
80
+ * // list is typed as Contact.Base[]
81
+ *
82
+ * // useGet
83
+ * const { item, isLoading, error, refetch } = contactHooks.useGet(id);
84
+ * // item is typed as Contact.WithCreatedBy ✅
85
+ *
86
+ * // useMutations
87
+ * const { create, update, remove } = contactHooks.useMutations();
87
88
  */
88
- declare function createResourceHooks<K extends string, S extends WithResourceTypes>(queryKeyPrefix: K, apiService: S, options?: CreateResourceHooksOptions<ListRaw<S>, Base<S>, SingleRaw<S>, Detail<S>>): {
89
- useList: (params?: TQueryParams) => UseListReturn<K, Base<S>>;
90
- useGet: (id: Id<S>, staleTime?: number) => {
91
- data: _tanstack_react_query.NoInfer<Detail<S>> | undefined;
92
- isLoading: boolean;
93
- error: Error | null;
94
- refetch: (options?: _tanstack_react_query.RefetchOptions) => Promise<_tanstack_react_query.QueryObserverResult<_tanstack_react_query.NoInfer<Detail<S>>, Error>>;
95
- };
89
+ declare function createResourceHooks<K extends string, S extends WithResourceTypes>(queryKeyPrefix: K, apiService: S, options?: VSResourceHooksOptions<ListRaw<S>, Base<S>, SingleRaw<S>, Detail<S>>): {
90
+ useList: (params?: VSQueryParams) => VSUseListReturn<Base<S>>;
91
+ useGet: (id: Id<S>, staleTime?: number) => VSUseGetReturn<Detail<S>>;
96
92
  useMutations: () => {
97
93
  create: _tanstack_react_query.UseMutationResult<Detail<S>, Error, Create<S>, unknown>;
98
94
  update: _tanstack_react_query.UseMutationResult<Detail<S>, Error, {
99
95
  _id: Id<S>;
100
96
  payload: Update<S>;
101
97
  }, unknown>;
102
- delete: _tanstack_react_query.UseMutationResult<Detail<S>, Error, Id<S>, unknown>;
98
+ remove: _tanstack_react_query.UseMutationResult<Detail<S>, Error, Id<S>, unknown>;
103
99
  };
104
- useInfinite: (params?: TQueryParams) => _tanstack_react_query.UseInfiniteQueryResult<_tanstack_react_query.InfiniteData<ResourceListResult<Base<S>>, unknown>, Error>;
100
+ useInfinite: (params?: VSQueryParams) => _tanstack_react_query.UseInfiniteQueryResult<_tanstack_react_query.InfiniteData<VSListResult<Base<S>>, unknown>, Error>;
105
101
  };
106
102
 
107
- export { type CreateResourceHooksOptions, type UseListReturn, createResourceHooks };
103
+ type VSAlertVariant = "success" | "info" | "error";
104
+ interface VSAlertState {
105
+ message: ReactNode | string;
106
+ type: VSAlertVariant;
107
+ isVisible: boolean;
108
+ }
109
+ /**
110
+ * Manages alert/toast message state with optional auto-hide.
111
+ *
112
+ * @param autoHideDuration - ms before alert hides automatically. Pass 0 to disable. Default: 3000
113
+ *
114
+ * @example
115
+ * const { alert, showAlert, hideAlert } = useAlertMessage();
116
+ * showAlert('Saved successfully!', 'success');
117
+ * showAlert(<b>Something went wrong</b>, 'error');
118
+ */
119
+ declare function useAlertMessage(autoHideDuration?: number): {
120
+ alert: VSAlertState;
121
+ showAlert: (message: ReactNode | string, type?: VSAlertVariant) => void;
122
+ hideAlert: () => void;
123
+ };
124
+
125
+ type VSAsyncStatus = "idle" | "pending" | "success" | "error";
126
+ interface VSAsyncState<T> {
127
+ data: T | null;
128
+ status: VSAsyncStatus;
129
+ error: Error | null;
130
+ }
131
+ interface VSUseAsyncStateReturn<T> extends VSAsyncState<T> {
132
+ isLoading: boolean;
133
+ isSuccess: boolean;
134
+ isError: boolean;
135
+ setData: (data: T | null) => void;
136
+ setError: (error: Error | null) => void;
137
+ reset: () => void;
138
+ /**
139
+ * Executes an async function, updates state, and returns a [err, data] tuple.
140
+ * Allows immediate result handling without try/catch.
141
+ *
142
+ * @example
143
+ * const [err, data] = await execute(() => ContactsApis.create(payload));
144
+ * if (err) return showAlert(err.message, 'error');
145
+ * showAlert('Created!', 'success');
146
+ */
147
+ execute: (asyncFn: () => Promise<T>, options?: {
148
+ onSuccess?: (data: T) => void;
149
+ onError?: (error: Error) => void;
150
+ }) => Promise<[Error, null] | [null, T]>;
151
+ }
152
+ /**
153
+ * Generic async state machine — tracks data, status, and error for any async operation.
154
+ * Pair with any async function: API calls, file reads, timers, etc.
155
+ *
156
+ * @param initialData - Optional initial data value. Default: null
157
+ *
158
+ * @example
159
+ * const { data, isLoading, isError, execute } = useAsyncState<User>();
160
+ *
161
+ * const handleSubmit = async () => {
162
+ * const [err, user] = await execute(() => fetchUser(id));
163
+ * if (err) return;
164
+ * console.log(user.name);
165
+ * };
166
+ */
167
+ declare function useAsyncState<T>(initialData?: T | null): VSUseAsyncStateReturn<T>;
168
+
169
+ /**
170
+ * Tracks elapsed time from a given start timestamp — useful for call durations,
171
+ * countdowns, or any elapsed-time display.
172
+ *
173
+ * @param startedAt - Unix timestamp in ms (e.g. Date.now()). Pass null/undefined to reset.
174
+ * @returns Formatted duration string "MM:SS"
175
+ *
176
+ * @example
177
+ * const duration = useCallTimer(call.startedAt);
178
+ * // duration → "02:45"
179
+ *
180
+ * // Reset when no active call
181
+ * const duration = useCallTimer(activeCall ? activeCall.startedAt : null);
182
+ */
183
+ declare function useCallTimer(startedAt?: number | null): string;
184
+
185
+ interface VSModalReturn<T> {
186
+ isOpen: boolean;
187
+ data: T | null;
188
+ isLoading: boolean;
189
+ openCreateModal: () => void;
190
+ openEditModal: (editData: T) => void;
191
+ setLoading: (loading: boolean) => void;
192
+ closeModal: () => void;
193
+ setModal: (open: boolean, editData?: T | null) => void;
194
+ }
195
+ /**
196
+ * Manages modal open/close state with optional data payload and loading state.
197
+ * Works for both create and edit modals — pass data to distinguish the mode.
198
+ *
199
+ * @typeParam T - The type of data the modal operates on (e.g. a Contact, User, etc.)
200
+ *
201
+ * @example
202
+ * const modal = useModal<Contact.Base>();
203
+ *
204
+ * modal.openCreateModal(); // data → null (create mode)
205
+ * modal.openEditModal(contact); // data → contact (edit mode)
206
+ *
207
+ * if (modal.data) {
208
+ * // Edit mode — modal.data is Contact.Base
209
+ * } else {
210
+ * // Create mode
211
+ * }
212
+ */
213
+ declare function useModal<T = unknown>(): VSModalReturn<T>;
214
+
215
+ interface VSPaginationReturn {
216
+ page: number;
217
+ limit: number;
218
+ onPaginationChange: (newPage: number, newLimit: number) => void;
219
+ resetPagination: () => void;
220
+ setPage: (page: number) => void;
221
+ setLimit: (limit: number) => void;
222
+ /** Ready-to-use query params object — pass directly to useList() */
223
+ queryParams: VSQueryParams;
224
+ }
225
+ /**
226
+ * Manages pagination state and produces a ready-to-use queryParams object
227
+ * compatible with createResourceHooks' useList() and useInfinite().
228
+ *
229
+ * @param initialPage - Starting page. Default: 1
230
+ * @param initialLimit - Items per page. Default: 10
231
+ *
232
+ * @example
233
+ * const { queryParams, onPaginationChange } = usePagination(1, 20);
234
+ *
235
+ * const { list, isLoading } = contactHooks.useList(queryParams);
236
+ *
237
+ * <Pagination onChange={onPaginationChange} total={pagination.totalDocuments} />
238
+ */
239
+ declare function usePagination(initialPage?: number, initialLimit?: number): VSPaginationReturn;
240
+
241
+ export { type VSAlertState, type VSAlertVariant, type VSAsyncStatus, type VSModalReturn, type VSPaginationReturn, type VSResourceHooksOptions, type VSUseAsyncStateReturn, type VSUseGetReturn, type VSUseListReturn, createResourceHooks, useAlertMessage, useAsyncState, useCallTimer, useModal, usePagination };
package/dist/index.js CHANGED
@@ -20,7 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- createResourceHooks: () => createResourceHooks
23
+ createResourceHooks: () => createResourceHooks,
24
+ useAlertMessage: () => useAlertMessage,
25
+ useAsyncState: () => useAsyncState,
26
+ useCallTimer: () => useCallTimer,
27
+ useModal: () => useModal,
28
+ usePagination: () => usePagination
24
29
  });
25
30
  module.exports = __toCommonJS(index_exports);
26
31
 
@@ -37,40 +42,35 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
37
42
  adapters = (0, import_core.createDefaultAdapters)(),
38
43
  defaultParams = DEFAULT_PAGINATION
39
44
  } = options;
40
- const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
41
- const capPrefix = capitalize(queryKeyPrefix);
42
45
  return {
43
46
  // -------------------------------------------------------------------------
44
- // useList — paginated list with invalidation helper
47
+ // useList — fixed generic shape: { list, isLoading, pagination, error, invalidate }
45
48
  // -------------------------------------------------------------------------
46
49
  useList: (params = defaultParams) => {
47
50
  var _a, _b, _c, _d, _e;
48
51
  const queryClient = (0, import_react_query.useQueryClient)();
49
- const queryKey = [queryKeyPrefix, params];
50
52
  const query = (0, import_react_query.useQuery)({
51
- queryKey,
53
+ queryKey: [queryKeyPrefix, params],
52
54
  queryFn: async () => {
53
55
  const raw = await service.list(params);
54
56
  return adapters.fromList(raw);
55
57
  }
56
58
  });
57
- const items = (_b = (_a = query.data) == null ? void 0 : _a.items) != null ? _b : [];
58
- const pagination = (_e = (_c = query.data) == null ? void 0 : _c.pagination) != null ? _e : {
59
- page: 1,
60
- limit: (_d = defaultParams.limit) != null ? _d : 10,
61
- totalPages: 0,
62
- totalDocuments: 0
63
- };
64
59
  return {
65
- [queryKeyPrefix]: items,
66
- pagination,
67
- [`is${capPrefix}Loading`]: query.isLoading || query.isFetching,
68
- [`${queryKeyPrefix}Error`]: query.error,
69
- [`invalidate${capPrefix}`]: () => queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] })
60
+ list: (_b = (_a = query.data) == null ? void 0 : _a.items) != null ? _b : [],
61
+ pagination: (_e = (_c = query.data) == null ? void 0 : _c.pagination) != null ? _e : {
62
+ page: 1,
63
+ limit: (_d = defaultParams.limit) != null ? _d : 10,
64
+ totalPages: 0,
65
+ totalDocuments: 0
66
+ },
67
+ isLoading: query.isLoading || query.isFetching,
68
+ error: query.error,
69
+ invalidate: () => queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] })
70
70
  };
71
71
  },
72
72
  // -------------------------------------------------------------------------
73
- // useGet — single item by id
73
+ // useGet — { item, isLoading, error, refetch }
74
74
  // -------------------------------------------------------------------------
75
75
  useGet: (id, staleTime = 3e4) => {
76
76
  const query = (0, import_react_query.useQuery)({
@@ -83,14 +83,14 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
83
83
  staleTime
84
84
  });
85
85
  return {
86
- data: query.data,
86
+ item: query.data,
87
87
  isLoading: query.isLoading || query.isFetching,
88
88
  error: query.error,
89
89
  refetch: query.refetch
90
90
  };
91
91
  },
92
92
  // -------------------------------------------------------------------------
93
- // useMutations — create / update / delete with auto-invalidation
93
+ // useMutations — create / update / remove (not delete reserved keyword)
94
94
  // -------------------------------------------------------------------------
95
95
  useMutations: () => {
96
96
  const queryClient = (0, import_react_query.useQueryClient)();
@@ -109,7 +109,7 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
109
109
  },
110
110
  onSuccess: invalidate
111
111
  });
112
- const deleteMutation = (0, import_react_query.useMutation)({
112
+ const removeMutation = (0, import_react_query.useMutation)({
113
113
  mutationFn: async (_id) => {
114
114
  const raw = await service.delete(_id);
115
115
  return adapters.fromSingle(raw);
@@ -119,7 +119,7 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
119
119
  return {
120
120
  create: createMutation,
121
121
  update: updateMutation,
122
- delete: deleteMutation
122
+ remove: removeMutation
123
123
  };
124
124
  },
125
125
  // -------------------------------------------------------------------------
@@ -146,8 +146,166 @@ function createResourceHooks(queryKeyPrefix, apiService, options = {}) {
146
146
  }
147
147
  };
148
148
  }
149
+
150
+ // src/hooks/useAlertMessage.ts
151
+ var import_react = require("react");
152
+ function useAlertMessage(autoHideDuration = 3e3) {
153
+ const [alert, setAlert] = (0, import_react.useState)({
154
+ message: null,
155
+ type: "info",
156
+ isVisible: false
157
+ });
158
+ const showAlert = (0, import_react.useCallback)(
159
+ (message, type = "info") => {
160
+ setAlert({ message, type, isVisible: true });
161
+ if (autoHideDuration) {
162
+ setTimeout(() => {
163
+ setAlert((prev) => ({ ...prev, isVisible: false }));
164
+ }, autoHideDuration);
165
+ }
166
+ },
167
+ [autoHideDuration]
168
+ );
169
+ const hideAlert = (0, import_react.useCallback)(() => {
170
+ setAlert((prev) => ({ ...prev, isVisible: false }));
171
+ }, []);
172
+ return { alert, showAlert, hideAlert };
173
+ }
174
+
175
+ // src/hooks/useAsyncState.ts
176
+ var import_react2 = require("react");
177
+ var import_core2 = require("@void-snippets/core");
178
+ function useAsyncState(initialData = null) {
179
+ const [state, setState] = (0, import_react2.useState)({
180
+ data: initialData,
181
+ status: "idle",
182
+ error: null
183
+ });
184
+ const setData = (0, import_react2.useCallback)((data) => {
185
+ setState({ data, status: "success", error: null });
186
+ }, []);
187
+ const setError = (0, import_react2.useCallback)((error) => {
188
+ setState((prev) => ({ ...prev, error, status: "error" }));
189
+ }, []);
190
+ const reset = (0, import_react2.useCallback)(() => {
191
+ setState({ data: initialData, status: "idle", error: null });
192
+ }, [initialData]);
193
+ const execute = (0, import_react2.useCallback)(
194
+ async (asyncFn, options) => {
195
+ var _a, _b;
196
+ setState((prev) => ({ ...prev, status: "pending", error: null }));
197
+ const [err, res] = await (0, import_core2.catchError)(asyncFn());
198
+ if (err) {
199
+ setState((prev) => ({ ...prev, status: "error", error: err }));
200
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, err);
201
+ return [err, null];
202
+ }
203
+ setState({ data: res, status: "success", error: null });
204
+ (_b = options == null ? void 0 : options.onSuccess) == null ? void 0 : _b.call(options, res);
205
+ return [null, res];
206
+ },
207
+ []
208
+ );
209
+ const flags = (0, import_react2.useMemo)(
210
+ () => ({
211
+ isLoading: state.status === "pending",
212
+ isSuccess: state.status === "success",
213
+ isError: state.status === "error"
214
+ }),
215
+ [state.status]
216
+ );
217
+ return { ...state, ...flags, setData, setError, reset, execute };
218
+ }
219
+
220
+ // src/hooks/useCallTimer.ts
221
+ var import_react3 = require("react");
222
+ function useCallTimer(startedAt) {
223
+ const [duration, setDuration] = (0, import_react3.useState)("00:00");
224
+ (0, import_react3.useEffect)(() => {
225
+ if (!startedAt) {
226
+ setDuration("00:00");
227
+ return;
228
+ }
229
+ const interval = setInterval(() => {
230
+ const diffInSeconds = Math.floor((Date.now() - startedAt) / 1e3);
231
+ const minutes = Math.floor(diffInSeconds / 60).toString().padStart(2, "0");
232
+ const seconds = (diffInSeconds % 60).toString().padStart(2, "0");
233
+ setDuration(`${minutes}:${seconds}`);
234
+ }, 1e3);
235
+ return () => clearInterval(interval);
236
+ }, [startedAt]);
237
+ return duration;
238
+ }
239
+
240
+ // src/hooks/useModal.ts
241
+ var import_react4 = require("react");
242
+ function useModal() {
243
+ const [isOpen, setIsOpen] = (0, import_react4.useState)(false);
244
+ const [data, setData] = (0, import_react4.useState)(null);
245
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
246
+ const openCreateModal = (0, import_react4.useCallback)(() => {
247
+ setIsOpen(true);
248
+ setData(null);
249
+ }, []);
250
+ const openEditModal = (0, import_react4.useCallback)((editData) => {
251
+ setIsOpen(true);
252
+ setData(editData);
253
+ }, []);
254
+ const setLoading = (0, import_react4.useCallback)((loading) => {
255
+ setIsLoading(loading);
256
+ }, []);
257
+ const closeModal = (0, import_react4.useCallback)(() => {
258
+ setIsOpen(false);
259
+ setData(null);
260
+ }, []);
261
+ const setModal = (0, import_react4.useCallback)((open, editData) => {
262
+ setIsOpen(open);
263
+ setData(editData != null ? editData : null);
264
+ }, []);
265
+ return {
266
+ isOpen,
267
+ data,
268
+ isLoading,
269
+ openCreateModal,
270
+ openEditModal,
271
+ setLoading,
272
+ closeModal,
273
+ setModal
274
+ };
275
+ }
276
+
277
+ // src/hooks/usePagination.ts
278
+ var import_react5 = require("react");
279
+ function usePagination(initialPage = 1, initialLimit = 10) {
280
+ const [page, setPage] = (0, import_react5.useState)(initialPage);
281
+ const [limit, setLimit] = (0, import_react5.useState)(initialLimit);
282
+ const onPaginationChange = (0, import_react5.useCallback)(
283
+ (newPage, newLimit) => {
284
+ setPage(newPage);
285
+ setLimit(newLimit);
286
+ },
287
+ []
288
+ );
289
+ const resetPagination = (0, import_react5.useCallback)(() => {
290
+ setPage(1);
291
+ }, []);
292
+ return {
293
+ page,
294
+ limit,
295
+ onPaginationChange,
296
+ resetPagination,
297
+ setPage,
298
+ setLimit,
299
+ queryParams: { page, limit }
300
+ };
301
+ }
149
302
  // Annotate the CommonJS export names for ESM import in node:
150
303
  0 && (module.exports = {
151
- createResourceHooks
304
+ createResourceHooks,
305
+ useAlertMessage,
306
+ useAsyncState,
307
+ useCallTimer,
308
+ useModal,
309
+ usePagination
152
310
  });
153
311
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/hooks/createResourceHooks.ts"],"sourcesContent":["export { createResourceHooks } from \"./hooks/createResourceHooks\";\nexport type {\n UseListReturn,\n CreateResourceHooksOptions,\n} from \"./hooks/createResourceHooks\";\n","import {\n useInfiniteQuery,\n useMutation,\n useQuery,\n useQueryClient,\n} from \"@tanstack/react-query\";\nimport type { ResourceService } from \"@void-snippets/client\";\nimport type {\n ResourceAdapters,\n ResourceListResult,\n TPagination,\n TQueryParams,\n TDefaultPaginatedResponse,\n TDefaultSingleResponse,\n} from \"@void-snippets/core\";\nimport { createDefaultAdapters } from \"@void-snippets/core\";\n\n// ============================================================================\n// TYPE HELPERS\n// ============================================================================\n\ntype CapitalizeStr<S extends string> = S extends `${infer F}${infer Rest}`\n ? `${Uppercase<F>}${Rest}`\n : S;\n\nconst DEFAULT_PAGINATION: TQueryParams = {\n page: 1,\n limit: 10,\n};\n\n// ============================================================================\n// PHANTOM TYPE CONSTRAINT\n//\n// We constrain S only on the __types phantom property (which is readonly,\n// i.e. covariant) so that ResourceService<string, ...> is assignable to\n// WithResourceTypes. This avoids the variance issue that arises when\n// constraining against method parameter types (contravariant positions).\n//\n// Inside the function we cast apiService once to a fully-typed ResourceService\n// using the phantom generics — this is safe because S IS a ResourceService.\n// ============================================================================\n\ninterface WithResourceTypes {\n readonly __types: {\n id: unknown;\n base: unknown;\n detail: unknown;\n create: unknown;\n update: unknown;\n listRaw: unknown;\n singleRaw: unknown;\n };\n}\n\n// Aliases for reading phantom types from S — keeps the function body readable\ntype Id<S extends WithResourceTypes> = S[\"__types\"][\"id\"];\ntype Base<S extends WithResourceTypes> = S[\"__types\"][\"base\"];\ntype Detail<S extends WithResourceTypes> = S[\"__types\"][\"detail\"];\ntype Create<S extends WithResourceTypes> = S[\"__types\"][\"create\"];\ntype Update<S extends WithResourceTypes> = S[\"__types\"][\"update\"];\ntype ListRaw<S extends WithResourceTypes> = S[\"__types\"][\"listRaw\"];\ntype SingleRaw<S extends WithResourceTypes> = S[\"__types\"][\"singleRaw\"];\n\n// ============================================================================\n// RETURN TYPE — useList\n// Dynamically named keys based on the queryKeyPrefix (e.g. \"contacts\"):\n// { contacts: TBase[], isContactsLoading: boolean, contactsError: Error | null, invalidateContacts: () => void }\n// ============================================================================\n\nexport type UseListReturn<K extends string, TBase> = {\n [P in K]: TBase[];\n} & {\n pagination: TPagination;\n} & {\n [P in `is${CapitalizeStr<K>}Loading`]: boolean;\n} & {\n [P in `${K}Error`]: Error | null;\n} & {\n [P in `invalidate${CapitalizeStr<K>}`]: () => void;\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport interface CreateResourceHooksOptions<\n TListRaw,\n TBase,\n TSingleRaw,\n TDetail,\n> {\n /**\n * Adapters map your API's raw response shapes to the library's internal\n * format. Omit this entirely if your API matches the default shape:\n * List: { data: { items, page, limit, totalPages, totalDocuments } }\n * Single: { data: <item> }\n *\n * @example\n * createResourceHooks(\"contacts\", ContactsApis, {\n * adapters: {\n * fromList: (raw) => ({\n * items: raw.results,\n * pagination: {\n * page: raw.meta.page,\n * limit: raw.meta.perPage,\n * totalPages: raw.meta.lastPage,\n * totalDocuments: raw.meta.total,\n * },\n * }),\n * fromSingle: (raw) => raw.payload,\n * },\n * })\n */\n adapters?: ResourceAdapters<TListRaw, TBase, TSingleRaw, TDetail>;\n\n /**\n * Default params passed to useList and useInfinite when none are provided.\n * @default { page: 1, limit: 10 }\n */\n defaultParams?: TQueryParams;\n}\n\n// ============================================================================\n// FACTORY\n// ============================================================================\n\n/**\n * Creates a set of TanStack Query hooks for a resource.\n * All types are fully inferred from the `apiService` instance — no generics\n * need to be passed manually.\n *\n * @param queryKeyPrefix - TanStack Query cache key prefix and the base name\n * for the returned hook properties.\n * e.g. \"contacts\" → { contacts, isContactsLoading, ... }\n * @param apiService - An instance of ResourceService (or a subclass).\n * @param options - Optional adapters and default params.\n *\n * @example\n * // contacts.hooks.ts\n * import { createResourceHooks } from '@void-snippets/react';\n * import { ContactsApis } from './contacts.api';\n *\n * // No generics needed — all types are inferred from ContactsApis\n * export const contactHooks = createResourceHooks('contacts', ContactsApis);\n *\n * // In a component:\n * const { contacts, isContactsLoading } = contactHooks.useList();\n * const { data } = contactHooks.useGet(id); // data: Contact.WithCreatedBy\n * const { create, update, delete: remove } = contactHooks.useMutations();\n */\nexport function createResourceHooks<\n K extends string,\n S extends WithResourceTypes,\n>(\n queryKeyPrefix: K,\n apiService: S,\n options: CreateResourceHooksOptions<\n ListRaw<S>,\n Base<S>,\n SingleRaw<S>,\n Detail<S>\n > = {},\n) {\n // One safe cast: S is guaranteed to be a ResourceService subclass because\n // __types is only present on ResourceService. Casting here lets us call\n // the service methods with the correct types extracted from the phantom.\n const service = apiService as unknown as ResourceService<\n Id<S>,\n Base<S>,\n Detail<S>,\n Create<S>,\n Update<S>,\n ListRaw<S>,\n SingleRaw<S>\n >;\n\n const {\n adapters = createDefaultAdapters<Base<S>, Detail<S>>() as ResourceAdapters<\n TDefaultPaginatedResponse<Base<S>>,\n Base<S>,\n TDefaultSingleResponse<Detail<S>>,\n Detail<S>\n > as ResourceAdapters<ListRaw<S>, Base<S>, SingleRaw<S>, Detail<S>>,\n defaultParams = DEFAULT_PAGINATION,\n } = options;\n\n const capitalize = (str: string) =>\n str.charAt(0).toUpperCase() + str.slice(1);\n const capPrefix = capitalize(queryKeyPrefix);\n\n return {\n // -------------------------------------------------------------------------\n // useList — paginated list with invalidation helper\n // -------------------------------------------------------------------------\n useList: (\n params: TQueryParams = defaultParams,\n ): UseListReturn<K, Base<S>> => {\n const queryClient = useQueryClient();\n const queryKey = [queryKeyPrefix, params];\n\n const query = useQuery<ResourceListResult<Base<S>>, Error>({\n queryKey,\n queryFn: async () => {\n const raw = await service.list(params);\n return adapters.fromList(raw as ListRaw<S>);\n },\n });\n\n const items = query.data?.items ?? [];\n const pagination: TPagination = query.data?.pagination ?? {\n page: 1,\n limit: defaultParams.limit ?? 10,\n totalPages: 0,\n totalDocuments: 0,\n };\n\n return {\n [queryKeyPrefix]: items,\n pagination,\n [`is${capPrefix}Loading`]: query.isLoading || query.isFetching,\n [`${queryKeyPrefix}Error`]: query.error,\n [`invalidate${capPrefix}`]: () =>\n queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] }),\n } as UseListReturn<K, Base<S>>;\n },\n\n // -------------------------------------------------------------------------\n // useGet — single item by id\n // -------------------------------------------------------------------------\n useGet: (id: Id<S>, staleTime = 30_000) => {\n const query = useQuery<Detail<S>, Error>({\n queryKey: [queryKeyPrefix, id],\n queryFn: async () => {\n const raw = await service.get(id);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n enabled: id !== undefined && id !== null && id !== \"\",\n staleTime,\n });\n\n return {\n data: query.data,\n isLoading: query.isLoading || query.isFetching,\n error: query.error,\n refetch: query.refetch,\n };\n },\n\n // -------------------------------------------------------------------------\n // useMutations — create / update / delete with auto-invalidation\n // -------------------------------------------------------------------------\n useMutations: () => {\n const queryClient = useQueryClient();\n const invalidate = () =>\n queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] });\n\n const createMutation = useMutation<Detail<S>, Error, Create<S>>({\n mutationFn: async (payload) => {\n const raw = await service.create(payload);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n onSuccess: invalidate,\n });\n\n const updateMutation = useMutation<\n Detail<S>,\n Error,\n { _id: Id<S>; payload: Update<S> }\n >({\n mutationFn: async ({ _id, payload }) => {\n const raw = await service.update(_id, payload);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n onSuccess: invalidate,\n });\n\n const deleteMutation = useMutation<Detail<S>, Error, Id<S>>({\n mutationFn: async (_id) => {\n const raw = await service.delete(_id);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n onSuccess: invalidate,\n });\n\n return {\n create: createMutation,\n update: updateMutation,\n delete: deleteMutation,\n };\n },\n\n // -------------------------------------------------------------------------\n // useInfinite — infinite scroll / load more\n // -------------------------------------------------------------------------\n useInfinite: (params: TQueryParams = defaultParams) => {\n return useInfiniteQuery<ResourceListResult<Base<S>>, Error>({\n queryKey: [queryKeyPrefix, \"INFINITE\", params],\n queryFn: async ({ pageParam }) => {\n const raw = await service.list({\n ...params,\n page: pageParam as number,\n limit: params.limit ?? 20,\n });\n return adapters.fromList(raw as ListRaw<S>);\n },\n getNextPageParam: (lastPage) => {\n const { page, totalPages } = lastPage.pagination;\n return page < totalPages ? page + 1 : undefined;\n },\n initialPageParam: 1,\n });\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/hooks/createResourceHooks.ts","../src/hooks/useAlertMessage.ts","../src/hooks/useAsyncState.ts","../src/hooks/useCallTimer.ts","../src/hooks/useModal.ts","../src/hooks/usePagination.ts"],"sourcesContent":["// Resource hooks factory\nexport { createResourceHooks } from \"./hooks/createResourceHooks\";\nexport type {\n VSUseListReturn,\n VSUseGetReturn,\n VSResourceHooksOptions,\n} from \"./hooks/createResourceHooks\";\n\n// General-purpose React hooks\nexport { useAlertMessage } from \"./hooks/useAlertMessage\";\nexport type { VSAlertVariant, VSAlertState } from \"./hooks/useAlertMessage\";\n\nexport { useAsyncState } from \"./hooks/useAsyncState\";\nexport type {\n VSAsyncStatus,\n VSUseAsyncStateReturn,\n} from \"./hooks/useAsyncState\";\n\nexport { useCallTimer } from \"./hooks/useCallTimer\";\n\nexport { useModal } from \"./hooks/useModal\";\nexport type { VSModalReturn } from \"./hooks/useModal\";\n\nexport { usePagination } from \"./hooks/usePagination\";\nexport type { VSPaginationReturn } from \"./hooks/usePagination\";\n","import {\n useInfiniteQuery,\n useMutation,\n useQuery,\n useQueryClient,\n} from \"@tanstack/react-query\";\nimport type { ResourceService } from \"@void-snippets/client\";\nimport type {\n VSAdapters,\n VSListResult,\n VSPagination,\n VSQueryParams,\n VSDefaultPaginatedResponse,\n VSDefaultSingleResponse,\n} from \"@void-snippets/core\";\nimport { createDefaultAdapters } from \"@void-snippets/core\";\n\n// ============================================================================\n// TYPE HELPERS\n// ============================================================================\n\nconst DEFAULT_PAGINATION: VSQueryParams = {\n page: 1,\n limit: 10,\n};\n\n// ============================================================================\n// PHANTOM TYPE CONSTRAINT\n//\n// We constrain S only on the __types phantom property (readonly = covariant)\n// so ResourceService<string, ...> is always assignable to WithResourceTypes.\n// This avoids the variance issue from constraining method parameter types.\n// ============================================================================\n\ninterface WithResourceTypes {\n readonly __types: {\n id: unknown;\n base: unknown;\n detail: unknown;\n create: unknown;\n update: unknown;\n listRaw: unknown;\n singleRaw: unknown;\n };\n}\n\ntype Id<S extends WithResourceTypes> = S[\"__types\"][\"id\"];\ntype Base<S extends WithResourceTypes> = S[\"__types\"][\"base\"];\ntype Detail<S extends WithResourceTypes> = S[\"__types\"][\"detail\"];\ntype Create<S extends WithResourceTypes> = S[\"__types\"][\"create\"];\ntype Update<S extends WithResourceTypes> = S[\"__types\"][\"update\"];\ntype ListRaw<S extends WithResourceTypes> = S[\"__types\"][\"listRaw\"];\ntype SingleRaw<S extends WithResourceTypes> = S[\"__types\"][\"singleRaw\"];\n\n// ============================================================================\n// RETURN TYPES\n// ============================================================================\n\nexport interface VSUseListReturn<TBase> {\n list: TBase[];\n pagination: VSPagination;\n isLoading: boolean;\n error: Error | null;\n invalidate: () => void;\n}\n\nexport interface VSUseGetReturn<TDetail> {\n item: TDetail | undefined;\n isLoading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport interface VSResourceHooksOptions<TListRaw, TBase, TSingleRaw, TDetail> {\n /**\n * Adapters map your API's raw response to the library's internal format.\n * Omit if your API matches the default shape:\n * List: { data: { items, page, limit, totalPages, totalDocuments } }\n * Single: { data: <item> }\n *\n * @example\n * createResourceHooks(\"contacts\", ContactsApis, {\n * adapters: {\n * fromList: (raw) => ({\n * items: raw.results,\n * pagination: {\n * page: raw.meta.page,\n * limit: raw.meta.perPage,\n * totalPages: raw.meta.lastPage,\n * totalDocuments: raw.meta.total,\n * },\n * }),\n * fromSingle: (raw) => raw.payload,\n * },\n * })\n */\n adapters?: VSAdapters<TListRaw, TBase, TSingleRaw, TDetail>;\n\n /**\n * Default params passed to useList and useInfinite when none are provided.\n * @default { page: 1, limit: 10 }\n */\n defaultParams?: VSQueryParams;\n}\n\n// ============================================================================\n// FACTORY\n// ============================================================================\n\n/**\n * Creates a set of TanStack Query hooks for a resource.\n * All types are fully inferred from the `apiService` instance — no generics needed.\n *\n * @param queryKeyPrefix - TanStack Query cache key. Used to scope the cache\n * and for auto-invalidation. e.g. \"contacts\"\n * @param apiService - An instance of ResourceService (or a subclass).\n * @param options - Optional adapters and default params.\n *\n * @example\n * export const contactHooks = createResourceHooks('contacts', ContactsApis);\n *\n * // useList — generic fixed shape\n * const { list, isLoading, pagination, error, invalidate } = contactHooks.useList();\n * // list is typed as Contact.Base[] ✅\n *\n * // useGet\n * const { item, isLoading, error, refetch } = contactHooks.useGet(id);\n * // item is typed as Contact.WithCreatedBy ✅\n *\n * // useMutations\n * const { create, update, remove } = contactHooks.useMutations();\n */\nexport function createResourceHooks<\n K extends string,\n S extends WithResourceTypes,\n>(\n queryKeyPrefix: K,\n apiService: S,\n options: VSResourceHooksOptions<\n ListRaw<S>,\n Base<S>,\n SingleRaw<S>,\n Detail<S>\n > = {},\n) {\n const service = apiService as unknown as ResourceService<\n Id<S>, Base<S>, Detail<S>, Create<S>, Update<S>, ListRaw<S>, SingleRaw<S>\n >;\n\n const {\n adapters = createDefaultAdapters<Base<S>, Detail<S>>() as VSAdapters<\n VSDefaultPaginatedResponse<Base<S>>,\n Base<S>,\n VSDefaultSingleResponse<Detail<S>>,\n Detail<S>\n > as VSAdapters<ListRaw<S>, Base<S>, SingleRaw<S>, Detail<S>>,\n defaultParams = DEFAULT_PAGINATION,\n } = options;\n\n return {\n // -------------------------------------------------------------------------\n // useList — fixed generic shape: { list, isLoading, pagination, error, invalidate }\n // -------------------------------------------------------------------------\n useList: (\n params: VSQueryParams = defaultParams,\n ): VSUseListReturn<Base<S>> => {\n const queryClient = useQueryClient();\n\n const query = useQuery<VSListResult<Base<S>>, Error>({\n queryKey: [queryKeyPrefix, params],\n queryFn: async () => {\n const raw = await service.list(params);\n return adapters.fromList(raw as ListRaw<S>);\n },\n });\n\n return {\n list: query.data?.items ?? [],\n pagination: query.data?.pagination ?? {\n page: 1,\n limit: defaultParams.limit ?? 10,\n totalPages: 0,\n totalDocuments: 0,\n },\n isLoading: query.isLoading || query.isFetching,\n error: query.error,\n invalidate: () =>\n queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] }),\n };\n },\n\n // -------------------------------------------------------------------------\n // useGet — { item, isLoading, error, refetch }\n // -------------------------------------------------------------------------\n useGet: (id: Id<S>, staleTime = 30_000): VSUseGetReturn<Detail<S>> => {\n const query = useQuery<Detail<S>, Error>({\n queryKey: [queryKeyPrefix, id],\n queryFn: async () => {\n const raw = await service.get(id);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n enabled: id !== undefined && id !== null && id !== \"\",\n staleTime,\n });\n\n return {\n item: query.data,\n isLoading: query.isLoading || query.isFetching,\n error: query.error,\n refetch: query.refetch,\n };\n },\n\n // -------------------------------------------------------------------------\n // useMutations — create / update / remove (not delete — reserved keyword)\n // -------------------------------------------------------------------------\n useMutations: () => {\n const queryClient = useQueryClient();\n const invalidate = () =>\n queryClient.invalidateQueries({ queryKey: [queryKeyPrefix] });\n\n const createMutation = useMutation<Detail<S>, Error, Create<S>>({\n mutationFn: async (payload) => {\n const raw = await service.create(payload);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n onSuccess: invalidate,\n });\n\n const updateMutation = useMutation<\n Detail<S>,\n Error,\n { _id: Id<S>; payload: Update<S> }\n >({\n mutationFn: async ({ _id, payload }) => {\n const raw = await service.update(_id, payload);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n onSuccess: invalidate,\n });\n\n const removeMutation = useMutation<Detail<S>, Error, Id<S>>({\n mutationFn: async (_id) => {\n const raw = await service.delete(_id);\n return adapters.fromSingle(raw as SingleRaw<S>);\n },\n onSuccess: invalidate,\n });\n\n return {\n create: createMutation,\n update: updateMutation,\n remove: removeMutation,\n };\n },\n\n // -------------------------------------------------------------------------\n // useInfinite — infinite scroll / load more\n // -------------------------------------------------------------------------\n useInfinite: (params: VSQueryParams = defaultParams) => {\n return useInfiniteQuery<VSListResult<Base<S>>, Error>({\n queryKey: [queryKeyPrefix, \"INFINITE\", params],\n queryFn: async ({ pageParam }) => {\n const raw = await service.list({\n ...params,\n page: pageParam as number,\n limit: params.limit ?? 20,\n });\n return adapters.fromList(raw as ListRaw<S>);\n },\n getNextPageParam: (lastPage) => {\n const { page, totalPages } = lastPage.pagination;\n return page < totalPages ? page + 1 : undefined;\n },\n initialPageParam: 1,\n });\n },\n };\n}\n","import { type ReactNode, useCallback, useState } from \"react\";\n\nexport type VSAlertVariant = \"success\" | \"info\" | \"error\";\n\nexport interface VSAlertState {\n message: ReactNode | string;\n type: VSAlertVariant;\n isVisible: boolean;\n}\n\n/**\n * Manages alert/toast message state with optional auto-hide.\n *\n * @param autoHideDuration - ms before alert hides automatically. Pass 0 to disable. Default: 3000\n *\n * @example\n * const { alert, showAlert, hideAlert } = useAlertMessage();\n * showAlert('Saved successfully!', 'success');\n * showAlert(<b>Something went wrong</b>, 'error');\n */\nexport function useAlertMessage(autoHideDuration = 3000) {\n const [alert, setAlert] = useState<VSAlertState>({\n message: null,\n type: \"info\",\n isVisible: false,\n });\n\n const showAlert = useCallback(\n (message: ReactNode | string, type: VSAlertVariant = \"info\") => {\n setAlert({ message, type, isVisible: true });\n if (autoHideDuration) {\n setTimeout(() => {\n setAlert((prev) => ({ ...prev, isVisible: false }));\n }, autoHideDuration);\n }\n },\n [autoHideDuration]\n );\n\n const hideAlert = useCallback(() => {\n setAlert((prev) => ({ ...prev, isVisible: false }));\n }, []);\n\n return { alert, showAlert, hideAlert };\n}\n","import { useCallback, useMemo, useState } from \"react\";\nimport { catchError } from \"@void-snippets/core\";\n\nexport type VSAsyncStatus = \"idle\" | \"pending\" | \"success\" | \"error\";\n\ninterface VSAsyncState<T> {\n data: T | null;\n status: VSAsyncStatus;\n error: Error | null;\n}\n\nexport interface VSUseAsyncStateReturn<T> extends VSAsyncState<T> {\n isLoading: boolean;\n isSuccess: boolean;\n isError: boolean;\n\n setData: (data: T | null) => void;\n setError: (error: Error | null) => void;\n reset: () => void;\n\n /**\n * Executes an async function, updates state, and returns a [err, data] tuple.\n * Allows immediate result handling without try/catch.\n *\n * @example\n * const [err, data] = await execute(() => ContactsApis.create(payload));\n * if (err) return showAlert(err.message, 'error');\n * showAlert('Created!', 'success');\n */\n execute: (\n asyncFn: () => Promise<T>,\n options?: {\n onSuccess?: (data: T) => void;\n onError?: (error: Error) => void;\n }\n ) => Promise<[Error, null] | [null, T]>;\n}\n\n/**\n * Generic async state machine — tracks data, status, and error for any async operation.\n * Pair with any async function: API calls, file reads, timers, etc.\n *\n * @param initialData - Optional initial data value. Default: null\n *\n * @example\n * const { data, isLoading, isError, execute } = useAsyncState<User>();\n *\n * const handleSubmit = async () => {\n * const [err, user] = await execute(() => fetchUser(id));\n * if (err) return;\n * console.log(user.name);\n * };\n */\nexport function useAsyncState<T>(\n initialData: T | null = null\n): VSUseAsyncStateReturn<T> {\n const [state, setState] = useState<VSAsyncState<T>>({\n data: initialData,\n status: \"idle\",\n error: null,\n });\n\n const setData = useCallback((data: T | null) => {\n setState({ data, status: \"success\", error: null });\n }, []);\n\n const setError = useCallback((error: Error | null) => {\n setState((prev) => ({ ...prev, error, status: \"error\" }));\n }, []);\n\n const reset = useCallback(() => {\n setState({ data: initialData, status: \"idle\", error: null });\n }, [initialData]);\n\n const execute = useCallback(\n async (\n asyncFn: () => Promise<T>,\n options?: {\n onSuccess?: (data: T) => void;\n onError?: (error: Error) => void;\n }\n ): Promise<[Error, null] | [null, T]> => {\n setState((prev) => ({ ...prev, status: \"pending\", error: null }));\n\n const [err, res] = await catchError(asyncFn());\n\n if (err) {\n setState((prev) => ({ ...prev, status: \"error\", error: err }));\n options?.onError?.(err);\n return [err, null];\n }\n\n setState({ data: res as T, status: \"success\", error: null });\n options?.onSuccess?.(res as T);\n return [null, res as T];\n },\n []\n );\n\n const flags = useMemo(\n () => ({\n isLoading: state.status === \"pending\",\n isSuccess: state.status === \"success\",\n isError: state.status === \"error\",\n }),\n [state.status]\n );\n\n return { ...state, ...flags, setData, setError, reset, execute };\n}\n","import { useEffect, useState } from \"react\";\n\n/**\n * Tracks elapsed time from a given start timestamp — useful for call durations,\n * countdowns, or any elapsed-time display.\n *\n * @param startedAt - Unix timestamp in ms (e.g. Date.now()). Pass null/undefined to reset.\n * @returns Formatted duration string \"MM:SS\"\n *\n * @example\n * const duration = useCallTimer(call.startedAt);\n * // duration → \"02:45\"\n *\n * // Reset when no active call\n * const duration = useCallTimer(activeCall ? activeCall.startedAt : null);\n */\nexport function useCallTimer(startedAt?: number | null): string {\n const [duration, setDuration] = useState(\"00:00\");\n\n useEffect(() => {\n if (!startedAt) {\n setDuration(\"00:00\");\n return;\n }\n\n const interval = setInterval(() => {\n const diffInSeconds = Math.floor((Date.now() - startedAt) / 1000);\n const minutes = Math.floor(diffInSeconds / 60).toString().padStart(2, \"0\");\n const seconds = (diffInSeconds % 60).toString().padStart(2, \"0\");\n setDuration(`${minutes}:${seconds}`);\n }, 1000);\n\n return () => clearInterval(interval);\n }, [startedAt]);\n\n return duration;\n}\n","import { useCallback, useState } from \"react\";\n\nexport interface VSModalReturn<T> {\n isOpen: boolean;\n data: T | null;\n isLoading: boolean;\n openCreateModal: () => void;\n openEditModal: (editData: T) => void;\n setLoading: (loading: boolean) => void;\n closeModal: () => void;\n setModal: (open: boolean, editData?: T | null) => void;\n}\n\n/**\n * Manages modal open/close state with optional data payload and loading state.\n * Works for both create and edit modals — pass data to distinguish the mode.\n *\n * @typeParam T - The type of data the modal operates on (e.g. a Contact, User, etc.)\n *\n * @example\n * const modal = useModal<Contact.Base>();\n *\n * modal.openCreateModal(); // data → null (create mode)\n * modal.openEditModal(contact); // data → contact (edit mode)\n *\n * if (modal.data) {\n * // Edit mode — modal.data is Contact.Base\n * } else {\n * // Create mode\n * }\n */\nexport function useModal<T = unknown>(): VSModalReturn<T> {\n const [isOpen, setIsOpen] = useState(false);\n const [data, setData] = useState<T | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n\n const openCreateModal = useCallback(() => {\n setIsOpen(true);\n setData(null);\n }, []);\n\n const openEditModal = useCallback((editData: T) => {\n setIsOpen(true);\n setData(editData);\n }, []);\n\n const setLoading = useCallback((loading: boolean) => {\n setIsLoading(loading);\n }, []);\n\n const closeModal = useCallback(() => {\n setIsOpen(false);\n setData(null);\n }, []);\n\n const setModal = useCallback((open: boolean, editData?: T | null) => {\n setIsOpen(open);\n setData(editData ?? null);\n }, []);\n\n return {\n isOpen,\n data,\n isLoading,\n openCreateModal,\n openEditModal,\n setLoading,\n closeModal,\n setModal,\n };\n}\n","import { useCallback, useState } from \"react\";\nimport type { VSQueryParams } from \"@void-snippets/core\";\n\nexport interface VSPaginationReturn {\n page: number;\n limit: number;\n onPaginationChange: (newPage: number, newLimit: number) => void;\n resetPagination: () => void;\n setPage: (page: number) => void;\n setLimit: (limit: number) => void;\n /** Ready-to-use query params object — pass directly to useList() */\n queryParams: VSQueryParams;\n}\n\n/**\n * Manages pagination state and produces a ready-to-use queryParams object\n * compatible with createResourceHooks' useList() and useInfinite().\n *\n * @param initialPage - Starting page. Default: 1\n * @param initialLimit - Items per page. Default: 10\n *\n * @example\n * const { queryParams, onPaginationChange } = usePagination(1, 20);\n *\n * const { list, isLoading } = contactHooks.useList(queryParams);\n *\n * <Pagination onChange={onPaginationChange} total={pagination.totalDocuments} />\n */\nexport function usePagination(\n initialPage = 1,\n initialLimit = 10\n): VSPaginationReturn {\n const [page, setPage] = useState(initialPage);\n const [limit, setLimit] = useState(initialLimit);\n\n const onPaginationChange = useCallback(\n (newPage: number, newLimit: number) => {\n setPage(newPage);\n setLimit(newLimit);\n },\n []\n );\n\n const resetPagination = useCallback(() => {\n setPage(1);\n }, []);\n\n return {\n page,\n limit,\n onPaginationChange,\n resetPagination,\n setPage,\n setLimit,\n queryParams: { page, limit },\n };\n}\n"],"mappings":"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","names":["import_react","import_core","import_react","import_react","import_react"]}