@ng-solid/mcp 22.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GUIDE.md +292 -0
- package/README.md +159 -0
- package/data/design-system.json +24204 -0
- package/data/icons.json +1 -0
- package/data/skills/ns-solid-components/SKILL.md +140 -0
- package/data/skills/ns-solid-forms/SKILL.md +183 -0
- package/data/skills/ns-solid-icons/SKILL.md +95 -0
- package/data/skills/ns-solid-install/SKILL.md +120 -0
- package/data/skills/ns-solid-style-contract/SKILL.md +84 -0
- package/data/skills/ns-solid-theming/SKILL.md +123 -0
- package/index.d.ts +3 -0
- package/index.js +38 -0
- package/lib/data.d.ts +26 -0
- package/lib/data.js +51 -0
- package/lib/install-skills.d.ts +1 -0
- package/lib/install-skills.js +159 -0
- package/lib/preset-validator.d.ts +44 -0
- package/lib/preset-validator.js +172 -0
- package/lib/prompts.d.ts +2 -0
- package/lib/prompts.js +173 -0
- package/lib/resources.d.ts +2 -0
- package/lib/resources.js +328 -0
- package/lib/server.d.ts +3 -0
- package/lib/server.js +31 -0
- package/lib/style-contract.d.ts +37 -0
- package/lib/style-contract.js +167 -0
- package/lib/template-parser.d.ts +29 -0
- package/lib/template-parser.js +230 -0
- package/lib/tools.components.d.ts +44 -0
- package/lib/tools.components.js +836 -0
- package/lib/tools.d.ts +9 -0
- package/lib/tools.guides.d.ts +15 -0
- package/lib/tools.guides.js +103 -0
- package/lib/tools.icons.d.ts +7 -0
- package/lib/tools.icons.js +106 -0
- package/lib/tools.js +18 -0
- package/lib/tools.tokens.d.ts +7 -0
- package/lib/tools.tokens.js +278 -0
- package/lib/types.d.ts +349 -0
- package/lib/types.js +11 -0
- package/package.json +52 -0
- package/skills/ns-solid-components/SKILL.md +77 -0
- package/skills/ns-solid-forms/SKILL.md +166 -0
- package/skills/ns-solid-icons/SKILL.md +95 -0
- package/skills/ns-solid-install/SKILL.md +119 -0
- package/skills/ns-solid-style-contract/SKILL.md +84 -0
- package/skills/ns-solid-theming/SKILL.md +123 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ns-solid-components
|
|
3
|
+
description: Reference for @ng-solid/design-system's public component catalog — parent/child composition rules, service-driven components, and internal subcomponents that are not part of the public API.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Catálogo de componentes de ng-solid
|
|
7
|
+
|
|
8
|
+
## Tabla generada
|
|
9
|
+
|
|
10
|
+
| Selector | Clase | Familia | Categoría |
|
|
11
|
+
| --- | --- | --- | --- |
|
|
12
|
+
| `ns-accordion` | `NsAccordionComponent` | accordion | Contenido |
|
|
13
|
+
| `ns-accordion-item` | `NsAccordionItemComponent` | accordion | — |
|
|
14
|
+
| `ns-alert` | `NsAlertComponent` | alert | Feedback |
|
|
15
|
+
| `ns-avatar` | `NsAvatarComponent` | avatar | Contenido |
|
|
16
|
+
| `ns-badge` | `NsBadgeComponent` | badge | Feedback |
|
|
17
|
+
| `ns-breadcrumb-item` | `NsBreadcrumbItemComponent` | breadcrumbs | — |
|
|
18
|
+
| `ns-breadcrumbs` | `NsBreadcrumbsComponent` | breadcrumbs | Navegación |
|
|
19
|
+
| `ns-button` | `NsButtonComponent` | button | Acciones |
|
|
20
|
+
| `ns-button-group` | `NsButtonGroupComponent` | button-group | Acciones |
|
|
21
|
+
| `ns-card` | `NsCardComponent` | card | Superficies |
|
|
22
|
+
| `ns-card-body` | `NsCardBodyComponent` | card | — |
|
|
23
|
+
| `ns-card-footer` | `NsCardFooterComponent` | card | — |
|
|
24
|
+
| `ns-card-header` | `NsCardHeaderComponent` | card | — |
|
|
25
|
+
| `ns-checkbox` | `NsCheckboxComponent` | form/checkbox | Formularios |
|
|
26
|
+
| `ns-chip` | `NsChipComponent` | chip | Feedback |
|
|
27
|
+
| `ns-comment` | `NsCommentComponent` | comment | Formularios |
|
|
28
|
+
| `ns-content-editable` | `NsContentEditableComponent` | form/directives | Formularios |
|
|
29
|
+
| `ns-date-picker` | `NsDatePickerComponent` | form/date-picker | Formularios |
|
|
30
|
+
| `ns-date-range-picker` | `NsDateRangePickerComponent` | form/date-picker | Formularios |
|
|
31
|
+
| `ns-description` | `NsDescriptionComponent` | description | Accesibilidad |
|
|
32
|
+
| `ns-divider` | `NsDividerComponent` | divider | Contenido |
|
|
33
|
+
| `ns-drawer` | `NsDrawerComponent` | drawer | Overlays |
|
|
34
|
+
| `ns-dropdown` | `NsDropdownComponent` | form/dropdown | Formularios |
|
|
35
|
+
| `ns-dropdown-item` | `NsDropdownItemDirective` | form/dropdown | — |
|
|
36
|
+
| `ns-editor` | `NsEditorComponent` | editor | Formularios |
|
|
37
|
+
| `ns-editor-color-picker` | `NsEditorColorPickerComponent` | editor | — |
|
|
38
|
+
| `ns-editor-link` | `NsEditorLinkComponent` | editor | — |
|
|
39
|
+
| `ns-editor-list` | `NsEditorListComponent` | editor | — |
|
|
40
|
+
| `ns-editor-paragraph` | `NsEditorParagraphComponent` | editor | — |
|
|
41
|
+
| `ns-editor-toggle` | `NsEditorToggleCommandComponent` | editor | — |
|
|
42
|
+
| `[nsField]` | `NsFieldDirective` | a11y | — |
|
|
43
|
+
| `ns-image-upload` | `NsImageUploadComponent` | image-upload | Formularios |
|
|
44
|
+
| `ns-input` | `NsInputComponent` | form/input | Formularios |
|
|
45
|
+
| `ns-input-code` | `NsInputCodeComponent` | form/input-code | Formularios |
|
|
46
|
+
| `ns-input-group` | `NsInputGroupComponent` | form/input-group | Formularios |
|
|
47
|
+
| `ns-input-group-addon` | `NsInputGroupAddonComponent` | form/input-group | — |
|
|
48
|
+
| `ns-input-number` | `NsInputNumberComponent` | form/input-number | Formularios |
|
|
49
|
+
| `ns-input-search` | `NsInputSearchComponent` | form/input-search | Formularios |
|
|
50
|
+
| `ns-link` | `NsLinkComponent` | link | Navegación |
|
|
51
|
+
| `ns-meter` | `NsMeterComponent` | meter | Datos |
|
|
52
|
+
| `ns-modal` | `NsModalComponent` | modal | Overlays |
|
|
53
|
+
| `ns-pagination` | `NsPaginationComponent` | pagination | Navegación |
|
|
54
|
+
| `ns-password` | `NsPasswordComponent` | form/password | Formularios |
|
|
55
|
+
| `ns-popover` | `NsPopoverComponent` | popover | Overlays |
|
|
56
|
+
| `ns-progress-bar` | `NsProgressBarComponent` | progress-bar | Feedback |
|
|
57
|
+
| `ns-radio` | `NsRadioComponent` | form/radio | Formularios |
|
|
58
|
+
| `ns-radio-group` | `NsRadioGroupComponent` | form/radio | Formularios |
|
|
59
|
+
| `ns-schedule` | `NsScheduleComponent` | schedule | Datos |
|
|
60
|
+
| `ns-schema` | `NsSchemaMarkupComponent` | schema | — |
|
|
61
|
+
| `ns-skeleton` | `NsSkeletonComponent` | skeleton | Feedback |
|
|
62
|
+
| `ns-slider` | `NsSliderComponent` | form/slider | Formularios |
|
|
63
|
+
| `ns-surface` | `NsSurfaceComponent` | surface | Superficies |
|
|
64
|
+
| `ns-switch` | `NsSwitchComponent` | form/switch | Formularios |
|
|
65
|
+
| `ns-tab` | `NsTabComponent` | tabs | — |
|
|
66
|
+
| `ns-tabs` | `NsTabsComponent` | tabs | Navegación |
|
|
67
|
+
| `ns-tag` | `NsTagComponent` | tag-group | — |
|
|
68
|
+
| `ns-tag-group` | `NsTagGroupComponent` | tag-group | Formularios |
|
|
69
|
+
| `[nsTemplate]` | `TemplateDirective` | template | — |
|
|
70
|
+
| `ns-textarea` | `NsTextAreaComponent` | form/text-area | Formularios |
|
|
71
|
+
| `ns-toast` | `NsToastComponent` | toast | Feedback |
|
|
72
|
+
| `ns-tooltip` | `NsTooltipComponent` | tooltip | — |
|
|
73
|
+
| `[nsTooltip]` | `NsTooltipDirective` | tooltip | Overlays |
|
|
74
|
+
|
|
75
|
+
Todos se importan desde `@ng-solid/design-system` (entry point único —
|
|
76
|
+
ver la skill `ns-solid-install`) y son standalone: deben declararse en
|
|
77
|
+
`imports: [...]` de quien los usa.
|
|
78
|
+
|
|
79
|
+
## Reglas de composición padre → hijo
|
|
80
|
+
|
|
81
|
+
Estos pares no son componentes independientes: el hijo sólo tiene sentido
|
|
82
|
+
proyectado dentro del padre, y ambos deben estar en `imports`:
|
|
83
|
+
|
|
84
|
+
| Padre | Hijo(s) | Notas |
|
|
85
|
+
| ---------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
86
|
+
| `ns-tabs` | `ns-tab` | Cada `ns-tab` es una pestaña; `ns-tabs` las descubre vía `contentChildren(NsTabComponent)`. |
|
|
87
|
+
| `ns-card` | `ns-card-header`, `ns-card-body`, `ns-card-footer` | Tres selectores adicionales del mismo fichero `card.component.ts`; son slots con nombre, no requieren los tres a la vez. |
|
|
88
|
+
| `ns-accordion` | `ns-accordion-item` | `ns-accordion` gestiona `expandedKeys` como `model()`; los items son sólo presentación + trigger. |
|
|
89
|
+
| `ns-dropdown` | `ns-dropdown-item` | `ns-dropdown-item` es una **directiva** (`NsDropdownItemDirective`), no un componente — se aplica sobre el elemento que hace de opción. |
|
|
90
|
+
| `ns-tag-group` | `ns-tag` | Agrupa chips seleccionables/removibles. |
|
|
91
|
+
| `ns-breadcrumbs` | `ns-breadcrumb-item` | Cada nivel de la ruta es un `ns-breadcrumb-item`. |
|
|
92
|
+
| `ns-radio-group` | `ns-radio` | `ns-radio-group` es el `ControlValueAccessor` (hereda de `NsValueAccessor`, ver `ns-solid-forms`); `ns-radio` por sí solo **no** implementa CVA. |
|
|
93
|
+
|
|
94
|
+
`ns-input-group` es sólo un wrapper de layout — no tiene `label` ni `wrap`;
|
|
95
|
+
los inputs de formulario y sus labels van en el `ns-input`/`ns-input-number`
|
|
96
|
+
proyectado dentro (`ns-input-group-addon` para prefijos/sufijos).
|
|
97
|
+
|
|
98
|
+
## Componentes que se usan vía servicio
|
|
99
|
+
|
|
100
|
+
Estos no se instancian con una etiqueta en el template del consumidor — se
|
|
101
|
+
abren/gestionan imperativamente inyectando el servicio correspondiente:
|
|
102
|
+
|
|
103
|
+
- **`NsToastService`** → produce instancias de `ns-toast` (y el
|
|
104
|
+
subcomponente interno `ns-toast-region` que las hostea).
|
|
105
|
+
- **`NsModalService`** → abre `ns-modal`, con contenido proyectado o un
|
|
106
|
+
`contentComponent` tipado.
|
|
107
|
+
- **`NsDrawerService`** → abre `ns-drawer`.
|
|
108
|
+
- **`NsScheduleService`** → coordina el estado del calendario detrás de
|
|
109
|
+
`ns-schedule` y sus celdas/eventos internos.
|
|
110
|
+
|
|
111
|
+
`NsThemeService` (ver `ns-solid-theming`) y `NsOverlayService` (posicionamiento,
|
|
112
|
+
focus trap, scroll lock, `inert` — usado internamente por
|
|
113
|
+
popover/tooltip/drawer/modal) también son servicios públicos, pero no
|
|
114
|
+
"envuelven" un componente de la misma forma.
|
|
115
|
+
|
|
116
|
+
## Subcomponentes internos — no exportados
|
|
117
|
+
|
|
118
|
+
Existen en el código pero **no** están en el barrel público
|
|
119
|
+
(`visibility: 'internal'` en el snapshot). Un agente no debe importarlos ni
|
|
120
|
+
sugerirlos; si un usuario pregunta por ellos, la respuesta correcta es
|
|
121
|
+
señalar el componente público que los engloba:
|
|
122
|
+
|
|
123
|
+
- `ns-schedule-cell`, `ns-schedule-day`, `ns-schedule-event`,
|
|
124
|
+
`ns-schedule-week`, `ns-schedule-workweek` → usar `ns-schedule`.
|
|
125
|
+
- `ns-date-picker-calendar` → usar `ns-date-picker` / `ns-date-range-picker`.
|
|
126
|
+
- `ns-toast-region` → se instancia sólo a través de `NsToastService`, nunca
|
|
127
|
+
directamente.
|
|
128
|
+
|
|
129
|
+
## Variantes visuales — lenguaje HeroUI
|
|
130
|
+
|
|
131
|
+
Los nombres de variante son consistentes entre componentes:
|
|
132
|
+
|
|
133
|
+
- `solid` — relleno de acción sólido.
|
|
134
|
+
- `soft` — versión plana (`flat` en HeroUI).
|
|
135
|
+
- `outline` — bordeada (`bordered` en HeroUI).
|
|
136
|
+
- `ghost` — ligera (`light` en HeroUI).
|
|
137
|
+
|
|
138
|
+
Cada familia posee sus propios tokens `--ns-component-<familia>-*` — no
|
|
139
|
+
tomes prestados los tokens de otro componente aunque el valor visual
|
|
140
|
+
coincida.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ns-solid-forms
|
|
3
|
+
description: Build forms with @ng-solid/design-system's ControlValueAccessor controls — formControlName/[(value)] bindings, the 14 inputs inherited from NsValueAccessor, and [nsField] wiring.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Formularios con ng-solid
|
|
7
|
+
|
|
8
|
+
## Controles CVA y sus inputs heredados
|
|
9
|
+
|
|
10
|
+
| Selector | Clase | Inputs propios (además de los 14 heredados) |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| `ns-checkbox` | `NsCheckboxComponent` | — |
|
|
13
|
+
| `ns-content-editable` | `NsContentEditableComponent` | `maxLength` |
|
|
14
|
+
| `ns-date-picker` | `NsDatePickerComponent` | `firstDayOfWeek`, `maxDate`, `minDate`, `unavailableDates` |
|
|
15
|
+
| `ns-date-range-picker` | `NsDateRangePickerComponent` | `firstDayOfWeek`, `maxDate`, `minDate`, `unavailableDates` |
|
|
16
|
+
| `ns-dropdown` | `NsDropdownComponent` | `collection`, `emptyText`, `loading`, `placement`, `searchable`, `searchPlaceholder`, `selectionMode` |
|
|
17
|
+
| `ns-input` | `NsInputComponent` | — |
|
|
18
|
+
| `ns-input-code` | `NsInputCodeComponent` | `length` |
|
|
19
|
+
| `ns-input-number` | `NsInputNumberComponent` | `currency`, `format`, `max`, `min`, `step` |
|
|
20
|
+
| `ns-input-search` | `NsInputSearchComponent` | — |
|
|
21
|
+
| `ns-password` | `NsPasswordComponent` | `forgotLink`, `showAndHide` |
|
|
22
|
+
| `ns-radio-group` | `NsRadioGroupComponent` | — |
|
|
23
|
+
| `ns-slider` | `NsSliderComponent` | `max`, `min`, `range`, `step` |
|
|
24
|
+
| `ns-switch` | `NsSwitchComponent` | — |
|
|
25
|
+
| `ns-textarea` | `NsTextAreaComponent` | `rows` |
|
|
26
|
+
|
|
27
|
+
Todos heredan de `NsValueAccessor`: 14 `input()` (`type`, `placeholder`, `label`, `description`, `errorMessage`, `required`, `invalid`, `readonly`, `name`, `ariaLabel`, `ariaDescribedBy`, `size`, `color`, `variant`) más `disabled` y `value` como `model()`.
|
|
28
|
+
|
|
29
|
+
Todos implementan `ControlValueAccessor` extendiendo la clase base abstracta
|
|
30
|
+
`NsValueAccessor<T>`
|
|
31
|
+
(`packages/design-system/src/lib/form/models/value-accessor.ts`). Muchos
|
|
32
|
+
componentes (`NsInputComponent`, `NsSwitchComponent`, `NsCheckboxComponent`…)
|
|
33
|
+
tienen cuerpo de clase vacío — **toda** su API pública viene por herencia. Si
|
|
34
|
+
consultas la API de un control y ves 0 inputs propios, no significa que el
|
|
35
|
+
componente no acepte nada: revisa `inheritedFrom: 'NsValueAccessor'`.
|
|
36
|
+
|
|
37
|
+
`NsValueAccessor` expone, en todos los controles por igual:
|
|
38
|
+
|
|
39
|
+
- `type`, `placeholder`, `label`, `description`, `errorMessage`, `required`,
|
|
40
|
+
`invalid`, `readonly`, `name`, `ariaLabel`, `ariaDescribedBy` — `input()`.
|
|
41
|
+
- `size: 'sm' | 'md' | 'lg'` (default `'md'`), `color: 'neutral' | 'primary'
|
|
42
|
+
| 'success' | 'warning' | 'danger'` (default `'neutral'`), `variant:
|
|
43
|
+
'solid' | 'soft' | 'outline' | 'ghost'` (default `'outline'`) — `input()`.
|
|
44
|
+
- `disabled` — **`model()`**, no `input()`. Por eso `formControl.disable()` /
|
|
45
|
+
`.enable()` funciona de forma transparente: `setDisabledState` (parte del
|
|
46
|
+
contrato `ControlValueAccessor`) llama a `this.disabled.set(isDisabled)`
|
|
47
|
+
por debajo.
|
|
48
|
+
- `value` — también `model()`, escrito por Angular Forms vía `writeValue` y
|
|
49
|
+
leído/emitido por el propio control con `setValue()`.
|
|
50
|
+
|
|
51
|
+
## Bindings — los tres equivalentes
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<!-- Reactive Forms -->
|
|
55
|
+
<ns-input formControlName="email" label="Correo" />
|
|
56
|
+
|
|
57
|
+
<!-- Template-driven -->
|
|
58
|
+
<ns-input [(ngModel)]="email" label="Correo" />
|
|
59
|
+
|
|
60
|
+
<!-- Binding directo al model, sin FormsModule -->
|
|
61
|
+
<ns-input [(value)]="email" label="Correo" />
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Los tres funcionan porque el control implementa `ControlValueAccessor` (los
|
|
65
|
+
dos primeros) y expone `value` como `model()` (el tercero). Para
|
|
66
|
+
`disabled` como two-way binding equivalente: `[(disabled)]="isDisabled"`.
|
|
67
|
+
|
|
68
|
+
## `[nsField]` — vincular label, descripción y error
|
|
69
|
+
|
|
70
|
+
`NsFieldDirective` (selector `[nsField]`,
|
|
71
|
+
`packages/design-system/src/lib/a11y/field-context.ts`) provee un
|
|
72
|
+
`NsFieldContext` al subárbol. El control lo consume vía `inject(NsFieldContext,
|
|
73
|
+
{ optional: true, skipSelf: true })` para resolver su `id` real
|
|
74
|
+
(`controlId()`), y componentes como `NsDescriptionComponent` (hint) se
|
|
75
|
+
registran contra el mismo contexto para el `aria-describedby`:
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<div nsField="email">
|
|
79
|
+
<ns-input formControlName="email" label="Correo" />
|
|
80
|
+
<ns-description id="email-hint"
|
|
81
|
+
>Usaremos este correo para notificarte.</ns-description
|
|
82
|
+
>
|
|
83
|
+
</div>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Sin `[nsField]`, cada control genera su propio id vía `NsAriaIdService`; con
|
|
87
|
+
él, el id explícito (`nsField="email"`) gana y el `aria-describedby` se
|
|
88
|
+
compone automáticamente con las descripciones registradas en el contexto.
|
|
89
|
+
|
|
90
|
+
## `errorMessage` + `invalid`
|
|
91
|
+
|
|
92
|
+
El mensaje de error sólo se renderiza cuando **ambos** están presentes:
|
|
93
|
+
`invalid()` es `true` y `errorMessage()` no es vacío/undefined (ver
|
|
94
|
+
`input.component.html`: `@if (invalid() && errorMessage())`). Calcula
|
|
95
|
+
`invalid` desde el estado del `FormControl`, no lo dejes fijo:
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<ns-input
|
|
99
|
+
[invalid]="control.invalid && control.touched"
|
|
100
|
+
[errorMessage]="
|
|
101
|
+
control.hasError('required') ? 'Este campo es obligatorio' : undefined
|
|
102
|
+
"
|
|
103
|
+
/>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Ejemplo completo con `FormBuilder`
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { Component, inject } from '@angular/core';
|
|
110
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
111
|
+
import {
|
|
112
|
+
NsButtonComponent,
|
|
113
|
+
NsFieldDirective,
|
|
114
|
+
NsInputComponent,
|
|
115
|
+
NsPasswordComponent,
|
|
116
|
+
NsSwitchComponent
|
|
117
|
+
} from '@ng-solid/design-system';
|
|
118
|
+
|
|
119
|
+
@Component({
|
|
120
|
+
selector: 'app-signup-form',
|
|
121
|
+
standalone: true,
|
|
122
|
+
imports: [
|
|
123
|
+
ReactiveFormsModule,
|
|
124
|
+
NsFieldDirective,
|
|
125
|
+
NsInputComponent,
|
|
126
|
+
NsPasswordComponent,
|
|
127
|
+
NsSwitchComponent,
|
|
128
|
+
NsButtonComponent
|
|
129
|
+
],
|
|
130
|
+
template: `
|
|
131
|
+
<form [formGroup]="form" (ngSubmit)="submit()">
|
|
132
|
+
<div nsField="signup-email">
|
|
133
|
+
<ns-input
|
|
134
|
+
formControlName="email"
|
|
135
|
+
label="Correo"
|
|
136
|
+
type="email"
|
|
137
|
+
[invalid]="form.controls.email.invalid && form.controls.email.touched"
|
|
138
|
+
[errorMessage]="'Ingresa un correo válido'"
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div nsField="signup-password">
|
|
143
|
+
<ns-password
|
|
144
|
+
formControlName="password"
|
|
145
|
+
label="Contraseña"
|
|
146
|
+
[invalid]="
|
|
147
|
+
form.controls.password.invalid && form.controls.password.touched
|
|
148
|
+
"
|
|
149
|
+
[errorMessage]="'Mínimo 8 caracteres'"
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<ns-switch formControlName="acceptTerms" label="Acepto los términos" />
|
|
154
|
+
|
|
155
|
+
<ns-button variant="primary" type="submit" [disabled]="form.invalid">
|
|
156
|
+
Crear cuenta
|
|
157
|
+
</ns-button>
|
|
158
|
+
</form>
|
|
159
|
+
`
|
|
160
|
+
})
|
|
161
|
+
export class SignupFormComponent {
|
|
162
|
+
private readonly fb = inject(FormBuilder);
|
|
163
|
+
|
|
164
|
+
readonly form = this.fb.group({
|
|
165
|
+
email: this.fb.control('', [Validators.required, Validators.email]),
|
|
166
|
+
password: this.fb.control('', [
|
|
167
|
+
Validators.required,
|
|
168
|
+
Validators.minLength(8)
|
|
169
|
+
]),
|
|
170
|
+
acceptTerms: this.fb.control(false, [Validators.requiredTrue])
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
submit(): void {
|
|
174
|
+
if (this.form.valid) {
|
|
175
|
+
// this.form.getRawValue()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Nota: `NsPasswordComponent` añade `forgotLink` y `showAndHide` propios sobre
|
|
182
|
+
la base heredada; `NsSwitchComponent` no añade inputs propios (todo viene de
|
|
183
|
+
`NsValueAccessor`).
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ns-solid-icons
|
|
3
|
+
description: Render Lucide icons with @ng-solid/icons' <ns-icon> component — registering symbols with provideIcons/provideIconsPatch and resolving camelCase symbol names.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Iconos con @ng-solid/icons
|
|
7
|
+
|
|
8
|
+
## El componente `<ns-icon>`
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<ns-icon name="alarm-clock" [size]="20" />
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`NsIconsComponent` (selector `ns-icon`) acepta:
|
|
15
|
+
|
|
16
|
+
- `name: string` — el nombre **kebab-case** del icono Lucide (`alarm-clock`,
|
|
17
|
+
no `alarmClock`).
|
|
18
|
+
- `size: number` (default `24`) — acepta el atributo plano (`numberAttribute`),
|
|
19
|
+
así que `<ns-icon name="x" size="16">` también funciona.
|
|
20
|
+
- `color: string` (default `'currentColor'`).
|
|
21
|
+
- `strokeWidth: number` (default `2`).
|
|
22
|
+
- `viewBox: string` (default `'0 0 24 24'`).
|
|
23
|
+
|
|
24
|
+
Internamente resuelve el SVG contra un `Map` poblado por
|
|
25
|
+
`provideIcons`/`provideIconsPatch`; si el nombre no fue registrado, emite un
|
|
26
|
+
`console.warn` y no renderiza el path.
|
|
27
|
+
|
|
28
|
+
## Registrar iconos — dos providers, dos momentos distintos
|
|
29
|
+
|
|
30
|
+
**`provideIcons(icons)`** — en el bootstrap de la app (`appConfig.providers`
|
|
31
|
+
o `bootstrapApplication`). Usar para los iconos que se necesitan siempre:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { provideIcons, alarmClock, x } from '@ng-solid/icons';
|
|
35
|
+
|
|
36
|
+
export const appConfig: ApplicationConfig = {
|
|
37
|
+
providers: [
|
|
38
|
+
// ...
|
|
39
|
+
provideIcons([alarmClock, x])
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**`provideIconsPatch(icons)`** — a nivel de `Route.providers`, para iconos
|
|
45
|
+
que sólo hace falta cargar cuando se navega a esa ruta (lazy):
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
const routes: Routes = [
|
|
49
|
+
{
|
|
50
|
+
path: 'products',
|
|
51
|
+
providers: [provideIconsPatch([alarmClock])],
|
|
52
|
+
loadComponent: () =>
|
|
53
|
+
import('./products.component').then((m) => m.ProductsComponent)
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`provideIconsPatch` registra además `NsIconsPatchService`, que
|
|
59
|
+
`NsIconsComponent` detecta e invoca (`doPatch()`) antes de intentar resolver
|
|
60
|
+
el icono, para que el registro parcial se aplique aunque el componente ya
|
|
61
|
+
esté instanciado.
|
|
62
|
+
|
|
63
|
+
## Los símbolos se importan en camelCase
|
|
64
|
+
|
|
65
|
+
El catálogo generado (`packages/icons/src/lib/generated/index.ts`) exporta
|
|
66
|
+
un símbolo por icono con el nombre kebab-case convertido a camelCase:
|
|
67
|
+
|
|
68
|
+
| Icono (`name` en `<ns-icon>`) | Símbolo importado de `@ng-solid/icons` |
|
|
69
|
+
| --------------------------------- | -------------------------------------- |
|
|
70
|
+
| `alarm-clock` | `alarmClock` |
|
|
71
|
+
| `a-arrow-down` | `aArrowDown` |
|
|
72
|
+
| `align-horizontal-justify-center` | `alignHorizontalJustifyCenter` |
|
|
73
|
+
| `circle-check` | `circleCheck` |
|
|
74
|
+
|
|
75
|
+
Cada símbolo es un objeto `{ name, icon }` (ver `NsIconDefinition`) — nunca
|
|
76
|
+
pases un string al provider, pasa el símbolo importado.
|
|
77
|
+
|
|
78
|
+
## Catálogo
|
|
79
|
+
|
|
80
|
+
**1790 iconos** Lucide registrados en el catálogo (`NS_ICON_COUNT`), licencia ISC.
|
|
81
|
+
|
|
82
|
+
- Fuente: Lucide (`LUCIDE_SOURCE_URL` en `packages/icons/src/lib/generated/metadata.ts`).
|
|
83
|
+
- Licencia: **ISC** (`LUCIDE_SOURCE_LICENSE`).
|
|
84
|
+
- Usa la tool `search_icons` del MCP para encontrar el `name`/símbolo correcto
|
|
85
|
+
por palabra clave en vez de adivinar el nombre exacto de Lucide.
|
|
86
|
+
|
|
87
|
+
## Errores comunes
|
|
88
|
+
|
|
89
|
+
- Registrar el símbolo pero usar un `name` distinto en el template (deben
|
|
90
|
+
coincidir exactamente, kebab-case).
|
|
91
|
+
- Olvidar `provideIcons`/`provideIconsPatch` por completo — el icono no
|
|
92
|
+
truena, simplemente no renderiza y deja un warning en consola.
|
|
93
|
+
- Confundir `provideIcons` (bootstrap, global) con `provideIconsPatch`
|
|
94
|
+
(`Route.providers`, por ruta) — usar el segundo en el bootstrap no falla,
|
|
95
|
+
pero pierde el propósito de carga diferida.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ns-solid-install
|
|
3
|
+
description: Install and bootstrap @ng-solid/design-system (Arvisfy) plus @ng-solid/icons in an Angular 21 standalone application, including the Tailwind bridge and theme provider.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Instalación de @ng-solid/design-system
|
|
7
|
+
|
|
8
|
+
## Paquetes y peer dependencies
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add @ng-solid/design-system @ng-solid/icons
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`@ng-solid/design-system` declara estos peer dependencies (ver
|
|
15
|
+
`packages/design-system/package.json`):
|
|
16
|
+
|
|
17
|
+
- `@angular/core`, `@angular/common`, `@angular/forms`,
|
|
18
|
+
`@angular/platform-browser`, `@angular/router` en `^21.0.0`.
|
|
19
|
+
- `@ng-solid/icons` en `^22.0.5`.
|
|
20
|
+
- `tailwindcss` en `^4.0.0`, marcado **opcional** (`peerDependenciesMeta`) —
|
|
21
|
+
sólo hace falta si la app consume el puente de utilidades semánticas.
|
|
22
|
+
|
|
23
|
+
Versiones publicadas actualmente:
|
|
24
|
+
|
|
25
|
+
- `@ng-solid/design-system@22.2.1` (peer `@angular/*@^21.0.0`)
|
|
26
|
+
- `@ng-solid/icons@22.1.0`
|
|
27
|
+
|
|
28
|
+
Si tu app usa Tailwind, añade también el plugin de PostCSS:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add -D tailwindcss @tailwindcss/postcss
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Entrypoint único — nada de subpaths
|
|
35
|
+
|
|
36
|
+
`@ng-solid/design-system` tiene **un solo entry point público**
|
|
37
|
+
(`ng-package.json` → `entryFile: src/index.ts`, sin `secondaryEntrypoints`).
|
|
38
|
+
Todo se importa desde la raíz del paquete:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { NsButtonComponent, NsInputComponent } from '@ng-solid/design-system';
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Nunca inventes rutas como `@ng-solid/design-system/button` — no existen. La
|
|
45
|
+
única excepción es el asset CSS publicado explícitamente como
|
|
46
|
+
`@ng-solid/design-system/themes/tailwind.css` (ver más abajo), que no es un
|
|
47
|
+
entry point de JS/TS.
|
|
48
|
+
|
|
49
|
+
## Puente de Tailwind
|
|
50
|
+
|
|
51
|
+
Si la app importa el puente semántico, hazlo una sola vez en el stylesheet
|
|
52
|
+
global, después de las capas de Tailwind:
|
|
53
|
+
|
|
54
|
+
```css
|
|
55
|
+
@import '@ng-solid/design-system/themes/tailwind.css';
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Ese fichero define el bridge `@theme inline` que mapea los tokens de
|
|
59
|
+
Tailwind a `--ns-semantic-*`, además de registrar localmente la fuente
|
|
60
|
+
variable Inter (sin peticiones de red en runtime).
|
|
61
|
+
|
|
62
|
+
## Registrar el tema en `appConfig`
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { Arvisfy, provideNsTheme } from '@ng-solid/design-system';
|
|
66
|
+
import { ApplicationConfig } from '@angular/core';
|
|
67
|
+
|
|
68
|
+
export const appConfig: ApplicationConfig = {
|
|
69
|
+
providers: [provideNsTheme({ preset: Arvisfy, colorScheme: 'system' })]
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`colorScheme: 'system'` sigue `prefers-color-scheme`. Ver la skill
|
|
74
|
+
`ns-solid-theming` para `defineNsPreset`, `NsThemeService.setScheme` y las
|
|
75
|
+
reglas de validación del preset.
|
|
76
|
+
|
|
77
|
+
## Standalone: `imports` es obligatorio
|
|
78
|
+
|
|
79
|
+
Todos los componentes son **standalone**. Angular no los resuelve
|
|
80
|
+
automáticamente: hay que declararlos explícitamente en el `imports` del
|
|
81
|
+
componente (o del `NgModule`) que los usa. Omitir esto es el error más común
|
|
82
|
+
al generar código con un agente:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { Component } from '@angular/core';
|
|
86
|
+
import { NsButtonComponent } from '@ng-solid/design-system';
|
|
87
|
+
|
|
88
|
+
@Component({
|
|
89
|
+
selector: 'app-toolbar',
|
|
90
|
+
standalone: true,
|
|
91
|
+
imports: [NsButtonComponent], // obligatorio — <ns-button> no funciona sin esto
|
|
92
|
+
template: `<ns-button variant="primary">Guardar</ns-button>`
|
|
93
|
+
})
|
|
94
|
+
export class ToolbarComponent {}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Esto vale también para directivas de composición (`NsFieldDirective`,
|
|
98
|
+
`NsTooltipDirective`, `NsDropdownItemDirective`, etc.) y para componentes
|
|
99
|
+
usados vía `contentChildren`/proyección (`ns-card-header`, `ns-tab`,
|
|
100
|
+
`ns-accordion-item`…): todos deben aparecer en `imports`.
|
|
101
|
+
|
|
102
|
+
## Iconos
|
|
103
|
+
|
|
104
|
+
`@ng-solid/icons` se instala aparte y se registra con `provideIcons(...)` en
|
|
105
|
+
el bootstrap. Ver la skill `ns-solid-icons` para el detalle completo
|
|
106
|
+
(símbolos camelCase, `provideIconsPatch` para rutas con lazy loading).
|
|
107
|
+
|
|
108
|
+
## Extender el preset
|
|
109
|
+
|
|
110
|
+
No copies `Arvisfy`; extiéndelo con `defineNsPreset` (ver `ns-solid-theming`
|
|
111
|
+
para las reglas de validación completas):
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { Arvisfy, defineNsPreset } from '@ng-solid/design-system';
|
|
115
|
+
|
|
116
|
+
export const ProductTheme = defineNsPreset(Arvisfy, {
|
|
117
|
+
name: 'product',
|
|
118
|
+
primitive: { color: { brand: { 500: '#0052ff' } } }
|
|
119
|
+
});
|
|
120
|
+
```
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ns-solid-style-contract
|
|
3
|
+
description: Follow ng-solid's enforced style architecture — ns-* BEM class naming, zero Tailwind utilities in markup, and the @reference-only SCSS contract checked by pnpm quality:styles.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contrato de estilos de ng-solid
|
|
7
|
+
|
|
8
|
+
Este contrato lo enforcea en CI
|
|
9
|
+
`tools/scripts/check-design-system-styles.mjs` (`pnpm quality:styles`) sobre
|
|
10
|
+
`packages/design-system/src/lib` y `apps/demo/src`. Un PR que lo viole falla
|
|
11
|
+
el build, tanto en la librería como en aplicaciones que sigan la misma
|
|
12
|
+
convención.
|
|
13
|
+
|
|
14
|
+
## 1. Clases de template: sólo `ns-*` BEM
|
|
15
|
+
|
|
16
|
+
Toda clase en `class="…"`, `[class.x]` o claves de objeto `[ngClass]` debe
|
|
17
|
+
matchear:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
ns-block__element--modifier
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Regex real usada por el gate:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
/^ns-[a-z0-9]+(?:-[a-z0-9]+)*(?:(?:__[a-z0-9]+(?:-[a-z0-9]+)*)?(?:--[a-z0-9]+(?:-[a-z0-9]+)*)?)?$/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Válido: `ns-button`, `ns-button__icon`, `ns-button--danger`,
|
|
30
|
+
`ns-card__header--sticky`. Inválido: `btn-primary`, `NsButton`,
|
|
31
|
+
`ns_button`, `flex`.
|
|
32
|
+
|
|
33
|
+
## 2. Cero utilidades Tailwind en markup
|
|
34
|
+
|
|
35
|
+
Ninguna clase de template puede empezar con un prefijo de utilidad Tailwind
|
|
36
|
+
(`flex`, `grid`, `gap-`, `p-`/`m-` + variantes, `text-`, `bg-`, `border`,
|
|
37
|
+
`rounded`, `shadow`, `absolute`/`relative`/`fixed`/`sticky`, `inset`/`top-`/
|
|
38
|
+
`bottom-`/`start-`/`end-`, `z-`, `w-`/`h-`, `min-`/`max-`, `overflow`,
|
|
39
|
+
`opacity`, `font-`, `items-`/`justify-`/`content-`). Tailwind se compone
|
|
40
|
+
**sólo** desde SCSS con `@apply`, nunca directamente en el HTML.
|
|
41
|
+
|
|
42
|
+
## 3. Nada de `styles:` inline
|
|
43
|
+
|
|
44
|
+
Un componente no puede declarar estilos inline en el decorador
|
|
45
|
+
(`styles: [...]`). Usa `styleUrl` (o `styleUrls`) apuntando a un `.scss`
|
|
46
|
+
hermano.
|
|
47
|
+
|
|
48
|
+
## 4. SCSS de componente: `@reference`, sin `@use`/`@import`, sin valores crudos
|
|
49
|
+
|
|
50
|
+
Cada `.scss` de componente (fuera de `lib/theme/`) debe:
|
|
51
|
+
|
|
52
|
+
- Incluir literalmente `@reference` (al bridge de Tailwind:
|
|
53
|
+
`@reference '../theme/tailwind.css';` o la ruta relativa que corresponda).
|
|
54
|
+
- **No** usar `@use` ni `@import` — extender Tailwind sólo vía `@reference`.
|
|
55
|
+
- **No** contener valores crudos: hex (`#fff`), `rgb()`/`rgba()`, literales
|
|
56
|
+
`px`/`rem`/`em`/`ms`/`s`, o `var(--x, fallback)` con valor por defecto.
|
|
57
|
+
Toda decisión visual se lee de un token `--ns-component-*` ya resuelto por
|
|
58
|
+
el preset (ver la skill `ns-solid-theming`) — sin fallback embebido, porque
|
|
59
|
+
el preset garantiza que la variable siempre está definida.
|
|
60
|
+
|
|
61
|
+
Ejemplo correcto:
|
|
62
|
+
|
|
63
|
+
```scss
|
|
64
|
+
@reference '../theme/tailwind.css';
|
|
65
|
+
|
|
66
|
+
.ns-button {
|
|
67
|
+
@apply inline-flex items-center;
|
|
68
|
+
background: var(--ns-component-button-background);
|
|
69
|
+
color: var(--ns-component-button-foreground);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 5. Prohibido reintroducir Bootstrap / `@popperjs`
|
|
74
|
+
|
|
75
|
+
El gate falla si `package.json` o `pnpm-lock.yaml` vuelven a traer
|
|
76
|
+
`bootstrap` o `@popperjs/*` — el design-system fue migrado fuera de esas
|
|
77
|
+
dependencias deliberadamente.
|
|
78
|
+
|
|
79
|
+
## Validar un snippet antes de proponerlo
|
|
80
|
+
|
|
81
|
+
Usa la tool del MCP `validate_style_contract` (o `validate_component_usage`
|
|
82
|
+
para un uso completo de componente) contra cualquier markup/SCSS que generes
|
|
83
|
+
antes de entregarlo — es la misma lógica que corre `pnpm quality:styles`,
|
|
84
|
+
expuesta como función pura.
|