@axzydev/axzy_ui_system 1.5.3 → 1.7.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 (35) hide show
  1. package/dist/index.cjs +6071 -4316
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +1 -1
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.d.cts +145 -1
  6. package/dist/index.d.ts +145 -1
  7. package/dist/index.js +4493 -2723
  8. package/dist/index.js.map +1 -1
  9. package/dist/layered.css +1 -1
  10. package/package.json +1 -1
  11. package/src/App.tsx +10 -0
  12. package/src/components/molecules/json-editor/json-editor-tree.tsx +235 -0
  13. package/src/components/molecules/json-editor/json-editor.index.ts +2 -0
  14. package/src/components/molecules/json-editor/json-editor.props.ts +82 -0
  15. package/src/components/molecules/json-editor/json-editor.tsx +972 -0
  16. package/src/components/molecules/json-editor/json-editor.utils.ts +717 -0
  17. package/src/components/molecules/json-editor/useJsonEditorKeyboard.ts +256 -0
  18. package/src/components/molecules/json-renderer/json-renderer.index.ts +2 -0
  19. package/src/components/molecules/json-renderer/json-renderer.props.ts +31 -0
  20. package/src/components/molecules/json-renderer/json-renderer.tsx +79 -0
  21. package/src/components/organisms/sidebar/sidebar.tsx +51 -14
  22. package/src/dev.css +24 -1
  23. package/src/hooks/useJsonRender.tsx +9 -0
  24. package/src/i18n/locales/en.ts +54 -0
  25. package/src/i18n/locales/es.ts +54 -0
  26. package/src/index.css +414 -0
  27. package/src/index.ts +10 -2
  28. package/src/sandbox/AxzyLogo.tsx +139 -0
  29. package/src/sandbox/i18n.ts +2 -0
  30. package/src/sandbox/navigation.ts +2 -0
  31. package/src/showcases/FeedbackShowcases.tsx +16 -5
  32. package/src/showcases/HomeShowcase.tsx +16 -9
  33. package/src/showcases/JsonEditorShowcase.tsx +150 -0
  34. package/src/showcases/JsonRendererShowcase.tsx +70 -0
  35. package/src/showcases/StructureShowcases.tsx +9 -3
@@ -15,6 +15,7 @@ import {
15
15
  import { PRESETS } from "../components/theme-provider/themePresets";
16
16
  import { ShowcaseLayout } from "./ShowcaseLayout";
17
17
  import { useITTranslation } from "@/i18n";
18
+ import { AxzyLoader, AxzyLogo } from "@/sandbox/AxzyLogo";
18
19
 
19
20
  // 1. ITDialog Showcase
20
21
  export const DialogShowcase = () => {
@@ -237,11 +238,21 @@ export const LoaderShowcase = () => {
237
238
  </>
238
239
  }
239
240
  gallery={
240
- <div className="flex flex-wrap items-center gap-6">
241
- <ITLoader variant="spinner" size="md" color="primary" />
242
- <ITLoader variant="dots" size="md" color="success" />
243
- <div className="w-32"><ITLoader variant="bar" size="md" color="danger" /></div>
244
- <ITLoader variant="pulse" size="md" color="purple" />
241
+ <div className="space-y-6">
242
+ <div className="flex flex-wrap items-center gap-6">
243
+ <ITLoader variant="spinner" size="md" color="primary" />
244
+ <ITLoader variant="dots" size="md" color="success" />
245
+ <div className="w-32"><ITLoader variant="bar" size="md" color="danger" /></div>
246
+ <ITLoader variant="pulse" size="md" color="purple" />
247
+ </div>
248
+
249
+ {/* Loader de marca: el ícono axzydev con un latido suave. En el kit la
250
+ versión animada es Lottie; aquí se usa el SVG estático. */}
251
+ <div className="flex flex-wrap items-center gap-8 border-t border-slate-200 pt-6 dark:border-slate-800">
252
+ <AxzyLoader etiqueta={t("sandbox.loading")} />
253
+ <AxzyLoader etiqueta={t("sandbox.loading")} tamano={24} />
254
+ <AxzyLogo variante="loader" tamano={56} etiqueta="axzydev" />
255
+ </div>
245
256
  </div>
246
257
  }
247
258
  doc={{
@@ -27,6 +27,7 @@ import {
27
27
  VolverArriba,
28
28
  } from "./portfolioUi";
29
29
  import { useITTranslation } from "@/i18n";
30
+ import { AxzyLogo } from "@/sandbox/AxzyLogo";
30
31
 
31
32
  const CV_URL = "/Martin_Amaro_CV_ES_2026.pdf";
32
33
  const GITHUB_URL = "https://github.com/maag070208";
@@ -125,9 +126,7 @@ const PortfolioNav = () => {
125
126
  <nav aria-label={t("showcase.home.sectionNav")} className={HOJA}>
126
127
  <div className="flex h-14 items-center gap-4">
127
128
  <a href="#sobre-mi" className="flex items-center gap-2.5">
128
- <span className="grid h-6 w-6 place-items-center rounded-[6px] bg-primary-700 font-mono !text-[10px] !font-bold text-white">
129
- AA
130
- </span>
129
+ <AxzyLogo variante="icono" tamano={24} />
131
130
  <ITText
132
131
  as="span"
133
132
  className="hidden font-display !text-[13px] !font-bold -tracking-[0.01em] text-slate-900 dark:text-white sm:block"
@@ -441,6 +440,10 @@ export const HomeShowcase = () => {
441
440
  </a>
442
441
  </ITFlex>
443
442
 
443
+ <ITFlex align="center" className="it-reveal !mt-8">
444
+ <AxzyLogo variante="wordmark" tamano={300} />
445
+ </ITFlex>
446
+
444
447
  <div className="mt-10 grid gap-10 lg:grid-cols-[minmax(0,1fr)_auto] lg:gap-14">
445
448
  <div className="min-w-0">
446
449
  <ITText
@@ -783,12 +786,16 @@ export const HomeShowcase = () => {
783
786
  <div
784
787
  className={`${HOJA} flex flex-col items-start justify-between gap-3 py-7 sm:flex-row sm:items-center`}
785
788
  >
786
- <ITText
787
- as="p"
788
- className="font-mono !text-[11px] uppercase !tracking-[0.14em] text-slate-500 dark:text-slate-400"
789
- >
790
- {`Asael Amaro · ${t("showcase.home.role")}`}
791
- </ITText>
789
+ <div className="flex items-center gap-4">
790
+ <AxzyLogo variante="wordmark" tamano={190} />
791
+ <span aria-hidden="true" className="h-8 w-px bg-slate-200 dark:bg-slate-800" />
792
+ <ITText
793
+ as="p"
794
+ className="font-mono !text-[11px] uppercase !tracking-[0.14em] text-slate-500 dark:text-slate-400"
795
+ >
796
+ {`Asael Amaro · ${t("showcase.home.role")}`}
797
+ </ITText>
798
+ </div>
792
799
  <div className="flex flex-wrap items-center gap-x-5 gap-y-2">
793
800
  <ITText
794
801
  as="span"
@@ -0,0 +1,150 @@
1
+ import React, { useMemo, useState } from "react";
2
+ import { ITCard, ITJSONEditor, ITFlex } from "../index";
3
+ import { ShowcaseLayout } from "./ShowcaseLayout";
4
+
5
+ /** Documento de arranque de la demo. */
6
+ const initial = `[
7
+ {
8
+ "type": "card",
9
+ "props": { "title": "Demo JSON Editor" },
10
+ "children": [
11
+ { "type": "text", "text": "Edita este JSON y mira el preview" },
12
+ { "type": "button", "props": { "label": "Aceptar" } }
13
+ ]
14
+ }
15
+ ]`;
16
+
17
+ export const JsonEditorShowcase: React.FC = () => {
18
+ const [value, setValue] = useState(initial);
19
+
20
+ /** Un documento más grande para que luzcan el plegado y el árbol. */
21
+ const bigDocument = useMemo(
22
+ () =>
23
+ JSON.stringify(
24
+ {
25
+ app: "axzy-ui-system",
26
+ version: "1.6.0",
27
+ layers: {
28
+ atoms: ["button", "input", "text", "flex", "grid", "stack"],
29
+ molecules: ["card", "alert", "table", "json-editor"],
30
+ organisms: ["data-table", "dialog", "sidebar", "navbar"],
31
+ },
32
+ flags: { darkMode: true, i18n: true, a11y: true, treeView: true },
33
+ limits: { maxDepth: 12, maxNodes: 5000, timeout: null },
34
+ },
35
+ null,
36
+ 2
37
+ ),
38
+ []
39
+ );
40
+
41
+ const code = `<ITJSONEditor
42
+ value={json}
43
+ onChange={setJson}
44
+ onParse={handleParse}
45
+ mode="text" // "text" | "tree"
46
+ indentSize={2} // 0 | 2 | 4 | 8
47
+ height={420}
48
+ showToolbar
49
+ showViewSettings
50
+ showValidation
51
+ showStatusBar
52
+ showFoldGutter
53
+ showLineNumbers
54
+ />`;
55
+
56
+ return (
57
+ <ShowcaseLayout
58
+ title="ITJSONEditor"
59
+ description="Editor JSON con resaltado, gutter plegable, errores por línea/columna y árbol navegable. Sin dependencias nuevas."
60
+ demo={
61
+ <ITFlex direction="column" gap={4} className="w-full">
62
+ <ITJSONEditor id="json-editor-demo" value={value} onChange={setValue} height={420} indentSize={2} />
63
+ </ITFlex>
64
+ }
65
+ gallery={
66
+ <ITFlex direction="column" gap={4} className="w-full">
67
+ <ITCard title="Vista de árbol (mode=&quot;tree&quot;)">
68
+ <ITJSONEditor
69
+ value={bigDocument}
70
+ readOnly
71
+ mode="tree"
72
+ treeShowLines
73
+ height={320}
74
+ showToolbar={false}
75
+ showViewSettings={false}
76
+ />
77
+ </ITCard>
78
+ <ITCard title="Documento grande con plegado">
79
+ <ITJSONEditor value={bigDocument} onChange={() => undefined} height={320} />
80
+ </ITCard>
81
+ <ITCard title="Lienzo claro y ajuste de línea (canvasTheme=&quot;light&quot;, wrapLines)">
82
+ <ITJSONEditor
83
+ value={bigDocument}
84
+ readOnly
85
+ canvasTheme="light"
86
+ wrapLines
87
+ height={260}
88
+ showTransformActions={false}
89
+ showImportButton={false}
90
+ />
91
+ </ITCard>
92
+ <ITCard title="Estado de error: línea y columna señaladas">
93
+ <ITJSONEditor
94
+ value={'{\n "a": 1,\n "b": ,\n "c": 3\n}'}
95
+ height={220}
96
+ showStatusBar
97
+ />
98
+ </ITCard>
99
+ </ITFlex>
100
+ }
101
+ code={code}
102
+ doc={{
103
+ summary: "Editor JSON para prototipar con ITJsonRenderer",
104
+ description:
105
+ "El resaltado se pinta en una capa bajo un textarea nativo, así que conserva el deshacer del navegador y no añade dependencias. Valida con JSON.parse y traduce el error a línea y columna.",
106
+ examples: [
107
+ '<ITJSONEditor value={json} onChange={setJson} />',
108
+ '<ITJSONEditor value={json} mode="tree" treeShowLines />',
109
+ '<ITJSONEditor value={json} indentSize={4} wrapLines canvasTheme="light" />',
110
+ '<ITJSONEditor value={json} readOnly showToolbar={false} height="100%" />',
111
+ ],
112
+ props: [
113
+ { name: "value", type: "string", default: '""', description: "Documento JSON como cadena." },
114
+ { name: "onChange", type: "(v: string) => void", default: "—", description: "Se dispara con el texto completo en cada cambio." },
115
+ { name: "onParse", type: "(parsed, isValid, error?) => void", default: "—", description: "Valor parseado y estado tras cada validación." },
116
+ { name: "mode", type: '"text" | "tree"', default: '"text"', description: "Modo inicial del lienzo." },
117
+ { name: "showModeSwitch", type: "boolean", default: "true", description: "Conmutador texto/árbol en la barra." },
118
+ { name: "showToolbar", type: "boolean", default: "true", description: "Barra superior de acciones." },
119
+ { name: "showViewSettings", type: "boolean", default: "true", description: "Fila de ajustes de vista." },
120
+ { name: "showValidation", type: "boolean", default: "true", description: "Indicador válido/ inválido con L:C." },
121
+ { name: "showStatusBar", type: "boolean", default: "true", description: "Pie con líneas, nodos, profundidad y tamaño." },
122
+ { name: "showLineNumbers", type: "boolean", default: "true", description: "Números de línea en el gutter." },
123
+ { name: "showFoldGutter", type: "boolean", default: "true", description: "Gutter de plegado de bloques." },
124
+ { name: "showTransformActions", type: "boolean", default: "true", description: "Formatear, minificar, ordenar, escapar." },
125
+ { name: "showCopyButton", type: "boolean", default: "true", description: "Botón de copiar el documento." },
126
+ { name: "showImportButton", type: "boolean", default: "true", description: "Carga de un archivo .json." },
127
+ { name: "showDownloadButton", type: "boolean", default: "true", description: "Descarga del documento." },
128
+ { name: "indentSize", type: "number", default: "2", description: "Espacios por nivel (0, 2, 4 u 8)." },
129
+ { name: "wrapLines", type: "boolean", default: "false", description: "Ajuste de línea dentro del ancho." },
130
+ { name: "treeShowLines", type: "boolean", default: "false", description: "Muestra la línea de cada nodo y permite saltar." },
131
+ { name: "canvasTheme", type: '"ink" | "light"', default: '"ink"', description: "Paleta del lienzo de código." },
132
+ { name: "height", type: "number | string", default: "340", description: "Alto del área de edición." },
133
+ { name: "readOnly", type: "boolean", default: "false", description: "Solo lectura, sin acciones de edición." },
134
+ { name: "actions", type: "ReactNode", default: "—", description: "Acciones propias al final de la barra." },
135
+ { name: "className", type: "string", default: "—", description: "Clases del contenedor raíz." },
136
+ { name: "textareaClassName", type: "string", default: "—", description: "Clases extra del textarea." },
137
+ { name: "errorLine", type: "number", default: "—", description: "Línea a marcar como errónea desde fuera." },
138
+ ],
139
+ notes: [
140
+ "Atajos: Tab indenta el bloque, Mayús+Tab lo desangra, Mayús+Alt+F formatea, +M minifica y +S ordena.",
141
+ "Las llaves, corchetes y comillas se cierran solos y se borran por parejas.",
142
+ "Con wrapLines activo el resaltado de línea vigente se desactiva: la posición dependería de las vueltas de cada línea.",
143
+ "Es totalmente independiente de ITJsonRenderer: los une solo el JSON que tú les pases.",
144
+ ],
145
+ }}
146
+ />
147
+ );
148
+ };
149
+
150
+ export default JsonEditorShowcase;
@@ -0,0 +1,70 @@
1
+ import React, { useState } from "react";
2
+ import { ITCard, ITJsonRenderer } from "../index";
3
+ import { ShowcaseLayout } from "./ShowcaseLayout";
4
+
5
+ const exampleWithChildren = [
6
+ {
7
+ type: "alert" as const,
8
+ props: { variant: "info" as const, title: "Información" },
9
+ text: "Esto se renderiza desde JSON",
10
+ },
11
+ {
12
+ type: "card" as const,
13
+ props: { title: "Tarjeta desde JSON" },
14
+ children: [
15
+ { type: "text" as const, text: "Contenido dentro de la tarjeta" },
16
+ {
17
+ type: "flex" as const,
18
+ props: { gap: 2, justify: "end" as const },
19
+ children: [
20
+ { type: "button" as const, props: { label: "Cancelar", color: "gray" as const, variant: "outlined" as const } },
21
+ { type: "button" as const, props: { label: "Aceptar", color: "primary" as const } },
22
+ ],
23
+ },
24
+ ],
25
+ },
26
+ ];
27
+
28
+ export const JsonRendererShowcase: React.FC = () => {
29
+ const [jsonText, setJsonText] = useState(JSON.stringify(exampleWithChildren, null, 2));
30
+
31
+ let parsed: any = exampleWithChildren;
32
+ try {
33
+ parsed = JSON.parse(jsonText);
34
+ } catch {
35
+ parsed = exampleWithChildren;
36
+ }
37
+
38
+ return (
39
+ <ShowcaseLayout
40
+ title="ITJsonRenderer"
41
+ description="Renderiza componentes del UI System a partir de un JSON."
42
+ doc={{
43
+ summary: "PoC: Builder JSON para AXZY UI System",
44
+ description:
45
+ "Permite construir interfaces pasándole un árbol JSON con type, props, children/text.",
46
+ examples: [
47
+ '<ITJsonRenderer json={{ type: "button", props: { label: "Click" } }} />',
48
+ ],
49
+ props: [],
50
+ }}
51
+ demo={
52
+ <ITCard title="Preview">
53
+ <ITJsonRenderer json={parsed} />
54
+ </ITCard>
55
+ }
56
+ code={jsonText}
57
+ controls={
58
+ <ITCard title="JSON Editor (editable)">
59
+ <textarea
60
+ value={jsonText}
61
+ onChange={(e) => setJsonText(e.target.value)}
62
+ className="w-full h-64 font-mono text-sm border rounded p-3"
63
+ />
64
+ </ITCard>
65
+ }
66
+ />
67
+ );
68
+ };
69
+
70
+ export default JsonRendererShowcase;
@@ -37,6 +37,7 @@ import type { ITSidebarProps } from "../index";
37
37
  import { ShowcaseLayout, CodeViewer } from "./ShowcaseLayout";
38
38
  import { EjemploContenedor } from "@/sandbox/ui";
39
39
  import { useITTranslation } from "@/i18n";
40
+ import { AxzyLogo } from "@/sandbox/AxzyLogo";
40
41
 
41
42
  // ─────────────────────────────────────────
42
43
  // 1. ITCard Showcase
@@ -972,7 +973,7 @@ export const LayoutShowcase = () => {
972
973
  const [navActiveId, setNavActiveId] = useState<string | null>("1");
973
974
 
974
975
  const topBar = {
975
- logo: <div className="w-9 h-9 rounded-lg bg-primary-500 flex items-center justify-center text-white font-bold">A</div>,
976
+ logo: <AxzyLogo variante="icono" tamano={36} etiqueta="" />,
976
977
  logoText: "AXZY Console",
977
978
  centerContent: (
978
979
  <input
@@ -1155,6 +1156,7 @@ export const LayoutShowcase = () => {
1155
1156
  numero="02"
1156
1157
  titulo={t("showcase.structure.galleryNavbarStandalone")}
1157
1158
  codigo={`<ITNavbar
1159
+ logo={<AxzyLogo variante="icono" tamano={28} etiqueta="" />}
1158
1160
  logoText="AXZY"
1159
1161
  navigationItems={[
1160
1162
  { id: "1", label: "Inicio", icon: <FaHome />, isActive: true },
@@ -1169,6 +1171,7 @@ export const LayoutShowcase = () => {
1169
1171
  >
1170
1172
  <div className="h-[380px] overflow-hidden [&>div]:!h-full">
1171
1173
  <ITNavbar
1174
+ logo={<AxzyLogo variante="icono" tamano={28} etiqueta="" />}
1172
1175
  logoText="AXZY"
1173
1176
  navigationItems={navbarItems}
1174
1177
  userMenu={{
@@ -1197,6 +1200,7 @@ export const LayoutShowcase = () => {
1197
1200
  numero="03"
1198
1201
  titulo={t("showcase.structure.galleryNavbarSubmenus")}
1199
1202
  codigo={`<ITNavbar
1203
+ logo={<AxzyLogo variante="icono" tamano={28} etiqueta="" />}
1200
1204
  logoText="Admin"
1201
1205
  navigationItems={[
1202
1206
  { id: "u", label: "Usuarios", icon: <FaUsers /> },
@@ -1215,6 +1219,7 @@ export const LayoutShowcase = () => {
1215
1219
  >
1216
1220
  <div className="h-[380px] overflow-hidden [&>div]:!h-full">
1217
1221
  <ITNavbar
1222
+ logo={<AxzyLogo variante="icono" tamano={28} etiqueta="" />}
1218
1223
  logoText="Admin"
1219
1224
  navigationItems={[
1220
1225
  { id: "u", label: t("showcase.structure.navUsers"), icon: <FaUsers /> },
@@ -1304,7 +1309,7 @@ export const LayoutShowcase = () => {
1304
1309
  { name: "children", type: "React.ReactNode", required: true, description: "Contenido principal." },
1305
1310
  { name: "className", type: "string", description: "Clases del wrapper externo." },
1306
1311
  { name: "contentClassName", type: "string", description: "Clases del contenedor de contenido." },
1307
- { name: "sidebar.header", type: "React.ReactNode", description: "Slot opcional sobre la navegación (ej. wordmark o filtro). Oculto al colapsar." },
1312
+ { name: "sidebar.header", type: "React.ReactNode", description: "Slot opcional sobre la navegación (ej. wordmark o filtro). Oculto al colapsar, pero reserva su altura." },
1308
1313
  { name: "sidebar.notification", type: "ITSidebarNotification", description: "Fila de notificación (icono + label + ITBadget) sobre la navegación: { count, icon?, label?, onClick?, badgeProps? }. Visible también al colapsar." },
1309
1314
  { name: "sidebar.navigationItems[].badge", type: "number | string", description: "Contador que se renderiza con ITBadget en el ítem." },
1310
1315
  { name: "sidebar.navigationItems[].badgeProps", type: "ITBadgetProps", description: "Estilo del badge del ítem (color, size, variant, className para padding)." },
@@ -1312,9 +1317,10 @@ export const LayoutShowcase = () => {
1312
1317
  notes: [
1313
1318
  "`subitems` acepta entradas planas o grupos con `label`+`items`; el título del grupo no es foco ni un tercer nivel y cada grupo se nombra por `aria-labelledby`. En el riel colapsado el flyout aplana los grupos (sin títulos).",
1314
1319
  "El sidebar hereda aria-current/aria-expanded y acepta un slot header opcional.",
1315
- "Los badges por ítem (`badge` + `badgeProps`) y la fila `notification` se dibujan con el átomo `ITBadget`; el padding se ajusta con `badgeProps.className`.",
1320
+ "Los badges por ítem (`badge` + `badgeProps`) y la fila `notification` se dibujan con el átomo `ITBadget`; el padding se ajusta con `badgeProps.className`. `badge` es un contador: en el riel colapsado solo caben ≤3 caracteres y un texto largo se degrada a un punto.",
1316
1321
  "Con `sidebar.isCollapsed` omitido el contenido reserva 88px (hover expande sobre el contenido). Si lo controlas a `false`, reserva 280px y no tapa el contenido.",
1317
1322
  "El riel colapsado se expande automáticamente al pasar el cursor por encima; no hay botón de colapso.",
1323
+ "Riel y panel comparten columna de iconos y alto de fila: los iconos no se mueven al expandir/colapsar. El slot `header` reserva su altura (invisible) en el riel por el mismo motivo.",
1318
1324
  ],
1319
1325
  }}
1320
1326
  />