@joanroucoux/cairn-ui 0.6.5 → 0.7.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": "@joanroucoux/cairn-ui",
3
- "version": "0.6.5",
3
+ "version": "0.7.0",
4
4
  "description": "Cairn UI — a monochrome, accessible Angular design system.",
5
5
  "keywords": [
6
6
  "angular",
@@ -0,0 +1,83 @@
1
+ @keyframes cairn-spin {
2
+ to {
3
+ transform: rotate(1turn);
4
+ }
5
+ }
6
+
7
+ @keyframes cairn-pulse {
8
+ from {
9
+ opacity: 1;
10
+ }
11
+ to {
12
+ opacity: 0.45;
13
+ }
14
+ }
15
+
16
+ @keyframes cairn-fade-in {
17
+ from {
18
+ opacity: 0;
19
+ }
20
+ }
21
+
22
+ @keyframes cairn-fade-up-in {
23
+ from {
24
+ opacity: 0;
25
+ transform: translateY(4px);
26
+ }
27
+ }
28
+
29
+ @keyframes cairn-panel-in {
30
+ from {
31
+ opacity: 0;
32
+ transform: translateX(8px);
33
+ }
34
+ }
35
+
36
+ @keyframes cairn-fade-out {
37
+ to {
38
+ opacity: 0;
39
+ }
40
+ }
41
+
42
+ .ui-enter-fade {
43
+ animation: cairn-fade-in var(--duration-fast) var(--ease-out);
44
+ }
45
+
46
+ .ui-enter-fade-up {
47
+ animation: cairn-fade-up-in var(--duration-fast) var(--ease-out);
48
+ }
49
+
50
+ .ui-enter-panel {
51
+ animation: cairn-panel-in var(--duration-base) var(--ease-out);
52
+ }
53
+
54
+ .ui-leave-fade {
55
+ animation: cairn-fade-out var(--duration-exit) var(--ease-out) forwards;
56
+ }
57
+
58
+ ::view-transition-old(root),
59
+ ::view-transition-new(root) {
60
+ animation-duration: var(--duration-base);
61
+ animation-timing-function: var(--ease-out);
62
+ }
63
+
64
+ @view-transition {
65
+ navigation: auto;
66
+ }
67
+
68
+ [data-theme-switching] * {
69
+ transition: none !important;
70
+ }
71
+
72
+ @media (prefers-reduced-motion: reduce) {
73
+ .ui-enter-fade-up,
74
+ .ui-enter-panel {
75
+ animation-name: cairn-fade-in;
76
+ }
77
+
78
+ ::view-transition-group(*),
79
+ ::view-transition-old(root),
80
+ ::view-transition-new(root) {
81
+ animation: none;
82
+ }
83
+ }
package/styles/theme.css CHANGED
@@ -28,4 +28,10 @@
28
28
 
29
29
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
30
30
  --ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);
31
+
32
+ --default-transition-timing-function: var(--ease-out);
33
+ --default-transition-duration: var(--duration-fast);
34
+
35
+ --animate-cairn-spin: cairn-spin var(--duration-spin) linear infinite;
36
+ --animate-cairn-pulse: cairn-pulse var(--pulse-duration) var(--ease-out) infinite alternate;
31
37
  }
package/styles/tokens.css CHANGED
@@ -101,6 +101,9 @@
101
101
  --press-scale: 0.97;
102
102
  --enter-scale: 0.95; /* dialogs and menus start at scale(0.95) and opacity 0, never scale(0) */
103
103
  --pulse-duration: 1600ms; /* skeleton opacity pulse */
104
+ --duration-spin: 800ms; /* button spinner, one turn */
105
+ --duration-highlight: 1200ms; /* a freshly edited row fading back to its background */
106
+ --toast-duration: 5000ms; /* how long a confirmation stays */
104
107
 
105
108
  /* Browser theme color (meta theme-color, iOS status bar) follows the top of the page:
106
109
  light #f7f8f8, dark #0a0b0b - the --background values. */
@@ -137,6 +140,8 @@
137
140
  --enter-scale: 1; /* slides and scales become short fades */
138
141
  --duration-base: 150ms;
139
142
  --pulse-duration: 0ms;
143
+ --duration-highlight: 600ms;
144
+ --duration-spin: 1600ms; /* the spinner still says work is in progress, only slower */
140
145
  }
141
146
  }
142
147
 
@@ -5,7 +5,8 @@ import { InjectionToken, Signal, ElementRef } from '@angular/core';
5
5
  * Bar of actions fixed above the tab bar on iPhone: the primary actions of a detail screen, within
6
6
  * thumb reach. It sits 52px plus the safe area above the bottom edge, so it clears `ui-tab-bar`, and
7
7
  * it is hidden from `64rem`, where the same actions live in the page itself. Its children share the
8
- * width equally.
8
+ * width equally. While it is on the page it publishes its height as `--action-bar-height` on the
9
+ * root element, which `ui-toaster` adds to its offset so a confirmation never covers the actions.
9
10
  *
10
11
  * @example
11
12
  * <ui-action-bar>
@@ -14,6 +15,7 @@ import { InjectionToken, Signal, ElementRef } from '@angular/core';
14
15
  * </ui-action-bar>
15
16
  */
16
17
  declare class UiActionBar {
18
+ constructor();
17
19
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiActionBar, never>;
18
20
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiActionBar, "ui-action-bar", never, {}, {}, never, ["*"], true, never>;
19
21
  }
@@ -22,6 +24,8 @@ declare const ALERT_VARIANTS: readonly ["error", "warning"];
22
24
  type AlertVariant = (typeof ALERT_VARIANTS)[number];
23
25
  /**
24
26
  * Inline message explaining a failure or a consequence: an icon, an optional heading and a text.
27
+ * It fades in when it appears; set `[fadeIn]="false"` on an alert that is part of the page as it
28
+ * opens (an error state after a load), which shows without motion.
25
29
  *
26
30
  * @example
27
31
  * <ui-alert heading="Sign-in failed">Try again.</ui-alert>
@@ -29,9 +33,10 @@ type AlertVariant = (typeof ALERT_VARIANTS)[number];
29
33
  declare class UiAlert {
30
34
  readonly variant: _angular_core.InputSignal<"error" | "warning">;
31
35
  readonly heading: _angular_core.InputSignal<string | undefined>;
36
+ readonly fadeIn: _angular_core.InputSignalWithTransform<boolean, unknown>;
32
37
  protected readonly classes: _angular_core.Signal<string>;
33
38
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiAlert, never>;
34
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiAlert, "ui-alert", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
39
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiAlert, "ui-alert", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "fadeIn": { "alias": "fadeIn"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
35
40
  }
36
41
 
37
42
  /** App-wide "amounts are hidden" state. Defaults to never masked; the app overrides it. */
@@ -104,6 +109,7 @@ declare class UiAsync {
104
109
  readonly card: _angular_core.InputSignalWithTransform<boolean, unknown>;
105
110
  readonly fill: _angular_core.InputSignalWithTransform<boolean, unknown>;
106
111
  readonly retry: _angular_core.OutputEmitterRef<void>;
112
+ protected readonly shown: _angular_core.Signal<"error" | "loading" | "empty" | "ready" | null>;
107
113
  protected readonly filling: _angular_core.Signal<boolean>;
108
114
  protected readonly iconSize: _angular_core.Signal<16 | 18>;
109
115
  protected readonly boxClasses: _angular_core.Signal<string>;
@@ -114,6 +120,17 @@ declare class UiAsync {
114
120
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiAsync, "ui-async", never, { "state": { "alias": "state"; "required": true; "isSignal": true; }; "errorTitle": { "alias": "errorTitle"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "retryLabel": { "alias": "retryLabel"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "card": { "alias": "card"; "required": false; "isSignal": true; }; "fill": { "alias": "fill"; "required": false; "isSignal": true; }; }, { "retry": "retry"; }, never, ["[asyncLoading]", "[asyncEmpty]", "*"], true, never>;
115
121
  }
116
122
 
123
+ /**
124
+ * Turns the live state of a call into the state to draw: `loading` only appears once the call has
125
+ * lasted 150 ms and, once shown, stays at least 400 ms, so a fast call never flashes a skeleton and a
126
+ * slow one never blinks. Until then the previous state stays on screen (a reload keeps its content);
127
+ * `null` means nothing to draw yet, for a first load still under 150 ms. Call it in an injection context.
128
+ *
129
+ * @example
130
+ * protected readonly shown = delayedState(this.state);
131
+ */
132
+ declare function delayedState(source: Signal<AsyncState>): Signal<AsyncState | null>;
133
+
117
134
  /** Available avatar sizes. `AvatarSize` is derived from this tuple. */
118
135
  declare const AVATAR_SIZES: readonly ["sm", "md", "lg", "auto"];
119
136
  type AvatarSize = (typeof AVATAR_SIZES)[number];
@@ -341,6 +358,9 @@ type LayoutClasses = {
341
358
  body: string;
342
359
  footer: string;
343
360
  };
361
+ /** What started a close, as `closed` reports it. `DialogCloseReason` is derived from this tuple. */
362
+ declare const DIALOG_CLOSE_REASONS: readonly ["escape", "backdrop", "cross", "drag", "programmatic"];
363
+ type DialogCloseReason = (typeof DIALOG_CLOSE_REASONS)[number];
344
364
  /**
345
365
  * Modal dialog on the native <dialog>: a bottom sheet under `64rem`, centered above it.
346
366
  *
@@ -356,11 +376,12 @@ declare class UiDialog {
356
376
  readonly heading: _angular_core.InputSignal<string>;
357
377
  readonly description: _angular_core.InputSignal<string | undefined>;
358
378
  readonly closeLabel: _angular_core.InputSignal<string | undefined>;
359
- readonly layout: _angular_core.InputSignal<"list" | "trade" | "form" | "confirm">;
379
+ readonly layout: _angular_core.InputSignal<"list" | "form" | "trade" | "confirm">;
360
380
  readonly truncateDescription: _angular_core.InputSignalWithTransform<boolean, unknown>;
361
381
  readonly width: _angular_core.InputSignal<"md" | "lg" | (string & {})>;
362
382
  readonly open: _angular_core.InputSignalWithTransform<boolean, unknown>;
363
383
  readonly dismissed: _angular_core.OutputEmitterRef<void>;
384
+ readonly closed: _angular_core.OutputEmitterRef<"drag" | "escape" | "backdrop" | "cross" | "programmatic">;
364
385
  protected readonly headingId: string;
365
386
  protected readonly descriptionId: string;
366
387
  protected readonly bodyId: string;
@@ -375,11 +396,13 @@ declare class UiDialog {
375
396
  protected readonly footerClasses: _angular_core.Signal<string>;
376
397
  protected readonly classes: _angular_core.Signal<string>;
377
398
  constructor();
399
+ protected close(reason: DialogCloseReason): void;
400
+ protected onCancel(event: Event): void;
378
401
  protected onPointerDown(event: MouseEvent): void;
379
402
  protected onClick(event: MouseEvent): void;
380
403
  protected onNativeClose(): void;
381
404
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiDialog, never>;
382
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiDialog, "ui-dialog", never, { "heading": { "alias": "heading"; "required": true; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "closeLabel": { "alias": "closeLabel"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; "truncateDescription": { "alias": "truncateDescription"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; }, { "dismissed": "dismissed"; }, never, ["*", "[dialogActions]"], true, never>;
405
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiDialog, "ui-dialog", never, { "heading": { "alias": "heading"; "required": true; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "closeLabel": { "alias": "closeLabel"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; "truncateDescription": { "alias": "truncateDescription"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; }, { "dismissed": "dismissed"; "closed": "closed"; }, never, ["*", "[dialogActions]"], true, never>;
383
406
  }
384
407
 
385
408
  type DonutSlice = {
@@ -423,7 +446,7 @@ declare class UiDonut {
423
446
  protected readonly radius = 78;
424
447
  protected readonly ringWidth = 30;
425
448
  protected strokeColor(slice: RankedSlice): string;
426
- protected strokeWidth(slice: RankedSlice): number;
449
+ protected sliceClasses(slice: RankedSlice): string;
427
450
  protected dotClasses(slice: RankedSlice): string;
428
451
  protected rowClasses(slice: RankedSlice): string;
429
452
  protected subLabel(slice: RankedSlice): string;
@@ -437,6 +460,22 @@ declare class UiDonut {
437
460
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiDonut, "ui-donut", never, { "slices": { "alias": "slices"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "othersLabel": { "alias": "othersLabel"; "required": true; "isSignal": true; }; "valueFormat": { "alias": "valueFormat"; "required": false; "isSignal": true; }; "shareFormat": { "alias": "shareFormat"; "required": false; "isSignal": true; }; "legendHref": { "alias": "legendHref"; "required": false; "isSignal": true; }; }, { "sliceSelect": "sliceSelect"; }, never, never, true, never>;
438
461
  }
439
462
 
463
+ /**
464
+ * Centred empty state: a title, a hint and an action slot, such as a catalogue search that matches
465
+ * nothing. The padding is 24px, 48px from 64rem.
466
+ *
467
+ * @example
468
+ * <ui-empty heading="Aucun instrument ne correspond à « zzz »" hint="La recherche porte sur le nom et l'ISIN.">
469
+ * <button ui-button variant="outline" size="md" type="button">Ajouter une ligne</button>
470
+ * </ui-empty>
471
+ */
472
+ declare class UiEmpty {
473
+ readonly heading: _angular_core.InputSignal<string>;
474
+ readonly hint: _angular_core.InputSignal<string>;
475
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiEmpty, never>;
476
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiEmpty, "ui-empty", never, { "heading": { "alias": "heading"; "required": true; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
477
+ }
478
+
440
479
  /**
441
480
  * Link to another site: the label followed by an external-link icon. A 44px target on touch,
442
481
  * an underline on hover with a mouse.
@@ -668,6 +707,8 @@ type TooltipSize = (typeof TOOLTIP_SIZES)[number];
668
707
  type AxisTicks = 3 | 5 | 'auto';
669
708
 
670
709
  type PlotRef = ElementRef<SVGSVGElement>;
710
+ type LineRef = ElementRef<SVGPathElement>;
711
+ type EndRef = ElementRef<SVGCircleElement>;
671
712
  /**
672
713
  * Value over time: a monotone curve with a dashed line at the starting value and a tooltip.
673
714
  *
@@ -697,8 +738,10 @@ declare class UiLineChart {
697
738
  readonly axisGap: _angular_core.InputSignal<number | "auto">;
698
739
  readonly axisTicks: _angular_core.InputSignalWithTransform<AxisTicks, string | 3 | 5>;
699
740
  protected readonly svgRef: _angular_core.Signal<PlotRef>;
741
+ protected readonly lineRef: _angular_core.Signal<LineRef | undefined>;
742
+ protected readonly endRef: _angular_core.Signal<EndRef | undefined>;
700
743
  protected readonly activeIndex: _angular_core.WritableSignal<number | null>;
701
- protected readonly animatedLine: _angular_core.WritableSignal<string | null>;
744
+ protected readonly held: _angular_core.WritableSignal<ChartGeometry | null>;
702
745
  protected readonly measuredSize: _angular_core.WritableSignal<{
703
746
  width: number;
704
747
  height: number;
@@ -713,7 +756,8 @@ declare class UiLineChart {
713
756
  delta: string;
714
757
  }>;
715
758
  protected readonly geometry: _angular_core.Signal<ChartGeometry | null>;
716
- protected readonly displayLine: _angular_core.Signal<string>;
759
+ protected readonly shown: _angular_core.Signal<ChartGeometry | null>;
760
+ protected readonly startFade: _angular_core.Signal<"opacity-0 duration-(--duration-exit)" | "duration-(--duration-fast)">;
717
761
  protected readonly ticks: _angular_core.Signal<PlottedPoint[]>;
718
762
  protected readonly tickTexts: _angular_core.Signal<(TickLabel | null)[]>;
719
763
  protected readonly active: _angular_core.Signal<PlottedPoint | null>;
@@ -815,7 +859,7 @@ declare class UiMeter {
815
859
  readonly value: _angular_core.InputSignal<number>;
816
860
  readonly label: _angular_core.InputSignal<string>;
817
861
  readonly valueText: _angular_core.InputSignal<string>;
818
- readonly tone: _angular_core.InputSignal<2 | 4 | 3 | 1 | 5 | 6>;
862
+ readonly tone: _angular_core.InputSignal<2 | 4 | 1 | 3 | 6 | 5>;
819
863
  readonly size: _angular_core.InputSignal<"sm" | "md">;
820
864
  protected readonly trackClasses: _angular_core.Signal<string>;
821
865
  protected readonly percent: _angular_core.Signal<number>;
@@ -824,6 +868,74 @@ declare class UiMeter {
824
868
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiMeter, "ui-meter", never, { "value": { "alias": "value"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "valueText": { "alias": "valueText"; "required": true; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
825
869
  }
826
870
 
871
+ /**
872
+ * Slides the remaining children of a list into place when one is removed or inserted (FLIP): it
873
+ * measures every child before and after the change, then animates the shift in `translateY` over
874
+ * `--duration-base` `--ease-out`. Pair it with `animate.leave="ui-leave-fade"` on the item so it fades
875
+ * out first. Nothing moves under `prefers-reduced-motion`: the list closes up at once.
876
+ *
877
+ * @example
878
+ * <ul uiFlipList>
879
+ * @for (key of keys(); track key.id) {
880
+ * <li uiListRow animate.leave="ui-leave-fade">...</li>
881
+ * }
882
+ * </ul>
883
+ */
884
+ declare class UiFlipList {
885
+ private readonly host;
886
+ private readonly reduced;
887
+ private readonly mutations;
888
+ private readonly resizes;
889
+ private readonly running;
890
+ private readonly watched;
891
+ private tops;
892
+ constructor();
893
+ private measure;
894
+ private interruptedOffset;
895
+ private onChange;
896
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiFlipList, never>;
897
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<UiFlipList, "[uiFlipList]", never, {}, {}, never, never, true, never>;
898
+ }
899
+
900
+ /**
901
+ * Draws the eye to an element that just changed: scrolls it into view when it is off screen, holds the
902
+ * `--soft` fill for 200 ms, then fades back to the element's own background over `--duration-highlight`.
903
+ * Each new non-null token plays once, so pass a fresh value per event (an object or a counter), not a
904
+ * boolean. A non-null token already present at first render plays on arrival. On a `tr` it paints the
905
+ * cells, on a `td[ui-group-cell]` the band, anywhere else the host. Under `prefers-reduced-motion` the
906
+ * scroll is instant and the fade lasts 600 ms.
907
+ *
908
+ * Give the element `scroll-margin-top` and `scroll-margin-bottom` equal to what is pinned above and
909
+ * below it (header, tab bar, action bar): a row under them counts as off screen and is scrolled out.
910
+ * For an arrival on a page opened at the element, scroll to it at once first (`scrollIntoView` in
911
+ * `afterNextRender`) and set the token afterwards: a token present at first render scrolls smoothly
912
+ * from where the page is.
913
+ *
914
+ * @example
915
+ * <a ui-row [uiHighlight]="changed() === holding.id ? token() : null" routerLink="/holdings/1">...</a>
916
+ */
917
+ declare class UiHighlight {
918
+ readonly uiHighlight: _angular_core.InputSignal<unknown>;
919
+ private readonly host;
920
+ private readonly reduced;
921
+ private cancel;
922
+ constructor();
923
+ private play;
924
+ private isVisible;
925
+ private flash;
926
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiHighlight, never>;
927
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<UiHighlight, "[uiHighlight]", never, { "uiHighlight": { "alias": "uiHighlight"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
928
+ }
929
+
930
+ /**
931
+ * A signal that follows the user's `prefers-reduced-motion` preference live.
932
+ * Call it in an injection context; the listener is removed with that context.
933
+ *
934
+ * @example
935
+ * protected readonly reduced = injectReducedMotion();
936
+ */
937
+ declare function injectReducedMotion(): Signal<boolean>;
938
+
827
939
  /**
828
940
  * One destination of the sidebar navigation (Portfolio, Holdings, Allocation, Accounts).
829
941
  *
@@ -847,7 +959,7 @@ type RowPadding = (typeof ROW_PADDINGS)[number];
847
959
  declare const ROW_GAPS: readonly ["default", "sm"];
848
960
  type RowGap = (typeof ROW_GAPS)[number];
849
961
  /**
850
- * Clickable row of a list: a holding, an envelope, an account.
962
+ * Clickable row of a list: a holding, an account, a search result.
851
963
  *
852
964
  * @example
853
965
  * <a ui-row [selected]="holding.id === openId()" [routerLink]="['/holdings', holding.id]">
@@ -856,12 +968,15 @@ type RowGap = (typeof ROW_GAPS)[number];
856
968
  */
857
969
  declare class UiRow {
858
970
  readonly selected: _angular_core.InputSignalWithTransform<boolean, unknown>;
971
+ readonly busy: _angular_core.InputSignalWithTransform<boolean, unknown>;
972
+ readonly unavailable: _angular_core.InputSignalWithTransform<boolean, unknown>;
859
973
  readonly size: _angular_core.InputSignal<"md" | "lg" | "xl" | "card" | "dense">;
860
974
  readonly padding: _angular_core.InputSignal<"sm" | "md" | "none">;
861
975
  readonly gap: _angular_core.InputSignal<"sm" | "default">;
862
976
  protected readonly classes: _angular_core.Signal<string>;
977
+ constructor();
863
978
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiRow, never>;
864
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiRow, "a[ui-row], button[ui-row]", never, { "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "padding": { "alias": "padding"; "required": false; "isSignal": true; }; "gap": { "alias": "gap"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
979
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiRow, "a[ui-row], button[ui-row]", never, { "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "busy": { "alias": "busy"; "required": false; "isSignal": true; }; "unavailable": { "alias": "unavailable"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "padding": { "alias": "padding"; "required": false; "isSignal": true; }; "gap": { "alias": "gap"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
865
980
  }
866
981
  /**
867
982
  * Lays out a `ui-row` with a trailing action beside it, such as a "more" icon button: the row takes
@@ -982,6 +1097,21 @@ declare class UiSkeleton {
982
1097
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiSkeleton, "ui-skeleton", never, { "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "lines": { "alias": "lines"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
983
1098
  }
984
1099
 
1100
+ /**
1101
+ * Link in the stale tone, for a caption about data that is late or left out of a total, such as
1102
+ * "1 ligne sans cours, non comptée". Caption size, weight 500, underlined on hover, with an optional
1103
+ * trailing chevron.
1104
+ *
1105
+ * @example
1106
+ * <a uiStaleLink chevron routerLink="/holdings">1 cours en retard</a>
1107
+ */
1108
+ declare class UiStaleLink {
1109
+ readonly chevron: _angular_core.InputSignalWithTransform<boolean, unknown>;
1110
+ protected readonly classes: _angular_core.Signal<"inline-flex items-center gap-0.5 rounded-[4px] text-caption font-medium text-(--stale) hover:underline underline-offset-3 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring)" | "inline-flex items-center gap-0.5 rounded-[4px] text-caption font-medium text-(--stale) hover:underline underline-offset-3 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) after:size-4 after:flex-none after:bg-current after:content-[''] after:mask-center after:mask-no-repeat after:mask-[url(data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%271.75%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m9%2018%206-6-6-6%27%2F%3E%3C%2Fsvg%3E)]">;
1111
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiStaleLink, never>;
1112
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<UiStaleLink, "a[uiStaleLink]", never, { "chevron": { "alias": "chevron"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1113
+ }
1114
+
985
1115
  /** Available stat sizes. `StatSize` is derived from this tuple. */
986
1116
  declare const STAT_SIZES: readonly ["hero", "tile"];
987
1117
  type StatSize = (typeof STAT_SIZES)[number];
@@ -1018,7 +1148,7 @@ declare class UiStat {
1018
1148
  * <input type="checkbox" uiSwitch [checked]="hidden()" (change)="toggle()" aria-label="Hide amounts" />
1019
1149
  */
1020
1150
  declare class UiSwitch {
1021
- protected readonly classes = "relative shrink-0 w-[51px] h-[31px] pointer-fine:w-10 pointer-fine:h-6 appearance-none cursor-pointer rounded-pill bg-(--muted) checked:bg-(--primary) transition-colors duration-(--duration-fast) ease-out disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) before:absolute before:inset-x-0 before:top-1/2 before:-translate-y-1/2 before:h-(--row-min) before:content-[''] after:absolute after:top-0.5 after:left-0.5 after:size-[27px] pointer-fine:after:size-5 after:rounded-pill after:bg-(--card) after:shadow-[0_1px_3px_rgb(0_0_0/0.2)] after:transition-transform after:duration-(--duration-fast) after:ease-out after:content-[''] checked:after:translate-x-[20px] pointer-fine:checked:after:translate-x-4";
1151
+ protected readonly classes = "relative shrink-0 w-[51px] h-[31px] pointer-fine:w-10 pointer-fine:h-6 appearance-none cursor-pointer rounded-pill bg-(--muted) checked:bg-(--primary) transition-[background-color] duration-(--duration-fast) ease-out disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) before:absolute before:inset-x-0 before:top-1/2 before:-translate-y-1/2 before:h-(--row-min) before:content-[''] after:absolute after:top-0.5 after:left-0.5 after:size-[27px] pointer-fine:after:size-5 after:rounded-pill after:bg-(--card) after:shadow-[0_1px_3px_rgb(0_0_0/0.2)] after:transition-transform after:duration-(--duration-fast) after:ease-out after:content-[''] checked:after:translate-x-[20px] pointer-fine:checked:after:translate-x-4";
1022
1152
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiSwitch, never>;
1023
1153
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<UiSwitch, "input[type=checkbox][uiSwitch]", never, {}, {}, never, never, true, never>;
1024
1154
  }
@@ -1048,6 +1178,53 @@ declare class UiTab {
1048
1178
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiTab, "a[ui-tab]", never, { "active": { "alias": "active"; "required": false; "isSignal": true; }; }, {}, never, ["[tabIcon]", "*"], true, never>;
1049
1179
  }
1050
1180
 
1181
+ type Toast = {
1182
+ readonly id: number;
1183
+ readonly text: string;
1184
+ };
1185
+ /**
1186
+ * One-slot confirmation message queue read by `ui-toaster`.
1187
+ *
1188
+ * @example
1189
+ * inject(UiToasts).show('Achat enregistré');
1190
+ */
1191
+ declare class UiToasts {
1192
+ private readonly current;
1193
+ private sequence;
1194
+ readonly toast: _angular_core.Signal<Toast | null>;
1195
+ show(message: string): void;
1196
+ dismiss(): void;
1197
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiToasts, never>;
1198
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
1199
+ }
1200
+
1201
+ /**
1202
+ * Confirmation message shown by `UiToasts.show`: one at a time, no action, gone after `--toast-duration`.
1203
+ * Place it once in the app shell.
1204
+ *
1205
+ * @example
1206
+ * <ui-toaster />
1207
+ */
1208
+ declare class UiToaster {
1209
+ protected readonly toasts: UiToasts;
1210
+ protected readonly toastClasses = "pointer-events-auto max-w-[min(28rem,calc(100vw-2rem))] rounded-container bg-(--elevated) px-4 py-3 text-label text-(--foreground) shadow-[0_8px_24px_rgb(0_0_0/0.16),inset_0_0_0_1px_var(--border)] transition-[opacity,translate] duration-(--duration-base) ease-out starting:opacity-0 starting:translate-y-2 motion-reduce:starting:translate-y-0";
1211
+ private readonly host;
1212
+ private readonly hovered;
1213
+ private readonly focused;
1214
+ private lastId;
1215
+ private remaining;
1216
+ private startedAt;
1217
+ private timer;
1218
+ constructor();
1219
+ protected setHovered(value: boolean): void;
1220
+ protected setFocused(value: boolean): void;
1221
+ private resume;
1222
+ private pause;
1223
+ private stop;
1224
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiToaster, never>;
1225
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<UiToaster, "ui-toaster", never, {}, {}, never, never, true, never>;
1226
+ }
1227
+
1051
1228
  /** Breakpoints a secondary column can be held back until. */
1052
1229
  declare const CELL_BREAKPOINTS: readonly ["md", "lg"];
1053
1230
  type CellBreakpoint = (typeof CELL_BREAKPOINTS)[number];
@@ -1182,5 +1359,5 @@ declare class UiCellSub {
1182
1359
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<UiCellSub, "[uiCellSub]", never, { "narrow": { "alias": "narrow"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1183
1360
  }
1184
1361
 
1185
- export { ALERT_VARIANTS, AMOUNT_MASKINGS, AMOUNT_NUMERICS, ASYNC_ALIGNS, ASYNC_STATES, ASYNC_VARIANTS, AVATAR_SIZES, BACK_LINK_SIZES, BADGE_SIZES, BADGE_VARIANTS, BUTTON_SIZES, BUTTON_VARIANTS, CARD_PADDINGS, CARD_SURFACES, CARD_VARIANTS, CELL_BREAKPOINTS, CONTROL_SIZES, CONTROL_SURFACES, DELTA_EMPHASES, DELTA_SIZES, DELTA_WEIGHTS, DIALOG_LAYOUTS, DIALOG_WIDTHS, FACT_SIZES, FACT_SUB_TONES, GROUP_SIZES, METER_SIZES, METER_TONES, ROW_GAPS, ROW_PADDINGS, ROW_SIZES, SEGMENTED_SIZES, SKELETON_SHAPES, STAT_SIZES, SUB_TONES, TABLE_ROWS, UI_AMOUNT_MASKED, UiActionBar, UiAlert, UiAmount, UiAsync, UiAvatar, UiAvatarLink, UiBackLink, UiBadge, UiButton, UiCard, UiCellSub, UiChoiceChips, UiDelta, UiDialog, UiDonut, UiExternalLink, UiFact, UiFacts, UiField, UiFieldLeading, UiFieldTrailing, UiFilterChips, UiGroup, UiGroupCell, UiInput, UiLineChart, UiListRow, UiMenu, UiMenuItem, UiMenuTrigger, UiMeter, UiNavItem, UiRow, UiRowAction, UiRowItem, UiRowLink, UiRowTile, UiSegmented, UiSelect, UiSkeleton, UiStat, UiSwitch, UiTab, UiTabBar, UiTable, UiTd, UiTextarea, UiTh, UiTr, formatAmount };
1186
- export type { AlertVariant, AmountFormatOptions, AmountMasking, AmountNumeric, AsyncAlign, AsyncState, AsyncVariant, AvatarSize, AxisTicks, BackLinkSize, BadgeSize, BadgeVariant, ButtonSize, ButtonVariant, CardPadding, CardSurface, CardVariant, CellBreakpoint, ChartPoint, ChoiceChipOption, ControlSize, ControlSurface, DeltaEmphasis, DeltaSize, DeltaWeight, DialogLayout, DialogWidth, DonutSlice, FactSize, FactSubTone, FilterChipOption, GroupSize, MeterSize, MeterTone, RowGap, RowPadding, RowSize, SegmentedOption, SegmentedSize, SkeletonShape, StatSize, SubTone, TableRow, TooltipSize, UiControlError };
1362
+ export { ALERT_VARIANTS, AMOUNT_MASKINGS, AMOUNT_NUMERICS, ASYNC_ALIGNS, ASYNC_STATES, ASYNC_VARIANTS, AVATAR_SIZES, BACK_LINK_SIZES, BADGE_SIZES, BADGE_VARIANTS, BUTTON_SIZES, BUTTON_VARIANTS, CARD_PADDINGS, CARD_SURFACES, CARD_VARIANTS, CELL_BREAKPOINTS, CONTROL_SIZES, CONTROL_SURFACES, DELTA_EMPHASES, DELTA_SIZES, DELTA_WEIGHTS, DIALOG_CLOSE_REASONS, DIALOG_LAYOUTS, DIALOG_WIDTHS, FACT_SIZES, FACT_SUB_TONES, GROUP_SIZES, METER_SIZES, METER_TONES, ROW_GAPS, ROW_PADDINGS, ROW_SIZES, SEGMENTED_SIZES, SKELETON_SHAPES, STAT_SIZES, SUB_TONES, TABLE_ROWS, UI_AMOUNT_MASKED, UiActionBar, UiAlert, UiAmount, UiAsync, UiAvatar, UiAvatarLink, UiBackLink, UiBadge, UiButton, UiCard, UiCellSub, UiChoiceChips, UiDelta, UiDialog, UiDonut, UiEmpty, UiExternalLink, UiFact, UiFacts, UiField, UiFieldLeading, UiFieldTrailing, UiFilterChips, UiFlipList, UiGroup, UiGroupCell, UiHighlight, UiInput, UiLineChart, UiListRow, UiMenu, UiMenuItem, UiMenuTrigger, UiMeter, UiNavItem, UiRow, UiRowAction, UiRowItem, UiRowLink, UiRowTile, UiSegmented, UiSelect, UiSkeleton, UiStaleLink, UiStat, UiSwitch, UiTab, UiTabBar, UiTable, UiTd, UiTextarea, UiTh, UiToaster, UiToasts, UiTr, delayedState, formatAmount, injectReducedMotion };
1363
+ export type { AlertVariant, AmountFormatOptions, AmountMasking, AmountNumeric, AsyncAlign, AsyncState, AsyncVariant, AvatarSize, AxisTicks, BackLinkSize, BadgeSize, BadgeVariant, ButtonSize, ButtonVariant, CardPadding, CardSurface, CardVariant, CellBreakpoint, ChartPoint, ChoiceChipOption, ControlSize, ControlSurface, DeltaEmphasis, DeltaSize, DeltaWeight, DialogCloseReason, DialogLayout, DialogWidth, DonutSlice, FactSize, FactSubTone, FilterChipOption, GroupSize, MeterSize, MeterTone, RowGap, RowPadding, RowSize, SegmentedOption, SegmentedSize, SkeletonShape, StatSize, SubTone, TableRow, Toast, TooltipSize, UiControlError };