@robosophy/ui-vue 1.0.17 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -145,7 +145,8 @@ createApp(App)
145
145
 
146
146
  ## Material Symbols
147
147
 
148
- `RsIcon` usa Material Symbols y debe combinarse con el plugin de Vite para generar fuentes locales optimizadas:
148
+ `RsIcon` renderiza iconos SVG inline. Combinalo con el plugin de Vite que descarga solo los SVGs
149
+ necesarios desde el repositorio oficial de Google y genera un sprite inyectado en el HTML:
149
150
 
150
151
  ```ts
151
152
  // vite.config.ts
@@ -163,9 +164,10 @@ export default defineConfig({
163
164
 
164
165
  El plugin escanea automaticamente usos de `<RsIcon name="..." />`, `rsPrimeIcon("...")` y
165
166
  objetos con `icon: "..."` en el codigo fuente. No hace falta declarar iconos a mano.
166
- En build emite fuentes locales subseteadas por familia, preload de esas fuentes y
167
- `font-display: block` para evitar que se vea brevemente el texto del ligature antes
168
- de que cargue el icono.
167
+ En build descarga los SVGs del CDN de GitHub, los cachea localmente y genera un sprite SVG
168
+ inline. No se cargan fuentes tipograficas.
169
+
170
+ El sprite incluye variantes para fill (outlined/filled) y grade (-25, 0, 200).
169
171
 
170
172
  ### Iconos dinamicos (opcional)
171
173
 
@@ -181,29 +183,7 @@ robosophyMaterialSymbols({
181
183
  });
182
184
  ```
183
185
 
184
- ### Directorio de salida
185
-
186
- Por defecto el plugin emite fuentes y CSS en `assets/`. Se puede cambiar con `outputDir`:
187
-
188
- ```ts
189
- robosophyMaterialSymbols({
190
- outputDir: "fonts",
191
- });
192
- ```
193
-
194
- Esto produce `fonts/rs-material-symbols.css` y `fonts/rs-material-symbols-{family}-{hash}.woff2`
195
- en el build, y ajusta los hrefs del preload y del stylesheet en el HTML.
196
-
197
- ### Estrategia de carga
198
-
199
- Se puede ajustar la estrategia de carga si una app lo necesita:
200
-
201
- ```ts
202
- robosophyMaterialSymbols({
203
- fontDisplay: "optional",
204
- preload: false,
205
- });
206
- ```
186
+ ### PrimeVue
207
187
 
208
188
  Para APIs de PrimeVue que reciben `icon` como string, usa el helper:
209
189
 
@@ -212,6 +192,16 @@ Para APIs de PrimeVue que reciben `icon` como string, usa el helper:
212
192
  <Button label="Configurar" :icon="rsPrimeIcon('settings', { family: 'rounded' })" />
213
193
  ```
214
194
 
195
+ Para control total sobre props (fill, grade, etc.), usa el slot `#icon`:
196
+
197
+ ```vue
198
+ <Button label="Favorito">
199
+ <template #icon>
200
+ <RsIcon name="favorite" fill color="danger" />
201
+ </template>
202
+ </Button>
203
+ ```
204
+
215
205
  ## Component Structure
216
206
 
217
207
  Every public component must be self-contained:
package/ai-context.md CHANGED
@@ -26,7 +26,7 @@ Use this package for shared Vue 3 UI components.
26
26
  - Use `@robosophy/tokens` CSS variables for colors, typography, strokes, and component surfaces.
27
27
  - Use PrimeVue only when it provides real behavior for that component; actions should use PrimeVue `Button` in PrimeVue apps.
28
28
  - `RsSidebar` and `RsAccountSelector` use native PrimeVue controls for interactive UI; PrimeVue apps should register the Robosophy PrimeVue preset and load `@robosophy/prime-theme/prime.css` so Button/Menu states receive Robosophy styling.
29
- - `RsIcon` needs `robosophyMaterialSymbols()` from `@robosophy/ui-vue/vite` in consuming Vite apps so builds ship local subset fonts. The plugin auto-detects icon usage from source — no manual declaration needed for static patterns. Use `rsPrimeIcon()` for PrimeVue APIs that accept icon class strings. Add dynamic icons via `include`.
29
+ - `RsIcon` needs `robosophyMaterialSymbols()` from `@robosophy/ui-vue/vite` in consuming Vite apps so builds generate an SVG sprite with only the needed icons. The plugin auto-detects icon usage from source — no manual declaration needed for static patterns. Use `rsPrimeIcon()` for PrimeVue APIs that accept icon class strings. Add dynamic icons via `include`. For full control over icon props, use the `#icon` slot instead of `rsPrimeIcon()`.
30
30
  - Use `RsSidebar` for reusable module navigation. `item-click` stays in the consuming app; the optional `modules` dropdown can redirect with the provided `link` when `navigateOnModuleSelect` is `true`.
31
31
  - Keep props focused on stable component API. Use slots for flexible content.
32
32
  - Export every public component and public type from `src/index.ts`.
package/components.md CHANGED
@@ -49,7 +49,7 @@ Apps without PrimeVue should import:
49
49
  import "@robosophy/tokens/tokens.css";
50
50
  ```
51
51
 
52
- Enable Material Symbols subsetting in Vite apps:
52
+ RsIcon uses SVG icons inline. Enable the Vite plugin to auto-detect icons and generate an SVG sprite:
53
53
 
54
54
  ```ts
55
55
  import { robosophyMaterialSymbols } from "@robosophy/ui-vue/vite";
@@ -60,27 +60,14 @@ export default defineConfig({
60
60
  ```
61
61
 
62
62
  El plugin escanea automaticamente `<RsIcon name="..." />`, `rsPrimeIcon("...")` y
63
- `icon: "..."` en el codigo fuente. No hace falta declarar iconos a mano. Para iconos
64
- dinamicos que el scanner no pueda detectar, agregalos en `include`.
65
-
66
- ### Directorio de salida
67
-
68
- Por defecto las fuentes y el CSS se emiten en `assets/`. Se puede cambiar con `outputDir`:
69
-
70
- ```ts
71
- robosophyMaterialSymbols({
72
- outputDir: "fonts",
73
- });
74
- ```
75
-
76
- Esto produce `fonts/rs-material-symbols.css` y `fonts/rs-material-symbols-{family}-{hash}.woff2`
77
- en el build, y ajusta los hrefs del preload y del stylesheet en el HTML.
63
+ `icon: "..."` en el codigo fuente. Descarga los SVGs del repositorio oficial de Google
64
+ durante el build, los cachea localmente y genera un sprite SVG inline. Sin fuentes
65
+ tipograficas.
78
66
 
79
67
  ## RsIcon
80
68
 
81
- Use for Material Symbols. The Vite plugin generates local subset fonts for only the detected
82
- icons, preloads those fonts in build HTML, and defaults to `font-display: block` to avoid
83
- ligature text flashing before the icon font is ready.
69
+ Use for Material Symbols. The Vite plugin generates an SVG sprite with only the detected
70
+ icons and inlines it in the HTML. No fonts are loaded.
84
71
 
85
72
  ```vue
86
73
  <template>
@@ -91,8 +78,17 @@ ligature text flashing before the icon font is ready.
91
78
  ```
92
79
 
93
80
  For dynamic icon names that the scanner cannot see, add them to
94
- `robosophyMaterialSymbols({ include: [...] })`. Use `preload: false` or
95
- `fontDisplay: "optional"` only when an app has a specific loading-policy reason.
81
+ `robosophyMaterialSymbols({ include: [...] })`.
82
+
83
+ For full control over icon props (fill, grade, etc.), use the slot approach:
84
+
85
+ ```vue
86
+ <Button label="Favorito">
87
+ <template #icon>
88
+ <RsIcon name="favorite" fill color="danger" />
89
+ </template>
90
+ </Button>
91
+ ```
96
92
 
97
93
  ## RsSidebar
98
94
 
@@ -42,6 +42,7 @@ declare const __VLS_base: import("vue").DefineComponent<RsAccountSelectorProps,
42
42
  onOpen?: (() => any) | undefined;
43
43
  onClose?: (() => any) | undefined;
44
44
  }>, {
45
+ width: string;
45
46
  accounts: RsAccountSelectorAccount[];
46
47
  actions: RsAccountSelectorAction[];
47
48
  footerActions: RsAccountSelectorAction[];
@@ -51,7 +52,6 @@ declare const __VLS_base: import("vue").DefineComponent<RsAccountSelectorProps,
51
52
  activeText: string;
52
53
  selectText: string;
53
54
  triggerAriaLabel: string;
54
- width: string;
55
55
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
56
56
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
57
57
  declare const _default: typeof __VLS_export;
@@ -1 +1 @@
1
- {"version":3,"file":"RsIcon.vue.d.ts","sourceRoot":"","sources":["../../../src/components/RsIcon/RsIcon.vue"],"names":[],"mappings":"AA6DA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAiFlD,QAAA,MAAM,YAAY;;;;;;;;6EAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"RsIcon.vue.d.ts","sourceRoot":"","sources":["../../../src/components/RsIcon/RsIcon.vue"],"names":[],"mappings":"AA2FA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAmHlD,QAAA,MAAM,YAAY;;;;;;;;6EAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
package/dist/index.css CHANGED
@@ -1,163 +1,73 @@
1
1
  .rs-icon {
2
- --rs-icon-color: currentColor;
3
- --rs-icon-fill: 0;
4
- --rs-icon-grade: 0;
5
- --rs-icon-optical-size: 24;
6
- --rs-icon-size: 24px;
7
- --rs-icon-weight: 400;
8
-
9
2
  align-items: center;
10
- color: var(--rs-icon-color);
3
+ color: var(--rs-icon-color, currentColor);
11
4
  display: inline-flex;
12
5
  flex: 0 0 auto;
13
- font-family: "Material Symbols Outlined Variable";
14
- font-size: var(--rs-icon-size);
15
- font-style: normal;
16
- font-feature-settings: "liga";
17
- font-variation-settings:
18
- "FILL" var(--rs-icon-fill),
19
- "wght" var(--rs-icon-weight),
20
- "GRAD" var(--rs-icon-grade),
21
- "opsz" var(--rs-icon-optical-size);
22
- font-weight: 400;
23
- -webkit-font-feature-settings: "liga";
6
+ flex-shrink: 0;
7
+ height: var(--rs-icon-size, 24px);
24
8
  justify-content: center;
25
- direction: ltr;
26
- letter-spacing: normal;
27
- line-height: 1;
28
- text-transform: none;
29
9
  user-select: none;
30
- white-space: nowrap;
31
- word-wrap: normal;
10
+ width: var(--rs-icon-size, 24px);
32
11
  }
33
12
 
34
- .rs-icon--sm {
35
- --rs-icon-optical-size: 20;
36
- --rs-icon-size: 18px;
37
- }
38
-
39
- .rs-icon--md {
40
- --rs-icon-optical-size: 24;
41
- --rs-icon-size: 24px;
42
- }
43
-
44
- .rs-icon--lg {
45
- --rs-icon-optical-size: 40;
46
- --rs-icon-size: 32px;
13
+ .rs-icon--interactive {
14
+ cursor: pointer;
47
15
  }
48
16
 
49
17
  .rs-icon--primary {
50
- --rs-icon-color: var(--rs-color-icon-primary, #48a0d4);
18
+ color: var(--rs-color-icon-primary, #48a0d4);
51
19
  }
52
20
 
53
21
  .rs-icon--secondary {
54
- --rs-icon-color: var(--rs-color-icon-secondary, #000000);
22
+ color: var(--rs-color-icon-secondary, #000000);
55
23
  }
56
24
 
57
25
  .rs-icon--success {
58
- --rs-icon-color: var(--rs-color-icon-success, #4EAE01);
26
+ color: var(--rs-color-icon-success, #4EAE01);
59
27
  }
60
28
 
61
29
  .rs-icon--warning {
62
- --rs-icon-color: var(--rs-color-icon-warning, #FF9001);
30
+ color: var(--rs-color-icon-warning, #FF9001);
63
31
  }
64
32
 
65
33
  .rs-icon--danger {
66
- --rs-icon-color: var(--rs-color-icon-danger, #d80115);
34
+ color: var(--rs-color-icon-danger, #d80115);
67
35
  }
68
36
 
69
37
  .rs-icon--inherit {
70
- --rs-icon-color: currentColor;
71
- }
72
-
73
- .rs-icon--rounded {
74
- font-family: "Material Symbols Rounded Variable";
75
- }
76
-
77
- .rs-icon--sharp {
78
- font-family: "Material Symbols Sharp Variable";
79
- }
80
-
81
- .rs-icon--interactive {
82
- cursor: pointer;
83
- }
84
-
85
- .rs-icon--prime::before {
86
- content: var(--rs-icon-content);
38
+ color: currentColor;
87
39
  }
88
40
  .rs-icon {
89
- --rs-icon-color: currentColor;
90
- --rs-icon-fill: 0;
91
- --rs-icon-grade: 0;
92
- --rs-icon-optical-size: 24;
93
- --rs-icon-size: 24px;
94
- --rs-icon-weight: 400;
95
-
96
41
  align-items: center;
97
- color: var(--rs-icon-color);
42
+ color: var(--rs-icon-color, currentColor);
98
43
  display: inline-flex;
99
44
  flex: 0 0 auto;
100
- font-family: "Material Symbols Outlined Variable";
101
- font-size: var(--rs-icon-size);
102
- font-style: normal;
103
- font-feature-settings: "liga";
104
- font-variation-settings:
105
- "FILL" var(--rs-icon-fill),
106
- "wght" var(--rs-icon-weight),
107
- "GRAD" var(--rs-icon-grade),
108
- "opsz" var(--rs-icon-optical-size);
109
- font-weight: 400;
110
- -webkit-font-feature-settings: "liga";
45
+ flex-shrink: 0;
46
+ height: var(--rs-icon-size, 24px);
111
47
  justify-content: center;
112
- direction: ltr;
113
- letter-spacing: normal;
114
- line-height: 1;
115
- text-transform: none;
116
48
  user-select: none;
117
- white-space: nowrap;
118
- word-wrap: normal;
119
- }
120
- .rs-icon--sm {
121
- --rs-icon-optical-size: 20;
122
- --rs-icon-size: 18px;
123
- }
124
- .rs-icon--md {
125
- --rs-icon-optical-size: 24;
126
- --rs-icon-size: 24px;
49
+ width: var(--rs-icon-size, 24px);
127
50
  }
128
- .rs-icon--lg {
129
- --rs-icon-optical-size: 40;
130
- --rs-icon-size: 32px;
51
+ .rs-icon--interactive {
52
+ cursor: pointer;
131
53
  }
132
54
  .rs-icon--primary {
133
- --rs-icon-color: var(--rs-color-icon-primary, #48a0d4);
55
+ color: var(--rs-color-icon-primary, #48a0d4);
134
56
  }
135
57
  .rs-icon--secondary {
136
- --rs-icon-color: var(--rs-color-icon-secondary, #000000);
58
+ color: var(--rs-color-icon-secondary, #000000);
137
59
  }
138
60
  .rs-icon--success {
139
- --rs-icon-color: var(--rs-color-icon-success, #4EAE01);
61
+ color: var(--rs-color-icon-success, #4EAE01);
140
62
  }
141
63
  .rs-icon--warning {
142
- --rs-icon-color: var(--rs-color-icon-warning, #FF9001);
64
+ color: var(--rs-color-icon-warning, #FF9001);
143
65
  }
144
66
  .rs-icon--danger {
145
- --rs-icon-color: var(--rs-color-icon-danger, #d80115);
67
+ color: var(--rs-color-icon-danger, #d80115);
146
68
  }
147
69
  .rs-icon--inherit {
148
- --rs-icon-color: currentColor;
149
- }
150
- .rs-icon--rounded {
151
- font-family: "Material Symbols Rounded Variable";
152
- }
153
- .rs-icon--sharp {
154
- font-family: "Material Symbols Sharp Variable";
155
- }
156
- .rs-icon--interactive {
157
- cursor: pointer;
158
- }
159
- .rs-icon--prime::before {
160
- content: var(--rs-icon-content);
70
+ color: currentColor;
161
71
  }
162
72
  .rs-account-selector-host {
163
73
  --rs-account-selector-width: 270px;
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  // AUTO-GENERATED FILE. DO NOT EDIT MANUALLY.
2
2
  import "./index.css";
3
- import { defineComponent, computed, openBlock, createElementBlock, normalizeStyle, normalizeClass, toDisplayString, ref, onMounted, onBeforeUnmount, watch, nextTick, createVNode, unref, withModifiers, withCtx, renderSlot, createElementVNode, createBlock, Teleport, createCommentVNode, Fragment, renderList, mergeProps, createTextVNode } from "vue";
3
+ import { defineComponent, computed, openBlock, createElementBlock, normalizeStyle, normalizeClass, createElementVNode, ref, onMounted, onBeforeUnmount, watch, nextTick, createVNode, unref, withModifiers, withCtx, renderSlot, toDisplayString, createBlock, Teleport, createCommentVNode, Fragment, renderList, mergeProps, createTextVNode } from "vue";
4
4
  import Button from "primevue/button";
5
5
  import Menu from "primevue/menu";
6
- const _hoisted_1$2 = ["aria-hidden", "aria-label", "role"];
6
+ const _hoisted_1$2 = ["aria-hidden", "aria-label", "role", "width", "height"];
7
+ const _hoisted_2$2 = ["href"];
7
8
  const _sfc_main$2 = /* @__PURE__ */ defineComponent({
8
9
  ...{
9
10
  name: "RsIcon"
@@ -22,7 +23,30 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
22
23
  ariaLabel: {}
23
24
  },
24
25
  setup(__props) {
26
+ const GRADE_VALUES = [200, 0, -25];
25
27
  const props = __props;
28
+ function clampGrade(grade) {
29
+ return GRADE_VALUES.reduce(
30
+ (nearest, g) => Math.abs(g - grade) < Math.abs(nearest - grade) ? g : nearest
31
+ );
32
+ }
33
+ const symbolHref = computed(() => {
34
+ const family = props.family;
35
+ const name = props.name;
36
+ let gradeSuffix = "";
37
+ const clamped = clampGrade(props.grade);
38
+ if (clamped !== 0) {
39
+ gradeSuffix = clamped > 0 ? `-grad${clamped}` : `-gradN${Math.abs(clamped)}`;
40
+ }
41
+ const fillSuffix = props.fill ? "-fill" : "";
42
+ return `#rs-icon-${family}-${name}${gradeSuffix}${fillSuffix}`;
43
+ });
44
+ const iconSize = computed(() => {
45
+ if (typeof props.size === "number") return props.size;
46
+ if (props.size === "sm") return 18;
47
+ if (props.size === "lg") return 32;
48
+ return 24;
49
+ });
26
50
  const rootClass = computed(() => [
27
51
  "rs-icon",
28
52
  `rs-icon--${props.family}`,
@@ -35,22 +59,24 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
35
59
  }
36
60
  ]);
37
61
  const rootStyle = computed(() => ({
38
- "--rs-icon-fill": props.fill ? 1 : 0,
39
- "--rs-icon-grade": props.grade,
40
- "--rs-icon-optical-size": props.opticalSize,
41
- "--rs-icon-size": typeof props.size === "number" ? `${props.size}px` : void 0,
42
- "--rs-icon-weight": props.weight
62
+ "--rs-icon-color": void 0,
63
+ "--rs-icon-size": `${iconSize.value}px`
43
64
  }));
44
65
  const ariaHidden = computed(() => props.ariaLabel ? void 0 : "true");
45
66
  const role = computed(() => props.ariaLabel ? "img" : void 0);
46
67
  return (_ctx, _cache) => {
47
- return openBlock(), createElementBlock("span", {
68
+ return openBlock(), createElementBlock("svg", {
48
69
  "aria-hidden": ariaHidden.value,
49
70
  "aria-label": __props.ariaLabel,
50
71
  class: normalizeClass(rootClass.value),
51
72
  role: role.value,
52
- style: normalizeStyle(rootStyle.value)
53
- }, toDisplayString(__props.name), 15, _hoisted_1$2);
73
+ style: normalizeStyle(rootStyle.value),
74
+ width: iconSize.value,
75
+ height: iconSize.value,
76
+ fill: "currentColor"
77
+ }, [
78
+ createElementVNode("use", { href: symbolHref.value }, null, 8, _hoisted_2$2)
79
+ ], 14, _hoisted_1$2);
54
80
  };
55
81
  }
56
82
  });