bonsaif-ui 0.1.0 → 0.1.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.
- package/CHANGELOG.md +5 -0
- package/README.md +271 -1
- package/dist/bonsaif-ui.js +1967 -1330
- package/dist/bonsaif-ui.umd.cjs +1 -1
- package/dist/components/composition/DynamicDetailTabs.d.ts +74 -0
- package/dist/components/composition/DynamicDetailTabs.d.ts.map +1 -0
- package/dist/components/composition/index.d.ts +3 -0
- package/dist/components/composition/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/PageSearchInput.d.ts +6 -1
- package/dist/components/layout/PageSearchInput.d.ts.map +1 -1
- package/dist/components/ui/DataTable.d.ts +3 -1
- package/dist/components/ui/DataTable.d.ts.map +1 -1
- package/dist/components/ui/StepWizard.d.ts +43 -0
- package/dist/components/ui/StepWizard.d.ts.map +1 -0
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/users/UserAvatar.d.ts +11 -4
- package/dist/components/users/UserAvatar.d.ts.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1 - 2026-05-02
|
|
4
|
+
|
|
5
|
+
- Mark the generated JavaScript bundle as a Next.js client bundle.
|
|
6
|
+
- Add compatibility props to `UserAvatar` for name display and label styling.
|
|
7
|
+
|
|
3
8
|
## 0.1.0 - 2026-05-02
|
|
4
9
|
|
|
5
10
|
- Initial npm-ready release of reusable Bonsaif React UI components.
|
package/README.md
CHANGED
|
@@ -23,13 +23,283 @@ export function Example() {
|
|
|
23
23
|
|
|
24
24
|
The package includes reusable components migrated from `prime-auth`:
|
|
25
25
|
|
|
26
|
-
- UI: `Button`, `CalendarPicker`, `CompactFilterSelect`, `CompactSearchInput`, `ConfirmDialog`, `DataTable`, `DateRangePicker`, `Drawer`, `InputField`, `LoadingState`, `Modal`, `MouseTooltip`, `SearchableSelect`, `TextInput`, `Toggle`.
|
|
26
|
+
- UI: `Button`, `CalendarPicker`, `CompactFilterSelect`, `CompactSearchInput`, `ConfirmDialog`, `DataTable`, `DateRangePicker`, `Drawer`, `InputField`, `LoadingState`, `Modal`, `MouseTooltip`, `SearchableSelect`, `StepWizard`, `TextInput`, `Toggle`.
|
|
27
|
+
- Composition: `DynamicDetailTabs`.
|
|
27
28
|
- Layout: `AuthShell`, `GlobalScrollbars`, `PageHeader`, `PageSearchInput`, `PrimeAuthRouteShell`.
|
|
28
29
|
- Messaging and services: `PrimeSuiteChatFrame`, `MicroserviceFrame`.
|
|
29
30
|
- Notifications and users: `PrimeAuthToaster`, `UserAvatar`.
|
|
30
31
|
|
|
31
32
|
App-coupled components were adapted to receive routing, session, and navigation data through props so the library does not import private `prime-auth` modules.
|
|
32
33
|
|
|
34
|
+
## DataTable
|
|
35
|
+
|
|
36
|
+
`DataTable` owns its horizontal and vertical scrolling. When a table is rendered inside a constrained panel, pass `minTableWidth` so the table keeps usable column widths and exposes its own internal horizontal scrollbar.
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<DataTable
|
|
40
|
+
columns={columns}
|
|
41
|
+
data={rows}
|
|
42
|
+
getRowId={(row) => row.id}
|
|
43
|
+
minTableWidth={810}
|
|
44
|
+
/>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## StepWizard
|
|
48
|
+
|
|
49
|
+
`StepWizard` renders animated, gated steps. A future step stays locked until every previous non-optional step is marked as completed.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { Button, StepWizard } from "bonsaif-ui";
|
|
53
|
+
|
|
54
|
+
<StepWizard
|
|
55
|
+
steps={[
|
|
56
|
+
{
|
|
57
|
+
id: "method",
|
|
58
|
+
title: "Metodo de carga",
|
|
59
|
+
description: "Cliente, plantilla y archivo",
|
|
60
|
+
content: (wizard) => (
|
|
61
|
+
<Button onClick={() => wizard.completeStep()}>
|
|
62
|
+
Marcar completado
|
|
63
|
+
</Button>
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "sms",
|
|
68
|
+
title: "Configuracion de SMS",
|
|
69
|
+
content: (wizard) => (
|
|
70
|
+
<Button onClick={() => wizard.completeStep()}>
|
|
71
|
+
Marcar completado
|
|
72
|
+
</Button>
|
|
73
|
+
),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "summary",
|
|
77
|
+
title: "Resumen",
|
|
78
|
+
content: <Summary />,
|
|
79
|
+
},
|
|
80
|
+
]}
|
|
81
|
+
onBlockedStep={(step, requiredStep) => {
|
|
82
|
+
toast.error(`Completa ${requiredStep.title} antes de abrir ${step.title}.`);
|
|
83
|
+
}}
|
|
84
|
+
/>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The component can be controlled with `activeStepId`, `completedStepIds`, `onActiveStepChange`, and `onCompletedStepIdsChange`.
|
|
88
|
+
|
|
89
|
+
## DynamicDetailTabs
|
|
90
|
+
|
|
91
|
+
`DynamicDetailTabs` renders a parent-detail panel next to a tabbed workspace. Each tab is declared with a `service` id and loaded lazily through a registry owned by the consuming app.
|
|
92
|
+
|
|
93
|
+
Use this component when a main entity detail needs to host multiple independent modules, such as related lists, API keys, settings, contacts, billing records, or any child feature that owns its own list/detail flow.
|
|
94
|
+
|
|
95
|
+
### Basic Setup
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import {
|
|
99
|
+
DynamicDetailTabs,
|
|
100
|
+
type DynamicDetailModuleProps,
|
|
101
|
+
type DynamicDetailModuleRegistry,
|
|
102
|
+
type DynamicDetailParentPanelProps,
|
|
103
|
+
} from "bonsaif-ui";
|
|
104
|
+
|
|
105
|
+
type ClientContext = {
|
|
106
|
+
clientId: string;
|
|
107
|
+
permissions: string[];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const moduleRegistry: DynamicDetailModuleRegistry<ClientContext> = {
|
|
111
|
+
"views/settings/lv_server_by_client": () =>
|
|
112
|
+
import("@/modules/settings/ServerByClientModule"),
|
|
113
|
+
"views/settings/lv_account_executive": () =>
|
|
114
|
+
import("@/modules/settings/AccountExecutiveModule"),
|
|
115
|
+
"views/api_keys/lv_api_keys": () =>
|
|
116
|
+
import("@/modules/api-keys/ApiKeysModule"),
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
function ClientSummaryPanel({
|
|
120
|
+
parent,
|
|
121
|
+
context,
|
|
122
|
+
selectedLink,
|
|
123
|
+
}: DynamicDetailParentPanelProps<ClientContext, Client>) {
|
|
124
|
+
return (
|
|
125
|
+
<aside>
|
|
126
|
+
<h2>{parent.name}</h2>
|
|
127
|
+
<p>Cliente: {context.clientId}</p>
|
|
128
|
+
<p>Vista activa: {selectedLink?.label ?? "Sin vista"}</p>
|
|
129
|
+
</aside>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function ClientDetailPage({ client }: { client: Client }) {
|
|
134
|
+
return (
|
|
135
|
+
<DynamicDetailTabs
|
|
136
|
+
title="Modulos relacionados"
|
|
137
|
+
subtitle="Listados y detalles cargados de forma independiente"
|
|
138
|
+
parent={client}
|
|
139
|
+
context={{ clientId: client._id, permissions: client.permissions }}
|
|
140
|
+
parentPanelComponent={ClientSummaryPanel}
|
|
141
|
+
links={[
|
|
142
|
+
{
|
|
143
|
+
service: "views/settings/lv_server_by_client",
|
|
144
|
+
label: "Servidores",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
service: "views/settings/lv_account_executive",
|
|
148
|
+
label: "Ejecutivos",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
service: "views/api_keys/lv_api_keys",
|
|
152
|
+
label: "API Keys",
|
|
153
|
+
requiredPermission: "api-keys:view",
|
|
154
|
+
},
|
|
155
|
+
]}
|
|
156
|
+
moduleRegistry={moduleRegistry}
|
|
157
|
+
searchableTabs="auto"
|
|
158
|
+
tabsSearchThreshold={6}
|
|
159
|
+
tabsSearchPlaceholder="Buscar modulo..."
|
|
160
|
+
canAccess={(link, context) => {
|
|
161
|
+
if (!link.requiredPermission) return true;
|
|
162
|
+
const permissions = Array.isArray(link.requiredPermission)
|
|
163
|
+
? link.requiredPermission
|
|
164
|
+
: [link.requiredPermission];
|
|
165
|
+
return permissions.every((permission) =>
|
|
166
|
+
context.permissions.includes(permission),
|
|
167
|
+
);
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Parent Panel Contract
|
|
175
|
+
|
|
176
|
+
The left panel can be static JSX with `parentPanel`, a reusable component with `parentPanelComponent`, or a render callback with `renderParentPanel`.
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
import { useState } from "react";
|
|
180
|
+
import { Button, Drawer, type DynamicDetailParentPanelProps } from "bonsaif-ui";
|
|
181
|
+
|
|
182
|
+
function ClientSummaryPanel({
|
|
183
|
+
parent,
|
|
184
|
+
context,
|
|
185
|
+
selectedLink,
|
|
186
|
+
visibleLinks,
|
|
187
|
+
onSelectService,
|
|
188
|
+
}: DynamicDetailParentPanelProps<ClientContext, Client>) {
|
|
189
|
+
const [editing, setEditing] = useState(false);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<>
|
|
193
|
+
<section>
|
|
194
|
+
<h2>{parent.name}</h2>
|
|
195
|
+
<p>{context.clientId}</p>
|
|
196
|
+
<p>{selectedLink?.label}</p>
|
|
197
|
+
<Button onClick={() => setEditing(true)}>Editar</Button>
|
|
198
|
+
<button onClick={() => onSelectService(visibleLinks[0])}>
|
|
199
|
+
Ir a primera vista
|
|
200
|
+
</button>
|
|
201
|
+
</section>
|
|
202
|
+
|
|
203
|
+
<Drawer open={editing} onClose={() => setEditing(false)} title="Editar cliente">
|
|
204
|
+
<ClientEditForm client={parent} onSaved={() => setEditing(false)} />
|
|
205
|
+
</Drawer>
|
|
206
|
+
</>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
<DynamicDetailTabs
|
|
211
|
+
parent={client}
|
|
212
|
+
context={context}
|
|
213
|
+
links={links}
|
|
214
|
+
moduleRegistry={moduleRegistry}
|
|
215
|
+
parentPanelComponent={ClientSummaryPanel}
|
|
216
|
+
/>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Child Module Contract
|
|
220
|
+
|
|
221
|
+
Each module should export a default React component. The module receives the shared parent context and navigation helpers.
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
import { Button, DataTable, type DynamicDetailModuleProps } from "bonsaif-ui";
|
|
225
|
+
|
|
226
|
+
type ClientContext = {
|
|
227
|
+
clientId: string;
|
|
228
|
+
permissions: string[];
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export default function ApiKeysModule({
|
|
232
|
+
context,
|
|
233
|
+
activeItemId,
|
|
234
|
+
onOpenDetail,
|
|
235
|
+
onBackToList,
|
|
236
|
+
}: DynamicDetailModuleProps<ClientContext>) {
|
|
237
|
+
if (activeItemId) {
|
|
238
|
+
return (
|
|
239
|
+
<section>
|
|
240
|
+
<Button variant="secondary" onClick={onBackToList}>
|
|
241
|
+
Volver al listado
|
|
242
|
+
</Button>
|
|
243
|
+
<ApiKeyDetail clientId={context.clientId} apiKeyId={activeItemId} />
|
|
244
|
+
</section>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return <ApiKeysList clientId={context.clientId} onOpen={onOpenDetail} />;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Props
|
|
253
|
+
|
|
254
|
+
| Prop | Description |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| `parentPanel` | Static React content rendered in the left parent-detail panel. |
|
|
257
|
+
| `parentPanelComponent` | Reusable component rendered in the left panel with workspace props. |
|
|
258
|
+
| `renderParentPanel` | Render callback alternative for fully custom left-panel composition. |
|
|
259
|
+
| `context` | Shared context passed to every loaded module. |
|
|
260
|
+
| `parent` | Optional parent object passed to every module. |
|
|
261
|
+
| `links` | Declarative tab config. Each item needs at least `service`. |
|
|
262
|
+
| `moduleRegistry` | Map of `service` ids to lazy import functions. |
|
|
263
|
+
| `loadModule` | Optional fallback loader when a service is not in `moduleRegistry`. |
|
|
264
|
+
| `canAccess` | Optional permission/filter callback for hiding tabs. |
|
|
265
|
+
| `activeService` | Controlled active tab service id. |
|
|
266
|
+
| `initialService` | Initial uncontrolled active tab service id. |
|
|
267
|
+
| `onActiveServiceChange` | Callback fired when the user selects a tab. |
|
|
268
|
+
| `keepAlive` | Keeps loaded module components cached. Defaults to `true`. |
|
|
269
|
+
| `loadingState` | Optional custom loading UI. |
|
|
270
|
+
| `errorState` | Optional render function for module loading errors. |
|
|
271
|
+
| `searchableTabs` | Enables the tab search box. Use `true`, `false`, or `"auto"`. Defaults to `"auto"`. |
|
|
272
|
+
| `tabsSearchThreshold` | Minimum visible tab count before automatic search appears. Defaults to `6`. |
|
|
273
|
+
| `tabsSearchPlaceholder` | Placeholder text for the tab search input. |
|
|
274
|
+
| `tabsSearchValue` | Optional controlled search value. |
|
|
275
|
+
| `onTabsSearchChange` | Callback fired when the tab search changes. |
|
|
276
|
+
| `emptySearchState` | Optional custom UI when the search returns no tabs. |
|
|
277
|
+
| `showTabCount` | Shows `filtered / total` tab count. Defaults to `true`. |
|
|
278
|
+
|
|
279
|
+
### Link Shape
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
type DynamicDetailTabLink = {
|
|
283
|
+
service: string;
|
|
284
|
+
label?: string;
|
|
285
|
+
description?: string;
|
|
286
|
+
icon?: React.ReactNode;
|
|
287
|
+
badge?: React.ReactNode;
|
|
288
|
+
disabled?: boolean;
|
|
289
|
+
hidden?: boolean;
|
|
290
|
+
requiredPermission?: string | string[];
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Notes
|
|
295
|
+
|
|
296
|
+
- The consuming app owns business logic, API calls, permissions, and actual module imports.
|
|
297
|
+
- `bonsaif-ui` owns only the layout, tabs, lazy loading state, error state, and list/detail navigation helpers.
|
|
298
|
+
- When there are many tabs, the component keeps horizontal scrolling and can show an animated header search. With `searchableTabs="auto"`, the search icon appears when the visible tab count reaches `tabsSearchThreshold`, and the input expands inline before the close icon.
|
|
299
|
+
- Tab search matches `label`, generated service label, `description`, and `service`. Arrow keys, Home, and End navigate between currently filtered tabs.
|
|
300
|
+
- Prefer static registry keys over arbitrary backend-provided import paths. If links come from a backend, map those service ids to known registry entries in the frontend.
|
|
301
|
+
- For Next.js consumers using the local package or monorepo setup, add `transpilePackages: ["bonsaif-ui"]` in `next.config.ts`.
|
|
302
|
+
|
|
33
303
|
## Scripts
|
|
34
304
|
|
|
35
305
|
```bash
|