@gaddario98/react-core 2.1.4 → 2.1.6
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 +630 -4
- package/dist/pages/index.d.ts +527 -682
- package/dist/pages/index.js +242 -258
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/index.mjs +242 -258
- package/dist/pages/index.mjs.map +1 -1
- package/dist/queries/index.d.ts +25 -4
- package/dist/queries/index.js +211 -216
- package/dist/queries/index.js.map +1 -1
- package/dist/queries/index.mjs +211 -216
- package/dist/queries/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,640 @@
|
|
|
1
|
-
# react-
|
|
1
|
+
# @gaddario98/react-core
|
|
2
|
+
|
|
3
|
+
A modular, type-safe React framework that unifies state management, forms, data fetching, page orchestration, localization, authentication, and notifications into a single cohesive package. Built on Jotai, TanStack Form, and TanStack Query.
|
|
4
|
+
|
|
5
|
+
**Version**: 2.1.5 | **License**: MIT | **Author**: Giosuè Addario
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Overview](#overview)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Architecture](#architecture)
|
|
14
|
+
- [Quick Start — Unified Configuration](#quick-start--unified-configuration)
|
|
15
|
+
- [Modules](#modules)
|
|
16
|
+
- [State (`/state`)](#state-state)
|
|
17
|
+
- [Auth (`/auth`)](#auth-auth)
|
|
18
|
+
- [Notifications (`/notifications`)](#notifications-notifications)
|
|
19
|
+
- [Localization (`/localization`)](#localization-localization)
|
|
20
|
+
- [Form (`/form`)](#form-form)
|
|
21
|
+
- [Queries (`/queries`)](#queries-queries)
|
|
22
|
+
- [Pages (`/pages`)](#pages-pages)
|
|
23
|
+
- [Providers (`/providers`)](#providers-providers)
|
|
24
|
+
- [Utilities (`/utiles`)](#utilities-utiles)
|
|
25
|
+
- [Config (`/config`)](#config-config)
|
|
26
|
+
- [Entry Points](#entry-points)
|
|
27
|
+
- [Cross-Platform Support](#cross-platform-support)
|
|
28
|
+
- [TypeScript Support](#typescript-support)
|
|
29
|
+
- [Contributing](#contributing)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Overview
|
|
34
|
+
|
|
35
|
+
`@gaddario98/react-core` is composed of independent modules that share a common state layer (Jotai atoms). Each module can be imported individually via sub-path exports or consumed together through the root entry point.
|
|
36
|
+
|
|
37
|
+
**Key design principles:**
|
|
38
|
+
|
|
39
|
+
- **Atom-based state**: Every module stores its configuration and runtime state in Jotai atoms via `atomStateGenerator`, enabling cross-module reactivity without React Context nesting
|
|
40
|
+
- **Platform-agnostic**: No DOM or React Native imports — platform behavior is injected via configurable container components
|
|
41
|
+
- **Tree-shakeable**: 10 independent sub-path exports; import only what you use
|
|
42
|
+
- **Zero-config defaults**: Every module works out of the box with sensible defaults, customizable at any depth
|
|
43
|
+
- **TypeScript-first**: Full generic type support with strict inference
|
|
44
|
+
|
|
45
|
+
---
|
|
2
46
|
|
|
3
47
|
## Installation
|
|
4
48
|
|
|
5
49
|
```bash
|
|
6
|
-
npm install @gaddario98/
|
|
50
|
+
npm install @gaddario98/react-core
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Peer Dependencies
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install react@">=18.0.0 <20.0.0"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
All other dependencies (`@tanstack/react-form`, `@tanstack/react-query`, `jotai`, `axios`, `fast-deep-equal`, `fflate`) are bundled.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Architecture
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
@gaddario98/react-core
|
|
67
|
+
├── state/ ← Jotai atom factory + compressed storage
|
|
68
|
+
├── auth/ ← Authentication state (built on state/)
|
|
69
|
+
├── notifications/ ← Toast/notification state (built on state/)
|
|
70
|
+
├── localization/ ← i18n engine with ICU formatting (built on state/)
|
|
71
|
+
├── form/ ← Dynamic form builder (built on TanStack Form + state/)
|
|
72
|
+
├── queries/ ← Data fetching layer (built on TanStack Query + state/)
|
|
73
|
+
├── pages/ ← Page orchestrator (composes form/ + queries/ + state/)
|
|
74
|
+
├── providers/ ← Generic provider compositor
|
|
75
|
+
├── utiles/ ← Utility functions (classnames, memoization)
|
|
76
|
+
└── config/ ← useCoreConfig — unified setup hook
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The **dependency flow** is bottom-up: `state/` is the foundation, `auth/`, `notifications/`, and `localization/` are state slices, `form/` and `queries/` are feature layers, and `pages/` orchestrates everything. `config/useCoreConfig` wires all modules together in a single hook.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Quick Start — Unified Configuration
|
|
84
|
+
|
|
85
|
+
The `useCoreConfig` hook initializes all modules at once. Call it near the root of your app:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { useCoreConfig, AppProviders, QueriesProvider } from "@gaddario98/react-core";
|
|
89
|
+
|
|
90
|
+
function CoreProvider({ children }: { children: React.ReactNode }) {
|
|
91
|
+
useCoreConfig({
|
|
92
|
+
localization: {
|
|
93
|
+
defaultLocale: "en",
|
|
94
|
+
supportedLocales: ["en", "it"],
|
|
95
|
+
locales: {
|
|
96
|
+
en: { common: { welcome: "Welcome" } },
|
|
97
|
+
it: { common: { welcome: "Benvenuto" } },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
pages: {
|
|
101
|
+
PageContainer: ({ children, id }) => <main id={id}>{children}</main>,
|
|
102
|
+
BodyContainer: ({ children }) => <div className="body">{children}</div>,
|
|
103
|
+
defaultMetadata: { title: "My App" },
|
|
104
|
+
},
|
|
105
|
+
form: {
|
|
106
|
+
formFieldContainer: ({ children }) => <div className="field">{children}</div>,
|
|
107
|
+
},
|
|
108
|
+
apiConfig: {
|
|
109
|
+
endpoints: { api: "https://api.example.com" },
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return <>{children}</>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default function App() {
|
|
117
|
+
return (
|
|
118
|
+
<AppProviders providers={[QueriesProvider]}>
|
|
119
|
+
<CoreProvider>
|
|
120
|
+
{/* your app */}
|
|
121
|
+
</CoreProvider>
|
|
122
|
+
</AppProviders>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`useCoreConfig` automatically:
|
|
128
|
+
- Wires `translateText` from the localization module into forms and pages
|
|
129
|
+
- Wires `showNotification` into forms and queries
|
|
130
|
+
- Sets the `Authorization` header from `auth.token` on all API requests
|
|
131
|
+
- Passes `authValues` into pages for access control
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Modules
|
|
136
|
+
|
|
137
|
+
### State (`/state`)
|
|
138
|
+
|
|
139
|
+
The foundational layer. Provides a factory function to create Jotai atoms with optional compressed persistence to `localStorage`.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { atomStateGenerator } from "@gaddario98/react-core/state";
|
|
143
|
+
|
|
144
|
+
const {
|
|
145
|
+
atom: themeAtom,
|
|
146
|
+
useValue: useThemeValue, // read-only hook
|
|
147
|
+
useState: useThemeState, // [value, setter] hook
|
|
148
|
+
useReset: useThemeReset, // reset to default
|
|
149
|
+
} = atomStateGenerator<"light" | "dark">({
|
|
150
|
+
key: "app-theme",
|
|
151
|
+
defaultValue: "light",
|
|
152
|
+
persist: true, // compressed localStorage persistence
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Storage features:**
|
|
157
|
+
- Data < 1 KB stored as raw JSON; larger payloads are deflated (fflate) and base64-encoded
|
|
158
|
+
- Writes are debounced (50 ms) and flushed on `beforeunload` / `visibilitychange`
|
|
159
|
+
- Swap the storage backend via `setCustomStorage(myStorage)` (e.g., AsyncStorage for React Native)
|
|
160
|
+
|
|
161
|
+
**Exports:**
|
|
162
|
+
|
|
163
|
+
| Export | Description |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `atomStateGenerator<T>(options)` | Creates an atom with `useValue`, `useState`, `useReset` hooks |
|
|
166
|
+
| `storage` | Default compressed storage singleton |
|
|
167
|
+
| `setCustomStorage(s)` | Replace the storage backend |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Auth (`/auth`)
|
|
172
|
+
|
|
173
|
+
A persisted authentication state slice.
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { useAuthState, useAuthValue } from "@gaddario98/react-core/auth";
|
|
177
|
+
|
|
178
|
+
// Read auth state
|
|
179
|
+
const auth = useAuthValue();
|
|
180
|
+
console.log(auth?.token, auth?.isLogged);
|
|
181
|
+
|
|
182
|
+
// Update auth state
|
|
183
|
+
const [auth, setAuth] = useAuthState();
|
|
184
|
+
setAuth({ id: "user-1", token: "jwt...", isLogged: true });
|
|
185
|
+
|
|
186
|
+
// Clear on logout
|
|
187
|
+
setAuth(null);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**`AuthState` type:**
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
type AuthState = {
|
|
194
|
+
id: string;
|
|
195
|
+
accountVerified?: boolean;
|
|
196
|
+
isLogged?: boolean;
|
|
197
|
+
token?: string;
|
|
198
|
+
phoneNumber?: string;
|
|
199
|
+
email?: string;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The atom is persisted under the key `"reactAuthStore"` using compressed storage.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### Notifications (`/notifications`)
|
|
208
|
+
|
|
209
|
+
In-memory notification state for toast/snackbar systems.
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import { useNotification } from "@gaddario98/react-core/notifications";
|
|
213
|
+
|
|
214
|
+
const { showNotification, clearNotification } = useNotification("myPage");
|
|
215
|
+
|
|
216
|
+
showNotification({
|
|
217
|
+
message: "Profile updated!",
|
|
218
|
+
type: "success",
|
|
219
|
+
autoHideDuration: 3000,
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**`NotificationMessage` type:**
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
interface NotificationMessage {
|
|
227
|
+
id: string;
|
|
228
|
+
message: string;
|
|
229
|
+
type: "success" | "error" | "info" | "warning";
|
|
230
|
+
autoHideDuration?: number;
|
|
231
|
+
textTransOption?: Record<string, unknown>;
|
|
232
|
+
ns?: string;
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Exports:**
|
|
237
|
+
|
|
238
|
+
| Export | Description |
|
|
239
|
+
|---|---|
|
|
240
|
+
| `useNotification(ns?)` | Returns `{ showNotification, clearNotification }` |
|
|
241
|
+
| `useNotificationValue()` | Read current notification |
|
|
242
|
+
| `useNotificationState()` | `[notification, setter]` tuple |
|
|
243
|
+
| `notificationAtom` | Raw Jotai atom |
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### Localization (`/localization`)
|
|
248
|
+
|
|
249
|
+
A built-in i18n engine with no external library dependencies. Supports ICU-style interpolation, pluralization, gender selection, and number/date/currency formatting.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
import { useTranslation, useLocalizationActions } from "@gaddario98/react-core/localization";
|
|
253
|
+
|
|
254
|
+
// Initialize locales at app startup
|
|
255
|
+
const { initializeLocale, switchLocale, addLocale } = useLocalizationActions();
|
|
256
|
+
|
|
257
|
+
initializeLocale({
|
|
258
|
+
defaultLocale: "en",
|
|
259
|
+
supportedLocales: ["en", "it"],
|
|
260
|
+
locales: {
|
|
261
|
+
en: { shop: { items: "{{count, plural, =0{No items} one{1 item} other{# items}}}" } },
|
|
262
|
+
it: { shop: { items: "{{count, plural, =0{Nessun articolo} one{1 articolo} other{# articoli}}}" } },
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Use translations
|
|
267
|
+
const { t, locale } = useTranslation("shop");
|
|
268
|
+
t("items", { count: 5 }); // "5 items"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Supported interpolation patterns:**
|
|
272
|
+
- `{{name}}` — simple variable substitution
|
|
273
|
+
- `{{count, number}}` — number formatting (locale-aware)
|
|
274
|
+
- `{{date, date}}` — date formatting
|
|
275
|
+
- `{{price, currency}}` — currency formatting
|
|
276
|
+
- `{{count, plural, =0{...} one{...} other{...}}}` — ICU plural rules
|
|
277
|
+
- `{{gender, select, male{...} female{...} other{...}}}` — gender/select
|
|
278
|
+
|
|
279
|
+
**Server-side usage (outside React):**
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { createServerTranslator } from "@gaddario98/react-core/localization";
|
|
283
|
+
|
|
284
|
+
const { t } = createServerTranslator(resources, "en");
|
|
285
|
+
t("shop.items", { count: 3 }); // "3 items"
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
### Form (`/form`)
|
|
291
|
+
|
|
292
|
+
A dynamic, type-safe form builder on top of TanStack React Form. Renders fields from a declarative configuration array.
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
import { FormManager } from "@gaddario98/react-core/form";
|
|
296
|
+
|
|
297
|
+
interface ContactForm {
|
|
298
|
+
name: string;
|
|
299
|
+
email: string;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
<FormManager<ContactForm>
|
|
303
|
+
defaultValues={{ name: "", email: "" }}
|
|
304
|
+
data={[
|
|
305
|
+
{
|
|
306
|
+
name: "name",
|
|
307
|
+
label: "Full Name",
|
|
308
|
+
rules: { onChange: (val) => (!val ? "Required" : undefined) },
|
|
309
|
+
component: (props) => <input value={props.value} onChange={(e) => props.onChange(e.target.value)} />,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: "email",
|
|
313
|
+
label: "Email",
|
|
314
|
+
component: (props) => <input value={props.value} onChange={(e) => props.onChange(e.target.value)} />,
|
|
315
|
+
},
|
|
316
|
+
]}
|
|
317
|
+
submit={[
|
|
318
|
+
{
|
|
319
|
+
component: ({ onClick }) => <button onClick={onClick}>Save</button>,
|
|
320
|
+
onSuccess: async (values) => console.log(values),
|
|
321
|
+
},
|
|
322
|
+
]}
|
|
323
|
+
/>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Key features:**
|
|
327
|
+
- Static or dynamic field definitions (factory functions with `{ get, set }` access to current values)
|
|
328
|
+
- Partial form submission — validate only a subset of fields via `values: ["field1", "field2"]`
|
|
329
|
+
- Custom layout containers via `viewSettings` (dialogs, cards, drawers)
|
|
330
|
+
- Built-in notification integration for success/error feedback
|
|
331
|
+
- Headless alternative via `useFormManager` hook
|
|
332
|
+
|
|
333
|
+
**Global configuration:**
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
import { useFormConfigState } from "@gaddario98/react-core/form";
|
|
337
|
+
|
|
338
|
+
const [, setFormConfig] = useFormConfigState();
|
|
339
|
+
setFormConfig((prev) => ({
|
|
340
|
+
...prev,
|
|
341
|
+
translateText: (key, opts) => t(key, opts),
|
|
342
|
+
formFieldContainer: MyFieldWrapper,
|
|
343
|
+
showNotification: (msg) => toast(msg.message),
|
|
344
|
+
}));
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
For full API details, see [github.com/gaddario98/react-form](https://github.com/gaddario98/react-form).
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
### Queries (`/queries`)
|
|
352
|
+
|
|
353
|
+
A unified data fetching layer on top of TanStack React Query and Jotai. Manages queries, mutations, and WebSockets through a single declarative API.
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
import { useApi } from "@gaddario98/react-core/queries";
|
|
357
|
+
import type { QueriesArray } from "@gaddario98/react-core/queries";
|
|
358
|
+
|
|
359
|
+
const queries = [
|
|
360
|
+
{
|
|
361
|
+
type: "query",
|
|
362
|
+
key: "products",
|
|
363
|
+
queryConfig: {
|
|
364
|
+
endpoint: ["api", "v1/products"],
|
|
365
|
+
queryKey: ["products"],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
type: "mutation",
|
|
370
|
+
key: "addProduct",
|
|
371
|
+
mutationConfig: {
|
|
372
|
+
endpoint: ["api", "v1/products"],
|
|
373
|
+
method: "POST",
|
|
374
|
+
queryKeyToInvalidate: ["products"],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
] as const satisfies QueriesArray;
|
|
378
|
+
|
|
379
|
+
const { allQuery, allMutation, refreshQueries } = useApi(queries, {
|
|
380
|
+
scopeId: "product-page",
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const products = allQuery.products.data;
|
|
384
|
+
allMutation.addProduct.mutate({ body: { name: "New Product" } });
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Key features:**
|
|
388
|
+
- Typed `allQuery` / `allMutation` / `allWebSocket` maps from the configuration array
|
|
389
|
+
- Automatic Jotai atom sync — query results are accessible cross-component without refetching
|
|
390
|
+
- Fine-grained subscriptions via `useApiValues` (re-render only on specific path changes)
|
|
391
|
+
- Built-in WebSocket support alongside REST queries
|
|
392
|
+
- Payload encryption/decryption (AES-GCM)
|
|
393
|
+
- Offline persistence via TanStack Query Persist
|
|
394
|
+
- Standalone `useQueryApi` / `useMutateApi` hooks for simpler one-off usage
|
|
395
|
+
|
|
396
|
+
**Global configuration:**
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
import { useApiConfigState } from "@gaddario98/react-core/queries";
|
|
400
|
+
|
|
401
|
+
const [, setApiConfig] = useApiConfigState();
|
|
402
|
+
setApiConfig({
|
|
403
|
+
endpoints: { api: "https://api.example.com" },
|
|
404
|
+
defaultHeaders: { "Cache-Control": "no-cache" },
|
|
405
|
+
validateAuthFn: () => !!localStorage.getItem("token"),
|
|
406
|
+
queryClient: new QueryClient({ defaultOptions: { queries: { retry: 2 } } }),
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Wrap your app with `QueriesProvider` to initialize the TanStack QueryClient:
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
import { QueriesProvider } from "@gaddario98/react-core/queries";
|
|
414
|
+
|
|
415
|
+
<QueriesProvider>
|
|
416
|
+
<App />
|
|
417
|
+
</QueriesProvider>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
For full API details, see [github.com/gaddario98/react-queries](https://github.com/gaddario98/react-queries).
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
### Pages (`/pages`)
|
|
425
|
+
|
|
426
|
+
A page orchestrator that composes forms, queries, metadata, lazy loading, and layout into a single `PageProps` configuration. Works on both web and React Native.
|
|
427
|
+
|
|
428
|
+
```tsx
|
|
429
|
+
import { PageGenerator } from "@gaddario98/react-core/pages";
|
|
430
|
+
import type { PageProps, QueryDefinition } from "@gaddario98/react-core/pages";
|
|
431
|
+
|
|
432
|
+
interface MyForm { search: string }
|
|
433
|
+
type MyQueries = [QueryDefinition<"results", "query", never, Product[]>];
|
|
434
|
+
|
|
435
|
+
const props: PageProps<MyForm, MyQueries> = {
|
|
436
|
+
id: "search-page",
|
|
437
|
+
meta: { title: "Search", description: "Find products" },
|
|
438
|
+
form: {
|
|
439
|
+
defaultValues: { search: "" },
|
|
440
|
+
data: [{ name: "search", debounceDelay: 300, component: SearchInput }],
|
|
441
|
+
},
|
|
442
|
+
queries: [
|
|
443
|
+
{
|
|
444
|
+
type: "query",
|
|
445
|
+
key: "results",
|
|
446
|
+
queryConfig: ({ get }) => ({
|
|
447
|
+
queryKey: ["results", get("form", "search")],
|
|
448
|
+
queryFn: () => fetchProducts(get("form", "search")),
|
|
449
|
+
enabled: get("form", "search", "").length > 2,
|
|
450
|
+
}),
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
contents: [
|
|
454
|
+
{
|
|
455
|
+
type: "custom",
|
|
456
|
+
component: ({ get }) => {
|
|
457
|
+
const results = get("query", "results.data", []);
|
|
458
|
+
return <ProductList products={results} />;
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
<PageGenerator<MyForm, MyQueries> {...props} />;
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Key features:**
|
|
468
|
+
- `get()` / `set()` API with automatic dependency tracking (90% fewer re-renders)
|
|
469
|
+
- Dynamic SEO metadata (Open Graph, Twitter Card, JSON-LD, AI hints, robots)
|
|
470
|
+
- Lazy loading with viewport, interaction, or conditional triggers
|
|
471
|
+
- Lifecycle callbacks (`onMountComplete`, `onQuerySuccess`, `onQueryError`, `onFormSubmit`, `onValuesChange`)
|
|
472
|
+
- Authentication gate via `enableAuthControl`
|
|
473
|
+
- Platform overrides via `platformOverrides: { web: {...}, native: {...} }`
|
|
474
|
+
|
|
475
|
+
For full API details, see [github.com/gaddario98/react-pages](https://github.com/gaddario98/react-pages).
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
### Providers (`/providers`)
|
|
480
|
+
|
|
481
|
+
A utility component that composes multiple React providers without deep nesting.
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
import { AppProviders } from "@gaddario98/react-core/providers";
|
|
485
|
+
|
|
486
|
+
<AppProviders
|
|
487
|
+
providers={[
|
|
488
|
+
QueriesProvider,
|
|
489
|
+
[ThemeProvider, { theme: "dark" }],
|
|
490
|
+
[IntlProvider, { locale: "en" }],
|
|
491
|
+
]}
|
|
492
|
+
>
|
|
493
|
+
<App />
|
|
494
|
+
</AppProviders>
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Supports both bare components and `[Component, props]` tuples. Providers are composed in declaration order (first = outermost).
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
### Utilities (`/utiles`)
|
|
502
|
+
|
|
503
|
+
General-purpose React and JavaScript utilities.
|
|
504
|
+
|
|
505
|
+
```tsx
|
|
506
|
+
import { cn, withMemo } from "@gaddario98/react-core/utiles";
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
| Export | Description |
|
|
510
|
+
|---|---|
|
|
511
|
+
| `cn(...inputs)` | Combines `clsx` + `tailwind-merge` for safe Tailwind class merging |
|
|
512
|
+
| `withMemo(Component, areEqual?)` | Type-safe `React.memo` wrapper that preserves generic types |
|
|
513
|
+
| `createExtractor(data, cache?, keys?)` | Picks a subset of keys from an object with stable reference caching |
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
### Config (`/config`)
|
|
518
|
+
|
|
519
|
+
The unified configuration hook that wires all modules together.
|
|
520
|
+
|
|
521
|
+
```tsx
|
|
522
|
+
import { useCoreConfig } from "@gaddario98/react-core/config";
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**`CoreConfig` interface:**
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
interface CoreConfig {
|
|
529
|
+
form?: Partial<FormConfigProps>;
|
|
530
|
+
localization?: LocalizationConfigProps;
|
|
531
|
+
pages?: Partial<PageConfigProps>;
|
|
532
|
+
apiConfig?: Partial<ApiConfig>;
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
See [Quick Start](#quick-start--unified-configuration) for usage.
|
|
537
|
+
|
|
538
|
+
**What `useCoreConfig` wires automatically:**
|
|
539
|
+
|
|
540
|
+
| Source | Target | What |
|
|
541
|
+
|---|---|---|
|
|
542
|
+
| `localization` | `form`, `pages` | `translateText` function |
|
|
543
|
+
| `notifications` | `form`, `queries` | `showNotification` handler |
|
|
544
|
+
| `auth` | `queries` | `Authorization` header (Bearer token) |
|
|
545
|
+
| `auth` | `queries` | `validateAuthFn` (auth validation) |
|
|
546
|
+
| `auth` | `pages` | `authValues` (access control) |
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
## Entry Points
|
|
551
|
+
|
|
552
|
+
The package exposes 10 sub-path exports for tree-shaking:
|
|
553
|
+
|
|
554
|
+
| Import Path | Module | Typical Use |
|
|
555
|
+
|---|---|---|
|
|
556
|
+
| `@gaddario98/react-core` | All modules | Full framework access |
|
|
557
|
+
| `@gaddario98/react-core/state` | State | Atom factory, storage |
|
|
558
|
+
| `@gaddario98/react-core/auth` | Auth | Authentication state |
|
|
559
|
+
| `@gaddario98/react-core/notifications` | Notifications | Toast state |
|
|
560
|
+
| `@gaddario98/react-core/localization` | Localization | i18n engine |
|
|
561
|
+
| `@gaddario98/react-core/form` | Form | Form builder |
|
|
562
|
+
| `@gaddario98/react-core/queries` | Queries | Data fetching |
|
|
563
|
+
| `@gaddario98/react-core/pages` | Pages | Page orchestrator |
|
|
564
|
+
| `@gaddario98/react-core/providers` | Providers | Provider compositor |
|
|
565
|
+
| `@gaddario98/react-core/utiles` | Utilities | Helpers |
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Cross-Platform Support
|
|
570
|
+
|
|
571
|
+
The entire package is platform-agnostic. No module imports `react-dom` or `react-native` directly.
|
|
572
|
+
|
|
573
|
+
**Web**: Works out of the box. Metadata is written to `document.head`.
|
|
574
|
+
|
|
575
|
+
**React Native**: Replace the storage backend and layout containers:
|
|
576
|
+
|
|
577
|
+
```tsx
|
|
578
|
+
import { setCustomStorage } from "@gaddario98/react-core/state";
|
|
579
|
+
import { usePageConfigState } from "@gaddario98/react-core/pages";
|
|
580
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
581
|
+
import { View, ScrollView } from "react-native";
|
|
582
|
+
|
|
583
|
+
// Swap storage for React Native
|
|
584
|
+
setCustomStorage({
|
|
585
|
+
getItem: (key) => AsyncStorage.getItem(key) ?? null,
|
|
586
|
+
setItem: (key, val) => { AsyncStorage.setItem(key, val) },
|
|
587
|
+
removeItem: (key) => { AsyncStorage.removeItem(key) },
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// Swap layout containers
|
|
591
|
+
const [, setPageConfig] = usePageConfigState();
|
|
592
|
+
setPageConfig((prev) => ({
|
|
593
|
+
...prev,
|
|
594
|
+
PageContainer: ({ children, id }) => <View style={{ flex: 1 }}>{children}</View>,
|
|
595
|
+
BodyContainer: ({ children }) => <ScrollView>{children}</ScrollView>,
|
|
596
|
+
HeaderContainer: ({ children }) => <View>{children}</View>,
|
|
597
|
+
FooterContainer: ({ children }) => <View>{children}</View>,
|
|
598
|
+
ItemsContainer: ({ children }) => <View>{children}</View>,
|
|
599
|
+
}));
|
|
7
600
|
```
|
|
8
601
|
|
|
9
|
-
|
|
602
|
+
See the [React Native Integration Strategy](https://github.com/gaddario98/react-pages#react-native-integration-strategy) in the pages documentation for a full setup guide.
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## TypeScript Support
|
|
607
|
+
|
|
608
|
+
All modules are fully typed with generics. Key generic interfaces:
|
|
609
|
+
|
|
610
|
+
```ts
|
|
611
|
+
// Form — generic over field values
|
|
612
|
+
FormManager<F extends FieldValues>
|
|
613
|
+
FormManagerProps<F extends FieldValues>
|
|
614
|
+
|
|
615
|
+
// Queries — generic over query array definition
|
|
616
|
+
useApi<Q extends QueriesArray>(queries: Q, options)
|
|
617
|
+
QueriesArray // tuple of query/mutation/websocket definitions
|
|
618
|
+
|
|
619
|
+
// Pages — generic over form, queries, and page variables
|
|
620
|
+
PageGenerator<F extends FieldValues, Q extends QueriesArray, V extends Record<string, unknown>>
|
|
621
|
+
PageProps<F, Q, V>
|
|
622
|
+
FunctionProps<F, Q, V> // the { get, set } interface
|
|
623
|
+
|
|
624
|
+
// State — generic over atom value type
|
|
625
|
+
atomStateGenerator<T>(options): AtomState<T>
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Contributing
|
|
631
|
+
|
|
632
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
633
|
+
|
|
634
|
+
- [Open an issue](https://github.com/gaddario98/react-core/issues)
|
|
635
|
+
- [Repository](https://github.com/gaddario98/react-core)
|
|
10
636
|
|
|
11
|
-
|
|
637
|
+
---
|
|
12
638
|
|
|
13
639
|
## License
|
|
14
640
|
|