@ng-solid/design-system 22.3.0 → 22.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-solid/design-system",
3
- "version": "22.3.0",
3
+ "version": "22.4.0",
4
4
  "license": "MIT",
5
5
  "description": "Arvisfy, the token-driven Angular design system for ng-solid.",
6
6
  "author": "Ulises Familia Rodríguez <chicromedia@hotmail.com>",
package/themes/README.md CHANGED
@@ -53,11 +53,51 @@ export const ProductTheme = defineNsPreset(Arvisfy, {
53
53
 
54
54
  Tailwind utilities are composed from SCSS, never placed in ng-solid component
55
55
  templates. Consumers that want the same semantic utility bridge may import
56
- `@ng-solid/design-system/themes/tailwind.css` after Tailwind.
56
+ `@ng-solid/design-system/themes/tailwind.css` after Tailwind; doing so also
57
+ applies the box-sizing contract described below, though the kit assumes that
58
+ contract regardless of whether the bridge is imported (see "Box model").
57
59
 
58
60
  Every component stylesheet starts with `@reference` to that bridge and reads
59
61
  only `--ns-component-*` variables for component-specific visual decisions.
60
62
 
63
+ ## Box model
64
+
65
+ Arvisfy assumes `box-sizing: border-box` everywhere. `theme/tailwind.css`
66
+ restates the `box-sizing` declaration from Tailwind v4's preflight, on that
67
+ preflight's exact selector:
68
+
69
+ ```css
70
+ *,
71
+ ::before,
72
+ ::after,
73
+ ::backdrop,
74
+ ::file-selector-button {
75
+ box-sizing: border-box;
76
+ }
77
+ ```
78
+
79
+ It is copied literally rather than `@import`ed, because ng-packagr ships this
80
+ file as a plain asset — an `@import` would reach the consumer unresolved. The
81
+ rest of preflight's box-sizing block (`margin/padding/border: 0` on every
82
+ element) is intentionally left out: unlike this single, additive rule, it is
83
+ destructive to markup that doesn't expect it.
84
+
85
+ Three properties follow from that choice:
86
+
87
+ - **Idempotent.** Applications that already load Tailwind's own preflight (or
88
+ any other reset that sets the same rule) see no change — selector and
89
+ declaration are identical.
90
+ - **Zero added specificity.** The universal selector `*` (and the
91
+ pseudo-elements listed alongside it) contributes no specificity, so any
92
+ consumer rule, however plain, overrides it trivially. It is a default, not
93
+ an imposition.
94
+ - **Opt-in scope.** Consumers who don't import the bridge are covered only
95
+ where a component's own SCSS declares `box-sizing` locally; the
96
+ workspace-wide default lives solely inside `tailwind.css`.
97
+
98
+ An audit across 53 playground pages found that adopting this rule eliminated
99
+ 17 real overflow bugs and introduced none.
100
+
61
101
  ## HeroUI foundations
62
102
 
63
103
  Arvisfy vendors Inter Variable in `theme/fonts/InterVariable.woff2` (with its
@@ -73,3 +113,34 @@ maps to `light`. Components own their state tokens (for example
73
113
  `--ns-component-slider-*` and `--ns-component-input-search-*`) rather than
74
114
  borrowing another component's variables; use the matching family when adding
75
115
  a visual decision.
116
+
117
+ ## Retired tokens
118
+
119
+ A token is retired when no component stylesheet reads its `--ns-component-*`
120
+ variable anymore, but it still ships as a published field of the preset —
121
+ removing the field outright would silently break any consumer override that
122
+ still targets it. `NsTokenTree` has an open index signature, so an override
123
+ of a retired (or otherwise unknown) key merges in cleanly with no type error
124
+ and no `validateNsPreset` error; it just has no effect.
125
+
126
+ To surface that instead of letting it fail silently, `theme/utils/theme.utils.ts`
127
+ keeps a small internal `NS_RETIRED_TOKENS` map of retired token paths to a
128
+ warning message, and `validateNsPreset` emits a `console.warn` for each one
129
+ still present in the resolved preset. This does not throw — an inert override
130
+ should not crash a consumer's app — and it is not deduplicated across calls,
131
+ so it warns every time a preset containing the retired path is validated.
132
+
133
+ The registry lives inside `theme.utils.ts` itself rather than its own file:
134
+ `packages/mcp`'s data extraction transpiles that file in isolation and cannot
135
+ resolve a relative value import from a sibling module at that point (see
136
+ `assertNoValueImports` in `packages/mcp/scripts/lib/extract-tokens.mjs`), so a
137
+ separate `retired-tokens.ts` imported for its data would break
138
+ `nx run mcp:build-data`.
139
+
140
+ Currently retired:
141
+
142
+ - `components.imageUpload.thumbnailSize` — used to size each stack thumbnail
143
+ and (indirectly) the height of `ns-image-upload[layout="cover"]`. Since the
144
+ height fix in AVF-29, `components.imageUpload.emptyMinHeight` alone sizes
145
+ that layout in both its empty and filled states, and no stylesheet reads
146
+ `--ns-component-image-upload-thumbnail-size` anymore.
@@ -1,3 +1,18 @@
1
+ /* This bridge ships as a raw asset (ng-package.json) and ng-packagr copies it
2
+ byte-for-byte to consumers, with no PostCSS pass — so it cannot `@import`
3
+ Tailwind's preflight (that import would reach consumers unresolved).
4
+ Instead this literally reproduces just the `box-sizing: border-box` line
5
+ from Tailwind v4's preflight (node_modules/tailwindcss/preflight.css),
6
+ intentionally omitting the accompanying margin/padding/border reset, which
7
+ would be destructive to existing markup. */
8
+ *,
9
+ ::before,
10
+ ::after,
11
+ ::backdrop,
12
+ ::file-selector-button {
13
+ box-sizing: border-box;
14
+ }
15
+
1
16
  @font-face {
2
17
  font-family: 'Inter Variable';
3
18
  font-style: normal;
@@ -122,7 +122,7 @@ declare class NsBreadcrumbsComponent {
122
122
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsBreadcrumbsComponent, "ns-breadcrumbs", never, { "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
123
123
  }
124
124
 
125
- type NsButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'success' | 'danger';
125
+ type NsButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'success' | 'danger' | 'dark';
126
126
  declare class NsButtonComponent {
127
127
  readonly type: _angular_core.InputSignal<"button" | "submit" | "reset">;
128
128
  readonly variant: _angular_core.InputSignal<NsButtonVariant>;
@@ -199,7 +199,7 @@ type NsChipColor = NsColor | 'info' | 'selected';
199
199
  * `ns-chip` and intentionally not added to the shared `NsVariant` union.
200
200
  */
201
201
  type NsChipVariant = Exclude<NsVariant, 'ghost'> | 'tint';
202
- type NsChipSize = 'md' | 'pill';
202
+ type NsChipSize = 'md' | 'pill' | 'choice';
203
203
  declare class NsChipComponent {
204
204
  readonly color: _angular_core.InputSignal<NsChipColor>;
205
205
  readonly variant: _angular_core.InputSignal<NsChipVariant>;
@@ -505,6 +505,7 @@ declare class NsEditorColorPickerComponent implements OnInit {
505
505
  private editor;
506
506
  readonly presets: _angular_core.InputSignal<string[][]>;
507
507
  readonly type: _angular_core.InputSignal<"text_color" | "background_color">;
508
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
508
509
  showPopup: boolean;
509
510
  activeColor?: string;
510
511
  isActive: boolean;
@@ -516,10 +517,23 @@ declare class NsEditorColorPickerComponent implements OnInit {
516
517
  onColorSelect(color: string): void;
517
518
  remove(): void;
518
519
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsEditorColorPickerComponent, never>;
519
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorColorPickerComponent, "ns-editor-color-picker", never, { "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
520
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorColorPickerComponent, "ns-editor-color-picker", never, { "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
521
+ }
522
+
523
+ declare class NsEditorImageComponent implements OnInit {
524
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
525
+ formImage: FormGroup;
526
+ show: boolean;
527
+ private fb;
528
+ private editor;
529
+ ngOnInit(): void;
530
+ insert(): void;
531
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsEditorImageComponent, never>;
532
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorImageComponent, "ns-editor-image", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
520
533
  }
521
534
 
522
535
  declare class NsEditorLinkComponent implements OnInit {
536
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
523
537
  formLink: FormGroup;
524
538
  show: boolean;
525
539
  private fb;
@@ -527,7 +541,7 @@ declare class NsEditorLinkComponent implements OnInit {
527
541
  ngOnInit(): void;
528
542
  insert(): void;
529
543
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsEditorLinkComponent, never>;
530
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorLinkComponent, "ns-editor-link", never, {}, {}, never, never, true, never>;
544
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorLinkComponent, "ns-editor-link", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
531
545
  }
532
546
 
533
547
  declare enum Commands {
@@ -554,29 +568,32 @@ declare enum Commands {
554
568
 
555
569
  declare class NsEditorListComponent implements OnInit {
556
570
  private editor;
571
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
557
572
  commands: typeof Commands;
558
573
  ngOnInit(): void;
559
574
  command(name: Commands): void;
560
575
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsEditorListComponent, never>;
561
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorListComponent, "ns-editor-list", never, {}, {}, never, never, true, never>;
576
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorListComponent, "ns-editor-list", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
562
577
  }
563
578
 
564
579
  declare class NsEditorParagraphComponent {
565
580
  private editor;
581
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
566
582
  id: string;
567
583
  private _value;
568
584
  get value(): string;
569
585
  set value(heading: string);
570
586
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsEditorParagraphComponent, never>;
571
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorParagraphComponent, "ns-editor-paragraph", never, {}, {}, never, never, true, never>;
587
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorParagraphComponent, "ns-editor-paragraph", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
572
588
  }
573
589
 
574
590
  declare class NsEditorToggleCommandComponent {
575
591
  private editor;
592
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
576
593
  commands: typeof Commands;
577
594
  command(name: Commands): void;
578
595
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsEditorToggleCommandComponent, never>;
579
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorToggleCommandComponent, "ns-editor-toggle", never, {}, {}, never, never, true, never>;
596
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorToggleCommandComponent, "ns-editor-toggle", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
580
597
  }
581
598
 
582
599
  declare abstract class NsValueAccessor<T = string> implements ControlValueAccessor {
@@ -829,6 +846,7 @@ declare class NsInputNumberComponent extends NsValueAccessor<number | null> {
829
846
  readonly currency: _angular_core.InputSignal<string>;
830
847
  readonly format: _angular_core.InputSignal<Intl.NumberFormatOptions>;
831
848
  readonly displayValue: _angular_core.WritableSignal<string>;
849
+ readonly currencyPrefix: _angular_core.Signal<string>;
832
850
  writeValue(value: number | null): void;
833
851
  onInput(text: string): void;
834
852
  onInputEvent(event: Event): void;
@@ -836,6 +854,7 @@ declare class NsInputNumberComponent extends NsValueAccessor<number | null> {
836
854
  onKeydown(event: KeyboardEvent): void;
837
855
  stepBy(direction: 1 | -1): void;
838
856
  private get formatter();
857
+ private formatDisplayValue;
839
858
  private parse;
840
859
  private constrain;
841
860
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsInputNumberComponent, never>;
@@ -954,9 +973,16 @@ declare class NsContentEditableComponent extends NsValueAccessor<string> {
954
973
  private elementRef;
955
974
  private renderer;
956
975
  readonly maxLength: _angular_core.InputSignal<number>;
976
+ /**
977
+ * Character count of the rendered content, measured the same way the
978
+ * `maxlength` guard below measures it (`innerText.length`) so a consumer's
979
+ * counter never drifts from what actually blocks typing.
980
+ */
981
+ readonly length: _angular_core.WritableSignal<number>;
957
982
  enabled: boolean;
958
983
  writeValue(value: string): void;
959
984
  onInput(): void;
985
+ private updateLength;
960
986
  onBlur(): void;
961
987
  onKeyUp(event: KeyboardEvent): void;
962
988
  setDisabledState(isDisabled: boolean): void;
@@ -982,22 +1008,28 @@ declare class NsFieldDirective {
982
1008
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NsFieldDirective, "[nsField]", never, { "controlId": { "alias": "nsField"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
983
1009
  }
984
1010
 
1011
+ type NsEditorTool = 'paragraph' | 'format' | 'color' | 'list' | 'link' | 'image' | 'separator';
1012
+ declare const NS_EDITOR_TOOLS_DEFAULT: readonly NsEditorTool[];
985
1013
  declare class NsEditorComponent extends NsValueAccessor<string> implements OnInit, OnDestroy {
986
1014
  private editor;
987
- readonly rows: _angular_core.InputSignal<number>;
988
1015
  readonly maxLength: _angular_core.InputSignal<number>;
989
1016
  readonly colorPresets: _angular_core.InputSignal<string[]>;
1017
+ readonly tools: _angular_core.InputSignal<readonly NsEditorTool[]>;
990
1018
  textArea: ElementRef<HTMLElement>;
1019
+ private contentEditable;
1020
+ /** Mirrors the content-editable's own counter, measured off `innerText`. */
1021
+ readonly length: _angular_core.Signal<number>;
1022
+ readonly hasFooter: _angular_core.Signal<boolean>;
991
1023
  focused: () => void;
992
1024
  ngOnInit(): void;
993
1025
  get presets(): string[][];
994
1026
  blur(): void;
995
1027
  ngOnDestroy(): void;
996
1028
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsEditorComponent, never>;
997
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorComponent, "ns-editor", never, { "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "maxLength": { "alias": "maxLength"; "required": false; "isSignal": true; }; "colorPresets": { "alias": "colorPresets"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1029
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEditorComponent, "ns-editor", never, { "maxLength": { "alias": "maxLength"; "required": false; "isSignal": true; }; "colorPresets": { "alias": "colorPresets"; "required": false; "isSignal": true; }; "tools": { "alias": "tools"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
998
1030
  }
999
1031
 
1000
- type NsEmptyStateSize = 'sm' | 'md';
1032
+ type NsEmptyStateSize = 'sm' | 'md' | 'lg';
1001
1033
  declare class NsEmptyStateComponent {
1002
1034
  readonly title: _angular_core.InputSignal<string>;
1003
1035
  readonly description: _angular_core.InputSignal<string>;
@@ -1006,31 +1038,189 @@ declare class NsEmptyStateComponent {
1006
1038
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsEmptyStateComponent, "ns-empty-state", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, ["[empty-state-icon]", "[empty-state-actions]"], true, never>;
1007
1039
  }
1008
1040
 
1041
+ /**
1042
+ * No structural difference from `NsFile` — the guarantee this alias
1043
+ * documents ("already has an entry in `uidRegistry`") lives in the registry,
1044
+ * not on the object. Kept as a distinct name at call sites (`images()`,
1045
+ * `image()`, `retry()`, `remove()`) that specifically require an image which
1046
+ * has already been through `assignUid`.
1047
+ */
1048
+ type TrackedFile = NsFile;
1049
+ /**
1050
+ * Per-file upload status, keyed by `uid` (AVF-16). Replaces the old
1051
+ * `loading: Set<string>` — a boolean set can't distinguish "still
1052
+ * uploading" from "upload failed", which was the actual gap this HU closes.
1053
+ * `'ok'` is set (not deleted) on success so a file that went through this
1054
+ * component's own upload pipeline stays traceable; a file that arrived via
1055
+ * `writeValue`/`[value]` and never uploaded here simply has no entry, which
1056
+ * every template check (`=== 'subiendo'` / `=== 'fallido'`) already treats
1057
+ * as the normal/idle state.
1058
+ */
1059
+ type NsImageUploadFileStatus = 'subiendo' | 'ok' | 'fallido';
1009
1060
  declare class NsImageUploadComponent extends NsValueAccessor<NsFile | NsFile[] | null> {
1010
1061
  private client;
1011
1062
  private renderer;
1063
+ private readonly cdr;
1012
1064
  readonly accept: _angular_core.InputSignal<string>;
1013
1065
  readonly multi: _angular_core.InputSignalWithTransform<boolean, unknown>;
1014
1066
  readonly limit: _angular_core.InputSignalWithTransform<number, unknown>;
1067
+ readonly layout: _angular_core.InputSignal<"grid" | "cover">;
1068
+ /**
1069
+ * Rows the `__stack` column reserves before it starts scrolling
1070
+ * internally, in `layout="cover"`: at most 3, and never more than
1071
+ * `limit() - 1` (the cover itself takes one slot).
1072
+ */
1073
+ readonly reservedRows: _angular_core.Signal<number>;
1015
1074
  imgRef: ElementRef<HTMLInputElement>;
1016
- loading: Set<string>;
1075
+ status: Map<string, NsImageUploadFileStatus>;
1076
+ /**
1077
+ * Bumped by `setStatus`/`deleteStatus` on every `status` mutation.
1078
+ * `status` itself stays a plain, publicly-mutable `Map` — kept for the
1079
+ * existing per-file bookkeeping and for tests that poll it to synchronise
1080
+ * with an async upload — but a plain `Map.set()`/`.delete()` is invisible
1081
+ * to Angular's change detection: no signal write and no `markForCheck()`,
1082
+ * so nothing tells Angular this control needs checking again. Worse, an
1083
+ * `OnPush` ancestor that wasn't otherwise dirtied (e.g. the "Fotografías"
1084
+ * `<card>` wrapping this control in the product form) can skip this
1085
+ * subtree entirely on the next tick. That is exactly why a failed upload
1086
+ * used to get stuck showing the spinner forever, silently (AVF-18
1087
+ * corrección de rechazo #2): `status.set(uid, 'fallido')` ran, but nothing
1088
+ * ever told Angular to look again — unlike a successful upload, which
1089
+ * reaches the template through `value.set()` (a real signal write) via
1090
+ * `assignValue`/`onChange`. `statusOf()` reads this counter before reading
1091
+ * `status`, giving every render that goes through it a real signal
1092
+ * dependency, and `touchStatus()` also calls `markForCheck()` directly —
1093
+ * belt and braces: the explicit call is the same, long-proven mechanism
1094
+ * Angular has always used to announce a change made from outside its own
1095
+ * detection cycle (an RxJS subscription callback, here), and is what
1096
+ * reliably walks the ancestor chain and clears an `OnPush` skip, the way
1097
+ * a signal read alone was not enough to guarantee in practice.
1098
+ */
1099
+ private readonly statusVersion;
1100
+ private touchStatus;
1101
+ private setStatus;
1102
+ private deleteStatus;
1103
+ /**
1104
+ * The only path from `status` + `uid` to the template — and the one every
1105
+ * test should use too (AVF-18 corrección de rechazo #2): reading
1106
+ * `status.get(...)` directly, the way earlier tests did, cannot tell a
1107
+ * status change nobody announced to Angular from one that actually
1108
+ * rendered, which is exactly how the previous regression shipped green —
1109
+ * the map held `'fallido'`, the DOM still showed `'subiendo'`, and the
1110
+ * assertions that read the map straight agreed with the bug.
1111
+ */
1112
+ statusOf(image: NsFile): NsImageUploadFileStatus | undefined;
1113
+ /**
1114
+ * Active while a file dragged from outside the browser (desktop, another
1115
+ * app) hovers a drop zone — drives the highlight ring/border (AVF-9,
1116
+ * criteria 3-5). Never set for the internal thumbnail-reorder drag,
1117
+ * distinguished via `isFileDrag`.
1118
+ */
1119
+ readonly dragActive: _angular_core.WritableSignal<boolean>;
1120
+ /**
1121
+ * Inline, Spanish, component-owned notice for when a selection/drop
1122
+ * exceeds `limit()` (AVF-16). Deliberately not an `output()`: a consumer
1123
+ * that doesn't wire one up would silently drop files again, which is
1124
+ * exactly the defect this HU removes. Overwritten (never accumulated) on
1125
+ * every enqueue attempt, successful or not.
1126
+ */
1127
+ readonly limitWarning: _angular_core.WritableSignal<string>;
1017
1128
  private _fromIndex;
1018
1129
  private _toIndex;
1019
- get images(): NsFile[];
1020
- get image(): NsFile | null;
1130
+ /**
1131
+ * The only path from `value()` to the template, whatever fed it: a plain
1132
+ * `[value]="misImagenes"` binding — `value` is the public two-way `model()`
1133
+ * from `NsValueAccessor`, so nothing forces a consumer through
1134
+ * `writeValue`/`add` — lands here same as a file picked through the
1135
+ * control itself. `assignUid` is idempotent (never overwrites an existing
1136
+ * registry entry) and never mutates the `NsFile`, so re-running it every
1137
+ * getter call is safe: the `uid` a given instance gets on its first pass
1138
+ * through here is the one it keeps on every later change-detection cycle,
1139
+ * and no signal write happens as a side effect of reading the template
1140
+ * (AVF-15). Works the same whether the array/elements are frozen or not
1141
+ * (AVF-18 corrección de rechazo).
1142
+ */
1143
+ get images(): TrackedFile[];
1144
+ get image(): TrackedFile | null;
1145
+ /**
1146
+ * Reads the `uid` assigned to `image` — the accessor the `.ts` and the
1147
+ * template both use instead of a `.uid` property on the `NsFile` itself,
1148
+ * which no longer exists (AVF-18 corrección de rechazo: that property
1149
+ * write is exactly what threw on a frozen image). Public so the
1150
+ * template's `track` expressions can call it directly, and so `statusOf`
1151
+ * can resolve the key it looks up. Every `NsFile` reachable from
1152
+ * `images()`/`image()` has already been through `assignUid`, so this only
1153
+ * ever reads — the `assignUid` call here is a safety net for a caller that
1154
+ * hands in an image from outside that path.
1155
+ */
1156
+ uidOf(image: NsFile): string;
1157
+ /**
1158
+ * True while the single (`!multi()`) image's own upload attempt is
1159
+ * `'fallido'` — the one flag the drag-container button needs to swap
1160
+ * "open the file picker" for "retry this upload" (AVF-16, corrección de
1161
+ * rechazo: criterios 5-6 no estaban cubiertos en modo single, solo en
1162
+ * `multi`).
1163
+ */
1164
+ get singleFailed(): boolean;
1021
1165
  writeValue(value: NsFile | NsFile[] | null): void;
1022
1166
  onClick(): void;
1167
+ /**
1168
+ * Click handler for the single-mode (`!multi()`) drag-container: it is
1169
+ * both the file picker trigger and, once the image has failed, the
1170
+ * retry control — same dual role the `multi` grid button and the cover
1171
+ * chip already had (AVF-16 corrección de rechazo).
1172
+ */
1173
+ onDragContainerClick(): void;
1023
1174
  load(): void;
1024
1175
  add(image: NsFile): void;
1176
+ /**
1177
+ * Re-subscribes to the same upload the failed attempt used — no new
1178
+ * `NsUploadClient` contract needed (AVF-16 evidence: `upload(file):
1179
+ * Observable<file>` is already re-subscribable). Shared by the first
1180
+ * attempt (`add`) and every later retry. `uid` is passed in rather than
1181
+ * re-derived from `image`, since `retry()` needs the *original* uid even
1182
+ * though the `image` it holds may itself be a server response object by
1183
+ * then.
1184
+ */
1185
+ private upload;
1186
+ /**
1187
+ * Retries a failed upload in place: same `uid`, same position in the
1188
+ * gallery, no re-read from disk (the preview already holds the file's
1189
+ * data).
1190
+ */
1191
+ retry(image: TrackedFile): void;
1025
1192
  private assignValue;
1026
- private compareWith;
1027
- remove(image: NsFile): void;
1193
+ remove(image: TrackedFile): void;
1028
1194
  dragstart(event: DragEvent, index: number): void;
1195
+ /**
1196
+ * Shared by every drop target (thumbnails, the `cover` node — index 0 —
1197
+ * and the plain drop zones). `event.dataTransfer.types` never carries
1198
+ * `'Files'` for the internal reorder drag (it writes nothing to the
1199
+ * clipboard today), so that check alone tells the external-file drop and
1200
+ * the reorder drag apart without a separate state flag.
1201
+ */
1029
1202
  dragover(event: DragEvent, index: number): void;
1203
+ dragleave(event: DragEvent): void;
1030
1204
  dragend(event: DragEvent): void;
1031
1205
  drop(event: DragEvent): void;
1206
+ private isFileDrag;
1207
+ private isAcceptedFile;
1208
+ private addDroppedFile;
1209
+ /**
1210
+ * Accepts as many of `files` as fit under `limit()`, all at once, and
1211
+ * reports the rest inline. `limit()` is evaluated once here, against
1212
+ * `this.images.length` as it stands *before* any of this batch's
1213
+ * `add()` calls run — never inside a per-file callback, where it would
1214
+ * lag behind sibling files whose `FileReader`/upload hasn't resolved yet
1215
+ * and let more through than actually fit (AVF-16).
1216
+ *
1217
+ * Returns how many files were actually enqueued, so callers can decide
1218
+ * whether to mark the control touched.
1219
+ */
1220
+ private enqueueFiles;
1221
+ private buildLimitWarning;
1032
1222
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsImageUploadComponent, never>;
1033
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsImageUploadComponent, "ns-image-upload", never, { "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "multi": { "alias": "multi"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1223
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsImageUploadComponent, "ns-image-upload", never, { "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "multi": { "alias": "multi"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1034
1224
  }
1035
1225
 
1036
1226
  declare class NsLinkComponent {
@@ -1416,20 +1606,34 @@ declare class NsSkeletonComponent {
1416
1606
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsSkeletonComponent, "ns-skeleton", never, { "width": { "alias": "width"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "animated": { "alias": "animated"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1417
1607
  }
1418
1608
 
1419
- type NsStatCardVariant = 'default' | 'dark' | 'compact';
1609
+ type NsStatCardVariant = 'default' | 'dark' | 'compact' | 'tile';
1420
1610
  type NsStatCardDeltaTone = 'up' | 'down' | 'neutral';
1421
1611
  type NsStatCardValueTone = 'default' | 'success' | 'warning' | 'danger' | 'info';
1612
+ type NsStatCardHintTone = 'default' | 'success' | 'warning' | 'danger' | 'info';
1613
+ type NsStatCardIconTone = 'info' | 'success' | 'warning' | 'danger' | 'neutral';
1422
1614
  /**
1423
1615
  * Dashboard metric card: label, formatted value (with an optional muted
1424
1616
  * suffix), and an optional delta pill (`up` / `down` / `neutral`) with a
1425
1617
  * caption and a secondary hint. `variant="dark"` renders the emphasis
1426
1618
  * surface used for headline metrics; `variant="compact"` renders a smaller
1427
- * card with a smaller value size. Set `interactive` to render the card as a
1428
- * `<button>` and receive `(activated)` instead of a static `<div>`.
1619
+ * card with a smaller value size; `variant="tile"` renders a compact KPI
1620
+ * tile (icon circle beside a label/value/delta column, delta as plain
1621
+ * colored text instead of a pill). Set `interactive` to render the card as
1622
+ * a `<button>` and receive `(activated)` instead of a static `<div>`.
1429
1623
  *
1430
1624
  * `valueTone` colors only the headline value with the matching semantic
1431
1625
  * status color (`success`/`warning`/`danger`/`info`); it is ignored on
1432
- * `variant="dark"`, where the value stays white.
1626
+ * `variant="dark"`, where the value stays white. `hintTone` colors only the
1627
+ * hint with the same semantic scale, independently of `valueTone`; like
1628
+ * `valueTone`, it is ignored on `variant="dark"`, where the hint keeps the
1629
+ * emphasis-surface foreground color instead of a status color. `iconTone`
1630
+ * colors the icon wrapper background/foreground and applies to every
1631
+ * variant.
1632
+ *
1633
+ * The hint's own `margin-inline-start: auto` (pushing it to the opposite
1634
+ * edge from the value) only applies when it shares the meta row with a
1635
+ * `delta` and/or `deltaLabel` — something to separate from. A lone hint
1636
+ * (no delta, no deltaLabel) aligns to the start, under the value.
1433
1637
  */
1434
1638
  declare class NsStatCardComponent {
1435
1639
  readonly label: _angular_core.InputSignal<string>;
@@ -1440,13 +1644,15 @@ declare class NsStatCardComponent {
1440
1644
  readonly deltaLabel: _angular_core.InputSignal<string>;
1441
1645
  readonly hint: _angular_core.InputSignal<string>;
1442
1646
  readonly valueTone: _angular_core.InputSignal<NsStatCardValueTone>;
1647
+ readonly hintTone: _angular_core.InputSignal<NsStatCardHintTone>;
1648
+ readonly iconTone: _angular_core.InputSignal<NsStatCardIconTone>;
1443
1649
  readonly variant: _angular_core.InputSignal<NsStatCardVariant>;
1444
1650
  readonly interactive: _angular_core.InputSignalWithTransform<boolean, unknown>;
1445
1651
  readonly activated: _angular_core.OutputEmitterRef<void>;
1446
1652
  protected deltaGlyph(): string;
1447
1653
  protected onActivate(): void;
1448
1654
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NsStatCardComponent, never>;
1449
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsStatCardComponent, "ns-stat-card", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "delta": { "alias": "delta"; "required": false; "isSignal": true; }; "deltaTone": { "alias": "deltaTone"; "required": false; "isSignal": true; }; "deltaLabel": { "alias": "deltaLabel"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "valueTone": { "alias": "valueTone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; }, { "activated": "activated"; }, never, ["[stat-card-icon]", "[stat-card-footer]"], true, never>;
1655
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NsStatCardComponent, "ns-stat-card", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "delta": { "alias": "delta"; "required": false; "isSignal": true; }; "deltaTone": { "alias": "deltaTone"; "required": false; "isSignal": true; }; "deltaLabel": { "alias": "deltaLabel"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "valueTone": { "alias": "valueTone"; "required": false; "isSignal": true; }; "hintTone": { "alias": "hintTone"; "required": false; "isSignal": true; }; "iconTone": { "alias": "iconTone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; }, { "activated": "activated"; }, never, ["[stat-card-icon]", "[stat-card-footer]"], true, never>;
1450
1656
  }
1451
1657
 
1452
1658
  declare class TemplateDirective {
@@ -1549,6 +1755,34 @@ declare const NS_THEME_OPTIONS: InjectionToken<NsResolvedThemeOptions>;
1549
1755
  declare const NS_TOAST_OPTIONS: InjectionToken<NsResolvedToastThemeOptions>;
1550
1756
  declare function provideNsTheme(options?: NsThemeOptions): EnvironmentProviders;
1551
1757
 
1758
+ /**
1759
+ * Registry of theme tokens that used to be part of the Arvisfy preset and no
1760
+ * longer have any effect on rendering. `NsTokenTree` has an open index
1761
+ * signature and `mergeObjects` (below) merges unknown keys in without
1762
+ * complaint, so a consumer overriding a retired token gets no type error and
1763
+ * no `validateNsPreset` error -- the override is silently inert.
1764
+ * `validateNsPreset` walks this map against the resolved preset and emits a
1765
+ * `console.warn` for every retired path still present, so the override at
1766
+ * least surfaces instead of doing nothing invisibly.
1767
+ *
1768
+ * Keys are full token paths, one of:
1769
+ * - `primitive.<path>`
1770
+ * - `semantic.common.<path>`
1771
+ * - `semantic.colorScheme.<light|dark>.<path>`
1772
+ * - `components.<path>`
1773
+ *
1774
+ * Values are the exact message logged for that path. Do not include a
1775
+ * version number in a message: the published package version and this
1776
+ * source's `package.json` version can diverge, so a hardcoded version would
1777
+ * go stale.
1778
+ *
1779
+ * This lives here rather than in its own file: `packages/mcp` transpiles
1780
+ * this module in isolation (`extract-tokens.mjs`'s `assertNoValueImports`)
1781
+ * and cannot resolve a relative value import from a sibling module at that
1782
+ * point, so a second file imported here for its data would break
1783
+ * `nx run mcp:build-data`.
1784
+ */
1785
+ declare const NS_RETIRED_TOKENS: Readonly<Record<string, string>>;
1552
1786
  declare function defineNsPreset(base: NsThemePreset, overrides: NsThemePresetOverrides): NsThemePreset;
1553
1787
  declare function validateNsPreset(preset: NsThemePreset): void;
1554
1788
  declare function flattenTokens<T>(tree: NsTokenTree<T>): Map<string, T>;
@@ -1742,5 +1976,5 @@ declare class NsTooltipDirective implements OnDestroy {
1742
1976
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NsTooltipDirective, "[nsTooltip]", never, { "message": { "alias": "nsTooltip"; "required": true; "isSignal": true; }; "placement": { "alias": "nsTooltipPlacement"; "required": false; "isSignal": true; }; "delay": { "alias": "nsTooltipDelay"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1743
1977
  }
1744
1978
 
1745
- export { Arvisfy, NS_THEME_OPTIONS, NS_TOAST_OPTIONS, NsAccordionComponent, NsAccordionItemComponent, NsAlertComponent, NsAvatarComponent, NsAvatarGroupComponent, NsBadgeComponent, NsBreadcrumbItemComponent, NsBreadcrumbsComponent, NsButtonComponent, NsButtonGroupComponent, NsCardBodyComponent, NsCardComponent, NsCardFooterComponent, NsCardHeaderComponent, NsCheckboxComponent, NsChipComponent, NsCollectionController, NsCommandPaletteComponent, NsCommentComponent, NsContentEditableComponent, NsDatePickerComponent, NsDateRangePickerComponent, NsDescriptionComponent, NsDividerComponent, NsDrawerComponent, NsDrawerRef, NsDrawerService, NsDropdownComponent, NsDropdownItemDirective, NsEditorColorPickerComponent, NsEditorComponent, NsEditorLinkComponent, NsEditorListComponent, NsEditorParagraphComponent, NsEditorToggleCommandComponent, NsEmptyStateComponent, NsFieldContext, NsFieldDirective, NsImageUploadComponent, NsInputCodeComponent, NsInputComponent, NsInputGroupAddonComponent, NsInputGroupComponent, NsInputNumberComponent, NsInputSearchComponent, NsLinkComponent, NsLoadMoreComponent, NsMenuComponent, NsMenuDividerComponent, NsMenuItemComponent, NsMenuSectionComponent, NsMeterComponent, NsModalComponent, NsModalRef, NsModalService, NsOptionCardComponent, NsOverlayRef, NsOverlayService, NsPaginationComponent, NsPasswordComponent, NsPopoverComponent, NsProgressBarComponent, NsRadioComponent, NsRadioGroupComponent, NsSchedule, NsScheduleComponent, NsScheduleService, NsSchemaMarkupComponent, NsSideNavComponent, NsSideNavItemComponent, NsSideNavSectionComponent, NsSkeletonComponent, NsSliderComponent, NsStatCardComponent, NsSurfaceComponent, NsSwitchComponent, NsTabComponent, NsTabsComponent, NsTagComponent, NsTagGroupComponent, NsTextAreaComponent, NsThemeService, NsTimelineComponent, NsTimelineItemComponent, NsToastComponent, NsToastHandle, NsToastService, NsTooltipComponent, NsTooltipDirective, NsValueAccessor, TemplateDirective, defineNsPreset, flattenTokens, getNsOverlayPosition, provideNsTheme, referenceToCssVariable, tokenPathToCssVariable, validateNsPreset };
1746
- export type { NsAlertVariant, NsBadgeMode, NsButtonGroupSelectionMode, NsButtonVariant, NsChipColor, NsChipSize, NsChipVariant, NsCollectionItem, NsColor, NsColorScheme, NsCommandGroup, NsCommandItem, NsComment, NsComponentInputs, NsDateRangeValue, NsDateView, NsDeepPartial, NsDrawerComponentInputs, NsDrawerConfig, NsDrawerOptions, NsDrawerPosition, NsDropdownItem, NsDropdownValue, NsEmptyStateSize, NsFlattenedPrimitiveTokens, NsKey, NsModalConfig, NsModalOptions, NsModalSize, NsOrientation, NsOverlayCloseReason, NsOverlayConfig, NsOverlayPosition, NsPlacement, NsPrimitiveReference, NsPrimitiveTokenValue, NsResolvedColorScheme, NsResolvedThemeOptions, NsResolvedToastThemeOptions, NsSchemaMarkup, NsSchemaType, NsSelectionMode, NsSemanticReference, NsSemanticTokens, NsSideNavColor, NsSize, NsSliderValue, NsStatCardDeltaTone, NsStatCardValueTone, NsStatCardVariant, NsTabsVariant, NsThemeOptions, NsThemePreset, NsThemePresetOverrides, NsTimelineItemColor, NsTimelineSize, NsToastAction, NsToastCloseReason, NsToastOptions, NsToastPlacement, NsToastThemeOptions, NsToastVariant, NsTokenTree, NsVariant };
1979
+ export { Arvisfy, NS_EDITOR_TOOLS_DEFAULT, NS_RETIRED_TOKENS, NS_THEME_OPTIONS, NS_TOAST_OPTIONS, NsAccordionComponent, NsAccordionItemComponent, NsAlertComponent, NsAvatarComponent, NsAvatarGroupComponent, NsBadgeComponent, NsBreadcrumbItemComponent, NsBreadcrumbsComponent, NsButtonComponent, NsButtonGroupComponent, NsCardBodyComponent, NsCardComponent, NsCardFooterComponent, NsCardHeaderComponent, NsCheckboxComponent, NsChipComponent, NsCollectionController, NsCommandPaletteComponent, NsCommentComponent, NsContentEditableComponent, NsDatePickerComponent, NsDateRangePickerComponent, NsDescriptionComponent, NsDividerComponent, NsDrawerComponent, NsDrawerRef, NsDrawerService, NsDropdownComponent, NsDropdownItemDirective, NsEditorColorPickerComponent, NsEditorComponent, NsEditorImageComponent, NsEditorLinkComponent, NsEditorListComponent, NsEditorParagraphComponent, NsEditorToggleCommandComponent, NsEmptyStateComponent, NsFieldContext, NsFieldDirective, NsImageUploadComponent, NsInputCodeComponent, NsInputComponent, NsInputGroupAddonComponent, NsInputGroupComponent, NsInputNumberComponent, NsInputSearchComponent, NsLinkComponent, NsLoadMoreComponent, NsMenuComponent, NsMenuDividerComponent, NsMenuItemComponent, NsMenuSectionComponent, NsMeterComponent, NsModalComponent, NsModalRef, NsModalService, NsOptionCardComponent, NsOverlayRef, NsOverlayService, NsPaginationComponent, NsPasswordComponent, NsPopoverComponent, NsProgressBarComponent, NsRadioComponent, NsRadioGroupComponent, NsSchedule, NsScheduleComponent, NsScheduleService, NsSchemaMarkupComponent, NsSideNavComponent, NsSideNavItemComponent, NsSideNavSectionComponent, NsSkeletonComponent, NsSliderComponent, NsStatCardComponent, NsSurfaceComponent, NsSwitchComponent, NsTabComponent, NsTabsComponent, NsTagComponent, NsTagGroupComponent, NsTextAreaComponent, NsThemeService, NsTimelineComponent, NsTimelineItemComponent, NsToastComponent, NsToastHandle, NsToastService, NsTooltipComponent, NsTooltipDirective, NsValueAccessor, TemplateDirective, defineNsPreset, flattenTokens, getNsOverlayPosition, provideNsTheme, referenceToCssVariable, tokenPathToCssVariable, validateNsPreset };
1980
+ export type { NsAlertVariant, NsBadgeMode, NsButtonGroupSelectionMode, NsButtonVariant, NsChipColor, NsChipSize, NsChipVariant, NsCollectionItem, NsColor, NsColorScheme, NsCommandGroup, NsCommandItem, NsComment, NsComponentInputs, NsDateRangeValue, NsDateView, NsDeepPartial, NsDrawerComponentInputs, NsDrawerConfig, NsDrawerOptions, NsDrawerPosition, NsDropdownItem, NsDropdownValue, NsEditorTool, NsEmptyStateSize, NsFlattenedPrimitiveTokens, NsKey, NsModalConfig, NsModalOptions, NsModalSize, NsOrientation, NsOverlayCloseReason, NsOverlayConfig, NsOverlayPosition, NsPlacement, NsPrimitiveReference, NsPrimitiveTokenValue, NsResolvedColorScheme, NsResolvedThemeOptions, NsResolvedToastThemeOptions, NsSchemaMarkup, NsSchemaType, NsSelectionMode, NsSemanticReference, NsSemanticTokens, NsSideNavColor, NsSize, NsSliderValue, NsStatCardDeltaTone, NsStatCardHintTone, NsStatCardIconTone, NsStatCardValueTone, NsStatCardVariant, NsTabsVariant, NsThemeOptions, NsThemePreset, NsThemePresetOverrides, NsTimelineItemColor, NsTimelineSize, NsToastAction, NsToastCloseReason, NsToastOptions, NsToastPlacement, NsToastThemeOptions, NsToastVariant, NsTokenTree, NsVariant };