@joanroucoux/cairn-ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,9 +1,202 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input,
|
|
2
|
+
import { InjectionToken, signal, input, inject, LOCALE_ID, booleanAttribute, computed, Component, output, ElementRef, DestroyRef, effect, contentChild, afterRenderEffect, forwardRef, Directive, contentChildren, model, viewChildren } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
const MINUS = '−';
|
|
5
|
+
const EM_DASH = '—';
|
|
6
|
+
const BULLETS = '••••';
|
|
7
|
+
/** Formats a number or an amount for the locale, with a missing value as an em dash and masking behind four bullets. */
|
|
8
|
+
function formatAmount(value, options, masked = false) {
|
|
9
|
+
if (value === null || value === undefined || Number.isNaN(value)) {
|
|
10
|
+
return EM_DASH;
|
|
11
|
+
}
|
|
12
|
+
const digits = options.fractionDigits ?? 2;
|
|
13
|
+
const format = new Intl.NumberFormat(options.locale, {
|
|
14
|
+
...(options.currency ? { style: 'currency', currency: options.currency } : {}),
|
|
15
|
+
minimumFractionDigits: digits,
|
|
16
|
+
maximumFractionDigits: digits,
|
|
17
|
+
});
|
|
18
|
+
const magnitude = Math.abs(value);
|
|
19
|
+
const parts = format.formatToParts(magnitude);
|
|
20
|
+
if (masked) {
|
|
21
|
+
let replaced = false;
|
|
22
|
+
return parts
|
|
23
|
+
.map((part) => {
|
|
24
|
+
if (['integer', 'group', 'decimal', 'fraction'].includes(part.type)) {
|
|
25
|
+
if (replaced) {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
replaced = true;
|
|
29
|
+
return BULLETS;
|
|
30
|
+
}
|
|
31
|
+
return part.value;
|
|
32
|
+
})
|
|
33
|
+
.join('');
|
|
34
|
+
}
|
|
35
|
+
const text = parts.map((part) => part.value).join('');
|
|
36
|
+
const isZero = Number(format.format(magnitude).replace(/[^\d]/g, '')) === 0;
|
|
37
|
+
if (!options.signed || isZero) {
|
|
38
|
+
return value < 0 && !isZero ? `${MINUS}${text}` : text;
|
|
39
|
+
}
|
|
40
|
+
return `${value > 0 ? '+' : MINUS}${text}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** App-wide "amounts are hidden" state. Defaults to never masked; the app overrides it. */
|
|
44
|
+
const UI_AMOUNT_MASKED = new InjectionToken('UI_AMOUNT_MASKED', {
|
|
45
|
+
providedIn: 'root',
|
|
46
|
+
factory: () => signal(false),
|
|
47
|
+
});
|
|
48
|
+
/** Available figure styles. `AmountNumeric` is derived from this tuple. */
|
|
49
|
+
const AMOUNT_NUMERICS = ['tabular', 'proportional'];
|
|
50
|
+
/** Available masking behaviors. `AmountMasking` is derived from this tuple. */
|
|
51
|
+
const AMOUNT_MASKINGS = ['dots', 'hide'];
|
|
52
|
+
/**
|
|
53
|
+
* A number or an amount of money, formatted for the locale and masked when the app hides amounts.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* <ui-amount [value]="total" currency="EUR" numeric="proportional" />
|
|
57
|
+
* <ui-amount [value]="dayChange" currency="EUR" signed whenMasked="hide" />
|
|
58
|
+
*/
|
|
59
|
+
class UiAmount {
|
|
60
|
+
value = input(/* @ts-ignore */
|
|
61
|
+
...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
62
|
+
currency = input(/* @ts-ignore */
|
|
63
|
+
...(ngDevMode ? [undefined, { debugName: "currency" }] : /* istanbul ignore next */ []));
|
|
64
|
+
locale = input(inject(LOCALE_ID), /* @ts-ignore */
|
|
65
|
+
...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
66
|
+
signed = input(false, { ...(ngDevMode ? { debugName: "signed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
67
|
+
fractionDigits = input(2, /* @ts-ignore */
|
|
68
|
+
...(ngDevMode ? [{ debugName: "fractionDigits" }] : /* istanbul ignore next */ []));
|
|
69
|
+
numeric = input('tabular', /* @ts-ignore */
|
|
70
|
+
...(ngDevMode ? [{ debugName: "numeric" }] : /* istanbul ignore next */ []));
|
|
71
|
+
whenMasked = input('dots', /* @ts-ignore */
|
|
72
|
+
...(ngDevMode ? [{ debugName: "whenMasked" }] : /* istanbul ignore next */ []));
|
|
73
|
+
#masked = inject(UI_AMOUNT_MASKED);
|
|
74
|
+
hidden = computed(() => this.#masked() && this.whenMasked() === 'hide', /* @ts-ignore */
|
|
75
|
+
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
76
|
+
text = computed(() => formatAmount(this.value(), {
|
|
77
|
+
locale: this.locale(),
|
|
78
|
+
currency: this.currency(),
|
|
79
|
+
signed: this.signed(),
|
|
80
|
+
fractionDigits: this.fractionDigits(),
|
|
81
|
+
}, this.#masked()), /* @ts-ignore */
|
|
82
|
+
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
83
|
+
classes = computed(() => {
|
|
84
|
+
const missing = this.value() === null || this.value() === undefined;
|
|
85
|
+
return [
|
|
86
|
+
'whitespace-nowrap',
|
|
87
|
+
this.numeric() === 'tabular' ? 'tabular-nums' : '',
|
|
88
|
+
missing ? 'text-(--subtle-foreground)' : '',
|
|
89
|
+
]
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join(' ');
|
|
92
|
+
}, /* @ts-ignore */
|
|
93
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
94
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAmount, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
95
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiAmount, isStandalone: true, selector: "ui-amount", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, signed: { classPropertyName: "signed", publicName: "signed", isSignal: true, isRequired: false, transformFunction: null }, fractionDigits: { classPropertyName: "fractionDigits", publicName: "fractionDigits", isSignal: true, isRequired: false, transformFunction: null }, numeric: { classPropertyName: "numeric", publicName: "numeric", isSignal: true, isRequired: false, transformFunction: null }, whenMasked: { classPropertyName: "whenMasked", publicName: "whenMasked", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.hidden": "hidden() || null" } }, ngImport: i0, template: '{{ text() }}', isInline: true });
|
|
96
|
+
}
|
|
97
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAmount, decorators: [{
|
|
98
|
+
type: Component,
|
|
99
|
+
args: [{
|
|
100
|
+
selector: 'ui-amount',
|
|
101
|
+
template: '{{ text() }}',
|
|
102
|
+
host: {
|
|
103
|
+
'[class]': 'classes()',
|
|
104
|
+
'[attr.hidden]': 'hidden() || null',
|
|
105
|
+
},
|
|
106
|
+
}]
|
|
107
|
+
}], 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 }] }] } });
|
|
108
|
+
|
|
109
|
+
/** Available async block states. `AsyncState` is derived from this tuple. */
|
|
110
|
+
const ASYNC_STATES = ['loading', 'error', 'empty', 'ready'];
|
|
111
|
+
const RETRY_BUTTON_CLASSES = 'mt-2 relative inline-flex items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap cursor-pointer select-none touch-manipulation transition-[transform,background-color,opacity] duration-(--duration-press) ease-out active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) min-h-(--row-min) px-4 text-label bg-(--card) text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] hover:bg-(--glow)';
|
|
112
|
+
/**
|
|
113
|
+
* The three states of a block that depends on a service call, next to the ready content.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* <ui-async [state]="totalState()" errorTitle="Total could not be loaded" errorMessage="The server did not answer." retryLabel="Retry" (retry)="reload()">
|
|
117
|
+
* <span asyncLoading><ui-skeleton shape="figure" /></span>
|
|
118
|
+
* <span asyncEmpty>Add a holding to see your total.</span>
|
|
119
|
+
* <span>{{ total() }}</span>
|
|
120
|
+
* </ui-async>
|
|
121
|
+
*/
|
|
122
|
+
class UiAsync {
|
|
123
|
+
state = input.required(/* @ts-ignore */
|
|
124
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
125
|
+
errorTitle = input(/* @ts-ignore */
|
|
126
|
+
...(ngDevMode ? [undefined, { debugName: "errorTitle" }] : /* istanbul ignore next */ []));
|
|
127
|
+
errorMessage = input(/* @ts-ignore */
|
|
128
|
+
...(ngDevMode ? [undefined, { debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
129
|
+
retryLabel = input(/* @ts-ignore */
|
|
130
|
+
...(ngDevMode ? [undefined, { debugName: "retryLabel" }] : /* istanbul ignore next */ []));
|
|
131
|
+
retry = output();
|
|
132
|
+
retryButtonClasses = RETRY_BUTTON_CLASSES;
|
|
133
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAsync, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
134
|
+
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 } }, outputs: { retry: "retry" }, host: { properties: { "attr.aria-busy": "state() === 'loading' || null" } }, ngImport: i0, template: `
|
|
135
|
+
@switch (state()) {
|
|
136
|
+
@case ('loading') {
|
|
137
|
+
<ng-content select="[asyncLoading]" />
|
|
138
|
+
}
|
|
139
|
+
@case ('error') {
|
|
140
|
+
<div
|
|
141
|
+
aria-live="polite"
|
|
142
|
+
class="rounded-control flex flex-col items-start gap-1 bg-(--elevated) p-(--inset-card)"
|
|
143
|
+
role="alert"
|
|
144
|
+
>
|
|
145
|
+
<span class="text-body font-medium">{{ errorTitle() }}</span>
|
|
146
|
+
<span class="text-label text-(--muted-foreground)">{{ errorMessage() }}</span>
|
|
147
|
+
<button type="button" [class]="retryButtonClasses" (click)="retry.emit()">
|
|
148
|
+
{{ retryLabel() }}
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
}
|
|
152
|
+
@case ('empty') {
|
|
153
|
+
<ng-content select="[asyncEmpty]" />
|
|
154
|
+
}
|
|
155
|
+
@default {
|
|
156
|
+
<ng-content />
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
`, isInline: true });
|
|
160
|
+
}
|
|
161
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAsync, decorators: [{
|
|
162
|
+
type: Component,
|
|
163
|
+
args: [{
|
|
164
|
+
selector: 'ui-async',
|
|
165
|
+
template: `
|
|
166
|
+
@switch (state()) {
|
|
167
|
+
@case ('loading') {
|
|
168
|
+
<ng-content select="[asyncLoading]" />
|
|
169
|
+
}
|
|
170
|
+
@case ('error') {
|
|
171
|
+
<div
|
|
172
|
+
aria-live="polite"
|
|
173
|
+
class="rounded-control flex flex-col items-start gap-1 bg-(--elevated) p-(--inset-card)"
|
|
174
|
+
role="alert"
|
|
175
|
+
>
|
|
176
|
+
<span class="text-body font-medium">{{ errorTitle() }}</span>
|
|
177
|
+
<span class="text-label text-(--muted-foreground)">{{ errorMessage() }}</span>
|
|
178
|
+
<button type="button" [class]="retryButtonClasses" (click)="retry.emit()">
|
|
179
|
+
{{ retryLabel() }}
|
|
180
|
+
</button>
|
|
181
|
+
</div>
|
|
182
|
+
}
|
|
183
|
+
@case ('empty') {
|
|
184
|
+
<ng-content select="[asyncEmpty]" />
|
|
185
|
+
}
|
|
186
|
+
@default {
|
|
187
|
+
<ng-content />
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
`,
|
|
191
|
+
host: {
|
|
192
|
+
'[attr.aria-busy]': "state() === 'loading' || null",
|
|
193
|
+
},
|
|
194
|
+
}]
|
|
195
|
+
}], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], errorTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorTitle", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], retryLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "retryLabel", required: false }] }], retry: [{ type: i0.Output, args: ["retry"] }] } });
|
|
3
196
|
|
|
4
197
|
/** Available avatar sizes. `AvatarSize` is derived from this tuple. */
|
|
5
198
|
const AVATAR_SIZES = ['sm', 'md'];
|
|
6
|
-
const BASE_CLASSES$
|
|
199
|
+
const BASE_CLASSES$6 = 'inline-flex items-center justify-center rounded-pill border border-(--primary) bg-(--soft) font-semibold tabular-nums text-(--primary)';
|
|
7
200
|
const SIZE_CLASSES$1 = {
|
|
8
201
|
sm: 'size-8 text-caption',
|
|
9
202
|
md: 'size-11 text-label',
|
|
@@ -21,7 +214,7 @@ class UiAvatar {
|
|
|
21
214
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
22
215
|
size = input('md', /* @ts-ignore */
|
|
23
216
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
24
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
217
|
+
classes = computed(() => `${BASE_CLASSES$6} ${SIZE_CLASSES$1[this.size()]}`, /* @ts-ignore */
|
|
25
218
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
26
219
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
27
220
|
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 });
|
|
@@ -41,7 +234,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
41
234
|
|
|
42
235
|
/** Available badge variants. `BadgeVariant` is derived from this tuple. */
|
|
43
236
|
const BADGE_VARIANTS = ['neutral', 'outline'];
|
|
44
|
-
const BASE_CLASSES$
|
|
237
|
+
const BASE_CLASSES$5 = 'inline-flex h-6 items-center rounded-pill px-2.5 text-caption font-medium';
|
|
45
238
|
const VARIANT_CLASSES$2 = {
|
|
46
239
|
neutral: 'bg-(--muted) text-(--foreground)',
|
|
47
240
|
outline: 'shadow-[inset_0_0_0_1px_var(--border)] text-(--foreground)',
|
|
@@ -55,7 +248,7 @@ const VARIANT_CLASSES$2 = {
|
|
|
55
248
|
class UiBadge {
|
|
56
249
|
variant = input('neutral', /* @ts-ignore */
|
|
57
250
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
58
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
251
|
+
classes = computed(() => `${BASE_CLASSES$5} ${VARIANT_CLASSES$2[this.variant()]}`, /* @ts-ignore */
|
|
59
252
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
60
253
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
61
254
|
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 } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
@@ -75,9 +268,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
75
268
|
const BUTTON_VARIANTS = ['primary', 'outline', 'ghost', 'destructive'];
|
|
76
269
|
/** Available button sizes. `md` is the touch target Cairn asks for everywhere. */
|
|
77
270
|
const BUTTON_SIZES = ['md', 'lg', 'xl', 'icon'];
|
|
78
|
-
const BASE_CLASSES$
|
|
79
|
-
// Tailwind's preflight gives buttons cursor: default; the hand cursor is what users expect here.
|
|
80
|
-
'relative inline-flex items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap cursor-pointer select-none touch-manipulation transition-[transform,background-color,opacity] duration-(--duration-press) ease-out active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) disabled:pointer-events-none disabled:opacity-40 aria-disabled:not-aria-busy:opacity-40';
|
|
271
|
+
const BASE_CLASSES$4 = 'relative inline-flex items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap cursor-pointer select-none touch-manipulation transition-[transform,background-color,opacity] duration-(--duration-press) ease-out active:scale-(--press-scale) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) disabled:pointer-events-none disabled:opacity-40 aria-disabled:not-aria-busy:opacity-40';
|
|
81
272
|
const VARIANT_CLASSES$1 = {
|
|
82
273
|
primary: 'bg-(--primary) text-(--primary-foreground) hover:opacity-90',
|
|
83
274
|
outline: 'bg-(--card) text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] hover:bg-(--glow)',
|
|
@@ -91,16 +282,7 @@ const SIZE_CLASSES = {
|
|
|
91
282
|
icon: 'size-(--row-min) p-0',
|
|
92
283
|
};
|
|
93
284
|
/**
|
|
94
|
-
* Styled native button
|
|
95
|
-
* button behavior (type, disabled, form submission, accessibility) for free.
|
|
96
|
-
*
|
|
97
|
-
* Also applies to an anchor, for the one case where the action really is a navigation:
|
|
98
|
-
* a download the browser has to perform itself.
|
|
99
|
-
*
|
|
100
|
-
* `size="icon"` has no visible text: give it an `aria-label`.
|
|
101
|
-
*
|
|
102
|
-
* A `loading` button shows a spinner before its label and swallows clicks, so a slow first
|
|
103
|
-
* response cannot turn into a double submission.
|
|
285
|
+
* Styled native button or anchor, applied as an attribute so the host keeps every native behavior.
|
|
104
286
|
*
|
|
105
287
|
* @example
|
|
106
288
|
* <button ui-button variant="destructive">Delete</button>
|
|
@@ -112,12 +294,9 @@ class UiButton {
|
|
|
112
294
|
size = input('md', /* @ts-ignore */
|
|
113
295
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
114
296
|
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
115
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
297
|
+
classes = computed(() => `${BASE_CLASSES$4} ${VARIANT_CLASSES$1[this.variant()]} ${SIZE_CLASSES[this.size()]}${this.loading() ? ' pointer-events-none opacity-70' : ''}`, /* @ts-ignore */
|
|
116
298
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
117
299
|
constructor() {
|
|
118
|
-
// A host `(click)` binding runs after the template's own listener, so a loading button would
|
|
119
|
-
// still fire the consumer's handler once before this guard got a say. Capturing from the
|
|
120
|
-
// constructor puts this listener first regardless of binding order.
|
|
121
300
|
const host = inject(ElementRef).nativeElement;
|
|
122
301
|
const guard = (event) => {
|
|
123
302
|
if (this.loading()) {
|
|
@@ -164,7 +343,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
164
343
|
const CARD_VARIANTS = ['default', 'elevated'];
|
|
165
344
|
/** Available card paddings. `CardPadding` is derived from this tuple. */
|
|
166
345
|
const CARD_PADDINGS = ['none', 'sm', 'md'];
|
|
167
|
-
const BASE_CLASSES$
|
|
346
|
+
const BASE_CLASSES$3 = 'block rounded-container border';
|
|
168
347
|
const VARIANT_CLASSES = {
|
|
169
348
|
default: 'bg-(--card) border-(--border) text-(--card-foreground)',
|
|
170
349
|
elevated: 'bg-(--elevated) border-(--hairline) text-(--foreground)',
|
|
@@ -185,7 +364,7 @@ class UiCard {
|
|
|
185
364
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
186
365
|
padding = input('md', /* @ts-ignore */
|
|
187
366
|
...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
|
|
188
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
367
|
+
classes = computed(() => `${BASE_CLASSES$3} ${VARIANT_CLASSES[this.variant()]} ${PADDING_CLASSES[this.padding()]}`, /* @ts-ignore */
|
|
189
368
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
190
369
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
191
370
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiCard, isStandalone: true, selector: "ui-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
@@ -213,7 +392,7 @@ const UI_CONTROL = new InjectionToken('UI_CONTROL');
|
|
|
213
392
|
|
|
214
393
|
/** Available delta emphases. `DeltaEmphasis` is derived from this tuple. */
|
|
215
394
|
const DELTA_EMPHASES = ['text', 'pill'];
|
|
216
|
-
const BASE_CLASSES = 'inline-flex items-center gap-1 text-label font-medium tabular-nums';
|
|
395
|
+
const BASE_CLASSES$2 = 'inline-flex items-center gap-1 text-label font-medium tabular-nums';
|
|
217
396
|
const PILL_CLASSES = 'h-6 rounded-pill px-2';
|
|
218
397
|
const TEXT_CLASSES = {
|
|
219
398
|
positive: 'text-(--positive)',
|
|
@@ -221,9 +400,6 @@ const TEXT_CLASSES = {
|
|
|
221
400
|
neutral: 'text-(--muted-foreground)',
|
|
222
401
|
unknown: 'text-(--subtle-foreground)',
|
|
223
402
|
};
|
|
224
|
-
// The handoff's `Cairn Composants.dc.html` uses --soft here, but --positive text on --soft only
|
|
225
|
-
// reaches 4.33:1 (axe flags it): --soft is a touch darker than --background, and --positive sits
|
|
226
|
-
// right at the edge of AA at 4.77:1 against --background alone. --muted keeps the chip legible.
|
|
227
403
|
const PILL_BACKGROUND_CLASS = 'bg-(--muted)';
|
|
228
404
|
/**
|
|
229
405
|
* Signed amount whose sign carries meaning: a gain, a loss, or an unknown.
|
|
@@ -261,7 +437,7 @@ class UiDelta {
|
|
|
261
437
|
classes = computed(() => {
|
|
262
438
|
const sign = this.sign();
|
|
263
439
|
const background = this.emphasis() === 'pill' ? `${PILL_CLASSES} ${PILL_BACKGROUND_CLASS}` : 'bg-transparent';
|
|
264
|
-
return `${BASE_CLASSES} ${TEXT_CLASSES[sign]} ${background}`;
|
|
440
|
+
return `${BASE_CLASSES$2} ${TEXT_CLASSES[sign]} ${background}`;
|
|
265
441
|
}, /* @ts-ignore */
|
|
266
442
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
267
443
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDelta, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
@@ -296,15 +472,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
296
472
|
const DIALOG_WIDTHS = ['md', 'lg'];
|
|
297
473
|
const WIDTH_CLASSES = {
|
|
298
474
|
md: 'max-w-md',
|
|
299
|
-
lg: 'max-w-
|
|
475
|
+
lg: 'max-w-[560px]',
|
|
300
476
|
};
|
|
301
|
-
const nextId$
|
|
477
|
+
const nextId$2 = (() => {
|
|
302
478
|
let count = 0;
|
|
303
479
|
return () => `ui-dialog-${++count}`;
|
|
304
480
|
})();
|
|
305
481
|
/**
|
|
306
|
-
* Modal dialog on the native <dialog>:
|
|
307
|
-
*
|
|
482
|
+
* Modal dialog on the native <dialog>: a sheet rising from the bottom under `64rem`, centered
|
|
483
|
+
* above it. Two projection slots - the body, and [dialogActions] for the footer.
|
|
308
484
|
*
|
|
309
485
|
* @example
|
|
310
486
|
* <ui-dialog heading="Delete this holding?" [open]="confirming()" (dismissed)="confirming.set(false)">
|
|
@@ -319,12 +495,11 @@ class UiDialog {
|
|
|
319
495
|
...(ngDevMode ? [undefined, { debugName: "description" }] : /* istanbul ignore next */ []));
|
|
320
496
|
width = input('lg', /* @ts-ignore */
|
|
321
497
|
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
322
|
-
open = input(false, /*
|
|
323
|
-
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
498
|
+
open = input(false, { ...(ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
324
499
|
dismissed = output();
|
|
325
|
-
headingId = nextId$
|
|
500
|
+
headingId = nextId$2();
|
|
326
501
|
descriptionId = `${this.headingId}-description`;
|
|
327
|
-
classes = computed(() => `w-full m-auto ${WIDTH_CLASSES[this.width()]}
|
|
502
|
+
classes = computed(() => `flex w-full flex-col m-auto mt-auto max-h-[calc(100dvh-2rem)] max-lg:mb-0 max-lg:max-w-none max-lg:rounded-b-none rounded-container ${WIDTH_CLASSES[this.width()]} border border-(--border) bg-(--card) text-(--foreground) backdrop:bg-black/60`, /* @ts-ignore */
|
|
328
503
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
329
504
|
#host = inject(ElementRef);
|
|
330
505
|
constructor() {
|
|
@@ -353,54 +528,73 @@ class UiDialog {
|
|
|
353
528
|
[class]="classes()"
|
|
354
529
|
(close)="onNativeClose()"
|
|
355
530
|
>
|
|
356
|
-
<
|
|
531
|
+
<div aria-hidden="true" class="flex h-7 items-center justify-center lg:hidden" data-dialog-handle>
|
|
532
|
+
<span class="rounded-pill h-1.5 w-9 bg-(--border)"></span>
|
|
533
|
+
</div>
|
|
357
534
|
|
|
358
|
-
|
|
359
|
-
<
|
|
360
|
-
}
|
|
535
|
+
<div class="px-6 pt-1 lg:pt-6">
|
|
536
|
+
<h2 class="text-title font-semibold" [id]="headingId">{{ heading() }}</h2>
|
|
361
537
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
538
|
+
@if (description()) {
|
|
539
|
+
<p class="text-label mt-2 text-(--muted-foreground)" [id]="descriptionId">{{ description() }}</p>
|
|
540
|
+
}
|
|
541
|
+
</div>
|
|
542
|
+
|
|
543
|
+
<div class="min-h-0 flex-1 overflow-y-auto px-6" data-dialog-body tabindex="0">
|
|
544
|
+
<!-- empty:hidden keeps a body-less dialog from carrying the body's top margin. -->
|
|
545
|
+
<div class="mt-4 flex flex-col gap-4 empty:hidden">
|
|
546
|
+
<ng-content />
|
|
547
|
+
</div>
|
|
365
548
|
</div>
|
|
366
549
|
|
|
367
|
-
<div
|
|
550
|
+
<div
|
|
551
|
+
class="flex justify-end gap-3 px-6 pt-6 pb-6 max-lg:pb-[calc(1rem+env(safe-area-inset-bottom))]"
|
|
552
|
+
data-dialog-footer
|
|
553
|
+
>
|
|
368
554
|
<ng-content select="[dialogActions]" />
|
|
369
555
|
</div>
|
|
370
556
|
</dialog>
|
|
371
|
-
`, isInline: true });
|
|
557
|
+
`, isInline: true, styles: ["dialog{transition:opacity var(--duration-base) var(--ease-sheet),transform var(--duration-base) var(--ease-sheet),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:translateY(0)}dialog:not([open]){opacity:0;transform:translateY(100%);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{opacity:0;transform:translateY(100%)}}@media(min-width:64rem){dialog{transform:scale(1)}dialog:not([open]){transform:scale(var(--enter-scale))}@starting-style{dialog[open]{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"] });
|
|
372
558
|
}
|
|
373
559
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDialog, decorators: [{
|
|
374
560
|
type: Component,
|
|
375
|
-
args: [{
|
|
376
|
-
selector: 'ui-dialog',
|
|
377
|
-
template: `
|
|
561
|
+
args: [{ selector: 'ui-dialog', template: `
|
|
378
562
|
<dialog
|
|
379
563
|
[attr.aria-describedby]="description() ? descriptionId : null"
|
|
380
564
|
[attr.aria-labelledby]="headingId"
|
|
381
565
|
[class]="classes()"
|
|
382
566
|
(close)="onNativeClose()"
|
|
383
567
|
>
|
|
384
|
-
<
|
|
568
|
+
<div aria-hidden="true" class="flex h-7 items-center justify-center lg:hidden" data-dialog-handle>
|
|
569
|
+
<span class="rounded-pill h-1.5 w-9 bg-(--border)"></span>
|
|
570
|
+
</div>
|
|
385
571
|
|
|
386
|
-
|
|
387
|
-
<
|
|
388
|
-
}
|
|
572
|
+
<div class="px-6 pt-1 lg:pt-6">
|
|
573
|
+
<h2 class="text-title font-semibold" [id]="headingId">{{ heading() }}</h2>
|
|
389
574
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
575
|
+
@if (description()) {
|
|
576
|
+
<p class="text-label mt-2 text-(--muted-foreground)" [id]="descriptionId">{{ description() }}</p>
|
|
577
|
+
}
|
|
393
578
|
</div>
|
|
394
579
|
|
|
395
|
-
<div class="
|
|
580
|
+
<div class="min-h-0 flex-1 overflow-y-auto px-6" data-dialog-body tabindex="0">
|
|
581
|
+
<!-- empty:hidden keeps a body-less dialog from carrying the body's top margin. -->
|
|
582
|
+
<div class="mt-4 flex flex-col gap-4 empty:hidden">
|
|
583
|
+
<ng-content />
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
|
|
587
|
+
<div
|
|
588
|
+
class="flex justify-end gap-3 px-6 pt-6 pb-6 max-lg:pb-[calc(1rem+env(safe-area-inset-bottom))]"
|
|
589
|
+
data-dialog-footer
|
|
590
|
+
>
|
|
396
591
|
<ng-content select="[dialogActions]" />
|
|
397
592
|
</div>
|
|
398
593
|
</dialog>
|
|
399
|
-
`,
|
|
400
|
-
}]
|
|
594
|
+
`, styles: ["dialog{transition:opacity var(--duration-base) var(--ease-sheet),transform var(--duration-base) var(--ease-sheet),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:translateY(0)}dialog:not([open]){opacity:0;transform:translateY(100%);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{opacity:0;transform:translateY(100%)}}@media(min-width:64rem){dialog{transform:scale(1)}dialog:not([open]){transform:scale(var(--enter-scale))}@starting-style{dialog[open]{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"] }]
|
|
401
595
|
}], ctorParameters: () => [], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", 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"] }] } });
|
|
402
596
|
|
|
403
|
-
const nextId = (() => {
|
|
597
|
+
const nextId$1 = (() => {
|
|
404
598
|
let count = 0;
|
|
405
599
|
return () => `ui-field-${++count}`;
|
|
406
600
|
})();
|
|
@@ -434,10 +628,9 @@ class UiField {
|
|
|
434
628
|
...(ngDevMode ? [undefined, { debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
435
629
|
error = input(/* @ts-ignore */
|
|
436
630
|
...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
|
|
437
|
-
/** Unit shown inside the control's right edge, such as `EUR` or `parts`. Not part of the control's value. */
|
|
438
631
|
unit = input(/* @ts-ignore */
|
|
439
632
|
...(ngDevMode ? [undefined, { debugName: "unit" }] : /* istanbul ignore next */ []));
|
|
440
|
-
#id = nextId();
|
|
633
|
+
#id = nextId$1();
|
|
441
634
|
hintId = `${this.#id}-hint`;
|
|
442
635
|
errorId = `${this.#id}-error`;
|
|
443
636
|
#host = inject(ElementRef);
|
|
@@ -608,6 +801,192 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
608
801
|
}]
|
|
609
802
|
}], propDecorators: { errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }] } });
|
|
610
803
|
|
|
804
|
+
const MENU_CLASSES = 'm-0 min-w-44 p-1 rounded-container bg-(--elevated) shadow-[0_8px_24px_rgb(0_0_0/0.16)] text-(--foreground) transition-[opacity,transform,overlay,display] transition-discrete duration-(--duration-fast) ease-out starting:open:opacity-0 starting:open:scale-(--enter-scale) opacity-0 scale-(--enter-scale) open:opacity-100 open:scale-100';
|
|
805
|
+
const ITEM_CLASSES = 'flex w-full items-center gap-2 min-h-(--row-min) px-3 rounded-control text-label hover:bg-(--soft) focus-visible:bg-(--soft) outline-none';
|
|
806
|
+
const nextId = (() => {
|
|
807
|
+
let count = 0;
|
|
808
|
+
return () => `ui-menu-${++count}`;
|
|
809
|
+
})();
|
|
810
|
+
/**
|
|
811
|
+
* One action of a `ui-menu`; `destructive` paints it in `--negative`.
|
|
812
|
+
*
|
|
813
|
+
* @example
|
|
814
|
+
* <button uiMenuItem destructive (click)="remove()">Delete the line</button>
|
|
815
|
+
*/
|
|
816
|
+
class UiMenuItem {
|
|
817
|
+
destructive = input(false, { ...(ngDevMode ? { debugName: "destructive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
818
|
+
#host = inject(ElementRef).nativeElement;
|
|
819
|
+
#menu = inject(UiMenu);
|
|
820
|
+
classes = computed(() => `${ITEM_CLASSES}${this.destructive() ? ' text-(--negative)' : ''}`, /* @ts-ignore */
|
|
821
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
822
|
+
focus() {
|
|
823
|
+
this.#host.focus();
|
|
824
|
+
}
|
|
825
|
+
hasFocus() {
|
|
826
|
+
return this.#host === document.activeElement;
|
|
827
|
+
}
|
|
828
|
+
onClick() {
|
|
829
|
+
this.#menu.close();
|
|
830
|
+
}
|
|
831
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMenuItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
832
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiMenuItem, isStandalone: true, selector: "button[uiMenuItem]", inputs: { destructive: { classPropertyName: "destructive", publicName: "destructive", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "menuitem", "tabindex": "-1" }, listeners: { "click": "onClick()" }, properties: { "class": "classes()" } }, ngImport: i0 });
|
|
833
|
+
}
|
|
834
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMenuItem, decorators: [{
|
|
835
|
+
type: Directive,
|
|
836
|
+
args: [{
|
|
837
|
+
selector: 'button[uiMenuItem]',
|
|
838
|
+
host: {
|
|
839
|
+
role: 'menuitem',
|
|
840
|
+
tabindex: '-1',
|
|
841
|
+
'[class]': 'classes()',
|
|
842
|
+
'(click)': 'onClick()',
|
|
843
|
+
},
|
|
844
|
+
}]
|
|
845
|
+
}], propDecorators: { destructive: [{ type: i0.Input, args: [{ isSignal: true, alias: "destructive", required: false }] }] } });
|
|
846
|
+
/**
|
|
847
|
+
* Secondary actions menu, opened from its trigger through the native Popover API.
|
|
848
|
+
*
|
|
849
|
+
* @example
|
|
850
|
+
* <button ui-button size="icon" aria-label="More" [uiMenuTrigger]="menu">...</button>
|
|
851
|
+
* <ui-menu #menu label="Line actions">
|
|
852
|
+
* <button uiMenuItem (click)="edit()">Edit</button>
|
|
853
|
+
* <button uiMenuItem destructive (click)="remove()">Delete the line</button>
|
|
854
|
+
* </ui-menu>
|
|
855
|
+
*/
|
|
856
|
+
class UiMenu {
|
|
857
|
+
label = input.required(/* @ts-ignore */
|
|
858
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
859
|
+
menuId = nextId();
|
|
860
|
+
isOpen = signal(false, /* @ts-ignore */
|
|
861
|
+
...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
862
|
+
classes = MENU_CLASSES;
|
|
863
|
+
items = contentChildren(UiMenuItem, /* @ts-ignore */
|
|
864
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
865
|
+
#host = inject(ElementRef).nativeElement;
|
|
866
|
+
#trigger = null;
|
|
867
|
+
open(trigger) {
|
|
868
|
+
this.#trigger = trigger;
|
|
869
|
+
this.isOpen.set(true);
|
|
870
|
+
if (typeof this.#host.showPopover === 'function') {
|
|
871
|
+
this.#host.showPopover();
|
|
872
|
+
}
|
|
873
|
+
this.#position(trigger);
|
|
874
|
+
this.items()[0]?.focus();
|
|
875
|
+
}
|
|
876
|
+
close() {
|
|
877
|
+
const wasOpen = this.isOpen();
|
|
878
|
+
this.#applyClosed();
|
|
879
|
+
if (wasOpen && typeof this.#host.hidePopover === 'function') {
|
|
880
|
+
this.#host.hidePopover();
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
#applyClosed() {
|
|
884
|
+
this.isOpen.set(false);
|
|
885
|
+
this.#trigger?.focus();
|
|
886
|
+
this.#trigger = null;
|
|
887
|
+
}
|
|
888
|
+
onToggle(event) {
|
|
889
|
+
if (event.newState === 'closed') {
|
|
890
|
+
this.#applyClosed();
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
onKeydown(event) {
|
|
894
|
+
if (event.key === 'Tab' || event.key === 'Escape') {
|
|
895
|
+
event.preventDefault();
|
|
896
|
+
this.close();
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
const items = this.items();
|
|
900
|
+
const currentIndex = items.findIndex((item) => item.hasFocus());
|
|
901
|
+
const lastIndex = items.length - 1;
|
|
902
|
+
let nextIndex;
|
|
903
|
+
switch (event.key) {
|
|
904
|
+
case 'ArrowDown':
|
|
905
|
+
nextIndex = currentIndex >= lastIndex ? 0 : currentIndex + 1;
|
|
906
|
+
break;
|
|
907
|
+
case 'ArrowUp':
|
|
908
|
+
nextIndex = currentIndex <= 0 ? lastIndex : currentIndex - 1;
|
|
909
|
+
break;
|
|
910
|
+
case 'Home':
|
|
911
|
+
nextIndex = 0;
|
|
912
|
+
break;
|
|
913
|
+
case 'End':
|
|
914
|
+
nextIndex = lastIndex;
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
if (nextIndex === undefined) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
event.preventDefault();
|
|
921
|
+
items[nextIndex]?.focus();
|
|
922
|
+
}
|
|
923
|
+
#position(trigger) {
|
|
924
|
+
const margin = 8;
|
|
925
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
926
|
+
const menuRect = this.#host.getBoundingClientRect();
|
|
927
|
+
const fitsBelow = window.innerHeight - triggerRect.bottom >= menuRect.height + margin;
|
|
928
|
+
const top = fitsBelow ? triggerRect.bottom + 4 : Math.max(margin, triggerRect.top - menuRect.height - 4);
|
|
929
|
+
const left = Math.min(Math.max(triggerRect.right - menuRect.width, margin), window.innerWidth - menuRect.width - margin);
|
|
930
|
+
this.#host.style.position = 'fixed';
|
|
931
|
+
this.#host.style.top = `${Math.min(top, window.innerHeight - menuRect.height - margin)}px`;
|
|
932
|
+
this.#host.style.left = `${left}px`;
|
|
933
|
+
this.#host.style.transformOrigin = fitsBelow ? 'top right' : 'bottom right';
|
|
934
|
+
}
|
|
935
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
936
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.7", type: UiMenu, isStandalone: true, selector: "ui-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "menu", "popover": "auto" }, listeners: { "toggle": "onToggle($event)", "keydown": "onKeydown($event)" }, properties: { "id": "menuId", "class": "classes", "attr.aria-label": "label()" } }, queries: [{ propertyName: "items", predicate: UiMenuItem, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true });
|
|
937
|
+
}
|
|
938
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMenu, decorators: [{
|
|
939
|
+
type: Component,
|
|
940
|
+
args: [{
|
|
941
|
+
selector: 'ui-menu',
|
|
942
|
+
template: '<ng-content />',
|
|
943
|
+
host: {
|
|
944
|
+
role: 'menu',
|
|
945
|
+
popover: 'auto',
|
|
946
|
+
'[id]': 'menuId',
|
|
947
|
+
'[class]': 'classes',
|
|
948
|
+
'[attr.aria-label]': 'label()',
|
|
949
|
+
'(toggle)': 'onToggle($event)',
|
|
950
|
+
'(keydown)': 'onKeydown($event)',
|
|
951
|
+
},
|
|
952
|
+
}]
|
|
953
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => UiMenuItem), { isSignal: true }] }] } });
|
|
954
|
+
/**
|
|
955
|
+
* Opens a `ui-menu` from a trigger element and wires `aria-haspopup`, `aria-expanded` and
|
|
956
|
+
* `aria-controls` on it.
|
|
957
|
+
*
|
|
958
|
+
* @example
|
|
959
|
+
* <button ui-button size="icon" aria-label="More" [uiMenuTrigger]="menu">...</button>
|
|
960
|
+
*/
|
|
961
|
+
class UiMenuTrigger {
|
|
962
|
+
uiMenuTrigger = input.required(/* @ts-ignore */
|
|
963
|
+
...(ngDevMode ? [{ debugName: "uiMenuTrigger" }] : /* istanbul ignore next */ []));
|
|
964
|
+
#host = inject(ElementRef).nativeElement;
|
|
965
|
+
toggle() {
|
|
966
|
+
const menu = this.uiMenuTrigger();
|
|
967
|
+
if (menu.isOpen()) {
|
|
968
|
+
menu.close();
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
menu.open(this.#host);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
975
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiMenuTrigger, isStandalone: true, selector: "[uiMenuTrigger]", inputs: { uiMenuTrigger: { classPropertyName: "uiMenuTrigger", publicName: "uiMenuTrigger", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "aria-haspopup": "menu" }, listeners: { "click": "toggle()" }, properties: { "attr.aria-expanded": "uiMenuTrigger().isOpen()", "attr.aria-controls": "uiMenuTrigger().menuId" } }, ngImport: i0 });
|
|
976
|
+
}
|
|
977
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMenuTrigger, decorators: [{
|
|
978
|
+
type: Directive,
|
|
979
|
+
args: [{
|
|
980
|
+
selector: '[uiMenuTrigger]',
|
|
981
|
+
host: {
|
|
982
|
+
'aria-haspopup': 'menu',
|
|
983
|
+
'[attr.aria-expanded]': 'uiMenuTrigger().isOpen()',
|
|
984
|
+
'[attr.aria-controls]': 'uiMenuTrigger().menuId',
|
|
985
|
+
'(click)': 'toggle()',
|
|
986
|
+
},
|
|
987
|
+
}]
|
|
988
|
+
}], propDecorators: { uiMenuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiMenuTrigger", required: true }] }] } });
|
|
989
|
+
|
|
611
990
|
/** Ramp step used to paint the fill. `MeterTone` is derived from this tuple. */
|
|
612
991
|
const METER_TONES = [1, 2, 3, 4, 5, 6];
|
|
613
992
|
const TONE_CLASSES = {
|
|
@@ -673,11 +1052,66 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
673
1052
|
}]
|
|
674
1053
|
}], 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 }] }] } });
|
|
675
1054
|
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
1055
|
+
const BASE_CLASSES$1 = 'flex items-center gap-3 h-10 px-3 rounded-control text-label font-medium text-(--muted-foreground) transition-colors duration-(--duration-press) hover:bg-(--glow) hover:text-(--foreground) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';
|
|
1056
|
+
const ACTIVE_CLASSES = 'bg-(--soft) text-(--foreground)';
|
|
1057
|
+
/**
|
|
1058
|
+
* One destination of the sidebar navigation (Portfolio, Holdings, Allocation, Accounts).
|
|
1059
|
+
*
|
|
1060
|
+
* @example
|
|
1061
|
+
* <a ui-nav-item [active]="url === '/'" routerLink="/">
|
|
1062
|
+
* <lucide-icon navIcon name="house" />
|
|
1063
|
+
* Portfolio
|
|
1064
|
+
* </a>
|
|
1065
|
+
*/
|
|
1066
|
+
class UiNavItem {
|
|
1067
|
+
active = input(false, { ...(ngDevMode ? { debugName: "active" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1068
|
+
classes = computed(() => `${BASE_CLASSES$1}${this.active() ? ` ${ACTIVE_CLASSES}` : ''}`, /* @ts-ignore */
|
|
1069
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
1070
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiNavItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1071
|
+
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 });
|
|
1072
|
+
}
|
|
1073
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiNavItem, decorators: [{
|
|
1074
|
+
type: Component,
|
|
1075
|
+
args: [{
|
|
1076
|
+
selector: 'a[ui-nav-item]',
|
|
1077
|
+
template: '<ng-content select="[navIcon]" /><ng-content />',
|
|
1078
|
+
host: {
|
|
1079
|
+
'[class]': 'classes()',
|
|
1080
|
+
'[attr.aria-current]': "active() ? 'page' : null",
|
|
1081
|
+
},
|
|
1082
|
+
}]
|
|
1083
|
+
}], propDecorators: { active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }] } });
|
|
1084
|
+
|
|
1085
|
+
const BASE_CLASSES = 'flex w-full items-center gap-2.5 min-h-14 px-2.5 py-1.5 rounded-control text-left select-none touch-manipulation transition-colors duration-(--duration-press) hover:bg-(--glow) active:bg-(--soft) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';
|
|
1086
|
+
const SELECTED_CLASSES$1 = 'bg-(--soft)';
|
|
1087
|
+
/**
|
|
1088
|
+
* Clickable row of a list: a holding, an envelope, an account.
|
|
1089
|
+
*
|
|
1090
|
+
* @example
|
|
1091
|
+
* <a ui-row [selected]="holding.id === openId()" [routerLink]="['/holdings', holding.id]">
|
|
1092
|
+
* {{ holding.name }}
|
|
1093
|
+
* </a>
|
|
1094
|
+
*/
|
|
1095
|
+
class UiRow {
|
|
1096
|
+
selected = input(false, { ...(ngDevMode ? { debugName: "selected" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1097
|
+
classes = computed(() => `${BASE_CLASSES}${this.selected() ? ` ${SELECTED_CLASSES$1}` : ''}`, /* @ts-ignore */
|
|
1098
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
1099
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiRow, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1100
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.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 } }, host: { properties: { "class": "classes()", "attr.aria-current": "selected() ? 'true' : null" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
1101
|
+
}
|
|
1102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiRow, decorators: [{
|
|
1103
|
+
type: Component,
|
|
1104
|
+
args: [{
|
|
1105
|
+
selector: 'a[ui-row], button[ui-row]',
|
|
1106
|
+
template: '<ng-content />',
|
|
1107
|
+
host: {
|
|
1108
|
+
'[class]': 'classes()',
|
|
1109
|
+
'[attr.aria-current]': "selected() ? 'true' : null",
|
|
1110
|
+
},
|
|
1111
|
+
}]
|
|
1112
|
+
}], propDecorators: { selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }] } });
|
|
1113
|
+
|
|
1114
|
+
const OPTION_CLASSES = "relative z-10 min-h-9 flex-1 cursor-pointer px-3 text-label font-medium transition-colors 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)";
|
|
681
1115
|
const SELECTED_CLASSES = 'text-(--foreground)';
|
|
682
1116
|
const UNSELECTED_CLASSES = 'text-(--muted-foreground) hover:text-(--foreground)';
|
|
683
1117
|
/**
|
|
@@ -696,7 +1130,6 @@ class UiSegmented {
|
|
|
696
1130
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
697
1131
|
radios = viewChildren('radio', /* @ts-ignore */
|
|
698
1132
|
...(ngDevMode ? [{ debugName: "radios" }] : /* istanbul ignore next */ []));
|
|
699
|
-
// 1-based so 0 (falsy) reliably means "no match", letting @if skip the thumb entirely.
|
|
700
1133
|
thumbIndex = computed(() => {
|
|
701
1134
|
const index = this.options().findIndex((option) => option.value === this.value());
|
|
702
1135
|
return index === -1 ? 0 : index + 1;
|
|
@@ -864,16 +1297,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
864
1297
|
/** Available skeleton shapes. `SkeletonShape` is derived from this tuple. */
|
|
865
1298
|
const SKELETON_SHAPES = ['text', 'figure', 'row', 'chart', 'ring'];
|
|
866
1299
|
const LAST_LINE_WIDTH = '62%';
|
|
867
|
-
// Declared here, not in tokens.css (a pure token sheet holds no element styles or keyframes).
|
|
868
1300
|
const PULSE_ANIMATION = 'cairn-pulse var(--pulse-duration) var(--ease-out) infinite alternate';
|
|
869
1301
|
const BAR_CLASSES = 'block rounded-control bg-(--muted)';
|
|
870
1302
|
/**
|
|
871
|
-
* Decorative placeholder for content that is still loading, at the shape of the content
|
|
872
|
-
* in for.
|
|
873
|
-
* `role="status"` sentence.
|
|
874
|
-
*
|
|
875
|
-
* Pulses opacity from 1 to 0.45 and back over `--pulse-duration`, which `tokens.css` sets to `0ms`
|
|
876
|
-
* under `prefers-reduced-motion: reduce`: the pulse stops there with no branching in this component.
|
|
1303
|
+
* Decorative, pulsing placeholder for content that is still loading, at the shape of the content
|
|
1304
|
+
* it stands in for.
|
|
877
1305
|
*
|
|
878
1306
|
* @example
|
|
879
1307
|
* <ui-skeleton shape="figure" />
|
|
@@ -1011,6 +1439,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1011
1439
|
}]
|
|
1012
1440
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
1013
1441
|
|
|
1442
|
+
const CLASSES = "relative shrink-0 w-[51px] h-[31px] appearance-none cursor-pointer rounded-pill bg-(--muted) checked:bg-(--primary) transition-colors duration-(--duration-fast) ease-out disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) before:absolute before:inset-x-0 before:top-1/2 before:-translate-y-1/2 before:h-(--row-min) before:content-[''] after:absolute after:top-0.5 after:left-0.5 after:size-[27px] 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]";
|
|
1443
|
+
/**
|
|
1444
|
+
* Native checkbox styled as an iOS-style switch, exposed to assistive technology as `role="switch"`.
|
|
1445
|
+
*
|
|
1446
|
+
* @example
|
|
1447
|
+
* <input type="checkbox" uiSwitch [checked]="hidden()" (change)="toggle()" aria-label="Hide amounts" />
|
|
1448
|
+
*/
|
|
1449
|
+
class UiSwitch {
|
|
1450
|
+
classes = CLASSES;
|
|
1451
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSwitch, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1452
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiSwitch, isStandalone: true, selector: "input[type=checkbox][uiSwitch]", host: { attributes: { "role": "switch" }, properties: { "class": "classes" } }, ngImport: i0 });
|
|
1453
|
+
}
|
|
1454
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSwitch, decorators: [{
|
|
1455
|
+
type: Directive,
|
|
1456
|
+
args: [{
|
|
1457
|
+
selector: 'input[type=checkbox][uiSwitch]',
|
|
1458
|
+
host: {
|
|
1459
|
+
role: 'switch',
|
|
1460
|
+
'[class]': 'classes',
|
|
1461
|
+
},
|
|
1462
|
+
}]
|
|
1463
|
+
}] });
|
|
1464
|
+
|
|
1014
1465
|
/** Breakpoints a secondary column can be held back until. */
|
|
1015
1466
|
const CELL_BREAKPOINTS = ['md', 'lg'];
|
|
1016
1467
|
const FROM_CLASSES = {
|
|
@@ -1105,6 +1556,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1105
1556
|
}]
|
|
1106
1557
|
}], propDecorators: { numeric: [{ type: i0.Input, args: [{ isSignal: true, alias: "numeric", required: false }] }], from: [{ type: i0.Input, args: [{ isSignal: true, alias: "from", required: false }] }] } });
|
|
1107
1558
|
|
|
1559
|
+
const TAB_CLASSES = 'flex h-[52px] flex-col items-center justify-center gap-0.5 touch-manipulation select-none text-(--muted-foreground) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring)';
|
|
1560
|
+
const ACTIVE_TAB_CLASSES = 'font-semibold text-(--foreground)';
|
|
1561
|
+
/**
|
|
1562
|
+
* Bottom navigation bar for iPhone: 52px of tabs plus the home indicator's safe area.
|
|
1563
|
+
*
|
|
1564
|
+
* @example
|
|
1565
|
+
* <nav ui-tab-bar>
|
|
1566
|
+
* <a ui-tab [active]="url === '/'" routerLink="/"><lucide-icon tabIcon name="house" />Portfolio</a>
|
|
1567
|
+
* </nav>
|
|
1568
|
+
*/
|
|
1569
|
+
class UiTabBar {
|
|
1570
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTabBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1571
|
+
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 });
|
|
1572
|
+
}
|
|
1573
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTabBar, decorators: [{
|
|
1574
|
+
type: Component,
|
|
1575
|
+
args: [{
|
|
1576
|
+
selector: 'nav[ui-tab-bar]',
|
|
1577
|
+
template: '<ng-content />',
|
|
1578
|
+
host: {
|
|
1579
|
+
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)]',
|
|
1580
|
+
},
|
|
1581
|
+
}]
|
|
1582
|
+
}] });
|
|
1583
|
+
/**
|
|
1584
|
+
* One destination of `ui-tab-bar`.
|
|
1585
|
+
*
|
|
1586
|
+
* @example
|
|
1587
|
+
* <a ui-tab active routerLink="/"><lucide-icon tabIcon name="house" />Portfolio</a>
|
|
1588
|
+
*/
|
|
1589
|
+
class UiTab {
|
|
1590
|
+
active = input(false, { ...(ngDevMode ? { debugName: "active" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1591
|
+
classes = computed(() => `${TAB_CLASSES}${this.active() ? ` ${ACTIVE_TAB_CLASSES}` : ''}`, /* @ts-ignore */
|
|
1592
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
1593
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTab, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1594
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiTab, isStandalone: true, selector: "a[ui-tab]", 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: `
|
|
1595
|
+
<span class="size-6" data-tab-icon><ng-content select="[tabIcon]" /></span>
|
|
1596
|
+
<span class="text-caption" data-tab-label><ng-content /></span>
|
|
1597
|
+
`, isInline: true });
|
|
1598
|
+
}
|
|
1599
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTab, decorators: [{
|
|
1600
|
+
type: Component,
|
|
1601
|
+
args: [{
|
|
1602
|
+
selector: 'a[ui-tab]',
|
|
1603
|
+
template: `
|
|
1604
|
+
<span class="size-6" data-tab-icon><ng-content select="[tabIcon]" /></span>
|
|
1605
|
+
<span class="text-caption" data-tab-label><ng-content /></span>
|
|
1606
|
+
`,
|
|
1607
|
+
host: {
|
|
1608
|
+
'[class]': 'classes()',
|
|
1609
|
+
'[attr.aria-current]': "active() ? 'page' : null",
|
|
1610
|
+
},
|
|
1611
|
+
}]
|
|
1612
|
+
}], propDecorators: { active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }] } });
|
|
1613
|
+
|
|
1108
1614
|
/*
|
|
1109
1615
|
* Public API surface of the ui library.
|
|
1110
1616
|
*/
|
|
@@ -1113,5 +1619,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
|
|
|
1113
1619
|
* Generated bundle index. Do not edit.
|
|
1114
1620
|
*/
|
|
1115
1621
|
|
|
1116
|
-
export { AVATAR_SIZES, BADGE_VARIANTS, BUTTON_SIZES, BUTTON_VARIANTS, CARD_PADDINGS, CARD_VARIANTS, CELL_BREAKPOINTS, CONTROL_SIZES, DELTA_EMPHASES, DIALOG_WIDTHS, METER_TONES, STAT_SIZES, UiAvatar, UiBadge, UiButton, UiCard, UiDelta, UiDialog, UiField, UiInput, UiMeter, UiSegmented, UiSelect, UiSkeleton, UiStat, UiTable, UiTd, UiTextarea, UiTh, UiTr };
|
|
1622
|
+
export { AMOUNT_MASKINGS, AMOUNT_NUMERICS, ASYNC_STATES, AVATAR_SIZES, BADGE_VARIANTS, BUTTON_SIZES, BUTTON_VARIANTS, CARD_PADDINGS, CARD_VARIANTS, CELL_BREAKPOINTS, CONTROL_SIZES, DELTA_EMPHASES, DIALOG_WIDTHS, METER_TONES, STAT_SIZES, UI_AMOUNT_MASKED, UiAmount, UiAsync, UiAvatar, UiBadge, UiButton, UiCard, UiDelta, UiDialog, UiField, UiInput, UiMenu, UiMenuItem, UiMenuTrigger, UiMeter, UiNavItem, UiRow, UiSegmented, UiSelect, UiSkeleton, UiStat, UiSwitch, UiTab, UiTabBar, UiTable, UiTd, UiTextarea, UiTh, UiTr, formatAmount };
|
|
1117
1623
|
//# sourceMappingURL=joanroucoux-cairn-ui.mjs.map
|