flysoft-react-ui 1.2.10 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/AI_CONTEXT.md +1533 -1401
  2. package/AI_INTEGRATION_GUIDE.md +358 -343
  3. package/README.md +498 -464
  4. package/dist/components/form-controls/Button.d.ts.map +1 -1
  5. package/dist/components/form-controls/Checkbox.d.ts.map +1 -1
  6. package/dist/components/form-controls/CurrencyInput.d.ts.map +1 -1
  7. package/dist/components/form-controls/DatePicker.d.ts.map +1 -1
  8. package/dist/components/form-controls/Input.d.ts.map +1 -1
  9. package/dist/components/form-controls/LinkButton.d.ts.map +1 -1
  10. package/dist/components/form-controls/Pagination.d.ts.map +1 -1
  11. package/dist/components/form-controls/RadioButtonGroup.d.ts.map +1 -1
  12. package/dist/components/layout/Accordion.d.ts +8 -0
  13. package/dist/components/layout/Accordion.d.ts.map +1 -1
  14. package/dist/components/layout/Card.d.ts +5 -0
  15. package/dist/components/layout/Card.d.ts.map +1 -1
  16. package/dist/components/layout/Collection.d.ts +17 -1
  17. package/dist/components/layout/Collection.d.ts.map +1 -1
  18. package/dist/components/layout/DataField.d.ts +19 -0
  19. package/dist/components/layout/DataField.d.ts.map +1 -1
  20. package/dist/components/layout/DataTable.d.ts.map +1 -1
  21. package/dist/components/layout/DropdownMenu.d.ts.map +1 -1
  22. package/dist/components/layout/DropdownPanel.d.ts.map +1 -1
  23. package/dist/components/layout/Filter.d.ts +8 -0
  24. package/dist/components/layout/Filter.d.ts.map +1 -1
  25. package/dist/components/layout/Menu.d.ts.map +1 -1
  26. package/dist/components/layout/TabsGroup.d.ts.map +1 -1
  27. package/dist/components/utils/Avatar.d.ts.map +1 -1
  28. package/dist/components/utils/Badge.d.ts.map +1 -1
  29. package/dist/components/utils/Dialog.d.ts.map +1 -1
  30. package/dist/components/utils/Loader.d.ts.map +1 -1
  31. package/dist/components/utils/RoadMap.d.ts.map +1 -1
  32. package/dist/components/utils/Snackbar.d.ts.map +1 -1
  33. package/dist/contexts/AppLayoutContext.d.ts +5 -1
  34. package/dist/contexts/AppLayoutContext.d.ts.map +1 -1
  35. package/dist/contexts/ThemeContext.d.ts +11 -1
  36. package/dist/contexts/ThemeContext.d.ts.map +1 -1
  37. package/dist/contexts/index.d.ts +2 -2
  38. package/dist/contexts/index.d.ts.map +1 -1
  39. package/dist/contexts/presets.d.ts +5 -1
  40. package/dist/contexts/presets.d.ts.map +1 -1
  41. package/dist/contexts/types.d.ts +51 -0
  42. package/dist/contexts/types.d.ts.map +1 -1
  43. package/dist/index.css +1 -1
  44. package/dist/index.js +8020 -7426
  45. package/dist/index.js.map +1 -1
  46. package/package.json +1 -1
@@ -1,343 +1,358 @@
1
- # AI Integration Guide (Consumer Projects)
2
-
3
- This file is designed to be copied into any client project that consumes `flysoft-react-ui`. It helps AI agents understand the complete API surface and generate correct code.
4
-
5
- ## 1) Install
6
-
7
- ```bash
8
- npm install flysoft-react-ui
9
- ```
10
-
11
- ## 2) Required Runtime Setup
12
-
13
- At app root:
14
-
15
- ```tsx
16
- import { ThemeProvider } from "flysoft-react-ui";
17
- import "flysoft-react-ui/styles";
18
-
19
- export function AppRoot() {
20
- return <ThemeProvider initialTheme="light">{/* app */}</ThemeProvider>;
21
- }
22
- ```
23
-
24
- For full app layout with navbar, sidebar, and snackbars:
25
-
26
- ```tsx
27
- import { AppLayoutProvider } from "flysoft-react-ui";
28
- import "flysoft-react-ui/styles";
29
-
30
- export function AppRoot() {
31
- return (
32
- <AppLayoutProvider
33
- initialTheme="light"
34
- initialNavbar={{ navBarLeftNode: <h1>Mi App</h1>, fullWidthNavbar: true }}
35
- >
36
- {/* routes */}
37
- </AppLayoutProvider>
38
- );
39
- }
40
- ```
41
-
42
- ## 3) Copy-paste prompt for AI agents
43
-
44
- Copy the following block into `.cursorrules`, `AGENTS.md`, `copilot-instructions.md`, `CLAUDE.md`, or your AI system prompt:
45
-
46
- ---
47
-
48
- ```md
49
- This project uses `flysoft-react-ui` as the default UI library.
50
-
51
- ## Rules
52
- 1. Always import from `flysoft-react-ui` (top-level only). Never from internal paths.
53
- 2. Prefer existing library components before creating custom UI. Never duplicate Button, Input, Card, Badge, Dialog, etc.
54
- 3. Use exported TypeScript types for type safety (e.g. ButtonProps, DataTableColumn<T>).
55
- 4. Keep style import at app root only: `import "flysoft-react-ui/styles";`
56
- 5. Wrap app with `ThemeProvider` (or `AppLayoutProvider` for full layout).
57
- 6. Use FontAwesome 5 icon classes (`fa-*`). Components auto-normalize to light style (fal). Never use other icon libraries.
58
- 7. Use theme CSS variables for custom styling: `var(--color-primary)`, `var(--color-bg-default)`, etc.
59
-
60
- ## Available Components
61
-
62
- ### Form Controls
63
- - `Button` variant: "primary"|"outline"|"ghost", size: "sm"|"md"|"lg", color: "primary"|"secondary"|"success"|"warning"|"danger"|"info", icon, loading, bg, textColor
64
- - `LinkButton` — Same as Button but renders as link. Props: to (route/URL), target, variant, size, color, icon
65
- - `Input` — label, error, icon, iconPosition, size, onIconClick, readOnly. Extends HTML input attributes
66
- - `AutocompleteInput<T,K>` Searchable dropdown. Props: options, value, onChange, multiple, getOptionLabel, getOptionValue, renderOption, noResultsText
67
- - `SearchSelectInput<T,K>` Dialog-based async search. Props: onSearchPromiseFn, onSingleSearchPromiseFn, dialogTitle, getOptionLabel, getOptionValue
68
- - `DatePicker` Standalone calendar. Props: value (Dayjs), onChange, startWeekOn
69
- - `DateInput` Input with DatePicker dropdown. Props: value (Dayjs|string), onChange, format ("dd/mm/yyyy"|"mm/dd/yyyy")
70
- - `Checkbox` Props: label, labelPosition, error, size, readOnly
71
- - `RadioButtonGroup` Props: options ({label,value,disabled}[]), value, onChange, direction ("vertical"|"horizontal"), gap, size
72
- - `CurrencyInput` Numeric input formatted as currency (es-AR: 1.234,56). Props: value (number), onChange
73
- - `Pagination` — URL-based pagination. Props: page, pages, total, isLoading, fieldName
74
-
75
- ### Layout & Data
76
- - `Card` — Props: title, subtitle, headerActions, footer, variant ("default"|"elevated"|"outlined"), compact, alwaysDisplayHeaderActions
77
- - `AppLayout` — Main layout. Props: navbar (NavbarInterface), leftDrawer (LeftDrawerInterface), children
78
- - `Collection` — Flex container. Props: gap, direction, wrap
79
- - `DataField` — Label+value pair. Props: label, value, inline, align, link
80
- - `TabsGroup` + `TabPanel` Tabbed interface. TabsGroup: tabs ({id,label}[]), paramName (URL sync). TabPanel: tabId
81
- - `DataTable<T>` — Data table. Props: columns (DataTableColumn<T>[]), rows, maxRows, isLoading, loadingRows, compact, locale
82
- - DataTableColumn: header, value (key or function), type ("text"|"numeric"|"currency"|"date"), actions, width, align, tooltip, footer
83
- - `Accordion` — Collapsible section. Props: title, icon, rightNode, defaultOpen, variant, onToggle
84
- - `Menu<T>`Simple menu list. Props: options, onOptionSelected, getOptionLabel, renderOption
85
- - `DropdownMenu<T>`Portal dropdown. Props: options, onOptionSelected, renderNode, openOnHover, replaceOnSingleOption
86
- - `DropdownPanel` — Portal dropdown with arbitrary content. Props: children, renderNode, openOnHover
87
- - `Filter` — Versatile filter by type. filterType: "text"|"number"|"date"|"autocomplete"|"search"|"searchSelect". Props: paramName (URL sync), label, value, onChange
88
-
89
- ### Utility & Feedback
90
- - `Badge` Props: variant, size, rounded, icon, iconPosition, bg, textColor, onClick
91
- - `Avatar` — Props: text (for initials), image, bgColor, textColor, size
92
- - `RoadMap` — Stage visualization. Props: stages ({name, description?, icon?, variant?, bg?, disabled?}[])
93
- - `Dialog` — Modal. Props: isOpen, title, children, footer, onClose, closeOnOverlayClick, compact, bodyWidth
94
- - `Loader` — Loading indicator. Props: isLoading, text, keepContentWhileLoading, contentLoadingNode, overlayClassName
95
- - `FiltersDialog` Groups multiple filters in dialog. Props: filters (FilterConfig[])
96
- - `Snackbar` — (Used internally) Toast notification
97
- - `SnackbarContainer` Place at root. Props: position, maxSnackbars
98
- - `Skeleton` — Pulse placeholder. Props: className
99
- - `ThemeSwitcher`No props. Self-contained theme toggle
100
-
101
- ### Templates
102
- - `LoginForm` — Props: onSubmit, loading, error, className
103
- - `RegistrationForm` — Props: onSubmit, loading, error, className
104
- - `ContactForm` Props: onSubmit, loading, success, error, className
105
- - `DashboardLayout` — Props: title, subtitle, stats (DashboardStat[]), actions, children
106
- - `SidebarLayout` — Props: title, menuItems (MenuItem[]), user (User), onLogout, children
107
- - `FormPattern` — Dynamic form builder. Props: title, fields (FormField[]), onSubmit, gridCols, submitText, submitIcon, loading, error, success
108
- - `ListPattern<T>`List page with Card + search + filters + pagination + DataTable. Props: title, columns, rows, searchParamName, onAdd, addButtonText, filtersNode, page, pages, total, isLoading, compact
109
-
110
- ### Contexts & Hooks
111
- - `ThemeProvider` — initialTheme, storageKey, forceInitialTheme, onThemeChange
112
- - `useTheme()` — Returns: theme, setTheme, updateTheme, currentThemeName, availableThemes, isDark, resetToDefault
113
- - `AuthProvider` — getToken, getUserData, refreshToken, removeToken
114
- - `AuthContext` — user, login, logout, isAuthenticated, isLoading
115
- - `CrudProvider<T>` — getPromise, getItemPromise, postPromise, putPromise, deletePromise, urlParams, limit, pageParam
116
- - `useCrud<T>()` — Returns: list, item, isLoading, pagination, fetchItems, fetchItem, createItem, updateItem, deleteItem, params, page, pages, total
117
- - `SnackbarProvider` + `useSnackbar()` showSnackbar(message, variant?, options?), removeSnackbar(id)
118
- - `AppLayoutProvider` — Combines Theme + Snackbar + AppLayout. useAppLayout() to set navbar/drawer dynamically
119
- - `useAsyncRequest(options)` — Returns: execute(fn), isLoading. Options: successMessage, errorMessage, onSuccess, onError
120
- - `useBreakpoint()` — Returns: breakpoint, windowSize, isMobile, isTablet, isDesktop
121
- - `useThemeOverride(options)` — Returns: applyOverride, revertOverride, revertAllOverrides
122
- - `useElementScroll(ref)` — Returns: scrollY, scrollDirection
123
- - `useEnum(enum)`Returns: getArray(), getInstance(id)
124
-
125
- ### Services
126
- - `apiClient` — HTTP client. Methods: get<T>, post<T>, put<T>, del<T>, getFile, downloadFile, uploadFile, openFile
127
- - `setApiClientTokenProvider(fn)` — Set auth token globally
128
- - `createApiClient(config)` — Create isolated API client instance
129
-
130
- ### Helpers
131
- - `currencyFormat(n)` "1.234,56"
132
- - `getErrorMessage(error)` user-friendly error string
133
- - `getInitialLetters(text)` "JP" from "Juan Pérez"
134
- - `objectToQueryString(obj)` "a=1&b=2"
135
- - `queryStringToObject(str)` {a: "1", b: "2"}
136
- - `promiseMapper(promise, mapperFn)` — Maps paginated/array/single results
137
- - `RegularExpressions` — .email, .dateString, .password(config)
138
-
139
- ### Interfaces
140
- - `NameValueInterface<T>` — { name: string; value: T; extras?: any }
141
- - `PaginationInterface<T>`{ list: T[]; limit: number; page: number; pages: number; total: number }
142
-
143
- ## Common Patterns
144
-
145
- ### CRUD List Page
146
- ​```tsx
147
- <CrudProvider<User>
148
- getPromise={(params) => apiClient.get({ url: "/api/users", params })}
149
- deletePromise={{ execute: (u) => apiClient.del({ url: `/api/users/${u.id}` }), successMessage: "Eliminado" }}
150
- urlParams={["nombre"]}
151
- >
152
- <UserList />
153
- </CrudProvider>
154
-
155
- function UserList() {
156
- const { list, isLoading, pagination, deleteItem } = useCrud<User>();
157
- const columns: DataTableColumn<User>[] = [
158
- { header: "Nombre", value: "name" },
159
- { header: "Email", value: "email" },
160
- { actions: (row) => [
161
- <Button key="del" variant="ghost" size="sm" icon="fa-trash" color="danger"
162
- loading={deleteItem.isLoading} onClick={() => deleteItem.execute(row)}>
163
- Eliminar
164
- </Button>
165
- ]}
166
- ];
167
- return (
168
- <Card title="Usuarios">
169
- <Filter filterType="text" paramName="nombre" label="Nombre" />
170
- <DataTable columns={columns} rows={list ?? []} isLoading={isLoading} />
171
- {pagination}
172
- </Card>
173
- );
174
- }
175
- ​```
176
-
177
- ### Form with Async Save
178
- ​```tsx
179
- const { execute, isLoading } = useAsyncRequest({
180
- successMessage: "Guardado exitosamente",
181
- errorMessage: (err) => getErrorMessage(err),
182
- });
183
-
184
- <Card title="Nuevo Usuario">
185
- <Input label="Nombre" value={name} onChange={(e) => setName(e.target.value)} icon="fa-user" />
186
- <Input label="Email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} icon="fa-envelope" />
187
- <Button variant="primary" icon="fa-save" loading={isLoading}
188
- onClick={() => execute(() => apiClient.post({ url: "/api/users", body: { name, email } }))}>
189
- Guardar
190
- </Button>
191
- </Card>
192
- ​```
193
-
194
- ### Dialog Confirmation
195
- ​```tsx
196
- <Dialog isOpen={showConfirm} title="Confirmar eliminación" onClose={() => setShowConfirm(false)}
197
- footer={<>
198
- <Button variant="ghost" onClick={() => setShowConfirm(false)}>Cancelar</Button>
199
- <Button variant="primary" color="danger" icon="fa-trash" onClick={handleDelete}>Eliminar</Button>
200
- </>}
201
- >
202
- <p>¿Está seguro?</p>
203
- </Dialog>
204
- ​```
205
-
206
- ### List Page (without CrudContext)
207
- Full implementation of Card + search + filters + pagination + DataTable. Uses URL query params for filters and pagination.
208
- ​```tsx
209
- import { useState, useEffect } from "react";
210
- import { useSearchParams } from "react-router-dom";
211
- import {
212
- Card, Button, Collection, Filter, Pagination, DataTable, Dialog,
213
- } from "flysoft-react-ui";
214
- import type { DataTableColumn } from "flysoft-react-ui";
215
-
216
- interface User { id: number; name: string; email: string; role: string; }
217
-
218
- function UserListPage() {
219
- const [searchParams] = useSearchParams();
220
- const [users, setUsers] = useState<User[]>([]);
221
- const [isLoading, setIsLoading] = useState(false);
222
- const [page, setPage] = useState(1);
223
- const [pages, setPages] = useState(1);
224
- const [total, setTotal] = useState(0);
225
- const [showDeleteDialog, setShowDeleteDialog] = useState(false);
226
- const [selectedUser, setSelectedUser] = useState<User>();
227
-
228
- // Read filters from URL
229
- const search = searchParams.get("buscar") || "";
230
- const role = searchParams.get("rol") || "";
231
- const currentPage = Number(searchParams.get("pagina") || "1");
232
-
233
- useEffect(() => {
234
- setIsLoading(true);
235
- fetchUsers({ search, role, page: currentPage }).then((res) => {
236
- setUsers(res.list);
237
- setPage(res.page);
238
- setPages(res.pages);
239
- setTotal(res.total);
240
- setIsLoading(false);
241
- });
242
- }, [search, role, currentPage]);
243
-
244
- const columns: DataTableColumn<User>[] = [
245
- { header: "Nombre", value: (row) => row.name },
246
- { header: "Email", value: (row) => row.email },
247
- { header: "Rol", value: (row) => row.role },
248
- {
249
- align: "center",
250
- actions: (row) => [
251
- <Button key="edit" size="sm" variant="ghost" icon="fa-edit"
252
- onClick={() => handleEdit(row)}>Editar</Button>,
253
- <Button key="del" size="sm" variant="ghost" icon="fa-trash"
254
- onClick={() => { setSelectedUser(row); setShowDeleteDialog(true); }}>
255
- Eliminar
256
- </Button>,
257
- ],
258
- },
259
- ];
260
-
261
- return (
262
- <>
263
- <Card
264
- title="Usuarios"
265
- alwaysDisplayHeaderActions
266
- headerActions={
267
- <Button icon="fa-plus" onClick={() => handleAdd()}>
268
- Nuevo Usuario
269
- </Button>
270
- }
271
- >
272
- <div className="flex justify-between items-center my-2">
273
- <Collection direction="row" wrap>
274
- <Filter paramName="buscar" label="Buscar" filterType="search" />
275
- <Filter paramName="rol" label="Rol" filterType="autocomplete"
276
- options={[
277
- { label: "Admin", value: "admin" },
278
- { label: "Editor", value: "editor" },
279
- ]}
280
- />
281
- </Collection>
282
- <Collection direction="row" wrap>
283
- <Pagination page={page} pages={pages} total={total}
284
- fieldName="pagina" isLoading={isLoading} />
285
- </Collection>
286
- </div>
287
- <DataTable columns={columns} rows={users}
288
- isLoading={isLoading} loadingRows={10} maxRows={10} />
289
- </Card>
290
-
291
- <Dialog isOpen={showDeleteDialog} title="Eliminar usuario"
292
- onClose={() => setShowDeleteDialog(false)}
293
- footer={<>
294
- <Button variant="outline" onClick={() => setShowDeleteDialog(false)}>Cancelar</Button>
295
- <Button variant="primary" color="danger" icon="fa-trash"
296
- onClick={() => handleDelete(selectedUser)}>Eliminar</Button>
297
- </>}
298
- >
299
- <p>¿Está seguro de eliminar a {selectedUser?.name}?</p>
300
- </Dialog>
301
- </>
302
- );
303
- }
304
- ​```
305
-
306
- ### List Page (using ListPattern template)
307
- Same pattern as above but using the `ListPattern` template component for less boilerplate:
308
- ​```tsx
309
- import { ListPattern, Filter } from "flysoft-react-ui";
310
- import type { DataTableColumn } from "flysoft-react-ui";
311
-
312
- const columns: DataTableColumn<User>[] = [
313
- { header: "Nombre", value: (row) => row.name },
314
- { header: "Email", value: (row) => row.email },
315
- { actions: (row) => [
316
- <Button key="edit" size="sm" variant="ghost" icon="fa-edit" onClick={() => edit(row)}>Editar</Button>,
317
- ]},
318
- ];
319
-
320
- <ListPattern<User>
321
- title="Usuarios"
322
- columns={columns}
323
- rows={users}
324
- searchParamName="buscar"
325
- addButtonText="Nuevo"
326
- onAdd={() => setShowForm(true)}
327
- filtersNode={
328
- <Filter paramName="rol" label="Rol" filterType="autocomplete"
329
- options={[{ label: "Admin", value: "admin" }]} />
330
- }
331
- page={page} pages={pages} total={total}
332
- isLoading={isLoading}
333
- />
334
- ​```
335
- ```
336
-
337
- ---
338
-
339
- ## 4) Notes for library maintainers
340
-
341
- - `src/docs/**` is local demo/dev-only and is not part of public API.
342
- - Public API must be exported from `src/index.ts`.
343
- - See `AI_CONTEXT.md` for complete prop interfaces and detailed documentation.
1
+ # AI Integration Guide (Consumer Projects)
2
+
3
+ This file is designed to be copied into any client project that consumes `flysoft-react-ui`. It helps AI agents understand the complete API surface and generate correct code.
4
+
5
+ ## 1) Install
6
+
7
+ ```bash
8
+ npm install flysoft-react-ui
9
+ ```
10
+
11
+ ## 2) Required Runtime Setup
12
+
13
+ At app root:
14
+
15
+ ```tsx
16
+ import { ThemeProvider } from "flysoft-react-ui";
17
+ import "flysoft-react-ui/styles";
18
+
19
+ export function AppRoot() {
20
+ return <ThemeProvider initialTheme="light">{/* app */}</ThemeProvider>;
21
+ }
22
+ ```
23
+
24
+ For data-heavy apps (admin panels, dashboards, CRUDs with lots of info per
25
+ screen), set a tighter global `density`:
26
+
27
+ ```tsx
28
+ <ThemeProvider initialTheme="light" density="dense">
29
+ {/* app */}
30
+ </ThemeProvider>
31
+ ```
32
+
33
+ Density is a global axis (`"comfortable"` | `"compact"` | `"dense"`) that
34
+ adjusts padding, gaps, typography and control heights via CSS variables.
35
+ Per-component overrides (`compact`, `size`) keep working on top of it.
36
+
37
+ For full app layout with navbar, sidebar, and snackbars:
38
+
39
+ ```tsx
40
+ import { AppLayoutProvider } from "flysoft-react-ui";
41
+ import "flysoft-react-ui/styles";
42
+
43
+ export function AppRoot() {
44
+ return (
45
+ <AppLayoutProvider
46
+ initialTheme="light"
47
+ density="dense" // optional: comfortable | compact | dense
48
+ initialNavbar={{ navBarLeftNode: <h1>Mi App</h1>, fullWidthNavbar: true }}
49
+ >
50
+ {/* routes */}
51
+ </AppLayoutProvider>
52
+ );
53
+ }
54
+ ```
55
+
56
+ ## 3) Copy-paste prompt for AI agents
57
+
58
+ Copy the following block into `.cursorrules`, `AGENTS.md`, `copilot-instructions.md`, `CLAUDE.md`, or your AI system prompt:
59
+
60
+ ---
61
+
62
+ ```md
63
+ This project uses `flysoft-react-ui` as the default UI library.
64
+
65
+ ## Rules
66
+ 1. Always import from `flysoft-react-ui` (top-level only). Never from internal paths.
67
+ 2. Prefer existing library components before creating custom UI. Never duplicate Button, Input, Card, Badge, Dialog, etc.
68
+ 3. Use exported TypeScript types for type safety (e.g. ButtonProps, DataTableColumn<T>).
69
+ 4. Keep style import at app root only: `import "flysoft-react-ui/styles";`
70
+ 5. Wrap app with `ThemeProvider` (or `AppLayoutProvider` for full layout).
71
+ 6. Use FontAwesome 5 icon classes (`fa-*`). Components auto-normalize to light style (fal). Never use other icon libraries.
72
+ 7. Use theme CSS variables for custom styling: `var(--color-primary)`, `var(--color-bg-default)`, etc.
73
+ 8. To change a form-control's background (e.g. inside a same-color Card), pass a `bg-*` via `className`form-controls merge classes with `twMerge` so it overrides the default. Ex: `<Input className="bg-[var(--color-bg-secondary)]" />`. For `Filter`, use its `bgColor` prop instead.
74
+
75
+ ## Available Components
76
+
77
+ ### Form Controls
78
+ - `Button` — variant: "primary"|"outline"|"ghost", size: "sm"|"md"|"lg", color: "primary"|"secondary"|"success"|"warning"|"danger"|"info", icon, loading, bg, textColor
79
+ - `LinkButton` — Same as Button but renders as link. Props: to (route/URL), target, variant, size, color, icon
80
+ - `Input` — label, error, icon, iconPosition, size, onIconClick, readOnly. Extends HTML input attributes
81
+ - `AutocompleteInput<T,K>` — Searchable dropdown. Props: options, value, onChange, multiple, getOptionLabel, getOptionValue, renderOption, noResultsText
82
+ - `SearchSelectInput<T,K>` Dialog-based async search. Props: onSearchPromiseFn, onSingleSearchPromiseFn, dialogTitle, getOptionLabel, getOptionValue
83
+ - `DatePicker` — Standalone calendar. Props: value (Dayjs), onChange, startWeekOn
84
+ - `DateInput`Input with DatePicker dropdown. Props: value (Dayjs|string), onChange, format ("dd/mm/yyyy"|"mm/dd/yyyy")
85
+ - `Checkbox` — Props: label, labelPosition, error, size, readOnly
86
+ - `RadioButtonGroup` — Props: options ({label,value,disabled}[]), value, onChange, direction ("vertical"|"horizontal"), gap, size
87
+ - `CurrencyInput` — Numeric input formatted as currency (es-AR: 1.234,56). Props: value (number), onChange
88
+ - `Pagination` — URL-based pagination. Props: page, pages, total, isLoading, fieldName
89
+
90
+ ### Layout & Data
91
+ - `Card` — Props: title, subtitle, headerActions, footer, variant ("default"|"elevated"|"outlined"), compact (override local de densidad → fuerza preset compact en `--flysoft-density-*` dentro de la card y descendientes), alwaysDisplayHeaderActions
92
+ - `AppLayout` — Main layout. Props: navbar (NavbarInterface), leftDrawer (LeftDrawerInterface), children
93
+ - `Collection` — Flex container density-aware. Props: gap ("tight"|"sm"|"md"|"lg"|string), direction, wrap, density ("comfortable"|"compact"|"dense", override local que redefine `--flysoft-density-*` para esta collection y descendientes)
94
+ - `DataField` — Label+value pair density-aware. Props: label, value, inline, align, link, size ("sm"|"md", "sm" baja un nivel de tipografía), gap ("tight"|"sm"|"md", separación label/value en stack), hideColon (oculta `:` en modo inline)
95
+ - `TabsGroup` + `TabPanel` Tabbed interface. TabsGroup: tabs ({id,label}[]), paramName (URL sync). TabPanel: tabId
96
+ - `DataTable<T>`Data table density-aware. Props: columns (DataTableColumn<T>[]), rows, maxRows, isLoading, loadingRows, compact (override local de densidad → fuerza preset compact en `--flysoft-density-*` dentro de la tabla y sus DropdownMenu de acciones), locale
97
+ - DataTableColumn: header, value (key or function), type ("text"|"numeric"|"currency"|"date"), actions, width, align, tooltip, footer
98
+ - `Accordion` — Collapsible section. Props: title, icon, rightNode, defaultOpen, variant, headerClassName, contentClassName, onToggle
99
+ - `Menu<T>`Simple menu list. Props: options, onOptionSelected, getOptionLabel, renderOption
100
+ - `DropdownMenu<T>` — Portal dropdown. Props: options, onOptionSelected, renderNode, openOnHover, replaceOnSingleOption
101
+ - `DropdownPanel` — Portal dropdown with arbitrary content. Props: children, renderNode, openOnHover
102
+ - `Filter` — Versatile filter by type. filterType: "text"|"number"|"date"|"autocomplete"|"search"|"searchSelect". Props: paramName (URL sync), label, value, onChange
103
+
104
+ ### Utility & Feedback
105
+ - `Badge` — Props: variant, size, rounded, icon, iconPosition, bg, textColor, onClick
106
+ - `Avatar` — Props: text (for initials), image, bgColor, textColor, size
107
+ - `RoadMap` — Stage visualization. Props: stages ({name, description?, icon?, variant?, bg?, disabled?}[])
108
+ - `Dialog`Modal density-aware. Props: isOpen, title, children, footer, onClose, closeOnOverlayClick, compact (override local de densidad), bodyWidth
109
+ - `Loader` — Loading indicator. Props: isLoading, text, keepContentWhileLoading, contentLoadingNode, overlayClassName
110
+ - `FiltersDialog` Groups multiple filters in dialog. Props: filters (FilterConfig[])
111
+ - `Snackbar` — (Used internally) Toast notification
112
+ - `SnackbarContainer` — Place at root. Props: position, maxSnackbars
113
+ - `Skeleton` — Pulse placeholder. Props: className
114
+ - `ThemeSwitcher` — No props. Self-contained theme toggle
115
+
116
+ ### Templates
117
+ - `LoginForm` Props: onSubmit, loading, error, className
118
+ - `RegistrationForm` — Props: onSubmit, loading, error, className
119
+ - `ContactForm` — Props: onSubmit, loading, success, error, className
120
+ - `DashboardLayout` — Props: title, subtitle, stats (DashboardStat[]), actions, children
121
+ - `SidebarLayout` — Props: title, menuItems (MenuItem[]), user (User), onLogout, children
122
+ - `FormPattern` — Dynamic form builder. Props: title, fields (FormField[]), onSubmit, gridCols, submitText, submitIcon, loading, error, success
123
+ - `ListPattern<T>`List page with Card + search + filters + pagination + DataTable. Props: title, columns, rows, searchParamName, onAdd, addButtonText, filtersNode, page, pages, total, isLoading, compact
124
+
125
+ ### Contexts & Hooks
126
+ - `ThemeProvider` — initialTheme, storageKey, forceInitialTheme, onThemeChange, density ("comfortable" | "compact" | "dense"), densityStorageKey, forceInitialDensity, onDensityChange
127
+ - `useTheme()` — Returns: theme, setTheme, updateTheme, currentThemeName, availableThemes, isDark, resetToDefault, density, setDensity
128
+ - `AuthProvider` — getToken, getUserData, refreshToken, removeToken
129
+ - `AuthContext` — user, login, logout, isAuthenticated, isLoading
130
+ - `CrudProvider<T>` — getPromise, getItemPromise, postPromise, putPromise, deletePromise, urlParams, limit, pageParam
131
+ - `useCrud<T>()` Returns: list, item, isLoading, pagination, fetchItems, fetchItem, createItem, updateItem, deleteItem, params, page, pages, total
132
+ - `SnackbarProvider` + `useSnackbar()` showSnackbar(message, variant?, options?), removeSnackbar(id)
133
+ - `AppLayoutProvider` — Combines Theme + Snackbar + AppLayout. Accepts density/densityStorageKey/forceInitialDensity/onDensityChange (propagated to ThemeProvider) in addition to theme props. useAppLayout() to set navbar/drawer dynamically
134
+ - `useAsyncRequest(options)` Returns: execute(fn), isLoading. Options: successMessage, errorMessage, onSuccess, onError
135
+ - `useBreakpoint()` Returns: breakpoint, windowSize, isMobile, isTablet, isDesktop
136
+ - `useThemeOverride(options)` — Returns: applyOverride, revertOverride, revertAllOverrides
137
+ - `useElementScroll(ref)` — Returns: scrollY, scrollDirection
138
+ - `useEnum(enum)` — Returns: getArray(), getInstance(id)
139
+
140
+ ### Services
141
+ - `apiClient`HTTP client. Methods: get<T>, post<T>, put<T>, del<T>, getFile, downloadFile, uploadFile, openFile
142
+ - `setApiClientTokenProvider(fn)` — Set auth token globally
143
+ - `createApiClient(config)` — Create isolated API client instance
144
+
145
+ ### Helpers
146
+ - `currencyFormat(n)` → "1.234,56"
147
+ - `getErrorMessage(error)` → user-friendly error string
148
+ - `getInitialLetters(text)` "JP" from "Juan Pérez"
149
+ - `objectToQueryString(obj)` "a=1&b=2"
150
+ - `queryStringToObject(str)` → {a: "1", b: "2"}
151
+ - `promiseMapper(promise, mapperFn)` — Maps paginated/array/single results
152
+ - `RegularExpressions` — .email, .dateString, .password(config)
153
+
154
+ ### Interfaces
155
+ - `NameValueInterface<T>` { name: string; value: T; extras?: any }
156
+ - `PaginationInterface<T>` — { list: T[]; limit: number; page: number; pages: number; total: number }
157
+
158
+ ## Common Patterns
159
+
160
+ ### CRUD List Page
161
+ ​```tsx
162
+ <CrudProvider<User>
163
+ getPromise={(params) => apiClient.get({ url: "/api/users", params })}
164
+ deletePromise={{ execute: (u) => apiClient.del({ url: `/api/users/${u.id}` }), successMessage: "Eliminado" }}
165
+ urlParams={["nombre"]}
166
+ >
167
+ <UserList />
168
+ </CrudProvider>
169
+
170
+ function UserList() {
171
+ const { list, isLoading, pagination, deleteItem } = useCrud<User>();
172
+ const columns: DataTableColumn<User>[] = [
173
+ { header: "Nombre", value: "name" },
174
+ { header: "Email", value: "email" },
175
+ { actions: (row) => [
176
+ <Button key="del" variant="ghost" size="sm" icon="fa-trash" color="danger"
177
+ loading={deleteItem.isLoading} onClick={() => deleteItem.execute(row)}>
178
+ Eliminar
179
+ </Button>
180
+ ]}
181
+ ];
182
+ return (
183
+ <Card title="Usuarios">
184
+ <Filter filterType="text" paramName="nombre" label="Nombre" />
185
+ <DataTable columns={columns} rows={list ?? []} isLoading={isLoading} />
186
+ {pagination}
187
+ </Card>
188
+ );
189
+ }
190
+ ​```
191
+
192
+ ### Form with Async Save
193
+ ​```tsx
194
+ const { execute, isLoading } = useAsyncRequest({
195
+ successMessage: "Guardado exitosamente",
196
+ errorMessage: (err) => getErrorMessage(err),
197
+ });
198
+
199
+ <Card title="Nuevo Usuario">
200
+ <Input label="Nombre" value={name} onChange={(e) => setName(e.target.value)} icon="fa-user" />
201
+ <Input label="Email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} icon="fa-envelope" />
202
+ <Button variant="primary" icon="fa-save" loading={isLoading}
203
+ onClick={() => execute(() => apiClient.post({ url: "/api/users", body: { name, email } }))}>
204
+ Guardar
205
+ </Button>
206
+ </Card>
207
+ ​```
208
+
209
+ ### Dialog Confirmation
210
+ ​```tsx
211
+ <Dialog isOpen={showConfirm} title="Confirmar eliminación" onClose={() => setShowConfirm(false)}
212
+ footer={<>
213
+ <Button variant="ghost" onClick={() => setShowConfirm(false)}>Cancelar</Button>
214
+ <Button variant="primary" color="danger" icon="fa-trash" onClick={handleDelete}>Eliminar</Button>
215
+ </>}
216
+ >
217
+ <p>¿Está seguro?</p>
218
+ </Dialog>
219
+ ​```
220
+
221
+ ### List Page (without CrudContext)
222
+ Full implementation of Card + search + filters + pagination + DataTable. Uses URL query params for filters and pagination.
223
+ ​```tsx
224
+ import { useState, useEffect } from "react";
225
+ import { useSearchParams } from "react-router-dom";
226
+ import {
227
+ Card, Button, Collection, Filter, Pagination, DataTable, Dialog,
228
+ } from "flysoft-react-ui";
229
+ import type { DataTableColumn } from "flysoft-react-ui";
230
+
231
+ interface User { id: number; name: string; email: string; role: string; }
232
+
233
+ function UserListPage() {
234
+ const [searchParams] = useSearchParams();
235
+ const [users, setUsers] = useState<User[]>([]);
236
+ const [isLoading, setIsLoading] = useState(false);
237
+ const [page, setPage] = useState(1);
238
+ const [pages, setPages] = useState(1);
239
+ const [total, setTotal] = useState(0);
240
+ const [showDeleteDialog, setShowDeleteDialog] = useState(false);
241
+ const [selectedUser, setSelectedUser] = useState<User>();
242
+
243
+ // Read filters from URL
244
+ const search = searchParams.get("buscar") || "";
245
+ const role = searchParams.get("rol") || "";
246
+ const currentPage = Number(searchParams.get("pagina") || "1");
247
+
248
+ useEffect(() => {
249
+ setIsLoading(true);
250
+ fetchUsers({ search, role, page: currentPage }).then((res) => {
251
+ setUsers(res.list);
252
+ setPage(res.page);
253
+ setPages(res.pages);
254
+ setTotal(res.total);
255
+ setIsLoading(false);
256
+ });
257
+ }, [search, role, currentPage]);
258
+
259
+ const columns: DataTableColumn<User>[] = [
260
+ { header: "Nombre", value: (row) => row.name },
261
+ { header: "Email", value: (row) => row.email },
262
+ { header: "Rol", value: (row) => row.role },
263
+ {
264
+ align: "center",
265
+ actions: (row) => [
266
+ <Button key="edit" size="sm" variant="ghost" icon="fa-edit"
267
+ onClick={() => handleEdit(row)}>Editar</Button>,
268
+ <Button key="del" size="sm" variant="ghost" icon="fa-trash"
269
+ onClick={() => { setSelectedUser(row); setShowDeleteDialog(true); }}>
270
+ Eliminar
271
+ </Button>,
272
+ ],
273
+ },
274
+ ];
275
+
276
+ return (
277
+ <>
278
+ <Card
279
+ title="Usuarios"
280
+ alwaysDisplayHeaderActions
281
+ headerActions={
282
+ <Button icon="fa-plus" onClick={() => handleAdd()}>
283
+ Nuevo Usuario
284
+ </Button>
285
+ }
286
+ >
287
+ <div className="flex justify-between items-center my-2">
288
+ <Collection direction="row" wrap>
289
+ <Filter paramName="buscar" label="Buscar" filterType="search" />
290
+ <Filter paramName="rol" label="Rol" filterType="autocomplete"
291
+ options={[
292
+ { label: "Admin", value: "admin" },
293
+ { label: "Editor", value: "editor" },
294
+ ]}
295
+ />
296
+ </Collection>
297
+ <Collection direction="row" wrap>
298
+ <Pagination page={page} pages={pages} total={total}
299
+ fieldName="pagina" isLoading={isLoading} />
300
+ </Collection>
301
+ </div>
302
+ <DataTable columns={columns} rows={users}
303
+ isLoading={isLoading} loadingRows={10} maxRows={10} />
304
+ </Card>
305
+
306
+ <Dialog isOpen={showDeleteDialog} title="Eliminar usuario"
307
+ onClose={() => setShowDeleteDialog(false)}
308
+ footer={<>
309
+ <Button variant="outline" onClick={() => setShowDeleteDialog(false)}>Cancelar</Button>
310
+ <Button variant="primary" color="danger" icon="fa-trash"
311
+ onClick={() => handleDelete(selectedUser)}>Eliminar</Button>
312
+ </>}
313
+ >
314
+ <p>¿Está seguro de eliminar a {selectedUser?.name}?</p>
315
+ </Dialog>
316
+ </>
317
+ );
318
+ }
319
+ ​```
320
+
321
+ ### List Page (using ListPattern template)
322
+ Same pattern as above but using the `ListPattern` template component for less boilerplate:
323
+ ​```tsx
324
+ import { ListPattern, Filter } from "flysoft-react-ui";
325
+ import type { DataTableColumn } from "flysoft-react-ui";
326
+
327
+ const columns: DataTableColumn<User>[] = [
328
+ { header: "Nombre", value: (row) => row.name },
329
+ { header: "Email", value: (row) => row.email },
330
+ { actions: (row) => [
331
+ <Button key="edit" size="sm" variant="ghost" icon="fa-edit" onClick={() => edit(row)}>Editar</Button>,
332
+ ]},
333
+ ];
334
+
335
+ <ListPattern<User>
336
+ title="Usuarios"
337
+ columns={columns}
338
+ rows={users}
339
+ searchParamName="buscar"
340
+ addButtonText="Nuevo"
341
+ onAdd={() => setShowForm(true)}
342
+ filtersNode={
343
+ <Filter paramName="rol" label="Rol" filterType="autocomplete"
344
+ options={[{ label: "Admin", value: "admin" }]} />
345
+ }
346
+ page={page} pages={pages} total={total}
347
+ isLoading={isLoading}
348
+ />
349
+ ​```
350
+ ```
351
+
352
+ ---
353
+
354
+ ## 4) Notes for library maintainers
355
+
356
+ - `src/docs/**` is local demo/dev-only and is not part of public API.
357
+ - Public API must be exported from `src/index.ts`.
358
+ - See `AI_CONTEXT.md` for complete prop interfaces and detailed documentation.