@axzydev/axzy_ui_system 1.7.2 → 1.7.4
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/dist/index.cjs +1630 -589
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +373 -13
- package/dist/index.d.ts +373 -13
- package/dist/index.js +1346 -314
- package/dist/index.js.map +1 -1
- package/dist/layered.css +1 -1
- package/package.json +4 -2
- package/src/App.tsx +6 -8
- package/src/components/atoms/flex/flex.props.ts +3 -1
- package/src/components/atoms/flex/flex.tsx +11 -3
- package/src/components/atoms/textarea/textarea.props.ts +4 -0
- package/src/components/atoms/textarea/textarea.tsx +13 -3
- package/src/components/molecules/card/card.props.ts +2 -0
- package/src/components/molecules/card/card.tsx +15 -2
- package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +4 -0
- package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +57 -41
- package/src/components/molecules/stat-card/stat-card.props.ts +7 -0
- package/src/components/molecules/stat-card/stat-card.tsx +18 -0
- package/src/components/organisms/dropfile/dropfile.props.ts +8 -2
- package/src/components/organisms/dropfile/dropfile.tsx +17 -0
- package/src/i18n/locales/en.ts +7 -0
- package/src/i18n/locales/es.ts +7 -0
- package/src/index.ts +31 -3
- package/src/json-renderer/actions/effects.test.tsx +115 -0
- package/src/json-renderer/actions/effects.ts +291 -0
- package/src/json-renderer/actions/effectsChain.test.ts +117 -0
- package/src/json-renderer/binding/binding.test.tsx +168 -0
- package/src/json-renderer/binding/resolveValue.ts +193 -0
- package/src/json-renderer/estado-acciones.test.tsx +166 -0
- package/src/json-renderer/form/FormNode.tsx +92 -0
- package/src/json-renderer/form/form.test.tsx +148 -0
- package/src/json-renderer/form/formLength.test.tsx +99 -0
- package/src/json-renderer/form/rules.ts +162 -0
- package/src/json-renderer/index.ts +21 -0
- package/src/json-renderer/kernel/SchemaRenderer.tsx +83 -7
- package/src/json-renderer/kernel/capabilities.ts +30 -0
- package/src/json-renderer/kernel/renderNode.tsx +256 -3
- package/src/json-renderer/kernel/schemaRenderer.props.ts +29 -3
- package/src/json-renderer/kernel/slots.test.tsx +104 -0
- package/src/json-renderer/kernel/slots.ts +34 -0
- package/src/json-renderer/kernel/types.ts +4 -0
- package/src/json-renderer/routing/match.ts +82 -0
- package/src/json-renderer/routing/navigation.ts +71 -0
- package/src/json-renderer/routing/routes.ts +179 -0
- package/src/json-renderer/routing/routing.test.ts +147 -0
- package/src/json-renderer/seguridad.test.tsx +299 -0
- package/src/json-renderer/shared/errors.ts +1 -0
- package/src/json-renderer/stream/tolerantParse.ts +5 -0
- package/src/json-renderer/stream/useSchemaStream.test.tsx +143 -0
- package/src/json-renderer/stream/useSchemaStream.ts +227 -0
- package/src/json-renderer/validation/jsonSchema.test.ts +105 -0
- package/src/json-renderer/validation/jsonSchema.ts +317 -0
- package/src/json-renderer/validation/validate.ts +17 -0
- package/src/sandbox/i18n.ts +38 -0
- package/src/sandbox/useHashRoute.ts +2 -1
- package/src/showcases/DemoJsonDashboard.tsx +218 -103
- package/src/showcases/DemoJsonDocs.tsx +11 -11
- package/src/showcases/JsonScreenShowcase.tsx +43 -53
- package/src/showcases/demoApp/demoJson.test.tsx +241 -0
- package/src/showcases/demoApp/routes.test.ts +63 -0
- package/src/showcases/demoApp/schemas.test.ts +6 -17
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"packageManager": "pnpm@10.34.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"version": "1.7.
|
|
7
|
+
"version": "1.7.4",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"sideEffects": [
|
|
10
10
|
"*.css"
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"audit:sandbox": "node scripts/audit-sandbox.mjs",
|
|
48
48
|
"docs:llm": "pnpm ext:catalog && node scripts/build-llm-docs.mjs && node scripts/build-ai-bundle.mjs",
|
|
49
49
|
"docs:ai": "node scripts/build-ai-bundle.mjs",
|
|
50
|
-
"docs:json-renderer": "node scripts/build-json-renderer-docs.mjs"
|
|
50
|
+
"docs:json-renderer": "node scripts/build-json-renderer-docs.mjs",
|
|
51
|
+
"schema:check": "node scripts/json-renderer/check.mjs"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
54
|
"clsx": "^2.1.1",
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
"@types/react-dom": "19.0.3",
|
|
73
74
|
"@types/vscode": "1.85.0",
|
|
74
75
|
"@vitejs/plugin-react-swc": "3.8.0",
|
|
76
|
+
"ajv": "8.18.0",
|
|
75
77
|
"esbuild": "^0.25.0",
|
|
76
78
|
"eslint": "9.19.0",
|
|
77
79
|
"eslint-plugin-react-hooks": "5.0.0",
|
package/src/App.tsx
CHANGED
|
@@ -70,7 +70,7 @@ import JsonRendererShowcase from "./showcases/JsonRendererShowcase";
|
|
|
70
70
|
import JsonEditorShowcase from "./showcases/JsonEditorShowcase";
|
|
71
71
|
import JsonScreenShowcase from "./showcases/JsonScreenShowcase";
|
|
72
72
|
import DemoJsonPortfolio from "./showcases/DemoJsonPortfolio";
|
|
73
|
-
import DemoJsonDashboard
|
|
73
|
+
import DemoJsonDashboard from "./showcases/DemoJsonDashboard";
|
|
74
74
|
|
|
75
75
|
// Sandbox routing / landing (dev-only, never published)
|
|
76
76
|
import {
|
|
@@ -377,18 +377,16 @@ function AppShell() {
|
|
|
377
377
|
) : route.view === "demo" ? (
|
|
378
378
|
route.group === "app" ? (
|
|
379
379
|
<DemoJsonDashboard
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
window.location.hash = `#demo/app/${pagina}`;
|
|
380
|
+
path={`/${route.item || "inicio"}`}
|
|
381
|
+
query={route.query}
|
|
382
|
+
onNavigate={(href) => {
|
|
383
|
+
window.location.hash = `#demo/app${href}`;
|
|
385
384
|
}}
|
|
386
385
|
onExit={() => {
|
|
387
386
|
window.location.hash = "#demo";
|
|
388
387
|
}}
|
|
389
|
-
filterEstado={route.query.estado ?? "Todos"}
|
|
390
388
|
onFilterChange={(estado) => {
|
|
391
|
-
const base = `#demo/app/${route.item
|
|
389
|
+
const base = `#demo/app/${route.item || "inicio"}`;
|
|
392
390
|
window.location.hash = estado === "Todos" ? base : `${base}?estado=${encodeURIComponent(estado)}`;
|
|
393
391
|
}}
|
|
394
392
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode, CSSProperties, ElementType } from "react";
|
|
1
|
+
import { ReactNode, CSSProperties, ElementType, UIEvent } from "react";
|
|
2
2
|
|
|
3
3
|
export type FlexDirection = "row" | "column" | "row-reverse" | "column-reverse";
|
|
4
4
|
export type FlexAlign = "start" | "end" | "center" | "stretch" | "baseline";
|
|
@@ -32,4 +32,6 @@ export interface ITFlexProps {
|
|
|
32
32
|
as?: ElementType;
|
|
33
33
|
/** Click handler */
|
|
34
34
|
onClick?: (e: React.MouseEvent) => void;
|
|
35
|
+
/** Scroll handler (useful for scrollable chat/log panels) */
|
|
36
|
+
onScroll?: (e: UIEvent<HTMLElement>) => void;
|
|
35
37
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
|
+
import { forwardRef } from "react";
|
|
2
3
|
import {
|
|
3
4
|
ITFlexProps,
|
|
4
5
|
FlexDirection,
|
|
@@ -52,7 +53,7 @@ const wrapMap: Record<FlexWrap, string> = {
|
|
|
52
53
|
* <main>Content</main>
|
|
53
54
|
* </ITFlex>
|
|
54
55
|
*/
|
|
55
|
-
|
|
56
|
+
const ITFlex = forwardRef<HTMLDivElement, ITFlexProps>(function ITFlex({
|
|
56
57
|
children,
|
|
57
58
|
direction = "row",
|
|
58
59
|
align,
|
|
@@ -66,7 +67,8 @@ export default function ITFlex({
|
|
|
66
67
|
style,
|
|
67
68
|
as: Component = "div",
|
|
68
69
|
onClick,
|
|
69
|
-
|
|
70
|
+
onScroll,
|
|
71
|
+
}, ref) {
|
|
70
72
|
const resolvedStyle: React.CSSProperties = {
|
|
71
73
|
gap: gap > 0 ? `${gap * 0.25}rem` : undefined,
|
|
72
74
|
flexGrow: typeof grow === "boolean" ? (grow ? 1 : 0) : grow,
|
|
@@ -77,6 +79,7 @@ export default function ITFlex({
|
|
|
77
79
|
|
|
78
80
|
return (
|
|
79
81
|
<Component
|
|
82
|
+
ref={ref}
|
|
80
83
|
className={clsx(
|
|
81
84
|
"flex",
|
|
82
85
|
directionMap[direction],
|
|
@@ -87,8 +90,13 @@ export default function ITFlex({
|
|
|
87
90
|
)}
|
|
88
91
|
style={resolvedStyle}
|
|
89
92
|
onClick={onClick}
|
|
93
|
+
onScroll={onScroll}
|
|
90
94
|
>
|
|
91
95
|
{children}
|
|
92
96
|
</Component>
|
|
93
97
|
);
|
|
94
|
-
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
ITFlex.displayName = "ITFlex";
|
|
101
|
+
|
|
102
|
+
export default ITFlex;
|
|
@@ -21,4 +21,8 @@ export interface ITTextareaProps {
|
|
|
21
21
|
maxLength?: number;
|
|
22
22
|
/** Resize behavior of the textarea: "none" | "vertical" | "horizontal" | "both". */
|
|
23
23
|
resize?: "none" | "vertical" | "horizontal" | "both";
|
|
24
|
+
/** Keydown handler (e.g. Enter to send in a chat composer). */
|
|
25
|
+
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
26
|
+
/** Autofocus the textarea. */
|
|
27
|
+
autoFocus?: boolean;
|
|
24
28
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
|
+
import { forwardRef } from "react";
|
|
2
3
|
import { ITTextareaProps } from "./textarea.props";
|
|
3
4
|
import { inputLabel, inputError } from "@/utils/styles";
|
|
4
5
|
import ITText from "@/components/atoms/text/text";
|
|
@@ -26,7 +27,7 @@ const resizeMap = {
|
|
|
26
27
|
* error={bio.length > 500 ? "Max 500 characters" : undefined}
|
|
27
28
|
* />
|
|
28
29
|
*/
|
|
29
|
-
|
|
30
|
+
const ITTextarea = forwardRef<HTMLTextAreaElement, ITTextareaProps>(function ITTextarea({
|
|
30
31
|
value,
|
|
31
32
|
onChange,
|
|
32
33
|
label,
|
|
@@ -38,7 +39,9 @@ export default function ITTextarea({
|
|
|
38
39
|
name,
|
|
39
40
|
maxLength,
|
|
40
41
|
resize = "vertical",
|
|
41
|
-
|
|
42
|
+
onKeyDown,
|
|
43
|
+
autoFocus = false,
|
|
44
|
+
}, ref) {
|
|
42
45
|
return (
|
|
43
46
|
<div className={clsx("flex flex-col gap-1.5", className)}>
|
|
44
47
|
{label && (
|
|
@@ -47,6 +50,7 @@ export default function ITTextarea({
|
|
|
47
50
|
</ITText>
|
|
48
51
|
)}
|
|
49
52
|
<textarea
|
|
53
|
+
ref={ref}
|
|
50
54
|
id={name}
|
|
51
55
|
name={name}
|
|
52
56
|
value={value}
|
|
@@ -55,6 +59,8 @@ export default function ITTextarea({
|
|
|
55
59
|
rows={rows}
|
|
56
60
|
disabled={disabled}
|
|
57
61
|
maxLength={maxLength}
|
|
62
|
+
onKeyDown={onKeyDown}
|
|
63
|
+
autoFocus={autoFocus}
|
|
58
64
|
className={clsx(
|
|
59
65
|
"w-full border border-solid transition-all duration-200 rounded-lg px-3 py-2 text-sm outline-none",
|
|
60
66
|
"focus:ring-2",
|
|
@@ -68,4 +74,8 @@ export default function ITTextarea({
|
|
|
68
74
|
{error && <ITText as="span" className={inputError}>{error}</ITText>}
|
|
69
75
|
</div>
|
|
70
76
|
);
|
|
71
|
-
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
ITTextarea.displayName = "ITTextarea";
|
|
80
|
+
|
|
81
|
+
export default ITTextarea;
|
|
@@ -3,6 +3,8 @@ export interface ITCardProps {
|
|
|
3
3
|
onClick?: () => void;
|
|
4
4
|
/** Card title displayed in the header area. */
|
|
5
5
|
title?: string;
|
|
6
|
+
/** Optional actions rendered at the right of the title row (e.g. a play button). */
|
|
7
|
+
headerActions?: React.ReactNode;
|
|
6
8
|
/** Image source URL displayed at the top of the card. */
|
|
7
9
|
image?: string;
|
|
8
10
|
/** Alt text for the card image. @default "Card Image" */
|
|
@@ -20,6 +20,7 @@ import { useITTranslation } from "@/i18n";
|
|
|
20
20
|
*/
|
|
21
21
|
export default function ITCard({
|
|
22
22
|
title,
|
|
23
|
+
headerActions,
|
|
23
24
|
image,
|
|
24
25
|
alt,
|
|
25
26
|
children,
|
|
@@ -97,14 +98,26 @@ export default function ITCard({
|
|
|
97
98
|
)}
|
|
98
99
|
|
|
99
100
|
<div className={clsx(contentClassName)} style={bodyStyle}>
|
|
100
|
-
{
|
|
101
|
+
{headerActions ? (
|
|
102
|
+
<div className="mb-2 flex items-center justify-between gap-3">
|
|
103
|
+
{title && (
|
|
104
|
+
<ITText
|
|
105
|
+
as="h3"
|
|
106
|
+
className={clsx(flat ? "text-base font-semibold" : "text-xl font-semibold", titleClassName)}
|
|
107
|
+
>
|
|
108
|
+
{title}
|
|
109
|
+
</ITText>
|
|
110
|
+
)}
|
|
111
|
+
{headerActions}
|
|
112
|
+
</div>
|
|
113
|
+
) : title ? (
|
|
101
114
|
<ITText
|
|
102
115
|
as="h3"
|
|
103
116
|
className={clsx(flat ? "text-base font-semibold mb-3" : "text-xl font-semibold mb-2", titleClassName)}
|
|
104
117
|
>
|
|
105
118
|
{title}
|
|
106
119
|
</ITText>
|
|
107
|
-
)}
|
|
120
|
+
) : null}
|
|
108
121
|
<ITText as="div" className="text-secondary-600">{children}</ITText>
|
|
109
122
|
</div>
|
|
110
123
|
{actions && (
|
|
@@ -19,6 +19,10 @@ export interface ITLineBarChartProps {
|
|
|
19
19
|
lineColor?: string;
|
|
20
20
|
/** Color de las barras. */
|
|
21
21
|
barColor?: string;
|
|
22
|
+
/** Muestra la serie de línea. @default true */
|
|
23
|
+
showLine?: boolean;
|
|
24
|
+
/** Muestra la serie de barras. @default true */
|
|
25
|
+
showBars?: boolean;
|
|
22
26
|
height?: number;
|
|
23
27
|
/** Formatea los valores del tooltip; por defecto número con separadores. */
|
|
24
28
|
formatValue?: (value: number) => string;
|
|
@@ -16,6 +16,8 @@ export function ITLineBarChart({
|
|
|
16
16
|
barLabel,
|
|
17
17
|
lineColor = "#10b981",
|
|
18
18
|
barColor = "#3b82f6",
|
|
19
|
+
showLine = true,
|
|
20
|
+
showBars = true,
|
|
19
21
|
height = 240,
|
|
20
22
|
formatValue,
|
|
21
23
|
formatAxis,
|
|
@@ -40,8 +42,12 @@ export function ITLineBarChart({
|
|
|
40
42
|
const plotHeight = height - padding.top - padding.bottom;
|
|
41
43
|
|
|
42
44
|
const max = useMemo(
|
|
43
|
-
() =>
|
|
44
|
-
|
|
45
|
+
() =>
|
|
46
|
+
Math.max(
|
|
47
|
+
...data.flatMap((d) => [showLine ? d.line : 0, showBars ? d.bar : 0]),
|
|
48
|
+
1
|
|
49
|
+
),
|
|
50
|
+
[data, showLine, showBars]
|
|
45
51
|
);
|
|
46
52
|
const gridValues = [0, 0.25, 0.5, 0.75, 1].map((f) => max * f);
|
|
47
53
|
const step = data.length > 0 ? plotWidth / data.length : plotWidth;
|
|
@@ -103,21 +109,22 @@ export function ITLineBarChart({
|
|
|
103
109
|
</g>
|
|
104
110
|
))}
|
|
105
111
|
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
{showBars &&
|
|
113
|
+
data.map((d, i) => (
|
|
114
|
+
<rect
|
|
115
|
+
key={`bar-${d.label}`}
|
|
116
|
+
x={x(i) - barWidth / 2}
|
|
117
|
+
y={y(d.bar)}
|
|
118
|
+
width={barWidth}
|
|
119
|
+
height={Math.max(padding.top + plotHeight - y(d.bar), 1)}
|
|
120
|
+
rx={3}
|
|
121
|
+
fill={barColor}
|
|
122
|
+
opacity={hover === null || hover === i ? 0.85 : 0.35}
|
|
123
|
+
/>
|
|
124
|
+
))}
|
|
118
125
|
|
|
119
|
-
{areaPath && <path d={areaPath} fill={`url(#area-${gradientId})`} />}
|
|
120
|
-
{data.length > 0 && (
|
|
126
|
+
{showLine && areaPath && <path d={areaPath} fill={`url(#area-${gradientId})`} />}
|
|
127
|
+
{showLine && data.length > 0 && (
|
|
121
128
|
<polyline
|
|
122
129
|
points={linePoints}
|
|
123
130
|
fill="none"
|
|
@@ -127,17 +134,18 @@ export function ITLineBarChart({
|
|
|
127
134
|
strokeLinejoin="round"
|
|
128
135
|
/>
|
|
129
136
|
)}
|
|
130
|
-
{
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
{showLine &&
|
|
138
|
+
data.map((d, i) => (
|
|
139
|
+
<circle
|
|
140
|
+
key={`dot-${d.label}`}
|
|
141
|
+
cx={x(i)}
|
|
142
|
+
cy={y(d.line)}
|
|
143
|
+
r={hover === i ? 4.5 : 3}
|
|
144
|
+
fill="#ffffff"
|
|
145
|
+
stroke={lineColor}
|
|
146
|
+
strokeWidth={2}
|
|
147
|
+
/>
|
|
148
|
+
))}
|
|
141
149
|
|
|
142
150
|
{data.map((d, i) => (
|
|
143
151
|
<g key={`hit-${d.label}`} onMouseEnter={() => setHover(i)}>
|
|
@@ -157,24 +165,32 @@ export function ITLineBarChart({
|
|
|
157
165
|
role="status"
|
|
158
166
|
>
|
|
159
167
|
<p className="text-[10px] font-semibold" style={{ color: "#e2e8f0" }}>{active.label}</p>
|
|
160
|
-
|
|
161
|
-
{
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
{showLine && (
|
|
169
|
+
<p className="text-[11px] tabular-nums" style={{ color: lineColor }}>
|
|
170
|
+
{resolvedLineLabel}: {resolvedFormatValue(active.line)}
|
|
171
|
+
</p>
|
|
172
|
+
)}
|
|
173
|
+
{showBars && (
|
|
174
|
+
<p className="text-[11px] tabular-nums" style={{ color: "#93c5fd" }}>
|
|
175
|
+
{resolvedBarLabel}: {resolvedFormatValue(active.bar)}
|
|
176
|
+
</p>
|
|
177
|
+
)}
|
|
166
178
|
</div>
|
|
167
179
|
)}
|
|
168
180
|
|
|
169
181
|
<ul className="flex flex-wrap items-center gap-4 text-[11px]" style={{ color: "#475569" }}>
|
|
170
|
-
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
{showLine && (
|
|
183
|
+
<li>
|
|
184
|
+
<span className="h-2.5 w-2.5 rounded-sm" style={{ background: lineColor }} aria-hidden />
|
|
185
|
+
{resolvedLineLabel}
|
|
186
|
+
</li>
|
|
187
|
+
)}
|
|
188
|
+
{showBars && (
|
|
189
|
+
<li>
|
|
190
|
+
<span className="h-2.5 w-2.5 rounded-sm" style={{ background: barColor }} aria-hidden />
|
|
191
|
+
{resolvedBarLabel}
|
|
192
|
+
</li>
|
|
193
|
+
)}
|
|
178
194
|
</ul>
|
|
179
195
|
</div>
|
|
180
196
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode, CSSProperties } from "react";
|
|
2
|
+
import { ColorsTypes } from "@/types/colors.types";
|
|
2
3
|
|
|
3
4
|
export interface ITStatCardProps {
|
|
4
5
|
/** The metric label displayed above the value. */
|
|
@@ -7,6 +8,12 @@ export interface ITStatCardProps {
|
|
|
7
8
|
value: string | number;
|
|
8
9
|
/** Optional trend indicator text (e.g. "+12%", "-3"). */
|
|
9
10
|
trend?: string;
|
|
11
|
+
/** Optional secondary line under the value (context, not a trend). */
|
|
12
|
+
hint?: string;
|
|
13
|
+
/** Optional progress 0–100 rendered under the hint. */
|
|
14
|
+
progress?: number;
|
|
15
|
+
/** Semantic color for the progress bar. @default "primary" */
|
|
16
|
+
progressColor?: ColorsTypes;
|
|
10
17
|
/** Direction of the trend, used to color the trend badge. */
|
|
11
18
|
trendDirection?: "up" | "down" | "neutral";
|
|
12
19
|
/** Icon element rendered next to the label in the top-right corner. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
2
|
import { ITStatCardProps } from "./stat-card.props";
|
|
3
3
|
import ITText from "@/components/atoms/text/text";
|
|
4
|
+
import ITProgress from "@/components/atoms/progress/progress";
|
|
4
5
|
|
|
5
6
|
const trendColors = {
|
|
6
7
|
up: "text-emerald-600 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-950/30",
|
|
@@ -23,6 +24,9 @@ export default function ITStatCard({
|
|
|
23
24
|
value,
|
|
24
25
|
trend,
|
|
25
26
|
trendDirection = trend && trend.startsWith("-") ? "down" : "neutral",
|
|
27
|
+
hint,
|
|
28
|
+
progress,
|
|
29
|
+
progressColor = "primary",
|
|
26
30
|
icon,
|
|
27
31
|
color = "bg-primary-50 dark:bg-primary-950/20",
|
|
28
32
|
className,
|
|
@@ -59,6 +63,20 @@ export default function ITStatCard({
|
|
|
59
63
|
</ITText>
|
|
60
64
|
)}
|
|
61
65
|
</div>
|
|
66
|
+
{hint && (
|
|
67
|
+
<ITText as="p" className="mt-1 text-[11px] leading-snug text-slate-500 dark:text-slate-400">
|
|
68
|
+
{hint}
|
|
69
|
+
</ITText>
|
|
70
|
+
)}
|
|
71
|
+
{progress !== undefined && (
|
|
72
|
+
<ITProgress
|
|
73
|
+
value={progress}
|
|
74
|
+
max={100}
|
|
75
|
+
color={progressColor}
|
|
76
|
+
size="sm"
|
|
77
|
+
className="mt-2"
|
|
78
|
+
/>
|
|
79
|
+
)}
|
|
62
80
|
</div>
|
|
63
81
|
);
|
|
64
82
|
}
|
|
@@ -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);
|
package/src/i18n/locales/en.ts
CHANGED
|
@@ -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;
|
package/src/i18n/locales/es.ts
CHANGED
|
@@ -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,
|
|
@@ -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 {
|
|
@@ -286,9 +312,11 @@ export {
|
|
|
286
312
|
ITWysiwygProps,
|
|
287
313
|
ITJsonRendererProps, JsonComponentNode, JsonComponentType,
|
|
288
314
|
ITJSONEditorProps,
|
|
289
|
-
ITJsonRenderEngineProps,
|
|
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,
|