@asteby/metacore-runtime-react 37.6.0 → 38.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/CHANGELOG.md +13 -0
- package/dist/action-modal-dispatcher.d.ts.map +1 -1
- package/dist/action-modal-dispatcher.js +2 -3
- package/dist/dialogs/dynamic-record.js +1 -1
- package/dist/dynamic-form.js +1 -1
- package/dist/form-layout-ui.d.ts +8 -4
- package/dist/form-layout-ui.d.ts.map +1 -1
- package/dist/form-layout-ui.js +9 -7
- package/package.json +3 -3
- package/src/action-modal-dispatcher.tsx +11 -20
- package/src/dialogs/dynamic-record.tsx +1 -1
- package/src/dynamic-form.tsx +1 -1
- package/src/form-layout-ui.tsx +13 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 38.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5f872f0: `ProcessStepper` llega a `@asteby/metacore-ui/wizard`: el indicador de pasos de los modales de proceso (círculos con icono o número, check al completar, etiqueta debajo y conectores flexibles), una sola implementación para toda la plataforma.
|
|
8
|
+
|
|
9
|
+
`runtime-react`: los wizards declarativos (`form_layout.mode = "steps"`) en `DynamicForm` y `DynamicRecordDialog` ahora usan ese stepper en lugar de la barra de progreso plana, con navegación hacia atrás al pulsar un paso completado.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [5f872f0]
|
|
14
|
+
- @asteby/metacore-ui@2.18.0
|
|
15
|
+
|
|
3
16
|
## 37.6.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAuEA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,SAAS,CAAA;AAE9E,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AAiBhD,MAAM,WAAW,WAAW;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,WAAW,CAM1D;AAsBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAStE;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAkB3F;AASD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAY1D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASjE;AAUD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAgBnF;AAED,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAkFlB"}
|
|
@@ -15,6 +15,7 @@ import { useState, useEffect, useMemo } from 'react';
|
|
|
15
15
|
import { useTranslation } from 'react-i18next';
|
|
16
16
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, } from '@asteby/metacore-ui/primitives';
|
|
17
17
|
import { Loader2 } from 'lucide-react';
|
|
18
|
+
import { ProcessStepper } from '@asteby/metacore-ui/wizard';
|
|
18
19
|
import { toast } from 'sonner';
|
|
19
20
|
import { toastServerError, toastServerSuccess, extractFieldErrors, localizeFieldErrorMap } from './server-error';
|
|
20
21
|
import { useBranchCreateGate } from './branch-create-gate';
|
|
@@ -666,9 +667,7 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
|
|
|
666
667
|
};
|
|
667
668
|
if (steps.length === 0)
|
|
668
669
|
return null;
|
|
669
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), _jsxs("div", { className: "pt-
|
|
670
|
-
backgroundColor: i <= stepIndex ? (action.color || 'hsl(var(--primary))') : 'hsl(var(--muted))',
|
|
671
|
-
} }, i))) }), _jsxs(DialogDescription, { className: "pt-2", children: [t('common.step', { defaultValue: 'Paso' }), " ", stepIndex + 1, "/", steps.length, step?.title ? ` · ${tl(step.title)}` : ''] }), step?.description && (_jsx("p", { className: "pt-1 text-sm text-muted-foreground", children: tl(step.description) }))] })] }), _jsx("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: _jsx(FieldGrid, { children: stepFields.map((field) => {
|
|
670
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90dvh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90dvh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), _jsxs("div", { className: "pt-1", children: [_jsx(ProcessStepper, { steps: steps.map((s, i) => ({ key: String(i), label: s.title ? tl(s.title) : `${t('common.step', { defaultValue: 'Paso' })} ${i + 1}` })), activeIndex: stepIndex, onStepClick: i => setStepIndex(i) }), _jsxs(DialogDescription, { className: "pt-3", children: [t('common.step', { defaultValue: 'Paso' }), " ", stepIndex + 1, "/", steps.length, step?.title ? ` · ${tl(step.title)}` : ''] }), step?.description && (_jsx("p", { className: "pt-1 text-sm text-muted-foreground", children: tl(step.description) }))] })] }), _jsx("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: _jsx(FieldGrid, { children: stepFields.map((field) => {
|
|
672
671
|
const fullWidth = isLineItemsField(field) ||
|
|
673
672
|
resolveWidget(field) === 'textarea' ||
|
|
674
673
|
resolveWidget(field) === 'richtext';
|
|
@@ -718,7 +718,7 @@ export function DynamicRecordDialog({ open, onOpenChange, nestedInlineCreateSelf
|
|
|
718
718
|
setStepIndex(Math.min(clampedStep + 1, groups.length - 1));
|
|
719
719
|
};
|
|
720
720
|
const goBackStep = () => setStepIndex(Math.max(clampedStep - 1, 0));
|
|
721
|
-
return (_jsx(RecordDialogModelContext.Provider, { value: model, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, nestedInlineCreateSelf: nestedInlineCreateSelf, children: _jsxs(DialogContent, { className: "sm:max-w-2xl max-h-[90dvh] flex flex-col p-0 gap-0 overflow-hidden", style: { maxHeight: '90dvh' }, children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: config.description })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: loading ? (_jsx(LoadingSkeleton, {})) : modalMeta ? (_jsx(ModelContext.Provider, { value: model, children: _jsx(ImageUrlContext.Provider, { value: getImageUrl, children: _jsx(TimeZoneContext.Provider, { value: timeZone, children: _jsxs(CurrencyContext.Provider, { value: currency, children: [_jsxs("form", { id: formId, onSubmit: handleSubmit, className: "grid gap-y-4", children: [isSteps && (_jsx(WizardProgress, { groups: groups, stepIndex: clampedStep })), (isSteps ? [groups[clampedStep]] : groups).map(group => (_jsx(FieldSection, { group: group, children: _jsx("div", { className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: renderFields(group.fields) }) }, group.key))), record?.external_url && (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2", children: _jsx("div", { className: "sm:col-span-2 min-w-0", children: _jsxs("a", { href: record.external_url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Ver en ", record.external_provider ?? 'proveedor externo'] }) }) }))] }), !isCreate && record && relations.length > 0 && (_jsx("div", { className: "mt-6", children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true, canCreate: mode === 'edit', canEdit: mode === 'edit', canDelete: mode === 'edit', onChange: handleChildChange }) }))] }) }) }) })) : null }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0 sm:justify-between", children: [isView && onOpenFullPage ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground", onClick: () => { onOpenChange(false); onOpenFullPage(); }, children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), "Ver p\u00E1gina completa"] })) : _jsx("span", {}), _jsxs("div", { className: "flex items-center gap-2", children: [isSteps && clampedStep > 0 ? (_jsx(Button, { variant: "outline", onClick: goBackStep, disabled: saving || deleting, children: "Anterior" })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || deleting, children: config.cancelLabel })), isView && onDelete && (_jsxs(Button, { variant: "destructive", onClick: handleDelete, disabled: deleting || loading, children: [deleting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), deleting ? 'Eliminando...' : 'Eliminar'] })), isView && onEdit && (_jsx(Button, { onClick: onEdit, disabled: deleting || loading, children: "Editar" })), isEditable && isSteps && !isLastStep && (_jsx(Button, { type: "button", onClick: goNextStep, disabled: saving || loading, children: "Siguiente" })), isEditable && (!isSteps || isLastStep) && (_jsxs(Button, { type: "submit", form: formId, disabled: saving || loading, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), saving ? config.submittingLabel : config.submitLabel] }))] })] })] }) }) }));
|
|
721
|
+
return (_jsx(RecordDialogModelContext.Provider, { value: model, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, nestedInlineCreateSelf: nestedInlineCreateSelf, children: _jsxs(DialogContent, { className: "sm:max-w-2xl max-h-[90dvh] flex flex-col p-0 gap-0 overflow-hidden", style: { maxHeight: '90dvh' }, children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: config.description })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: loading ? (_jsx(LoadingSkeleton, {})) : modalMeta ? (_jsx(ModelContext.Provider, { value: model, children: _jsx(ImageUrlContext.Provider, { value: getImageUrl, children: _jsx(TimeZoneContext.Provider, { value: timeZone, children: _jsxs(CurrencyContext.Provider, { value: currency, children: [_jsxs("form", { id: formId, onSubmit: handleSubmit, className: "grid gap-y-4", children: [isSteps && (_jsx(WizardProgress, { groups: groups, stepIndex: clampedStep, onStepClick: i => setStepIndex(i) })), (isSteps ? [groups[clampedStep]] : groups).map(group => (_jsx(FieldSection, { group: group, children: _jsx("div", { className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: renderFields(group.fields) }) }, group.key))), record?.external_url && (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2", children: _jsx("div", { className: "sm:col-span-2 min-w-0", children: _jsxs("a", { href: record.external_url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Ver en ", record.external_provider ?? 'proveedor externo'] }) }) }))] }), !isCreate && record && relations.length > 0 && (_jsx("div", { className: "mt-6", children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true, canCreate: mode === 'edit', canEdit: mode === 'edit', canDelete: mode === 'edit', onChange: handleChildChange }) }))] }) }) }) })) : null }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0 sm:justify-between", children: [isView && onOpenFullPage ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground", onClick: () => { onOpenChange(false); onOpenFullPage(); }, children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), "Ver p\u00E1gina completa"] })) : _jsx("span", {}), _jsxs("div", { className: "flex items-center gap-2", children: [isSteps && clampedStep > 0 ? (_jsx(Button, { variant: "outline", onClick: goBackStep, disabled: saving || deleting, children: "Anterior" })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || deleting, children: config.cancelLabel })), isView && onDelete && (_jsxs(Button, { variant: "destructive", onClick: handleDelete, disabled: deleting || loading, children: [deleting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), deleting ? 'Eliminando...' : 'Eliminar'] })), isView && onEdit && (_jsx(Button, { onClick: onEdit, disabled: deleting || loading, children: "Editar" })), isEditable && isSteps && !isLastStep && (_jsx(Button, { type: "button", onClick: goNextStep, disabled: saving || loading, children: "Siguiente" })), isEditable && (!isSteps || isLastStep) && (_jsxs(Button, { type: "submit", form: formId, disabled: saving || loading, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), saving ? config.submittingLabel : config.submitLabel] }))] })] })] }) }) }));
|
|
722
722
|
}
|
|
723
723
|
function LoadingSkeleton() {
|
|
724
724
|
return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Skeleton, { className: "h-3.5 w-24" }), _jsx(Skeleton, { className: "h-9 w-full" })] }, i))) }));
|
package/dist/dynamic-form.js
CHANGED
|
@@ -132,7 +132,7 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
|
|
|
132
132
|
setStepIndex((i) => Math.min(i + 1, groups.length - 1));
|
|
133
133
|
};
|
|
134
134
|
const goBack = () => setStepIndex((i) => Math.max(i - 1, 0));
|
|
135
|
-
return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [_jsx(WizardProgress, { groups: groups, stepIndex: stepIndex }), renderGrid(step.fields), _jsxs("div", { className: "flex justify-between gap-2 pt-2", children: [stepIndex > 0 ? (_jsx(Button, { type: "button", variant: "outline", onClick: goBack, disabled: submitting || disabled, children: "Anterior" })) : onCancel ? (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })) : (_jsx("span", {})), isLast ? (_jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })) : (_jsx(Button, { type: "button", onClick: goNext, disabled: submitting || disabled, children: "Siguiente" }))] })] }));
|
|
135
|
+
return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [_jsx(WizardProgress, { groups: groups, stepIndex: stepIndex, onStepClick: i => setStepIndex(i) }), renderGrid(step.fields), _jsxs("div", { className: "flex justify-between gap-2 pt-2", children: [stepIndex > 0 ? (_jsx(Button, { type: "button", variant: "outline", onClick: goBack, disabled: submitting || disabled, children: "Anterior" })) : onCancel ? (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })) : (_jsx("span", {})), isLast ? (_jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })) : (_jsx(Button, { type: "button", onClick: goNext, disabled: submitting || disabled, children: "Siguiente" }))] })] }));
|
|
136
136
|
}
|
|
137
137
|
// ── Sections / flat mode ───────────────────────────────────────────────
|
|
138
138
|
// Layout: scalar header fields flow through a responsive 2-column grid;
|
package/dist/form-layout-ui.d.ts
CHANGED
|
@@ -13,13 +13,17 @@ export declare function FieldSection({ group, children, }: {
|
|
|
13
13
|
children: React.ReactNode;
|
|
14
14
|
}): import("react").JSX.Element;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Step indicator for `mode: "steps"`: the platform's ProcessStepper (numbered
|
|
17
|
+
* circles with a check when done, label underneath, connectors) plus a
|
|
18
|
+
* "Paso i/n · <title>" caption and the step's description. Same component the
|
|
19
|
+
* process modals (workshop, warehouse) use, so a model wizard, an action wizard
|
|
20
|
+
* and a process modal read the same.
|
|
19
21
|
*/
|
|
20
|
-
export declare function WizardProgress({ groups, stepIndex, stepLabel, }: {
|
|
22
|
+
export declare function WizardProgress({ groups, stepIndex, stepLabel, onStepClick, }: {
|
|
21
23
|
groups: FieldGroup<unknown>[];
|
|
22
24
|
stepIndex: number;
|
|
23
25
|
stepLabel?: string;
|
|
26
|
+
/** Jump back to an already completed step (forward jumps are never offered). */
|
|
27
|
+
onStepClick?: (index: number) => void;
|
|
24
28
|
}): import("react").JSX.Element;
|
|
25
29
|
//# sourceMappingURL=form-layout-ui.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-layout-ui.d.ts","sourceRoot":"","sources":["../src/form-layout-ui.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"form-layout-ui.d.ts","sourceRoot":"","sources":["../src/form-layout-ui.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,GACX,EAAE;IACC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B,+BA6CA;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,SAAS,EACT,SAAkB,EAClB,WAAW,GACd,EAAE;IACC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gFAAgF;IAChF,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,+BAeA"}
|
package/dist/form-layout-ui.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
6
6
|
import { useState } from 'react';
|
|
7
7
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger, Button, } from '@asteby/metacore-ui/primitives';
|
|
8
8
|
import { ChevronDown } from 'lucide-react';
|
|
9
|
+
import { ProcessStepper } from '@asteby/metacore-ui/wizard';
|
|
9
10
|
/**
|
|
10
11
|
* Section chrome for `mode: "sections"`. Wraps a group's (already gridded)
|
|
11
12
|
* fields in a titled block.
|
|
@@ -27,13 +28,14 @@ export function FieldSection({ group, children, }) {
|
|
|
27
28
|
return (_jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: "grid gap-3", children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(Button, { type: "button", variant: "ghost", className: "h-auto w-full justify-between px-0 py-1 hover:bg-transparent", children: [header, _jsx(ChevronDown, { className: 'h-4 w-4 shrink-0 transition-transform ' + (open ? 'rotate-180' : '') })] }) }), _jsx(CollapsibleContent, { className: "grid gap-3", children: children })] }));
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* Step indicator for `mode: "steps"`: the platform's ProcessStepper (numbered
|
|
32
|
+
* circles with a check when done, label underneath, connectors) plus a
|
|
33
|
+
* "Paso i/n · <title>" caption and the step's description. Same component the
|
|
34
|
+
* process modals (workshop, warehouse) use, so a model wizard, an action wizard
|
|
35
|
+
* and a process modal read the same.
|
|
33
36
|
*/
|
|
34
|
-
export function WizardProgress({ groups, stepIndex, stepLabel = 'Paso', }) {
|
|
37
|
+
export function WizardProgress({ groups, stepIndex, stepLabel = 'Paso', onStepClick, }) {
|
|
35
38
|
const current = groups[stepIndex];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
} }, g.key))) }), _jsxs("p", { className: "pt-2 text-sm text-muted-foreground", children: [stepLabel, " ", stepIndex + 1, "/", groups.length, current?.title ? ` · ${current.title}` : ''] }), current?.description && (_jsx("p", { className: "pt-1 text-sm text-muted-foreground", children: current.description }))] }));
|
|
39
|
+
const steps = groups.map((g, i) => ({ key: g.key, label: g.title ?? `${stepLabel} ${i + 1}` }));
|
|
40
|
+
return (_jsxs("div", { className: "pt-1", children: [_jsx(ProcessStepper, { steps: steps, activeIndex: stepIndex, onStepClick: onStepClick }), _jsxs("p", { className: "pt-3 text-sm text-muted-foreground", children: [stepLabel, " ", stepIndex + 1, "/", groups.length, current?.title ? ` · ${current.title}` : ''] }), current?.description && (_jsx("p", { className: "pt-1 text-sm text-muted-foreground", children: current.description }))] }));
|
|
39
41
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "38.0.0",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"sonner": ">=1.7",
|
|
39
39
|
"zustand": ">=5",
|
|
40
40
|
"@asteby/metacore-sdk": "^3.7.0",
|
|
41
|
-
"@asteby/metacore-ui": "^2.
|
|
41
|
+
"@asteby/metacore-ui": "^2.18.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@tanstack/react-router": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "^4.0.0",
|
|
69
69
|
"zustand": "^5.0.0",
|
|
70
70
|
"@asteby/metacore-sdk": "3.7.0",
|
|
71
|
-
"@asteby/metacore-ui": "2.
|
|
71
|
+
"@asteby/metacore-ui": "2.18.0"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsc -p tsconfig.json",
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
Switch,
|
|
39
39
|
} from '@asteby/metacore-ui/primitives'
|
|
40
40
|
import { Loader2 } from 'lucide-react'
|
|
41
|
+
import { ProcessStepper } from '@asteby/metacore-ui/wizard'
|
|
41
42
|
import { toast } from 'sonner'
|
|
42
43
|
import { toastServerError, toastServerSuccess, extractFieldErrors, localizeFieldErrorMap } from './server-error'
|
|
43
44
|
import { useBranchCreateGate } from './branch-create-gate'
|
|
@@ -994,26 +995,16 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
|
|
|
994
995
|
<DynamicIcon name={action.icon} className="h-5 w-5" />
|
|
995
996
|
{tl(action.label)}
|
|
996
997
|
</DialogTitle>
|
|
997
|
-
{/*
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
{
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
role="listitem"
|
|
1008
|
-
aria-current={i === stepIndex ? 'step' : undefined}
|
|
1009
|
-
className="h-1.5 flex-1 rounded-full"
|
|
1010
|
-
style={{
|
|
1011
|
-
backgroundColor: i <= stepIndex ? (action.color || 'hsl(var(--primary))') : 'hsl(var(--muted))',
|
|
1012
|
-
}}
|
|
1013
|
-
/>
|
|
1014
|
-
))}
|
|
1015
|
-
</div>
|
|
1016
|
-
<DialogDescription className="pt-2">
|
|
998
|
+
{/* Step indicator: the platform ProcessStepper (same as the
|
|
999
|
+
declarative form_layout wizards and the process modals) +
|
|
1000
|
+
"Paso i/n · título" and the step's description. */}
|
|
1001
|
+
<div className="pt-1">
|
|
1002
|
+
<ProcessStepper
|
|
1003
|
+
steps={steps.map((s, i) => ({ key: String(i), label: s.title ? tl(s.title) : `${t('common.step', { defaultValue: 'Paso' })} ${i + 1}` }))}
|
|
1004
|
+
activeIndex={stepIndex}
|
|
1005
|
+
onStepClick={i => setStepIndex(i)}
|
|
1006
|
+
/>
|
|
1007
|
+
<DialogDescription className="pt-3">
|
|
1017
1008
|
{t('common.step', { defaultValue: 'Paso' })} {stepIndex + 1}/{steps.length}
|
|
1018
1009
|
{step?.title ? ` · ${tl(step.title)}` : ''}
|
|
1019
1010
|
</DialogDescription>
|
|
@@ -1080,7 +1080,7 @@ export function DynamicRecordDialog({
|
|
|
1080
1080
|
className="grid gap-y-4"
|
|
1081
1081
|
>
|
|
1082
1082
|
{isSteps && (
|
|
1083
|
-
<WizardProgress groups={groups} stepIndex={clampedStep} />
|
|
1083
|
+
<WizardProgress groups={groups} stepIndex={clampedStep} onStepClick={i => setStepIndex(i)} />
|
|
1084
1084
|
)}
|
|
1085
1085
|
{(isSteps ? [groups[clampedStep]] : groups).map(group => (
|
|
1086
1086
|
<FieldSection key={group.key} group={group}>
|
package/src/dynamic-form.tsx
CHANGED
|
@@ -211,7 +211,7 @@ export function DynamicForm({
|
|
|
211
211
|
|
|
212
212
|
return (
|
|
213
213
|
<form onSubmit={handleSubmit} className="grid gap-4">
|
|
214
|
-
<WizardProgress groups={groups} stepIndex={stepIndex} />
|
|
214
|
+
<WizardProgress groups={groups} stepIndex={stepIndex} onStepClick={i => setStepIndex(i)} />
|
|
215
215
|
{renderGrid(step.fields)}
|
|
216
216
|
<div className="flex justify-between gap-2 pt-2">
|
|
217
217
|
{stepIndex > 0 ? (
|
package/src/form-layout-ui.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Button,
|
|
11
11
|
} from '@asteby/metacore-ui/primitives'
|
|
12
12
|
import { ChevronDown } from 'lucide-react'
|
|
13
|
+
import { ProcessStepper } from '@asteby/metacore-ui/wizard'
|
|
13
14
|
import type { FieldGroup } from './form-layout'
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -75,37 +76,30 @@ export function FieldSection({
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
79
|
+
* Step indicator for `mode: "steps"`: the platform's ProcessStepper (numbered
|
|
80
|
+
* circles with a check when done, label underneath, connectors) plus a
|
|
81
|
+
* "Paso i/n · <title>" caption and the step's description. Same component the
|
|
82
|
+
* process modals (workshop, warehouse) use, so a model wizard, an action wizard
|
|
83
|
+
* and a process modal read the same.
|
|
81
84
|
*/
|
|
82
85
|
export function WizardProgress({
|
|
83
86
|
groups,
|
|
84
87
|
stepIndex,
|
|
85
88
|
stepLabel = 'Paso',
|
|
89
|
+
onStepClick,
|
|
86
90
|
}: {
|
|
87
91
|
groups: FieldGroup<unknown>[]
|
|
88
92
|
stepIndex: number
|
|
89
93
|
stepLabel?: string
|
|
94
|
+
/** Jump back to an already completed step (forward jumps are never offered). */
|
|
95
|
+
onStepClick?: (index: number) => void
|
|
90
96
|
}) {
|
|
91
97
|
const current = groups[stepIndex]
|
|
98
|
+
const steps = groups.map((g, i) => ({ key: g.key, label: g.title ?? `${stepLabel} ${i + 1}` }))
|
|
92
99
|
return (
|
|
93
|
-
<div className="pt-
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
<div
|
|
97
|
-
key={g.key}
|
|
98
|
-
role="listitem"
|
|
99
|
-
aria-current={i === stepIndex ? 'step' : undefined}
|
|
100
|
-
className="h-1.5 flex-1 rounded-full"
|
|
101
|
-
style={{
|
|
102
|
-
backgroundColor:
|
|
103
|
-
i <= stepIndex ? 'hsl(var(--primary))' : 'hsl(var(--muted))',
|
|
104
|
-
}}
|
|
105
|
-
/>
|
|
106
|
-
))}
|
|
107
|
-
</div>
|
|
108
|
-
<p className="pt-2 text-sm text-muted-foreground">
|
|
100
|
+
<div className="pt-1">
|
|
101
|
+
<ProcessStepper steps={steps} activeIndex={stepIndex} onStepClick={onStepClick} />
|
|
102
|
+
<p className="pt-3 text-sm text-muted-foreground">
|
|
109
103
|
{stepLabel} {stepIndex + 1}/{groups.length}
|
|
110
104
|
{current?.title ? ` · ${current.title}` : ''}
|
|
111
105
|
</p>
|