@robosophy/ui-react 3.0.0 → 4.0.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/AGENTS.md CHANGED
@@ -29,21 +29,33 @@ The main barrel (`@robosophy/ui-react`) loads all components including PrimeReac
29
29
 
30
30
  PrimeReact apps:
31
31
 
32
+ Crea un archivo `layers.css` en tu aplicación:
33
+
34
+ ```css
35
+ /* layers.css */
36
+ @layer prime-base, prime-design;
37
+ @import "primereact/resources/themes/fluent-light/theme.css" layer(prime-base);
38
+ @import "@robosophy/prime-theme/prime.css" layer(prime-design);
39
+ ```
40
+
41
+ Luego importa este archivo en tu aplicación:
42
+
32
43
  ```tsx
33
- import { PrimeReactProvider } from "@primereact/core";
34
- import RobosophyPreset from "@robosophy/prime-theme";
35
- import "@robosophy/prime-theme/prime.css";
44
+ import { PrimeReactProvider } from "primereact/api";
45
+ import "./layers.css";
36
46
  import "@robosophy/ui-react/styles.css";
37
47
 
38
48
  function MyApp({ Component, pageProps }) {
39
49
  return (
40
- <PrimeReactProvider theme={{ preset: RobosophyPreset }}>
50
+ <PrimeReactProvider>
41
51
  <Component {...pageProps} />
42
52
  </PrimeReactProvider>
43
53
  );
44
54
  }
45
55
  ```
46
56
 
57
+ El archivo `layers.css` configura los CSS Cascade Layers, permitiendo que los estilos de la aplicación sobrescriban los del design system si es necesario.
58
+
47
59
  Non-PrimeReact apps:
48
60
 
49
61
  ```tsx
package/README.md CHANGED
@@ -28,25 +28,31 @@ npm install @robosophy/ui-react @robosophy/tokens react react-dom
28
28
 
29
29
  ## PrimeReact theme
30
30
 
31
- If your app uses PrimeReact, configure the Robosophy preset and load its tokens/overrides once:
31
+ PrimeReact 10 usa un sistema de temas CSS. Necesitas importar el tema `fluent-light` seguido de los overrides de Robosophy:
32
32
 
33
33
  ```tsx
34
- // app/layout.tsx or pages/_app.tsx
35
- import { PrimeReactProvider } from "@primereact/core";
36
- import RobosophyPreset from "@robosophy/prime-theme";
34
+ // app/layout.tsx o pages/_app.tsx
35
+ import { PrimeReactProvider } from "primereact/api";
36
+ import "primereact/resources/themes/fluent-light/theme.css";
37
37
  import "@robosophy/prime-theme/prime.css";
38
38
  import "@robosophy/ui-react/styles.css";
39
39
 
40
40
  function MyApp({ Component, pageProps }) {
41
41
  return (
42
- <PrimeReactProvider theme={{ preset: RobosophyPreset }}>
42
+ <PrimeReactProvider>
43
43
  <Component {...pageProps} />
44
44
  </PrimeReactProvider>
45
45
  );
46
46
  }
47
47
  ```
48
48
 
49
- `prime.css` already imports `@robosophy/tokens/tokens.css`, so don't duplicate that import.
49
+ **Orden de importación (crítico):**
50
+ 1. Tema base de PrimeReact (ej: `fluent-light/theme.css`) — estilos estructurales
51
+ 2. `@robosophy/prime-theme/prime.css` — aplica tokens `--rs-*` a componentes de PrimeReact
52
+
53
+ `prime.css` ya importa `@robosophy/tokens/tokens.css`, así que no dupliques ese import.
54
+
55
+ Solo `fluent-light` es compatible con los overrides de Robosophy.
50
56
 
51
57
  If your app doesn't use PrimeReact, import tokens once:
52
58
 
@@ -148,7 +154,7 @@ robosophyMaterialSymbols({
148
154
  For PrimeReact APIs that receive `icon` as a string, use the helper:
149
155
 
150
156
  ```tsx
151
- import { Button } from "@primereact/ui/button";
157
+ import { Button } from "primereact/button";
152
158
  import { rsPrimeIcon } from "@robosophy/ui-react";
153
159
 
154
160
  <Button label="Inicio" icon={rsPrimeIcon("home")} />
@@ -158,7 +164,7 @@ import { rsPrimeIcon } from "@robosophy/ui-react";
158
164
  For full control over props (fill, grade, etc.), use the `icon` slot:
159
165
 
160
166
  ```tsx
161
- import { Button } from "@primereact/ui/button";
167
+ import { Button } from "primereact/button";
162
168
  import { RsIcon } from "@robosophy/ui-react";
163
169
 
164
170
  <Button label="Favorito" icon={<RsIcon name="favorite" fill color="danger" />} />
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // src/components/RsSidebar/RsSidebar.tsx
6
6
  import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
7
- import { Menu } from "@primereact/ui/menu";
7
+ import { Menu } from "primereact/menu";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  var variantIconMap = {
10
10
  pc: "article",
@@ -44,7 +44,9 @@ var RsSidebar = forwardRef(
44
44
  const [isHovered, setIsHovered] = useState(false);
45
45
  const [activeParent, setActiveParent] = useState(null);
46
46
  const [isModuleMenuOpen, setIsModuleMenuOpen] = useState(false);
47
+ const [menuTop, setMenuTop] = useState(88);
47
48
  const rootRef = useRef(null);
49
+ const headerRef = useRef(null);
48
50
  const isFirstRenderRef = useRef(true);
49
51
  const onExpandedChangeRef = useRef(onExpandedChange);
50
52
  onExpandedChangeRef.current = onExpandedChange;
@@ -77,11 +79,30 @@ var RsSidebar = forwardRef(
77
79
  setIsModuleMenuOpen(false);
78
80
  onModuleSelect?.(module);
79
81
  if (navigateOnModuleSelect && typeof window !== "undefined" && module.link) {
82
+ console.warn(
83
+ "[RsSidebar] navigateOnModuleSelect is deprecated. Use onModuleSelect and handle navigation in your app to avoid full page reloads in SPAs."
84
+ );
80
85
  window.location.href = module.link;
81
86
  }
82
87
  },
83
88
  [navigateOnModuleSelect, onModuleSelect]
84
89
  );
90
+ const moduleMenuItems = useMemo(
91
+ () => modules.map((module) => {
92
+ const isCurrent = module.active || module.text === displayedHeaderTitle;
93
+ return {
94
+ className: [
95
+ "rs-sidebar__module-option",
96
+ isCurrent ? "rs-sidebar__module-option--active" : "",
97
+ module.disabled || isCurrent ? "rs-sidebar__module-option--disabled" : ""
98
+ ].filter(Boolean).join(" "),
99
+ command: () => selectModule(module),
100
+ disabled: module.disabled || isCurrent,
101
+ label: module.text
102
+ };
103
+ }),
104
+ [modules, displayedHeaderTitle, selectModule]
105
+ );
85
106
  const displayedItems = useMemo(
86
107
  () => activeParent ? (activeParent.children ?? []).filter(isVisible) : visibleItems,
87
108
  [activeParent, visibleItems]
@@ -106,6 +127,11 @@ var RsSidebar = forwardRef(
106
127
  }
107
128
  return classes.join(" ");
108
129
  }, [variant, isExpanded, mobileOpen, activeParent]);
130
+ useEffect(() => {
131
+ if (headerRef.current) {
132
+ setMenuTop(headerRef.current.offsetHeight);
133
+ }
134
+ }, [isExpanded, mobileOpen]);
109
135
  useEffect(() => {
110
136
  if (!mobileOpen) {
111
137
  setActiveParent(null);
@@ -196,7 +222,7 @@ var RsSidebar = forwardRef(
196
222
  if (header) {
197
223
  return header;
198
224
  }
199
- return /* @__PURE__ */ jsxs("header", { className: "rs-sidebar__header", children: [
225
+ return /* @__PURE__ */ jsxs("header", { ref: headerRef, className: "rs-sidebar__header", children: [
200
226
  /* @__PURE__ */ jsx(
201
227
  "div",
202
228
  {
@@ -232,7 +258,8 @@ var RsSidebar = forwardRef(
232
258
  ] })
233
259
  }
234
260
  ),
235
- renderHeaderAction()
261
+ renderHeaderAction(),
262
+ renderModuleMenu()
236
263
  ] });
237
264
  };
238
265
  const renderHeaderAction = () => {
@@ -265,24 +292,14 @@ var RsSidebar = forwardRef(
265
292
  if (!hasModuleSelector || !isModuleMenuOpen) {
266
293
  return null;
267
294
  }
268
- return /* @__PURE__ */ jsx(Menu.Root, { className: "rs-sidebar__module-menu", children: /* @__PURE__ */ jsx(Menu.List, { role: "menu", children: modules.map((module) => {
269
- const isCurrent = module.active || module.text === displayedHeaderTitle;
270
- return /* @__PURE__ */ jsx(
271
- Menu.Item,
272
- {
273
- value: module.text,
274
- className: [
275
- "rs-sidebar__module-option",
276
- isCurrent ? "rs-sidebar__module-option--active" : "",
277
- module.disabled || isCurrent ? "rs-sidebar__module-option--disabled" : ""
278
- ].filter(Boolean).join(" "),
279
- disabled: module.disabled || isCurrent,
280
- onSelect: () => selectModule(module),
281
- children: module.text
282
- },
283
- module.text
284
- );
285
- }) }) });
295
+ return /* @__PURE__ */ jsx(
296
+ Menu,
297
+ {
298
+ className: "rs-sidebar__module-menu",
299
+ style: { "--rs-sidebar-menu-top": `${menuTop}px` },
300
+ model: moduleMenuItems
301
+ }
302
+ );
286
303
  };
287
304
  const renderFooterContent = () => {
288
305
  if (typeof footer === "function") {
@@ -327,7 +344,6 @@ var RsSidebar = forwardRef(
327
344
  onMouseLeave: () => setHovered(false),
328
345
  children: [
329
346
  renderHeaderContent(),
330
- renderModuleMenu(),
331
347
  /* @__PURE__ */ jsxs("div", { className: "rs-sidebar__content", children: [
332
348
  /* @__PURE__ */ jsx("nav", { className: "rs-sidebar__nav", "aria-label": "Secciones", children: /* @__PURE__ */ jsxs("ul", { className: "rs-sidebar__list", children: [
333
349
  activeParent && /* @__PURE__ */ jsx("li", { className: "rs-sidebar__list-item", children: /* @__PURE__ */ jsxs(
@@ -132,7 +132,7 @@
132
132
  line-height: 1;
133
133
  text-overflow: ellipsis;
134
134
  }
135
- .rs-sidebar__module-menu {
135
+ .rs-sidebar__module-menu.p-menu {
136
136
  background: var(--rs-sidebar-background);
137
137
  border: 1px solid var(--rs-sidebar-border);
138
138
  border-radius: 4px;
@@ -142,37 +142,41 @@
142
142
  overflow: hidden;
143
143
  padding: 0;
144
144
  position: absolute;
145
- top: 88px;
145
+ top: var(--rs-sidebar-menu-top, 88px);
146
146
  z-index: 20;
147
147
  }
148
148
  .rs-sidebar__module-menu .p-menu-list {
149
149
  gap: 0;
150
150
  padding: 0;
151
151
  }
152
- .rs-sidebar__module-menu .rs-sidebar__module-option {
152
+ .rs-sidebar__module-option .p-menuitem-content {
153
153
  color: var(--rs-sidebar-color);
154
154
  border-radius: 0;
155
+ }
156
+ .rs-sidebar__module-option .p-menuitem-link {
155
157
  font-family: inherit;
156
158
  font-size: inherit;
157
159
  line-height: 1;
158
160
  min-height: 38px;
159
161
  padding: 10px 14px;
160
162
  }
161
- .rs-sidebar__module-menu .rs-sidebar__module-option:not(.p-disabled):hover,
162
- .rs-sidebar__module-menu .rs-sidebar__module-option--active {
163
+ .rs-sidebar__module-option:not(.p-disabled) .p-menuitem-content:hover,
164
+ .rs-sidebar__module-option--active .p-menuitem-content {
163
165
  background: var(--rs-sidebar-hover);
164
166
  color: var(--rs-sidebar-color);
165
167
  }
166
- .rs-sidebar__module-menu .rs-sidebar__module-option--disabled {
168
+ .rs-sidebar__module-option--disabled .p-menuitem-content {
167
169
  color: var(--rs-sidebar-muted);
168
170
  cursor: not-allowed;
169
171
  }
170
- .rs-sidebar__module-menu .rs-sidebar__module-option--disabled:hover {
172
+ .rs-sidebar__module-option--disabled .p-menuitem-content:hover {
171
173
  background: transparent;
172
174
  }
173
- .rs-sidebar__module-menu .rs-sidebar__module-option.p-focus {
175
+ .rs-sidebar__module-option.p-focus .p-menuitem-content {
174
176
  background: var(--rs-sidebar-hover);
175
177
  color: var(--rs-sidebar-color);
178
+ }
179
+ .rs-sidebar__module-option.p-focus .p-menuitem-link {
176
180
  outline: 2px solid var(--rs-color-stroke-focused, #48a0d4);
177
181
  outline-offset: -2px;
178
182
  }
@@ -294,7 +298,7 @@
294
298
  .rs-sidebar--mobile-open .rs-sidebar__module-menu {
295
299
  left: 16px;
296
300
  min-width: min(320px, calc(100% - 32px));
297
- top: 88px;
301
+ top: var(--rs-sidebar-menu-top, 88px);
298
302
  }
299
303
  .rs-sidebar--mobile-open .rs-sidebar__item {
300
304
  font-size: 18px;
@@ -22,6 +22,10 @@ type RsSidebarProps = {
22
22
  items: RsSidebarItem[];
23
23
  footerItems?: RsSidebarItem[];
24
24
  modules?: RsSidebarModule[];
25
+ /**
26
+ * @deprecated Use `onModuleSelect` instead and handle navigation in your app.
27
+ * This prop causes a full page reload in SPAs.
28
+ */
25
29
  navigateOnModuleSelect?: boolean;
26
30
  title?: string;
27
31
  icon?: string;
@@ -1,7 +1,7 @@
1
1
  // AUTO-GENERATED FILE. DO NOT EDIT MANUALLY.
2
2
  import {
3
3
  RsSidebar_default
4
- } from "../../chunk-DA5JOU36.js";
4
+ } from "../../chunk-H3GFVSAQ.js";
5
5
  import "../../chunk-PEJOAJYL.js";
6
6
  import "../../chunk-D4ZW5VUU.js";
7
7
  export {
package/dist/index.css CHANGED
@@ -182,7 +182,7 @@
182
182
  line-height: 1;
183
183
  text-overflow: ellipsis;
184
184
  }
185
- .rs-sidebar__module-menu {
185
+ .rs-sidebar__module-menu.p-menu {
186
186
  background: var(--rs-sidebar-background);
187
187
  border: 1px solid var(--rs-sidebar-border);
188
188
  border-radius: 4px;
@@ -192,37 +192,41 @@
192
192
  overflow: hidden;
193
193
  padding: 0;
194
194
  position: absolute;
195
- top: 88px;
195
+ top: var(--rs-sidebar-menu-top, 88px);
196
196
  z-index: 20;
197
197
  }
198
198
  .rs-sidebar__module-menu .p-menu-list {
199
199
  gap: 0;
200
200
  padding: 0;
201
201
  }
202
- .rs-sidebar__module-menu .rs-sidebar__module-option {
202
+ .rs-sidebar__module-option .p-menuitem-content {
203
203
  color: var(--rs-sidebar-color);
204
204
  border-radius: 0;
205
+ }
206
+ .rs-sidebar__module-option .p-menuitem-link {
205
207
  font-family: inherit;
206
208
  font-size: inherit;
207
209
  line-height: 1;
208
210
  min-height: 38px;
209
211
  padding: 10px 14px;
210
212
  }
211
- .rs-sidebar__module-menu .rs-sidebar__module-option:not(.p-disabled):hover,
212
- .rs-sidebar__module-menu .rs-sidebar__module-option--active {
213
+ .rs-sidebar__module-option:not(.p-disabled) .p-menuitem-content:hover,
214
+ .rs-sidebar__module-option--active .p-menuitem-content {
213
215
  background: var(--rs-sidebar-hover);
214
216
  color: var(--rs-sidebar-color);
215
217
  }
216
- .rs-sidebar__module-menu .rs-sidebar__module-option--disabled {
218
+ .rs-sidebar__module-option--disabled .p-menuitem-content {
217
219
  color: var(--rs-sidebar-muted);
218
220
  cursor: not-allowed;
219
221
  }
220
- .rs-sidebar__module-menu .rs-sidebar__module-option--disabled:hover {
222
+ .rs-sidebar__module-option--disabled .p-menuitem-content:hover {
221
223
  background: transparent;
222
224
  }
223
- .rs-sidebar__module-menu .rs-sidebar__module-option.p-focus {
225
+ .rs-sidebar__module-option.p-focus .p-menuitem-content {
224
226
  background: var(--rs-sidebar-hover);
225
227
  color: var(--rs-sidebar-color);
228
+ }
229
+ .rs-sidebar__module-option.p-focus .p-menuitem-link {
226
230
  outline: 2px solid var(--rs-color-stroke-focused, #48a0d4);
227
231
  outline-offset: -2px;
228
232
  }
@@ -344,7 +348,7 @@
344
348
  .rs-sidebar--mobile-open .rs-sidebar__module-menu {
345
349
  left: 16px;
346
350
  min-width: min(320px, calc(100% - 32px));
347
- top: 88px;
351
+ top: var(--rs-sidebar-menu-top, 88px);
348
352
  }
349
353
  .rs-sidebar--mobile-open .rs-sidebar__item {
350
354
  font-size: 18px;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // AUTO-GENERATED FILE. DO NOT EDIT MANUALLY.
2
2
  import {
3
3
  RsSidebar_default
4
- } from "./chunk-DA5JOU36.js";
4
+ } from "./chunk-H3GFVSAQ.js";
5
5
  import {
6
6
  RsIcon_default,
7
7
  rsPrimeIcon
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robosophy/ui-react",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -43,9 +43,10 @@
43
43
  "test": "vitest run"
44
44
  },
45
45
  "peerDependencies": {
46
- "@primereact/ui": ">=11",
47
- "react": ">=19",
48
- "react-dom": ">=19"
46
+ "@robosophy/prime-theme": ">=2.0.0",
47
+ "primereact": ">=10",
48
+ "react": ">=18",
49
+ "react-dom": ">=18"
49
50
  },
50
51
  "peerDependenciesMeta": {
51
52
  "react": {
@@ -54,15 +55,17 @@
54
55
  "react-dom": {
55
56
  "optional": true
56
57
  },
57
- "@primereact/ui": {
58
+ "primereact": {
59
+ "optional": true
60
+ },
61
+ "@robosophy/prime-theme": {
58
62
  "optional": true
59
63
  }
60
64
  },
61
65
  "dependencies": {
62
- "@robosophy/tokens": "^1.0.4"
66
+ "@robosophy/tokens": "^1.1.0"
63
67
  },
64
68
  "devDependencies": {
65
- "@primereact/ui": "^11.2.0",
66
69
  "@storybook/addon-docs": "^9.1.0",
67
70
  "@storybook/react": "^9.1.0",
68
71
  "@storybook/react-vite": "^9.1.0",
@@ -71,7 +74,7 @@
71
74
  "@types/react": "^19.1.0",
72
75
  "@types/react-dom": "^19.1.0",
73
76
  "jsdom": "^26.1.0",
74
- "primereact": "^11.2.0",
77
+ "primereact": "^10.9.6",
75
78
  "react": "^19.1.0",
76
79
  "react-dom": "^19.1.0",
77
80
  "storybook": "^9.1.0",