@void-snippets/react 0.2.2 → 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/README.md +106 -92
- package/dist/index.d.mts +177 -43
- package/dist/index.d.ts +177 -43
- package/dist/index.js +182 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +176 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @void-snippets/react
|
|
2
2
|
|
|
3
|
-
TanStack Query v5
|
|
3
|
+
TanStack Query v5 resource hooks factory + general-purpose React hooks. All fully typed, zero boilerplate.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,119 +10,87 @@ npm install @void-snippets/react @void-snippets/client @tanstack/react-query axi
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Setup
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### Configure axios once
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
// src/main.ts
|
|
19
18
|
import axios from 'axios';
|
|
20
19
|
import { configure } from '@void-snippets/client';
|
|
21
|
-
|
|
22
|
-
const axiosInstance = axios.create({ baseURL: 'https://api.example.com' });
|
|
23
|
-
configure(axiosInstance);
|
|
20
|
+
configure(axios.create({ baseURL: 'https://api.example.com' }));
|
|
24
21
|
```
|
|
25
22
|
|
|
26
|
-
###
|
|
23
|
+
### Wrap your app with QueryClientProvider
|
|
27
24
|
|
|
28
25
|
```tsx
|
|
29
|
-
// src/main.tsx
|
|
30
26
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
31
|
-
|
|
32
27
|
const queryClient = new QueryClient();
|
|
33
|
-
|
|
34
28
|
function App() {
|
|
35
|
-
return
|
|
36
|
-
<QueryClientProvider client={queryClient}>
|
|
37
|
-
<YourApp />
|
|
38
|
-
</QueryClientProvider>
|
|
39
|
-
);
|
|
29
|
+
return <QueryClientProvider client={queryClient}><YourApp /></QueryClientProvider>;
|
|
40
30
|
}
|
|
41
31
|
```
|
|
42
32
|
|
|
43
|
-
|
|
33
|
+
---
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
// contacts/contacts.api.ts
|
|
47
|
-
import { ResourceService } from '@void-snippets/client';
|
|
48
|
-
import type { Contact } from './contacts.types';
|
|
35
|
+
## Resource Hooks — `createResourceHooks`
|
|
49
36
|
|
|
37
|
+
Define a service once, get all hooks free — fully typed, no generics needed.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// contacts.api.ts
|
|
50
41
|
export class ContactsApiService extends ResourceService<
|
|
51
|
-
Contact.Id,
|
|
52
|
-
Contact.
|
|
53
|
-
Contact.WithCreatedBy,
|
|
54
|
-
Contact.Apis.CreatePayload,
|
|
55
|
-
Contact.Apis.UpdatePayload
|
|
42
|
+
Contact.Id, Contact.Base, Contact.WithCreatedBy,
|
|
43
|
+
Contact.Apis.CreatePayload, Contact.Apis.UpdatePayload
|
|
56
44
|
> {
|
|
57
|
-
constructor() {
|
|
58
|
-
super('/contacts');
|
|
59
|
-
}
|
|
45
|
+
constructor() { super('/contacts'); }
|
|
60
46
|
}
|
|
61
|
-
|
|
62
47
|
export const ContactsApis = new ContactsApiService();
|
|
63
|
-
```
|
|
64
48
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
// contacts/contacts.hooks.ts
|
|
69
|
-
import { createResourceHooks } from '@void-snippets/react';
|
|
70
|
-
import { ContactsApis } from './contacts.api';
|
|
71
|
-
|
|
72
|
-
// All types are inferred from ContactsApis — no generics needed
|
|
49
|
+
// contacts.hooks.ts
|
|
73
50
|
export const contactHooks = createResourceHooks('contacts', ContactsApis);
|
|
74
51
|
```
|
|
75
52
|
|
|
76
|
-
###
|
|
53
|
+
### `useList(params?)`
|
|
77
54
|
|
|
78
55
|
```tsx
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
limit: 20,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
if (isContactsLoading) return <Spinner />;
|
|
87
|
-
return contacts.map(c => <ContactCard key={c._id} contact={c} />);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Single item
|
|
91
|
-
function ContactDetail({ id }: { id: Contact.Id }) {
|
|
92
|
-
const { data, isLoading } = contactHooks.useGet(id);
|
|
93
|
-
// data is typed as Contact.WithCreatedBy ✅
|
|
94
|
-
}
|
|
56
|
+
const { list, isLoading, pagination, error, invalidate } =
|
|
57
|
+
contactHooks.useList({ page: 1, limit: 20 });
|
|
58
|
+
// list is typed as Contact.Base[] ✅
|
|
59
|
+
```
|
|
95
60
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
61
|
+
| Key | Type |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `list` | `TBase[]` |
|
|
64
|
+
| `pagination` | `VSPagination` |
|
|
65
|
+
| `isLoading` | `boolean` |
|
|
66
|
+
| `error` | `Error \| null` |
|
|
67
|
+
| `invalidate` | `() => void` |
|
|
99
68
|
|
|
100
|
-
|
|
101
|
-
<button onClick={() => create.mutate({ name: 'John' })}>
|
|
102
|
-
Create
|
|
103
|
-
</button>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
69
|
+
### `useGet(id, staleTime?)`
|
|
106
70
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const all = data?.pages.flatMap(p => p.items) ?? [];
|
|
111
|
-
}
|
|
71
|
+
```tsx
|
|
72
|
+
const { item, isLoading, error, refetch } = contactHooks.useGet(id);
|
|
73
|
+
// item is typed as Contact.WithCreatedBy ✅
|
|
112
74
|
```
|
|
113
75
|
|
|
114
|
-
|
|
76
|
+
### `useMutations()`
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const { create, update, remove } = contactHooks.useMutations();
|
|
115
80
|
|
|
116
|
-
|
|
81
|
+
create.mutate({ name: 'John' });
|
|
82
|
+
update.mutate({ _id: id, payload: { name: 'Jane' } });
|
|
83
|
+
remove.mutate(id);
|
|
84
|
+
```
|
|
117
85
|
|
|
118
|
-
|
|
86
|
+
### `useInfinite(params?)`
|
|
119
87
|
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
|
|
88
|
+
```tsx
|
|
89
|
+
const { data, fetchNextPage, hasNextPage } = contactHooks.useInfinite({ limit: 20 });
|
|
90
|
+
const all = data?.pages.flatMap(p => p.items) ?? [];
|
|
123
91
|
```
|
|
124
92
|
|
|
125
|
-
|
|
93
|
+
### Custom API shapes
|
|
126
94
|
|
|
127
95
|
```ts
|
|
128
96
|
export const contactHooks = createResourceHooks('contacts', ContactsApis, {
|
|
@@ -143,25 +111,71 @@ export const contactHooks = createResourceHooks('contacts', ContactsApis, {
|
|
|
143
111
|
|
|
144
112
|
---
|
|
145
113
|
|
|
146
|
-
##
|
|
114
|
+
## General-Purpose Hooks
|
|
147
115
|
|
|
148
|
-
### `
|
|
149
|
-
| Returned key | Type | Description |
|
|
150
|
-
|---|---|---|
|
|
151
|
-
| `[prefix]` | `TBase[]` | e.g. `contacts` for prefix `"contacts"` |
|
|
152
|
-
| `pagination` | `TPagination` | `{ page, limit, totalPages, totalDocuments }` |
|
|
153
|
-
| `is[Prefix]Loading` | `boolean` | e.g. `isContactsLoading` |
|
|
154
|
-
| `[prefix]Error` | `Error \| null` | e.g. `contactsError` |
|
|
155
|
-
| `invalidate[Prefix]` | `() => void` | e.g. `invalidateContacts` |
|
|
116
|
+
### `useAlertMessage(autoHideDuration?)`
|
|
156
117
|
|
|
157
|
-
|
|
158
|
-
|
|
118
|
+
```tsx
|
|
119
|
+
const { alert, showAlert, hideAlert } = useAlertMessage(3000);
|
|
159
120
|
|
|
160
|
-
|
|
161
|
-
|
|
121
|
+
showAlert('Saved!', 'success');
|
|
122
|
+
showAlert('Something went wrong', 'error');
|
|
123
|
+
showAlert(<b>Custom JSX</b>, 'info');
|
|
124
|
+
// alert.isVisible, alert.message, alert.type
|
|
125
|
+
```
|
|
162
126
|
|
|
163
|
-
|
|
164
|
-
|
|
127
|
+
Variants: `"success" | "info" | "error"`
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### `useAsyncState<T>(initialData?)`
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
const { data, isLoading, isError, execute } = useAsyncState<User>();
|
|
135
|
+
|
|
136
|
+
const [err, user] = await execute(() => fetchUser(id), {
|
|
137
|
+
onSuccess: (u) => showAlert(`Welcome ${u.name}`, 'success'),
|
|
138
|
+
onError: (e) => showAlert(e.message, 'error'),
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Status values: `"idle" | "pending" | "success" | "error"`
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
### `useCallTimer(startedAt?)`
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
const duration = useCallTimer(call.startedAt); // "02:45"
|
|
150
|
+
const duration = useCallTimer(null); // "00:00"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### `useModal<T>()`
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
const modal = useModal<Contact.Base>();
|
|
159
|
+
|
|
160
|
+
modal.openCreateModal(); // data → null
|
|
161
|
+
modal.openEditModal(contact); // data → contact
|
|
162
|
+
|
|
163
|
+
if (modal.data) { /* edit mode */ } else { /* create mode */ }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### `usePagination(initialPage?, initialLimit?)`
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
const { queryParams, onPaginationChange } = usePagination(1, 20);
|
|
172
|
+
|
|
173
|
+
// Wire directly to useList
|
|
174
|
+
const { list } = contactHooks.useList(queryParams);
|
|
175
|
+
|
|
176
|
+
// Wire to your pagination UI
|
|
177
|
+
<Pagination onChange={onPaginationChange} total={pagination.totalDocuments} />
|
|
178
|
+
```
|
|
165
179
|
|
|
166
180
|
---
|
|
167
181
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import {
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
37
|
-
*
|
|
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?:
|
|
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?:
|
|
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
|
|
70
|
-
* for
|
|
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
|
-
* //
|
|
84
|
-
* const {
|
|
85
|
-
*
|
|
86
|
-
*
|
|
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?:
|
|
89
|
-
useList: (params?:
|
|
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
|
-
|
|
98
|
+
remove: _tanstack_react_query.UseMutationResult<Detail<S>, Error, Id<S>, unknown>;
|
|
103
99
|
};
|
|
104
|
-
useInfinite: (params?:
|
|
100
|
+
useInfinite: (params?: VSQueryParams) => _tanstack_react_query.UseInfiniteQueryResult<_tanstack_react_query.InfiniteData<VSListResult<Base<S>>, unknown>, Error>;
|
|
105
101
|
};
|
|
106
102
|
|
|
107
|
-
|
|
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 };
|