@joanroucoux/cairn-ui 0.6.5 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/fesm2022/joanroucoux-cairn-ui.mjs +1305 -180
- package/fesm2022/joanroucoux-cairn-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/motion.css +83 -0
- package/styles/theme.css +6 -0
- package/styles/tokens.css +5 -0
- package/types/joanroucoux-cairn-ui.d.ts +237 -15
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
1
2
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, input, computed, InjectionToken, signal,
|
|
3
|
-
import { NgTemplateOutlet } from '@angular/common';
|
|
3
|
+
import { inject, ElementRef, DestroyRef, afterNextRender, Component, input, booleanAttribute, computed, InjectionToken, signal, LOCALE_ID, effect, untracked, output, Directive, forwardRef, model, viewChildren, afterRenderEffect, contentChild, viewChild, numberAttribute, contentChildren, Injectable } from '@angular/core';
|
|
4
4
|
import { scaleLinear } from 'd3-scale';
|
|
5
5
|
import { curveMonotoneX, line } from 'd3-shape';
|
|
6
6
|
|
|
7
|
+
const HEIGHT_PROPERTY$1 = '--action-bar-height';
|
|
7
8
|
/**
|
|
8
9
|
* Bar of actions fixed above the tab bar on iPhone: the primary actions of a detail screen, within
|
|
9
|
-
* thumb reach. It sits 52px plus the safe area above the bottom edge, so it clears `ui-tab-bar`, and
|
|
10
|
+
* thumb reach. It sits `--tab-bar-height` (52px plus the safe area) above the bottom edge, so it clears `ui-tab-bar`, and
|
|
10
11
|
* it is hidden from `64rem`, where the same actions live in the page itself. Its children share the
|
|
11
|
-
* width equally.
|
|
12
|
+
* width equally. While it is on the page it publishes its height as `--action-bar-height` on the
|
|
13
|
+
* root element, which `ui-toaster` adds to its offset so a confirmation never covers the actions.
|
|
12
14
|
*
|
|
13
15
|
* @example
|
|
14
16
|
* <ui-action-bar>
|
|
@@ -17,8 +19,23 @@ import { curveMonotoneX, line } from 'd3-shape';
|
|
|
17
19
|
* </ui-action-bar>
|
|
18
20
|
*/
|
|
19
21
|
class UiActionBar {
|
|
22
|
+
constructor() {
|
|
23
|
+
const host = inject(ElementRef).nativeElement;
|
|
24
|
+
const root = inject(DOCUMENT).documentElement;
|
|
25
|
+
const destroyRef = inject(DestroyRef);
|
|
26
|
+
const publish = () => root.style.setProperty(HEIGHT_PROPERTY$1, `${host.offsetHeight}px`);
|
|
27
|
+
afterNextRender(() => {
|
|
28
|
+
publish();
|
|
29
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
30
|
+
const observer = new ResizeObserver(publish);
|
|
31
|
+
observer.observe(host);
|
|
32
|
+
destroyRef.onDestroy(() => observer.disconnect());
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
destroyRef.onDestroy(() => root.style.removeProperty(HEIGHT_PROPERTY$1));
|
|
36
|
+
}
|
|
20
37
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiActionBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
21
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: UiActionBar, isStandalone: true, selector: "ui-action-bar", host: { classAttribute: "fixed inset-x-0 bottom-[calc(52px+env(safe-area-inset-bottom))] z-40 grid grid-flow-col auto-cols-fr gap-2 bg-(--background) px-(--gutter) py-3 shadow-[0_-1px_0_var(--hairline)] lg:hidden" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
38
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: UiActionBar, isStandalone: true, selector: "ui-action-bar", host: { classAttribute: "fixed inset-x-0 bottom-[var(--tab-bar-height,calc(52px+env(safe-area-inset-bottom)))] z-40 grid grid-flow-col auto-cols-fr gap-2 bg-(--background) px-(--gutter) py-3 shadow-[0_-1px_0_var(--hairline)] lg:hidden" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
22
39
|
}
|
|
23
40
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiActionBar, decorators: [{
|
|
24
41
|
type: Component,
|
|
@@ -26,19 +43,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
26
43
|
selector: 'ui-action-bar',
|
|
27
44
|
template: '<ng-content />',
|
|
28
45
|
host: {
|
|
29
|
-
class: 'fixed inset-x-0 bottom-[calc(52px+env(safe-area-inset-bottom))] z-40 grid grid-flow-col auto-cols-fr gap-2 bg-(--background) px-(--gutter) py-3 shadow-[0_-1px_0_var(--hairline)] lg:hidden',
|
|
46
|
+
class: 'fixed inset-x-0 bottom-[var(--tab-bar-height,calc(52px+env(safe-area-inset-bottom)))] z-40 grid grid-flow-col auto-cols-fr gap-2 bg-(--background) px-(--gutter) py-3 shadow-[0_-1px_0_var(--hairline)] lg:hidden',
|
|
30
47
|
},
|
|
31
48
|
}]
|
|
32
|
-
}] });
|
|
49
|
+
}], ctorParameters: () => [] });
|
|
33
50
|
|
|
34
51
|
const ALERT_VARIANTS = ['error', 'warning'];
|
|
35
|
-
const BASE_CLASSES$
|
|
52
|
+
const BASE_CLASSES$9 = 'flex items-start gap-2.5 text-left text-label';
|
|
36
53
|
const VARIANT_CLASSES$3 = {
|
|
37
54
|
error: 'rounded-control bg-(--card) px-3.5 py-3 shadow-[inset_0_0_0_1px_var(--border)]',
|
|
38
55
|
warning: 'text-(--foreground)',
|
|
39
56
|
};
|
|
40
57
|
/**
|
|
41
58
|
* Inline message explaining a failure or a consequence: an icon, an optional heading and a text.
|
|
59
|
+
* It fades in when it appears; set `[fadeIn]="false"` on an alert that is part of the page as it
|
|
60
|
+
* opens (an error state after a load), which shows without motion.
|
|
42
61
|
*
|
|
43
62
|
* @example
|
|
44
63
|
* <ui-alert heading="Sign-in failed">Try again.</ui-alert>
|
|
@@ -48,10 +67,11 @@ class UiAlert {
|
|
|
48
67
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
49
68
|
heading = input(/* @ts-ignore */
|
|
50
69
|
...(ngDevMode ? [undefined, { debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
51
|
-
|
|
70
|
+
fadeIn = input(true, { ...(ngDevMode ? { debugName: "fadeIn" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
71
|
+
classes = computed(() => `${BASE_CLASSES$9} ${VARIANT_CLASSES$3[this.variant()]}${this.fadeIn() ? ' ui-enter-fade' : ''}`, /* @ts-ignore */
|
|
52
72
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
53
73
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAlert, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
54
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiAlert, isStandalone: true, selector: "ui-alert", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.role": "variant() === 'warning' ? 'status' : 'alert'" } }, ngImport: i0, template: `
|
|
74
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiAlert, isStandalone: true, selector: "ui-alert", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, fadeIn: { classPropertyName: "fadeIn", publicName: "fadeIn", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "animate.leave": "ui-leave-fade" }, properties: { "class": "classes()", "attr.role": "variant() === 'warning' ? 'status' : 'alert'" } }, ngImport: i0, template: `
|
|
55
75
|
@if (variant() === 'warning') {
|
|
56
76
|
<svg
|
|
57
77
|
aria-hidden="true"
|
|
@@ -141,11 +161,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
141
161
|
</div>
|
|
142
162
|
`,
|
|
143
163
|
host: {
|
|
164
|
+
'animate.leave': 'ui-leave-fade',
|
|
144
165
|
'[class]': 'classes()',
|
|
145
166
|
'[attr.role]': "variant() === 'warning' ? 'status' : 'alert'",
|
|
146
167
|
},
|
|
147
168
|
}]
|
|
148
|
-
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }] } });
|
|
169
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], fadeIn: [{ type: i0.Input, args: [{ isSignal: true, alias: "fadeIn", required: false }] }] } });
|
|
149
170
|
|
|
150
171
|
const MINUS = '−';
|
|
151
172
|
const EM_DASH = '—';
|
|
@@ -252,13 +273,66 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
252
273
|
}]
|
|
253
274
|
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], signed: [{ type: i0.Input, args: [{ isSignal: true, alias: "signed", required: false }] }], fractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "fractionDigits", required: false }] }], numeric: [{ type: i0.Input, args: [{ isSignal: true, alias: "numeric", required: false }] }], whenMasked: [{ type: i0.Input, args: [{ isSignal: true, alias: "whenMasked", required: false }] }] } });
|
|
254
275
|
|
|
276
|
+
const SKELETON_DELAY_MS = 150;
|
|
277
|
+
const SKELETON_MIN_MS = 400;
|
|
278
|
+
/**
|
|
279
|
+
* Turns the live state of a call into the state to draw: `loading` only appears once the call has
|
|
280
|
+
* lasted 150 ms and, once shown, stays at least 400 ms, so a fast call never flashes a skeleton and a
|
|
281
|
+
* slow one never blinks. Until then the previous state stays on screen (a reload keeps its content);
|
|
282
|
+
* `null` means nothing to draw yet, for a first load still under 150 ms. Call it in an injection context.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* protected readonly shown = delayedState(this.state);
|
|
286
|
+
*/
|
|
287
|
+
function delayedState(source) {
|
|
288
|
+
const displayed = signal(null, /* @ts-ignore */
|
|
289
|
+
...(ngDevMode ? [{ debugName: "displayed" }] : /* istanbul ignore next */ []));
|
|
290
|
+
let timer;
|
|
291
|
+
let shownAt = 0;
|
|
292
|
+
const clear = () => {
|
|
293
|
+
clearTimeout(timer);
|
|
294
|
+
timer = undefined;
|
|
295
|
+
};
|
|
296
|
+
inject(DestroyRef).onDestroy(clear);
|
|
297
|
+
effect(() => {
|
|
298
|
+
const state = source();
|
|
299
|
+
untracked(() => {
|
|
300
|
+
if (state === 'loading') {
|
|
301
|
+
if (displayed() === 'loading') {
|
|
302
|
+
clear();
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
timer = setTimeout(() => {
|
|
306
|
+
timer = undefined;
|
|
307
|
+
shownAt = Date.now();
|
|
308
|
+
displayed.set('loading');
|
|
309
|
+
}, SKELETON_DELAY_MS);
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
clear();
|
|
314
|
+
const remaining = shownAt + SKELETON_MIN_MS - Date.now();
|
|
315
|
+
if (displayed() === 'loading' && remaining > 0) {
|
|
316
|
+
timer = setTimeout(() => {
|
|
317
|
+
timer = undefined;
|
|
318
|
+
displayed.set(untracked(source));
|
|
319
|
+
}, remaining);
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
displayed.set(state);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
return displayed.asReadonly();
|
|
327
|
+
}
|
|
328
|
+
|
|
255
329
|
/** Available async block states. `AsyncState` is derived from this tuple. */
|
|
256
330
|
const ASYNC_STATES = ['loading', 'error', 'empty', 'ready'];
|
|
257
331
|
/** Looks of the error block. `AsyncVariant` is derived from this tuple. */
|
|
258
332
|
const ASYNC_VARIANTS = ['elevated', 'plain', 'emphasis', 'inline'];
|
|
259
333
|
/** Alignments of the error block; `auto` is centred from 64rem. `AsyncAlign` is derived from this tuple. */
|
|
260
334
|
const ASYNC_ALIGNS = ['start', 'center', 'auto'];
|
|
261
|
-
const RETRY_BASE_CLASSES = 'relative inline-flex items-center justify-center rounded-control font-medium whitespace-nowrap cursor-pointer select-none touch-manipulation transition-[
|
|
335
|
+
const RETRY_BASE_CLASSES = 'relative inline-flex items-center justify-center rounded-control font-medium whitespace-nowrap cursor-pointer select-none touch-manipulation transition-[scale,background-color,opacity] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) text-label';
|
|
262
336
|
const RETRY_OUTLINE_CLASSES = 'bg-(--card) text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] hover:bg-(--glow)';
|
|
263
337
|
const RETRY_PRIMARY_CLASSES = 'bg-(--primary) text-(--primary-foreground) hover:bg-(--primary-to)';
|
|
264
338
|
const RETRY_BLOCK_CLASSES = 'min-h-(--row-min) gap-2 pl-3 pr-4';
|
|
@@ -334,7 +408,8 @@ class UiAsync {
|
|
|
334
408
|
card = input(false, { ...(ngDevMode ? { debugName: "card" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
335
409
|
fill = input(false, { ...(ngDevMode ? { debugName: "fill" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
336
410
|
retry = output();
|
|
337
|
-
|
|
411
|
+
shown = delayedState(this.state);
|
|
412
|
+
filling = computed(() => this.fill() && this.shown() === 'error', /* @ts-ignore */
|
|
338
413
|
...(ngDevMode ? [{ debugName: "filling" }] : /* istanbul ignore next */ []));
|
|
339
414
|
iconSize = computed(() => (this.variant() === 'inline' ? 16 : 18), /* @ts-ignore */
|
|
340
415
|
...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
|
|
@@ -353,9 +428,9 @@ class UiAsync {
|
|
|
353
428
|
return `flex flex-col ${surface} ${spec.gap} ${ALIGN_CLASSES[this.align()]} ${spec.padding[this.align()]}${fill}`;
|
|
354
429
|
}, /* @ts-ignore */
|
|
355
430
|
...(ngDevMode ? [{ debugName: "boxClasses" }] : /* istanbul ignore next */ []));
|
|
356
|
-
titleClasses = computed(() => this.#spec()
|
|
431
|
+
titleClasses = computed(() => this.#spec().title, /* @ts-ignore */
|
|
357
432
|
...(ngDevMode ? [{ debugName: "titleClasses" }] : /* istanbul ignore next */ []));
|
|
358
|
-
messageClasses = computed(() => this.#spec()
|
|
433
|
+
messageClasses = computed(() => this.#spec().message[this.align()], /* @ts-ignore */
|
|
359
434
|
...(ngDevMode ? [{ debugName: "messageClasses" }] : /* istanbul ignore next */ []));
|
|
360
435
|
retryButtonClasses = computed(() => {
|
|
361
436
|
const spec = this.#spec();
|
|
@@ -367,7 +442,8 @@ class UiAsync {
|
|
|
367
442
|
...(ngDevMode ? [{ debugName: "retryButtonClasses" }] : /* istanbul ignore next */ []));
|
|
368
443
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAsync, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
369
444
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiAsync, isStandalone: true, selector: "ui-async", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, errorTitle: { classPropertyName: "errorTitle", publicName: "errorTitle", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, retryLabel: { classPropertyName: "retryLabel", publicName: "retryLabel", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, fill: { classPropertyName: "fill", publicName: "fill", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retry: "retry" }, host: { properties: { "attr.aria-busy": "state() === 'loading' || null", "class.flex": "filling()", "class.flex-col": "filling()" } }, ngImport: i0, template: `
|
|
370
|
-
@switch (
|
|
445
|
+
@switch (shown()) {
|
|
446
|
+
@case (null) {}
|
|
371
447
|
@case ('loading') {
|
|
372
448
|
<ng-content select="[asyncLoading]" />
|
|
373
449
|
}
|
|
@@ -415,7 +491,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
415
491
|
args: [{
|
|
416
492
|
selector: 'ui-async',
|
|
417
493
|
template: `
|
|
418
|
-
@switch (
|
|
494
|
+
@switch (shown()) {
|
|
495
|
+
@case (null) {}
|
|
419
496
|
@case ('loading') {
|
|
420
497
|
<ng-content select="[asyncLoading]" />
|
|
421
498
|
}
|
|
@@ -467,7 +544,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
467
544
|
|
|
468
545
|
/** Available avatar sizes. `AvatarSize` is derived from this tuple. */
|
|
469
546
|
const AVATAR_SIZES = ['sm', 'md', 'lg', 'auto'];
|
|
470
|
-
const BASE_CLASSES$
|
|
547
|
+
const BASE_CLASSES$8 = 'inline-flex items-center justify-center rounded-pill bg-(--muted) font-medium tabular-nums';
|
|
471
548
|
const SIZE_CLASSES$7 = {
|
|
472
549
|
sm: 'size-8 text-caption',
|
|
473
550
|
md: 'size-[34px] text-label',
|
|
@@ -487,7 +564,7 @@ class UiAvatar {
|
|
|
487
564
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
488
565
|
size = input('md', /* @ts-ignore */
|
|
489
566
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
490
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
567
|
+
classes = computed(() => `${BASE_CLASSES$8} ${SIZE_CLASSES$7[this.size()]}`, /* @ts-ignore */
|
|
491
568
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
492
569
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
493
570
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiAvatar, isStandalone: true, selector: "ui-avatar", inputs: { initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "img" }, properties: { "attr.aria-label": "label()", "class": "classes()" } }, ngImport: i0, template: '<span aria-hidden="true">{{ initials() }}</span>', isInline: true });
|
|
@@ -514,21 +591,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
514
591
|
*/
|
|
515
592
|
class UiAvatarLink {
|
|
516
593
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAvatarLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
517
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiAvatarLink, isStandalone: true, selector: "a[uiAvatarLink], button[uiAvatarLink]", host: { classAttribute: "rounded-pill grid size-11 lg:size-10 place-items-center cursor-pointer select-none touch-manipulation transition-[
|
|
594
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiAvatarLink, isStandalone: true, selector: "a[uiAvatarLink], button[uiAvatarLink]", host: { classAttribute: "rounded-pill grid size-11 lg:size-10 place-items-center cursor-pointer select-none touch-manipulation transition-[scale,background-color] [transition-duration:var(--duration-press),var(--duration-fast)] ease-out hover:bg-(--glow) aria-[current=page]:bg-(--soft) active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring)" }, ngImport: i0 });
|
|
518
595
|
}
|
|
519
596
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAvatarLink, decorators: [{
|
|
520
597
|
type: Directive,
|
|
521
598
|
args: [{
|
|
522
599
|
selector: 'a[uiAvatarLink], button[uiAvatarLink]',
|
|
523
600
|
host: {
|
|
524
|
-
class: 'rounded-pill grid size-11 lg:size-10 place-items-center cursor-pointer select-none touch-manipulation transition-[
|
|
601
|
+
class: 'rounded-pill grid size-11 lg:size-10 place-items-center cursor-pointer select-none touch-manipulation transition-[scale,background-color] [transition-duration:var(--duration-press),var(--duration-fast)] ease-out hover:bg-(--glow) aria-[current=page]:bg-(--soft) active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring)',
|
|
525
602
|
},
|
|
526
603
|
}]
|
|
527
604
|
}] });
|
|
528
605
|
|
|
529
606
|
/** Available back link sizes. `BackLinkSize` is derived from this tuple. */
|
|
530
607
|
const BACK_LINK_SIZES = ['md', 'sm', 'header'];
|
|
531
|
-
const BASE_CLASSES$
|
|
608
|
+
const BASE_CLASSES$7 = 'inline-flex items-center gap-0.5 rounded-control text-body font-medium whitespace-nowrap cursor-pointer select-none touch-manipulation transition-[scale,background-color,color] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-(--ring)';
|
|
532
609
|
const SIZE_CLASSES$6 = {
|
|
533
610
|
md: 'h-11 pr-3 pl-1 text-(--foreground) focus-visible:outline-offset-2',
|
|
534
611
|
sm: 'h-9 pr-2 pl-0.5 text-(--muted-foreground) hover:bg-(--glow) hover:text-(--foreground) focus-visible:outline-offset-2',
|
|
@@ -546,7 +623,7 @@ class UiBackLink {
|
|
|
546
623
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
547
624
|
chevron = computed(() => CHEVRON[this.size()], /* @ts-ignore */
|
|
548
625
|
...(ngDevMode ? [{ debugName: "chevron" }] : /* istanbul ignore next */ []));
|
|
549
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
626
|
+
classes = computed(() => `${BASE_CLASSES$7} ${SIZE_CLASSES$6[this.size()]}`, /* @ts-ignore */
|
|
550
627
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
551
628
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiBackLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
552
629
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiBackLink, isStandalone: true, selector: "a[ui-back-link]", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: `
|
|
@@ -595,7 +672,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
595
672
|
/** Available badge variants. `BadgeVariant` is derived from this tuple. */
|
|
596
673
|
const BADGE_VARIANTS = ['neutral', 'outline'];
|
|
597
674
|
const BADGE_SIZES = ['md', 'sm'];
|
|
598
|
-
const BASE_CLASSES$
|
|
675
|
+
const BASE_CLASSES$6 = 'inline-flex items-center rounded-pill text-caption font-medium';
|
|
599
676
|
const SIZE_CLASSES$5 = {
|
|
600
677
|
md: 'h-6 px-2.5',
|
|
601
678
|
sm: 'h-[22px] px-2',
|
|
@@ -615,7 +692,7 @@ class UiBadge {
|
|
|
615
692
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
616
693
|
size = input('md', /* @ts-ignore */
|
|
617
694
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
618
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
695
|
+
classes = computed(() => `${BASE_CLASSES$6} ${SIZE_CLASSES$5[this.size()]} ${VARIANT_CLASSES$2[this.variant()]}`, /* @ts-ignore */
|
|
619
696
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
620
697
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
621
698
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiBadge, isStandalone: true, selector: "ui-badge", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
@@ -658,7 +735,7 @@ const BUTTON_SIZES = [
|
|
|
658
735
|
'icon',
|
|
659
736
|
'icon-sm',
|
|
660
737
|
];
|
|
661
|
-
const BASE_CLASSES$
|
|
738
|
+
const BASE_CLASSES$5 = 'relative inline-flex items-center justify-center rounded-control font-medium whitespace-nowrap cursor-pointer select-none touch-manipulation transition-[scale,background-color,opacity] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-(--ring) disabled:pointer-events-none disabled:opacity-40 aria-disabled:not-aria-busy:opacity-40';
|
|
662
739
|
const RING_OUTSIDE = 'focus-visible:outline-offset-2';
|
|
663
740
|
const RING_INSIDE = 'focus-visible:-outline-offset-2';
|
|
664
741
|
const OUTLINE = 'bg-(--card) shadow-[inset_0_0_0_1px_var(--border)] hover:bg-(--glow) active:bg-(--soft)';
|
|
@@ -678,10 +755,10 @@ const SIZE_CLASSES$4 = {
|
|
|
678
755
|
slim: 'h-11 lg:h-8 gap-1.5 px-2.5 text-label',
|
|
679
756
|
xs: 'h-8 gap-1.5 px-2.5 text-label',
|
|
680
757
|
sm: 'h-9 gap-2 px-3 text-label',
|
|
681
|
-
md: 'min-h-(--row-min) gap-2 px-4 text-label has-[>svg:first-child]:pl-3',
|
|
758
|
+
md: 'min-h-(--row-min) gap-2 px-4 text-label has-[>span>svg:first-child]:pl-3',
|
|
682
759
|
lg: 'h-11 gap-2 px-[18px] text-label',
|
|
683
760
|
xl: 'h-[50px] gap-2 px-6 text-body',
|
|
684
|
-
xxl: 'h-[52px] lg:h-11 gap-2.5 px-6 lg:px-5 text-body lg:has-[>svg:first-child]:pl-4',
|
|
761
|
+
xxl: 'h-[52px] lg:h-11 gap-2.5 px-6 lg:px-5 text-body lg:has-[>span>svg:first-child]:pl-4',
|
|
685
762
|
tall: 'h-12 lg:h-10 gap-2 px-4 text-body',
|
|
686
763
|
block: 'h-[50px] lg:h-11 gap-2 px-4 text-body',
|
|
687
764
|
icon: 'size-(--row-min) gap-2 p-0',
|
|
@@ -703,7 +780,7 @@ class UiButton {
|
|
|
703
780
|
busy = input(false, { ...(ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
704
781
|
inactive = computed(() => this.loading() || this.busy(), /* @ts-ignore */
|
|
705
782
|
...(ngDevMode ? [{ debugName: "inactive" }] : /* istanbul ignore next */ []));
|
|
706
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
783
|
+
classes = computed(() => `${BASE_CLASSES$5} ${VARIANT_CLASSES$1[this.variant()]} ${SIZE_CLASSES$4[this.size()]}${this.inactive() ? ' pointer-events-none opacity-70' : ''}`, /* @ts-ignore */
|
|
707
784
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
708
785
|
constructor() {
|
|
709
786
|
const host = inject(ElementRef).nativeElement;
|
|
@@ -721,10 +798,10 @@ class UiButton {
|
|
|
721
798
|
@if (loading()) {
|
|
722
799
|
<span
|
|
723
800
|
aria-hidden="true"
|
|
724
|
-
class="rounded-pill size-4 border-2 border-current border-r-transparent
|
|
801
|
+
class="rounded-pill animate-cairn-spin absolute inset-0 m-auto size-4 border-2 border-current border-r-transparent"
|
|
725
802
|
></span>
|
|
726
803
|
}
|
|
727
|
-
<ng-content
|
|
804
|
+
<span class="contents" [class.text-transparent]="loading()"><ng-content /></span>
|
|
728
805
|
`, isInline: true });
|
|
729
806
|
}
|
|
730
807
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiButton, decorators: [{
|
|
@@ -735,10 +812,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
735
812
|
@if (loading()) {
|
|
736
813
|
<span
|
|
737
814
|
aria-hidden="true"
|
|
738
|
-
class="rounded-pill size-4 border-2 border-current border-r-transparent
|
|
815
|
+
class="rounded-pill animate-cairn-spin absolute inset-0 m-auto size-4 border-2 border-current border-r-transparent"
|
|
739
816
|
></span>
|
|
740
817
|
}
|
|
741
|
-
<ng-content
|
|
818
|
+
<span class="contents" [class.text-transparent]="loading()"><ng-content /></span>
|
|
742
819
|
`,
|
|
743
820
|
host: {
|
|
744
821
|
'[class]': 'classes()',
|
|
@@ -756,7 +833,7 @@ const CARD_PADDINGS = ['none', 'xs', 'sm', 'md', 'list', 'rows', 'recap', 'panel
|
|
|
756
833
|
const CARD_SURFACES = ['always', 'lg', 'max-lg'];
|
|
757
834
|
/** Border of the card surface; `auto` follows the variant. `CardBorder` is derived from this tuple. */
|
|
758
835
|
const CARD_BORDERS = ['auto', 'none', 'border', 'hairline'];
|
|
759
|
-
const BASE_CLASSES$
|
|
836
|
+
const BASE_CLASSES$4 = 'block';
|
|
760
837
|
const VARIANT_CLASSES = {
|
|
761
838
|
default: {
|
|
762
839
|
always: 'rounded-container bg-(--card) text-(--card-foreground)',
|
|
@@ -861,7 +938,7 @@ class UiCard {
|
|
|
861
938
|
const surface = this.surface();
|
|
862
939
|
const border = this.border();
|
|
863
940
|
return [
|
|
864
|
-
BASE_CLASSES$
|
|
941
|
+
BASE_CLASSES$4,
|
|
865
942
|
VARIANT_CLASSES[variant][surface],
|
|
866
943
|
BORDER_CLASSES[border === 'auto' ? AUTO_BORDERS[variant] : border][surface],
|
|
867
944
|
PADDING_CLASSES$1[this.padding()][surface],
|
|
@@ -897,7 +974,7 @@ const UI_CONTROL = new InjectionToken('UI_CONTROL');
|
|
|
897
974
|
|
|
898
975
|
/** Available control sizes. `md` is the 44/40px touch target; `lg` and `xl` are the 48px sheet and sign-in fields. */
|
|
899
976
|
const CONTROL_SIZES = ['sm', 'md', 'lg', 'xl'];
|
|
900
|
-
const CONTROL_BASE_CLASSES = 'w-full rounded-control px-3 text-body text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)]
|
|
977
|
+
const CONTROL_BASE_CLASSES = 'w-full rounded-control px-3 text-body text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] placeholder:text-(--muted-foreground) focus-visible:shadow-[inset_0_0_0_2px_var(--ring)] aria-invalid:shadow-[inset_0_0_0_2px_var(--negative)] disabled:cursor-not-allowed disabled:opacity-50';
|
|
901
978
|
const CONTROL_SURFACES = ['background', 'card'];
|
|
902
979
|
const CONTROL_SURFACE_CLASSES = {
|
|
903
980
|
background: 'bg-(--background)',
|
|
@@ -962,7 +1039,7 @@ class UiTextarea {
|
|
|
962
1039
|
touched = input(false, /* @ts-ignore */
|
|
963
1040
|
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
964
1041
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTextarea, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
965
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiTextarea, isStandalone: true, selector: "textarea[uiTextarea]", inputs: { errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "w-full rounded-control px-3 text-body text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)]
|
|
1042
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiTextarea, isStandalone: true, selector: "textarea[uiTextarea]", inputs: { errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "w-full rounded-control px-3 text-body text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] placeholder:text-(--muted-foreground) focus-visible:shadow-[inset_0_0_0_2px_var(--ring)] aria-invalid:shadow-[inset_0_0_0_2px_var(--negative)] disabled:cursor-not-allowed disabled:opacity-50 bg-(--background) min-h-24 py-2" }, providers: [{ provide: UI_CONTROL, useExisting: forwardRef(() => UiTextarea) }], ngImport: i0 });
|
|
966
1043
|
}
|
|
967
1044
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTextarea, decorators: [{
|
|
968
1045
|
type: Directive,
|
|
@@ -1142,7 +1219,7 @@ const DELTA_EMPHASES = ['text', 'pill'];
|
|
|
1142
1219
|
const DELTA_SIZES = ['label', 'inherit'];
|
|
1143
1220
|
/** Font weights. `DeltaWeight` is derived from this tuple. */
|
|
1144
1221
|
const DELTA_WEIGHTS = ['medium', 'regular'];
|
|
1145
|
-
const BASE_CLASSES$
|
|
1222
|
+
const BASE_CLASSES$3 = 'inline-flex items-center gap-1 tabular-nums';
|
|
1146
1223
|
const SIZE_CLASSES$3 = {
|
|
1147
1224
|
label: 'text-label',
|
|
1148
1225
|
inherit: '',
|
|
@@ -1199,7 +1276,7 @@ class UiDelta {
|
|
|
1199
1276
|
classes = computed(() => {
|
|
1200
1277
|
const sign = this.sign();
|
|
1201
1278
|
const background = this.emphasis() === 'pill' ? `${PILL_CLASSES$1} ${PILL_BACKGROUND_CLASS}` : 'bg-transparent';
|
|
1202
|
-
return `${BASE_CLASSES$
|
|
1279
|
+
return `${BASE_CLASSES$3} ${SIZE_CLASSES$3[this.size()]} ${WEIGHT_CLASSES[this.weight()]} ${TEXT_CLASSES[sign]} ${background}`;
|
|
1203
1280
|
}, /* @ts-ignore */
|
|
1204
1281
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
1205
1282
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDelta, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
@@ -1230,6 +1307,244 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1230
1307
|
}]
|
|
1231
1308
|
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], unknownLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "unknownLabel", required: true }] }], emphasis: [{ type: i0.Input, args: [{ isSignal: true, alias: "emphasis", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], weight: [{ type: i0.Input, args: [{ isSignal: true, alias: "weight", required: false }] }] } });
|
|
1232
1309
|
|
|
1310
|
+
const GRACE = 50;
|
|
1311
|
+
function afterExit(element, done) {
|
|
1312
|
+
const style = getComputedStyle(element);
|
|
1313
|
+
const wait = longest(style.transitionDuration) + longest(style.transitionDelay);
|
|
1314
|
+
if (wait === 0) {
|
|
1315
|
+
done();
|
|
1316
|
+
return () => undefined;
|
|
1317
|
+
}
|
|
1318
|
+
const onEnd = (event) => {
|
|
1319
|
+
if (event.target === element && !event.pseudoElement) {
|
|
1320
|
+
cancel();
|
|
1321
|
+
done();
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
const timer = setTimeout(() => {
|
|
1325
|
+
cancel();
|
|
1326
|
+
done();
|
|
1327
|
+
}, wait + GRACE);
|
|
1328
|
+
const cancel = () => {
|
|
1329
|
+
clearTimeout(timer);
|
|
1330
|
+
element.removeEventListener('transitionend', onEnd);
|
|
1331
|
+
};
|
|
1332
|
+
element.addEventListener('transitionend', onEnd);
|
|
1333
|
+
return cancel;
|
|
1334
|
+
}
|
|
1335
|
+
function longest(list) {
|
|
1336
|
+
return Math.max(0, ...list.split(',').map(toMs));
|
|
1337
|
+
}
|
|
1338
|
+
function toMs(value) {
|
|
1339
|
+
const amount = Number.parseFloat(value);
|
|
1340
|
+
if (Number.isNaN(amount)) {
|
|
1341
|
+
return 0;
|
|
1342
|
+
}
|
|
1343
|
+
return value.trim().endsWith('ms') ? amount : amount * 1000;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
const DIALOG_STYLES = `
|
|
1347
|
+
dialog {
|
|
1348
|
+
--dialog-ease: var(--ease-sheet);
|
|
1349
|
+
transition:
|
|
1350
|
+
transform var(--duration-base) var(--dialog-ease),
|
|
1351
|
+
overlay var(--duration-base) allow-discrete,
|
|
1352
|
+
display var(--duration-base) allow-discrete;
|
|
1353
|
+
transform: translateY(var(--drag-y, 0px));
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
dialog:not([open]) {
|
|
1357
|
+
transform: translateY(100%);
|
|
1358
|
+
transition-duration: var(--duration-exit);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
@starting-style {
|
|
1362
|
+
dialog[open] {
|
|
1363
|
+
transform: translateY(100%);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
dialog[data-dragging] {
|
|
1368
|
+
transition: none;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1372
|
+
dialog {
|
|
1373
|
+
transition:
|
|
1374
|
+
opacity var(--duration-base) var(--dialog-ease),
|
|
1375
|
+
overlay var(--duration-base) allow-discrete,
|
|
1376
|
+
display var(--duration-base) allow-discrete;
|
|
1377
|
+
opacity: 1;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
dialog:not([open]) {
|
|
1381
|
+
opacity: 0;
|
|
1382
|
+
transform: translateY(var(--drag-y, 0px));
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
@starting-style {
|
|
1386
|
+
dialog[open] {
|
|
1387
|
+
opacity: 0;
|
|
1388
|
+
transform: translateY(0);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
@media (min-width: 64rem) {
|
|
1394
|
+
dialog {
|
|
1395
|
+
--dialog-ease: var(--ease-out);
|
|
1396
|
+
transition:
|
|
1397
|
+
opacity var(--duration-base) var(--dialog-ease),
|
|
1398
|
+
transform var(--duration-base) var(--dialog-ease),
|
|
1399
|
+
overlay var(--duration-base) allow-discrete,
|
|
1400
|
+
display var(--duration-base) allow-discrete;
|
|
1401
|
+
opacity: 1;
|
|
1402
|
+
transform: scale(1);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
dialog:not([open]) {
|
|
1406
|
+
opacity: 0;
|
|
1407
|
+
transform: scale(var(--enter-scale));
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
@starting-style {
|
|
1411
|
+
dialog[open] {
|
|
1412
|
+
opacity: 0;
|
|
1413
|
+
transform: scale(var(--enter-scale));
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
dialog::backdrop {
|
|
1419
|
+
transition: opacity var(--duration-base) var(--ease-out);
|
|
1420
|
+
opacity: 1;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
dialog:not([open])::backdrop {
|
|
1424
|
+
opacity: 0;
|
|
1425
|
+
transition-duration: var(--duration-exit);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
@starting-style {
|
|
1429
|
+
dialog[open]::backdrop {
|
|
1430
|
+
opacity: 0;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
`;
|
|
1434
|
+
|
|
1435
|
+
const DISMISS_RATIO = 0.3;
|
|
1436
|
+
const DISMISS_SPEED = 0.5;
|
|
1437
|
+
const SPEED_WINDOW = 100;
|
|
1438
|
+
const SLOP = 6;
|
|
1439
|
+
const CONTROLS = 'button, a, input, select, textarea, [role="button"]';
|
|
1440
|
+
class SheetDrag {
|
|
1441
|
+
#panel;
|
|
1442
|
+
#grips;
|
|
1443
|
+
#dismiss;
|
|
1444
|
+
#enabled;
|
|
1445
|
+
#gesture = null;
|
|
1446
|
+
constructor(panel, grips, dismiss, enabled) {
|
|
1447
|
+
this.#panel = panel;
|
|
1448
|
+
this.#grips = grips;
|
|
1449
|
+
this.#dismiss = dismiss;
|
|
1450
|
+
this.#enabled = enabled;
|
|
1451
|
+
for (const grip of grips) {
|
|
1452
|
+
grip.addEventListener('pointerdown', this.#onDown);
|
|
1453
|
+
grip.addEventListener('pointermove', this.#onMove);
|
|
1454
|
+
grip.addEventListener('pointerup', this.#onUp);
|
|
1455
|
+
grip.addEventListener('pointercancel', this.#onCancel);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
reset() {
|
|
1459
|
+
this.#gesture = null;
|
|
1460
|
+
this.#panel.removeAttribute('data-dragging');
|
|
1461
|
+
this.#panel.style.removeProperty('--drag-y');
|
|
1462
|
+
}
|
|
1463
|
+
destroy() {
|
|
1464
|
+
for (const grip of this.#grips) {
|
|
1465
|
+
grip.removeEventListener('pointerdown', this.#onDown);
|
|
1466
|
+
grip.removeEventListener('pointermove', this.#onMove);
|
|
1467
|
+
grip.removeEventListener('pointerup', this.#onUp);
|
|
1468
|
+
grip.removeEventListener('pointercancel', this.#onCancel);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
#onDown = (event) => {
|
|
1472
|
+
const grip = event.currentTarget;
|
|
1473
|
+
if (event.button !== 0 || !this.#enabled() || !isSheet() || event.target.closest(CONTROLS)) {
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
grip.setPointerCapture(event.pointerId);
|
|
1477
|
+
this.#gesture = {
|
|
1478
|
+
id: event.pointerId,
|
|
1479
|
+
startX: event.clientX,
|
|
1480
|
+
startY: event.clientY,
|
|
1481
|
+
dragging: false,
|
|
1482
|
+
height: 0,
|
|
1483
|
+
offset: 0,
|
|
1484
|
+
samples: [{ y: event.clientY, at: event.timeStamp }],
|
|
1485
|
+
};
|
|
1486
|
+
};
|
|
1487
|
+
#onMove = (event) => {
|
|
1488
|
+
const gesture = this.#current(event);
|
|
1489
|
+
if (!gesture) {
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
const dx = event.clientX - gesture.startX;
|
|
1493
|
+
const dy = event.clientY - gesture.startY;
|
|
1494
|
+
if (!gesture.dragging) {
|
|
1495
|
+
if (Math.max(Math.abs(dx), Math.abs(dy)) < SLOP) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (Math.abs(dx) > Math.abs(dy)) {
|
|
1499
|
+
this.#gesture = null;
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
gesture.dragging = true;
|
|
1503
|
+
gesture.height = this.#panel.getBoundingClientRect().height;
|
|
1504
|
+
this.#panel.setAttribute('data-dragging', '');
|
|
1505
|
+
}
|
|
1506
|
+
gesture.offset = Math.max(0, dy);
|
|
1507
|
+
gesture.samples.push({ y: event.clientY, at: event.timeStamp });
|
|
1508
|
+
this.#panel.style.setProperty('--drag-y', `${gesture.offset}px`);
|
|
1509
|
+
};
|
|
1510
|
+
#onUp = (event) => {
|
|
1511
|
+
const gesture = this.#current(event);
|
|
1512
|
+
if (!gesture) {
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
this.#gesture = null;
|
|
1516
|
+
if (!gesture.dragging) {
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
this.#panel.removeAttribute('data-dragging');
|
|
1520
|
+
const release = { y: event.clientY, at: event.timeStamp };
|
|
1521
|
+
if (gesture.offset > gesture.height * DISMISS_RATIO || releaseSpeed(gesture.samples, release) > DISMISS_SPEED) {
|
|
1522
|
+
this.#dismiss();
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
this.reset();
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
#onCancel = (event) => {
|
|
1529
|
+
if (this.#current(event)) {
|
|
1530
|
+
this.#gesture = null;
|
|
1531
|
+
this.#panel.removeAttribute('data-dragging');
|
|
1532
|
+
this.reset();
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
#current(event) {
|
|
1536
|
+
return this.#gesture?.id === event.pointerId ? this.#gesture : null;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
function isSheet() {
|
|
1540
|
+
return typeof matchMedia === 'function' && !matchMedia('(min-width: 64rem)').matches;
|
|
1541
|
+
}
|
|
1542
|
+
function releaseSpeed(samples, last) {
|
|
1543
|
+
const first = samples.find((sample) => sample.at >= last.at - SPEED_WINDOW) ?? last;
|
|
1544
|
+
const elapsed = last.at - first.at;
|
|
1545
|
+
return elapsed > 0 ? (last.y - first.y) / elapsed : 0;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1233
1548
|
/** Named dialog widths; any other CSS length is accepted as is. `DialogWidth` is derived from this tuple. */
|
|
1234
1549
|
const DIALOG_WIDTHS = ['md', 'lg'];
|
|
1235
1550
|
const WIDTH_VALUES = {
|
|
@@ -1280,6 +1595,8 @@ const LAYOUTS = {
|
|
|
1280
1595
|
footer: 'max-lg:pb-[calc(8px+env(safe-area-inset-bottom))] lg:pb-6',
|
|
1281
1596
|
},
|
|
1282
1597
|
};
|
|
1598
|
+
/** What started a close, as `closed` reports it. `DialogCloseReason` is derived from this tuple. */
|
|
1599
|
+
const DIALOG_CLOSE_REASONS = ['escape', 'backdrop', 'cross', 'drag', 'programmatic'];
|
|
1283
1600
|
const nextId$3 = (() => {
|
|
1284
1601
|
let count = 0;
|
|
1285
1602
|
return () => `ui-dialog-${++count}`;
|
|
@@ -1287,6 +1604,9 @@ const nextId$3 = (() => {
|
|
|
1287
1604
|
/**
|
|
1288
1605
|
* Modal dialog on the native <dialog>: a bottom sheet under `64rem`, centered above it.
|
|
1289
1606
|
*
|
|
1607
|
+
* `busy` keeps it open while its action runs: Escape, a click on the backdrop and a drag of the sheet do
|
|
1608
|
+
* nothing and the cross is disabled. The owner disables its own buttons and still closes it through `open`.
|
|
1609
|
+
*
|
|
1290
1610
|
* @example
|
|
1291
1611
|
* <ui-dialog heading="Buy" description="Ferrari" closeLabel="Close" width="528px" [open]="buying()" (dismissed)="buying.set(false)">
|
|
1292
1612
|
* <p>Body</p>
|
|
@@ -1307,7 +1627,9 @@ class UiDialog {
|
|
|
1307
1627
|
width = input('lg', /* @ts-ignore */
|
|
1308
1628
|
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
1309
1629
|
open = input(false, { ...(ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1630
|
+
busy = input(false, { ...(ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1310
1631
|
dismissed = output();
|
|
1632
|
+
closed = output();
|
|
1311
1633
|
headingId = nextId$3();
|
|
1312
1634
|
descriptionId = `${this.headingId}-description`;
|
|
1313
1635
|
bodyId = `${this.headingId}-body`;
|
|
@@ -1327,11 +1649,11 @@ class UiDialog {
|
|
|
1327
1649
|
...(ngDevMode ? [{ debugName: "widthValue" }] : /* istanbul ignore next */ []));
|
|
1328
1650
|
spec = computed(() => LAYOUTS[this.layout()], /* @ts-ignore */
|
|
1329
1651
|
...(ngDevMode ? [{ debugName: "spec" }] : /* istanbul ignore next */ []));
|
|
1330
|
-
headerClasses = computed(() => `flex gap-2 pl-4 lg:gap-3 lg:pl-6 ${this.spec().header} ${this.closeLabel() ? 'pr-2 lg:pr-4' : 'pr-4 lg:pr-6'}`, /* @ts-ignore */
|
|
1652
|
+
headerClasses = computed(() => `flex gap-2 pl-4 max-lg:touch-none lg:gap-3 lg:pl-6 ${this.spec().header} ${this.closeLabel() ? 'pr-2 lg:pr-4' : 'pr-4 lg:pr-6'}`, /* @ts-ignore */
|
|
1331
1653
|
...(ngDevMode ? [{ debugName: "headerClasses" }] : /* istanbul ignore next */ []));
|
|
1332
1654
|
titleClasses = computed(() => this.spec().title, /* @ts-ignore */
|
|
1333
1655
|
...(ngDevMode ? [{ debugName: "titleClasses" }] : /* istanbul ignore next */ []));
|
|
1334
|
-
crossClasses = computed(() => `rounded-pill lg:rounded-control grid size-11 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[
|
|
1656
|
+
crossClasses = computed(() => `rounded-pill lg:rounded-control grid size-11 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[scale,background-color,color] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out outline-none focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-(--ring) active:scale-(--press-scale) active:bg-(--soft) disabled:pointer-events-none disabled:opacity-40 lg:size-9 lg:hover:bg-(--glow) lg:hover:text-(--foreground) ${this.spec().cross}`, /* @ts-ignore */
|
|
1335
1657
|
...(ngDevMode ? [{ debugName: "crossClasses" }] : /* istanbul ignore next */ []));
|
|
1336
1658
|
iconClasses = computed(() => this.spec().icon, /* @ts-ignore */
|
|
1337
1659
|
...(ngDevMode ? [{ debugName: "iconClasses" }] : /* istanbul ignore next */ []));
|
|
@@ -1343,6 +1665,13 @@ class UiDialog {
|
|
|
1343
1665
|
constructor() {
|
|
1344
1666
|
const destroyRef = inject(DestroyRef);
|
|
1345
1667
|
afterNextRender(() => {
|
|
1668
|
+
const host = this.#host.nativeElement;
|
|
1669
|
+
const grips = ['[data-dialog-handle]', '[data-dialog-header]'].map((grip) => host.querySelector(grip));
|
|
1670
|
+
this.#drag = new SheetDrag(this.#dialog, grips, () => this.close('drag'), () => !this.busy());
|
|
1671
|
+
destroyRef.onDestroy(() => {
|
|
1672
|
+
this.#drag?.destroy();
|
|
1673
|
+
this.#cancelExit();
|
|
1674
|
+
});
|
|
1346
1675
|
if (typeof ResizeObserver === 'undefined') {
|
|
1347
1676
|
return;
|
|
1348
1677
|
}
|
|
@@ -1356,20 +1685,45 @@ class UiDialog {
|
|
|
1356
1685
|
// viewChild.required can't target a #private field (NG1053); safe because the template has exactly one <dialog>.
|
|
1357
1686
|
const dialog = this.#host.nativeElement.querySelector('dialog');
|
|
1358
1687
|
if (this.open() && !dialog.open) {
|
|
1688
|
+
this.#cancelExit();
|
|
1689
|
+
this.#drag?.reset();
|
|
1359
1690
|
dialog.showModal();
|
|
1360
1691
|
}
|
|
1361
1692
|
else if (!this.open() && dialog.open) {
|
|
1362
|
-
|
|
1693
|
+
this.close('programmatic');
|
|
1363
1694
|
}
|
|
1364
1695
|
});
|
|
1365
1696
|
}
|
|
1366
1697
|
#pressedOnBackdrop = false;
|
|
1698
|
+
#drag = null;
|
|
1699
|
+
#reason = null;
|
|
1700
|
+
#cancelExit = () => undefined;
|
|
1701
|
+
close(reason) {
|
|
1702
|
+
if (this.#dialog.open) {
|
|
1703
|
+
this.#reason = reason;
|
|
1704
|
+
this.#dialog.close();
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
onCancel(event) {
|
|
1708
|
+
if (event.target !== event.currentTarget) {
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
event.preventDefault();
|
|
1712
|
+
if (!this.busy()) {
|
|
1713
|
+
this.close('escape');
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
onKeydown(event) {
|
|
1717
|
+
if (event.key === 'Escape' && this.busy()) {
|
|
1718
|
+
event.preventDefault();
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1367
1721
|
onPointerDown(event) {
|
|
1368
1722
|
this.#pressedOnBackdrop = event.target === this.#dialog && this.#outside(event);
|
|
1369
1723
|
}
|
|
1370
1724
|
onClick(event) {
|
|
1371
|
-
if (this.#pressedOnBackdrop && event.target === this.#dialog && this.#outside(event)) {
|
|
1372
|
-
this
|
|
1725
|
+
if (this.#pressedOnBackdrop && !this.busy() && event.target === this.#dialog && this.#outside(event)) {
|
|
1726
|
+
this.close('backdrop');
|
|
1373
1727
|
}
|
|
1374
1728
|
this.#pressedOnBackdrop = false;
|
|
1375
1729
|
}
|
|
@@ -1381,23 +1735,48 @@ class UiDialog {
|
|
|
1381
1735
|
return (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom);
|
|
1382
1736
|
}
|
|
1383
1737
|
onNativeClose() {
|
|
1384
|
-
|
|
1385
|
-
|
|
1738
|
+
const asked = this.#reason;
|
|
1739
|
+
this.#reason = null;
|
|
1740
|
+
if (this.#dialog.open) {
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
if (asked === null && this.busy()) {
|
|
1744
|
+
this.#dialog.showModal();
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
const reason = asked ?? 'escape';
|
|
1748
|
+
if (reason !== 'programmatic') {
|
|
1386
1749
|
this.dismissed.emit();
|
|
1387
1750
|
}
|
|
1751
|
+
this.#awaitExit(reason);
|
|
1752
|
+
}
|
|
1753
|
+
#awaitExit(reason) {
|
|
1754
|
+
this.#cancelExit();
|
|
1755
|
+
this.#cancelExit = afterExit(this.#dialog, () => {
|
|
1756
|
+
this.#cancelExit = () => undefined;
|
|
1757
|
+
this.#drag?.reset();
|
|
1758
|
+
this.closed.emit(reason);
|
|
1759
|
+
});
|
|
1388
1760
|
}
|
|
1389
1761
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1390
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiDialog, isStandalone: true, selector: "ui-dialog", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, truncateDescription: { classPropertyName: "truncateDescription", publicName: "truncateDescription", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed" }, host: { listeners: { "pointerdown": "onPointerDown($event)", "click": "onClick($event)" } }, ngImport: i0, template: `
|
|
1762
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiDialog, isStandalone: true, selector: "ui-dialog", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, truncateDescription: { classPropertyName: "truncateDescription", publicName: "truncateDescription", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed", closed: "closed" }, host: { listeners: { "pointerdown": "onPointerDown($event)", "click": "onClick($event)", "keydown": "onKeydown($event)" } }, ngImport: i0, template: `
|
|
1391
1763
|
<dialog
|
|
1392
1764
|
#dlg
|
|
1765
|
+
[attr.aria-busy]="busy() || null"
|
|
1393
1766
|
[attr.aria-describedby]="describedBy()"
|
|
1394
1767
|
[attr.aria-labelledby]="headingId"
|
|
1395
1768
|
[attr.role]="layout() === 'confirm' ? 'alertdialog' : 'dialog'"
|
|
1396
1769
|
[class]="classes()"
|
|
1397
1770
|
[style.--dialog-width]="widthValue()"
|
|
1771
|
+
(cancel)="onCancel($event)"
|
|
1398
1772
|
(close)="onNativeClose()"
|
|
1399
1773
|
>
|
|
1400
|
-
<div
|
|
1774
|
+
<div
|
|
1775
|
+
aria-hidden="true"
|
|
1776
|
+
class="flex h-5 touch-none justify-center lg:hidden"
|
|
1777
|
+
data-dialog-handle
|
|
1778
|
+
[class]="spec().handle"
|
|
1779
|
+
>
|
|
1401
1780
|
<span class="rounded-pill h-[5px] w-9 bg-(--border)"></span>
|
|
1402
1781
|
</div>
|
|
1403
1782
|
|
|
@@ -1417,7 +1796,13 @@ class UiDialog {
|
|
|
1417
1796
|
</div>
|
|
1418
1797
|
|
|
1419
1798
|
@if (closeLabel()) {
|
|
1420
|
-
<button
|
|
1799
|
+
<button
|
|
1800
|
+
type="button"
|
|
1801
|
+
[attr.aria-label]="closeLabel()"
|
|
1802
|
+
[class]="crossClasses()"
|
|
1803
|
+
[disabled]="busy()"
|
|
1804
|
+
(click)="close('cross')"
|
|
1805
|
+
>
|
|
1421
1806
|
<svg
|
|
1422
1807
|
aria-hidden="true"
|
|
1423
1808
|
class="block size-[22px] fill-none stroke-current stroke-[1.75]"
|
|
@@ -1451,21 +1836,32 @@ class UiDialog {
|
|
|
1451
1836
|
<ng-content select="[dialogActions]" />
|
|
1452
1837
|
</div>
|
|
1453
1838
|
</dialog>
|
|
1454
|
-
`, isInline: true, styles: ["dialog{--dialog-ease: var(--ease-sheet);transition:
|
|
1839
|
+
`, isInline: true, styles: ["dialog{--dialog-ease: var(--ease-sheet);transition:transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;transform:translateY(var(--drag-y, 0px))}dialog:not([open]){transform:translateY(100%);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{transform:translateY(100%)}}dialog[data-dragging]{transition:none}@media(prefers-reduced-motion:reduce){dialog{transition:opacity var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1}dialog:not([open]){opacity:0;transform:translateY(var(--drag-y, 0px))}@starting-style{dialog[open]{opacity:0;transform:translateY(0)}}}@media(min-width:64rem){dialog{--dialog-ease: var(--ease-out);transition:opacity var(--duration-base) var(--dialog-ease),transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:scale(1)}dialog:not([open]){opacity:0;transform:scale(var(--enter-scale))}@starting-style{dialog[open]{opacity:0;transform:scale(var(--enter-scale))}}}dialog::backdrop{transition:opacity var(--duration-base) var(--ease-out);opacity:1}dialog:not([open])::backdrop{opacity:0;transition-duration:var(--duration-exit)}@starting-style{dialog[open]::backdrop{opacity:0}}\n"] });
|
|
1455
1840
|
}
|
|
1456
1841
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDialog, decorators: [{
|
|
1457
1842
|
type: Component,
|
|
1458
|
-
args: [{ selector: 'ui-dialog', host: {
|
|
1843
|
+
args: [{ selector: 'ui-dialog', host: {
|
|
1844
|
+
'(pointerdown)': 'onPointerDown($event)',
|
|
1845
|
+
'(click)': 'onClick($event)',
|
|
1846
|
+
'(keydown)': 'onKeydown($event)',
|
|
1847
|
+
}, template: `
|
|
1459
1848
|
<dialog
|
|
1460
1849
|
#dlg
|
|
1850
|
+
[attr.aria-busy]="busy() || null"
|
|
1461
1851
|
[attr.aria-describedby]="describedBy()"
|
|
1462
1852
|
[attr.aria-labelledby]="headingId"
|
|
1463
1853
|
[attr.role]="layout() === 'confirm' ? 'alertdialog' : 'dialog'"
|
|
1464
1854
|
[class]="classes()"
|
|
1465
1855
|
[style.--dialog-width]="widthValue()"
|
|
1856
|
+
(cancel)="onCancel($event)"
|
|
1466
1857
|
(close)="onNativeClose()"
|
|
1467
1858
|
>
|
|
1468
|
-
<div
|
|
1859
|
+
<div
|
|
1860
|
+
aria-hidden="true"
|
|
1861
|
+
class="flex h-5 touch-none justify-center lg:hidden"
|
|
1862
|
+
data-dialog-handle
|
|
1863
|
+
[class]="spec().handle"
|
|
1864
|
+
>
|
|
1469
1865
|
<span class="rounded-pill h-[5px] w-9 bg-(--border)"></span>
|
|
1470
1866
|
</div>
|
|
1471
1867
|
|
|
@@ -1485,7 +1881,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1485
1881
|
</div>
|
|
1486
1882
|
|
|
1487
1883
|
@if (closeLabel()) {
|
|
1488
|
-
<button
|
|
1884
|
+
<button
|
|
1885
|
+
type="button"
|
|
1886
|
+
[attr.aria-label]="closeLabel()"
|
|
1887
|
+
[class]="crossClasses()"
|
|
1888
|
+
[disabled]="busy()"
|
|
1889
|
+
(click)="close('cross')"
|
|
1890
|
+
>
|
|
1489
1891
|
<svg
|
|
1490
1892
|
aria-hidden="true"
|
|
1491
1893
|
class="block size-[22px] fill-none stroke-current stroke-[1.75]"
|
|
@@ -1519,12 +1921,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1519
1921
|
<ng-content select="[dialogActions]" />
|
|
1520
1922
|
</div>
|
|
1521
1923
|
</dialog>
|
|
1522
|
-
`, styles: ["dialog{--dialog-ease: var(--ease-sheet);transition:
|
|
1523
|
-
}], ctorParameters: () => [], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], truncateDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "truncateDescription", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }] } });
|
|
1924
|
+
`, styles: ["dialog{--dialog-ease: var(--ease-sheet);transition:transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;transform:translateY(var(--drag-y, 0px))}dialog:not([open]){transform:translateY(100%);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{transform:translateY(100%)}}dialog[data-dragging]{transition:none}@media(prefers-reduced-motion:reduce){dialog{transition:opacity var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1}dialog:not([open]){opacity:0;transform:translateY(var(--drag-y, 0px))}@starting-style{dialog[open]{opacity:0;transform:translateY(0)}}}@media(min-width:64rem){dialog{--dialog-ease: var(--ease-out);transition:opacity var(--duration-base) var(--dialog-ease),transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:scale(1)}dialog:not([open]){opacity:0;transform:scale(var(--enter-scale))}@starting-style{dialog[open]{opacity:0;transform:scale(var(--enter-scale))}}}dialog::backdrop{transition:opacity var(--duration-base) var(--ease-out);opacity:1}dialog:not([open])::backdrop{opacity:0;transition-duration:var(--duration-exit)}@starting-style{dialog[open]::backdrop{opacity:0}}\n"] }]
|
|
1925
|
+
}], ctorParameters: () => [], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], truncateDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "truncateDescription", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
1524
1926
|
|
|
1525
1927
|
const RADIUS = 78;
|
|
1526
1928
|
const RING_WIDTH = 30;
|
|
1527
|
-
const ACTIVE_RING_WIDTH = 38;
|
|
1528
1929
|
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
|
|
1529
1930
|
const GAP = 2;
|
|
1530
1931
|
function positionSlices(slices) {
|
|
@@ -1571,7 +1972,8 @@ function rankSlices(slices, othersLabel) {
|
|
|
1571
1972
|
|
|
1572
1973
|
const identityValue$1 = (value) => `${value}`;
|
|
1573
1974
|
const identityShare = (share) => `${Math.round(share * 100)}%`;
|
|
1574
|
-
const ROW_CLASSES$1 = 'flex w-full items-center gap-3 min-h-14 px-2 py-1 [font-variant-numeric:var(--numeric)] rounded-control text-left transition-
|
|
1975
|
+
const ROW_CLASSES$1 = 'flex w-full items-center gap-3 min-h-14 px-2 py-1 [font-variant-numeric:var(--numeric)] rounded-control text-left transition-[scale,background-color] [transition-duration:var(--duration-press),var(--duration-fast)] ease-out hover:bg-(--glow) active:bg-(--soft) active:scale-(--press-scale) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';
|
|
1976
|
+
const SLICE_CLASSES = 'cursor-pointer origin-center [transform-box:view-box] transition-[scale,opacity] duration-(--duration-fast) ease-out';
|
|
1575
1977
|
const RAMP_DOT_CLASSES = {
|
|
1576
1978
|
1: 'bg-(--ramp-1)',
|
|
1577
1979
|
2: 'bg-(--ramp-2)',
|
|
@@ -1625,8 +2027,12 @@ class UiDonut {
|
|
|
1625
2027
|
strokeColor(slice) {
|
|
1626
2028
|
return `var(--ramp-${slice.ramp})`;
|
|
1627
2029
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
2030
|
+
sliceClasses(slice) {
|
|
2031
|
+
const id = this.activeId();
|
|
2032
|
+
if (id === null) {
|
|
2033
|
+
return SLICE_CLASSES;
|
|
2034
|
+
}
|
|
2035
|
+
return `${SLICE_CLASSES} ${id === slice.id ? 'scale-104 motion-reduce:scale-100' : 'opacity-50'}`;
|
|
1630
2036
|
}
|
|
1631
2037
|
dotClasses(slice) {
|
|
1632
2038
|
return `size-3 flex-none rounded-pill shadow-[inset_0_0_0_1px_var(--border)] ${RAMP_DOT_CLASSES[slice.ramp]}`;
|
|
@@ -1676,7 +2082,6 @@ class UiDonut {
|
|
|
1676
2082
|
/>
|
|
1677
2083
|
@for (slice of positioned(); track slice.id) {
|
|
1678
2084
|
<circle
|
|
1679
|
-
class="cursor-pointer transition-[stroke-width] duration-(--duration-fast) ease-out"
|
|
1680
2085
|
cx="100"
|
|
1681
2086
|
cy="100"
|
|
1682
2087
|
data-slice
|
|
@@ -1684,8 +2089,9 @@ class UiDonut {
|
|
|
1684
2089
|
[attr.r]="radius"
|
|
1685
2090
|
[attr.stroke-dasharray]="slice.dash"
|
|
1686
2091
|
[attr.stroke-dashoffset]="slice.offset"
|
|
2092
|
+
[attr.stroke-width]="ringWidth"
|
|
2093
|
+
[class]="sliceClasses(slice)"
|
|
1687
2094
|
[style.stroke]="strokeColor(slice)"
|
|
1688
|
-
[style.stroke-width.px]="strokeWidth(slice)"
|
|
1689
2095
|
(click)="select(slice)"
|
|
1690
2096
|
(pointerenter)="highlight(slice.id)"
|
|
1691
2097
|
(pointerleave)="clearHighlight(slice.id)"
|
|
@@ -1771,7 +2177,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1771
2177
|
/>
|
|
1772
2178
|
@for (slice of positioned(); track slice.id) {
|
|
1773
2179
|
<circle
|
|
1774
|
-
class="cursor-pointer transition-[stroke-width] duration-(--duration-fast) ease-out"
|
|
1775
2180
|
cx="100"
|
|
1776
2181
|
cy="100"
|
|
1777
2182
|
data-slice
|
|
@@ -1779,8 +2184,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1779
2184
|
[attr.r]="radius"
|
|
1780
2185
|
[attr.stroke-dasharray]="slice.dash"
|
|
1781
2186
|
[attr.stroke-dashoffset]="slice.offset"
|
|
2187
|
+
[attr.stroke-width]="ringWidth"
|
|
2188
|
+
[class]="sliceClasses(slice)"
|
|
1782
2189
|
[style.stroke]="strokeColor(slice)"
|
|
1783
|
-
[style.stroke-width.px]="strokeWidth(slice)"
|
|
1784
2190
|
(click)="select(slice)"
|
|
1785
2191
|
(pointerenter)="highlight(slice.id)"
|
|
1786
2192
|
(pointerleave)="clearHighlight(slice.id)"
|
|
@@ -1855,6 +2261,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1855
2261
|
}]
|
|
1856
2262
|
}], propDecorators: { slices: [{ type: i0.Input, args: [{ isSignal: true, alias: "slices", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], othersLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "othersLabel", required: true }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], shareFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "shareFormat", required: false }] }], legendHref: [{ type: i0.Input, args: [{ isSignal: true, alias: "legendHref", required: false }] }], sliceSelect: [{ type: i0.Output, args: ["sliceSelect"] }] } });
|
|
1857
2263
|
|
|
2264
|
+
/**
|
|
2265
|
+
* Centred empty state: a title, a hint and an action slot, such as a catalogue search that matches
|
|
2266
|
+
* nothing. The padding is 24px, 48px from 64rem.
|
|
2267
|
+
*
|
|
2268
|
+
* @example
|
|
2269
|
+
* <ui-empty heading="Aucun instrument ne correspond à « zzz »" hint="La recherche porte sur le nom et l'ISIN.">
|
|
2270
|
+
* <button ui-button variant="outline" size="md" type="button">Ajouter une ligne</button>
|
|
2271
|
+
* </ui-empty>
|
|
2272
|
+
*/
|
|
2273
|
+
class UiEmpty {
|
|
2274
|
+
heading = input.required(/* @ts-ignore */
|
|
2275
|
+
...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
2276
|
+
hint = input('', /* @ts-ignore */
|
|
2277
|
+
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
2278
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiEmpty, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2279
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiEmpty, isStandalone: true, selector: "ui-empty", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12" }, ngImport: i0, template: `
|
|
2280
|
+
<span class="text-body font-medium text-pretty">{{ heading() }}</span>
|
|
2281
|
+
@if (hint()) {
|
|
2282
|
+
<span class="text-label text-(--muted-foreground)">{{ hint() }}</span>
|
|
2283
|
+
}
|
|
2284
|
+
<div class="mt-2 empty:hidden"><ng-content /></div>
|
|
2285
|
+
`, isInline: true });
|
|
2286
|
+
}
|
|
2287
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiEmpty, decorators: [{
|
|
2288
|
+
type: Component,
|
|
2289
|
+
args: [{
|
|
2290
|
+
selector: 'ui-empty',
|
|
2291
|
+
template: `
|
|
2292
|
+
<span class="text-body font-medium text-pretty">{{ heading() }}</span>
|
|
2293
|
+
@if (hint()) {
|
|
2294
|
+
<span class="text-label text-(--muted-foreground)">{{ hint() }}</span>
|
|
2295
|
+
}
|
|
2296
|
+
<div class="mt-2 empty:hidden"><ng-content /></div>
|
|
2297
|
+
`,
|
|
2298
|
+
host: { class: 'flex flex-col items-center gap-1.5 px-2 py-6 text-center lg:py-12' },
|
|
2299
|
+
}]
|
|
2300
|
+
}], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }] } });
|
|
2301
|
+
|
|
1858
2302
|
/**
|
|
1859
2303
|
* Link to another site: the label followed by an external-link icon. A 44px target on touch,
|
|
1860
2304
|
* an underline on hover with a mouse.
|
|
@@ -2045,14 +2489,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
2045
2489
|
*/
|
|
2046
2490
|
class UiFieldTrailing {
|
|
2047
2491
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiFieldTrailing, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2048
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiFieldTrailing, isStandalone: true, selector: "[uiFieldTrailing]", host: { classAttribute: "absolute right-0 grid h-full aspect-square place-items-center rounded-control cursor-pointer text-(--muted-foreground) hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-(--ring)" }, ngImport: i0 });
|
|
2492
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiFieldTrailing, isStandalone: true, selector: "[uiFieldTrailing]", host: { classAttribute: "absolute right-0 grid h-full aspect-square place-items-center rounded-control cursor-pointer text-(--muted-foreground) transition-[color] duration-(--duration-fast) ease-out hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-(--ring)" }, ngImport: i0 });
|
|
2049
2493
|
}
|
|
2050
2494
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiFieldTrailing, decorators: [{
|
|
2051
2495
|
type: Directive,
|
|
2052
2496
|
args: [{
|
|
2053
2497
|
selector: '[uiFieldTrailing]',
|
|
2054
2498
|
host: {
|
|
2055
|
-
class: 'absolute right-0 grid h-full aspect-square place-items-center rounded-control cursor-pointer text-(--muted-foreground) hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-(--ring)',
|
|
2499
|
+
class: 'absolute right-0 grid h-full aspect-square place-items-center rounded-control cursor-pointer text-(--muted-foreground) transition-[color] duration-(--duration-fast) ease-out hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-(--ring)',
|
|
2056
2500
|
},
|
|
2057
2501
|
}]
|
|
2058
2502
|
}] });
|
|
@@ -2179,7 +2623,9 @@ class UiField {
|
|
|
2179
2623
|
}
|
|
2180
2624
|
|
|
2181
2625
|
@if (message()) {
|
|
2182
|
-
<p class="text-label font-medium text-(--negative)" role="alert" [id]="errorId">
|
|
2626
|
+
<p animate.enter="ui-enter-fade" class="text-label font-medium text-(--negative)" role="alert" [id]="errorId">
|
|
2627
|
+
{{ message() }}
|
|
2628
|
+
</p>
|
|
2183
2629
|
}
|
|
2184
2630
|
</div>
|
|
2185
2631
|
`, isInline: true });
|
|
@@ -2212,7 +2658,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
2212
2658
|
}
|
|
2213
2659
|
|
|
2214
2660
|
@if (message()) {
|
|
2215
|
-
<p class="text-label font-medium text-(--negative)" role="alert" [id]="errorId">
|
|
2661
|
+
<p animate.enter="ui-enter-fade" class="text-label font-medium text-(--negative)" role="alert" [id]="errorId">
|
|
2662
|
+
{{ message() }}
|
|
2663
|
+
</p>
|
|
2216
2664
|
}
|
|
2217
2665
|
</div>
|
|
2218
2666
|
`,
|
|
@@ -2299,6 +2747,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
2299
2747
|
}]
|
|
2300
2748
|
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }, { type: i0.Output, args: ["valueChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: true }] }] } });
|
|
2301
2749
|
|
|
2750
|
+
const QUERY = '(prefers-reduced-motion: reduce)';
|
|
2751
|
+
/**
|
|
2752
|
+
* A signal that follows the user's `prefers-reduced-motion` preference live.
|
|
2753
|
+
* Call it in an injection context; the listener is removed with that context.
|
|
2754
|
+
*
|
|
2755
|
+
* @example
|
|
2756
|
+
* protected readonly reduced = injectReducedMotion();
|
|
2757
|
+
*/
|
|
2758
|
+
function injectReducedMotion() {
|
|
2759
|
+
if (typeof matchMedia === 'undefined') {
|
|
2760
|
+
return signal(false).asReadonly();
|
|
2761
|
+
}
|
|
2762
|
+
const query = matchMedia(QUERY);
|
|
2763
|
+
const reduced = signal(query.matches, /* @ts-ignore */
|
|
2764
|
+
...(ngDevMode ? [{ debugName: "reduced" }] : /* istanbul ignore next */ []));
|
|
2765
|
+
const listener = (event) => reduced.set(event.matches);
|
|
2766
|
+
query.addEventListener('change', listener);
|
|
2767
|
+
inject(DestroyRef).onDestroy(() => query.removeEventListener('change', listener));
|
|
2768
|
+
return reduced.asReadonly();
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2302
2771
|
const axisTicks = (points, maxTicks = 5) => {
|
|
2303
2772
|
if (points.length <= maxTicks) {
|
|
2304
2773
|
return [...points];
|
|
@@ -2441,6 +2910,23 @@ const buildGeometry = (points, width, height, padding = {}) => {
|
|
|
2441
2910
|
};
|
|
2442
2911
|
};
|
|
2443
2912
|
|
|
2913
|
+
const observeSize = (element, destroyRef, measured) => {
|
|
2914
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
2915
|
+
return;
|
|
2916
|
+
}
|
|
2917
|
+
const { clientWidth: width, clientHeight: height } = element;
|
|
2918
|
+
if (width > 0 && height > 0) {
|
|
2919
|
+
measured({ width, height });
|
|
2920
|
+
}
|
|
2921
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
2922
|
+
if (entry) {
|
|
2923
|
+
measured({ width: entry.contentRect.width, height: entry.contentRect.height });
|
|
2924
|
+
}
|
|
2925
|
+
});
|
|
2926
|
+
observer.observe(element);
|
|
2927
|
+
destroyRef.onDestroy(() => observer.disconnect());
|
|
2928
|
+
};
|
|
2929
|
+
|
|
2444
2930
|
const LINE_GAP = 6;
|
|
2445
2931
|
const COLLISION_MARGIN = 6;
|
|
2446
2932
|
const EDGE_TOLERANCE = 3;
|
|
@@ -2489,7 +2975,15 @@ const placeStartLabel = (points, startY, box, label) => {
|
|
|
2489
2975
|
const TRANSITION_SAMPLES = 120;
|
|
2490
2976
|
const TRANSITION_DURATION = 260;
|
|
2491
2977
|
const easeOutQuint = (t) => 1 - (1 - t) ** 5;
|
|
2492
|
-
const
|
|
2978
|
+
const durationFrom = (value) => {
|
|
2979
|
+
const match = /^\s*([\d.]+)(ms|s)\s*$/.exec(value);
|
|
2980
|
+
const duration = match ? Number(match[1]) * (match[2] === 's' ? 1000 : 1) : Number.NaN;
|
|
2981
|
+
return Number.isFinite(duration) ? duration : TRANSITION_DURATION;
|
|
2982
|
+
};
|
|
2983
|
+
const sameSeries = (a, b) => a === b ||
|
|
2984
|
+
(b !== null &&
|
|
2985
|
+
a.length === b.length &&
|
|
2986
|
+
a.every((point, index) => point.t === b[index].t && point.v === b[index].v));
|
|
2493
2987
|
const monotoneTangents = (xs, ys) => {
|
|
2494
2988
|
const n = xs.length;
|
|
2495
2989
|
const slopes = [];
|
|
@@ -2545,15 +3039,85 @@ const sampleMonotone = (points, sampleXs) => {
|
|
|
2545
3039
|
return (2 * f3 - 3 * f2 + 1) * y0 + (f3 - 2 * f2 + f) * m0 * dx + (-2 * f3 + 3 * f2) * y1 + (f3 - f2) * m1 * dx;
|
|
2546
3040
|
});
|
|
2547
3041
|
};
|
|
2548
|
-
const
|
|
2549
|
-
const
|
|
2550
|
-
const left = to
|
|
2551
|
-
const right = to.
|
|
2552
|
-
const
|
|
2553
|
-
|
|
2554
|
-
const toYs = sampleMonotone(to.points, samples);
|
|
2555
|
-
return buildPolyline(samples, fromYs.map((fromY, index) => fromY + (toYs[index] - fromY) * progress));
|
|
3042
|
+
const polyline = (xs, ys) => xs.map((x, index) => `${index === 0 ? 'M' : 'L'}${x.toFixed(2)},${ys[index].toFixed(2)}`).join(' ');
|
|
3043
|
+
const shapeTransition = (from, to) => {
|
|
3044
|
+
const left = to[0].x;
|
|
3045
|
+
const right = to.at(-1).x;
|
|
3046
|
+
const xs = Array.from({ length: TRANSITION_SAMPLES }, (_, index) => left + ((right - left) * index) / (TRANSITION_SAMPLES - 1));
|
|
3047
|
+
return { xs, from: sampleMonotone(from, xs), to: sampleMonotone(to, xs) };
|
|
2556
3048
|
};
|
|
3049
|
+
const shapeAt = (transition, progress) => transition.from.map((fromY, index) => fromY + (transition.to[index] - fromY) * progress);
|
|
3050
|
+
const animateShape = (from, to, duration, draw, done) => {
|
|
3051
|
+
const transition = shapeTransition(from, to);
|
|
3052
|
+
const start = performance.now();
|
|
3053
|
+
let frame = 0;
|
|
3054
|
+
const step = (now) => {
|
|
3055
|
+
const t = Math.min(1, Math.max(0, (now - start) / duration));
|
|
3056
|
+
if (t === 1) {
|
|
3057
|
+
done();
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
const ys = shapeAt(transition, easeOutQuint(t));
|
|
3061
|
+
draw(transition.xs.map((x, index) => ({ x, y: ys[index] })), polyline(transition.xs, ys));
|
|
3062
|
+
frame = requestAnimationFrame(step);
|
|
3063
|
+
};
|
|
3064
|
+
frame = requestAnimationFrame(step);
|
|
3065
|
+
return () => cancelAnimationFrame(frame);
|
|
3066
|
+
};
|
|
3067
|
+
class ShapeMotion {
|
|
3068
|
+
#view;
|
|
3069
|
+
#points = null;
|
|
3070
|
+
#target = null;
|
|
3071
|
+
#held = null;
|
|
3072
|
+
#drawn = null;
|
|
3073
|
+
#stop = null;
|
|
3074
|
+
constructor(view) {
|
|
3075
|
+
this.#view = view;
|
|
3076
|
+
}
|
|
3077
|
+
follow(points, geometry, options) {
|
|
3078
|
+
const newSeries = !sameSeries(points, this.#points);
|
|
3079
|
+
this.#points = points;
|
|
3080
|
+
if (this.#stop && !newSeries && !options.reduced && geometry?.line === this.#target?.line) {
|
|
3081
|
+
this.#target = geometry;
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
const from = this.#drawn;
|
|
3085
|
+
const held = this.#held ?? this.#target;
|
|
3086
|
+
this.#target = geometry;
|
|
3087
|
+
this.stop();
|
|
3088
|
+
if (!newSeries || !geometry || !from || from.length < 2 || geometry.points.length < 2 || options.reduced) {
|
|
3089
|
+
this.#settle(geometry);
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
const duration = options.duration();
|
|
3093
|
+
if (duration <= 0) {
|
|
3094
|
+
this.#settle(geometry);
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3097
|
+
this.#hold(held);
|
|
3098
|
+
this.#stop = animateShape(from, geometry.points, duration, (shape, line) => this.#draw(shape, line, shape.at(-1)), () => this.#settle(geometry));
|
|
3099
|
+
}
|
|
3100
|
+
stop() {
|
|
3101
|
+
this.#stop?.();
|
|
3102
|
+
this.#stop = null;
|
|
3103
|
+
}
|
|
3104
|
+
#hold(geometry) {
|
|
3105
|
+
this.#held = geometry;
|
|
3106
|
+
this.#view.hold(geometry);
|
|
3107
|
+
}
|
|
3108
|
+
#settle(geometry) {
|
|
3109
|
+
this.#stop = null;
|
|
3110
|
+
this.#hold(null);
|
|
3111
|
+
this.#drawn = null;
|
|
3112
|
+
if (geometry) {
|
|
3113
|
+
this.#draw(geometry.points, geometry.line, geometry.end);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
#draw(shape, line, end) {
|
|
3117
|
+
this.#drawn = shape;
|
|
3118
|
+
this.#view.draw(line, end);
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
2557
3121
|
|
|
2558
3122
|
const WIDE_QUERY = '(min-width: 64rem)';
|
|
2559
3123
|
const wideViewport = () => {
|
|
@@ -2608,16 +3172,30 @@ class UiLineChart {
|
|
|
2608
3172
|
...(ngDevMode ? [{ debugName: "axisGap" }] : /* istanbul ignore next */ []));
|
|
2609
3173
|
axisTicks = input('auto', { ...(ngDevMode ? { debugName: "axisTicks" } : /* istanbul ignore next */ {}), transform: parseAxisTicks });
|
|
2610
3174
|
#destroyRef = inject(DestroyRef);
|
|
3175
|
+
#host = inject(ElementRef);
|
|
2611
3176
|
#wide = wideViewport();
|
|
2612
|
-
#
|
|
2613
|
-
#
|
|
2614
|
-
|
|
3177
|
+
#reducedMotion = injectReducedMotion();
|
|
3178
|
+
#motion = new ShapeMotion({
|
|
3179
|
+
hold: (geometry) => {
|
|
3180
|
+
this.held.set(geometry);
|
|
3181
|
+
this.activeIndex.update((index) => (geometry ? null : index));
|
|
3182
|
+
},
|
|
3183
|
+
draw: (line, end) => {
|
|
3184
|
+
this.lineRef()?.nativeElement.setAttribute('d', line);
|
|
3185
|
+
this.endRef()?.nativeElement.setAttribute('cx', String(end.x));
|
|
3186
|
+
this.endRef()?.nativeElement.setAttribute('cy', String(end.y));
|
|
3187
|
+
},
|
|
3188
|
+
});
|
|
2615
3189
|
svgRef = viewChild.required('svgRef', /* @ts-ignore */
|
|
2616
3190
|
...(ngDevMode ? [{ debugName: "svgRef" }] : /* istanbul ignore next */ []));
|
|
3191
|
+
lineRef = viewChild('lineRef', /* @ts-ignore */
|
|
3192
|
+
...(ngDevMode ? [{ debugName: "lineRef" }] : /* istanbul ignore next */ []));
|
|
3193
|
+
endRef = viewChild('endRef', /* @ts-ignore */
|
|
3194
|
+
...(ngDevMode ? [{ debugName: "endRef" }] : /* istanbul ignore next */ []));
|
|
2617
3195
|
activeIndex = signal(null, /* @ts-ignore */
|
|
2618
3196
|
...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
|
|
2619
|
-
|
|
2620
|
-
...(ngDevMode ? [{ debugName: "
|
|
3197
|
+
held = signal(null, /* @ts-ignore */
|
|
3198
|
+
...(ngDevMode ? [{ debugName: "held" }] : /* istanbul ignore next */ []));
|
|
2621
3199
|
measuredSize = signal(null, /* @ts-ignore */
|
|
2622
3200
|
...(ngDevMode ? [{ debugName: "measuredSize" }] : /* istanbul ignore next */ []));
|
|
2623
3201
|
effectiveWidth = computed(() => this.measuredSize()?.width ?? DEFAULT_WIDTH, /* @ts-ignore */
|
|
@@ -2635,8 +3213,10 @@ class UiLineChart {
|
|
|
2635
3213
|
return buildGeometry(this.points(), this.effectiveWidth(), plotHeight, { x: 0, top: padding, bottom: padding });
|
|
2636
3214
|
}, /* @ts-ignore */
|
|
2637
3215
|
...(ngDevMode ? [{ debugName: "geometry" }] : /* istanbul ignore next */ []));
|
|
2638
|
-
|
|
2639
|
-
...(ngDevMode ? [{ debugName: "
|
|
3216
|
+
shown = computed(() => this.held() ?? this.geometry(), /* @ts-ignore */
|
|
3217
|
+
...(ngDevMode ? [{ debugName: "shown" }] : /* istanbul ignore next */ []));
|
|
3218
|
+
startFade = computed(() => this.held() ? 'opacity-0 duration-(--duration-exit)' : 'duration-(--duration-fast)', /* @ts-ignore */
|
|
3219
|
+
...(ngDevMode ? [{ debugName: "startFade" }] : /* istanbul ignore next */ []));
|
|
2640
3220
|
ticks = computed(() => axisTicks(this.geometry().points), /* @ts-ignore */
|
|
2641
3221
|
...(ngDevMode ? [{ debugName: "ticks" }] : /* istanbul ignore next */ []));
|
|
2642
3222
|
tickTexts = computed(() => tickLabels(this.ticks(), this.axisFormat(), this.axisTicks()), /* @ts-ignore */
|
|
@@ -2644,12 +3224,12 @@ class UiLineChart {
|
|
|
2644
3224
|
active = computed(() => {
|
|
2645
3225
|
const geometry = this.geometry();
|
|
2646
3226
|
const index = this.activeIndex();
|
|
2647
|
-
return geometry && index !== null ? geometry.points[index] : null;
|
|
3227
|
+
return geometry && index !== null && !this.held() ? geometry.points[index] : null;
|
|
2648
3228
|
}, /* @ts-ignore */
|
|
2649
3229
|
...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
2650
3230
|
startLabelInfo = computed(() => {
|
|
2651
3231
|
const text = this.startLabel();
|
|
2652
|
-
const geometry = this.
|
|
3232
|
+
const geometry = this.shown();
|
|
2653
3233
|
if (!text || !geometry) {
|
|
2654
3234
|
return null;
|
|
2655
3235
|
}
|
|
@@ -2659,63 +3239,21 @@ class UiLineChart {
|
|
|
2659
3239
|
return { label, placement: { ...placement, y: startLabelBaseline(placement.y) } };
|
|
2660
3240
|
}, /* @ts-ignore */
|
|
2661
3241
|
...(ngDevMode ? [{ debugName: "startLabelInfo" }] : /* istanbul ignore next */ []));
|
|
2662
|
-
#renderRef = afterNextRender(() =>
|
|
3242
|
+
#renderRef = afterNextRender(() => {
|
|
3243
|
+
this.#destroyRef.onDestroy(() => this.#renderRef.destroy());
|
|
3244
|
+
observeSize(this.svgRef().nativeElement, this.#destroyRef, (size) => this.measuredSize.set(size));
|
|
3245
|
+
});
|
|
2663
3246
|
constructor() {
|
|
2664
3247
|
effect(() => {
|
|
3248
|
+
const points = this.points();
|
|
2665
3249
|
const geometry = this.geometry();
|
|
2666
|
-
const
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
this.#cancelAnimation();
|
|
2672
|
-
if (!this.#hasRenderedOnce) {
|
|
2673
|
-
this.#hasRenderedOnce = true;
|
|
2674
|
-
return;
|
|
2675
|
-
}
|
|
2676
|
-
if (!previous || !geometry) {
|
|
2677
|
-
this.animatedLine.set(null);
|
|
2678
|
-
return;
|
|
2679
|
-
}
|
|
2680
|
-
if (prefersReducedMotion()) {
|
|
2681
|
-
this.animatedLine.set(null);
|
|
2682
|
-
return;
|
|
2683
|
-
}
|
|
2684
|
-
this.#animate(previous, geometry);
|
|
2685
|
-
});
|
|
2686
|
-
this.#destroyRef.onDestroy(() => this.#cancelAnimation());
|
|
2687
|
-
}
|
|
2688
|
-
#observeSize() {
|
|
2689
|
-
this.#destroyRef.onDestroy(() => this.#renderRef.destroy());
|
|
2690
|
-
if (typeof ResizeObserver === 'undefined') {
|
|
2691
|
-
return;
|
|
2692
|
-
}
|
|
2693
|
-
const element = this.svgRef().nativeElement;
|
|
2694
|
-
const observer = new ResizeObserver(([entry]) => {
|
|
2695
|
-
if (entry) {
|
|
2696
|
-
const { width, height } = entry.contentRect;
|
|
2697
|
-
this.measuredSize.set({ width, height });
|
|
2698
|
-
}
|
|
3250
|
+
const reduced = this.#reducedMotion();
|
|
3251
|
+
untracked(() => this.#motion.follow(points, geometry, {
|
|
3252
|
+
reduced,
|
|
3253
|
+
duration: () => durationFrom(getComputedStyle(this.#host.nativeElement).getPropertyValue('--duration-base')),
|
|
3254
|
+
}));
|
|
2699
3255
|
});
|
|
2700
|
-
|
|
2701
|
-
this.#destroyRef.onDestroy(() => observer.disconnect());
|
|
2702
|
-
}
|
|
2703
|
-
#cancelAnimation() {
|
|
2704
|
-
cancelAnimationFrame(this.#frame ?? 0);
|
|
2705
|
-
this.#frame = null;
|
|
2706
|
-
}
|
|
2707
|
-
#animate(from, to) {
|
|
2708
|
-
if (from.points.length < 2 || to.points.length < 2) {
|
|
2709
|
-
this.animatedLine.set(null);
|
|
2710
|
-
return;
|
|
2711
|
-
}
|
|
2712
|
-
const startTime = performance.now();
|
|
2713
|
-
const step = (now) => {
|
|
2714
|
-
const t = Math.min(1, (now - startTime) / TRANSITION_DURATION);
|
|
2715
|
-
this.animatedLine.set(t < 1 ? interpolateLine(from, to, easeOutQuint(t)) : null);
|
|
2716
|
-
this.#frame = t < 1 ? requestAnimationFrame(step) : null;
|
|
2717
|
-
};
|
|
2718
|
-
this.#frame = requestAnimationFrame(step);
|
|
3256
|
+
this.#destroyRef.onDestroy(() => this.#motion.stop());
|
|
2719
3257
|
}
|
|
2720
3258
|
deltaFor(point) {
|
|
2721
3259
|
return point.v - this.points()[0].v;
|
|
@@ -2751,7 +3289,7 @@ class UiLineChart {
|
|
|
2751
3289
|
this.activeIndex.set(navigation.index);
|
|
2752
3290
|
}
|
|
2753
3291
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiLineChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2754
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiLineChart, isStandalone: true, selector: "ui-line-chart", inputs: { points: { classPropertyName: "points", publicName: "points", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, tooltipFormat: { classPropertyName: "tooltipFormat", publicName: "tooltipFormat", isSignal: true, isRequired: false, transformFunction: null }, deltaFormat: { classPropertyName: "deltaFormat", publicName: "deltaFormat", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, axisFormat: { classPropertyName: "axisFormat", publicName: "axisFormat", isSignal: true, isRequired: false, transformFunction: null }, startLabel: { classPropertyName: "startLabel", publicName: "startLabel", isSignal: true, isRequired: false, transformFunction: null }, timeColumnLabel: { classPropertyName: "timeColumnLabel", publicName: "timeColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, valueColumnLabel: { classPropertyName: "valueColumnLabel", publicName: "valueColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, sparkline: { classPropertyName: "sparkline", publicName: "sparkline", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipDelta: { classPropertyName: "tooltipDelta", publicName: "tooltipDelta", isSignal: true, isRequired: false, transformFunction: null }, axisGap: { classPropertyName: "axisGap", publicName: "axisGap", isSignal: true, isRequired: false, transformFunction: null }, axisTicks: { classPropertyName: "axisTicks", publicName: "axisTicks", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block h-full" }, viewQueries: [{ propertyName: "svgRef", first: true, predicate: ["svgRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
3292
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiLineChart, isStandalone: true, selector: "ui-line-chart", inputs: { points: { classPropertyName: "points", publicName: "points", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, tooltipFormat: { classPropertyName: "tooltipFormat", publicName: "tooltipFormat", isSignal: true, isRequired: false, transformFunction: null }, deltaFormat: { classPropertyName: "deltaFormat", publicName: "deltaFormat", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, axisFormat: { classPropertyName: "axisFormat", publicName: "axisFormat", isSignal: true, isRequired: false, transformFunction: null }, startLabel: { classPropertyName: "startLabel", publicName: "startLabel", isSignal: true, isRequired: false, transformFunction: null }, timeColumnLabel: { classPropertyName: "timeColumnLabel", publicName: "timeColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, valueColumnLabel: { classPropertyName: "valueColumnLabel", publicName: "valueColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, sparkline: { classPropertyName: "sparkline", publicName: "sparkline", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipDelta: { classPropertyName: "tooltipDelta", publicName: "tooltipDelta", isSignal: true, isRequired: false, transformFunction: null }, axisGap: { classPropertyName: "axisGap", publicName: "axisGap", isSignal: true, isRequired: false, transformFunction: null }, axisTicks: { classPropertyName: "axisTicks", publicName: "axisTicks", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block h-full" }, viewQueries: [{ propertyName: "svgRef", first: true, predicate: ["svgRef"], descendants: true, isSignal: true }, { propertyName: "lineRef", first: true, predicate: ["lineRef"], descendants: true, isSignal: true }, { propertyName: "endRef", first: true, predicate: ["endRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
2755
3293
|
<div class="relative h-full">
|
|
2756
3294
|
<svg
|
|
2757
3295
|
#svgRef
|
|
@@ -2766,48 +3304,52 @@ class UiLineChart {
|
|
|
2766
3304
|
(pointerleave)="onPointerLeave()"
|
|
2767
3305
|
(pointermove)="onPointerMove($event)"
|
|
2768
3306
|
>
|
|
2769
|
-
@if (
|
|
3307
|
+
@if (shown(); as shape) {
|
|
2770
3308
|
<path
|
|
3309
|
+
#lineRef
|
|
2771
3310
|
class="stroke-(--foreground)"
|
|
2772
3311
|
data-chart-line
|
|
2773
3312
|
fill="none"
|
|
2774
3313
|
stroke-linecap="round"
|
|
2775
3314
|
stroke-linejoin="round"
|
|
2776
|
-
[attr.d]="
|
|
3315
|
+
[attr.d]="shape.line"
|
|
2777
3316
|
[attr.stroke-width]="sparkline() ? 1.5 : 2"
|
|
2778
3317
|
/>
|
|
2779
3318
|
|
|
2780
3319
|
@if (!sparkline()) {
|
|
2781
3320
|
<line
|
|
2782
|
-
class="stroke-(--border)"
|
|
3321
|
+
class="stroke-(--border) transition-opacity ease-(--ease-out)"
|
|
2783
3322
|
data-chart-start-line
|
|
2784
3323
|
stroke-dasharray="3 4"
|
|
2785
3324
|
stroke-width="1"
|
|
2786
3325
|
x1="0"
|
|
2787
3326
|
[attr.x2]="effectiveWidth()"
|
|
2788
|
-
[attr.y1]="
|
|
2789
|
-
[attr.y2]="
|
|
3327
|
+
[attr.y1]="shape.start.y"
|
|
3328
|
+
[attr.y2]="shape.start.y"
|
|
3329
|
+
[class]="startFade()"
|
|
2790
3330
|
/>
|
|
2791
3331
|
|
|
2792
3332
|
@if (startLabelInfo(); as info) {
|
|
2793
3333
|
<text
|
|
2794
|
-
class="text-caption fill-(--subtle-foreground) tabular-nums"
|
|
3334
|
+
class="text-caption fill-(--subtle-foreground) tabular-nums transition-opacity ease-(--ease-out)"
|
|
2795
3335
|
data-chart-start-label
|
|
2796
3336
|
[attr.text-anchor]="info.placement.anchor"
|
|
2797
3337
|
[attr.x]="info.placement.x"
|
|
2798
3338
|
[attr.y]="info.placement.y"
|
|
3339
|
+
[class]="startFade()"
|
|
2799
3340
|
>
|
|
2800
3341
|
{{ info.label }}
|
|
2801
3342
|
</text>
|
|
2802
3343
|
}
|
|
2803
3344
|
|
|
2804
3345
|
<circle
|
|
3346
|
+
#endRef
|
|
2805
3347
|
class="fill-(--foreground) stroke-(--card) [paint-order:stroke]"
|
|
2806
3348
|
data-chart-end
|
|
2807
3349
|
r="4"
|
|
2808
3350
|
stroke-width="6"
|
|
2809
|
-
[attr.cx]="
|
|
2810
|
-
[attr.cy]="
|
|
3351
|
+
[attr.cx]="shape.end.x"
|
|
3352
|
+
[attr.cy]="shape.end.y"
|
|
2811
3353
|
/>
|
|
2812
3354
|
|
|
2813
3355
|
@for (tick of ticks(); track tick.t; let index = $index; let first = $first; let last = $last) {
|
|
@@ -2908,48 +3450,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
2908
3450
|
(pointerleave)="onPointerLeave()"
|
|
2909
3451
|
(pointermove)="onPointerMove($event)"
|
|
2910
3452
|
>
|
|
2911
|
-
@if (
|
|
3453
|
+
@if (shown(); as shape) {
|
|
2912
3454
|
<path
|
|
3455
|
+
#lineRef
|
|
2913
3456
|
class="stroke-(--foreground)"
|
|
2914
3457
|
data-chart-line
|
|
2915
3458
|
fill="none"
|
|
2916
3459
|
stroke-linecap="round"
|
|
2917
3460
|
stroke-linejoin="round"
|
|
2918
|
-
[attr.d]="
|
|
3461
|
+
[attr.d]="shape.line"
|
|
2919
3462
|
[attr.stroke-width]="sparkline() ? 1.5 : 2"
|
|
2920
3463
|
/>
|
|
2921
3464
|
|
|
2922
3465
|
@if (!sparkline()) {
|
|
2923
3466
|
<line
|
|
2924
|
-
class="stroke-(--border)"
|
|
3467
|
+
class="stroke-(--border) transition-opacity ease-(--ease-out)"
|
|
2925
3468
|
data-chart-start-line
|
|
2926
3469
|
stroke-dasharray="3 4"
|
|
2927
3470
|
stroke-width="1"
|
|
2928
3471
|
x1="0"
|
|
2929
3472
|
[attr.x2]="effectiveWidth()"
|
|
2930
|
-
[attr.y1]="
|
|
2931
|
-
[attr.y2]="
|
|
3473
|
+
[attr.y1]="shape.start.y"
|
|
3474
|
+
[attr.y2]="shape.start.y"
|
|
3475
|
+
[class]="startFade()"
|
|
2932
3476
|
/>
|
|
2933
3477
|
|
|
2934
3478
|
@if (startLabelInfo(); as info) {
|
|
2935
3479
|
<text
|
|
2936
|
-
class="text-caption fill-(--subtle-foreground) tabular-nums"
|
|
3480
|
+
class="text-caption fill-(--subtle-foreground) tabular-nums transition-opacity ease-(--ease-out)"
|
|
2937
3481
|
data-chart-start-label
|
|
2938
3482
|
[attr.text-anchor]="info.placement.anchor"
|
|
2939
3483
|
[attr.x]="info.placement.x"
|
|
2940
3484
|
[attr.y]="info.placement.y"
|
|
3485
|
+
[class]="startFade()"
|
|
2941
3486
|
>
|
|
2942
3487
|
{{ info.label }}
|
|
2943
3488
|
</text>
|
|
2944
3489
|
}
|
|
2945
3490
|
|
|
2946
3491
|
<circle
|
|
3492
|
+
#endRef
|
|
2947
3493
|
class="fill-(--foreground) stroke-(--card) [paint-order:stroke]"
|
|
2948
3494
|
data-chart-end
|
|
2949
3495
|
r="4"
|
|
2950
3496
|
stroke-width="6"
|
|
2951
|
-
[attr.cx]="
|
|
2952
|
-
[attr.cy]="
|
|
3497
|
+
[attr.cx]="shape.end.x"
|
|
3498
|
+
[attr.cy]="shape.end.y"
|
|
2953
3499
|
/>
|
|
2954
3500
|
|
|
2955
3501
|
@for (tick of ticks(); track tick.t; let index = $index; let first = $first; let last = $last) {
|
|
@@ -3034,12 +3580,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
3034
3580
|
class: 'block h-full',
|
|
3035
3581
|
},
|
|
3036
3582
|
}]
|
|
3037
|
-
}], ctorParameters: () => [], propDecorators: { points: [{ type: i0.Input, args: [{ isSignal: true, alias: "points", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], tooltipFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipFormat", required: false }] }], deltaFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "deltaFormat", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], axisFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisFormat", required: false }] }], startLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "startLabel", required: false }] }], timeColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeColumnLabel", required: false }] }], valueColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueColumnLabel", required: false }] }], sparkline: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkline", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], tooltipDelta: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDelta", required: false }] }], axisGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisGap", required: false }] }], axisTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisTicks", required: false }] }], svgRef: [{ type: i0.ViewChild, args: ['svgRef', { isSignal: true }] }] } });
|
|
3583
|
+
}], ctorParameters: () => [], propDecorators: { points: [{ type: i0.Input, args: [{ isSignal: true, alias: "points", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], tooltipFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipFormat", required: false }] }], deltaFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "deltaFormat", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], axisFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisFormat", required: false }] }], startLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "startLabel", required: false }] }], timeColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeColumnLabel", required: false }] }], valueColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueColumnLabel", required: false }] }], sparkline: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkline", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], tooltipDelta: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDelta", required: false }] }], axisGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisGap", required: false }] }], axisTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisTicks", required: false }] }], svgRef: [{ type: i0.ViewChild, args: ['svgRef', { isSignal: true }] }], lineRef: [{ type: i0.ViewChild, args: ['lineRef', { isSignal: true }] }], endRef: [{ type: i0.ViewChild, args: ['endRef', { isSignal: true }] }] } });
|
|
3038
3584
|
|
|
3039
|
-
const MENU_CLASSES = 'm-0 min-w-44 p-1 rounded-container bg-(--elevated) shadow-[0_8px_24px_rgb(0_0_0/0.16),inset_0_0_0_1px_var(--border)] text-(--foreground) transition-[opacity,
|
|
3040
|
-
const SHEET_CLASSES = 'max-lg:fixed max-lg:inset-x-4 max-lg:top-auto max-lg:w-auto max-lg:bottom-[calc(76px+env(safe-area-inset-bottom))] max-lg:min-w-0 max-lg:p-1.5 max-lg:bg-(--card) max-lg:shadow-[0_12px_32px_rgb(0_0_0/0.2),inset_0_0_0_1px_var(--border)] max-lg:backdrop:bg-[rgb(0_0_0/0.36)] max-lg:transition-[opacity,translate,overlay,display] max-lg:scale-100 max-lg:translate-y-4 max-lg:open:translate-y-0 max-lg:starting:open:scale-100 max-lg:starting:open:translate-y-4';
|
|
3041
|
-
const SHEET_ITEM_CLASSES = 'flex w-full items-center gap-3 lg:pointer-fine:gap-2.5 min-h-12 lg:min-h-11 lg:pointer-fine:min-h-9 px-3 lg:pointer-fine:px-2.5 rounded-[calc(var(--radius-container)-6px)] lg:rounded-[calc(var(--radius-container)-4px)] text-body lg:pointer-fine:text-label hover:bg-(--glow) focus-visible:bg-(--glow) active:bg-(--soft) outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) [:where(&>svg)]:size-5 [:where(&>svg)]:flex-none lg:[:where(&>svg)]:size-[18px] lg:pointer-fine:[:where(&>svg)]:size-4';
|
|
3042
|
-
const ITEM_CLASSES = 'flex w-full items-center gap-3 pointer-fine:gap-2.5 min-h-11 pointer-fine:min-h-9 px-3 pointer-fine:px-2.5 rounded-[calc(var(--radius-container)-4px)] text-body pointer-fine:text-label hover:bg-(--glow) focus-visible:bg-(--glow) active:bg-(--soft) outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) [:where(&>svg)]:size-[18px] [:where(&>svg)]:flex-none pointer-fine:[:where(&>svg)]:size-4';
|
|
3585
|
+
const MENU_CLASSES = 'm-0 min-w-44 p-1 rounded-container bg-(--elevated) shadow-[0_8px_24px_rgb(0_0_0/0.16),inset_0_0_0_1px_var(--border)] text-(--foreground) transition-[opacity,scale,overlay,display] transition-discrete duration-(--duration-fast) not-open:duration-[120ms] ease-out starting:open:opacity-0 starting:open:scale-(--enter-scale) opacity-0 scale-(--enter-scale) open:opacity-100 open:scale-100';
|
|
3586
|
+
const SHEET_CLASSES = 'max-lg:fixed max-lg:inset-x-4 max-lg:top-auto max-lg:w-auto max-lg:bottom-[calc(76px+env(safe-area-inset-bottom))] max-lg:min-w-0 max-lg:p-1.5 max-lg:bg-(--card) max-lg:shadow-[0_12px_32px_rgb(0_0_0/0.2),inset_0_0_0_1px_var(--border)] max-lg:backdrop:bg-[rgb(0_0_0/0.36)] max-lg:transition-[opacity,translate,overlay,display] max-lg:scale-100 max-lg:translate-y-4 max-lg:open:translate-y-0 max-lg:starting:open:scale-100 max-lg:starting:open:translate-y-4 max-lg:motion-reduce:translate-y-0 max-lg:motion-reduce:starting:open:translate-y-0';
|
|
3587
|
+
const SHEET_ITEM_CLASSES = 'flex w-full items-center gap-3 lg:pointer-fine:gap-2.5 min-h-12 lg:min-h-11 lg:pointer-fine:min-h-9 px-3 lg:pointer-fine:px-2.5 rounded-[calc(var(--radius-container)-6px)] lg:rounded-[calc(var(--radius-container)-4px)] text-body lg:pointer-fine:text-label hover:bg-(--glow) focus-visible:bg-(--glow) active:bg-(--soft) transition-[background-color] duration-(--duration-fast) ease-out outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) [:where(&>svg)]:size-5 [:where(&>svg)]:flex-none lg:[:where(&>svg)]:size-[18px] lg:pointer-fine:[:where(&>svg)]:size-4';
|
|
3588
|
+
const ITEM_CLASSES = 'flex w-full items-center gap-3 pointer-fine:gap-2.5 min-h-11 pointer-fine:min-h-9 px-3 pointer-fine:px-2.5 rounded-[calc(var(--radius-container)-4px)] text-body pointer-fine:text-label hover:bg-(--glow) focus-visible:bg-(--glow) active:bg-(--soft) transition-[background-color] duration-(--duration-fast) ease-out outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) [:where(&>svg)]:size-[18px] [:where(&>svg)]:flex-none pointer-fine:[:where(&>svg)]:size-4';
|
|
3043
3589
|
const nextId$1 = (() => {
|
|
3044
3590
|
let count = 0;
|
|
3045
3591
|
return () => `ui-menu-${++count}`;
|
|
@@ -3341,7 +3887,369 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
3341
3887
|
}]
|
|
3342
3888
|
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], valueText: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueText", required: true }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
3343
3889
|
|
|
3344
|
-
|
|
3890
|
+
class FlipItems {
|
|
3891
|
+
items = new Set();
|
|
3892
|
+
destroyed = new Set();
|
|
3893
|
+
used = false;
|
|
3894
|
+
get marked() {
|
|
3895
|
+
return this.used;
|
|
3896
|
+
}
|
|
3897
|
+
add(item) {
|
|
3898
|
+
this.used = true;
|
|
3899
|
+
this.items.add(item);
|
|
3900
|
+
}
|
|
3901
|
+
remove(item) {
|
|
3902
|
+
this.destroyed.add(item);
|
|
3903
|
+
}
|
|
3904
|
+
inside(host) {
|
|
3905
|
+
const inside = [];
|
|
3906
|
+
for (const item of this.items) {
|
|
3907
|
+
if (host.contains(item)) {
|
|
3908
|
+
inside.push(item);
|
|
3909
|
+
}
|
|
3910
|
+
else if (this.destroyed.has(item)) {
|
|
3911
|
+
this.items.delete(item);
|
|
3912
|
+
this.destroyed.delete(item);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
return inside;
|
|
3916
|
+
}
|
|
3917
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: FlipItems, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3918
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: FlipItems });
|
|
3919
|
+
}
|
|
3920
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: FlipItems, decorators: [{
|
|
3921
|
+
type: Injectable
|
|
3922
|
+
}] });
|
|
3923
|
+
|
|
3924
|
+
function readDuration$1(element, name, fallback) {
|
|
3925
|
+
const raw = getComputedStyle(element).getPropertyValue(name).trim();
|
|
3926
|
+
const value = parseFloat(raw);
|
|
3927
|
+
if (Number.isNaN(value)) {
|
|
3928
|
+
return fallback;
|
|
3929
|
+
}
|
|
3930
|
+
return raw.endsWith('ms') ? value : value * 1000;
|
|
3931
|
+
}
|
|
3932
|
+
function readEasing(element, name, fallback) {
|
|
3933
|
+
return getComputedStyle(element).getPropertyValue(name).trim() || fallback;
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
function translateY(element) {
|
|
3937
|
+
const matrix = /^matrix\((.+)\)$/.exec(getComputedStyle(element).transform);
|
|
3938
|
+
return matrix ? parseFloat(matrix[1].split(',')[5]) : 0;
|
|
3939
|
+
}
|
|
3940
|
+
/**
|
|
3941
|
+
* Slides the items of a list into place when one is removed or inserted (FLIP): it measures every item
|
|
3942
|
+
* before and after the change, from the top of the list itself, then animates the shift in `translateY`
|
|
3943
|
+
* over `--duration-base` `--ease-out`. Pair it with `animate.leave="ui-leave-fade"` on the item so it fades
|
|
3944
|
+
* out first. Nothing moves under `prefers-reduced-motion`: the list closes up at once.
|
|
3945
|
+
*
|
|
3946
|
+
* By default the items are the direct children of the list. Mark them with `uiFlipItem` instead when they
|
|
3947
|
+
* sit deeper, for instance a page of several groups: put `uiFlipList` on the element around every group and
|
|
3948
|
+
* `uiFlipItem` on each group heading and row, so a removal also slides the groups below. An item may sit
|
|
3949
|
+
* inside another item (a row inside a card): it then moves by its own share and its card carries the rest.
|
|
3950
|
+
* Only an item added or removed starts a slide; a change inside an item does not. Positions leave out the
|
|
3951
|
+
* translate of any animation running inside the list (a slide in progress, an enter fade-up).
|
|
3952
|
+
*
|
|
3953
|
+
* @example
|
|
3954
|
+
* <ul uiFlipList>
|
|
3955
|
+
* @for (key of keys(); track key.id) {
|
|
3956
|
+
* <li uiListRow animate.leave="ui-leave-fade">...</li>
|
|
3957
|
+
* }
|
|
3958
|
+
* </ul>
|
|
3959
|
+
*
|
|
3960
|
+
* <table uiTable uiFlipList>
|
|
3961
|
+
* @for (group of groups(); track group.id) {
|
|
3962
|
+
* <tbody>
|
|
3963
|
+
* <tr uiTr group uiFlipItem>...</tr>
|
|
3964
|
+
* @for (row of group.rows; track row.id) {
|
|
3965
|
+
* <tr uiTr uiFlipItem animate.leave="ui-leave-fade">...</tr>
|
|
3966
|
+
* }
|
|
3967
|
+
* </tbody>
|
|
3968
|
+
* }
|
|
3969
|
+
* </table>
|
|
3970
|
+
*/
|
|
3971
|
+
class UiFlipList {
|
|
3972
|
+
host = inject(ElementRef).nativeElement;
|
|
3973
|
+
reduced = injectReducedMotion();
|
|
3974
|
+
mutations = new MutationObserver(() => this.onChange());
|
|
3975
|
+
resizes = new ResizeObserver(() => {
|
|
3976
|
+
this.tops = this.measure();
|
|
3977
|
+
});
|
|
3978
|
+
running = new Map();
|
|
3979
|
+
watched = new Set();
|
|
3980
|
+
items = inject(FlipItems, { self: true });
|
|
3981
|
+
tops = new Map();
|
|
3982
|
+
shifts = new Map();
|
|
3983
|
+
constructor() {
|
|
3984
|
+
afterNextRender(() => {
|
|
3985
|
+
this.tops = this.measure();
|
|
3986
|
+
this.mutations.observe(this.host, { childList: true, subtree: true });
|
|
3987
|
+
this.resizes.observe(this.host);
|
|
3988
|
+
});
|
|
3989
|
+
inject(DestroyRef).onDestroy(() => {
|
|
3990
|
+
this.mutations.disconnect();
|
|
3991
|
+
this.resizes.disconnect();
|
|
3992
|
+
});
|
|
3993
|
+
}
|
|
3994
|
+
tracked() {
|
|
3995
|
+
if (!this.items.marked) {
|
|
3996
|
+
return Array.from(this.host.children).filter((child) => child instanceof HTMLElement);
|
|
3997
|
+
}
|
|
3998
|
+
return this.items.inside(this.host);
|
|
3999
|
+
}
|
|
4000
|
+
measure() {
|
|
4001
|
+
const elements = this.tracked();
|
|
4002
|
+
const origin = this.host.getBoundingClientRect().top - this.host.scrollTop;
|
|
4003
|
+
const tops = new Map(elements.map((element) => [element, element.getBoundingClientRect().top - origin]));
|
|
4004
|
+
this.shifts = new Map();
|
|
4005
|
+
for (const animation of this.host.getAnimations({ subtree: true })) {
|
|
4006
|
+
const target = animation.effect.target;
|
|
4007
|
+
if (target !== this.host) {
|
|
4008
|
+
this.shifts.set(target, translateY(target));
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
for (const [moving, shift] of this.shifts) {
|
|
4012
|
+
for (const [element, top] of tops) {
|
|
4013
|
+
if (moving.contains(element)) {
|
|
4014
|
+
tops.set(element, top - shift);
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
for (const element of elements) {
|
|
4019
|
+
if (!this.watched.has(element)) {
|
|
4020
|
+
this.watched.add(element);
|
|
4021
|
+
this.resizes.observe(element);
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
for (const element of this.watched) {
|
|
4025
|
+
if (!tops.has(element)) {
|
|
4026
|
+
this.watched.delete(element);
|
|
4027
|
+
this.running.delete(element);
|
|
4028
|
+
this.resizes.unobserve(element);
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
return tops;
|
|
4032
|
+
}
|
|
4033
|
+
onChange() {
|
|
4034
|
+
const before = this.tops;
|
|
4035
|
+
const after = this.measure();
|
|
4036
|
+
this.tops = after;
|
|
4037
|
+
const changed = after.size !== before.size || Array.from(after.keys()).some((element) => !before.has(element));
|
|
4038
|
+
if (this.reduced() || !changed) {
|
|
4039
|
+
return;
|
|
4040
|
+
}
|
|
4041
|
+
const duration = readDuration$1(this.host, '--duration-base', 260);
|
|
4042
|
+
const easing = readEasing(this.host, '--ease-out', 'ease-out');
|
|
4043
|
+
const moved = (element) => (before.get(element) ?? 0) - (after.get(element) ?? 0);
|
|
4044
|
+
for (const [element] of after) {
|
|
4045
|
+
if (!before.has(element)) {
|
|
4046
|
+
continue;
|
|
4047
|
+
}
|
|
4048
|
+
const own = moved(element) - moved(this.parentItem(element, before, after));
|
|
4049
|
+
if (Math.abs(own) < 0.5) {
|
|
4050
|
+
continue;
|
|
4051
|
+
}
|
|
4052
|
+
const from = own + (this.shifts.get(element) ?? 0);
|
|
4053
|
+
this.running.get(element)?.cancel();
|
|
4054
|
+
const animation = element.animate([{ transform: `translateY(${from}px)` }, { transform: 'translateY(0)' }], {
|
|
4055
|
+
duration,
|
|
4056
|
+
easing,
|
|
4057
|
+
});
|
|
4058
|
+
animation.onfinish = () => {
|
|
4059
|
+
if (this.running.get(element) === animation) {
|
|
4060
|
+
this.running.delete(element);
|
|
4061
|
+
}
|
|
4062
|
+
};
|
|
4063
|
+
this.running.set(element, animation);
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
parentItem(element, before, after) {
|
|
4067
|
+
let node = element.parentElement;
|
|
4068
|
+
while (node !== this.host && !(before.has(node) && after.has(node))) {
|
|
4069
|
+
node = node.parentElement;
|
|
4070
|
+
}
|
|
4071
|
+
return node;
|
|
4072
|
+
}
|
|
4073
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiFlipList, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4074
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiFlipList, isStandalone: true, selector: "[uiFlipList]", providers: [FlipItems], ngImport: i0 });
|
|
4075
|
+
}
|
|
4076
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiFlipList, decorators: [{
|
|
4077
|
+
type: Directive,
|
|
4078
|
+
args: [{
|
|
4079
|
+
selector: '[uiFlipList]',
|
|
4080
|
+
providers: [FlipItems],
|
|
4081
|
+
}]
|
|
4082
|
+
}], ctorParameters: () => [] });
|
|
4083
|
+
/**
|
|
4084
|
+
* Marks an element as an item of the nearest `uiFlipList`, at any depth: a group heading, a row inside a
|
|
4085
|
+
* group, a card. Once one item is marked, the list follows its marked items only.
|
|
4086
|
+
*
|
|
4087
|
+
* @example
|
|
4088
|
+
* <tr uiTr uiFlipItem animate.leave="ui-leave-fade">...</tr>
|
|
4089
|
+
*/
|
|
4090
|
+
class UiFlipItem {
|
|
4091
|
+
constructor() {
|
|
4092
|
+
const items = inject(FlipItems, { optional: true });
|
|
4093
|
+
if (items) {
|
|
4094
|
+
const element = inject(ElementRef).nativeElement;
|
|
4095
|
+
items.add(element);
|
|
4096
|
+
inject(DestroyRef).onDestroy(() => items.remove(element));
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiFlipItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4100
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiFlipItem, isStandalone: true, selector: "[uiFlipItem]", ngImport: i0 });
|
|
4101
|
+
}
|
|
4102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiFlipItem, decorators: [{
|
|
4103
|
+
type: Directive,
|
|
4104
|
+
args: [{
|
|
4105
|
+
selector: '[uiFlipItem]',
|
|
4106
|
+
}]
|
|
4107
|
+
}], ctorParameters: () => [] });
|
|
4108
|
+
|
|
4109
|
+
const HOLD_MS = 200;
|
|
4110
|
+
const SCROLL_SETTLE_MS = 200;
|
|
4111
|
+
const SCROLL_START_MS = 300;
|
|
4112
|
+
function paintedElements(host) {
|
|
4113
|
+
if (host.tagName === 'TR') {
|
|
4114
|
+
return Array.from(host.children).filter((cell) => cell instanceof HTMLElement);
|
|
4115
|
+
}
|
|
4116
|
+
if (host.hasAttribute('ui-group-cell') && host.firstElementChild instanceof HTMLElement) {
|
|
4117
|
+
return [host.firstElementChild];
|
|
4118
|
+
}
|
|
4119
|
+
return [host];
|
|
4120
|
+
}
|
|
4121
|
+
/**
|
|
4122
|
+
* Draws the eye to an element that just changed: scrolls it into view when it is off screen, holds the
|
|
4123
|
+
* `--soft` fill for 200 ms, then fades back to the element's own background over `--duration-highlight`.
|
|
4124
|
+
* Each new non-null token plays once, so pass a fresh value per event (an object or a counter), not a
|
|
4125
|
+
* boolean. A non-null token already present at first render plays on arrival. On a `tr` it paints the
|
|
4126
|
+
* cells, on a `td[ui-group-cell]` the band, anywhere else the host. Under `prefers-reduced-motion` the
|
|
4127
|
+
* scroll is instant and the fade lasts 600 ms.
|
|
4128
|
+
*
|
|
4129
|
+
* It starts on the next frame and, when the element is still playing a finite animation of its own (an
|
|
4130
|
+
* `animate.enter` fade on a new row), once that animation has finished, waiting no longer than
|
|
4131
|
+
* `--duration-base`: the element fades in, then lights up. Both can sit on the same element.
|
|
4132
|
+
*
|
|
4133
|
+
* Give the element `scroll-margin-top` and `scroll-margin-bottom` equal to what is pinned above and
|
|
4134
|
+
* below it (header, tab bar, action bar): a row under them counts as off screen and is scrolled out.
|
|
4135
|
+
* For an arrival on a page opened at the element, scroll to it at once first (`scrollIntoView` in
|
|
4136
|
+
* `afterNextRender`) and set the token afterwards: a token present at first render scrolls smoothly
|
|
4137
|
+
* from where the page is.
|
|
4138
|
+
*
|
|
4139
|
+
* @example
|
|
4140
|
+
* <a ui-row [uiHighlight]="changed() === holding.id ? token() : null" routerLink="/holdings/1">...</a>
|
|
4141
|
+
*/
|
|
4142
|
+
class UiHighlight {
|
|
4143
|
+
uiHighlight = input(null, /* @ts-ignore */
|
|
4144
|
+
...(ngDevMode ? [{ debugName: "uiHighlight" }] : /* istanbul ignore next */ []));
|
|
4145
|
+
host = inject(ElementRef).nativeElement;
|
|
4146
|
+
reduced = injectReducedMotion();
|
|
4147
|
+
cancel = null;
|
|
4148
|
+
constructor() {
|
|
4149
|
+
effect(() => {
|
|
4150
|
+
const token = this.uiHighlight();
|
|
4151
|
+
if (token === null || token === undefined) {
|
|
4152
|
+
return;
|
|
4153
|
+
}
|
|
4154
|
+
untracked(() => this.play());
|
|
4155
|
+
});
|
|
4156
|
+
inject(DestroyRef).onDestroy(() => this.cancel?.());
|
|
4157
|
+
}
|
|
4158
|
+
play() {
|
|
4159
|
+
this.cancel?.();
|
|
4160
|
+
if (this.isVisible()) {
|
|
4161
|
+
this.cancel = this.flash();
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
const reduced = this.reduced();
|
|
4165
|
+
this.host.scrollIntoView({ behavior: reduced ? 'auto' : 'smooth', block: 'center' });
|
|
4166
|
+
if (reduced) {
|
|
4167
|
+
this.cancel = this.flash();
|
|
4168
|
+
return;
|
|
4169
|
+
}
|
|
4170
|
+
let stopFlash = null;
|
|
4171
|
+
let timer;
|
|
4172
|
+
const moves = (event) => event.target === document || (event.target instanceof Node && event.target.contains(this.host));
|
|
4173
|
+
const start = () => {
|
|
4174
|
+
stopListening();
|
|
4175
|
+
stopFlash = this.flash();
|
|
4176
|
+
};
|
|
4177
|
+
const onScrollEnd = (event) => {
|
|
4178
|
+
if (moves(event)) {
|
|
4179
|
+
start();
|
|
4180
|
+
}
|
|
4181
|
+
};
|
|
4182
|
+
const onScroll = (event) => {
|
|
4183
|
+
if (moves(event)) {
|
|
4184
|
+
clearTimeout(timer);
|
|
4185
|
+
timer = setTimeout(start, SCROLL_SETTLE_MS);
|
|
4186
|
+
}
|
|
4187
|
+
};
|
|
4188
|
+
const stopListening = () => {
|
|
4189
|
+
clearTimeout(timer);
|
|
4190
|
+
window.removeEventListener('scrollend', onScrollEnd, true);
|
|
4191
|
+
window.removeEventListener('scroll', onScroll, true);
|
|
4192
|
+
};
|
|
4193
|
+
timer = setTimeout(start, SCROLL_START_MS);
|
|
4194
|
+
window.addEventListener('scrollend', onScrollEnd, true);
|
|
4195
|
+
window.addEventListener('scroll', onScroll, true);
|
|
4196
|
+
this.cancel = () => {
|
|
4197
|
+
stopListening();
|
|
4198
|
+
stopFlash?.();
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4201
|
+
isVisible() {
|
|
4202
|
+
const rect = this.host.getBoundingClientRect();
|
|
4203
|
+
const style = getComputedStyle(this.host);
|
|
4204
|
+
const top = parseFloat(style.scrollMarginTop) || 0;
|
|
4205
|
+
const bottom = parseFloat(style.scrollMarginBottom) || 0;
|
|
4206
|
+
return rect.top >= top && rect.bottom <= window.innerHeight - bottom;
|
|
4207
|
+
}
|
|
4208
|
+
flash() {
|
|
4209
|
+
const painted = paintedElements(this.host);
|
|
4210
|
+
let animations = [];
|
|
4211
|
+
let timer;
|
|
4212
|
+
const paint = () => {
|
|
4213
|
+
const fade = readDuration$1(this.host, '--duration-highlight', 1200);
|
|
4214
|
+
const easing = readEasing(this.host, '--ease-out', 'ease-out');
|
|
4215
|
+
const total = HOLD_MS + fade;
|
|
4216
|
+
animations = painted.map((element) => element.animate([
|
|
4217
|
+
{ backgroundColor: 'var(--soft)', offset: 0 },
|
|
4218
|
+
{ backgroundColor: 'var(--soft)', offset: HOLD_MS / total, easing },
|
|
4219
|
+
{ offset: 1 },
|
|
4220
|
+
], { duration: total }));
|
|
4221
|
+
};
|
|
4222
|
+
const frame = requestAnimationFrame(() => {
|
|
4223
|
+
const remaining = Array.from(new Set([this.host, ...painted]))
|
|
4224
|
+
.flatMap((element) => element.getAnimations())
|
|
4225
|
+
.map((animation) => {
|
|
4226
|
+
const timing = animation.effect.getComputedTiming();
|
|
4227
|
+
return Number(timing.endTime) - Number(timing.localTime ?? 0);
|
|
4228
|
+
})
|
|
4229
|
+
.filter((left) => Number.isFinite(left));
|
|
4230
|
+
if (remaining.length === 0) {
|
|
4231
|
+
paint();
|
|
4232
|
+
return;
|
|
4233
|
+
}
|
|
4234
|
+
timer = setTimeout(paint, Math.min(Math.max(...remaining), readDuration$1(this.host, '--duration-base', 260)));
|
|
4235
|
+
});
|
|
4236
|
+
return () => {
|
|
4237
|
+
cancelAnimationFrame(frame);
|
|
4238
|
+
clearTimeout(timer);
|
|
4239
|
+
animations.forEach((animation) => animation.cancel());
|
|
4240
|
+
};
|
|
4241
|
+
}
|
|
4242
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiHighlight, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4243
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiHighlight, isStandalone: true, selector: "[uiHighlight]", inputs: { uiHighlight: { classPropertyName: "uiHighlight", publicName: "uiHighlight", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
4244
|
+
}
|
|
4245
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiHighlight, decorators: [{
|
|
4246
|
+
type: Directive,
|
|
4247
|
+
args: [{
|
|
4248
|
+
selector: '[uiHighlight]',
|
|
4249
|
+
}]
|
|
4250
|
+
}], ctorParameters: () => [], propDecorators: { uiHighlight: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiHighlight", required: false }] }] } });
|
|
4251
|
+
|
|
4252
|
+
const BASE_CLASSES$2 = 'flex items-center gap-3 h-10 px-3 rounded-control text-label leading-normal font-medium transition-[scale,background-color,color] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out active:scale-(--press-scale) hover:bg-(--glow) hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';
|
|
3345
4253
|
const ACTIVE_CLASSES = 'bg-(--soft) text-(--foreground)';
|
|
3346
4254
|
const IDLE_CLASSES = 'text-(--muted-foreground) active:bg-(--soft)';
|
|
3347
4255
|
/**
|
|
@@ -3355,7 +4263,7 @@ const IDLE_CLASSES = 'text-(--muted-foreground) active:bg-(--soft)';
|
|
|
3355
4263
|
*/
|
|
3356
4264
|
class UiNavItem {
|
|
3357
4265
|
active = input(false, { ...(ngDevMode ? { debugName: "active" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3358
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
4266
|
+
classes = computed(() => `${BASE_CLASSES$2} ${this.active() ? ACTIVE_CLASSES : IDLE_CLASSES}`, /* @ts-ignore */
|
|
3359
4267
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
3360
4268
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiNavItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3361
4269
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiNavItem, isStandalone: true, selector: "a[ui-nav-item]", inputs: { active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.aria-current": "active() ? 'page' : null" } }, ngImport: i0, template: '<ng-content select="[navIcon]" /><ng-content />', isInline: true });
|
|
@@ -3384,10 +4292,12 @@ const SIZE_CLASSES$1 = {
|
|
|
3384
4292
|
};
|
|
3385
4293
|
const SIZE_GAPS = { md: 'gap-2.5', lg: 'gap-2.5', xl: 'gap-3', card: 'gap-2', dense: 'gap-3' };
|
|
3386
4294
|
const PADDING_CLASSES = { md: 'px-2.5', sm: 'px-2', none: 'px-0' };
|
|
3387
|
-
const BASE_CLASSES = 'flex w-full items-center py-1.5 rounded-control text-left select-none touch-manipulation
|
|
4295
|
+
const BASE_CLASSES$1 = 'flex w-full items-center py-1.5 rounded-control text-left select-none touch-manipulation focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';
|
|
4296
|
+
const INTERACTIVE_CLASSES = 'transition-[scale,background-color] [transition-duration:var(--duration-press),var(--duration-fast)] ease-out hover:bg-(--glow) active:bg-(--soft) active:scale-(--press-scale)';
|
|
4297
|
+
const UNAVAILABLE_CLASSES = 'opacity-50 cursor-not-allowed';
|
|
3388
4298
|
const SELECTED_CLASSES$1 = 'bg-(--soft)';
|
|
3389
4299
|
/**
|
|
3390
|
-
* Clickable row of a list: a holding, an
|
|
4300
|
+
* Clickable row of a list: a holding, an account, a search result.
|
|
3391
4301
|
*
|
|
3392
4302
|
* @example
|
|
3393
4303
|
* <a ui-row [selected]="holding.id === openId()" [routerLink]="['/holdings', holding.id]">
|
|
@@ -3396,28 +4306,59 @@ const SELECTED_CLASSES$1 = 'bg-(--soft)';
|
|
|
3396
4306
|
*/
|
|
3397
4307
|
class UiRow {
|
|
3398
4308
|
selected = input(false, { ...(ngDevMode ? { debugName: "selected" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4309
|
+
busy = input(false, { ...(ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4310
|
+
unavailable = input(false, { ...(ngDevMode ? { debugName: "unavailable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3399
4311
|
size = input('md', /* @ts-ignore */
|
|
3400
4312
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
3401
4313
|
padding = input('md', /* @ts-ignore */
|
|
3402
4314
|
...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
|
|
3403
4315
|
gap = input('default', /* @ts-ignore */
|
|
3404
4316
|
...(ngDevMode ? [{ debugName: "gap" }] : /* istanbul ignore next */ []));
|
|
3405
|
-
classes = computed(() => `${BASE_CLASSES} ${SIZE_CLASSES$1[this.size()]} ${this.gap() === 'sm' ? 'gap-2' : SIZE_GAPS[this.size()]} ${PADDING_CLASSES[this.padding()]}${this.selected() ? ` ${SELECTED_CLASSES$1}` : ''}`, /* @ts-ignore */
|
|
4317
|
+
classes = computed(() => `${BASE_CLASSES$1} ${SIZE_CLASSES$1[this.size()]} ${this.gap() === 'sm' ? 'gap-2' : SIZE_GAPS[this.size()]} ${PADDING_CLASSES[this.padding()]}${this.unavailable() ? ` ${UNAVAILABLE_CLASSES}` : ` ${INTERACTIVE_CLASSES}`}${this.selected() ? ` ${SELECTED_CLASSES$1}` : ''}${this.busy() ? ' pointer-events-none' : ''}`, /* @ts-ignore */
|
|
3406
4318
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
4319
|
+
constructor() {
|
|
4320
|
+
const host = inject(ElementRef).nativeElement;
|
|
4321
|
+
const guard = (event) => {
|
|
4322
|
+
if (this.busy() || this.unavailable()) {
|
|
4323
|
+
event.preventDefault();
|
|
4324
|
+
event.stopImmediatePropagation();
|
|
4325
|
+
}
|
|
4326
|
+
};
|
|
4327
|
+
host.addEventListener('click', guard, { capture: true });
|
|
4328
|
+
inject(DestroyRef).onDestroy(() => host.removeEventListener('click', guard, { capture: true }));
|
|
4329
|
+
}
|
|
3407
4330
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiRow, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3408
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
4331
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiRow, isStandalone: true, selector: "a[ui-row], button[ui-row]", inputs: { selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, unavailable: { classPropertyName: "unavailable", publicName: "unavailable", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.aria-busy": "busy() || null", "attr.aria-disabled": "busy() || unavailable() ? 'true' : null", "attr.aria-current": "selected() ? 'true' : null" } }, ngImport: i0, template: `
|
|
4332
|
+
<ng-content />
|
|
4333
|
+
@if (busy()) {
|
|
4334
|
+
<span
|
|
4335
|
+
aria-hidden="true"
|
|
4336
|
+
class="rounded-pill animate-cairn-spin size-4 flex-none border-2 border-current border-r-transparent"
|
|
4337
|
+
></span>
|
|
4338
|
+
}
|
|
4339
|
+
`, isInline: true });
|
|
3409
4340
|
}
|
|
3410
4341
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiRow, decorators: [{
|
|
3411
4342
|
type: Component,
|
|
3412
4343
|
args: [{
|
|
3413
4344
|
selector: 'a[ui-row], button[ui-row]',
|
|
3414
|
-
template:
|
|
4345
|
+
template: `
|
|
4346
|
+
<ng-content />
|
|
4347
|
+
@if (busy()) {
|
|
4348
|
+
<span
|
|
4349
|
+
aria-hidden="true"
|
|
4350
|
+
class="rounded-pill animate-cairn-spin size-4 flex-none border-2 border-current border-r-transparent"
|
|
4351
|
+
></span>
|
|
4352
|
+
}
|
|
4353
|
+
`,
|
|
3415
4354
|
host: {
|
|
3416
4355
|
'[class]': 'classes()',
|
|
4356
|
+
'[attr.aria-busy]': 'busy() || null',
|
|
4357
|
+
'[attr.aria-disabled]': "busy() || unavailable() ? 'true' : null",
|
|
3417
4358
|
'[attr.aria-current]': "selected() ? 'true' : null",
|
|
3418
4359
|
},
|
|
3419
4360
|
}]
|
|
3420
|
-
}], propDecorators: { selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }] } });
|
|
4361
|
+
}], ctorParameters: () => [], propDecorators: { selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], unavailable: [{ type: i0.Input, args: [{ isSignal: true, alias: "unavailable", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }] } });
|
|
3421
4362
|
/**
|
|
3422
4363
|
* Lays out a `ui-row` with a trailing action beside it, such as a "more" icon button: the row takes
|
|
3423
4364
|
* the room the action leaves and the action keeps its own size.
|
|
@@ -3496,7 +4437,7 @@ const nextId = (() => {
|
|
|
3496
4437
|
let count = 0;
|
|
3497
4438
|
return () => `ui-segmented-${++count}`;
|
|
3498
4439
|
})();
|
|
3499
|
-
const OPTION_CLASSES = "relative z-10 flex-1 cursor-pointer px-3 text-label font-medium transition-
|
|
4440
|
+
const OPTION_CLASSES = "relative z-10 flex-1 cursor-pointer px-3 text-label font-medium transition-[scale,color] duration-(--duration-press) ease-out after:absolute after:inset-x-0 after:top-1/2 after:h-(--row-min) after:-translate-y-1/2 after:content-[''] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) active:scale-(--press-scale)";
|
|
3500
4441
|
const SEGMENTED_SIZES = ['md', 'sm'];
|
|
3501
4442
|
const SIZE_CLASSES = {
|
|
3502
4443
|
md: 'h-10 pointer-fine:h-8',
|
|
@@ -3731,11 +4672,13 @@ class UiSkeleton {
|
|
|
3731
4672
|
}, /* @ts-ignore */
|
|
3732
4673
|
...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
|
|
3733
4674
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3734
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiSkeleton, isStandalone: true, selector: "ui-skeleton", inputs: { shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, classAttribute: "flex flex-col gap-2" }, ngImport: i0, template: "\n @switch (shape()) {\n @case ('figure') {\n <span class=\"block rounded-control bg-(--muted) h-10 w-65\" [style.animation]=\"animation\"></span>\n }\n @case ('row') {\n <span class=\"flex h-14 items-center justify-between gap-4\">\n <span class=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <span class=\"block rounded-control bg-(--muted) h-4 w-3/5\" [style.animation]=\"animation\"></span>\n <span class=\"block rounded-control bg-(--muted) h-3 w-20\" [style.animation]=\"animation\"></span>\n </span>\n <span class=\"block rounded-control bg-(--muted) h-4 w-[90px] flex-none\" [style.animation]=\"animation\"></span>\n </span>\n }\n @case ('chart') {\n <span class=\"rounded-control min-h-50 w-full flex-1 bg-(--muted)\" [style.animation]=\"animation\"></span>\n }\n @case ('dot') {\n <span class=\"rounded-pill size-3 bg-(--muted)\" [style.animation]=\"animation\"></span>\n }\n @case ('ring') {\n <span\n class=\"rounded-pill shadow-[inset_0_0_0_30px_var(--muted)]\"\n [style.animation]=\"animation\"\n [style.height.px]=\"size()\"\n [style.width.px]=\"size()\"\n ></span>\n }\n @default {\n @for (line of bars(); track $index) {\n <span\n class=\"block rounded-control bg-(--muted)\"\n [style.animation]=\"animation\"\n [style.height.px]=\"height()\"\n [style.width]=\"line\"\n ></span>\n }\n }\n }\n ", isInline: true
|
|
4675
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiSkeleton, isStandalone: true, selector: "ui-skeleton", inputs: { shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, classAttribute: "flex flex-col gap-2" }, ngImport: i0, template: "\n @switch (shape()) {\n @case ('figure') {\n <span class=\"block rounded-control bg-(--muted) h-10 w-65\" [style.animation]=\"animation\"></span>\n }\n @case ('row') {\n <span class=\"flex h-14 items-center justify-between gap-4\">\n <span class=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <span class=\"block rounded-control bg-(--muted) h-4 w-3/5\" [style.animation]=\"animation\"></span>\n <span class=\"block rounded-control bg-(--muted) h-3 w-20\" [style.animation]=\"animation\"></span>\n </span>\n <span class=\"block rounded-control bg-(--muted) h-4 w-[90px] flex-none\" [style.animation]=\"animation\"></span>\n </span>\n }\n @case ('chart') {\n <span class=\"rounded-control min-h-50 w-full flex-1 bg-(--muted)\" [style.animation]=\"animation\"></span>\n }\n @case ('dot') {\n <span class=\"rounded-pill size-3 bg-(--muted)\" [style.animation]=\"animation\"></span>\n }\n @case ('ring') {\n <span\n class=\"rounded-pill shadow-[inset_0_0_0_30px_var(--muted)]\"\n [style.animation]=\"animation\"\n [style.height.px]=\"size()\"\n [style.width.px]=\"size()\"\n ></span>\n }\n @default {\n @for (line of bars(); track $index) {\n <span\n class=\"block rounded-control bg-(--muted)\"\n [style.animation]=\"animation\"\n [style.height.px]=\"height()\"\n [style.width]=\"line\"\n ></span>\n }\n }\n }\n ", isInline: true });
|
|
3735
4676
|
}
|
|
3736
4677
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSkeleton, decorators: [{
|
|
3737
4678
|
type: Component,
|
|
3738
|
-
args: [{
|
|
4679
|
+
args: [{
|
|
4680
|
+
selector: 'ui-skeleton',
|
|
4681
|
+
template: `
|
|
3739
4682
|
@switch (shape()) {
|
|
3740
4683
|
@case ('figure') {
|
|
3741
4684
|
<span class="${BAR_CLASSES} h-10 w-65" [style.animation]="animation"></span>
|
|
@@ -3774,12 +4717,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
3774
4717
|
}
|
|
3775
4718
|
}
|
|
3776
4719
|
}
|
|
3777
|
-
`,
|
|
4720
|
+
`,
|
|
4721
|
+
host: {
|
|
3778
4722
|
'aria-hidden': 'true',
|
|
3779
4723
|
class: 'flex flex-col gap-2',
|
|
3780
|
-
},
|
|
4724
|
+
},
|
|
4725
|
+
}]
|
|
3781
4726
|
}], propDecorators: { shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
3782
4727
|
|
|
4728
|
+
const BASE_CLASSES = "relative 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) before:absolute before:inset-x-0 before:top-1/2 before:h-11 pointer-fine:before:h-10 before:-translate-y-1/2 before:content-['']";
|
|
4729
|
+
const CHEVRON_CLASSES = "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)]";
|
|
4730
|
+
/**
|
|
4731
|
+
* Link in the stale tone, for a caption about data that is late or left out of a total, such as
|
|
4732
|
+
* "1 ligne sans cours, non comptée". Caption size, weight 500, underlined on hover, with an optional
|
|
4733
|
+
* trailing chevron.
|
|
4734
|
+
*
|
|
4735
|
+
* @example
|
|
4736
|
+
* <a uiStaleLink chevron routerLink="/holdings">1 cours en retard</a>
|
|
4737
|
+
*/
|
|
4738
|
+
class UiStaleLink {
|
|
4739
|
+
chevron = input(false, { ...(ngDevMode ? { debugName: "chevron" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4740
|
+
classes = computed(() => (this.chevron() ? `${BASE_CLASSES} ${CHEVRON_CLASSES}` : BASE_CLASSES), /* @ts-ignore */
|
|
4741
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
4742
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiStaleLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4743
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiStaleLink, isStandalone: true, selector: "a[uiStaleLink]", inputs: { chevron: { classPropertyName: "chevron", publicName: "chevron", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0 });
|
|
4744
|
+
}
|
|
4745
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiStaleLink, decorators: [{
|
|
4746
|
+
type: Directive,
|
|
4747
|
+
args: [{
|
|
4748
|
+
selector: 'a[uiStaleLink]',
|
|
4749
|
+
host: { '[class]': 'classes()' },
|
|
4750
|
+
}]
|
|
4751
|
+
}], propDecorators: { chevron: [{ type: i0.Input, args: [{ isSignal: true, alias: "chevron", required: false }] }] } });
|
|
4752
|
+
|
|
3783
4753
|
/** Available stat sizes. `StatSize` is derived from this tuple. */
|
|
3784
4754
|
const STAT_SIZES = ['hero', 'tile'];
|
|
3785
4755
|
const VALUE_CLASSES = {
|
|
@@ -3867,7 +4837,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
3867
4837
|
}]
|
|
3868
4838
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
3869
4839
|
|
|
3870
|
-
const 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-
|
|
4840
|
+
const 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";
|
|
3871
4841
|
/**
|
|
3872
4842
|
* Native checkbox styled as an iOS-style switch, exposed to assistive technology as `role="switch"`.
|
|
3873
4843
|
* 51 by 31 on touch, 40 by 24 with a mouse.
|
|
@@ -3891,11 +4861,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
3891
4861
|
}]
|
|
3892
4862
|
}] });
|
|
3893
4863
|
|
|
3894
|
-
const
|
|
4864
|
+
const HEIGHT_PROPERTY = '--tab-bar-height';
|
|
4865
|
+
const HEIGHT = 'calc(52px + env(safe-area-inset-bottom))';
|
|
4866
|
+
const TAB_CLASSES = 'flex h-[52px] flex-col items-center justify-center gap-[3px] touch-manipulation select-none transition-transform duration-(--duration-press) ease-out active:scale-(--press-scale) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';
|
|
3895
4867
|
const INACTIVE_TAB_CLASSES = 'font-medium text-(--muted-foreground)';
|
|
3896
4868
|
const ACTIVE_TAB_CLASSES = 'font-semibold text-(--foreground)';
|
|
3897
4869
|
/**
|
|
3898
|
-
* Bottom navigation bar for iPhone: 52px of tabs plus the home indicator's safe area.
|
|
4870
|
+
* Bottom navigation bar for iPhone: 52px of tabs plus the home indicator's safe area. While it is on the
|
|
4871
|
+
* page it publishes that height as `--tab-bar-height` on the root element, which `ui-action-bar` and
|
|
4872
|
+
* `ui-toaster` sit above; use it rather than restating 52px for anything that must clear the bar.
|
|
3899
4873
|
*
|
|
3900
4874
|
* @example
|
|
3901
4875
|
* <nav ui-tab-bar>
|
|
@@ -3903,6 +4877,11 @@ const ACTIVE_TAB_CLASSES = 'font-semibold text-(--foreground)';
|
|
|
3903
4877
|
* </nav>
|
|
3904
4878
|
*/
|
|
3905
4879
|
class UiTabBar {
|
|
4880
|
+
constructor() {
|
|
4881
|
+
const root = inject(DOCUMENT).documentElement;
|
|
4882
|
+
root.style.setProperty(HEIGHT_PROPERTY, HEIGHT);
|
|
4883
|
+
inject(DestroyRef).onDestroy(() => root.style.removeProperty(HEIGHT_PROPERTY));
|
|
4884
|
+
}
|
|
3906
4885
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTabBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3907
4886
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: UiTabBar, isStandalone: true, selector: "nav[ui-tab-bar]", host: { classAttribute: "grid grid-flow-col auto-cols-fr items-stretch bg-(--background)/90 backdrop-blur [@media(prefers-reduced-transparency:reduce)]:bg-(--background) shadow-[inset_0_1px_0_var(--hairline)] pb-[env(safe-area-inset-bottom)]" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
3908
4887
|
}
|
|
@@ -3915,7 +4894,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
3915
4894
|
class: 'grid grid-flow-col auto-cols-fr items-stretch bg-(--background)/90 backdrop-blur [@media(prefers-reduced-transparency:reduce)]:bg-(--background) shadow-[inset_0_1px_0_var(--hairline)] pb-[env(safe-area-inset-bottom)]',
|
|
3916
4895
|
},
|
|
3917
4896
|
}]
|
|
3918
|
-
}] });
|
|
4897
|
+
}], ctorParameters: () => [] });
|
|
3919
4898
|
/**
|
|
3920
4899
|
* One destination of `ui-tab-bar`.
|
|
3921
4900
|
*
|
|
@@ -3947,6 +4926,150 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
3947
4926
|
}]
|
|
3948
4927
|
}], propDecorators: { active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }] } });
|
|
3949
4928
|
|
|
4929
|
+
/**
|
|
4930
|
+
* One-slot confirmation message queue read by `ui-toaster`.
|
|
4931
|
+
*
|
|
4932
|
+
* @example
|
|
4933
|
+
* inject(UiToasts).show('Achat enregistré');
|
|
4934
|
+
*/
|
|
4935
|
+
class UiToasts {
|
|
4936
|
+
current = signal(null, /* @ts-ignore */
|
|
4937
|
+
...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
4938
|
+
sequence = 0;
|
|
4939
|
+
toast = this.current.asReadonly();
|
|
4940
|
+
show(message) {
|
|
4941
|
+
this.current.set({ id: ++this.sequence, text: message });
|
|
4942
|
+
}
|
|
4943
|
+
dismiss() {
|
|
4944
|
+
this.current.set(null);
|
|
4945
|
+
}
|
|
4946
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiToasts, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4947
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiToasts, providedIn: 'root' });
|
|
4948
|
+
}
|
|
4949
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiToasts, decorators: [{
|
|
4950
|
+
type: Injectable,
|
|
4951
|
+
args: [{ providedIn: 'root' }]
|
|
4952
|
+
}] });
|
|
4953
|
+
|
|
4954
|
+
const DEFAULT_DURATION = 5000;
|
|
4955
|
+
const TOAST_CLASSES = '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';
|
|
4956
|
+
const readDuration = (host) => {
|
|
4957
|
+
const raw = getComputedStyle(host).getPropertyValue('--toast-duration').trim();
|
|
4958
|
+
const value = parseFloat(raw);
|
|
4959
|
+
if (Number.isNaN(value)) {
|
|
4960
|
+
return DEFAULT_DURATION;
|
|
4961
|
+
}
|
|
4962
|
+
return raw.endsWith('ms') ? value : value * 1000;
|
|
4963
|
+
};
|
|
4964
|
+
/**
|
|
4965
|
+
* Confirmation message shown by `UiToasts.show`: one at a time, no action, gone after `--toast-duration`.
|
|
4966
|
+
* Place it once in the app shell.
|
|
4967
|
+
*
|
|
4968
|
+
* @example
|
|
4969
|
+
* <ui-toaster />
|
|
4970
|
+
*/
|
|
4971
|
+
class UiToaster {
|
|
4972
|
+
toasts = inject(UiToasts);
|
|
4973
|
+
toastClasses = TOAST_CLASSES;
|
|
4974
|
+
host = inject(ElementRef).nativeElement;
|
|
4975
|
+
hovered = signal(false, /* @ts-ignore */
|
|
4976
|
+
...(ngDevMode ? [{ debugName: "hovered" }] : /* istanbul ignore next */ []));
|
|
4977
|
+
focused = signal(false, /* @ts-ignore */
|
|
4978
|
+
...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
|
|
4979
|
+
lastId = 0;
|
|
4980
|
+
remaining = DEFAULT_DURATION;
|
|
4981
|
+
startedAt = 0;
|
|
4982
|
+
timer;
|
|
4983
|
+
constructor() {
|
|
4984
|
+
effect(() => {
|
|
4985
|
+
const toast = this.toasts.toast();
|
|
4986
|
+
const paused = this.hovered() || this.focused();
|
|
4987
|
+
untracked(() => {
|
|
4988
|
+
if (!toast) {
|
|
4989
|
+
this.stop();
|
|
4990
|
+
this.hovered.set(false);
|
|
4991
|
+
this.focused.set(false);
|
|
4992
|
+
return;
|
|
4993
|
+
}
|
|
4994
|
+
this.host.querySelectorAll('.ui-leave-fade').forEach((leaving) => leaving.remove());
|
|
4995
|
+
if (paused) {
|
|
4996
|
+
this.pause();
|
|
4997
|
+
}
|
|
4998
|
+
else {
|
|
4999
|
+
this.resume(toast.id);
|
|
5000
|
+
}
|
|
5001
|
+
});
|
|
5002
|
+
});
|
|
5003
|
+
inject(DestroyRef).onDestroy(() => this.stop());
|
|
5004
|
+
}
|
|
5005
|
+
setHovered(value) {
|
|
5006
|
+
this.hovered.set(value);
|
|
5007
|
+
}
|
|
5008
|
+
setFocused(value) {
|
|
5009
|
+
this.focused.set(value);
|
|
5010
|
+
}
|
|
5011
|
+
resume(id) {
|
|
5012
|
+
if (id !== this.lastId) {
|
|
5013
|
+
this.lastId = id;
|
|
5014
|
+
this.remaining = readDuration(this.host);
|
|
5015
|
+
}
|
|
5016
|
+
clearTimeout(this.timer);
|
|
5017
|
+
this.startedAt = Date.now();
|
|
5018
|
+
this.timer = setTimeout(() => this.toasts.dismiss(), this.remaining);
|
|
5019
|
+
}
|
|
5020
|
+
pause() {
|
|
5021
|
+
if (this.timer !== undefined) {
|
|
5022
|
+
this.remaining -= Date.now() - this.startedAt;
|
|
5023
|
+
clearTimeout(this.timer);
|
|
5024
|
+
this.timer = undefined;
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
stop() {
|
|
5028
|
+
clearTimeout(this.timer);
|
|
5029
|
+
this.timer = undefined;
|
|
5030
|
+
this.lastId = 0;
|
|
5031
|
+
}
|
|
5032
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiToaster, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5033
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiToaster, isStandalone: true, selector: "ui-toaster", host: { attributes: { "role": "status" }, classAttribute: "pointer-events-none fixed inset-x-0 z-50 flex justify-center bottom-[calc(var(--tab-bar-height,calc(52px+env(safe-area-inset-bottom)))+var(--action-bar-height,0px)+8px)] lg:inset-x-auto lg:right-6 lg:bottom-6" }, ngImport: i0, template: `
|
|
5034
|
+
@if (toasts.toast(); as toast) {
|
|
5035
|
+
<div
|
|
5036
|
+
animate.leave="ui-leave-fade"
|
|
5037
|
+
[class]="toastClasses"
|
|
5038
|
+
(focusin)="setFocused(true)"
|
|
5039
|
+
(focusout)="setFocused(false)"
|
|
5040
|
+
(mouseenter)="setHovered(true)"
|
|
5041
|
+
(mouseleave)="setHovered(false)"
|
|
5042
|
+
>
|
|
5043
|
+
{{ toast.text }}
|
|
5044
|
+
</div>
|
|
5045
|
+
}
|
|
5046
|
+
`, isInline: true });
|
|
5047
|
+
}
|
|
5048
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiToaster, decorators: [{
|
|
5049
|
+
type: Component,
|
|
5050
|
+
args: [{
|
|
5051
|
+
selector: 'ui-toaster',
|
|
5052
|
+
template: `
|
|
5053
|
+
@if (toasts.toast(); as toast) {
|
|
5054
|
+
<div
|
|
5055
|
+
animate.leave="ui-leave-fade"
|
|
5056
|
+
[class]="toastClasses"
|
|
5057
|
+
(focusin)="setFocused(true)"
|
|
5058
|
+
(focusout)="setFocused(false)"
|
|
5059
|
+
(mouseenter)="setHovered(true)"
|
|
5060
|
+
(mouseleave)="setHovered(false)"
|
|
5061
|
+
>
|
|
5062
|
+
{{ toast.text }}
|
|
5063
|
+
</div>
|
|
5064
|
+
}
|
|
5065
|
+
`,
|
|
5066
|
+
host: {
|
|
5067
|
+
role: 'status',
|
|
5068
|
+
class: 'pointer-events-none fixed inset-x-0 z-50 flex justify-center bottom-[calc(var(--tab-bar-height,calc(52px+env(safe-area-inset-bottom)))+var(--action-bar-height,0px)+8px)] lg:inset-x-auto lg:right-6 lg:bottom-6',
|
|
5069
|
+
},
|
|
5070
|
+
}]
|
|
5071
|
+
}], ctorParameters: () => [] });
|
|
5072
|
+
|
|
3950
5073
|
/** Breakpoints a secondary column can be held back until. */
|
|
3951
5074
|
const CELL_BREAKPOINTS = ['md', 'lg'];
|
|
3952
5075
|
/** Body row heights. */
|
|
@@ -4030,8 +5153,10 @@ class UiTr {
|
|
|
4030
5153
|
return [
|
|
4031
5154
|
'relative [&>td:first-child]:rounded-l-control [&>td:last-child]:rounded-r-control',
|
|
4032
5155
|
this.selected() ? '[&>td]:bg-(--soft)' : '',
|
|
4033
|
-
'hover:[&>td]:bg-(--glow)',
|
|
4034
|
-
this.interactive()
|
|
5156
|
+
'hover:[&>td]:bg-(--glow) [&>td]:transition-colors [&>td]:duration-(--duration-fast) [&>td]:ease-out',
|
|
5157
|
+
this.interactive()
|
|
5158
|
+
? 'active:[&>td]:bg-(--soft) transition-transform duration-(--duration-press) ease-out active:scale-(--press-scale)'
|
|
5159
|
+
: '',
|
|
4035
5160
|
]
|
|
4036
5161
|
.filter(Boolean)
|
|
4037
5162
|
.join(' ');
|
|
@@ -4261,5 +5386,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
4261
5386
|
* Generated bundle index. Do not edit.
|
|
4262
5387
|
*/
|
|
4263
5388
|
|
|
4264
|
-
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 };
|
|
5389
|
+
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, UiFlipItem, 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 };
|
|
4265
5390
|
//# sourceMappingURL=joanroucoux-cairn-ui.mjs.map
|