@axzydev/axzy_ui_system 1.7.3 → 1.8.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.
Files changed (82) hide show
  1. package/dist/index.cjs +1845 -721
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.cts +386 -15
  5. package/dist/index.d.ts +386 -15
  6. package/dist/index.js +1532 -418
  7. package/dist/index.js.map +1 -1
  8. package/dist/layered.css +1 -1
  9. package/package.json +4 -2
  10. package/src/App.tsx +6 -8
  11. package/src/components/atoms/button/button.tsx +7 -0
  12. package/src/components/atoms/flex/flex.props.ts +3 -1
  13. package/src/components/atoms/flex/flex.tsx +11 -3
  14. package/src/components/atoms/input/input.tsx +5 -0
  15. package/src/components/atoms/segmented-control/segmented-control.tsx +22 -5
  16. package/src/components/atoms/textarea/textarea.props.ts +4 -0
  17. package/src/components/atoms/textarea/textarea.tsx +13 -3
  18. package/src/components/molecules/card/card.props.ts +14 -0
  19. package/src/components/molecules/card/card.tsx +66 -23
  20. package/src/components/molecules/chip-input/chip-input.tsx +2 -0
  21. package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +4 -0
  22. package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +57 -41
  23. package/src/components/molecules/multi-select/multi-select.tsx +2 -0
  24. package/src/components/molecules/tabs/tabs.tsx +22 -8
  25. package/src/components/organisms/dropfile/dropfile.props.ts +8 -2
  26. package/src/components/organisms/dropfile/dropfile.tsx +17 -0
  27. package/src/components/organisms/page-header/page-header.tsx +22 -6
  28. package/src/components/organisms/sidebar/sidebar.tsx +8 -5
  29. package/src/components/organisms/topbar/topbar.props.ts +2 -0
  30. package/src/components/organisms/topbar/topbar.tsx +56 -34
  31. package/src/components/templates/layout/layout.tsx +3 -2
  32. package/src/components/theme-provider/themeProvider.props.ts +1 -1
  33. package/src/components/theme-provider/themeProvider.tsx +54 -1
  34. package/src/i18n/locales/en.ts +7 -0
  35. package/src/i18n/locales/es.ts +7 -0
  36. package/src/index.ts +35 -7
  37. package/src/json-renderer/actions/effects.test.tsx +115 -0
  38. package/src/json-renderer/actions/effects.ts +291 -0
  39. package/src/json-renderer/actions/effectsChain.test.ts +117 -0
  40. package/src/json-renderer/binding/binding.test.tsx +168 -0
  41. package/src/json-renderer/binding/resolveValue.ts +193 -0
  42. package/src/json-renderer/catalog/axzyCatalog.ts +5 -0
  43. package/src/json-renderer/estado-acciones.test.tsx +166 -0
  44. package/src/json-renderer/form/FormNode.tsx +92 -0
  45. package/src/json-renderer/form/form.test.tsx +148 -0
  46. package/src/json-renderer/form/formLength.test.tsx +99 -0
  47. package/src/json-renderer/form/rules.ts +162 -0
  48. package/src/json-renderer/index.ts +21 -0
  49. package/src/json-renderer/kernel/SchemaRenderer.tsx +83 -7
  50. package/src/json-renderer/kernel/capabilities.ts +30 -0
  51. package/src/json-renderer/kernel/renderNode.tsx +256 -3
  52. package/src/json-renderer/kernel/schemaRenderer.props.ts +29 -3
  53. package/src/json-renderer/kernel/slots.test.tsx +104 -0
  54. package/src/json-renderer/kernel/slots.ts +34 -0
  55. package/src/json-renderer/kernel/types.ts +4 -0
  56. package/src/json-renderer/routing/match.ts +82 -0
  57. package/src/json-renderer/routing/navigation.ts +71 -0
  58. package/src/json-renderer/routing/routes.ts +179 -0
  59. package/src/json-renderer/routing/routing.test.ts +147 -0
  60. package/src/json-renderer/seguridad.test.tsx +299 -0
  61. package/src/json-renderer/shared/errors.ts +1 -0
  62. package/src/json-renderer/stream/tolerantParse.ts +5 -0
  63. package/src/json-renderer/stream/useSchemaStream.test.tsx +143 -0
  64. package/src/json-renderer/stream/useSchemaStream.ts +227 -0
  65. package/src/json-renderer/validation/jsonSchema.test.ts +105 -0
  66. package/src/json-renderer/validation/jsonSchema.ts +317 -0
  67. package/src/json-renderer/validation/validate.ts +17 -0
  68. package/src/sandbox/axzyConfig.test.ts +4 -0
  69. package/src/sandbox/axzyConfig.ts +1 -1
  70. package/src/sandbox/i18n.ts +44 -0
  71. package/src/sandbox/useHashRoute.ts +2 -1
  72. package/src/showcases/DemoJsonDashboard.tsx +218 -103
  73. package/src/showcases/DemoJsonDocs.tsx +11 -11
  74. package/src/showcases/FeedbackShowcases.tsx +2 -1
  75. package/src/showcases/JsonScreenShowcase.tsx +43 -53
  76. package/src/showcases/StructureShowcases.tsx +17 -2
  77. package/src/showcases/demoApp/demoJson.test.tsx +241 -0
  78. package/src/showcases/demoApp/routes.test.ts +63 -0
  79. package/src/showcases/demoApp/schemas.test.ts +6 -17
  80. package/src/theme/theme-context.ts +10 -2
  81. package/src/theme/theme.ts +1 -1
  82. package/src/theme/theme.types.ts +2 -1
@@ -2,10 +2,12 @@ import React, { useState } from 'react';
2
2
  import { ITTabsProps } from './tabs.props';
3
3
  import { clsx } from 'clsx';
4
4
  import ITText from "@/components/atoms/text/text";
5
+ import { useITAppearance } from "@/theme/theme-context";
5
6
 
6
7
  /**
7
8
  * Tabbed navigation component with "line" and "pill" visual variants.
8
9
  * Manages its own active state internally via a defaultActiveId.
10
+ * With `appearance="bento"` the "pill" variant becomes a rounded track with an ink active pill.
9
11
  *
10
12
  * @example
11
13
  * <ITTabs
@@ -25,6 +27,7 @@ const ITTabs: React.FC<ITTabsProps> = ({
25
27
  className = '',
26
28
  containerClassName = ''
27
29
  }) => {
30
+ const bento = useITAppearance() === "bento" && variant === 'pill';
28
31
  const [activeId, setActiveId] = useState(defaultActiveId || items[0]?.id);
29
32
 
30
33
  const handleTabClick = (id: string, disabled?: boolean) => {
@@ -40,9 +43,10 @@ const ITTabs: React.FC<ITTabsProps> = ({
40
43
  {/* HEADER */}
41
44
  <div className={clsx(
42
45
  "flex border-secondary-200 mb-4",
43
- variant === 'line' ? "border-b" : "gap-2 p-1 bg-secondary-100 rounded-lg w-fit",
46
+ variant === 'line' ? "border-b" : bento ? "gap-2 p-1 rounded-full w-fit" : "gap-2 p-1 bg-secondary-100 rounded-lg w-fit",
44
47
  className
45
- )}>
48
+ )}
49
+ style={bento ? { backgroundColor: "var(--it-bento-control-bg, #f1f5f9)", borderColor: "transparent" } : undefined}>
46
50
  {items.map((item) => {
47
51
  const isActive = item.id === activeId;
48
52
 
@@ -61,14 +65,24 @@ const ITTabs: React.FC<ITTabsProps> = ({
61
65
  : "border-transparent text-secondary-500 hover:text-secondary-700 hover:border-secondary-300",
62
66
  ],
63
67
  // PILL VARIANT
64
- variant === 'pill' && [
65
- "rounded-md",
66
- isActive
67
- ? "bg-white text-primary-600 shadow-sm"
68
- : "text-secondary-500 hover:text-secondary-700",
69
- ],
68
+ variant === 'pill' && (bento
69
+ ? "rounded-full"
70
+ : [
71
+ "rounded-md",
72
+ isActive
73
+ ? "bg-white text-primary-600 shadow-sm"
74
+ : "text-secondary-500 hover:text-secondary-700",
75
+ ]),
70
76
  item.disabled && "opacity-50 cursor-not-allowed"
71
77
  )}
78
+ style={
79
+ bento
80
+ ? {
81
+ backgroundColor: isActive ? "var(--it-bento-ink-bg, #0f172a)" : "transparent",
82
+ color: isActive ? "var(--it-bento-ink-text, #ffffff)" : "var(--it-bento-control-text, #475569)",
83
+ }
84
+ : undefined
85
+ }
72
86
  >
73
87
  {item.icon && <span className="w-4 h-4">{item.icon}</span>}
74
88
  <ITText as="span">{item.label}</ITText>
@@ -28,8 +28,14 @@ export interface ITDropfileProps {
28
28
  onFileSelect: (file: File | null) => void;
29
29
  /** Called when the user cancels the current selection. */
30
30
  onCancel?: () => void;
31
- /** Called when the user confirms and submits the file. */
32
- onSubmit?: (file: File) => void;
31
+ /** Called when the user confirms and submits the file. With `autoUpload`, it runs right after selection and may return a promise that drives the status. */
32
+ onSubmit?: (file: File) => void | Promise<void>;
33
+ /**
34
+ * Sube en cuanto se elige el archivo, sin paso de confirmar. Usa `onSubmit` como la subida:
35
+ * el estado pasa a subiendo y luego a subido, o a error si la promesa falla.
36
+ * @default false
37
+ */
38
+ autoUpload?: boolean;
33
39
  /** List of accepted MIME types. */
34
40
  acceptedFileTypes?: FileTypeEnum[];
35
41
  /** Additional classes for the preview/content area. */
@@ -43,6 +43,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
43
43
  onFileSelect,
44
44
  onCancel,
45
45
  onSubmit,
46
+ autoUpload = false,
46
47
  contentClassName,
47
48
  containerClassName,
48
49
  acceptedFileTypes = [FileTypeEnum.PDF, FileTypeEnum.XLS, FileTypeEnum.XLSX, FileTypeEnum.JPG, FileTypeEnum.PNG, FileTypeEnum.JPEG],
@@ -207,6 +208,20 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
207
208
  );
208
209
  };
209
210
 
211
+ /** Subida automática: corre `onSubmit` al elegir el archivo y sigue su estado. */
212
+ const runAutoUpload = async (file: File) => {
213
+ setIsConfirmed(true);
214
+ setUploadStatus(UploadStatus.UPLOADING);
215
+ try {
216
+ onFileSelect(file);
217
+ await onSubmit?.(file);
218
+ setUploadStatus(UploadStatus.UPLOADED);
219
+ } catch (error) {
220
+ setUploadStatus(UploadStatus.ERROR);
221
+ console.error("Error al subir archivo:", error);
222
+ }
223
+ };
224
+
210
225
  const onDrop = (acceptedFiles: File[]) => {
211
226
  const file = acceptedFiles[0];
212
227
  if (file) {
@@ -224,6 +239,8 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
224
239
  const previewUrl = URL.createObjectURL(file);
225
240
  setImagePreview(previewUrl);
226
241
  }
242
+
243
+ if (autoUpload) void runAutoUpload(file);
227
244
  } else {
228
245
  alert(t("dropfile.fileTypeNotAllowed"));
229
246
  setSelectedFile(null);
@@ -2,7 +2,7 @@ import { FaChevronLeft } from "react-icons/fa";
2
2
  import { ITPageHeaderProps } from "./page-header.props";
3
3
  import ITBreadcrumbs from "@/components/molecules/breadcrumbs/breadcrumbs";
4
4
  import ITText from "@/components/atoms/text/text";
5
- import { useITFlatAppearance } from "@/theme/theme-context";
5
+ import { useITAppearance } from "@/theme/theme-context";
6
6
  import { useITTranslation } from "@/i18n";
7
7
 
8
8
  /**
@@ -33,7 +33,10 @@ export default function ITPageHeader({
33
33
  const { t } = useITTranslation();
34
34
  const showTopRow = breadcrumbs?.length || backAction;
35
35
  // Flat appearance: compact title, no decorative icon tile.
36
- const flat = useITFlatAppearance();
36
+ // Bento: no icon tile either, but a larger tight title and a borderless round back button.
37
+ const appearance = useITAppearance();
38
+ const bento = appearance === "bento";
39
+ const flat = appearance === "flat";
37
40
 
38
41
  return (
39
42
  <div className={`${className} space-y-3`}>
@@ -44,7 +47,16 @@ export default function ITPageHeader({
44
47
  {backAction && (
45
48
  <button
46
49
  onClick={backAction}
47
- className="flex items-center justify-center w-9 h-9 rounded-full border border-slate-200 dark:border-slate-700 text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:border-slate-300 dark:hover:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-800/60 transition-all duration-200 flex-shrink-0"
50
+ className={
51
+ bento
52
+ ? "flex items-center justify-center w-10 h-10 rounded-full hover:opacity-80 transition-opacity duration-200 flex-shrink-0"
53
+ : "flex items-center justify-center w-9 h-9 rounded-full border border-slate-200 dark:border-slate-700 text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:border-slate-300 dark:hover:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-800/60 transition-all duration-200 flex-shrink-0"
54
+ }
55
+ style={
56
+ bento
57
+ ? { backgroundColor: "var(--it-bento-control-bg, #f1f5f9)", color: "var(--it-bento-control-text, #475569)" }
58
+ : undefined
59
+ }
48
60
  aria-label={t("pageHeader.back")}
49
61
  >
50
62
  <FaChevronLeft size={14} />
@@ -65,7 +77,7 @@ export default function ITPageHeader({
65
77
  {/* Fila principal: icono + título + descripción + (acciones si no hay top row) */}
66
78
  <div className="flex items-start justify-between gap-6">
67
79
  <div className="flex items-start gap-4 min-w-0">
68
- {icon && !flat && (
80
+ {icon && !flat && !bento && (
69
81
  <div
70
82
  className="flex items-center justify-center w-12 h-12 rounded-full border border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-800/50 text-slate-600 dark:text-slate-300 flex-shrink-0 transition-all duration-200 hover:border-slate-300 dark:hover:border-slate-500 hover:shadow-sm"
71
83
  style={iconColor ? { color: iconColor } : undefined}
@@ -77,7 +89,9 @@ export default function ITPageHeader({
77
89
  <ITText
78
90
  as="h1"
79
91
  className={
80
- flat
92
+ bento
93
+ ? "text-3xl font-bold text-slate-900 dark:text-white tracking-tighter leading-tight"
94
+ : flat
81
95
  ? "text-[1.625rem] font-bold text-slate-900 dark:text-white tracking-tight leading-tight"
82
96
  : "text-3xl font-bold text-slate-800 dark:text-white tracking-tight leading-tight"
83
97
  }
@@ -88,7 +102,9 @@ export default function ITPageHeader({
88
102
  <ITText
89
103
  as="p"
90
104
  className={
91
- flat
105
+ bento
106
+ ? "text-sm font-normal text-slate-500 dark:text-slate-400 mt-1.5 leading-relaxed"
107
+ : flat
92
108
  ? "text-sm font-normal text-slate-500 dark:text-slate-400 mt-0.5 leading-relaxed"
93
109
  : "text-sm font-light text-slate-500 dark:text-slate-400 mt-1 leading-relaxed"
94
110
  }
@@ -10,7 +10,7 @@ import {
10
10
  } from "./sidebar.props";
11
11
  import ITBadget from "@/components/atoms/badget/badget";
12
12
  import ITText from "@/components/atoms/text/text";
13
- import { useITFlatAppearance } from "@/theme/theme-context";
13
+ import { useITAppearance } from "@/theme/theme-context";
14
14
  import { useITTranslation } from "@/i18n";
15
15
 
16
16
  /** Narrows a submenu entry to a titled group. */
@@ -95,7 +95,10 @@ export default function ITSidebar({
95
95
  expandOnHover = true,
96
96
  }: ITSidebarProps) {
97
97
  // Flat appearance: plain surface, no glow/accent bar, narrower panel.
98
- const flat = useITFlatAppearance();
98
+ // Bento shares that structure; its tokens make the active item a neutral pill with bold ink text.
99
+ const appearance = useITAppearance();
100
+ const bento = appearance === "bento";
101
+ const flat = appearance === "flat" || bento;
99
102
  const { t } = useITTranslation();
100
103
  const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
101
104
  const [isHovering, setIsHovering] = useState(false);
@@ -202,7 +205,7 @@ export default function ITSidebar({
202
205
  color: subitem.isActive ? "var(--it-sidebar-active-color, var(--color-secondary-900))" : "var(--it-sidebar-label-color, var(--color-secondary-600))",
203
206
  backgroundColor: subitem.isActive ? "var(--it-sidebar-active-bg, var(--color-secondary-50))" : 'transparent',
204
207
  fontSize: flat ? '0.84rem' : '0.78rem',
205
- fontWeight: subitem.isActive ? 600 : 500,
208
+ fontWeight: subitem.isActive ? (bento ? 700 : 600) : 500,
206
209
  letterSpacing: '0.01em',
207
210
  marginLeft: subitemConnector === '|' ? '-1px' : '0',
208
211
  }}
@@ -365,7 +368,7 @@ export default function ITSidebar({
365
368
  style={{
366
369
  zIndex: 50,
367
370
  backgroundColor: "var(--it-sidebar-bg, rgba(255, 255, 255, 0.90))",
368
- borderRight: "1px solid var(--it-sidebar-border, var(--color-secondary-200))",
371
+ borderRight: bento ? "none" : "1px solid var(--it-sidebar-border, var(--color-secondary-200))",
369
372
  ...(flat ? {} : { WebkitBackdropFilter: 'blur(12px)', backdropFilter: 'blur(12px)' }),
370
373
  }}
371
374
  >
@@ -480,7 +483,7 @@ export default function ITSidebar({
480
483
  style={{
481
484
  color: active ? "var(--it-sidebar-active-color, #ffffff)" : "var(--it-sidebar-label-color, var(--color-secondary-300))",
482
485
  fontSize: flat ? '0.875rem' : '0.8rem',
483
- fontWeight: active ? '600' : '500'
486
+ fontWeight: active ? (bento ? '700' : '600') : '500'
484
487
  }}
485
488
  >
486
489
  {item.label}
@@ -7,6 +7,8 @@ export interface ITTopBarNavItem {
7
7
  label: string;
8
8
  /** Optional icon element rendered beside the label. */
9
9
  icon?: any;
10
+ /** Marks the item as the current one. Sets `aria-current="page"`; with `appearance="bento"` it renders as the ink pill (dark fill, white text), in the other appearances as a subtle highlight. @default false */
11
+ isActive?: boolean;
10
12
  /** Click handler for this navigation item (legacy, prefer onNavItemClick). */
11
13
  action: () => void;
12
14
  }
@@ -3,7 +3,7 @@ import useClickOutside from "@/hooks/useClickOutside";
3
3
  import { useRef, useState } from "react";
4
4
  import { ITTopBarProps } from "./topbar.props";
5
5
  import ITText from "@/components/atoms/text/text";
6
- import { useITFlatAppearance } from "@/theme/theme-context";
6
+ import { useITAppearance } from "@/theme/theme-context";
7
7
  import { useITTranslation } from "@/i18n";
8
8
 
9
9
  /**
@@ -73,7 +73,10 @@ export default function ITTopBar({
73
73
  showDesktopMenuButton = false,
74
74
  }: ITTopBarProps) {
75
75
  // Flat appearance: plain bar (no shadow/blur), initials avatar, name before it.
76
- const flat = useITFlatAppearance();
76
+ // Bento: same plain structure, blended with the canvas, pill nav and round icon buttons.
77
+ const appearance = useITAppearance();
78
+ const bento = appearance === "bento";
79
+ const flat = appearance === "flat" || bento;
77
80
  const { t } = useITTranslation();
78
81
  const initials = (userMenu?.userName ?? "")
79
82
  .trim()
@@ -81,6 +84,8 @@ export default function ITTopBar({
81
84
  .slice(0, 2)
82
85
  .map((word) => word.charAt(0).toUpperCase())
83
86
  .join("");
87
+ const userRestBg = bento ? "var(--it-bento-control-bg, #f1f5f9)" : "transparent";
88
+ const userHoverBg = bento ? "var(--it-bento-control-bg-hover, #e2e8f0)" : "var(--it-topbar-user-hover, #f1f5f9)";
84
89
  const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
85
90
  const userMenuRef = useRef<HTMLDivElement>(null);
86
91
 
@@ -90,8 +95,10 @@ export default function ITTopBar({
90
95
  <header
91
96
  className={`sticky top-0 z-40 transition-all duration-300 ${flat ? "" : "backdrop-blur-md"}`}
92
97
  style={{
93
- backgroundColor: "var(--it-topbar-bg, rgba(255, 255, 255, 0.9))",
94
- borderBottom: "1px solid var(--it-topbar-border, rgba(15, 23, 42, 0.06))",
98
+ backgroundColor: bento
99
+ ? "var(--it-layout-bg, #f4f4f6)"
100
+ : "var(--it-topbar-bg, rgba(255, 255, 255, 0.9))",
101
+ borderBottom: bento ? "none" : "1px solid var(--it-topbar-border, rgba(15, 23, 42, 0.06))",
95
102
  boxShadow: flat ? "none" : "var(--shadow-xs)",
96
103
  }}
97
104
  >
@@ -105,12 +112,13 @@ export default function ITTopBar({
105
112
  <button
106
113
  type="button"
107
114
  aria-label={t("common.menu")}
108
- className={`${showDesktopMenuButton ? "" : "lg:hidden"} p-2.5 -ml-2.5 rounded-xl transition-colors duration-200`}
115
+ className={`${showDesktopMenuButton ? "" : "lg:hidden"} ${bento ? "w-10 h-10 flex items-center justify-center rounded-full" : "p-2.5 -ml-2.5 rounded-xl"} transition-colors duration-200`}
109
116
  style={{
110
- color: "var(--it-topbar-icon, #64748b)",
117
+ color: bento ? "var(--it-bento-control-text, #475569)" : "var(--it-topbar-icon, #64748b)",
118
+ backgroundColor: bento ? "var(--it-bento-control-bg, #f1f5f9)" : undefined,
111
119
  }}
112
- onMouseEnter={(e) => e.currentTarget.style.backgroundColor = "var(--it-topbar-user-hover, #f1f5f9)"}
113
- onMouseLeave={(e) => e.currentTarget.style.backgroundColor = "transparent"}
120
+ onMouseEnter={(e) => e.currentTarget.style.backgroundColor = bento ? "var(--it-bento-control-bg-hover, #e2e8f0)" : "var(--it-topbar-user-hover, #f1f5f9)"}
121
+ onMouseLeave={(e) => e.currentTarget.style.backgroundColor = bento ? "var(--it-bento-control-bg, #f1f5f9)" : "transparent"}
114
122
  onClick={onToggleMobileMenu}
115
123
  >
116
124
  <FaBars className="w-[1.125rem] h-[1.125rem]" />
@@ -137,28 +145,42 @@ export default function ITTopBar({
137
145
 
138
146
  {/* Top Navigation Items (Desktop) */}
139
147
  {navItems && navItems.length > 0 && (
140
- <nav className="hidden md:flex ml-8 space-x-1 border-l pl-8" style={{ borderColor: "var(--it-topbar-border, rgba(15, 23, 42, 0.06))" }}>
141
- {navItems.map((item) => (
142
- <button
143
- key={item.id}
144
- onClick={() => onNavItemClick?.(item.id)}
145
- className="px-4 py-2 rounded-lg font-medium text-[0.9rem] transition-all duration-200 ease-[cubic-bezier(0.2,0,0,1)]"
146
- style={{ color: "var(--it-topbar-text, #475569)" }}
147
- onMouseEnter={(e) => {
148
- e.currentTarget.style.color = "var(--it-topbar-text, #0f172a)";
149
- e.currentTarget.style.backgroundColor = "var(--it-topbar-user-hover, #f1f5f9)";
150
- }}
151
- onMouseLeave={(e) => {
152
- e.currentTarget.style.color = "var(--it-topbar-text, #475569)";
153
- e.currentTarget.style.backgroundColor = "transparent";
154
- }}
155
- >
156
- <div className="flex items-center gap-2">
157
- {item.icon && <span className="opacity-70">{item.icon}</span>}
158
- <ITText as="span">{item.label}</ITText>
159
- </div>
160
- </button>
161
- ))}
148
+ <nav
149
+ className={bento ? "hidden md:flex ml-4 gap-2" : "hidden md:flex ml-8 space-x-1 border-l pl-8"}
150
+ style={bento ? undefined : { borderColor: "var(--it-topbar-border, rgba(15, 23, 42, 0.06))" }}
151
+ >
152
+ {navItems.map((item) => {
153
+ // Resting look per appearance; the active item is the ink pill in bento.
154
+ const restBg = bento
155
+ ? item.isActive ? "var(--it-bento-ink-bg, #0f172a)" : "var(--it-bento-control-bg, #f1f5f9)"
156
+ : item.isActive ? "var(--it-topbar-user-hover, #f1f5f9)" : "transparent";
157
+ const restColor = bento
158
+ ? item.isActive ? "var(--it-bento-ink-text, #ffffff)" : "var(--it-bento-control-text, #475569)"
159
+ : "var(--it-topbar-text, #475569)";
160
+ return (
161
+ <button
162
+ key={item.id}
163
+ onClick={() => onNavItemClick?.(item.id)}
164
+ aria-current={item.isActive ? "page" : undefined}
165
+ className={`px-4 py-2 ${bento ? "rounded-full" : "rounded-lg"} font-medium text-[0.9rem] transition-all duration-200 ease-[cubic-bezier(0.2,0,0,1)]`}
166
+ style={{ color: restColor, backgroundColor: restBg }}
167
+ onMouseEnter={(e) => {
168
+ if (bento && item.isActive) return;
169
+ e.currentTarget.style.color = bento ? "var(--it-bento-control-text, #0f172a)" : "var(--it-topbar-text, #0f172a)";
170
+ e.currentTarget.style.backgroundColor = bento ? "var(--it-bento-control-bg-hover, #e2e8f0)" : "var(--it-topbar-user-hover, #f1f5f9)";
171
+ }}
172
+ onMouseLeave={(e) => {
173
+ e.currentTarget.style.color = restColor;
174
+ e.currentTarget.style.backgroundColor = restBg;
175
+ }}
176
+ >
177
+ <div className="flex items-center gap-2">
178
+ {item.icon && <span className="opacity-70">{item.icon}</span>}
179
+ <ITText as="span">{item.label}</ITText>
180
+ </div>
181
+ </button>
182
+ );
183
+ })}
162
184
  </nav>
163
185
  )}
164
186
  </div>
@@ -184,15 +206,15 @@ export default function ITTopBar({
184
206
  <div className="relative">
185
207
  <button
186
208
  type="button"
187
- className={`flex items-center gap-3 rounded-full py-1.5 transition-all duration-200 ease-[cubic-bezier(0.2,0,0,1)] border border-transparent hover:border-secondary-200 ${flat ? "flex-row-reverse pl-4 pr-2" : "pl-2 pr-4"}`}
209
+ className={`flex items-center gap-3 rounded-full py-1.5 transition-all duration-200 ease-[cubic-bezier(0.2,0,0,1)] border border-transparent ${bento ? "" : "hover:border-secondary-200"} ${flat ? "flex-row-reverse pl-4 pr-2" : "pl-2 pr-4"}`}
188
210
  style={{
189
- backgroundColor: isUserMenuOpen ? "var(--it-topbar-user-hover, #f1f5f9)" : "transparent",
211
+ backgroundColor: isUserMenuOpen ? userHoverBg : userRestBg,
190
212
  }}
191
213
  onMouseEnter={(e) => {
192
- if (!isUserMenuOpen) e.currentTarget.style.backgroundColor = "var(--it-topbar-user-hover, #f1f5f9)";
214
+ if (!isUserMenuOpen) e.currentTarget.style.backgroundColor = userHoverBg;
193
215
  }}
194
216
  onMouseLeave={(e) => {
195
- if (!isUserMenuOpen) e.currentTarget.style.backgroundColor = "transparent";
217
+ if (!isUserMenuOpen) e.currentTarget.style.backgroundColor = userRestBg;
196
218
  }}
197
219
  onClick={() => setIsUserMenuOpen(!isUserMenuOpen)}
198
220
  >
@@ -4,7 +4,7 @@ import ITTopBar from "@/components/organisms/topbar/topbar";
4
4
  import ITSidebar from "@/components/organisms/sidebar/sidebar";
5
5
  import { ITLayoutProps } from "./layout.props";
6
6
  import { theme } from "@/theme/theme";
7
- import { useITFlatAppearance } from "@/theme/theme-context";
7
+ import { useITAppearance } from "@/theme/theme-context";
8
8
 
9
9
  /**
10
10
  * Main application shell with sidebar, topbar, and content area.
@@ -49,7 +49,8 @@ export default function ITLayout({
49
49
  }: ITLayoutProps) {
50
50
  const [internalCollapsed, setInternalCollapsed] = useState(true);
51
51
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
52
- const flat = useITFlatAppearance();
52
+ // Bento shares the flat sidebar structure (narrow panel, no drop shadow).
53
+ const flat = useITAppearance() !== "soft";
53
54
 
54
55
  const isControlled = sidebar.isCollapsed !== undefined;
55
56
  const desktopCollapsed = isControlled ? sidebar.isCollapsed : internalCollapsed;
@@ -58,6 +58,6 @@ export interface ITThemeProviderProps {
58
58
  radius?: number;
59
59
  /** Global shadow strength level. `0` ⇒ no shadows, `1` ⇒ subtle, `2` ⇒ default current look, `3` ⇒ pronounced. Shadows are injected as `--shadow-*`/`.shadow-*` overrides so cards, dropdowns and dialogs follow the level. @default 2 (keeps current default when omitted) */
60
60
  shadow?: number;
61
- /** Visual language of the whole system. `"soft"` keeps the current look (floating cards, tinted shadows, glass sidebar/top bar). `"flat"` switches the app shell and surfaces to flat ones: hairline borders instead of shadows on `ITCard`, a plain `ITSidebar` (no glow, no accent bar, narrower), a plain `ITTopBar` (no shadow, initials avatar) and a compact `ITPageHeader` without the icon tile. It also sets `data-it-appearance` on `<html>` so app CSS can follow it. Combine it with `shadow` and `radius` to finish the look. @default "soft" */
61
+ /** Visual language of the whole system. `"soft"` keeps the current look (floating cards, tinted shadows, glass sidebar/top bar). `"flat"` switches the app shell and surfaces to flat ones: hairline borders instead of shadows on `ITCard`, a plain `ITSidebar` (no glow, no accent bar, narrower), a plain `ITTopBar` (no shadow, initials avatar) and a compact `ITPageHeader` without the icon tile. It also sets `data-it-appearance` on `<html>` so app CSS can follow it. `"bento"` gives a modern pastel dashboard: light-gray canvas, borderless white cards with a diffuse shadow and 24px radius, pill buttons and inputs, an ink-pill active item in `ITTopBar` and a neutral-pill one in `ITSidebar`, a large `ITPageHeader` title and pastel tiles through `ITCard` `tone`; an explicit `radius`/`shadow` prop wins over the bento defaults. Combine it with `shadow` and `radius` to finish the look. @default "soft" */
62
62
  appearance?: ITThemeAppearance;
63
63
  }
@@ -83,7 +83,7 @@ const PRESET_I18N_KEY: Record<string, string> = {
83
83
  // Contexto y hooks (useITTheme / useITThemeSafe) viven en @/theme/theme-context.
84
84
  // Re-exportados desde aquí para mantener compatibilidad de la API pública.
85
85
  // eslint-disable-next-line react-refresh/only-export-components
86
- export { useITTheme, useITThemeSafe, useITFlatAppearance } from "@/theme/theme-context";
86
+ export { useITTheme, useITThemeSafe, useITFlatAppearance, useITAppearance } from "@/theme/theme-context";
87
87
 
88
88
  const getNestedValue = (obj: any, path: string) => {
89
89
  return path.split(".").reduce((acc, part) => acc && acc[part], obj);
@@ -343,6 +343,59 @@ export default function ITThemeProvider({
343
343
  };
344
344
  }, [radius, shadow]);
345
345
 
346
+ // Tokens del lenguaje "bento": solo se inyectan con appearance="bento".
347
+ // Los componentes leen estos vars (con fallback al aspecto base), así que un
348
+ // `radius`/`shadow` explícito gana: en ese caso no se definen los vars de
349
+ // radio/sombra y los componentes caen a la escala global.
350
+ useLayoutEffect(() => {
351
+ const STYLE_ID = "it-theme-bento-tokens";
352
+ let tag = document.getElementById(STYLE_ID) as HTMLStyleElement | null;
353
+ if (appearance !== "bento") {
354
+ tag?.remove();
355
+ return;
356
+ }
357
+ if (!tag) {
358
+ tag = document.createElement("style");
359
+ tag.id = STYLE_ID;
360
+ document.head.appendChild(tag);
361
+ }
362
+ const hasRadius = typeof radius === "number" && isFinite(radius);
363
+ const hasShadow = shadowLevel(shadow) !== null;
364
+ const shapeLight = hasRadius
365
+ ? ""
366
+ : "--it-bento-card-radius:24px;--it-bento-pill-radius:9999px;--it-bento-field-radius:1rem;--it-input-radius:var(--it-bento-pill-radius);";
367
+ const shadowLight = hasShadow
368
+ ? ""
369
+ : "--it-bento-card-shadow:0 1px 2px rgba(16,24,40,.04),0 12px 32px -16px rgba(16,24,40,.10);--it-bento-card-shadow-hover:0 1px 2px rgba(16,24,40,.06),0 18px 40px -16px rgba(16,24,40,.18);";
370
+ const shadowDark = hasShadow
371
+ ? ""
372
+ : "--it-bento-card-shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px -16px rgba(0,0,0,.55);--it-bento-card-shadow-hover:0 1px 2px rgba(0,0,0,.5),0 18px 40px -16px rgba(0,0,0,.7);";
373
+ // El item activo del sidebar sigue los tokens bento (píldora neutra, texto tinta).
374
+ const navTokens =
375
+ "--it-sidebar-active-bg:var(--it-bento-nav-active-bg);--it-sidebar-active-color:var(--it-bento-nav-active-text);--it-sidebar-active-icon:var(--it-bento-nav-active-text);";
376
+ const light =
377
+ "--it-layout-bg:#f4f4f6;--it-card-bg:#ffffff;--it-bento-card-padding:1.5rem;" +
378
+ "--it-input-bg:#f1f5f9;--it-input-border:transparent;" +
379
+ "--it-input-focus-ring:0 0 0 2px color-mix(in srgb,var(--color-primary) 50%,transparent);" +
380
+ "--it-bento-control-bg:#f1f5f9;--it-bento-control-bg-hover:#e2e8f0;--it-bento-control-text:#475569;" +
381
+ "--it-bento-ink-bg:#0f172a;--it-bento-ink-text:#ffffff;" +
382
+ "--it-bento-nav-active-bg:#f1f5f9;--it-bento-nav-active-text:#0f172a;" + navTokens;
383
+ const dark =
384
+ "--it-layout-bg:#020617;--it-card-bg:#0f172a;" +
385
+ "--it-input-bg:#1e293b;--it-input-border:transparent;" +
386
+ "--it-bento-control-bg:#1e293b;--it-bento-control-bg-hover:#334155;--it-bento-control-text:#cbd5e1;" +
387
+ "--it-bento-ink-bg:#ffffff;--it-bento-ink-text:#0f172a;" +
388
+ "--it-bento-nav-active-bg:#1e293b;--it-bento-nav-active-text:#ffffff;" + navTokens;
389
+ const A = '[data-it-appearance="bento"]';
390
+ tag.textContent =
391
+ `:root${A}{${light}${shapeLight}${shadowLight}}` +
392
+ `:root${A}.dark,:root${A} .dark,:root${A} [data-theme="dark"]{${dark}${shadowDark}}` +
393
+ `:root${A} [data-theme="light"]{${light}${shadowLight}}`;
394
+ return () => {
395
+ tag?.remove();
396
+ };
397
+ }, [appearance, radius, shadow]);
398
+
346
399
  // Inyectar variables CSS en el :root al cambiar la paleta o tema resuelto
347
400
  useEffect(() => {
348
401
  const injectStyles = (obj: any, prefix = "") => {
@@ -212,6 +212,13 @@ const en: ITMessages = {
212
212
  "jsonEditor.kindNumber": "number",
213
213
  "jsonEditor.kindBoolean": "boolean",
214
214
  "jsonEditor.kindNull": "null",
215
+
216
+ // ---------------------------------------------------------------- jsonRenderer
217
+ "jsonRenderer.form.required": "Required field.",
218
+ "jsonRenderer.form.email": "Enter a valid email address.",
219
+ "jsonRenderer.form.minLength": "Enter at least {min} characters.",
220
+ "jsonRenderer.form.maxLength": "Enter at most {max} characters.",
221
+ "jsonRenderer.form.submit": "Submit",
215
222
  };
216
223
 
217
224
  export default en;
@@ -214,6 +214,13 @@ const es: ITMessages = {
214
214
  "jsonEditor.kindNumber": "número",
215
215
  "jsonEditor.kindBoolean": "booleano",
216
216
  "jsonEditor.kindNull": "nulo",
217
+
218
+ // ---------------------------------------------------------------- jsonRenderer
219
+ "jsonRenderer.form.required": "Campo obligatorio.",
220
+ "jsonRenderer.form.email": "Escribe un correo válido.",
221
+ "jsonRenderer.form.minLength": "Escribe al menos {min} caracteres.",
222
+ "jsonRenderer.form.maxLength": "Escribe como máximo {max} caracteres.",
223
+ "jsonRenderer.form.submit": "Enviar",
217
224
  };
218
225
 
219
226
  export default es;
package/src/index.ts CHANGED
@@ -84,6 +84,7 @@ import {
84
84
  GLOBALLY_FORBIDDEN_PROPS,
85
85
  resolveNode,
86
86
  parsePartialSchema,
87
+ useSchemaStream,
87
88
  isNodePending,
88
89
  JsonEngineError,
89
90
  issue,
@@ -102,8 +103,30 @@ import {
102
103
  DEFAULT_MIGRATIONS,
103
104
  buildVocabularyContract,
104
105
  formatVocabularyForPrompt,
106
+ createBindingResolver,
107
+ validateRoutes,
108
+ flattenRoutes,
109
+ joinRoutePath,
110
+ matchRoute,
111
+ routeHref,
112
+ buildNavigationTree,
113
+ navContainsRoute,
105
114
  } from "./json-renderer";
106
115
  import type {
116
+ DataHandler,
117
+ DataLoader,
118
+ BindingScope,
119
+ BindingResolver,
120
+ SlotComponent,
121
+ SlotRegistry,
122
+ EffectRegistry,
123
+ ToastVariant,
124
+ RoutesSchema,
125
+ RouteSchema,
126
+ RouteNavSchema,
127
+ FlatRoute,
128
+ RouteMatch,
129
+ NavNode,
107
130
  ITJsonRenderEngineProps,
108
131
  ScreenSchema,
109
132
  NodeSchema,
@@ -122,6 +145,7 @@ import type {
122
145
  UnresolvedNode,
123
146
  PartialParseResult,
124
147
  StreamState,
148
+ StreamSource,
125
149
  JsonEngineIssue,
126
150
  JsonEngineCode,
127
151
  JsonEngineSeverity,
@@ -147,7 +171,7 @@ import type { ITBadgetProps } from "./components/atoms/badget/badget.props";
147
171
  import type { ITBreadcrumbsProps, ITBreadcrumbItem } from "./components/molecules/breadcrumbs/breadcrumbs.props";
148
172
  import type { ITButtonProps } from "./components/atoms/button/button.props";
149
173
  import type { ITCalendarProps } from "./components/molecules/calendar/calendar.props";
150
- import type { ITCardProps } from "./components/molecules/card/card.props";
174
+ import type { ITCardProps, ITCardTone } from "./components/molecules/card/card.props";
151
175
  import type { ITCheckboxProps } from "./components/atoms/checkbox/checkbox.props";
152
176
  import type { ITChipProps } from "./components/atoms/chip/chip.props";
153
177
  import type { ITChipInputProps } from "./components/molecules/chip-input/chip-input.props";
@@ -208,7 +232,7 @@ import ITLoader from "./components/atoms/loader/loader";
208
232
  import type { LoaderProps as ITLoaderProps } from "./components/atoms/loader/loader.props";
209
233
  import ITStepper from "./components/molecules/stepper/stepper";
210
234
  import type { ITStepperProps } from "./components/molecules/stepper/stepper.props";
211
- import ITThemeProvider, { useITTheme, useITThemeSafe, useITFlatAppearance } from "./components/theme-provider/themeProvider";
235
+ import ITThemeProvider, { useITTheme, useITThemeSafe, useITFlatAppearance, useITAppearance } from "./components/theme-provider/themeProvider";
212
236
  import type { ITThemeProviderProps, ITThemePalette, ITThemeAppearance } from "./components/theme-provider/themeProvider.props";
213
237
  import ITTimePicker from "./components/molecules/time-picker/timePicker";
214
238
  import type { ITTimePickerProps } from "./components/molecules/time-picker/timePicker.props";
@@ -238,7 +262,7 @@ export {
238
262
  ITSearchTable,
239
263
  ITSegmentedControl, ITSidebar, ITSkeleton, ITSlideToggle, ITSlider, ITStack, ITStatCard, ITStepper, ITLineBarChart,
240
264
  ITTable, ITTabs, ITText, ITTextarea, ITTripleFilter, ITThemeProvider,
241
- useITTheme, useITThemeSafe, useITFlatAppearance, ITTimePicker, ITToast, ITWysiwyg,
265
+ useITTheme, useITThemeSafe, useITFlatAppearance, useITAppearance, ITTimePicker, ITToast, ITWysiwyg,
242
266
  UploadStatus, FileTypeEnum,
243
267
  getContrastTextColor, isLightColor, resolveCssColor,
244
268
  ITI18nProvider, useITTranslation, esMessages, enMessages,
@@ -247,11 +271,13 @@ export {
247
271
  ITSchemaRenderer, NodeBoundary, renderNode, auditTree, countPendingNodes,
248
272
  axzyCatalog, axzyCatalogTypes, AXZY_CATALOG_ENTRIES, AXZY_EXCLUDED_TYPES,
249
273
  createCatalog, getCatalogEntry, isExcludedType, getExclusionReason, GLOBALLY_FORBIDDEN_PROPS,
250
- resolveNode, parsePartialSchema, isNodePending,
274
+ resolveNode, parsePartialSchema, isNodePending, useSchemaStream,
251
275
  JsonEngineError, issue, formatPath, parsePath, readPath, hasPath, pushPath, propPath,
252
276
  DEFAULT_LIMITS, resolveLimits, createLimitTracker,
253
277
  validateSchema, CURRENT_SCHEMA_VERSION, migrateSchema, DEFAULT_MIGRATIONS,
254
278
  buildVocabularyContract, formatVocabularyForPrompt,
279
+ createBindingResolver,
280
+ validateRoutes, flattenRoutes, joinRoutePath, matchRoute, routeHref, buildNavigationTree, navContainsRoute,
255
281
  };
256
282
 
257
283
  export type {
@@ -262,7 +288,7 @@ export {
262
288
  ITAlertProps, ITAvatarProps, ITBadgetProps,
263
289
  ITBreadcrumbItem, ITBreadcrumbsProps, ITButtonProps,
264
290
  ITCalendarProps,
265
- ITCardProps, ITCheckboxProps, ITChipProps, ITChipInputProps, ITConfirmDialogProps,
291
+ ITCardProps, ITCardTone, ITCheckboxProps, ITChipProps, ITChipInputProps, ITConfirmDialogProps,
266
292
  ITDropdownMenuItem, ITDropdownMenuProps, ITFieldProps, ITMaskedInputProps, ITMultiSelectOption, ITMultiSelectProps,
267
293
  ITDataTableFetchParams, ITDataTableProps, ITDataTableResponse, ITDatePickerProps,
268
294
  ITDialogProps, ITDividerProps, ITDrawerProps,
@@ -286,9 +312,11 @@ export {
286
312
  ITWysiwygProps,
287
313
  ITJsonRendererProps, JsonComponentNode, JsonComponentType,
288
314
  ITJSONEditorProps,
289
- ITJsonRenderEngineProps, ScreenSchema, NodeSchema, SchemaValue, SchemaReference, WhenSchema,
315
+ ITJsonRenderEngineProps, DataHandler, DataLoader, BindingScope, BindingResolver, SlotComponent, SlotRegistry, EffectRegistry, ToastVariant,
316
+ RoutesSchema, RouteSchema, RouteNavSchema, FlatRoute, RouteMatch, NavNode,
317
+ ScreenSchema, NodeSchema, SchemaValue, SchemaReference, WhenSchema,
290
318
  ActionSchema, EffectSchema, DataSourceSchema, NodeStatus, Catalog, CatalogEntry, PropSpec, PropKind,
291
- ResolvedNode, UnresolvedNode, PartialParseResult, StreamState, JsonEngineIssue, JsonEngineCode,
319
+ ResolvedNode, UnresolvedNode, PartialParseResult, StreamState, StreamSource, JsonEngineIssue, JsonEngineCode,
292
320
  JsonEngineSeverity, SchemaPath, PathSegment, EngineLimits, LimitTracker,
293
321
  ValidationReport, ValidationVerdict, ValidateOptions, SchemaMigration, MigrationRegistry, MigrationResult,
294
322
  VocabularyContract, ComponentContract, PropContract,