Cayaqui.MPS.Components
0.49.8
See the version list below for details.
dotnet add package Cayaqui.MPS.Components --version 0.49.8
NuGet\Install-Package Cayaqui.MPS.Components -Version 0.49.8
<PackageReference Include="Cayaqui.MPS.Components" Version="0.49.8" />
<PackageVersion Include="Cayaqui.MPS.Components" Version="0.49.8" />
<PackageReference Include="Cayaqui.MPS.Components" />
paket add Cayaqui.MPS.Components --version 0.49.8
#r "nuget: Cayaqui.MPS.Components, 0.49.8"
#:package Cayaqui.MPS.Components@0.49.8
#addin nuget:?package=Cayaqui.MPS.Components&version=0.49.8
#tool nuget:?package=Cayaqui.MPS.Components&version=0.49.8
Cayaqui.MPS.Components
Design system + componentes EVM/EPC reutilizables para .NET 10 Blazor WebApp (Interactive Server) construidos sobre Syncfusion.Blazor 33.x y Blazor-ApexCharts.
v0.49.6 — MpsCardGroup: MaxCardWidth
MpsCardGroupgana parámetroMaxCardWidth(default"1fr"). PermiteMinCardWidth="280px" MaxCardWidth="400px"para grids con ancho acotado.
v0.49.5 — MpsGauge absorbe CpiGauge/SpiGauge · MpsDeltaChip absorbe VariancePill/TrendDelta
MpsGaugegana parámetroPreset = GaugePreset.Cpi | Spi | None. Elimina necesidad deMpsCpiGaugeyMpsSpiGauge.MpsDeltaChipnuevo componente unificado (Variant = DeltaVariant.Variance | Trend). EliminaMpsVariancePillyMpsTrendDelta.- Migración:
<MpsCpiGauge>→<MpsGauge Preset="MpsGauge.GaugePreset.Cpi">.<MpsTrendDelta Period="WoW">→<MpsDeltaChip Variant="MpsDeltaChip.DeltaVariant.Trend" Period="WoW">.
v0.49.4 — Fix: MpsGrid Number columns respetan cultura del sistema
MpsGridColumnkindNumberusabaCultureInfo.InvariantCulture→ separadores incorrectos en es-CL y otros locales.- Corregido a
CultureInfo.CurrentCulture— valores numéricos ahora usan los separadores del sistema (ej. es-CL:1.234.567).
v0.49.3 — MpsProgressSCurve absorbe MpsPhysicalProgressCurve
MpsPhysicalProgressCurveeliminado — su funcionalidad (curva S clásica, solo líneas) vive ahora enMpsProgressSCurveconShowPartialBars="false".ProgressSCurvePointganaBaselineCumulative?(antesBaselinePct?del componente eliminado).- Migración: reemplazar
<MpsPhysicalProgressCurve>→<MpsProgressSCurve ShowPartialBars="false">y renombrar propiedades:BaselinePct→BaselineCumulative,PlanPct→PlanCumulative,ActualPct→ActualCumulative,ForecastPct→ForecastCumulative.
v0.49.2 — IntelliSense XML docs para todos los componentes
GenerateDocumentationFilehabilitado — el.xmlde IntelliSense se empaqueta junto al DLL.- 876 parámetros
[Parameter]ahora tienen/// <summary>en los 181 componentes (Design, Evm, Engineering). - Sin cambios de API ni binarios.
v0.49.1 — MpsCardGroup
MpsCardGroup: contenedor grid que iguala la altura de todos los cards de cada fila. Auto-fill responsive por defecto; parámetroColumnspara columnas fijas.
v0.48.3 — ConfirmDialog: X-button OnCancel, CloseOnOverlayClick=false, CSS
ConfirmDialog: cerrar con X button o overlay ahora disparaOnCancel(antes se ignoraba).CloseOnOverlayClickdefault cambiado afalse(evita dismiss accidental en acciones destructivas).- CSS movido de
mps-indicators.css→mps-syncfusion-overrides.css(sección dialog). - Variant
Warning: color ámbar aplicado en estado normal + hover; eliminados fallback hex.
v0.48.2 — CashflowChart: eje Y sin sufijo (es-CL), Curve.Straight
- Etiquetas eje Y:
Intl.NumberFormat('es-CL')sin sufijo kUSD (el título del eje ya lo indica). Ej. "25.000" en vez de "25000.0 kUSD". Curve.StraightreemplazaCurve.Smooth— elimina el overshoot visual que extendía la línea "Real acum." más allá del mes de control.
v0.48.1 — CashflowChart: ejes Y en kUSD con 1 decimal
CashflowChart: ambos ejes Y usan(v/1000).toFixed(1) + ' kUSD'— etiquetas y títulos muestran "Acumulado (kUSD)" / "Mensual (kUSD)".GanttChart: columnas Real% y Plan% conFormat="N1".
v0.48.0 — Design audit: CSS quality, dark mode, accesibilidad
Breaking changes — ver BREAKING_CHANGES_v0.48.md.
- Eliminados selectores
WbsKanbanlegacy (display:griden.mps-kanban,.mps-kanban-body,.phase-*) que nunca matcheaban el DOM real. KpiCard: inline styles → clases CSS; nuevo parámetroSize(KpiSize.Md/Sm).GaugeTile: corregido token--color-text-primary(no existía) →--color-text; marker visible en dark mode.RiskHeatmap level-critical: contraste 3.3:1 → 6.1:1 (WCAG AA) conerror-700+ texto blanco.--color-hoverdefinido en tokens (causaba hover invisible enChangeOrderLog).prefers-reduced-motionactivo en toda la librería.MpsDomainHeaderdark mode adaptativo.
v0.44.2–0.44.3 — Fix: ArgumentException en 6 componentes de chart
EvmCashflowCurve, PhysicalProgressCurve, MpsProgressSCurve, ContingencyDrawdown, CashflowChart y MpsTimelineChart lanzaban ArgumentException en DateTimeOffset..ctor(DateTime, TimeSpan.Zero) cuando el parámetro de fecha (p.ej. ControlDate, StartField, EndField) tenía DateTimeKind.Local — que es el Kind que devuelve DateTime.Today y DateTime.Now. Todos los componentes ahora usan DateTime.SpecifyKind(x.Date, DateTimeKind.Utc) internamente: acepta cualquier Kind sin excepción. Sin breaking changes.
v0.44.0 — MpsListGroup + MpsFileManager + Migración ApexCharts
Nuevos componentes:
MpsListGroup— Lista estilizada model-driven con items accionables. Soporte para icono leading, subtítulo, badge trailing (MpsBadgeVariant), selección (@bind-SelectedItemId), estadoDisabled, varianteFlusheItemTemplatecustom.
<MpsListGroup Items="_items" @bind-SelectedItemId="_selectedId" />
@code {
private readonly MpsListGroupItem[] _items =
[
new("1", "Planos estructurales", Subtitle: "Ingeniería Civil",
Icon: "e-icons e-folder", BadgeText: "12", BadgeVariant: MpsBadgeVariant.Brand),
new("2", "Especificaciones técnicas"),
new("3", "Bloqueado", Disabled: true),
];
private string? _selectedId;
}
MpsFileManager— Browser de archivos/documentos con vista Grid (tarjetas) y Lista (tabla). Toggle de vista integrado (@bind-ViewMode). Auto-detección de icono por extensión (.pdf, .dwg, .docx, .xlsx, imágenes, .zip). CallbacksOnOpenyOnDelete. El consumer proveeIReadOnlyList<MpsFileItem>— sin dependencia de sistema de archivos.
<MpsFileManager Items="_files"
Title="Documentos del Proyecto"
@bind-ViewMode="_view"
OnOpen="f => NavigateTo(f.Id)"
OnDelete="f => RemoveFile(f.Id)" />
@code {
private MpsFileItem[] _files =
[
new("f1", "Plano-Estructura.pdf", Category: "Planos",
SizeBytes: 1_420_000, UpdatedAt: DateTime.UtcNow.AddDays(-3)),
new("f2", "Especificaciones.docx", Category: "Specs", SizeBytes: 85_000),
new("d1", "Carpeta Ingeniería", IsFolder: true),
];
private MpsFileViewMode _view = MpsFileViewMode.Grid;
}
Migración Syncfusion Charts → ApexCharts completada:
Todos los componentes de chart han sido migrados a ApexCharts. Los paquetes Syncfusion.Blazor.Charts y Syncfusion.Blazor.Sparkline han sido eliminados de las dependencias del paquete. MpsSparkline reescrito como SVG puro (sin dependencia JS).
Componentes migrados (17 total):
BarChart, StackedBarChart, DonutChart, MonteCarloHistogram, BurndownChart, Waterfall, CommodityPriceChart, ManagementReserveTracker, ProductionCurve, ResourceHistogram, ContingencyDrawdown, EvmCashflowCurve, PhysicalProgressCurve, EvmControlChart, CashflowChart, CpiIndicator, SpiIndicator.
Sin breaking changes en la API pública.
Acción requerida para consumers: Si en tu propio
.csprojtenías una referencia aSyncfusion.Blazor.ChartsoSyncfusion.Blazor.Sparklineúnicamente porque era dependencia transitiva de este paquete, puedes eliminarla con seguridad. Si la usas directamente, consérvala.
EvmCashflowCurve — Curva S EVM monetaria
4 series EVM sobre un gráfico ApexCharts: Plan (PV) azul, Earned Value (EV) área púrpura, Actual Cost (AC) rojo, Forecast (EAC) verde dashed. Anotación horizontal BAC + anotación vertical de fecha de control con etiqueta rotada −90°. Pie con título y fecha.
<EvmCashflowCurve Points="@_points"
Bac="10_000_000m"
ControlDate="@_controlDate"
Title="Talara U3"
Currency="USD" />
@code {
private readonly DateTime _controlDate = new DateTime(2026, 4, 1);
private readonly EvmCashflowCurve.EvmCashflowPoint[] _points =
[
new() { Date = new DateTime(2026,1,1), Pv = 1_200_000m },
new() { Date = new DateTime(2026,2,1), Pv = 2_800_000m, Ev = 2_600_000m, Ac = 2_750_000m },
new() { Date = new DateTime(2026,3,1), Pv = 4_500_000m, Ev = 4_100_000m, Ac = 4_300_000m },
new() { Date = new DateTime(2026,4,1), Pv = 6_200_000m, Ev = 5_600_000m, Ac = 5_900_000m },
new() { Date = new DateTime(2026,5,1), Pv = 7_800_000m, Forecast = 8_400_000m },
new() { Date = new DateTime(2026,6,1), Pv = 9_100_000m, Forecast = 9_800_000m },
new() { Date = new DateTime(2026,7,1), Pv = 10_000_000m, Forecast = 10_800_000m },
];
}
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
Points ⚠️ |
IList<EvmCashflowPoint> |
— | Requerido. |
Bac |
decimal? |
null |
Budget At Completion — línea horizontal de referencia. |
ControlDate |
DateTime? |
null |
Data date — línea vertical dashed + etiqueta. Acepta cualquier DateTimeKind. |
Title |
string |
"" |
Título en el pie del chart. |
ControlDateLabel |
string |
"Control al" |
Prefijo de la etiqueta de fecha de control. |
Currency |
string |
"USD" |
Código ISO 4217 — se incluye en los nombres de serie. |
Height |
string |
"360" |
Alto en px. |
YAxisTitle |
string |
"" |
Título del eje Y. |
Class |
string? |
null |
Clase CSS adicional para el contenedor raíz. |
EvmCashflowPoint (clase anidada pública):
| Propiedad | Tipo | Descripción |
|---|---|---|
Date |
DateTime |
Fecha del punto (eje X). |
Pv |
decimal? |
Planned Value. |
Ev |
decimal? |
Earned Value. null para meses futuros. |
Ac |
decimal? |
Actual Cost. null para meses futuros. |
Forecast |
decimal? |
EAC proyectado. Solo en meses > ControlDate. |
PhysicalProgressCurve — Curva S de avance físico (%)
4 series en porcentaje: Baseline (gris dashed, opcional), Plan (azul), Real (rojo con markers) y Forecast (verde dashed). Meta al 100% como línea horizontal. Anotación vertical de fecha de control.
<PhysicalProgressCurve Points="@_curve"
ControlDate="@_controlDate"
Title="Avance Físico · Talara U3" />
@code {
private readonly DateTime _controlDate = new DateTime(2026, 4, 1);
private readonly PhysicalProgressCurve.ProgressCurvePoint[] _curve =
[
new() { Date = new DateTime(2026,1,1), PlanPct=8m, ActualPct=7m },
new() { Date = new DateTime(2026,2,1), PlanPct=18m, ActualPct=17m },
new() { Date = new DateTime(2026,3,1), PlanPct=30m, ActualPct=27m },
new() { Date = new DateTime(2026,4,1), PlanPct=43m, ActualPct=38m },
new() { Date = new DateTime(2026,5,1), PlanPct=57m, ForecastPct=52m },
new() { Date = new DateTime(2026,6,1), PlanPct=72m, ForecastPct=68m },
new() { Date = new DateTime(2026,7,1), PlanPct=85m, ForecastPct=82m },
new() { Date = new DateTime(2026,8,1), PlanPct=100m, ForecastPct=100m },
];
}
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
Points ⚠️ |
IList<ProgressCurvePoint> |
— | Requerido. |
ControlDate |
DateTime? |
null |
Data date — línea vertical dashed + etiqueta. Acepta cualquier DateTimeKind. |
Title |
string |
"" |
Título en el pie del chart. |
ControlDateLabel |
string |
"Control al" |
Prefijo de la etiqueta de fecha de control. |
YMax |
decimal |
100 |
Máximo del eje Y. Cambiar si la escala no es 0–100. |
ShowGoalLine |
bool |
true |
Muestra/oculta la línea horizontal al 100% (o YMax). |
Height |
string |
"360" |
Alto en px. |
YAxisTitle |
string |
"" |
Título del eje Y. |
Class |
string? |
null |
Clase CSS adicional para el contenedor raíz. |
ProgressCurvePoint (clase anidada pública):
| Propiedad | Tipo | Descripción |
|---|---|---|
Date |
DateTime |
Fecha del punto (eje X). |
BaselinePct |
decimal? |
Baseline original (gris dashed). Omitir si no aplica. |
PlanPct |
decimal? |
Plan vigente. |
ActualPct |
decimal? |
Avance real. Solo para puntos ≤ ControlDate. |
ForecastPct |
decimal? |
Pronóstico. Solo para puntos > ControlDate. |
v0.43.0 — MpsProgressSCurve (Curva S ApexCharts)
Curva S de avance físico con barras periódicas + líneas acumuladas:
MpsProgressSCurve— Gráfico mixto ApexCharts (Bar + Line) para Plan, Real y Forecast. Paleta EVM canónica. Anotaciones deControlDatey meta 100%. El consumer provee parciales y acumulados de forma explícita.
<MpsProgressSCurve Points="@_scurve"
ControlDate="@_controlDate"
Title="Avance Físico · Talara U3" />
@code {
private static readonly DateTime _controlDate = new DateTime(2025, 5, 1);
private MpsProgressSCurve.ProgressSCurvePoint[] _scurve =
[
new() { Date = new DateTime(2025,1,1), PlanPartial=8m, PlanCumulative=8m, ActualPartial=7m, ActualCumulative=7m },
new() { Date = new DateTime(2025,5,1), PlanPartial=14m, PlanCumulative=59m, ActualPartial=12m, ActualCumulative=52m },
new() { Date = new DateTime(2025,6,1), PlanPartial=13m, PlanCumulative=72m, ForecastPartial=14m, ForecastCumulative=66m },
new() { Date = new DateTime(2025,8,1), PlanPartial=13m, PlanCumulative=100m, ForecastPartial=18m, ForecastCumulative=100m },
];
}
v0.42.0 — Engineering Progress Control Table
Tabla de control de avance para ingeniería de proyectos EPC:
MpsEngProgressTable— Tabla Plan/Forecast/Actual/Var por documento con columnas de hitos configurables. Cálculo automático de avance ponderado y reglas de color por fecha de control.
<MpsEngProgressTable Documents="@_docs"
Milestones="@_milestones"
ControlDate="@_controlDate"
Title="Control de Avance de Ingeniería" />
v0.41.0 — Engineering Document Control
Tres componentes atómicos para gestión de entregables de ingeniería:
MpsDocStatusBadge— Badge de estado libre. El consumer mapea sus códigos (IFC, IFA, IFR…) a 5 variantes de color.MpsDocRevisionList— Lista de revisiones con activa destacada, fecha, razón de emisión y link de descarga.MpsDocCard— Card completa: número, disciplina, tipo, revisión activa, HH presupuestado vs real, revisiones colapsables, upload drag & drop opcional.
<MpsDocCard Document="@_doc"
StatusVariantResolver="@(s => s switch {
"IFC" => MpsDocStatusBadge.BadgeVariant.Success,
"IFA" => MpsDocStatusBadge.BadgeVariant.Warning,
"IFR" => MpsDocStatusBadge.BadgeVariant.Info,
_ => MpsDocStatusBadge.BadgeVariant.Neutral })"
ShowUploader="true"
OnFilesSelected="@HandleFiles" />
v0.40.0 — MpsCalendar (FullCalendar 6)
Calendario interactivo completo basado en FullCalendar 6.x (bundle incluido en el paquete):
MpsCalendar— Calendario con vistas Mes, Semana, Día y Agenda. Soporte drag-drop, resize, click en eventos y click en fechas.MpsCalendarEvent— DTO para eventos (Id,Title,Start,End,AllDay,Color,Category).MpsCalendarEventMoveArgs— DTO para callbacks de drag/resize (EventId,NewStart,NewEnd).
@* Registro del script en App.razor *@
<script src="_content/MPS.Components/js/mps-calendar.js"></script>
@* Uso básico en la página *@
<MpsCalendar Events="@_events"
View="MpsCalendar.CalendarView.Month"
Editable="true"
OnEventClick="@HandleEventClick"
OnDateClick="@HandleDateClick"
OnEventDrop="@HandleEventDrop" />
@code {
private List<MpsCalendarEvent> _events = new()
{
new MpsCalendarEvent { Id = "1", Title = "Reunión", Start = DateTime.Today.AddDays(1), AllDay = true },
new MpsCalendarEvent { Id = "2", Title = "Visita", Start = DateTime.Today.AddHours(10), End = DateTime.Today.AddHours(12) },
};
private void HandleEventClick(MpsCalendarEvent ev) => Console.WriteLine($"Click: {ev.Title}");
private void HandleDateClick(DateTime date) => Console.WriteLine($"Fecha: {date:dd-MMM-yy}");
private void HandleEventDrop(MpsCalendarEventMoveArgs a) => Console.WriteLine($"Movido a: {a.NewStart:dd-MMM-yy}");
}
Demo vivo: /catalog/calendar.
v0.39.0 — Track 2: MpsHeatmapChart + MpsTimelineChart (ApexCharts)
Dos nuevos componentes de gráficos basados en Blazor-ApexCharts 6.1.0:
MpsHeatmapChart<TItem>— Agrupa datos planos porRowFielden series y renderiza un heatmap. Params:Data,RowField,ColField,ValueField,Title,Height.MpsTimelineChart<TItem>— Gráfico de barras horizontales tipo Gantt (range-bar). Agrupa porGroupFielden series y convierteDateTimea Unix ms. Params:Data,NameField,GroupField,StartField,EndField,Title,Height.
@* Heatmap: avance % por disciplina y semana *@
<MpsHeatmapChart TItem="HeatCell"
Data="@_heatmap"
RowField="@(x => x.Discipline)"
ColField="@(x => x.Week)"
ValueField="@(x => (decimal?)x.Progress)" />
@* Timeline (Gantt-style): fases de proyecto *@
<MpsTimelineChart TItem="Phase"
Data="@_timeline"
NameField="@(x => x.Name)"
GroupField="@(x => x.Discipline)"
StartField="@(x => x.Start)"
EndField="@(x => x.End)" />
Demo vivo: /catalog/charts (incluye los 6 chart components: Area, Scatter, Funnel, Treemap, Heatmap, Timeline).
v0.38.0 — Track 1: 6 nuevos componentes CSS sin dependencias adicionales
MpsSpinner, MpsAvatar, MpsAvatarGroup, MpsInputGroup, MpsFloatingLabel, MpsFormWizard.
@* Spinner de carga — 4 tamaños, 6 colores *@
<MpsSpinner Size="MpsSpinner.SpinnerSize.Md" Color="MpsSpinner.SpinnerColor.Primary" />
@* Avatar con iniciales determinísticas o imagen *@
<MpsAvatar Name="Pedro Vera" Size="MpsAvatar.AvatarSize.Md" />
<MpsAvatar Name="María López" Src="@user.AvatarUrl" Shape="MpsAvatar.AvatarShape.Circle" />
@* Stack de avatares con badge de overflow *@
<MpsAvatarGroup Avatars="@_equipo" Max="4" />
@* Input con addons de texto *@
<MpsInputGroup Prefix="$" Suffix="USD">
<MpsTextBox Placeholder="Costo estimado" />
</MpsInputGroup>
@* Floating label — el input DEBE tener Placeholder=" " (espacio) *@
<MpsFloatingLabel Label="Nombre del proyecto" InputId="fl-nombre">
<MpsTextBox Placeholder=" " @bind-Value="@_nombre" />
</MpsFloatingLabel>
@* Wizard multi-paso con validación async por step *@
<MpsFormWizard OnFinish="HandleFinish" FinishLabel="Crear Proyecto">
<MpsFormWizardStep Title="Datos generales">
</MpsFormWizardStep>
<MpsFormWizardStep Title="Presupuesto" OnValidate="ValidarPresupuesto">
</MpsFormWizardStep>
</MpsFormWizard>
MpsSpinner: tamaños Xs/Sm/Md/Lg, colores Primary/Muted/White/Success/Warning/Danger. Respeta prefers-reduced-motion.
MpsAvatar: tamaños Xs/Sm/Md/Lg/Xl, formas Circle/Square. Color de iniciales determinístico (paleta de 8 colores semánticos).
MpsAvatarGroup: parámetros Avatars (IReadOnlyList<MpsAvatarSpec>) y Max (default 4).
MpsInputGroup: clase raíz mps-input-addon-group. Props Prefix, Suffix, PrefixIcon, SuffixIcon.
MpsFloatingLabel: requiere Placeholder=" " en el input hijo. InputId para asociación accesible.
MpsFormWizardStep: parámetros Title, OnValidate (Func<Task<bool>>?). Sin parámetro Icon.
v0.37.0 — Gestión de imágenes y fotos: MpsPhotoCard · MpsPhotoGallery · MpsPhotoUploader · MpsAvatarUploader
4 componentes nuevos para el flujo EPC de fotos de campo (iPhone → procesado → galería + lightbox). Integración callback-based — MPS.Components no depende de MPS.Images ni MPS.Storage.
@* Galería con lightbox EXIF — pure Blazor, sin JS *@
<MpsPhotoGallery Items="_fotos" MaxVisible="5" OnDelete="HandleDelete" />
@* Uploader con preview inmediato y progreso por foto *@
<MpsPhotoUploader OnUpload="HandleUpload" OnUploaded="HandleUploaded"
MaxPhotos="10" MaxParallel="3" />
@* Avatar circular con spinner y rollback en error *@
<MpsAvatarUploader OnUpload="HandleUpload" CurrentName="@_user.FullName"
Size="MpsAvatarUploaderSize.Lg" />
AttachmentList actualizado: nuevo parámetro ShowImagePreview (default false) — muestra thumbnail 40×40 inline para extensiones de imagen.
v0.36.0 — MpsSegmented: Variants, Sizes, FullWidth y Disabled por ítem
MpsSegmented<TValue> rediseñado con estilo Pill/Float y API completa. Corrige el CSS class mismatch que impedía que el componente tuviera estilos visuales.
<MpsSegmented TValue="string"
Value="@_view"
ValueChanged="v => _view = v"
Options="@_opts"
Variant="MpsSegmentedVariant.Accent"
Size="MpsSegmentedSize.Sm"
FullWidth="true" />
@code {
private string _view = "kanban";
private static readonly IReadOnlyList<MpsSegmented<string>.Option> _opts =
[
new("lista", "Lista", "e-icons e-list"),
new("kanban", "Kanban", "e-icons e-kanban"),
new("gantt", "Gantt", "e-icons e-gantt", Disabled: true),
];
}
Variantes: Default · Accent · Success · Warning · Danger
Tamaños: Sm · Md (default) · Lg
FullWidth: false por default — true estira al 100% del contenedor
Disabled por ítem: new Option(value, label, icon, Disabled: true) — grisado, no clickeable
v0.35.0 — KpiCard variants, MpsDomainHeader, MpsKpiTile, MpsGaugeTile
v0.34.0 — Skeleton composites para arquetipos de loading-state
Cinco composites nuevos sobre MpsSkeleton cubren los arquetipos comunes de loading-state — eligen el correcto y reducís layout shift sin reinventar el primitivo:
<MpsSkeletonStack /> @* N líneas (forms, sidebars, párrafos) *@
<MpsSkeletonCard Height="280px" /> @* header + rect (chart placeholder) *@
<MpsSkeletonRow /> @* avatar + título + subtítulo *@
<MpsSkeletonGrid /> @* 1×4 KPI strip por default *@
<MpsSkeletonTable Rows="15" /> @* toolbar + 15 filas *@
Composición típica de dashboard:
@if (_loading)
{
<MpsSkeletonGrid Rows="1" Columns="4" /> @* KPI strip *@
<MpsSkeletonCard Height="280px" /> @* chart *@
<MpsSkeletonTable Rows="6" ShowToolbar="false" /> @* tabla resumen *@
}
Todos consumen internamente <MpsSkeleton/> (heredan shimmer + prefers-reduced-motion). +37 tests bUnit (798 total). Sin breaking changes — son aditivos sobre la v0.33.0. Ver scripts/migrate-to-components-0.34.0.md para recetas por arquetipo.
v0.33.0 — MpsSkeleton runtime fix
Bug crítico runtime (presente desde la primera versión): MpsSkeleton.razor rendereaba <div class="mps-skeleton ..."> pero la regla CSS empacada era solo .skeleton. El componente nunca aplicó shimmer ni estilos en consumidores. En 0.33.0 la clase y el markup quedan alineados (.mps-skeleton), @keyframes shimmer se namespacea como mps-skeleton-shimmer, y se agregan defaults dimensionales por shape:
<MpsSkeleton /> @* line · 100% × 0.875rem *@
<MpsSkeleton Shape="rect" /> @* rect · 100% × 6rem *@
<MpsSkeleton Shape="circle" /> @* circle · 2.5rem × 2.5rem *@
<MpsSkeleton /> sin parámetros ahora es visible. Cualquier llamada que ya pasaba Width/Height mantiene el override. Soporte prefers-reduced-motion: reduce (WCAG 2.3.3). +7 tests bUnit (761 total).
Acción opcional para consumers: si tu app aplicó el hotfix consumer-side (regla .mps-skeleton en wwwroot/app.css), podés removerlo — el paquete ya la incluye. Ver scripts/migrate-to-components-0.33.0.md. Sin breaking changes.
v0.32.0 — MpsPageHeader browser tab + Double/TripleProgress 90% opacidad
Fix bug: la versión anterior tenía <PageTitle>@Title - MOVES</PageTitle> hardcodeado en MpsPageHeader — toda app no-MOVES recibía "MOVES" en sus pestañas. Ahora el sufijo es configurable vía ThemeOptions.AppName:
// Program.cs
builder.Services.AddMpsComponents(o => o.AppName = "MOVES");
MpsPageHeader emite automáticamente <PageTitle>{Title} · {AppName}</PageTitle>. Overrides:
SetBrowserTab="false"→ no emite (tu page maneja su propio<PageTitle>).BrowserTabTitle="Corto"→ título distinto al del header visual.BrowserTabSuffix="Demo"→ sufijo por instancia (override de AppName).BrowserTabSuffix=""→ fuerza sin sufijo.
DoubleProgress / TripleProgress: barras a 90% opacidad (antes 50%) para mejor legibilidad EVM.
v0.31.0 — CashflowChart leyenda en CutOffDate
La leyenda del chart ahora se renderiza como HTML propio debajo del gráfico, con dos grupos:
- Mes de cutoff · valor del mes de control para Plan / Real / Forecast (columnas). Forecast muestra el total restante.
- Acumulado al cutoff · cumulativos al cierre de la fecha de control. Forecast acum. = EAC (Real cum + Forecast restante).
Si CutOffDate es null, la leyenda muestra los totales del rango. Cada entrada usa MoneyDisplay (formato consistente con el resto del design system) y un swatch que replica el estilo visual del chart (columnas 65% opacidad, líneas sólidas, Forecast acum. dashed verde).
Sin cambios de API. Helper estático CashflowChart.ComputeLegendValues(raw, cutOff) para tests. Migración: si testeás contra .cf-summary (header anterior), migrá a .cf-legend.
v0.30.0 — CashflowChart con acumulados
CashflowChart ahora muestra columnas mensuales + líneas acumuladas en el mismo gráfico (eje Y secundario):
- Columnas (eje primario, monto mensual): Plan en todos los meses, Real ≤
CutOffDate, Forecast >CutOffDate(filtrado automático). - Líneas acumuladas (eje secundario): Plan acum., Real acum., Forecast acum. — esta última arranca en el Real acum. del cutoff para continuidad visual con la línea Real.
<CashflowChart Points="@cashflow"
Currency="USD"
CutOffDate="@DateTime.Today"
Title="Cashflow mensual · Talara U3" />
ShowCumulative="false" para volver al aspecto 0.29.x (sólo columnas). El parámetro ControlDate queda como alias retro-compat de CutOffDate. Sin breaking de API; +10 tests; demo en /catalogo/evm#CashflowChart.
v0.29.0 — Tipografía dual MPS (Inter + Roboto Condensed)
Convención:
- Inter → cuerpo, labels, títulos, botones — "el resto del contenido".
- Roboto Condensed → charts, tablas, valores numéricos, KPIs, paginadores, códigos tabulares.
- JetBrains Mono (reservado para
--font-mono/.kbd) → atajos de teclado, snippets de código.
Tokens nuevos:
:root {
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
--font-numeric: 'Roboto Condensed', 'Inter', system-ui, sans-serif;
--font-table: var(--font-numeric);
--font-chart: var(--font-numeric);
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}
Recomendación de loading (LCP óptimo). mps-tokens.css ya trae un @import con las fuentes/pesos, pero @import bloquea el render. Para tu host page preferí <link> en App.razor:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;500;700&family=Inter:wght@300;400;500;600;700&display=swap">
Aplicado automáticamente a:
- Charts: SfChart, AccumulationChart, StockChart, RangeNavigator, Sparkline, Gauges, Gantt, PivotView (vía selector
textSVG). - Grids: SfGrid, SfTreeGrid, SfGantt + tablas custom (
.mps-table,.mps-po-table,.mps-deliv-table,.mps-risk-table,.mps-svt-table). - Componentes con valores numéricos: MoneyDisplay, MpsCounter, MpsRangeSlider, CurrencyInput, KpiCard, CpiIndicator, EarnedScheduleIndicator, VarianceIndicator, ResourceHistogram, StockpileLevel, R9cTable, PurchaseOrderRegister, AttachmentList, DonutChart, MpsGauge, MpsBulletGauge.
Override por consumer (mantener apariencia 0.28.x con JetBrains Mono en valores numéricos):
:root { --font-numeric: var(--font-mono); }
Sin breaking changes.
v0.28.0 — Theme SSR (elimina FOUC)
Si tu app usa un DefaultAccent distinto a Emerald, el cold load mostraba un flash visible de Emerald → tu accent (~300ms). Causa: la paleta vivía como CSS estático Emerald + JS interop que la reescribía post-paint. Fix: nuevos selectors :root[data-mps-accent="..."] + helper MpsThemeAttributes para SSR.
Patrón canónico (sin más workarounds inline):
@* App.razor *@
@inject IOptions<ThemeOptions> ThemeOpts
<!DOCTYPE html>
<html lang="es" @attributes="MpsThemeAttributes.For(ThemeOpts.Value)">
<head>
...
</head>
</html>
// Program.cs
builder.Services.AddMpsComponents(opts => opts.DefaultAccent = ThemeAccent.Cobalt);
Resultado: el HTML SSR-rendered ya tiene <html data-mps-accent="cobalt"> antes del primer paint → cero flicker. El ThemeService.SetAsync runtime sigue funcionando para overrides per-user (CSS specificity garantiza que el setProperty inline gane sobre el attribute selector).
5 accents soportados con paleta completa (50→800 stops): Cobalt, Indigo, Emerald (default), Amber, Slate. 3 densities (Compact, Comfort, Spacious) y 2 directions (A warm/sobria, B cool/expresiva) también soportan SSR via data-mps-density y data-mps-direction.
Sin breaking changes — los consumers que no adoptan el patrón siguen funcionando con Emerald como default.
v0.27.0 — Tier 1 native migration (sin breaking changes)
4 componentes core que internamente wrappeaban Syncfusion ahora son HTML nativo:
MpsButton→<button>con CSS existente (mps-btn primary/secondary/tertiary/link/destructive).MpsIconButton→<button class="mps-btn icon-only">.MpsRadio→<input type="radio">conrole="radiogroup"y keyboard handling del browser.MpsTabs→<div role="tablist">con<button role="tab">y keyboard nav WAI-ARIA (← → / ↑ ↓ / Home / End).
API pública conservada — drop-in upgrade. Las clases CSS internas cambiaron: Ghost ahora emite class="mps-btn tertiary" y Danger emite destructive. Si tu CSS custom tenía selectores .mps-btn.ghost/.danger, hay que adaptarlos.
Por qué la migración: independencia de runtime de Syncfusion para los componentes más usados, control completo del CSS, menor superficie de licencia para estos cuatro. Tier 2 (MpsDialog → <dialog>, MpsDrawer, MpsToastHost) en una release futura.
v0.26.0 — UX hardening (5 fixes/features desde feedback)
🐛 Bug fix — MpsRangeSlider tooltip: Syncfusion 33.x no interpola C# format strings en SliderTooltip.Format — el tooltip mostraba "USD {600000:N0} - USD {30000000:N0}" literal. Fix: usar OnTooltipChange callback que construye el texto en C#.
💰 Smart compact money — MpsMoneyScope + nueva regla MPS:
@* Antes: cada MoneyDisplay decidía su escala individualmente *@
<MoneyDisplay Value="450_000m" Compact="true" /> @* "USD 450K" *@
<MoneyDisplay Value="3_500_000m" Compact="true" /> @* "USD 3.5M" — distinto criterio *@
@* v0.26.0: scope unifica escala basado en max value *@
<MpsMoneyScope Values="@(new [] { 450_000m, 3_500_000m })">
<MoneyDisplay Value="450_000m" Compact="true" /> @* "MUSD 0,5" *@
<MoneyDisplay Value="3_500_000m" Compact="true" /> @* "MUSD 3,5" *@
</MpsMoneyScope>
Regla MPS nueva: kUSD con 0 decimales (kUSD 450); MUSD con 1 decimal (MUSD 4.500,1 en es-CL). Una página entera usa la escala del monto mayor — coherencia visual.
Breaking en MoneyDisplay compact: el output cambió de "USD 10.5M" (suffix) a "MUSD 10,5" (prefix). Añadidos params Scale: MoneyScale?, Culture: CultureInfo?, y CompactDecimals ahora es int? con default por-escala (override solo si querés saltarte la regla).
🌐 MpsCounter cultura: ahora hereda CultureInfo.CurrentCulture (era InvariantCulture). Con DefaultThreadCurrentCulture = es-CL en MPS.Web, los números se formatean automáticamente con punto thousands y coma decimal.
💬 MpsButton + MpsIconButton — Tooltip param:
<MpsButton Tooltip="Guardar cambios pendientes">Guardar</MpsButton>
<MpsIconButton Icon="e-icons e-edit" Tooltip="Editar fila" />
Renderiza title (browser tooltip nativo) + aria-label (a11y) si AriaLabel no se especificó explícitamente.
🎨 MpsCard — header bg + line toggle:
@* Header con tint del Variant + sin línea inferior *@
<MpsCard Title="Resumen"
Variant="MpsCardVariant.Success"
HeaderTinted="true"
HeaderBorder="false">
...
</MpsCard>
HeaderTinted (default false) aplica --mps-card-tint al fondo del header. HeaderBorder (default true) controla la línea inferior. Combinados generan un header con look cohesionado al body.
v0.25.0 — MpsEnumSelectRequired + Metadata 0.4.0
MpsEnumSelect<TEnum> (introducido en v0.23) requiere Value: TEnum? (nullable
struct) para soportar el placeholder "Seleccionar...". Esto NO compila con
forms que tienen propiedades non-nullable:
@* NO COMPILA si _form.Status es 'Status' (non-nullable) *@
<MpsEnumSelect TEnum="Status" @bind-Value="_form.Status" />
MpsEnumSelectRequired<TEnum> es una sister component aditiva con
Value: TEnum para el caso non-nullable:
@* OK con _form.Status: Status non-nullable *@
<MpsEnumSelectRequired TEnum="Status" @bind-Value="_form.Status" />
Cuándo usar cuál:
| Escenario | Componente |
|---|---|
Form con [Required] + non-nullable enum prop |
<MpsEnumSelectRequired> |
| Filtro que permite "todas las opciones" (sin selección) | <MpsEnumSelect> (nullable) |
Bump transitivo: Cayaqui.MPS.Metadata 0.4.0+ — los enums con [Display(Name)]
ahora se renderizan con la etiqueta localizada en MpsAutoGrid,
MpsAutoBadge, MpsAutoForm y los renderers de Cayaqui.MPS.Reports.
v0.24.0 — AutoGrid Rich Renderers
MpsAutoGrid ahora rinde automáticamente celdas con componentes ricos del design system según attributes/types del DTO:
<StatusChip>automático cuando una propiedad del DTO es de tipoStatusChip.WorkflowStatus(auto-detect, sin atributo).<UserChip>automático cuando se decora con[UserColumn(AvatarSource, RoleSource)](atributo nuevo enCayaqui.MPS.Metadata 0.3.0). Apunta a propiedades hermanas del DTO.- Refactor de Status/AutoBadge cells: ahora rinden con
<MpsBadge>componente real (conMpsBadgeVariantenum tipado v0.20+) en lugar de<span class="mps-badge">inline.
public class ChangeOrderDto
{
public string Code { get; set; } = "";
public StatusChip.WorkflowStatus Status { get; set; } // → automático <StatusChip>
[UserColumn(AvatarSource = nameof(OwnerAvatar), RoleSource = nameof(OwnerRole))]
public string OwnerName { get; set; } = ""; // → automático <UserChip>
public string? OwnerAvatar { get; set; }
public string? OwnerRole { get; set; }
}
Sin breaking changes. Bumpea dependencia de Cayaqui.MPS.Metadata a 0.3.0. Migración en scripts/migrate-to-components-0.24.0.md.
v0.23.1 — Fix MpsNumeric culture (es-CL)
MpsNumeric ahora formatea + parsea con culture es-CL (. thousands, , decimal) en lugar de Invariant. Default Format=N2 con 1234.5m rinde "1.234,50". Sin breaking change.
v0.23.0 — Forms HTML-native + MpsEnumSelect
Los 6 componentes de formulario simples (MpsTextBox, MpsNumeric, MpsSelect, MpsInputMask, MpsCheckBox, MpsToggle) migran de wrappers Syncfusion a HTML-native:
- Cero JS interop → SSR más confiable y primer paint más rápido
- Tests sin
[Collection("Syncfusion")]— no necesitan serialización - Bundle reducido: drop de
Syncfusion.Blazor.DropDowns
Nuevo: MpsEnumSelect<TEnum> con auto-projection desde Enum.GetValues y labels desde [Display(Name="…")]:
public enum Status { [Display(Name="Aprobado")] Approved, ... }
<MpsFormField Label="Estado">
<MpsEnumSelect TEnum="Status" @bind-Value="_status" />
</MpsFormField>
Breaking changes (2, mecánicos): MpsTextBox.InputType enum cambia (era SF, ahora propio); MpsSelect TextField/ValueField → TextSelector/ValueSelector. Ver scripts/migrate-to-components-0.23.0.md.
Suite EPC completo + design system con 70 componentes activos del roadmap publicado (v0.20.0 — 5 nuevos: Accordion, Timeline, FileUploader, InputMask, AvatarGroup + improvements Button.Loading/Badge enum/Alert enum/Avatar.Status; v0.19.0 — Cards suite: variants/accents/behaviors + 4 nuevos cards especializados + token --color-info-*; v0.18.4 — fix MpsSelect popup + docs sidebar accent override; v0.18.3 — MpsPageHeader breadcrumbs auto-generados + acento izquierdo del tema en el card + tipografía refinada; v0.18.2 — fix 19 constantes MpsIcons con nombres CSS inválidos en Syncfusion; v0.18.1 — fix MpsDialog compatible con Syncfusion 33.2.3; v0.18.0 — MpsIcons ~150 constantes, IRouteLabelsProvider para breadcrumbs automáticos con label+icono, MpsPageHeader.Card mode; v0.17.0 — RiskHeatmap v2 chips/tooltip/filtro; v0.16.0 — UserProfileCard + UserChip.PopoverContent). Agrupados por sección:
- Design system (Buttons · Forms · Data Display · Navigation · Overlays · Feedback · StatusChip)
- Auto-metadata (v0.2.0) —
MpsAutoGrid<T>,MpsAutoForm<TModel>,MpsAutoFormField<T>,MpsAutoBadgeque leen attrs del DTO desdeCayaqui.MPS.Metadata - EVM / EPC (KPI strip · gauges CPI/SPI/PF · curvas S · Gantt · WBS tree grid · R9C · Risk heatmap · Change orders · Purchase orders · Engineering deliverables · LookaheadGrid Last Planner)
- Project controls completos — P3 Schedule (MilestoneStrip, ResourceHistogram, CriticalPathSummary, ScheduleVarianceTable, BurndownChart) · P4 Risk (RiskRegisterTable, TornadoChart, MonteCarloHistogram, RiskBoard, RiskTrendLine) · P5 Scope/Change Control (ApprovalWorkflow, MpsStepper, ConfirmDialog, AttachmentList, RaciMatrix) · P6 Forms de dominio (WbsPicker, PeriodNavigator, DateRangePicker, CurrencyInput, CodedTextbox, SearchCombo) · P7 Data display genérico (BarChart, DonutChart, StackedBarChart, UserChip, UserProfileCard, Banner, EmptyStateIllustrated) · P8 Mining-specific (CommodityPriceChart, StockpileLevel, ProductionCurve, ShiftSchedule)
Distribución propietaria — requiere contrato comercial con Cayaqui. Ver
LICENSE.txt.
Instalación
dotnet add package Cayaqui.MPS.Components
Registro de servicios
// Program.cs
using MPS.Components;
builder.Services.AddMpsComponents(); // ThemeService + ToastService (scoped)
// Override de defaults:
builder.Services.AddMpsComponents(opts =>
{
opts.DefaultAccent = ThemeAccent.Emerald;
opts.DefaultDensity = ThemeDensity.Comfort;
opts.DefaultRadiusScale = 10m / 6m; // "10 · Redondeado"
});
La persistencia por usuario del tema (guardar accent/density/radius en BD) no forma parte de este paquete — se implementa con IThemePersistence (interfaz expuesta aquí) y un módulo de administración propio del consumidor.
Imports globales
En _Imports.razor:
@using MPS.Components.Design ← MpsIcons y otros tipos raíz
@using MPS.Components.Design.Buttons
@using MPS.Components.Design.Forms
@using MPS.Components.Design.DataDisplay
@using MPS.Components.Design.Navigation
@using MPS.Components.Design.Overlays
@using MPS.Components.Design.Feedback
@using MPS.Components.Evm
@using MPS.Components.Theming
Iconos — MpsIcons
MpsIcons es una clase estática de constantes (MPS.Components.Design) que centraliza las clases CSS Syncfusion (e-icons e-*) usadas en toda la suite. Evita strings hardcodeados dispersos y hace los usos refactorizables.
// Namespace — ya incluido si seguís los imports recomendados:
@using MPS.Components.Design
Uso básico
<MpsButton IconLeft="@MpsIcons.Add">Nuevo CA</MpsButton>
<MpsButton IconLeft="@MpsIcons.ExportExcel" Variant="MpsButton.ButtonVariant.Ghost">Exportar</MpsButton>
<MpsIconButton Icon="@MpsIcons.Edit" AriaLabel="Editar" Size="MpsButton.ButtonSize.Sm" />
<MpsIconButton Icon="@MpsIcons.Delete" AriaLabel="Eliminar" Size="MpsButton.ButtonSize.Sm" />
En items de navegación
new NavItem("Dashboard", "/", MpsIcons.Home, IsActive: true)
new NavItem("Control Accounts", "/ca", MpsIcons.GridView)
new NavItem("WBS", "/wbs", MpsIcons.Folder, Badge: "16")
new NavItem("Cronograma", "/schedule", MpsIcons.Schedule)
new NavItem("Riesgos", "/risk", MpsIcons.Warning)
Grupos disponibles (~150 constantes)
| Grupo | Ejemplos |
|---|---|
| Actions | Add, Edit, Delete, Save, Download, Upload, Filter, Search, Undo, Redo, … |
| Export | ExportExcel, ExportCsv, ExportPdf, ExportWord, ExportPng, … |
| Navigation | Home, Folder, FolderOpen, Menu, Expand, Collapse, Pin, OpenLink, … |
| Arrows & Chevrons | ArrowDown/Left/Up/Right, ChevronDown/Up/Left/Right + fills |
| Files & Attachments | File, FilePdf, FileDocument, Attachment, Image, Link, … |
| People & Identity | User, People, Password, Location |
| Status / Feedback | Check, CircleCheck, CircleInfo, Warning, Clock, History, Star, … |
| Data & Tables | Table, GrandTotal, SubTotal, Calculation, Level1–Level5, … |
| Charts | Chart, ChartLine, ChartDonut, ChartScatter, ChartColumn, … |
| EVM / Project Controls | Kpi, CriticalPath, GanttGripper, TimelineDay/Week/Month, … |
La galería completa con búsqueda y preview está en /catalogo/icons del proyecto MPS.Web de referencia.
Estáticos
Los CSS/JS se sirven automáticamente bajo _content/MPS.Components/. Snippet recomendado en App.razor — un único bundle más el theme de Syncfusion (que viaja como dependencia transitiva):
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/tailwind3.css" />
<link rel="stylesheet" href="_content/MPS.Components/css/mps-bundle.css" />
<script src="_content/MPS.Components/js/mps-theme.js"></script>
El theme Syncfusion debe ir antes que
mps-bundle.csspara quemps-syncfusion-overrides.css(incluido en el bundle) tenga la última palabra.
Cargar CSS individuales (opcional)
Si prefieres tree-shake manual o cargar sólo un subset, sustituye mps-bundle.css por los archivos puntuales en este orden:
<link rel="stylesheet" href="_content/MPS.Components/css/mps-tokens.css" />
<link rel="stylesheet" href="_content/MPS.Components/css/mps-syncfusion-overrides.css" />
<link rel="stylesheet" href="_content/MPS.Components/css/mps-components.css" />
<link rel="stylesheet" href="_content/MPS.Components/css/mps-indicators.css" />
<link rel="stylesheet" href="_content/MPS.Components/css/mps-evm-extras.css" />
<link rel="stylesheet" href="_content/MPS.Components/css/mps-wbs-treegrid.css" />
<link rel="stylesheet" href="_content/MPS.Components/css/mps-gantt.css" />
Requisitos
- .NET 10.0 o superior
- Syncfusion.Blazor 33.2.3+ — el consumidor debe registrar su propia licencia Syncfusion:
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR_LICENSE_KEY");
Defaults del tema
| Propiedad | Default |
|---|---|
| Accent | Emerald |
| Density | Comfort |
| RadiusScale | 10 / 6 (opción "10 · Redondeado") |
Configurable vía ThemeOptions al registrar AddMpsComponents(opts => { ... }).
Auto-metadata (v0.2.0)
Con un DTO decorado usando atributos de Cayaqui.MPS.Metadata, los componentes MpsAutoGrid<T> y MpsAutoForm<TModel> generan columnas y campos automáticamente sin ceremonia manual.
public sealed class ControlAccountRow
{
[Label("Código"), Wbs] public string Code { get; set; } = "";
[Label("Nombre"), Display(Order = 1)] public string Name { get; set; } = "";
[Currency("USD")] public decimal Bac { get; set; }
[Percentage(0)] public decimal PctComplete { get; set; }
[EvmIndicator(EvmKind.Cpi), EvmBand] public decimal Cpi { get; set; }
[Badge] public CaPhase Phase { get; set; }
[Hidden] public Guid Id { get; set; }
}
public enum CaPhase
{
[BadgeColor(BadgeKind.Brand)] Engineering,
[BadgeColor(BadgeKind.Purple)] Procurement,
[BadgeColor(BadgeKind.Warning)] Construction
}
<MpsAutoGrid TItem="ControlAccountRow" DataSource="@rows" Title="Control Accounts" />
<MpsAutoForm TModel="ControlAccountRow" Model="@row" OnValidSubmit="SaveAsync" />
La dependencia Cayaqui.MPS.Metadata se auto-instala como transitive y AddMpsComponents() también llama internamente a AddMpsMetadata().
Layout shell (v0.4.0) — MpsSidebar + MpsAppHeader
Ambos componentes siguen Untitled UI Pro v8. Patrón canónico para una app:
@* MainLayout.razor *@
@inherits LayoutComponentBase
@inject ThemeService Theme
<div class="app-shell">
<MpsSidebar @ref="_sidebar"
Tone="MpsSidebar.SidebarTone.Branded"
BrandLogo="@(new MpsSidebar.SidebarLogo(
Src: \"/img/brand-app.svg\", @* 200×40 lockup horizontal *@
SlimSrc: \"/img/brand-app-slim.svg\", @* 36×36 icon/iso *@
Alt: \"Mi App\"))"
CompanyLogo="@(new MpsSidebar.SidebarLogo(
Src: \"/img/owner-logo.svg\", @* 160×28 lockup horizontal *@
SlimSrc: \"/img/owner-logo-slim.svg\", @* 28×28 icon/iso *@
Alt: \"Owner Inc\"))"
Collapsible="true"
CollapseStorageKey="myapp.sidebar.collapsed">
<ChildContent>
<MpsSidebarSection>
<MpsSidebarItem Text="Inicio" Href="/" Match="MpsSidebar.ItemMatch.Exact">
<Icon><svg width="18" height="18" ...></svg></Icon>
</MpsSidebarItem>
</MpsSidebarSection>
<MpsSidebarSection Label="Control">
<MpsSidebarItem Text="Proyectos" Href="/projects" Badge="12">
<Icon><svg ...></svg></Icon>
</MpsSidebarItem>
</MpsSidebarSection>
</ChildContent>
<Footer>
<MpsSidebarUser Name="Ana Pérez" Subtitle="ana@cayaqui.com" />
</Footer>
</MpsSidebar>
<div class="app-content">
<MpsAppHeader Sidebar="_sidebar"
ShowHamburger="true"
ShowSearch="true"
SearchPlaceholder="Buscar…"
OnSearch="@HandleSearch"
UserName="Ana Pérez"
UserSubtitle="ana@cayaqui.com">
<UserMenu>
<a href="/perfil">Mi perfil</a>
<a href="/config">Configuración</a>
<button @onclick="SignOut">Cerrar sesión</button>
</UserMenu>
</MpsAppHeader>
<main class="app-main">
@Body
</main>
</div>
</div>
@code {
private MpsSidebar? _sidebar;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) await Theme.InitializeAsync();
}
private void HandleSearch(string q) { /* navegar / filtrar */ }
private async Task SignOut() { /* ... */ }
}
CSS mínimo del shell (en app.css del consumer — el bundle no incluye estos selectores porque varían según el layout):
.app-shell { display: flex; min-height: 100vh; background: var(--color-bg); }
.app-content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.app-main { flex: 1; min-width: 0; padding: 32px; }
MpsSidebar — opciones
| Parámetro | Default | Notas |
|---|---|---|
Variant |
Application (280px) |
Slim (72px solo iconos) |
Tone |
Neutral |
Tinted (lavado accent-50) o Branded (accent-700 con texto claro) |
BrandLogo (v0.15.0) |
null |
SidebarLogo(Src, SlimSrc?, Alt?). Logo del producto en el header. App: 200×40 px. Slim: 36×36 px. |
CompanyLogo (v0.15.0) |
null |
SidebarLogo(Src, SlimSrc?, Alt?). Logo del owner en franja inferior. App: 160×28 px. Slim: 28×28 px. |
Sticky |
true |
Sticky al top del viewport (position: sticky; top: 0; height: 100vh). Setear false si la sidebar va embebida en un layout que no quiere ese comportamiento. |
Collapsible |
false |
Toggle Application↔Slim en el header. |
CollapseStorageKey |
null |
Si seteás un key, el estado collapsed persiste por usuario en localStorage. |
AutoActive |
true |
Infiere item activo desde NavigationManager.Uri. |
ChildContent |
null |
Modo declarativo (<MpsSidebarSection> + <MpsSidebarItem>). |
Sections |
[] |
Modo data-driven legacy con NavSection/NavItem records. |
Sticky requiere viewport scroll. Si tu shell tiene un ancestor con
overflow: autoooverflow: hiddenpropio,position: stickyse ancla a ese ancestor — usualmente no es lo que querés. Mantené el shell sin overflow propio (el body scrollea) y el sticky funciona como espera el patrón Untitled UI v8.
Sidebar logos (v0.15.0)
MpsSidebar soporta de forma tipada dos logos parametrizables: del producto (header) y de la compañía/owner (franja inferior, opcional). Cada logo se declara con dos variantes — lockup horizontal para Application mode y icon/iso square para Slim/Collapsed.
<MpsSidebar Tone="MpsSidebar.SidebarTone.Branded"
BrandLogo="@(new MpsSidebar.SidebarLogo(
Src: "/img/brand-myapp.svg",
SlimSrc: "/img/brand-myapp-slim.svg",
Alt: "MyApp Suite"))"
CompanyLogo="@(new MpsSidebar.SidebarLogo(
Src: "/img/owner-logo.svg",
SlimSrc: "/img/owner-logo-slim.svg",
Alt: "Owner Inc"))"
Collapsible="true"
CollapseStorageKey="myapp.sidebar.collapsed">
@* ChildContent / Footer slots ... *@
</MpsSidebar>
Tipo público
public sealed record SidebarLogo(string Src, string? SlimSrc = null, string? Alt = null);
Src— URL del lockup horizontal (Application mode).SlimSrc— URL del icon/iso 1:1 (Slim mode oCollapsed=true). Opcional pero recomendado. Si esnull, se reusaSrcy puede verse apretado/deformado.Alt— texto alt para accesibilidad.
Dimensiones recomendadas de los assets
| Slot | Application (sidebar 280px) | Slim/Collapsed (sidebar 72px) |
|---|---|---|
| Brand (producto) | 200×40 px | 36×36 px |
| Company (owner) | 160×28 px | 28×28 px |
- SVG preferido (escalable, sin pérdida en retina). PNG aceptable a @2x (400×80, 72×72, 320×56, 56×56). Evitar JPG (sin alpha).
- Las imágenes deben llegar trim (sin padding baked-in). El padding interno lo provee el slot CSS (
.sb-header,.sb-company). CompanyLogose renderiza conopacity: 0.75(Neutral/Tinted) ó0.85(Branded) — visualmente subordinado al brand del producto.
Tone="Branded" + logos
En modo Branded (sidebar pintado con accent-700), los logos a color sobre el fondo oscuro pueden no leer bien. Dos opciones:
- Recomendado: proveer un
Srcapuntando a la versión blanca/monocroma del logo (ej.logo-white.svg). - Si tu logo es monocromo con alpha (typical SVG con
fill="#000"o sin fill explícito), agregar la utility classinvert-on-brandedal<img>del logo invierte automáticamente a blanco víafilter: brightness(0) invert(1). La utility está disponible globalmente — no requiere parámetro adicional.
Layout en runtime
Application (280px) Slim (72px)
┌──────────────────────────┐ ┌──────┐
│ [logo MyApp lockup] ⇄ │ │ [Mi] │ ← BrandLogo.SlimSrc
├──────────────────────────┤ ├──────┤
│ ▸ Dashboard │ │ ▦ │
│ ▸ Proyectos 12 │ │ □ │
│ ▸ Riesgos │ │ ⚠ │
├──────────────────────────┤ ├──────┤
│ [Footer slot — user] │ │ [Av] │ ← Footer slot (opcional, MpsSidebarUser)
├──────────────────────────┤ ├──────┤
│ [logo Owner Inc] │ │ [Ow] │ ← CompanyLogo.SlimSrc
└──────────────────────────┘ └──────┘
Coexistencia con
Footerslot: si declarásFooter(típicamenteMpsSidebarUser) yCompanyLogo, ambos se apilan (Footer arriba, CompanyLogo abajo). El divisor entre ambos se suprime automáticamente para evitar doble línea (regla CSS> .sb-footer + .sb-company).
Precedencia en el header: si pasás un
HeaderRenderFragment custom, ignoraBrandLogoyBrandMark. Si pasásBrandLogo, ignoraBrandMark. La intención es que cada nivel de override sea opt-in claro.
MpsAppHeader — opciones
| Parámetro | Default | Notas |
|---|---|---|
Sticky |
true |
Sticky al top del area de contenido. |
ShowHamburger |
false |
Muestra el botón menú a la izquierda. Si querés que aparezca solo en mobile, condicionalo en el consumer (no se fuerza por CSS). |
Sidebar |
null |
Ref al MpsSidebar. Si está seteado, el hamburger toggle-a Collapsed automáticamente. |
OnHamburgerClick |
— | Override manual; toma precedencia sobre Sidebar. |
ShowSearch |
false |
Renderiza un <input type="search"> con icono. OnSearch se dispara al apretar Enter. |
UserName / UserSubtitle / UserImage |
— | Si UserName no es null, renderiza el dropdown de usuario. |
UserMenu |
— | Slot con los items del dropdown (links, botones). Click cierra el menú. |
Left / Center / Right |
— | Slots libres. Center se oculta en <768px. |
Recomendaciones de uso
- Usá una sola instancia de
MpsAppHeaderyMpsSidebarpor shell. Para shells anidados (admin / contractor portal) duplicá el shell completo. - Tono de la sidebar:
Brandedpara shell global (la marca visible). Si tenés un sub-shell (ej. configuración), usarTintedoNeutralpara diferenciar jerarquía. - Sidebar collapsed default: si tu app tiene una densidad alta de items, considerá iniciar con
Collapsed=truey dejar al usuario expandir. - Iconos: SVG inline 18×18 con
stroke="currentColor"heredan el color del item según estado (hover/active/branded). Evitá íconos rasterizados. - Auth-aware: el componente no decide qué items mostrar; envolvé
<MpsSidebarItem>con<AuthorizeView Roles="...">cuando necesites filtrar por rol.
Accesibilidad de formularios (v0.4.2+)
MpsFormField ahora cascadea un MpsFieldContext a sus inputs hijos. Los inputs (MpsTextBox, MpsNumeric, MpsSelect, MpsDatePicker, MpsCheckBox) leen el cascade y propagan id, aria-describedby (apunta a hint o error) y aria-invalid automáticamente al input subyacente — sin código extra del consumer.
<MpsFormField Label="Email" Hint="Correo de trabajo" Error="@_emailError" Required="true">
<MpsTextBox @bind-Value="_email" />
</MpsFormField>
Resultado renderizado (simplificado):
<div class="mps-field has-error">
<label class="mps-label" for="mps-fld-abc123">Email <span aria-hidden="true">*</span></label>
<div class="mps-field-control">
<input id="mps-fld-abc123" aria-describedby="mps-fld-abc123-error" aria-invalid="true" ... />
</div>
<div class="mps-field-error" id="mps-fld-abc123-error" role="alert" aria-live="polite">
@_emailError
</div>
</div>
El error se anuncia automáticamente a screen readers vía role="alert" + aria-live="polite".
AriaLabel para controles sin label visible
Cuando un input vive fuera de un MpsFormField (ej. checkbox dentro de tabla, button icon-only, dialog con HeaderTemplate sin texto), pasá AriaLabel explícito:
<MpsCheckBox @bind-Checked="@row.Selected" AriaLabel="Seleccionar fila" />
<MpsButton AriaLabel="Cerrar" IconLeft="e-icons e-close" />
<MpsDialog @bind-Visible="_open" AriaLabel="Detalle del proyecto">
<ChildContent>...</ChildContent>
</MpsDialog>
MpsDialog deriva aria-label desde Title automáticamente si no pasás AriaLabel.
0.4.1 retirado: tenía un bug donde el id auto-generado de
MpsFormFieldse regeneraba en cada rerender del padre, rompiendo la conexiónlabel[for]/aria-describedby. Si estás en 0.4.1, subí a 0.4.2 (fix incluido).
Inicialización del tema (recomendado)
Para que el accent del tema (default Emerald) se aplique desde el primer frame, el consumer debe inicializar ThemeService una vez en su layout principal:
@inject ThemeService Theme
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) await Theme.InitializeAsync();
}
}
Sin esto, los tokens accent quedan en su fallback estático (Direction A = emerald) hasta que el usuario cambie el tema desde la UI.
Dark / Light / System Mode (v0.46.0)
Add two inline scripts and the Syncfusion dual-link to your App.razor <head>, in this exact order:
<script>
(function(){
try {
var m = localStorage.getItem('mps-color-mode') || 'system';
var dark = m==='dark' || (m==='system' && matchMedia('(prefers-color-scheme: dark)').matches);
if (dark) document.documentElement.setAttribute('data-mps-mode','dark');
} catch(_){}
})();
</script>
<link id="sf-theme-light" rel="stylesheet"
href="_content/Syncfusion.Blazor.Themes/tailwind3.css" />
<link id="sf-theme-dark" rel="stylesheet"
href="_content/Syncfusion.Blazor.Themes/tailwind3-dark.css"
disabled />
<script>
(function(){
if (document.documentElement.getAttribute('data-mps-mode')==='dark') {
var l = document.getElementById('sf-theme-light');
var d = document.getElementById('sf-theme-dark');
if (l) l.disabled = true;
if (d) d.disabled = false;
}
})();
</script>
<link rel="stylesheet" href="_content/Cayaqui.MPS.Components/css/mps-bundle.css" />
Then place <MpsColorModeToggle /> anywhere in your layout:
@using MPS.Components.Design.Navigation
<MpsColorModeToggle />
Or switch programmatically:
@inject ThemeService Theme
<button @onclick="() => Theme.SetAsync(mode: ThemeMode.Dark)">Dark</button>
Configure the default in Program.cs:
builder.Services.AddMpsComponents(o =>
{
o.DefaultMode = ThemeMode.System; // Light, Dark, or System (default)
});
Known limitation: In dark mode on first load a brief (~50–150 ms) Syncfusion theme flash may occur because tailwind3.css starts downloading before Script 1 can swap to tailwind3-dark.css. This is the tradeoff of localStorage-based detection.
Cambio dinámico de acento
ThemeService permite cambiar el accent (y density / direction / radiusScale / fontFamily) en tiempo real desde cualquier página. La aplicación re-pinta instantáneamente — sidebar branded, KPI gauges, botones primary, focus rings, etc. — porque internamente el servicio reescribe las CSS custom properties (--color-accent-50 … --color-accent-700) en :root vía JS interop.
Acentos disponibles
public enum ThemeAccent { Cobalt, Indigo, Emerald, Amber, Slate }
5 paletas built-in. Cada acento expone una rampa completa de 9 stops (50/100/200/300/400/500/600/700/800/900) ya validada para contraste WCAG AA en texto y backgrounds. Emerald es el default si no override-as DefaultAccent.
Cambiar el acento desde una página
Patrón recomendado: MpsSegmented con un option por accent + binding al ThemeService.Accent.
@page "/configuracion"
@inject ThemeService Theme
@implements IDisposable
<MpsCard Title="Apariencia">
<MpsFormField Label="Color del tema">
<MpsSegmented TValue="ThemeAccent"
Value="@Theme.Accent"
ValueChanged="@(async v => await Theme.SetAsync(accent: v))"
Options="@_accentOpts" />
</MpsFormField>
<MpsFormField Label="Densidad">
<MpsSegmented TValue="ThemeDensity"
Value="@Theme.Density"
ValueChanged="@(async v => await Theme.SetAsync(density: v))"
Options="@_densityOpts" />
</MpsFormField>
</MpsCard>
@code {
MpsSegmented<ThemeAccent>.Option[] _accentOpts = new[]
{
new MpsSegmented<ThemeAccent>.Option(ThemeAccent.Cobalt, "Cobalt"),
new MpsSegmented<ThemeAccent>.Option(ThemeAccent.Indigo, "Indigo"),
new MpsSegmented<ThemeAccent>.Option(ThemeAccent.Emerald, "Emerald"),
new MpsSegmented<ThemeAccent>.Option(ThemeAccent.Amber, "Amber"),
new MpsSegmented<ThemeAccent>.Option(ThemeAccent.Slate, "Slate"),
};
MpsSegmented<ThemeDensity>.Option[] _densityOpts = new[]
{
new MpsSegmented<ThemeDensity>.Option(ThemeDensity.Compact, "Compact"),
new MpsSegmented<ThemeDensity>.Option(ThemeDensity.Comfort, "Comfort"),
new MpsSegmented<ThemeDensity>.Option(ThemeDensity.Spacious, "Spacious"),
};
protected override void OnInitialized() => Theme.Changed += StateHasChanged;
public void Dispose() => Theme.Changed -= StateHasChanged;
}
Theme.Changed es un event Action? que se dispara después de aplicar el cambio. Suscribirse + StateHasChanged permite que la página refleje el Theme.Accent actualizado en bindings (Value= del segmented).
Cambio programático
// Cambiar solo accent:
await Theme.SetAsync(accent: ThemeAccent.Cobalt);
// Cambiar varios atributos en un solo apply:
await Theme.SetAsync(accent: ThemeAccent.Indigo, density: ThemeDensity.Compact);
// Volver al default del tenant:
await Theme.SetAsync(accent: ThemeAccent.Emerald, density: ThemeDensity.Comfort);
SetAsync es debounced de facto — múltiples await consecutivos son seguros, pero en UIs reactivas (sliders, color pickers continuos) sumá tu propio debounce client-side para evitar flicker.
Persistencia entre sesiones
Para que la elección del usuario sobreviva F5 y logout/login, registrá una implementación de IThemePersistence:
public interface IThemePersistence
{
Task<ThemeState?> LoadAsync(CancellationToken ct);
Task SaveAsync(ThemeState state, CancellationToken ct);
}
Opción A — ProtectedLocalStorage (Blazor Server, default)
public sealed class LocalStorageThemePersistence : IThemePersistence
{
private readonly ProtectedLocalStorage _store;
private const string Key = "mps.theme";
public LocalStorageThemePersistence(ProtectedLocalStorage store) => _store = store;
public async Task<ThemeState?> LoadAsync(CancellationToken ct)
{
try
{
var r = await _store.GetAsync<ThemeState>(Key);
return r.Success ? r.Value : null;
}
catch { return null; }
}
public async Task SaveAsync(ThemeState state, CancellationToken ct)
=> await _store.SetAsync(Key, state);
}
// Program.cs
services.AddScoped<IThemePersistence, LocalStorageThemePersistence>();
Opción B — DB por usuario (multi-device)
Si tu app necesita que el tema viaje entre dispositivos, persistí ThemeState en una columna JSON sobre la tabla de usuarios:
public sealed class DbThemePersistence : IThemePersistence
{
private readonly ICurrentUser _user;
private readonly AppDbContext _db;
public async Task<ThemeState?> LoadAsync(CancellationToken ct)
{
var u = await _db.Users.FindAsync([_user.Id], ct);
return u?.ThemeJson is not null
? JsonSerializer.Deserialize<ThemeState>(u.ThemeJson)
: null;
}
public async Task SaveAsync(ThemeState state, CancellationToken ct)
{
var u = await _db.Users.FindAsync([_user.Id], ct);
if (u is null) return;
u.ThemeJson = JsonSerializer.Serialize(state);
await _db.SaveChangesAsync(ct);
}
}
ThemeState es un record público con todos los atributos del tema:
public sealed record ThemeState(
ThemeDirection Direction,
ThemeAccent Accent,
ThemeDensity Density,
decimal RadiusScale,
string FontFamily,
ThemeMode Mode);
Migration: IThemePersistence implementors
If you implement IThemePersistence directly and persist theme state to a database, ThemeState now has a 6th required parameter Mode (added in v0.46.0):
// Before (0.45.x):
new ThemeState(Direction, Accent, Density, RadiusScale, FontFamily)
// After (0.46.0):
new ThemeState(Direction, Accent, Density, RadiusScale, FontFamily, Mode)
// When loading from DB with no stored mode yet, use System as default:
Mode: Enum.TryParse<ThemeMode>(savedModeString, true, out var m) ? m : ThemeMode.System
If you store theme columns individually (not as JSON), add a nullable ThemeMode string column to your preferences table:
ALTER TABLE UserPreferences ADD ThemeMode nvarchar(16) NULL;
Reactividad en componentes custom del consumer
Para componentes custom que no usen CSS variables (ej. canvas/SVG con colores hardcodeados), suscribirse al evento Changed y recolorear:
@inject ThemeService Theme
@implements IDisposable
<canvas @ref="_canvas" width="400" height="200"></canvas>
@code {
private ElementReference _canvas;
protected override void OnInitialized() => Theme.Changed += OnThemeChanged;
public void Dispose() => Theme.Changed -= OnThemeChanged;
private async void OnThemeChanged()
{
// Theme.Accent cambió — recolorear el canvas custom.
await JS.InvokeVoidAsync("myChart.recolor", _canvas, Theme.Accent.ToString());
StateHasChanged();
}
}
Tip: la mayoría de componentes MPS y tu UI consumer nativa no requieren esto — heredan el accent automáticamente vía CSS variables. Solo te preocupa esto si tenés rendering custom fuera del flujo CSS (canvas/WebGL/charts third-party que reciben colores como argumento, no como CSS).
Acento exclusivo de un componente (override puntual)
ThemeService.SetAsync(accent: ...) reescribe los --color-accent-* en :root — el cambio afecta toda la app. Si necesitás que solo el sidebar (o cualquier componente puntual) use un acento distinto al global, sobrescribí las custom properties en un selector más específico — las variables CSS cascadean y solo afectan al sub-árbol donde se redefinen.
<MpsSidebar Class="sb-corporate" Sections="@_sections" />
<style>
.mps-sidebar.sb-corporate {
--color-accent-50: #EEF2FF;
--color-accent-100: #E0E7FF;
--color-accent-700: #4338CA;
}
</style>
Botones, KPIs, focus rings y resto de la UI conservan el acento global. Solo cambia el highlight del item activo del sidebar marcado con Class="sb-corporate".
Variables que consume MpsSidebar (por impacto visual):
| Variable | Dónde se usa |
|---|---|
--color-accent-50 |
Fondo del item activo (Tone Neutral/Tinted) |
--color-accent-100 |
Borde del item activo + tinte de fondo Tone="Tinted" |
--color-accent-700 |
Texto + iconos del item activo |
--color-accent-600 |
Fondo de la sidebar en Tone="Branded" |
Si solo querés cambiar el highlight sin migrar toda la rampa, podés ir más directo:
.mps-sidebar.sb-corporate .sb-item.active { background: #1F2937; color: #FFFFFF; }
Default por tenant en Program.cs
Si tu app es multi-tenant y cada tenant tiene un default distinto:
services.AddMpsComponents(opts =>
{
opts.DefaultAccent = currentTenant.BrandAccent; // ej. ThemeAccent.Cobalt
opts.DefaultDensity = ThemeDensity.Comfort;
opts.DefaultFontFamily = "'Inter', system-ui, sans-serif";
});
El usuario siempre puede sobrescribir vía Theme.SetAsync — el default solo aplica en la primera carga si no hay state persistido.
ThemeDirection — A vs B
public enum ThemeDirection { A, B }
| Valor | Efecto visual |
|---|---|
A |
Sidebar neutral (fondo --color-bg), acentos sólo en items activos. Estética corporativa/sobria. Default. |
B |
Sidebar usa el acento como color de fondo. Estética más expresiva/colorida. |
// Cambiar a dirección B:
await Theme.SetAsync(direction: ThemeDirection.B);
MpsColorModeToggle — props
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
Size |
MpsSegmentedSize |
Md |
Tamaño del toggle: Sm · Md · Lg |
Class |
string? |
null |
Clase CSS adicional |
@using MPS.Components.Design.Navigation
<MpsColorModeToggle /> @* Md (default) *@
<MpsColorModeToggle Size="MpsSegmentedSize.Sm" /> @* compacto para toolbar *@
CSS tokens semánticos — dark mode compatibility
Para que componentes custom del consumer reaccionen automáticamente al dark mode, usar exclusivamente estos tokens (no hardcodear colores):
| Token | Descripción | Uso típico |
|---|---|---|
--color-bg |
Fondo de app/página | body, main, page wrapper |
--color-bg-subtle |
Fondo alternativo | Table rows alternadas, cards secundarias |
--color-surface |
Superficies elevadas | Cards, dropdowns, modals |
--color-surface-alt |
Superficies más elevadas | Popups sobre cards |
--color-border |
Borde estándar | Inputs, tablas, divisores |
--color-border-strong |
Borde enfatizado | Focus rings, separadores importantes |
--color-text |
Texto primario | Títulos, labels, body copy |
--color-text-secondary |
Texto secundario | Subtítulos, descripciones |
--color-text-tertiary |
Texto apagado | Placeholders, metadata |
--color-accent-600 |
Acento principal activo | Botones primary, links, highlights |
Ejemplo de componente custom dark-compatible:
.mi-tarjeta {
background: var(--color-surface);
border: 1px solid var(--color-border);
color: var(--color-text);
border-radius: calc(var(--radius-base) * 1px);
}
.mi-tarjeta .descripcion {
color: var(--color-text-secondary);
}
.mi-tarjeta .accion {
color: var(--color-accent-600);
}
El selector dark activo es [data-mps-mode="dark"] en <html> — los tokens se redefinen automáticamente bajo ese selector en mps-tokens.css.
Catálogo de componentes por categoría
Lista resumida — para sintaxis + props + casos completos ver /docs/components en el repo.
Buttons (MPS.Components.Design.Buttons)
| Componente | Versión | Propósito |
|---|---|---|
MpsButton |
0.1.0 | Botón base — wrapper de SfButton. Variants: Primary / Secondary / Ghost / Danger / Link. Sizes Sm / Md / Lg. Slots IconLeft / IconRight (clase CSS, ej. e-icons e-plus). AriaLabel para botones icon-only o texto no descriptivo. HtmlType="button" (default), "submit", "reset". |
MpsIconButton |
0.1.0 | Botón cuadrado solo con icono. Required: Icon (clase CSS). Required best-practice: AriaLabel. |
MpsButtonGroup |
0.21.0 | Toggle data-driven entre opciones (Items: IReadOnlyList<MpsButtonGroupItem> + @bind-Value). Reusa MpsButton.ButtonSize. Selected item recibe ButtonVariant.Primary. |
v0.15.0 fix: tanto
MpsButtoncomoMpsIconButtonenrutabanTypeyaria-labelcomo atributos sueltos alSfButtoninterno. En Syncfusion 33.2.3,SfButtondeclaraHtmlAttributescon[Parameter(CaptureUnmatchedValues=true)]— cualquier atributo no reconocido choca con la asignación explícita y disparaInvalidOperationExceptional renderizar. Ahora ambos componentes consolidan los atributos HTML en el diccionarioHtmlAttributesinterno (incluyendotypeyaria-label). Cero impacto en tu API —HtmlTypeyAriaLabelsiguen siendo los mismos parámetros del consumer.
<MpsButton Variant="MpsButton.ButtonVariant.Primary"
IconLeft="e-icons e-plus">Nuevo CA</MpsButton>
<MpsButton Variant="MpsButton.ButtonVariant.Ghost"
IconLeft="e-icons e-export-excel">Exportar</MpsButton>
<MpsIconButton Icon="e-icons e-edit"
AriaLabel="Editar fila"
Size="MpsButton.ButtonSize.Sm" />
<MpsButton HtmlType="submit"
Variant="MpsButton.ButtonVariant.Primary"
Block="true">Guardar cambios</MpsButton>
Forms (MPS.Components.Design.Forms)
| Componente | Versión | Propósito |
|---|---|---|
MpsFormField |
0.1.0 | Wrapper label + hint + error. |
MpsTextBox |
0.1.0 | Input texto (single/multi/password). |
MpsNumeric |
0.1.0 | Numérico con stepper, min/max, format. |
MpsSelect<TValue, TItem> |
0.1.0 | DropDown genérico. |
MpsDatePicker |
0.1.0 | Selector de fecha. |
MpsCheckBox |
0.1.0 | Checkbox 2-state / tristate. |
MpsRadio<TValue> |
0.1.0 | Grupo radio genérico. |
MpsToggle |
0.1.0 | Switch on/off. |
WbsPicker |
0.12.0 | Selector jerárquico CA/WP con búsqueda + chip. |
PeriodNavigator |
0.12.0 | < Apr-2026 > Day/Week/Month/Quarter/Year. |
DateRangePicker |
0.12.0 | Rango con 8 presets + Custom + ExtraPresets. |
CurrencyInput |
0.12.0 | Numeric + currency selector, default 0 decimales. |
CodedTextbox |
0.12.0 | Auto-formato WBS/CBS uppercase + grouping. |
SearchCombo<TItem> |
0.12.0 | Autocomplete genérico con debounce + match highlight. |
MpsFileUploader |
0.20.0 | Drag-drop dropzone; emite IBrowserFile[]; validación MIME + tamaño; OnRejected. |
MpsInputMask |
0.20.0 | Wrapper SfMaskedTextBox con presets RutChile/RucPeru/PhoneIntl. |
MpsRangeSlider |
0.22.0 | Slider dual-handle numérico (wrapper SfSlider<double[]> Range). API decimal. Format/Prefix/Suffix en tooltips. |
MpsColorPicker |
0.22.0 | Color picker free-form (Picker) o restringido a paleta (Palette). Wrapper SfColorPicker. |
Data Display (MPS.Components.Design.DataDisplay)
MpsAvatar · MpsBadge · StatusChip · MpsCard · MpsStatCard · MpsMediaCard · MpsReviewCard · MpsActionCard · MpsEmpty · MpsProgress · MpsGrid<T> · MpsGridColumn · MpsGridActionColumn · MpsAutoGrid<T> · MpsAutoBadge + nuevos en 0.11.0/0.13.0/0.19.0/0.20.0:
| Componente | Versión | Propósito |
|---|---|---|
| AttachmentList | 0.11.0 | Lista archivos + extension icons + preview/download/delete. |
| BarChart | 0.13.0 | Wrapper SfChart Vertical/Horizontal con paleta MPS. |
| DonutChart | 0.13.0 | SfAccumulationChart con InnerRadius + center label. |
| StackedBarChart | 0.13.0 | N series stacked, modo Percent (StackingColumn100). |
| UserChip | 0.13.0 | Avatar + nombre + rol compacto inline, 3 tamaños. |
| MpsStatCard | 0.19.0 | Card de métricas inline con trend indicator. |
| MpsMediaCard | 0.19.0 | Card con imagen (top cap o side). |
| MpsReviewCard | 0.19.0 | Rating estrellas + autor. |
| MpsActionCard | 0.19.0 | Header con persona + dropdown de acciones. |
| MpsTimeline | 0.20.0 | Eventos cronológicos con dots por MpsTimelineStatus; orientación Vertical/Horizontal. |
| MpsAvatarGroup | 0.20.0 | Stack horizontal de N avatares con overlap -8px; excedente colapsa a chip +N. |
| MpsKanban<TItem> | 0.21.0 | Kanban genérico con selector functions (IdSelector, ColumnSelector) y drag-drop HTML5 native. OnItemMoved reporta MpsKanbanMove<TItem>. |
| MpsRating | 0.21.0 | Rating estrellas standalone. Interactive (@bind-Value + hover preview) o read-only. 3 sizes (Sm/Md/Lg). |
| MpsCounter | 0.21.0 | Número animado con easing cubic-out. Format/Prefix/Suffix/Culture configurables. Duration=0 = set inmediato. |
| MpsTreeView<TNode> | 0.22.0 | Tree view genérico con selector functions (IdSelector, ParentSelector, LabelSelector, IconSelector). Wrapper SfTreeView. Single-select + ExpandAll. |
| MpsListGroup | 0.44.0 | Lista estilizada model-driven. Icono, subtítulo, badge, @bind-SelectedItemId, Disabled, Flush, ItemTemplate. |
| MpsFileManager | 0.44.0 | Browser Grid/Lista de archivos. Auto-icono por extensión. @bind-ViewMode, OnOpen, OnDelete. |
Navigation (MPS.Components.Design.Navigation)
MpsBreadcrumbs · MpsPageHeader · MpsSegmented<TValue> · MpsSidebar (+ MpsSidebarSection + MpsSidebarItem + MpsSidebarUser) · MpsTabs · MpsAppHeader + nuevo:
| Componente | Versión | Propósito |
|---|---|---|
| MpsStepper | 0.11.0 | Wizard multi-paso con CanAdvance/CanFinish + AllowJumpBack. |
| MpsPagination | 0.22.0 | Page nav hand-rolled con ellipsis + SortedSet dedup. ShowSummary, ShowSizeSelector opcionales. |
MpsBreadcrumbs — resolución automática vía IRouteLabelsProvider
Registrar una única vez en DI y todos los breadcrumbs con AutoGenerate="true" resuelven label + icono automáticamente sin parámetros por página.
1 — Implementar en la app:
using MPS.Components.Design.Navigation;
public sealed class AppRouteLabelsProvider : IRouteLabelsProvider
{
private static readonly Dictionary<string, RouteInfo> Routes =
new(StringComparer.OrdinalIgnoreCase)
{
["/"] = new("Inicio", "e-icons e-home"),
["/proyectos"] = new("Proyectos", "e-icons e-gantt-chart"),
["/proyectos/overview"] = new("Vista General", "e-icons e-eye"),
["/control-accounts"] = new("Control Accounts", "e-icons e-hierarchy"),
["/riesgos"] = new("Riesgos", "e-icons e-warning"),
["/schedule"] = new("Cronograma", "e-icons e-schedule"),
// Segmentos sueltos (fallback cuando el path completo no matchea)
["overview"] = new("Vista General", "e-icons e-eye"),
};
public RouteInfo? GetRoute(string pathOrSegment) =>
Routes.TryGetValue(pathOrSegment, out var info) ? info : null;
}
2 — Registrar en Program.cs (una sola vez):
builder.Services.AddSingleton<IRouteLabelsProvider, AppRouteLabelsProvider>();
3 — Usar en cualquier página sin parámetros extra:
<MpsBreadcrumbs AutoGenerate="true" />
Prioridad de resolución por segmento: Items explícito → RouteLabels param → IRouteLabelsProvider DI → title-case del segmento.
RouteInfo(string Label, string? Icon) — Icon acepta cualquier clase CSS de Syncfusion (ej. "e-icons e-home").
Si IRouteLabelsProvider no está registrado en DI, el componente lo ignora silenciosamente (sin excepción).
Overlays (MPS.Components.Design.Overlays)
MpsDialog · MpsDrawer · MpsPopover · MpsTooltip · MpsDropdownMenu · MpsCommandMenu + nuevo:
| Componente | Versión | Propósito |
|---|---|---|
| ConfirmDialog | 0.11.0 | Wrapper Default/Danger/Warning sobre MpsDialog + IsBusy. |
Feedback (MPS.Components.Design.Feedback)
MpsAlert · MpsSkeleton · MpsToastHost (con ToastService) + nuevos en 0.13.0/0.20.0:
| Componente | Versión | Propósito |
|---|---|---|
| Banner | 0.13.0 | Sticky top con 5 variants — Env purple gradient para QA/STAGING/DEV. |
| EmptyStateIllustrated | 0.13.0 | 6 illustration kinds inline SVG + acción opcional. |
| MpsAccordion | 0.20.0 | Contenedor colapsable template-based vía CascadingValue. Multiple permite varios abiertos. |
| MpsAccordionItem | 0.20.0 | Sección hija de MpsAccordion. Disabled suprime click. Animación chevron + body via CSS. |
EVM / EPC (MPS.Components.Evm) — 42 componentes
P1 EVM/AACE (8): EvmKpiStrip · KpiCard · MpsGauge · MpsBulletGauge · CpiGauge · SpiGauge · CpiIndicator · SpiIndicator · VarianceIndicator · ForecastIndicator · EvmSemaforo · TrendChip · DoubleProgress · TripleProgress · EvmCashflowCurve · PhysicalProgressCurve · TcpiIndicator · EarnedScheduleIndicator · HealthDot · VariancePill · TrendDelta · MpsSparkline · CsiIndicator · EvmControlChart
P2 Cost Controls (7): MoneyDisplay · QuantityDisplay · CommitmentGauge · Waterfall · CashflowChart · ContingencyDrawdown · ManagementReserveTracker
Otros pre-roadmap: GanttChart · WbsTreeGrid · WbsKanban · R9cRow · R9cTable · RiskHeatmap · ChangeOrderLog · PurchaseOrderRegister · DeliverablesProgressTable · LookaheadGrid
| Componente | Versión | Sección | Propósito |
|---|---|---|---|
MilestoneStrip |
0.10.0 | P3 Schedule | Barra horizontal hitos + health + Hoy + critical rombo. |
ResourceHistogram |
0.10.0 | P3 Schedule | StackingColumn por categoría + capacity stripline. |
CriticalPathSummary |
0.10.0 | P3 Schedule | Widget ejecutivo float + critical + slip + next milestone. |
ScheduleVarianceTable |
0.10.0 | P3 Schedule | Tabla baseline/current/forecast + slip pill + indent WBS. |
BurndownChart |
0.10.0 | P3 Schedule | Plan dashed + Actual con markers + status burn rate. |
RiskRegisterTable |
0.9.0 | P4 Risk | Tabla PMI completa con filtros + búsqueda + drill-down. |
TornadoChart |
0.9.0 | P4 Risk | Sensitivity ranking low/high simétricas. |
MonteCarloHistogram |
0.9.0 | P4 Risk | Distribución QRA + percentile striplines (P50/P80/P90). |
RiskBoard |
0.9.0 | P4 Risk | Kanban PMI lifecycle. |
RiskTrendLine |
0.9.0 | P4 Risk | Sparkline burden total + TrendDelta LowerIsBetter. |
ApprovalWorkflow |
0.11.0 | P5 Scope | Cadena aprobadores con states + comments + overall computed. |
RaciMatrix |
0.11.0 | P5 Scope | Role × actividad + validación PMBoK 9.1.2.1 automática. |
CommodityPriceChart |
0.14.0 | P8 Mining | Cu/Au/Li temporal + baseline stripline + Δ% pill. |
StockpileLevel |
0.14.0 | P8 Mining | Barra horizontal + bandas operacionales + status auto. |
ProductionCurve |
0.14.0 | P8 Mining | Plan vs Actual + nameplate + status achievement %. |
ShiftSchedule |
0.14.0 | P8 Mining | Calendario semanal turnos Day/Night/Maint/Off/Custom. |
Novedades v0.22.0 — Specialized Inputs
4 componentes nuevos:
MpsRangeSlider— slider dual-handle numérico (wrapper SfSlider Range).MpsColorPicker— color picker free-form + palette (wrapper SfColorPicker).MpsTreeView<TNode>— tree view genérico con selector functions (wrapper SfTreeView).MpsPagination— page nav hand-rolled con ellipsis dedup.
Sin breaking changes.
Novedades v0.21.0 — Dashboards Polish
4 componentes nuevos:
MpsKanban<TItem>— kanban genérico con selector functions y drag-drop HTML5 native.MpsRating— rating estrellas standalone (interactive / read-only).MpsCounter— número animado con easing cubic-out + cancellation correcta.MpsButtonGroup— toggle data-driven entre opciones.
3 improvements opt-in:
MpsProgress.Shape="Circular"con SVG ring.MpsTabs.Orientation="Vertical"(mapea a Syncfusion).MpsTooltip.ContentTemplatepara markup rico.
Refactor interno: WbsKanban ahora wrappea MpsKanban<KanbanPackage> — API pública idéntica. Sin breaking changes.
Novedades v0.20.0 — Forms & Patterns
5 componentes nuevos:
MpsAccordion+MpsAccordionItem— secciones colapsables template-based conCascadingValue.MpsTimeline— eventos cronológicos, vertical/horizontal con status-colored dots.MpsFileUploader— drag-drop dropzone (sin HTTP); emiteIBrowserFile[].MpsInputMask— wrapper sobreSfMaskedTextBoxcon presets RUT/RUC/Phone.MpsAvatarGroup— stack de N avatares con chip+N.
4 improvements:
MpsButton.Loadingcon spinner inline.MpsBadgeenum + Pill + Outline modifiers.MpsAlertenum + Dismissible + OnDismiss.MpsAvatar.Statusoverlay dot.
Breaking minor: MpsBadge.Variant y MpsAlert.Variant pasaron de string a enum (MpsBadgeVariant/MpsAlertVariant). Migración mecánica vía find/replace; VariantString [Obsolete] provee escape hatch hasta v1.0. Detalles en scripts/migrate-to-components-0.20.0.md.
Novedades v0.19.0 — Cards suite
MpsCardrefactored — 6 variants semánticas (Default | Primary | Success | Warning | Danger | Info), 4 modos de acento (Bar | Border | Tinted | None), 2 densidades (Comfortable | Compact). Todos los params opt-in; defaults 100% backward-compat. Cards conTitlemuestran una barra vertical gris muy suave por default (Accent="Bar"); suprimir conAccent="MpsCardAccent.None".- 4 comportamientos opt-in:
Collapsible(@bind-Open),Closable(OnClose),Fullscreen(ESC sin JS interop),Tabs(@bind-ActiveTabId+ slotTabbedContent). - 4 componentes nuevos:
MpsStatCard(métrica + trend inline),MpsMediaCard(image cap top/side),MpsReviewCard(rating estrellas + autor),MpsActionCard(header persona + dropdown). - Token palette
--color-info-*enmps-tokens.css(Direction A + B). Resuelve referencias huérfanas pre-existentes enmps-evm-extras.cssymps-wbs-treegrid.css. UserProfileCardrefactorizado internamente para envolver<MpsCard NoPadding>— API pública idéntica.
Novedades v0.18.0
MpsIcons— clase estática (~150 constantes) con iconos Syncfusion agrupados por categoría semántica (Actions, Navigation, Export, Files, People, Status, Data, Charts, EVM, Arrows). NamespaceMPS.Components.Design. Elimina strings hardcodeados y hace los usos refactorizables.IRouteLabelsProvider+RouteInfo— interfaz DI para resolución automática de label + icono enMpsBreadcrumbs. RegistrarAddSingleton<IRouteLabelsProvider, MyProvider>()una sola vez enProgram.cs; todos los breadcrumbs conAutoGenerate="true"lo consumen automáticamente.MpsPageHeader.Card— nuevo parámetrobool Card = trueque envuelve el header en una card blanca con borde gris. Personalizable conCardBackgroundyCardBorderColor(valores CSS arbitrarios).MpsDialogfix —DialogEvents.OnOverlayClickrenombrado aOnOverlayModalClicken Syncfusion 33.2.3. CorrigeInvalidOperationExceptionen runtime al renderizar cualquier modal.- SVGs eliminados — todos los iconos SVG inline de la librería (AppHeader, Sidebar, Breadcrumbs, Grid, AutoGrid, etc.) reemplazados por clases
e-iconsde Syncfusion — bundle más pequeño, consistencia visual con el design system.
Novedades v0.17.0 — RiskHeatmap v2
RiskHeatmap fue completamente rediseñado. Las celdas ahora muestran chips con el ID del riesgo (R-01, O-02) en lugar de un contador, tooltip rico con título/responsable/categoría/score al hover, overflow "+N más" con popover de lista completa, y un filtro segmentado Inherente / Residual / Objetivo. Las amenazas se distinguen en rojo y las oportunidades en verde.
Nuevos campos en RiskItem (todos nullable, backwards-compatible):
ProbabilityResidual,ImpactResidual— coordenadas de la vista Residual.ProbabilityTarget,ImpactTarget— coordenadas de la vista Objetivo.IsOpportunity—truepara oportunidades (chip verde).
Nuevos parámetros en RiskHeatmap:
View+ViewChanged— binding bidireccional (@bind-View).ShowViewFilter— muestra/oculta el selector de vista (defaulttrue).
Migración entre versiones
Cada versión tiene su script de migración en scripts/migrate-to-components-X.Y.Z.md del repo. Para upgrades multi-versión (e.g. 0.4.x → 0.14.0) usar scripts/consumer-upgrade-to-0.10.0.md + el script de la versión target.
Para greenfield (proyecto Blazor WebApp nuevo): scripts/consumer-setup.md configura todo desde cero (Program.cs + App.razor + MainLayout).
Documentación completa
El catálogo vivo (demos + props detallados + ejemplos) está en github.com/Cayaqui/MPS_DESIGN:
/docs/components/*.md— sintaxis, ejemplos y tabla de props para cada componente./docs/roadmap-componentes.md— estado del roadmap (47 items, P1–P8 cerrado)./CHANGELOG.md— historial de versiones con breaking changes y migration notes./scripts/migrate-to-components-*.md— scripts de migración por versión (0.5.0 → 0.14.0)./docs/infrastructure/metadata.md— integración conCayaqui.MPS.Metadata.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Blazor-ApexCharts (>= 6.1.0)
- Cayaqui.MPS.Metadata (>= 0.17.0)
- Microsoft.AspNetCore.Components.Web (>= 10.0.7)
- Syncfusion.Blazor.Buttons (>= 33.2.3)
- Syncfusion.Blazor.Calendars (>= 33.2.3)
- Syncfusion.Blazor.Core (>= 33.2.3)
- Syncfusion.Blazor.Gantt (>= 33.2.3)
- Syncfusion.Blazor.Grid (>= 33.2.3)
- Syncfusion.Blazor.Inputs (>= 33.2.3)
- Syncfusion.Blazor.Navigations (>= 33.2.3)
- Syncfusion.Blazor.Notifications (>= 33.2.3)
- Syncfusion.Blazor.Popups (>= 33.2.3)
- Syncfusion.Blazor.SplitButtons (>= 33.2.3)
- Syncfusion.Blazor.Themes (>= 33.2.3)
- Syncfusion.Blazor.TreeGrid (>= 33.2.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.51.1 | 0 | 5/25/2026 |
| 0.51.0 | 0 | 5/25/2026 |
| 0.50.0 | 0 | 5/24/2026 |
| 0.49.8 | 0 | 5/24/2026 |
| 0.49.6 | 0 | 5/24/2026 |
| 0.49.5 | 0 | 5/24/2026 |
| 0.49.2 | 8 | 5/24/2026 |
| 0.49.1 | 29 | 5/24/2026 |
| 0.49.0 | 24 | 5/24/2026 |
| 0.48.4 | 26 | 5/24/2026 |
| 0.48.3 | 31 | 5/24/2026 |
| 0.48.2 | 26 | 5/24/2026 |
| 0.48.1 | 28 | 5/24/2026 |
| 0.48.0 | 31 | 5/24/2026 |
| 0.47.2 | 81 | 5/19/2026 |
| 0.47.1 | 65 | 5/19/2026 |
| 0.47.0 | 80 | 5/19/2026 |
| 0.46.0 | 86 | 5/18/2026 |
| 0.45.2 | 94 | 5/18/2026 |
| 0.45.1 | 87 | 5/18/2026 |
0.49.8 — Fix: MpsCardGroup reverted to minmax(min, max) — MaxCardWidth="Xpx" is a hard column cap; use default 1fr for full-width rows. App.razor link to {App}.styles.css documented as required.
0.49.7 — Fix: MpsCardGroup grid CSS moved to mps-bundle.css (mps-components.css) — previously only in Blazor scoped bundle, causing layout to break when styles.css was not linked.
0.49.6 — MpsCardGroup: new MaxCardWidth parameter (default "1fr"). Use MinCardWidth="280px" MaxCardWidth="400px" to cap card size in auto-fill grids.
0.49.5 — Merge: MpsCpiGauge + MpsSpiGauge absorbed into MpsGauge via GaugePreset.Cpi/Spi. Merge: MpsVariancePill + MpsTrendDelta absorbed into MpsDeltaChip (Variant=Variance/Trend). Migration: MpsCpiGauge → MpsGauge Preset="GaugePreset.Cpi"; MpsTrendDelta → MpsDeltaChip Variant="DeltaVariant.Trend".
0.49.4 — Fix: MpsGrid Number column formatting now uses CultureInfo.CurrentCulture instead of InvariantCulture — numeric values now display with correct locale separators (e.g. es-CL: 1.234.567 instead of 1,234,567).
0.49.3 — Merge: MpsPhysicalProgressCurve absorbed into MpsProgressSCurve (ShowPartialBars=false). ProgressSCurvePoint gains BaselineCumulative?. Migration: use MpsProgressSCurve ShowPartialBars="false"; rename BaselinePct→BaselineCumulative, PlanPct→PlanCumulative, ActualPct→ActualCumulative, ForecastPct→ForecastCumulative.
0.49.2 — Docs: GenerateDocumentationFile enabled; 876 [Parameter] properties now carry /// <summary> XML IntelliSense docs across all 181 components (Design, Evm, Engineering). No API or binary changes.
0.49.1 — MpsCardGroup: equal-height card grid container (auto-fill responsive + optional Columns override).
0.47.2 — Fix: MpsGrid Height defaults to "auto" when not set — prevents NullReferenceException in SfGrid.GetStyle() during SSR. No breaking changes.
0.47.1 — Fix: MpsGrid now accepts Height parameter (string?, e.g. "280px") and forwards it to SfGrid — fixes InvalidOperationException "does not have a property matching the name 'Height'". No breaking changes.
0.47.0 — R9cRow.R9cEntry: breaking change — Ac→Incurred, ToCommit→Paid, nuevo Trends, eliminado Variance. PctExec renderiza correctamente en escala 0..1 (fix: era 0.9% en vez de 85%). 0.46.1 — Docs: theming guide expandida en PackageReadme. Secciones nuevas: ThemeDirection A vs B, MpsColorModeToggle props, tabla de CSS tokens semánticos (--color-bg, --color-text, --color-surface, --color-accent-*) para dark mode compatibility en componentes custom del consumer. Sin cambios de API ni binarios.
0.46.0 — Dark / Light / System color mode. New: ThemeMode enum, ThemeOptions.DefaultMode (default=System), ThemeService.Mode + SetAsync(mode:) + InitializeAsync with Mode, MpsThemeAttributes dark support (emits data-mps-mode=dark), MpsColorModeToggle component (Light/System/Dark segmented toggle with SVG icons). CSS: dark palette in mps-tokens.css for Direction A+B and all 5 accents. JS: mps-theme.js applyMode() + OS prefers-color-scheme change listener. Consumer setup: add dual Syncfusion <link> + 2 anti-FOUC scripts to App.razor (see PackageReadme). Breaking: ThemeState record gains Mode as last parameter — update any ThemeState(...) direct constructors to add ThemeMode.System as final arg.
v0.45.2 — Fix: MPS.Licensing.dll ahora se incluye en lib/net10.0/ del .nupkg. En 0.45.0/0.45.1 el DLL faltaba en el deploy del consumer (502.5 en IIS). Sin cambios de API.
v0.45.1 — Fix: re-publicación de 0.45.0 con binarios correctos (0.45.0 empaquetó binarios sin las adiciones de licensing por flag --no-build). Sin diferencia de API.
v0.45.0 — Licensing: AddMpsComponents acepta opts.LicenseKey (ECDSA P-256). Sin clave válida se muestra banner en app header y marca de agua en output. ComponentsLicenseState registrado en DI. Sin breaking changes — licenseKey es opcional y todos los comportamientos existentes se mantienen cuando no se provee.
v0.44.4 — Docs: secciones completas de uso (parámetros + ejemplos) para EvmCashflowCurve y PhysicalProgressCurve en PackageReadme. Nota de los fixes 0.44.2–0.44.3 (ArgumentException DateTimeOffset). Sin cambios de código.
v0.44.3 — Fix: mismo ArgumentException en DateTimeOffset..ctor(DateTime, TimeSpan.Zero) corregido en los 5 componentes restantes con el patrón: PhysicalProgressCurve, MpsProgressSCurve, ContingencyDrawdown, CashflowChart, MpsTimelineChart. Todos usan DateTime.SpecifyKind(x.Date, DateTimeKind.Utc). Sin breaking changes.
v0.44.2 — Fix: EvmCashflowCurve lanzaba ArgumentException al recibir un ControlDate con DateTimeKind.Local (ej. DateTime.Today). DateTimeOffset..ctor(DateTime, TimeSpan.Zero) requiere Kind.Utc o Kind.Unspecified. Corregido con DateTime.SpecifyKind(cd.Date, DateTimeKind.Utc). Sin breaking changes.
v0.44.1 — Fix: Tooltip.Intersect=false en 7 componentes con Shared=true (StackedBarChart, ContingencyDrawdown, ResourceHistogram, EvmControlChart, EvmCashflowCurve, PhysicalProgressCurve, ProductionCurve) — ApexCharts lanza excepción si ambos son true. Sin breaking changes.
v0.44.0 — MpsListGroup: lista estilizada model-driven (icono, badge, selección, Flush, ItemTemplate). MpsFileManager: browser Grid/Lista para documentos — auto-icono por extensión, OnOpen/OnDelete, @bind-ViewMode. Migración Syncfusion.Blazor.Charts → ApexCharts (17 componentes). Sin breaking changes en API pública. Consumers deben quitar Syncfusion.Blazor.Charts de sus propios csproj si no lo usan en otro lugar. 999 tests totales.
0.43.8 — MpsProgressSCurve: etiqueta de Fecha de Control vertical sobre línea punteada; leyendas simplificadas (Plan/Real/Forecast, sin " acum."). v0.43.7 — MpsProgressSCurve: títulos de ejes Y — "Avance Acumulado" en Y1 izquierdo y "Avance Parcial" en Y2 derecho, ambos con escala %. v0.43.6 — Fix MpsProgressSCurve leyenda: selector CSS corregido de data-series-index a rel (1-based) para ocultar barras parciales — ApexCharts usa rel en ítems de leyenda. v0.43.5 — Fix MpsProgressSCurve: eje Y secundario para barras parciales implementado con 6 entradas YAxis + SeriesName binding (elimina InvalidOperationException por YAxisIndex no disponible como parámetro Blazor). 956 tests. v0.43.4 — MpsProgressSCurve: barras parciales en eje Y2, leyenda limpia (series bar ocultas via CSS), Forecast acum. anclado al último punto Real acum., Plan acum. línea continua. v0.43.3 — Fix tooltip JS error (shared+intersect). v0.43.2 — MpsEngProgressTable totales y bordes. v0.43.1/0.43.0 — MpsProgressSCurve: Curva S de avance físico, gráfico mixto ApexCharts. Fix MpsTimelineChart eje X. v0.43.0 — MpsProgressSCurve: Curva S de avance físico con gráfico mixto ApexCharts (barras periódicas + líneas acumuladas) para Plan/Real/Forecast. Paleta EVM canónica (#2E5BFF/#D92D20/#039855). Anotación ControlDate vertical y goal line 100%. Consumer provee parciales y acumulados explícitamente (ProgressSCurvePoint). Fix: MpsTimelineChart eje X corregido a XAxisType.Datetime. 956 tests totales. v0.42.0 — MpsEngProgressTable: tabla Plan/Forecast/Actual/Var por documento con hitos configurables. v0.41.0 — MpsDocCard + MpsDocRevisionList + MpsDocStatusBadge. v0.40.0 — MpsCalendar (FullCalendar 6). v0.39.0 — MpsHeatmapChart + MpsTimelineChart (ApexCharts). v0.34.0 — Skeleton composites para arquetipos de loading-state. Cinco componentes nuevos sobre MpsSkeleton cubren los arquetipos comunes: MpsSkeletonStack (N líneas para forms/párrafos), MpsSkeletonCard (header + body línea/rect para detail/charts), MpsSkeletonRow (avatar + título + subtítulo para listas), MpsSkeletonGrid (grilla de tiles para KPI strips, default 1x4), MpsSkeletonTable (toolbar + N filas para index pages, RowHeight default 2.5rem para match con MpsAutoGrid). Composición típica de dashboard reemplaza un <MpsSkeleton /> plano por <MpsSkeletonGrid /> + <MpsSkeletonCard Height="280px" /> + <MpsSkeletonTable /> — reduce layout shift y comunica visualmente qué tipo de contenido viene. Todos los composites consumen internamente <MpsSkeleton/> — heredan shimmer y prefers-reduced-motion. +37 tests bUnit (798 total). Sin breaking changes — aditivos sobre v0.33.0. Ver scripts/migrate-to-components-0.34.0.md para recetas. v0.33.0 — MpsSkeleton runtime fix. El componente rendereaba `<div class="mps-skeleton ...">` pero la regla CSS empacada era solo `.skeleton` — desde la primera versión. En 0.33.0 la clase se alinea (`.mps-skeleton`), `@keyframes shimmer` se namespacea como `mps-skeleton-shimmer`, se agregan defaults dimensionales por shape (line 100% × 0.875rem, rect 100% × 6rem, circle 2.5rem × 2.5rem) — `<MpsSkeleton />` sin parámetros ahora es visible. Soporte `prefers-reduced-motion: reduce` (WCAG 2.3.3). +7 tests bUnit (761 total). Sin breaking changes; consumers que aplicaron hotfix consumer-side (regla `.mps-skeleton` en `app.css`) pueden removerlo. v0.32.0 — MpsPageHeader sincroniza el browser tab title automáticamente. Nuevo `ThemeOptions.AppName` (configurable en AddMpsComponents) sirve como sufijo. Formato `{Title} · {AppName}` (middot). Override por instancia con `BrowserTabSuffix` (sufijo) y `BrowserTabTitle` (título distinto al header). Opt-out con `SetBrowserTab=false`. Helper estático `MpsPageHeader.ComputeBrowserTabTitle(...)` testeable. Fix de bug: la versión anterior tenía `<PageTitle>@Title - MOVES</PageTitle>` hardcodeado en una librería genérica — rompía consumers no-MOVES y filtraba el nombre del proyecto. DoubleProgress / TripleProgress: barras a 90% opacidad (antes 50%). +12 tests (754 total). Sin breaking de API; el browser tab default sigue activo (ahora correcto). v0.31.0 — CashflowChart leyenda con valores en CutOffDate. Reemplaza el header "totales" por una leyenda HTML con dos grupos: "Mes de cutoff" (Plan/Real/Forecast columnas — valor del mes de control, Forecast=total restante) y "Acumulado al cutoff" (Plan acum./Real acum./Forecast acum.=EAC). Si no hay CutOffDate, los valores son los totales del rango. Cada entrada con swatch que replica el estilo del chart (columnas opacidad 0.65, líneas sólidas, Forecast acum. dashed verde). Legend Syncfusion built-in se oculta — la nueva leyenda HTML usa MoneyDisplay para formato consistente con el resto del design system. Nuevo helper estático `ComputeLegendValues(raw, cutOffDate)` testeable. +3 tests (742 total). Sin breaking de API. v0.30.1 — CashflowChart palette EVM canónica. Plan=azul (#2E5BFF), Real=rojo (#D92D20), Forecast=verde (#039855) con la línea acumulada de Forecast en verde segmentado (DashArray 6,3). Las columnas mensuales usan los mismos hex al 75% de opacidad para diferenciarse visualmente de las líneas acumuladas (100%). Reemplaza la paleta inicial 0.30.0 (Plan azul, Real verde, Forecast naranja).
v0.30.0 — CashflowChart 2.0: extiende el componente con capa acumulada (3 series tipo línea sobre eje Y secundario) además de las columnas mensuales. Plan acum. cubre todo el rango; Real acum. ≤ CutOffDate; Forecast acum. arranca en el Real acum. del cutoff (continuidad visual con la línea Real) y luego suma los Forecast period-by-period. Las columnas Forecast quedan filtradas automáticamente a meses > CutOffDate (antes el caller debía nullear los pre-cutoff manualmente). Nuevo parámetro `ShowCumulative` (default `true`) controla la capa de líneas. Nuevo parámetro `CutOffDate` (alias retro-compat de `ControlDate`). Anchor robusto cuando CutOffDate no coincide con un punto exacto del dataset (cae en el último ≤ cutoff). +10 tests (739 total). Sin breaking changes; consumers que pasen `ShowCumulative="false"` mantienen el aspecto 0.29.x.
v0.29.0 — Tipografía dual MPS. Roboto Condensed para charts, tablas, valores numéricos, KPIs y códigos tabulares (Money/Counter/Range/Stat-card/Pagination/R9C/PO/Risk/Sensitivity/Lookahead). Inter para cuerpo, labels, títulos, botones — "el resto del contenido". Nuevos tokens CSS: --font-numeric, --font-table, --font-chart. Charts Syncfusion (SfChart, AccumulationChart, StockChart, RangeNavigator, Sparkline, Gauges, Gantt) y custom (mps-gauge, mps-bullet) reciben Roboto Condensed via SVG <text> rule. SfGrid + TreeGrid + .mps-table + .mps-po-table + .mps-deliv-table + .mps-risk-table + .mps-svt-table heredan font-table. Utility `.mono` re-apuntada a --font-numeric (los 30+ usos en razor — MoneyDisplay/MpsCounter/CurrencyInput/MpsGridColumn/etc — quedan correctos sin migración). Nuevo `.kbd` para mono real (JetBrains Mono kept). El @import del Google Fonts ahora trae Inter 300/400/500/600/700 + Roboto Condensed 300/400/500/700 + JetBrains Mono. Recomendado: el consumer precarga las fuentes con <link rel="preconnect"> en App.razor para LCP óptimo (ver PackageReadme). v0.28.1 — Patch fix de IOptions<ThemeOptions> en AddMpsComponents. v0.28.0 — Theme SSR support, elimina FOUC. Sin breaking changes en v0.29.0; sólo cambia la familia tipográfica visual.