@robosophy/ui-vue 1.2.0 → 1.2.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/AGENTS.md +40 -40
- package/README.md +235 -235
- package/ai-context.md +56 -56
- package/components.md +468 -468
- package/dist/components/RsSidebar/RsSidebar.vue.d.ts.map +1 -1
- package/dist/index.css +535 -535
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +30 -31
- package/dist/vite.js.map +1 -1
- package/icon-scanner.md +388 -388
- package/package.json +71 -71
package/components.md
CHANGED
|
@@ -1,468 +1,468 @@
|
|
|
1
|
-
# @robosophy/ui-vue Components
|
|
2
|
-
|
|
3
|
-
Reference for agents and developers using Robosophy Vue components in consuming projects.
|
|
4
|
-
|
|
5
|
-
## Priority
|
|
6
|
-
|
|
7
|
-
When building a module or complex UI, prefer these components before creating local equivalents:
|
|
8
|
-
|
|
9
|
-
- `RsSidebar`: module navigation, side menu, mobile sidebar.
|
|
10
|
-
- `RsAccountSelector`: user/account selector.
|
|
11
|
-
- `RsIcon`: Material Symbols icon component and PrimeVue icon helper.
|
|
12
|
-
- `RsIconPicker`: searchable icon picker for choosing Material Symbols names from a curated catalog (~400 icons in the sprite).
|
|
13
|
-
|
|
14
|
-
Keep app behavior in the consuming project. Components emit events; the app handles routing, stores, fetches, permissions, logout, and redirects.
|
|
15
|
-
|
|
16
|
-
## Setup
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
import {
|
|
20
|
-
RsAccountSelector,
|
|
21
|
-
RsIcon,
|
|
22
|
-
RsIconPicker,
|
|
23
|
-
RsSidebar,
|
|
24
|
-
iconCatalog,
|
|
25
|
-
rsPrimeIcon,
|
|
26
|
-
} from "@robosophy/ui-vue";
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
PrimeVue apps should register the Robosophy preset and import the theme CSS:
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
import PrimeVue from "primevue/config";
|
|
33
|
-
import RobosophyPreset from "@robosophy/prime-theme";
|
|
34
|
-
import "@robosophy/prime-theme/prime.css";
|
|
35
|
-
|
|
36
|
-
app.use(PrimeVue, {
|
|
37
|
-
theme: {
|
|
38
|
-
preset: RobosophyPreset,
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
`RsSidebar` and `RsAccountSelector` render their interactive controls with PrimeVue components, so PrimeVue apps should use the setup above to apply PrimeVue defaults plus Robosophy Button/Menu styling.
|
|
44
|
-
|
|
45
|
-
Apps without PrimeVue should import:
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
import "@robosophy/tokens/tokens.css";
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
RsIcon uses SVG icons inline. Enable the Vite plugin to auto-detect icons and generate an SVG sprite:
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
import { robosophyMaterialSymbols } from "@robosophy/ui-vue/vite";
|
|
55
|
-
|
|
56
|
-
export default defineConfig({
|
|
57
|
-
plugins: [vue(), robosophyMaterialSymbols()],
|
|
58
|
-
});
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
El plugin escanea automaticamente `<RsIcon name="..." />`, `rsPrimeIcon("...")` y
|
|
62
|
-
`icon: "..."` en el codigo fuente. Descarga los SVGs del repositorio oficial de Google
|
|
63
|
-
durante el build, los cachea localmente y genera un sprite SVG inline. Sin fuentes
|
|
64
|
-
tipograficas.
|
|
65
|
-
|
|
66
|
-
## RsIcon
|
|
67
|
-
|
|
68
|
-
Use for Material Symbols. The Vite plugin generates an SVG sprite with only the detected
|
|
69
|
-
icons and inlines it in the HTML. No fonts are loaded.
|
|
70
|
-
|
|
71
|
-
```vue
|
|
72
|
-
<template>
|
|
73
|
-
<RsIcon name="home" />
|
|
74
|
-
<RsIcon name="settings" family="rounded" color="primary" />
|
|
75
|
-
<RsIcon name="manage_accounts" notification />
|
|
76
|
-
<RsIcon name="manage_accounts" notification notification-color="#d80115" />
|
|
77
|
-
<Button label="Inicio" :icon="rsPrimeIcon('home')" />
|
|
78
|
-
</template>
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
`notification` muestra un punto de aviso sobre el ícono (por defecto amarillo del design system,
|
|
82
|
-
`--rs-color-yellow-500`). `notificationColor` permite sobreescribir el color del punto.
|
|
83
|
-
|
|
84
|
-
For dynamic icon names that the scanner cannot see, add them to
|
|
85
|
-
`robosophyMaterialSymbols({ include: [...] })`.
|
|
86
|
-
|
|
87
|
-
### How the scanner works
|
|
88
|
-
|
|
89
|
-
The plugin scans source files and detects icons using five patterns:
|
|
90
|
-
|
|
91
|
-
| Pattern | Detects | Example |
|
|
92
|
-
|---------|---------|---------|
|
|
93
|
-
| 1 | `<RsIcon name="..." />` tags | `<RsIcon name="home" />` |
|
|
94
|
-
| 2 | `rsPrimeIcon("...")` calls | `rsPrimeIcon('settings', { family: 'rounded' })` |
|
|
95
|
-
| 3 | `icon: "..."` in objects | `{ icon: "inbox" }` |
|
|
96
|
-
| 4 | Magic comments | `// @robosophy-icon: home` |
|
|
97
|
-
| 5 | Arrays of strings (name has "icon") | `const iconNames = ["home", "search"]` |
|
|
98
|
-
|
|
99
|
-
**Pattern 3** also extracts `weight` and `family` from the enclosing object:
|
|
100
|
-
```ts
|
|
101
|
-
{ icon: "home", weight: 200, family: "rounded" }
|
|
102
|
-
// → weight 200, family "rounded"
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
**Pattern 4** (magic comments) is for cases where the scanner cannot detect an icon
|
|
106
|
-
statically — typically when icon names are passed as props, stored in variables, or
|
|
107
|
-
returned from functions:
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
// @robosophy-icon: manage_accounts
|
|
111
|
-
// @robosophy-icon: settings family=rounded weight=200
|
|
112
|
-
// @robosophy-icon: logout
|
|
113
|
-
|
|
114
|
-
const action = { icon: "manage_accounts", label: "Perfil" };
|
|
115
|
-
const getIcon = () => "settings";
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
**Pattern 5** detects arrays assigned to variables whose name contains "icon":
|
|
119
|
-
```ts
|
|
120
|
-
const icons = ["home", "search"]; // detected
|
|
121
|
-
const sidebarIconNames = ["warning"]; // detected
|
|
122
|
-
const names = ["home", "search"]; // NOT detected (no "icon" in name)
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### When to use `include`
|
|
126
|
-
|
|
127
|
-
Use the `include` option when an icon is used in many places or is controlled by
|
|
128
|
-
remote configuration:
|
|
129
|
-
|
|
130
|
-
```ts
|
|
131
|
-
robosophyMaterialSymbols({
|
|
132
|
-
include: [
|
|
133
|
-
"home",
|
|
134
|
-
{ family: "rounded", name: "settings", weight: 200 },
|
|
135
|
-
],
|
|
136
|
-
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
If `weight` is omitted, all valid weights (100–700) are included.
|
|
140
|
-
|
|
141
|
-
### When to use magic comments
|
|
142
|
-
|
|
143
|
-
Use `// @robosophy-icon:` when the icon is used in a single file and the variable
|
|
144
|
-
name doesn't contain "icon":
|
|
145
|
-
|
|
146
|
-
```ts
|
|
147
|
-
// @robosophy-icon: manage_accounts
|
|
148
|
-
// @robosophy-icon: logout
|
|
149
|
-
const actions = [
|
|
150
|
-
{ id: "profile", label: "Perfil", icon: "manage_accounts" },
|
|
151
|
-
{ id: "logout", label: "Salir", icon: "logout" },
|
|
152
|
-
];
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Special cases: props and iteration
|
|
156
|
-
|
|
157
|
-
**Props to RsSidebar:** icons defined in the `items` array's `icon` property
|
|
158
|
-
are detected by Pattern 3:
|
|
159
|
-
```ts
|
|
160
|
-
const items = [
|
|
161
|
-
{ id: "home", label: "Home", icon: "home" }, // ✅ detected
|
|
162
|
-
];
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**Props to RsAccountSelector:** same — `icon` in action objects is detected.
|
|
166
|
-
|
|
167
|
-
**v-for iteration with inline objects:** objects in the iterated array are detected:
|
|
168
|
-
```vue
|
|
169
|
-
<RsIcon v-for="item in items" :name="item.icon" />
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
**v-for with external variable:** not detectable — use magic comments:
|
|
173
|
-
```vue
|
|
174
|
-
<RsIcon v-for="name in iconNames" :name="name" /> <!-- add @robosophy-icon -->
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Full documentation
|
|
178
|
-
|
|
179
|
-
For the complete technical reference (regex patterns, scanner architecture, plugin
|
|
180
|
-
options, and known limitations), read `icon-scanner.md` in this package.
|
|
181
|
-
|
|
182
|
-
For full control over icon props (fill, grade, etc.), use the slot approach:
|
|
183
|
-
|
|
184
|
-
```vue
|
|
185
|
-
<Button label="Favorito">
|
|
186
|
-
<template #icon>
|
|
187
|
-
<RsIcon name="favorite" fill color="danger" />
|
|
188
|
-
</template>
|
|
189
|
-
</Button>
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## RsIconPicker
|
|
193
|
-
|
|
194
|
-
Searchable icon picker for choosing a Material Symbols icon name from a curated catalog
|
|
195
|
-
(~400 icons) included in the build-time SVG sprite via `iconCatalog`.
|
|
196
|
-
|
|
197
|
-
```vue
|
|
198
|
-
<template>
|
|
199
|
-
<RsIconPicker v-model="icon" placeholder="Selecciona ícono" :invalid="hasError" />
|
|
200
|
-
</template>
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Props: `modelValue`, `placeholder`, `invalid`.
|
|
204
|
-
Emits: `update:modelValue`.
|
|
205
|
-
|
|
206
|
-
Export `iconCatalog` to validate runtime icon names from API data:
|
|
207
|
-
|
|
208
|
-
```ts
|
|
209
|
-
import { iconCatalog } from "@robosophy/ui-vue";
|
|
210
|
-
|
|
211
|
-
const safeIcon = iconCatalog.includes(iconFromApi) ? iconFromApi : "article";
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## RsSidebar
|
|
215
|
-
|
|
216
|
-
Use for module navigation. It is controlled and presentational.
|
|
217
|
-
|
|
218
|
-
Main props:
|
|
219
|
-
|
|
220
|
-
```ts
|
|
221
|
-
type RsSidebarItem = {
|
|
222
|
-
id: string;
|
|
223
|
-
label: string;
|
|
224
|
-
icon?: string;
|
|
225
|
-
active?: boolean;
|
|
226
|
-
disabled?: boolean;
|
|
227
|
-
visible?: boolean;
|
|
228
|
-
children?: RsSidebarItem[];
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
type RsSidebarProps = {
|
|
232
|
-
items: RsSidebarItem[];
|
|
233
|
-
footerItems?: RsSidebarItem[];
|
|
234
|
-
modules?: Array<{
|
|
235
|
-
text: string;
|
|
236
|
-
link: string;
|
|
237
|
-
active?: boolean;
|
|
238
|
-
disabled?: boolean;
|
|
239
|
-
}>;
|
|
240
|
-
navigateOnModuleSelect?: boolean;
|
|
241
|
-
title?: string;
|
|
242
|
-
icon?: string;
|
|
243
|
-
variant?: "pc" | "alta" | "cc" | "pog" | "cyo";
|
|
244
|
-
expanded?: boolean;
|
|
245
|
-
expandOnHover?: boolean;
|
|
246
|
-
mobileOpen?: boolean;
|
|
247
|
-
widthCollapsed?: string;
|
|
248
|
-
widthExpanded?: string;
|
|
249
|
-
ariaLabel?: string;
|
|
250
|
-
};
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Emits: `item-click`, `close-mobile`, `expanded-change`, `module-select`.
|
|
254
|
-
|
|
255
|
-
Slots: `header`, `header-action`, `item`, `footer`.
|
|
256
|
-
|
|
257
|
-
Example:
|
|
258
|
-
|
|
259
|
-
```vue
|
|
260
|
-
<script setup lang="ts">
|
|
261
|
-
import { ref } from "vue";
|
|
262
|
-
import { RsSidebar, type RsSidebarItem } from "@robosophy/ui-vue";
|
|
263
|
-
|
|
264
|
-
const items: RsSidebarItem[] = [
|
|
265
|
-
{ id: "liquidaciones", label: "Liquidacion", icon: "contract", active: true },
|
|
266
|
-
{ id: "adquisiciones", label: "Adquisiciones", icon: "sell" },
|
|
267
|
-
{
|
|
268
|
-
id: "config",
|
|
269
|
-
label: "Config",
|
|
270
|
-
icon: "subtitles_gear",
|
|
271
|
-
children: [
|
|
272
|
-
{ id: "excel", label: "Cargas Excel", icon: "upload" },
|
|
273
|
-
{ id: "ayuda", label: "Ayuda", icon: "info" },
|
|
274
|
-
],
|
|
275
|
-
},
|
|
276
|
-
];
|
|
277
|
-
|
|
278
|
-
const footerItems: RsSidebarItem[] = [{ id: "home", label: "Volver al home", icon: "home" }];
|
|
279
|
-
const modules = [
|
|
280
|
-
{ text: "Mi Alta", link: "/alta" },
|
|
281
|
-
{ text: "Mi Cuenta y Orden", link: "/cyo", active: true },
|
|
282
|
-
{ text: "Mi Cuenta Corriente", link: "/cc" },
|
|
283
|
-
];
|
|
284
|
-
const isMenuOpen = ref(false);
|
|
285
|
-
|
|
286
|
-
function handleSidebarItem(item: RsSidebarItem) {
|
|
287
|
-
// Route, redirect, or store updates belong in the consuming app.
|
|
288
|
-
}
|
|
289
|
-
</script>
|
|
290
|
-
|
|
291
|
-
<template>
|
|
292
|
-
<RsSidebar
|
|
293
|
-
title="CYO"
|
|
294
|
-
variant="cyo"
|
|
295
|
-
:items="items"
|
|
296
|
-
:modules="modules"
|
|
297
|
-
:footer-items="footerItems"
|
|
298
|
-
:mobile-open="isMenuOpen"
|
|
299
|
-
@item-click="handleSidebarItem"
|
|
300
|
-
@close-mobile="isMenuOpen = false"
|
|
301
|
-
/>
|
|
302
|
-
</template>
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
`modules` renders the default header dropdown. Each option has `text` and `link`. Selecting one emits `module-select`.
|
|
306
|
-
|
|
307
|
-
If `navigateOnModuleSelect` is `true` the component also redirects with `window.location.href = module.link`. Set it to `false` in Storybook, tests, or apps that want to intercept navigation and route manually.
|
|
308
|
-
|
|
309
|
-
Use the `header` slot only when the app needs to replace that default dropdown entirely.
|
|
310
|
-
|
|
311
|
-
Project variants set sidebar icon and active colors:
|
|
312
|
-
|
|
313
|
-
- `pc`: Plan Comercial, `#014A9F`.
|
|
314
|
-
- `alta`: Alta, `#65AC1E`.
|
|
315
|
-
- `cc`: Cuenta Corriente, `#49A0D4`.
|
|
316
|
-
- `pog`: POG, `#007C2F`.
|
|
317
|
-
- `cyo`: Cuenta y Orden, `#49A0D4`.
|
|
318
|
-
|
|
319
|
-
## RsAccountSelector
|
|
320
|
-
|
|
321
|
-
Use for user/account switching. It renders the clickable profile image trigger,
|
|
322
|
-
opens the account panel, and closes it on outside click. It does not fetch,
|
|
323
|
-
redirect, log out, or mutate stores.
|
|
324
|
-
|
|
325
|
-
Main props:
|
|
326
|
-
|
|
327
|
-
```ts
|
|
328
|
-
type RsAccountSelectorUser = {
|
|
329
|
-
email: string;
|
|
330
|
-
name?: string;
|
|
331
|
-
avatarSrc?: string;
|
|
332
|
-
secondaryAvatarSrc?: string;
|
|
333
|
-
initials?: string;
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
type RsAccountSelectorAccount = {
|
|
337
|
-
id: string | number;
|
|
338
|
-
label: string;
|
|
339
|
-
avatarSrc?: string;
|
|
340
|
-
initials?: string;
|
|
341
|
-
active?: boolean;
|
|
342
|
-
disabled?: boolean;
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
type RsAccountSelectorAction = {
|
|
346
|
-
id: string;
|
|
347
|
-
label: string;
|
|
348
|
-
icon?: string;
|
|
349
|
-
visible?: boolean;
|
|
350
|
-
disabled?: boolean;
|
|
351
|
-
variant?: "ghost" | "danger";
|
|
352
|
-
};
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
Use `v-model` when the open state needs to be controlled from the parent.
|
|
356
|
-
|
|
357
|
-
Emits: `select-account`, `action`, `footer-action`, `update:modelValue`, `open`, `close`.
|
|
358
|
-
|
|
359
|
-
Slots: `trigger-avatar`, `user-avatar`, `account-avatar`, `account-trailing`, `actions`, `footer`, `empty`.
|
|
360
|
-
|
|
361
|
-
Example:
|
|
362
|
-
|
|
363
|
-
```vue
|
|
364
|
-
<script setup lang="ts">
|
|
365
|
-
import { RsAccountSelector } from "@robosophy/ui-vue";
|
|
366
|
-
|
|
367
|
-
const user = {
|
|
368
|
-
email: "usuario@robosophy.com",
|
|
369
|
-
initials: "UR",
|
|
370
|
-
name: "Usuario Robosophy",
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const accounts = [
|
|
374
|
-
{ id: "1", label: "Cuenta Activa SA", initials: "CA", active: true },
|
|
375
|
-
{ id: "2", label: "Cuenta Inactiva SRL", initials: "CI" },
|
|
376
|
-
];
|
|
377
|
-
|
|
378
|
-
const actions = [{ id: "profile", label: "Ver mi perfil", icon: "manage_accounts" }];
|
|
379
|
-
const footerActions = [{ id: "logout", label: "Cerrar sesion", icon: "logout", variant: "danger" }];
|
|
380
|
-
</script>
|
|
381
|
-
|
|
382
|
-
<template>
|
|
383
|
-
<RsAccountSelector
|
|
384
|
-
:user="user"
|
|
385
|
-
:accounts="accounts"
|
|
386
|
-
:actions="actions"
|
|
387
|
-
:footer-actions="footerActions"
|
|
388
|
-
@select-account="selectAccount"
|
|
389
|
-
@action="handleAccountAction"
|
|
390
|
-
@footer-action="handleFooterAction"
|
|
391
|
-
/>
|
|
392
|
-
</template>
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
## RsAvatar
|
|
396
|
-
|
|
397
|
-
Use for user or account identity.
|
|
398
|
-
|
|
399
|
-
Useful props: `src`, `alt`, `initials`, `size`, `interactive`.
|
|
400
|
-
|
|
401
|
-
Example:
|
|
402
|
-
|
|
403
|
-
```vue
|
|
404
|
-
<template>
|
|
405
|
-
<RsAvatar src="/avatar.png" alt="Usuario Robosophy" size="lg" />
|
|
406
|
-
<RsAvatar initials="CA" size="sm" interactive />
|
|
407
|
-
</template>
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
## RsCard
|
|
411
|
-
|
|
412
|
-
Use as a reusable content container.
|
|
413
|
-
|
|
414
|
-
Variants: `default`, `brand`, `success`, `warning`, `danger`.
|
|
415
|
-
|
|
416
|
-
Padding: `none`, `sm`, `md`, `lg`.
|
|
417
|
-
|
|
418
|
-
Useful props: `title`, `subtitle`, `variant`, `padding`, `interactive`, `as`, `ariaLabel`.
|
|
419
|
-
|
|
420
|
-
Example:
|
|
421
|
-
|
|
422
|
-
```vue
|
|
423
|
-
<template>
|
|
424
|
-
<RsCard title="Resumen" subtitle="Estado del modulo">
|
|
425
|
-
Contenido del proyecto consumidor.
|
|
426
|
-
</RsCard>
|
|
427
|
-
|
|
428
|
-
<RsCard variant="success" title="Operacion completada">
|
|
429
|
-
Mensaje de estado.
|
|
430
|
-
</RsCard>
|
|
431
|
-
</template>
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
## Module Composition Pattern
|
|
435
|
-
|
|
436
|
-
For a new module, start with `RsSidebar` for navigation, `RsCard` for sections, PrimeVue `Button` for actions, and `RsAccountSelector` only when user/account switching is needed.
|
|
437
|
-
|
|
438
|
-
```vue
|
|
439
|
-
<script setup lang="ts">
|
|
440
|
-
import Button from "primevue/button";
|
|
441
|
-
import { RsCard, RsSidebar, type RsSidebarItem } from "@robosophy/ui-vue";
|
|
442
|
-
|
|
443
|
-
const sidebarItems: RsSidebarItem[] = [
|
|
444
|
-
{ id: "dashboard", label: "Dashboard", icon: "dashboard", active: true },
|
|
445
|
-
{ id: "settings", label: "Configuracion", icon: "settings" },
|
|
446
|
-
];
|
|
447
|
-
|
|
448
|
-
function navigateToSection(item: RsSidebarItem) {
|
|
449
|
-
// Push a route or update local module state in the consuming app.
|
|
450
|
-
}
|
|
451
|
-
</script>
|
|
452
|
-
|
|
453
|
-
<template>
|
|
454
|
-
<div class="module-layout">
|
|
455
|
-
<RsSidebar title="Modulo" :items="sidebarItems" @item-click="navigateToSection" />
|
|
456
|
-
|
|
457
|
-
<main>
|
|
458
|
-
<RsCard title="Dashboard" subtitle="Indicadores principales">
|
|
459
|
-
<Button>
|
|
460
|
-
<span class="action-content">
|
|
461
|
-
<span>Crear</span>
|
|
462
|
-
</span>
|
|
463
|
-
</Button>
|
|
464
|
-
</RsCard>
|
|
465
|
-
</main>
|
|
466
|
-
</div>
|
|
467
|
-
</template>
|
|
468
|
-
```
|
|
1
|
+
# @robosophy/ui-vue Components
|
|
2
|
+
|
|
3
|
+
Reference for agents and developers using Robosophy Vue components in consuming projects.
|
|
4
|
+
|
|
5
|
+
## Priority
|
|
6
|
+
|
|
7
|
+
When building a module or complex UI, prefer these components before creating local equivalents:
|
|
8
|
+
|
|
9
|
+
- `RsSidebar`: module navigation, side menu, mobile sidebar.
|
|
10
|
+
- `RsAccountSelector`: user/account selector.
|
|
11
|
+
- `RsIcon`: Material Symbols icon component and PrimeVue icon helper.
|
|
12
|
+
- `RsIconPicker`: searchable icon picker for choosing Material Symbols names from a curated catalog (~400 icons in the sprite).
|
|
13
|
+
|
|
14
|
+
Keep app behavior in the consuming project. Components emit events; the app handles routing, stores, fetches, permissions, logout, and redirects.
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import {
|
|
20
|
+
RsAccountSelector,
|
|
21
|
+
RsIcon,
|
|
22
|
+
RsIconPicker,
|
|
23
|
+
RsSidebar,
|
|
24
|
+
iconCatalog,
|
|
25
|
+
rsPrimeIcon,
|
|
26
|
+
} from "@robosophy/ui-vue";
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
PrimeVue apps should register the Robosophy preset and import the theme CSS:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import PrimeVue from "primevue/config";
|
|
33
|
+
import RobosophyPreset from "@robosophy/prime-theme";
|
|
34
|
+
import "@robosophy/prime-theme/prime.css";
|
|
35
|
+
|
|
36
|
+
app.use(PrimeVue, {
|
|
37
|
+
theme: {
|
|
38
|
+
preset: RobosophyPreset,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`RsSidebar` and `RsAccountSelector` render their interactive controls with PrimeVue components, so PrimeVue apps should use the setup above to apply PrimeVue defaults plus Robosophy Button/Menu styling.
|
|
44
|
+
|
|
45
|
+
Apps without PrimeVue should import:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import "@robosophy/tokens/tokens.css";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
RsIcon uses SVG icons inline. Enable the Vite plugin to auto-detect icons and generate an SVG sprite:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { robosophyMaterialSymbols } from "@robosophy/ui-vue/vite";
|
|
55
|
+
|
|
56
|
+
export default defineConfig({
|
|
57
|
+
plugins: [vue(), robosophyMaterialSymbols()],
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
El plugin escanea automaticamente `<RsIcon name="..." />`, `rsPrimeIcon("...")` y
|
|
62
|
+
`icon: "..."` en el codigo fuente. Descarga los SVGs del repositorio oficial de Google
|
|
63
|
+
durante el build, los cachea localmente y genera un sprite SVG inline. Sin fuentes
|
|
64
|
+
tipograficas.
|
|
65
|
+
|
|
66
|
+
## RsIcon
|
|
67
|
+
|
|
68
|
+
Use for Material Symbols. The Vite plugin generates an SVG sprite with only the detected
|
|
69
|
+
icons and inlines it in the HTML. No fonts are loaded.
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<template>
|
|
73
|
+
<RsIcon name="home" />
|
|
74
|
+
<RsIcon name="settings" family="rounded" color="primary" />
|
|
75
|
+
<RsIcon name="manage_accounts" notification />
|
|
76
|
+
<RsIcon name="manage_accounts" notification notification-color="#d80115" />
|
|
77
|
+
<Button label="Inicio" :icon="rsPrimeIcon('home')" />
|
|
78
|
+
</template>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`notification` muestra un punto de aviso sobre el ícono (por defecto amarillo del design system,
|
|
82
|
+
`--rs-color-yellow-500`). `notificationColor` permite sobreescribir el color del punto.
|
|
83
|
+
|
|
84
|
+
For dynamic icon names that the scanner cannot see, add them to
|
|
85
|
+
`robosophyMaterialSymbols({ include: [...] })`.
|
|
86
|
+
|
|
87
|
+
### How the scanner works
|
|
88
|
+
|
|
89
|
+
The plugin scans source files and detects icons using five patterns:
|
|
90
|
+
|
|
91
|
+
| Pattern | Detects | Example |
|
|
92
|
+
|---------|---------|---------|
|
|
93
|
+
| 1 | `<RsIcon name="..." />` tags | `<RsIcon name="home" />` |
|
|
94
|
+
| 2 | `rsPrimeIcon("...")` calls | `rsPrimeIcon('settings', { family: 'rounded' })` |
|
|
95
|
+
| 3 | `icon: "..."` in objects | `{ icon: "inbox" }` |
|
|
96
|
+
| 4 | Magic comments | `// @robosophy-icon: home` |
|
|
97
|
+
| 5 | Arrays of strings (name has "icon") | `const iconNames = ["home", "search"]` |
|
|
98
|
+
|
|
99
|
+
**Pattern 3** also extracts `weight` and `family` from the enclosing object:
|
|
100
|
+
```ts
|
|
101
|
+
{ icon: "home", weight: 200, family: "rounded" }
|
|
102
|
+
// → weight 200, family "rounded"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Pattern 4** (magic comments) is for cases where the scanner cannot detect an icon
|
|
106
|
+
statically — typically when icon names are passed as props, stored in variables, or
|
|
107
|
+
returned from functions:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// @robosophy-icon: manage_accounts
|
|
111
|
+
// @robosophy-icon: settings family=rounded weight=200
|
|
112
|
+
// @robosophy-icon: logout
|
|
113
|
+
|
|
114
|
+
const action = { icon: "manage_accounts", label: "Perfil" };
|
|
115
|
+
const getIcon = () => "settings";
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Pattern 5** detects arrays assigned to variables whose name contains "icon":
|
|
119
|
+
```ts
|
|
120
|
+
const icons = ["home", "search"]; // detected
|
|
121
|
+
const sidebarIconNames = ["warning"]; // detected
|
|
122
|
+
const names = ["home", "search"]; // NOT detected (no "icon" in name)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### When to use `include`
|
|
126
|
+
|
|
127
|
+
Use the `include` option when an icon is used in many places or is controlled by
|
|
128
|
+
remote configuration:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
robosophyMaterialSymbols({
|
|
132
|
+
include: [
|
|
133
|
+
"home",
|
|
134
|
+
{ family: "rounded", name: "settings", weight: 200 },
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
If `weight` is omitted, all valid weights (100–700) are included.
|
|
140
|
+
|
|
141
|
+
### When to use magic comments
|
|
142
|
+
|
|
143
|
+
Use `// @robosophy-icon:` when the icon is used in a single file and the variable
|
|
144
|
+
name doesn't contain "icon":
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
// @robosophy-icon: manage_accounts
|
|
148
|
+
// @robosophy-icon: logout
|
|
149
|
+
const actions = [
|
|
150
|
+
{ id: "profile", label: "Perfil", icon: "manage_accounts" },
|
|
151
|
+
{ id: "logout", label: "Salir", icon: "logout" },
|
|
152
|
+
];
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Special cases: props and iteration
|
|
156
|
+
|
|
157
|
+
**Props to RsSidebar:** icons defined in the `items` array's `icon` property
|
|
158
|
+
are detected by Pattern 3:
|
|
159
|
+
```ts
|
|
160
|
+
const items = [
|
|
161
|
+
{ id: "home", label: "Home", icon: "home" }, // ✅ detected
|
|
162
|
+
];
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Props to RsAccountSelector:** same — `icon` in action objects is detected.
|
|
166
|
+
|
|
167
|
+
**v-for iteration with inline objects:** objects in the iterated array are detected:
|
|
168
|
+
```vue
|
|
169
|
+
<RsIcon v-for="item in items" :name="item.icon" />
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**v-for with external variable:** not detectable — use magic comments:
|
|
173
|
+
```vue
|
|
174
|
+
<RsIcon v-for="name in iconNames" :name="name" /> <!-- add @robosophy-icon -->
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Full documentation
|
|
178
|
+
|
|
179
|
+
For the complete technical reference (regex patterns, scanner architecture, plugin
|
|
180
|
+
options, and known limitations), read `icon-scanner.md` in this package.
|
|
181
|
+
|
|
182
|
+
For full control over icon props (fill, grade, etc.), use the slot approach:
|
|
183
|
+
|
|
184
|
+
```vue
|
|
185
|
+
<Button label="Favorito">
|
|
186
|
+
<template #icon>
|
|
187
|
+
<RsIcon name="favorite" fill color="danger" />
|
|
188
|
+
</template>
|
|
189
|
+
</Button>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## RsIconPicker
|
|
193
|
+
|
|
194
|
+
Searchable icon picker for choosing a Material Symbols icon name from a curated catalog
|
|
195
|
+
(~400 icons) included in the build-time SVG sprite via `iconCatalog`.
|
|
196
|
+
|
|
197
|
+
```vue
|
|
198
|
+
<template>
|
|
199
|
+
<RsIconPicker v-model="icon" placeholder="Selecciona ícono" :invalid="hasError" />
|
|
200
|
+
</template>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Props: `modelValue`, `placeholder`, `invalid`.
|
|
204
|
+
Emits: `update:modelValue`.
|
|
205
|
+
|
|
206
|
+
Export `iconCatalog` to validate runtime icon names from API data:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { iconCatalog } from "@robosophy/ui-vue";
|
|
210
|
+
|
|
211
|
+
const safeIcon = iconCatalog.includes(iconFromApi) ? iconFromApi : "article";
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## RsSidebar
|
|
215
|
+
|
|
216
|
+
Use for module navigation. It is controlled and presentational.
|
|
217
|
+
|
|
218
|
+
Main props:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
type RsSidebarItem = {
|
|
222
|
+
id: string;
|
|
223
|
+
label: string;
|
|
224
|
+
icon?: string;
|
|
225
|
+
active?: boolean;
|
|
226
|
+
disabled?: boolean;
|
|
227
|
+
visible?: boolean;
|
|
228
|
+
children?: RsSidebarItem[];
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
type RsSidebarProps = {
|
|
232
|
+
items: RsSidebarItem[];
|
|
233
|
+
footerItems?: RsSidebarItem[];
|
|
234
|
+
modules?: Array<{
|
|
235
|
+
text: string;
|
|
236
|
+
link: string;
|
|
237
|
+
active?: boolean;
|
|
238
|
+
disabled?: boolean;
|
|
239
|
+
}>;
|
|
240
|
+
navigateOnModuleSelect?: boolean;
|
|
241
|
+
title?: string;
|
|
242
|
+
icon?: string;
|
|
243
|
+
variant?: "pc" | "alta" | "cc" | "pog" | "cyo";
|
|
244
|
+
expanded?: boolean;
|
|
245
|
+
expandOnHover?: boolean;
|
|
246
|
+
mobileOpen?: boolean;
|
|
247
|
+
widthCollapsed?: string;
|
|
248
|
+
widthExpanded?: string;
|
|
249
|
+
ariaLabel?: string;
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Emits: `item-click`, `close-mobile`, `expanded-change`, `module-select`.
|
|
254
|
+
|
|
255
|
+
Slots: `header`, `header-action`, `item`, `footer`.
|
|
256
|
+
|
|
257
|
+
Example:
|
|
258
|
+
|
|
259
|
+
```vue
|
|
260
|
+
<script setup lang="ts">
|
|
261
|
+
import { ref } from "vue";
|
|
262
|
+
import { RsSidebar, type RsSidebarItem } from "@robosophy/ui-vue";
|
|
263
|
+
|
|
264
|
+
const items: RsSidebarItem[] = [
|
|
265
|
+
{ id: "liquidaciones", label: "Liquidacion", icon: "contract", active: true },
|
|
266
|
+
{ id: "adquisiciones", label: "Adquisiciones", icon: "sell" },
|
|
267
|
+
{
|
|
268
|
+
id: "config",
|
|
269
|
+
label: "Config",
|
|
270
|
+
icon: "subtitles_gear",
|
|
271
|
+
children: [
|
|
272
|
+
{ id: "excel", label: "Cargas Excel", icon: "upload" },
|
|
273
|
+
{ id: "ayuda", label: "Ayuda", icon: "info" },
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
const footerItems: RsSidebarItem[] = [{ id: "home", label: "Volver al home", icon: "home" }];
|
|
279
|
+
const modules = [
|
|
280
|
+
{ text: "Mi Alta", link: "/alta" },
|
|
281
|
+
{ text: "Mi Cuenta y Orden", link: "/cyo", active: true },
|
|
282
|
+
{ text: "Mi Cuenta Corriente", link: "/cc" },
|
|
283
|
+
];
|
|
284
|
+
const isMenuOpen = ref(false);
|
|
285
|
+
|
|
286
|
+
function handleSidebarItem(item: RsSidebarItem) {
|
|
287
|
+
// Route, redirect, or store updates belong in the consuming app.
|
|
288
|
+
}
|
|
289
|
+
</script>
|
|
290
|
+
|
|
291
|
+
<template>
|
|
292
|
+
<RsSidebar
|
|
293
|
+
title="CYO"
|
|
294
|
+
variant="cyo"
|
|
295
|
+
:items="items"
|
|
296
|
+
:modules="modules"
|
|
297
|
+
:footer-items="footerItems"
|
|
298
|
+
:mobile-open="isMenuOpen"
|
|
299
|
+
@item-click="handleSidebarItem"
|
|
300
|
+
@close-mobile="isMenuOpen = false"
|
|
301
|
+
/>
|
|
302
|
+
</template>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`modules` renders the default header dropdown. Each option has `text` and `link`. Selecting one emits `module-select`.
|
|
306
|
+
|
|
307
|
+
If `navigateOnModuleSelect` is `true` the component also redirects with `window.location.href = module.link`. Set it to `false` in Storybook, tests, or apps that want to intercept navigation and route manually.
|
|
308
|
+
|
|
309
|
+
Use the `header` slot only when the app needs to replace that default dropdown entirely.
|
|
310
|
+
|
|
311
|
+
Project variants set sidebar icon and active colors:
|
|
312
|
+
|
|
313
|
+
- `pc`: Plan Comercial, `#014A9F`.
|
|
314
|
+
- `alta`: Alta, `#65AC1E`.
|
|
315
|
+
- `cc`: Cuenta Corriente, `#49A0D4`.
|
|
316
|
+
- `pog`: POG, `#007C2F`.
|
|
317
|
+
- `cyo`: Cuenta y Orden, `#49A0D4`.
|
|
318
|
+
|
|
319
|
+
## RsAccountSelector
|
|
320
|
+
|
|
321
|
+
Use for user/account switching. It renders the clickable profile image trigger,
|
|
322
|
+
opens the account panel, and closes it on outside click. It does not fetch,
|
|
323
|
+
redirect, log out, or mutate stores.
|
|
324
|
+
|
|
325
|
+
Main props:
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
type RsAccountSelectorUser = {
|
|
329
|
+
email: string;
|
|
330
|
+
name?: string;
|
|
331
|
+
avatarSrc?: string;
|
|
332
|
+
secondaryAvatarSrc?: string;
|
|
333
|
+
initials?: string;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
type RsAccountSelectorAccount = {
|
|
337
|
+
id: string | number;
|
|
338
|
+
label: string;
|
|
339
|
+
avatarSrc?: string;
|
|
340
|
+
initials?: string;
|
|
341
|
+
active?: boolean;
|
|
342
|
+
disabled?: boolean;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
type RsAccountSelectorAction = {
|
|
346
|
+
id: string;
|
|
347
|
+
label: string;
|
|
348
|
+
icon?: string;
|
|
349
|
+
visible?: boolean;
|
|
350
|
+
disabled?: boolean;
|
|
351
|
+
variant?: "ghost" | "danger";
|
|
352
|
+
};
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Use `v-model` when the open state needs to be controlled from the parent.
|
|
356
|
+
|
|
357
|
+
Emits: `select-account`, `action`, `footer-action`, `update:modelValue`, `open`, `close`.
|
|
358
|
+
|
|
359
|
+
Slots: `trigger-avatar`, `user-avatar`, `account-avatar`, `account-trailing`, `actions`, `footer`, `empty`.
|
|
360
|
+
|
|
361
|
+
Example:
|
|
362
|
+
|
|
363
|
+
```vue
|
|
364
|
+
<script setup lang="ts">
|
|
365
|
+
import { RsAccountSelector } from "@robosophy/ui-vue";
|
|
366
|
+
|
|
367
|
+
const user = {
|
|
368
|
+
email: "usuario@robosophy.com",
|
|
369
|
+
initials: "UR",
|
|
370
|
+
name: "Usuario Robosophy",
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const accounts = [
|
|
374
|
+
{ id: "1", label: "Cuenta Activa SA", initials: "CA", active: true },
|
|
375
|
+
{ id: "2", label: "Cuenta Inactiva SRL", initials: "CI" },
|
|
376
|
+
];
|
|
377
|
+
|
|
378
|
+
const actions = [{ id: "profile", label: "Ver mi perfil", icon: "manage_accounts" }];
|
|
379
|
+
const footerActions = [{ id: "logout", label: "Cerrar sesion", icon: "logout", variant: "danger" }];
|
|
380
|
+
</script>
|
|
381
|
+
|
|
382
|
+
<template>
|
|
383
|
+
<RsAccountSelector
|
|
384
|
+
:user="user"
|
|
385
|
+
:accounts="accounts"
|
|
386
|
+
:actions="actions"
|
|
387
|
+
:footer-actions="footerActions"
|
|
388
|
+
@select-account="selectAccount"
|
|
389
|
+
@action="handleAccountAction"
|
|
390
|
+
@footer-action="handleFooterAction"
|
|
391
|
+
/>
|
|
392
|
+
</template>
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## RsAvatar
|
|
396
|
+
|
|
397
|
+
Use for user or account identity.
|
|
398
|
+
|
|
399
|
+
Useful props: `src`, `alt`, `initials`, `size`, `interactive`.
|
|
400
|
+
|
|
401
|
+
Example:
|
|
402
|
+
|
|
403
|
+
```vue
|
|
404
|
+
<template>
|
|
405
|
+
<RsAvatar src="/avatar.png" alt="Usuario Robosophy" size="lg" />
|
|
406
|
+
<RsAvatar initials="CA" size="sm" interactive />
|
|
407
|
+
</template>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## RsCard
|
|
411
|
+
|
|
412
|
+
Use as a reusable content container.
|
|
413
|
+
|
|
414
|
+
Variants: `default`, `brand`, `success`, `warning`, `danger`.
|
|
415
|
+
|
|
416
|
+
Padding: `none`, `sm`, `md`, `lg`.
|
|
417
|
+
|
|
418
|
+
Useful props: `title`, `subtitle`, `variant`, `padding`, `interactive`, `as`, `ariaLabel`.
|
|
419
|
+
|
|
420
|
+
Example:
|
|
421
|
+
|
|
422
|
+
```vue
|
|
423
|
+
<template>
|
|
424
|
+
<RsCard title="Resumen" subtitle="Estado del modulo">
|
|
425
|
+
Contenido del proyecto consumidor.
|
|
426
|
+
</RsCard>
|
|
427
|
+
|
|
428
|
+
<RsCard variant="success" title="Operacion completada">
|
|
429
|
+
Mensaje de estado.
|
|
430
|
+
</RsCard>
|
|
431
|
+
</template>
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Module Composition Pattern
|
|
435
|
+
|
|
436
|
+
For a new module, start with `RsSidebar` for navigation, `RsCard` for sections, PrimeVue `Button` for actions, and `RsAccountSelector` only when user/account switching is needed.
|
|
437
|
+
|
|
438
|
+
```vue
|
|
439
|
+
<script setup lang="ts">
|
|
440
|
+
import Button from "primevue/button";
|
|
441
|
+
import { RsCard, RsSidebar, type RsSidebarItem } from "@robosophy/ui-vue";
|
|
442
|
+
|
|
443
|
+
const sidebarItems: RsSidebarItem[] = [
|
|
444
|
+
{ id: "dashboard", label: "Dashboard", icon: "dashboard", active: true },
|
|
445
|
+
{ id: "settings", label: "Configuracion", icon: "settings" },
|
|
446
|
+
];
|
|
447
|
+
|
|
448
|
+
function navigateToSection(item: RsSidebarItem) {
|
|
449
|
+
// Push a route or update local module state in the consuming app.
|
|
450
|
+
}
|
|
451
|
+
</script>
|
|
452
|
+
|
|
453
|
+
<template>
|
|
454
|
+
<div class="module-layout">
|
|
455
|
+
<RsSidebar title="Modulo" :items="sidebarItems" @item-click="navigateToSection" />
|
|
456
|
+
|
|
457
|
+
<main>
|
|
458
|
+
<RsCard title="Dashboard" subtitle="Indicadores principales">
|
|
459
|
+
<Button>
|
|
460
|
+
<span class="action-content">
|
|
461
|
+
<span>Crear</span>
|
|
462
|
+
</span>
|
|
463
|
+
</Button>
|
|
464
|
+
</RsCard>
|
|
465
|
+
</main>
|
|
466
|
+
</div>
|
|
467
|
+
</template>
|
|
468
|
+
```
|