@joanroucoux/cairn-ui 0.1.5 → 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.
- package/fesm2022/joanroucoux-cairn-ui.mjs +831 -185
- package/fesm2022/joanroucoux-cairn-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/fonts/OFL.txt +93 -0
- package/styles/fonts/rubik-latin-400-normal.woff2 +0 -0
- package/styles/fonts/rubik-latin-500-normal.woff2 +0 -0
- package/styles/fonts/rubik-latin-600-normal.woff2 +0 -0
- package/styles/fonts/rubik-latin-700-normal.woff2 +0 -0
- package/styles/fonts.css +29 -0
- package/styles/theme.css +31 -0
- package/styles/tokens.css +74 -0
- package/types/joanroucoux-cairn-ui.d.ts +241 -38
|
@@ -1,12 +1,205 @@
|
|
|
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
|
-
sm: 'size-8 text-
|
|
9
|
-
md: 'size-11 text-
|
|
201
|
+
sm: 'size-8 text-caption',
|
|
202
|
+
md: 'size-11 text-label',
|
|
10
203
|
};
|
|
11
204
|
/**
|
|
12
205
|
* Initials in a disc, standing in for a person. Single-user application: there is exactly one.
|
|
@@ -21,12 +214,12 @@ 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
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
27
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
219
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
28
221
|
}
|
|
29
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
222
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiAvatar, decorators: [{
|
|
30
223
|
type: Component,
|
|
31
224
|
args: [{
|
|
32
225
|
selector: 'ui-avatar',
|
|
@@ -40,29 +233,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
40
233
|
}], propDecorators: { initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
41
234
|
|
|
42
235
|
/** Available badge variants. `BadgeVariant` is derived from this tuple. */
|
|
43
|
-
const BADGE_VARIANTS = ['
|
|
44
|
-
const BASE_CLASSES$
|
|
236
|
+
const BADGE_VARIANTS = ['neutral', 'outline'];
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
destructive: 'bg-(--destructive) text-(--destructive-foreground)',
|
|
49
|
-
outline: 'border border-(--border) text-(--foreground)',
|
|
239
|
+
neutral: 'bg-(--muted) text-(--foreground)',
|
|
240
|
+
outline: 'shadow-[inset_0_0_0_1px_var(--border)] text-(--foreground)',
|
|
50
241
|
};
|
|
51
242
|
/**
|
|
52
|
-
* Small status descriptor for counts, states or labels.
|
|
243
|
+
* Small status descriptor for counts, states or labels, such as an instrument's asset class.
|
|
53
244
|
*
|
|
54
245
|
* @example
|
|
55
|
-
* <ui-badge
|
|
246
|
+
* <ui-badge>ETF</ui-badge>
|
|
56
247
|
*/
|
|
57
248
|
class UiBadge {
|
|
58
|
-
variant = input('
|
|
249
|
+
variant = input('neutral', /* @ts-ignore */
|
|
59
250
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
60
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
251
|
+
classes = computed(() => `${BASE_CLASSES$5} ${VARIANT_CLASSES$2[this.variant()]}`, /* @ts-ignore */
|
|
61
252
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
62
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
63
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
253
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
64
255
|
}
|
|
65
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
256
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiBadge, decorators: [{
|
|
66
257
|
type: Component,
|
|
67
258
|
args: [{
|
|
68
259
|
selector: 'ui-badge',
|
|
@@ -74,63 +265,85 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
74
265
|
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }] } });
|
|
75
266
|
|
|
76
267
|
/** Available button variants. `ButtonVariant` is derived from this tuple. */
|
|
77
|
-
const BUTTON_VARIANTS = ['primary', '
|
|
78
|
-
/** Available button sizes. `md` is the
|
|
79
|
-
const BUTTON_SIZES = ['
|
|
80
|
-
const BASE_CLASSES$
|
|
81
|
-
// Tailwind's preflight sets buttons to cursor: default (matching the native element) since v3.4:
|
|
82
|
-
// cursor-pointer restores the hand cursor consumers expect from a clickable control.
|
|
83
|
-
'inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ring) disabled:pointer-events-none disabled:opacity-50';
|
|
268
|
+
const BUTTON_VARIANTS = ['primary', 'outline', 'ghost', 'destructive'];
|
|
269
|
+
/** Available button sizes. `md` is the touch target Cairn asks for everywhere. */
|
|
270
|
+
const BUTTON_SIZES = ['md', 'lg', 'xl', 'icon'];
|
|
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';
|
|
84
272
|
const VARIANT_CLASSES$1 = {
|
|
85
|
-
primary: 'bg-(--primary)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
destructive: 'bg-(--destructive) text-(--destructive-foreground) hover:
|
|
89
|
-
ghost: 'hover:bg-(--accent) hover:text-(--accent-foreground)',
|
|
273
|
+
primary: 'bg-(--primary) text-(--primary-foreground) hover:opacity-90',
|
|
274
|
+
outline: 'bg-(--card) text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] hover:bg-(--glow)',
|
|
275
|
+
ghost: 'bg-transparent text-(--foreground) hover:bg-(--glow)',
|
|
276
|
+
destructive: 'bg-(--destructive) text-(--destructive-foreground) hover:opacity-90',
|
|
90
277
|
};
|
|
91
278
|
const SIZE_CLASSES = {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
279
|
+
md: 'min-h-(--row-min) px-4 text-label',
|
|
280
|
+
lg: 'h-11 px-[18px] text-label',
|
|
281
|
+
xl: 'h-[50px] px-6 text-body',
|
|
282
|
+
icon: 'size-(--row-min) p-0',
|
|
95
283
|
};
|
|
96
284
|
/**
|
|
97
|
-
* Styled native button
|
|
98
|
-
* button behavior (type, disabled, form submission, accessibility) for free.
|
|
99
|
-
*
|
|
100
|
-
* Also applies to an anchor, for the one case where the action really is a navigation:
|
|
101
|
-
* a download the browser has to perform itself. The element stays a link, and so does
|
|
102
|
-
* its role - never use this to make an ordinary link look like a form control.
|
|
285
|
+
* Styled native button or anchor, applied as an attribute so the host keeps every native behavior.
|
|
103
286
|
*
|
|
104
287
|
* @example
|
|
105
|
-
* <button ui-button variant="destructive"
|
|
106
|
-
* <
|
|
288
|
+
* <button ui-button variant="destructive">Delete</button>
|
|
289
|
+
* <button ui-button size="icon" variant="ghost" aria-label="More actions">...</button>
|
|
107
290
|
*/
|
|
108
291
|
class UiButton {
|
|
109
292
|
variant = input('primary', /* @ts-ignore */
|
|
110
293
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
111
294
|
size = input('md', /* @ts-ignore */
|
|
112
295
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
113
|
-
|
|
296
|
+
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
297
|
+
classes = computed(() => `${BASE_CLASSES$4} ${VARIANT_CLASSES$1[this.variant()]} ${SIZE_CLASSES[this.size()]}${this.loading() ? ' pointer-events-none opacity-70' : ''}`, /* @ts-ignore */
|
|
114
298
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
115
|
-
|
|
116
|
-
|
|
299
|
+
constructor() {
|
|
300
|
+
const host = inject(ElementRef).nativeElement;
|
|
301
|
+
const guard = (event) => {
|
|
302
|
+
if (this.loading()) {
|
|
303
|
+
event.preventDefault();
|
|
304
|
+
event.stopImmediatePropagation();
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
host.addEventListener('click', guard, { capture: true });
|
|
308
|
+
inject(DestroyRef).onDestroy(() => host.removeEventListener('click', guard, { capture: true }));
|
|
309
|
+
}
|
|
310
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
311
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiButton, isStandalone: true, selector: "button[ui-button], a[ui-button]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.aria-busy": "loading() || null", "attr.aria-disabled": "loading() || null" } }, ngImport: i0, template: `
|
|
312
|
+
@if (loading()) {
|
|
313
|
+
<span
|
|
314
|
+
aria-hidden="true"
|
|
315
|
+
class="rounded-pill size-4 border-2 border-current border-r-transparent motion-safe:animate-spin"
|
|
316
|
+
></span>
|
|
317
|
+
}
|
|
318
|
+
<ng-content />
|
|
319
|
+
`, isInline: true });
|
|
117
320
|
}
|
|
118
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
321
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiButton, decorators: [{
|
|
119
322
|
type: Component,
|
|
120
323
|
args: [{
|
|
121
324
|
selector: 'button[ui-button], a[ui-button]',
|
|
122
|
-
template:
|
|
325
|
+
template: `
|
|
326
|
+
@if (loading()) {
|
|
327
|
+
<span
|
|
328
|
+
aria-hidden="true"
|
|
329
|
+
class="rounded-pill size-4 border-2 border-current border-r-transparent motion-safe:animate-spin"
|
|
330
|
+
></span>
|
|
331
|
+
}
|
|
332
|
+
<ng-content />
|
|
333
|
+
`,
|
|
123
334
|
host: {
|
|
124
335
|
'[class]': 'classes()',
|
|
336
|
+
'[attr.aria-busy]': 'loading() || null',
|
|
337
|
+
'[attr.aria-disabled]': 'loading() || null',
|
|
125
338
|
},
|
|
126
339
|
}]
|
|
127
|
-
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
340
|
+
}], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
|
|
128
341
|
|
|
129
342
|
/** Available card variants. `CardVariant` is derived from this tuple. */
|
|
130
343
|
const CARD_VARIANTS = ['default', 'elevated'];
|
|
131
344
|
/** Available card paddings. `CardPadding` is derived from this tuple. */
|
|
132
345
|
const CARD_PADDINGS = ['none', 'sm', 'md'];
|
|
133
|
-
const BASE_CLASSES$
|
|
346
|
+
const BASE_CLASSES$3 = 'block rounded-container border';
|
|
134
347
|
const VARIANT_CLASSES = {
|
|
135
348
|
default: 'bg-(--card) border-(--border) text-(--card-foreground)',
|
|
136
349
|
elevated: 'bg-(--elevated) border-(--hairline) text-(--foreground)',
|
|
@@ -151,12 +364,12 @@ class UiCard {
|
|
|
151
364
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
152
365
|
padding = input('md', /* @ts-ignore */
|
|
153
366
|
...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
|
|
154
|
-
classes = computed(() => `${BASE_CLASSES$
|
|
367
|
+
classes = computed(() => `${BASE_CLASSES$3} ${VARIANT_CLASSES[this.variant()]} ${PADDING_CLASSES[this.padding()]}`, /* @ts-ignore */
|
|
155
368
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
156
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
157
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
369
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
158
371
|
}
|
|
159
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
372
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiCard, decorators: [{
|
|
160
373
|
type: Component,
|
|
161
374
|
args: [{
|
|
162
375
|
selector: 'ui-card',
|
|
@@ -179,8 +392,8 @@ const UI_CONTROL = new InjectionToken('UI_CONTROL');
|
|
|
179
392
|
|
|
180
393
|
/** Available delta emphases. `DeltaEmphasis` is derived from this tuple. */
|
|
181
394
|
const DELTA_EMPHASES = ['text', 'pill'];
|
|
182
|
-
const BASE_CLASSES = 'inline-flex items-center gap-1 font-
|
|
183
|
-
const PILL_CLASSES = 'rounded-
|
|
395
|
+
const BASE_CLASSES$2 = 'inline-flex items-center gap-1 text-label font-medium tabular-nums';
|
|
396
|
+
const PILL_CLASSES = 'h-6 rounded-pill px-2';
|
|
184
397
|
const TEXT_CLASSES = {
|
|
185
398
|
positive: 'text-(--positive)',
|
|
186
399
|
negative: 'text-(--negative)',
|
|
@@ -224,11 +437,11 @@ class UiDelta {
|
|
|
224
437
|
classes = computed(() => {
|
|
225
438
|
const sign = this.sign();
|
|
226
439
|
const background = this.emphasis() === 'pill' ? `${PILL_CLASSES} ${PILL_BACKGROUND_CLASS}` : 'bg-transparent';
|
|
227
|
-
return `${BASE_CLASSES} ${TEXT_CLASSES[sign]} ${background}`;
|
|
440
|
+
return `${BASE_CLASSES$2} ${TEXT_CLASSES[sign]} ${background}`;
|
|
228
441
|
}, /* @ts-ignore */
|
|
229
442
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
230
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
231
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
443
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDelta, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
444
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiDelta, isStandalone: true, selector: "ui-delta", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, unknownLabel: { classPropertyName: "unknownLabel", publicName: "unknownLabel", isSignal: true, isRequired: true, transformFunction: null }, emphasis: { classPropertyName: "emphasis", publicName: "emphasis", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: `
|
|
232
445
|
@if (sign() === 'unknown') {
|
|
233
446
|
<span aria-hidden="true">—</span>
|
|
234
447
|
<span class="sr-only">{{ unknownLabel() }}</span>
|
|
@@ -237,7 +450,7 @@ class UiDelta {
|
|
|
237
450
|
}
|
|
238
451
|
`, isInline: true });
|
|
239
452
|
}
|
|
240
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
453
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDelta, decorators: [{
|
|
241
454
|
type: Component,
|
|
242
455
|
args: [{
|
|
243
456
|
selector: 'ui-delta',
|
|
@@ -259,15 +472,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
259
472
|
const DIALOG_WIDTHS = ['md', 'lg'];
|
|
260
473
|
const WIDTH_CLASSES = {
|
|
261
474
|
md: 'max-w-md',
|
|
262
|
-
lg: 'max-w-
|
|
475
|
+
lg: 'max-w-[560px]',
|
|
263
476
|
};
|
|
264
|
-
const nextId$
|
|
477
|
+
const nextId$2 = (() => {
|
|
265
478
|
let count = 0;
|
|
266
479
|
return () => `ui-dialog-${++count}`;
|
|
267
480
|
})();
|
|
268
481
|
/**
|
|
269
|
-
* Modal dialog on the native <dialog>:
|
|
270
|
-
*
|
|
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.
|
|
271
484
|
*
|
|
272
485
|
* @example
|
|
273
486
|
* <ui-dialog heading="Delete this holding?" [open]="confirming()" (dismissed)="confirming.set(false)">
|
|
@@ -282,12 +495,11 @@ class UiDialog {
|
|
|
282
495
|
...(ngDevMode ? [undefined, { debugName: "description" }] : /* istanbul ignore next */ []));
|
|
283
496
|
width = input('lg', /* @ts-ignore */
|
|
284
497
|
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
285
|
-
open = input(false, /*
|
|
286
|
-
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
498
|
+
open = input(false, { ...(ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
287
499
|
dismissed = output();
|
|
288
|
-
headingId = nextId$
|
|
500
|
+
headingId = nextId$2();
|
|
289
501
|
descriptionId = `${this.headingId}-description`;
|
|
290
|
-
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 */
|
|
291
503
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
292
504
|
#host = inject(ElementRef);
|
|
293
505
|
constructor() {
|
|
@@ -308,62 +520,81 @@ class UiDialog {
|
|
|
308
520
|
this.dismissed.emit();
|
|
309
521
|
}
|
|
310
522
|
}
|
|
311
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
312
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
523
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
524
|
+
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 }, 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" }, ngImport: i0, template: `
|
|
313
525
|
<dialog
|
|
314
526
|
[attr.aria-describedby]="description() ? descriptionId : null"
|
|
315
527
|
[attr.aria-labelledby]="headingId"
|
|
316
528
|
[class]="classes()"
|
|
317
529
|
(close)="onNativeClose()"
|
|
318
530
|
>
|
|
319
|
-
<
|
|
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>
|
|
320
534
|
|
|
321
|
-
|
|
322
|
-
<
|
|
323
|
-
}
|
|
535
|
+
<div class="px-6 pt-1 lg:pt-6">
|
|
536
|
+
<h2 class="text-title font-semibold" [id]="headingId">{{ heading() }}</h2>
|
|
324
537
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
538
|
+
@if (description()) {
|
|
539
|
+
<p class="text-label mt-2 text-(--muted-foreground)" [id]="descriptionId">{{ description() }}</p>
|
|
540
|
+
}
|
|
328
541
|
</div>
|
|
329
542
|
|
|
330
|
-
<div class="
|
|
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>
|
|
548
|
+
</div>
|
|
549
|
+
|
|
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
|
+
>
|
|
331
554
|
<ng-content select="[dialogActions]" />
|
|
332
555
|
</div>
|
|
333
556
|
</dialog>
|
|
334
|
-
`, 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"] });
|
|
335
558
|
}
|
|
336
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
559
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDialog, decorators: [{
|
|
337
560
|
type: Component,
|
|
338
|
-
args: [{
|
|
339
|
-
selector: 'ui-dialog',
|
|
340
|
-
template: `
|
|
561
|
+
args: [{ selector: 'ui-dialog', template: `
|
|
341
562
|
<dialog
|
|
342
563
|
[attr.aria-describedby]="description() ? descriptionId : null"
|
|
343
564
|
[attr.aria-labelledby]="headingId"
|
|
344
565
|
[class]="classes()"
|
|
345
566
|
(close)="onNativeClose()"
|
|
346
567
|
>
|
|
347
|
-
<
|
|
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>
|
|
348
571
|
|
|
349
|
-
|
|
350
|
-
<
|
|
351
|
-
}
|
|
572
|
+
<div class="px-6 pt-1 lg:pt-6">
|
|
573
|
+
<h2 class="text-title font-semibold" [id]="headingId">{{ heading() }}</h2>
|
|
352
574
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
575
|
+
@if (description()) {
|
|
576
|
+
<p class="text-label mt-2 text-(--muted-foreground)" [id]="descriptionId">{{ description() }}</p>
|
|
577
|
+
}
|
|
578
|
+
</div>
|
|
579
|
+
|
|
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>
|
|
356
585
|
</div>
|
|
357
586
|
|
|
358
|
-
<div
|
|
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
|
+
>
|
|
359
591
|
<ng-content select="[dialogActions]" />
|
|
360
592
|
</div>
|
|
361
593
|
</dialog>
|
|
362
|
-
`,
|
|
363
|
-
}]
|
|
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"] }]
|
|
364
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"] }] } });
|
|
365
596
|
|
|
366
|
-
const nextId = (() => {
|
|
597
|
+
const nextId$1 = (() => {
|
|
367
598
|
let count = 0;
|
|
368
599
|
return () => `ui-field-${++count}`;
|
|
369
600
|
})();
|
|
@@ -397,13 +628,15 @@ class UiField {
|
|
|
397
628
|
...(ngDevMode ? [undefined, { debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
398
629
|
error = input(/* @ts-ignore */
|
|
399
630
|
...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
|
|
400
|
-
|
|
631
|
+
unit = input(/* @ts-ignore */
|
|
632
|
+
...(ngDevMode ? [undefined, { debugName: "unit" }] : /* istanbul ignore next */ []));
|
|
633
|
+
#id = nextId$1();
|
|
401
634
|
hintId = `${this.#id}-hint`;
|
|
402
635
|
errorId = `${this.#id}-error`;
|
|
403
636
|
#host = inject(ElementRef);
|
|
404
637
|
control = contentChild(UI_CONTROL, /* @ts-ignore */
|
|
405
638
|
...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
|
|
406
|
-
labelClasses = computed(() => this.labelHidden() ? 'sr-only' : 'text-
|
|
639
|
+
labelClasses = computed(() => this.labelHidden() ? 'sr-only' : 'text-label font-medium text-(--muted-foreground)', /* @ts-ignore */
|
|
407
640
|
...(ngDevMode ? [{ debugName: "labelClasses" }] : /* istanbul ignore next */ []));
|
|
408
641
|
/**
|
|
409
642
|
* The caller's own `error` wins: it is the only way to report something the control does not
|
|
@@ -445,25 +678,31 @@ class UiField {
|
|
|
445
678
|
}
|
|
446
679
|
});
|
|
447
680
|
}
|
|
448
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
449
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
681
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
682
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiField, isStandalone: true, selector: "ui-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, labelHidden: { classPropertyName: "labelHidden", publicName: "labelHidden", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "control", first: true, predicate: UI_CONTROL, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
450
683
|
<div class="group flex flex-col gap-1.5">
|
|
451
684
|
<!-- eslint-disable-next-line @angular-eslint/template/label-has-associated-control -- htmlFor is wired at runtime, once the projected control's id is known -->
|
|
452
685
|
<label [class]="labelClasses()">{{ label() }}</label>
|
|
453
686
|
|
|
454
|
-
<
|
|
687
|
+
<div class="relative flex items-center">
|
|
688
|
+
<ng-content />
|
|
689
|
+
|
|
690
|
+
@if (unit()) {
|
|
691
|
+
<span class="text-label pointer-events-none absolute right-3 text-(--subtle-foreground)">{{ unit() }}</span>
|
|
692
|
+
}
|
|
693
|
+
</div>
|
|
455
694
|
|
|
456
695
|
@if (hint()) {
|
|
457
|
-
<span class="text-
|
|
696
|
+
<span class="text-label text-(--muted-foreground)" [id]="hintId">{{ hint() }}</span>
|
|
458
697
|
}
|
|
459
698
|
|
|
460
699
|
@if (message()) {
|
|
461
|
-
<p class="text-
|
|
700
|
+
<p class="text-label font-medium text-(--negative)" role="alert" [id]="errorId">{{ message() }}</p>
|
|
462
701
|
}
|
|
463
702
|
</div>
|
|
464
703
|
`, isInline: true });
|
|
465
704
|
}
|
|
466
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
705
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiField, decorators: [{
|
|
467
706
|
type: Component,
|
|
468
707
|
args: [{
|
|
469
708
|
selector: 'ui-field',
|
|
@@ -472,26 +711,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
472
711
|
<!-- eslint-disable-next-line @angular-eslint/template/label-has-associated-control -- htmlFor is wired at runtime, once the projected control's id is known -->
|
|
473
712
|
<label [class]="labelClasses()">{{ label() }}</label>
|
|
474
713
|
|
|
475
|
-
<
|
|
714
|
+
<div class="relative flex items-center">
|
|
715
|
+
<ng-content />
|
|
716
|
+
|
|
717
|
+
@if (unit()) {
|
|
718
|
+
<span class="text-label pointer-events-none absolute right-3 text-(--subtle-foreground)">{{ unit() }}</span>
|
|
719
|
+
}
|
|
720
|
+
</div>
|
|
476
721
|
|
|
477
722
|
@if (hint()) {
|
|
478
|
-
<span class="text-
|
|
723
|
+
<span class="text-label text-(--muted-foreground)" [id]="hintId">{{ hint() }}</span>
|
|
479
724
|
}
|
|
480
725
|
|
|
481
726
|
@if (message()) {
|
|
482
|
-
<p class="text-
|
|
727
|
+
<p class="text-label font-medium text-(--negative)" role="alert" [id]="errorId">{{ message() }}</p>
|
|
483
728
|
}
|
|
484
729
|
</div>
|
|
485
730
|
`,
|
|
486
731
|
}]
|
|
487
|
-
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], labelHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelHidden", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => UI_CONTROL), { isSignal: true }] }] } });
|
|
732
|
+
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], labelHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelHidden", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => UI_CONTROL), { isSignal: true }] }] } });
|
|
488
733
|
|
|
489
|
-
/** Available control sizes. `md` is the
|
|
734
|
+
/** Available control sizes. `md` is the 44/40px touch target. */
|
|
490
735
|
const CONTROL_SIZES = ['sm', 'md'];
|
|
491
|
-
const CONTROL_BASE_CLASSES = 'w-full rounded-
|
|
736
|
+
const CONTROL_BASE_CLASSES = 'w-full rounded-control bg-(--background) px-3 text-body text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] transition-shadow duration-(--duration-fast) ease-out 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-40';
|
|
492
737
|
const CONTROL_SIZE_CLASSES = {
|
|
493
738
|
sm: 'h-9',
|
|
494
|
-
md: 'h-
|
|
739
|
+
md: 'min-h-(--row-min)',
|
|
495
740
|
};
|
|
496
741
|
/**
|
|
497
742
|
* Styled native text input. A directive (not a wrapper component) so the host
|
|
@@ -513,10 +758,10 @@ class UiInput {
|
|
|
513
758
|
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
514
759
|
classes = computed(() => `${CONTROL_BASE_CLASSES} ${CONTROL_SIZE_CLASSES[this.size()]}`, /* @ts-ignore */
|
|
515
760
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
516
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
517
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
761
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
762
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiInput, isStandalone: true, selector: "input[uiInput]", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, providers: [{ provide: UI_CONTROL, useExisting: forwardRef(() => UiInput) }], ngImport: i0 });
|
|
518
763
|
}
|
|
519
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
764
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiInput, decorators: [{
|
|
520
765
|
type: Directive,
|
|
521
766
|
args: [{
|
|
522
767
|
selector: 'input[uiInput]',
|
|
@@ -542,10 +787,10 @@ class UiTextarea {
|
|
|
542
787
|
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
543
788
|
touched = input(false, /* @ts-ignore */
|
|
544
789
|
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
545
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
546
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
790
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTextarea, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
791
|
+
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 bg-(--background) px-3 text-body text-(--foreground) shadow-[inset_0_0_0_1px_var(--border)] transition-shadow duration-(--duration-fast) ease-out 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-40 min-h-24 py-2" }, providers: [{ provide: UI_CONTROL, useExisting: forwardRef(() => UiTextarea) }], ngImport: i0 });
|
|
547
792
|
}
|
|
548
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
793
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTextarea, decorators: [{
|
|
549
794
|
type: Directive,
|
|
550
795
|
args: [{
|
|
551
796
|
selector: 'textarea[uiTextarea]',
|
|
@@ -556,6 +801,192 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
556
801
|
}]
|
|
557
802
|
}], propDecorators: { errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }] } });
|
|
558
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
|
+
|
|
559
990
|
/** Ramp step used to paint the fill. `MeterTone` is derived from this tuple. */
|
|
560
991
|
const METER_TONES = [1, 2, 3, 4, 5, 6];
|
|
561
992
|
const TONE_CLASSES = {
|
|
@@ -584,12 +1015,12 @@ class UiMeter {
|
|
|
584
1015
|
...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
585
1016
|
percent = computed(() => Math.min(100, Math.max(0, this.value() * 100)), /* @ts-ignore */
|
|
586
1017
|
...(ngDevMode ? [{ debugName: "percent" }] : /* istanbul ignore next */ []));
|
|
587
|
-
fillClasses = computed(() => `h-full rounded-
|
|
1018
|
+
fillClasses = computed(() => `h-full rounded-control ${TONE_CLASSES[this.tone()]}`, /* @ts-ignore */
|
|
588
1019
|
...(ngDevMode ? [{ debugName: "fillClasses" }] : /* istanbul ignore next */ []));
|
|
589
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
590
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
1020
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMeter, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1021
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiMeter, isStandalone: true, selector: "ui-meter", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, valueText: { classPropertyName: "valueText", publicName: "valueText", isSignal: true, isRequired: true, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
591
1022
|
<div
|
|
592
|
-
class="h-2.5 w-full overflow-hidden
|
|
1023
|
+
class="rounded-control h-2.5 w-full overflow-hidden bg-(--elevated)"
|
|
593
1024
|
role="meter"
|
|
594
1025
|
[attr.aria-label]="label()"
|
|
595
1026
|
[attr.aria-valuemax]="1"
|
|
@@ -601,13 +1032,13 @@ class UiMeter {
|
|
|
601
1032
|
</div>
|
|
602
1033
|
`, isInline: true });
|
|
603
1034
|
}
|
|
604
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1035
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiMeter, decorators: [{
|
|
605
1036
|
type: Component,
|
|
606
1037
|
args: [{
|
|
607
1038
|
selector: 'ui-meter',
|
|
608
1039
|
template: `
|
|
609
1040
|
<div
|
|
610
|
-
class="h-2.5 w-full overflow-hidden
|
|
1041
|
+
class="rounded-control h-2.5 w-full overflow-hidden bg-(--elevated)"
|
|
611
1042
|
role="meter"
|
|
612
1043
|
[attr.aria-label]="label()"
|
|
613
1044
|
[attr.aria-valuemax]="1"
|
|
@@ -621,11 +1052,71 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
621
1052
|
}]
|
|
622
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 }] }] } });
|
|
623
1054
|
|
|
624
|
-
const
|
|
625
|
-
const
|
|
626
|
-
const UNSELECTED_CLASSES = 'bg-(--elevated) text-(--muted-foreground) hover:text-(--foreground)';
|
|
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)';
|
|
627
1057
|
/**
|
|
628
|
-
*
|
|
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)";
|
|
1115
|
+
const SELECTED_CLASSES = 'text-(--foreground)';
|
|
1116
|
+
const UNSELECTED_CLASSES = 'text-(--muted-foreground) hover:text-(--foreground)';
|
|
1117
|
+
/**
|
|
1118
|
+
* Exclusive choice within a small, known set: a time range, a unit, a mode. The selected option's
|
|
1119
|
+
* background is a single thumb that slides under it in `transform`, instead of being redrawn.
|
|
629
1120
|
*
|
|
630
1121
|
* @example
|
|
631
1122
|
* <ui-segmented [options]="ranges" label="Time range" [(value)]="range" />
|
|
@@ -639,6 +1130,11 @@ class UiSegmented {
|
|
|
639
1130
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
640
1131
|
radios = viewChildren('radio', /* @ts-ignore */
|
|
641
1132
|
...(ngDevMode ? [{ debugName: "radios" }] : /* istanbul ignore next */ []));
|
|
1133
|
+
thumbIndex = computed(() => {
|
|
1134
|
+
const index = this.options().findIndex((option) => option.value === this.value());
|
|
1135
|
+
return index === -1 ? 0 : index + 1;
|
|
1136
|
+
}, /* @ts-ignore */
|
|
1137
|
+
...(ngDevMode ? [{ debugName: "thumbIndex" }] : /* istanbul ignore next */ []));
|
|
642
1138
|
optionClasses(optionValue) {
|
|
643
1139
|
return `${OPTION_CLASSES} ${optionValue === this.value() ? SELECTED_CLASSES : UNSELECTED_CLASSES}`;
|
|
644
1140
|
}
|
|
@@ -663,9 +1159,24 @@ class UiSegmented {
|
|
|
663
1159
|
this.value.set(next.value);
|
|
664
1160
|
this.radios()[nextIndex]?.nativeElement.focus();
|
|
665
1161
|
}
|
|
666
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
667
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
668
|
-
<div
|
|
1162
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSegmented, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1163
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiSegmented, isStandalone: true, selector: "ui-segmented", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { value: "valueChange" }, viewQueries: [{ propertyName: "radios", predicate: ["radio"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1164
|
+
<div
|
|
1165
|
+
class="rounded-control relative grid bg-(--muted) p-0.5"
|
|
1166
|
+
role="radiogroup"
|
|
1167
|
+
[attr.aria-label]="label()"
|
|
1168
|
+
[style.grid-template-columns]="'repeat(' + options().length + ', 1fr)'"
|
|
1169
|
+
>
|
|
1170
|
+
@if (thumbIndex(); as index) {
|
|
1171
|
+
<span
|
|
1172
|
+
aria-hidden="true"
|
|
1173
|
+
class="absolute top-0.5 bottom-0.5 left-0.5 rounded-[calc(var(--radius-control)-2px)] bg-(--card) shadow-[0_1px_2px_rgb(0_0_0/0.08)] transition-transform duration-(--duration-fast) ease-out"
|
|
1174
|
+
data-thumb
|
|
1175
|
+
[style.transform]="'translateX(' + (index - 1) * 100 + '%)'"
|
|
1176
|
+
[style.width]="'calc(100% / ' + options().length + ')'"
|
|
1177
|
+
></span>
|
|
1178
|
+
}
|
|
1179
|
+
|
|
669
1180
|
@for (option of options(); track option.value) {
|
|
670
1181
|
<button
|
|
671
1182
|
#radio
|
|
@@ -683,12 +1194,27 @@ class UiSegmented {
|
|
|
683
1194
|
</div>
|
|
684
1195
|
`, isInline: true });
|
|
685
1196
|
}
|
|
686
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1197
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSegmented, decorators: [{
|
|
687
1198
|
type: Component,
|
|
688
1199
|
args: [{
|
|
689
1200
|
selector: 'ui-segmented',
|
|
690
1201
|
template: `
|
|
691
|
-
<div
|
|
1202
|
+
<div
|
|
1203
|
+
class="rounded-control relative grid bg-(--muted) p-0.5"
|
|
1204
|
+
role="radiogroup"
|
|
1205
|
+
[attr.aria-label]="label()"
|
|
1206
|
+
[style.grid-template-columns]="'repeat(' + options().length + ', 1fr)'"
|
|
1207
|
+
>
|
|
1208
|
+
@if (thumbIndex(); as index) {
|
|
1209
|
+
<span
|
|
1210
|
+
aria-hidden="true"
|
|
1211
|
+
class="absolute top-0.5 bottom-0.5 left-0.5 rounded-[calc(var(--radius-control)-2px)] bg-(--card) shadow-[0_1px_2px_rgb(0_0_0/0.08)] transition-transform duration-(--duration-fast) ease-out"
|
|
1212
|
+
data-thumb
|
|
1213
|
+
[style.transform]="'translateX(' + (index - 1) * 100 + '%)'"
|
|
1214
|
+
[style.width]="'calc(100% / ' + options().length + ')'"
|
|
1215
|
+
></span>
|
|
1216
|
+
}
|
|
1217
|
+
|
|
692
1218
|
@for (option of options(); track option.value) {
|
|
693
1219
|
<button
|
|
694
1220
|
#radio
|
|
@@ -716,17 +1242,17 @@ const PICKER_CLASSES = [
|
|
|
716
1242
|
'pointer-fine:supports-[appearance:base-select]:items-center',
|
|
717
1243
|
'pointer-fine:supports-[appearance:base-select]:pr-3',
|
|
718
1244
|
'pointer-fine:supports-[appearance:base-select]:[&_option]:cursor-pointer',
|
|
719
|
-
'pointer-fine:supports-[appearance:base-select]:[&_option]:rounded-
|
|
1245
|
+
'pointer-fine:supports-[appearance:base-select]:[&_option]:rounded-control',
|
|
720
1246
|
'pointer-fine:supports-[appearance:base-select]:[&_option]:px-2.5',
|
|
721
1247
|
'pointer-fine:supports-[appearance:base-select]:[&_option]:py-2',
|
|
722
1248
|
'pointer-fine:supports-[appearance:base-select]:[&_option:hover]:bg-(--soft)',
|
|
723
1249
|
'pointer-fine:supports-[appearance:base-select]:[&_option:focus]:bg-(--soft)',
|
|
724
1250
|
'pointer-fine:supports-[appearance:base-select]:[&_option:checked]:font-medium',
|
|
725
|
-
'[&::picker-icon]:text-
|
|
1251
|
+
'[&::picker-icon]:text-caption',
|
|
726
1252
|
'[&::picker-icon]:text-(--muted-foreground)',
|
|
727
1253
|
'[&::picker(select)]:[appearance:base-select]',
|
|
728
1254
|
'[&::picker(select)]:my-1.5',
|
|
729
|
-
'[&::picker(select)]:rounded-
|
|
1255
|
+
'[&::picker(select)]:rounded-control',
|
|
730
1256
|
'[&::picker(select)]:border',
|
|
731
1257
|
'[&::picker(select)]:border-(--border)',
|
|
732
1258
|
'[&::picker(select)]:bg-(--card)',
|
|
@@ -754,10 +1280,10 @@ class UiSelect {
|
|
|
754
1280
|
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
755
1281
|
classes = computed(() => `${CONTROL_BASE_CLASSES} ${SELECT_CLASSES} ${PICKER_CLASSES} ${CONTROL_SIZE_CLASSES[this.size()]}`, /* @ts-ignore */
|
|
756
1282
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
757
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
758
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
1283
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSelect, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1284
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiSelect, isStandalone: true, selector: "select[uiSelect]", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, providers: [{ provide: UI_CONTROL, useExisting: forwardRef(() => UiSelect) }], ngImport: i0 });
|
|
759
1285
|
}
|
|
760
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1286
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSelect, decorators: [{
|
|
761
1287
|
type: Directive,
|
|
762
1288
|
args: [{
|
|
763
1289
|
selector: 'select[uiSelect]',
|
|
@@ -768,63 +1294,81 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
768
1294
|
}]
|
|
769
1295
|
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }] } });
|
|
770
1296
|
|
|
1297
|
+
/** Available skeleton shapes. `SkeletonShape` is derived from this tuple. */
|
|
1298
|
+
const SKELETON_SHAPES = ['text', 'figure', 'row', 'chart', 'ring'];
|
|
771
1299
|
const LAST_LINE_WIDTH = '62%';
|
|
1300
|
+
const PULSE_ANIMATION = 'cairn-pulse var(--pulse-duration) var(--ease-out) infinite alternate';
|
|
1301
|
+
const BAR_CLASSES = 'block rounded-control bg-(--muted)';
|
|
772
1302
|
/**
|
|
773
|
-
* Decorative placeholder for content that is still loading
|
|
774
|
-
*
|
|
1303
|
+
* Decorative, pulsing placeholder for content that is still loading, at the shape of the content
|
|
1304
|
+
* it stands in for.
|
|
775
1305
|
*
|
|
776
1306
|
* @example
|
|
1307
|
+
* <ui-skeleton shape="figure" />
|
|
777
1308
|
* <ui-skeleton [lines]="3" />
|
|
778
|
-
* <ui-skeleton [height]="196" />
|
|
779
1309
|
*/
|
|
780
1310
|
class UiSkeleton {
|
|
1311
|
+
shape = input('text', /* @ts-ignore */
|
|
1312
|
+
...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
781
1313
|
lines = input(1, /* @ts-ignore */
|
|
782
1314
|
...(ngDevMode ? [{ debugName: "lines" }] : /* istanbul ignore next */ []));
|
|
783
1315
|
height = input(12, /* @ts-ignore */
|
|
784
1316
|
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
1317
|
+
animation = PULSE_ANIMATION;
|
|
785
1318
|
bars = computed(() => {
|
|
786
1319
|
const count = Math.max(1, this.lines());
|
|
787
1320
|
return Array.from({ length: count }, (_, index) => (index === count - 1 && count > 1 ? LAST_LINE_WIDTH : '100%'));
|
|
788
1321
|
}, /* @ts-ignore */
|
|
789
1322
|
...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
|
|
790
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
791
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
792
|
-
@for (line of bars(); track $index) {
|
|
793
|
-
<span
|
|
794
|
-
class="block rounded bg-(--soft) motion-safe:animate-pulse"
|
|
795
|
-
[style.height.px]="height()"
|
|
796
|
-
[style.width]="line"
|
|
797
|
-
></span>
|
|
798
|
-
}
|
|
799
|
-
`, isInline: true });
|
|
1323
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1324
|
+
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 } }, 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-11 w-56\" [style.animation]=\"animation\"></span>\n }\n @case ('row') {\n <span class=\"flex h-14 flex-col justify-center gap-2\">\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-2/5\" [style.animation]=\"animation\"></span>\n </span>\n }\n @case ('chart') {\n <span class=\"rounded-container h-48 w-full bg-(--muted)\" [style.animation]=\"animation\"></span>\n }\n @case ('ring') {\n <span\n class=\"rounded-pill size-40 border-[24px] border-(--muted) bg-transparent\"\n [style.animation]=\"animation\"\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, styles: ["@keyframes cairn-pulse{0%{opacity:1}to{opacity:.45}}\n"] });
|
|
800
1325
|
}
|
|
801
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1326
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiSkeleton, decorators: [{
|
|
802
1327
|
type: Component,
|
|
803
|
-
args: [{
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1328
|
+
args: [{ selector: 'ui-skeleton', template: `
|
|
1329
|
+
@switch (shape()) {
|
|
1330
|
+
@case ('figure') {
|
|
1331
|
+
<span class="${BAR_CLASSES} h-11 w-56" [style.animation]="animation"></span>
|
|
1332
|
+
}
|
|
1333
|
+
@case ('row') {
|
|
1334
|
+
<span class="flex h-14 flex-col justify-center gap-2">
|
|
1335
|
+
<span class="${BAR_CLASSES} h-4 w-3/5" [style.animation]="animation"></span>
|
|
1336
|
+
<span class="${BAR_CLASSES} h-3 w-2/5" [style.animation]="animation"></span>
|
|
1337
|
+
</span>
|
|
1338
|
+
}
|
|
1339
|
+
@case ('chart') {
|
|
1340
|
+
<span class="rounded-container h-48 w-full bg-(--muted)" [style.animation]="animation"></span>
|
|
1341
|
+
}
|
|
1342
|
+
@case ('ring') {
|
|
1343
|
+
<span
|
|
1344
|
+
class="rounded-pill size-40 border-[24px] border-(--muted) bg-transparent"
|
|
1345
|
+
[style.animation]="animation"
|
|
1346
|
+
></span>
|
|
1347
|
+
}
|
|
1348
|
+
@default {
|
|
1349
|
+
@for (line of bars(); track $index) {
|
|
1350
|
+
<span
|
|
1351
|
+
class="${BAR_CLASSES}"
|
|
1352
|
+
[style.animation]="animation"
|
|
1353
|
+
[style.height.px]="height()"
|
|
1354
|
+
[style.width]="line"
|
|
1355
|
+
></span>
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
812
1358
|
}
|
|
813
|
-
`,
|
|
814
|
-
host: {
|
|
1359
|
+
`, host: {
|
|
815
1360
|
'aria-hidden': 'true',
|
|
816
1361
|
class: 'flex flex-col gap-2',
|
|
817
|
-
},
|
|
818
|
-
|
|
819
|
-
}], propDecorators: { lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
|
|
1362
|
+
}, styles: ["@keyframes cairn-pulse{0%{opacity:1}to{opacity:.45}}\n"] }]
|
|
1363
|
+
}], 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 }] }] } });
|
|
820
1364
|
|
|
821
1365
|
/** Available stat sizes. `StatSize` is derived from this tuple. */
|
|
822
1366
|
const STAT_SIZES = ['hero', 'tile'];
|
|
823
1367
|
const VALUE_CLASSES = {
|
|
824
|
-
hero: 'text-
|
|
825
|
-
tile: 'text-
|
|
1368
|
+
hero: 'text-display font-semibold text-(--foreground)',
|
|
1369
|
+
tile: 'text-title font-semibold text-(--foreground)',
|
|
826
1370
|
};
|
|
827
|
-
const LABEL_CLASSES = 'text-
|
|
1371
|
+
const LABEL_CLASSES = 'text-label font-medium text-(--subtle-foreground)';
|
|
828
1372
|
/**
|
|
829
1373
|
* Label, figure and a named period of change, at two sizes: a `hero` for a screen's single
|
|
830
1374
|
* headline number, or a smaller `tile` repeated in a grid. The figure itself, its delta and its
|
|
@@ -848,8 +1392,8 @@ class UiStat {
|
|
|
848
1392
|
labelClasses = LABEL_CLASSES;
|
|
849
1393
|
valueClasses = computed(() => VALUE_CLASSES[this.size()], /* @ts-ignore */
|
|
850
1394
|
...(ngDevMode ? [{ debugName: "valueClasses" }] : /* istanbul ignore next */ []));
|
|
851
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
852
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
1395
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiStat, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1396
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: UiStat, isStandalone: true, selector: "ui-stat", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
853
1397
|
<div class="flex items-start justify-between gap-4">
|
|
854
1398
|
<div class="flex flex-col gap-1">
|
|
855
1399
|
<dl>
|
|
@@ -859,7 +1403,7 @@ class UiStat {
|
|
|
859
1403
|
</dd>
|
|
860
1404
|
</dl>
|
|
861
1405
|
|
|
862
|
-
<div class="flex flex-wrap items-center gap-x-2 gap-y-1 text-
|
|
1406
|
+
<div class="text-label flex flex-wrap items-center gap-x-2 gap-y-1 text-(--muted-foreground)">
|
|
863
1407
|
<ng-content select="[uiStatDelta]" />
|
|
864
1408
|
<ng-content select="[uiStatCaption]" />
|
|
865
1409
|
</div>
|
|
@@ -869,7 +1413,7 @@ class UiStat {
|
|
|
869
1413
|
</div>
|
|
870
1414
|
`, isInline: true });
|
|
871
1415
|
}
|
|
872
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1416
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiStat, decorators: [{
|
|
873
1417
|
type: Component,
|
|
874
1418
|
args: [{
|
|
875
1419
|
selector: 'ui-stat',
|
|
@@ -883,7 +1427,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
883
1427
|
</dd>
|
|
884
1428
|
</dl>
|
|
885
1429
|
|
|
886
|
-
<div class="flex flex-wrap items-center gap-x-2 gap-y-1 text-
|
|
1430
|
+
<div class="text-label flex flex-wrap items-center gap-x-2 gap-y-1 text-(--muted-foreground)">
|
|
887
1431
|
<ng-content select="[uiStatDelta]" />
|
|
888
1432
|
<ng-content select="[uiStatCaption]" />
|
|
889
1433
|
</div>
|
|
@@ -895,22 +1439,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
895
1439
|
}]
|
|
896
1440
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
897
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
|
+
|
|
898
1465
|
/** Breakpoints a secondary column can be held back until. */
|
|
899
1466
|
const CELL_BREAKPOINTS = ['md', 'lg'];
|
|
900
1467
|
const FROM_CLASSES = {
|
|
901
1468
|
md: 'hidden md:table-cell',
|
|
902
1469
|
lg: 'hidden lg:table-cell',
|
|
903
1470
|
};
|
|
904
|
-
const TH_CLASSES = '
|
|
905
|
-
const TD_CLASSES = '
|
|
1471
|
+
const TH_CLASSES = 'h-9 px-2 align-bottom text-caption font-medium text-(--subtle-foreground) shadow-[inset_0_-1px_0_var(--hairline)]';
|
|
1472
|
+
const TD_CLASSES = 'h-12 px-2 align-middle text-body';
|
|
906
1473
|
const NUMERIC_CLASSES = 'text-right tabular-nums';
|
|
907
1474
|
const TEXTUAL_CLASSES = 'text-left';
|
|
908
1475
|
/** Styled native table. The rows and cells stay plain <tr> and <td>. */
|
|
909
1476
|
class UiTable {
|
|
910
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
911
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.
|
|
1477
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1478
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: UiTable, isStandalone: true, selector: "table[uiTable]", host: { classAttribute: "w-full border-collapse" }, ngImport: i0 });
|
|
912
1479
|
}
|
|
913
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1480
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTable, decorators: [{
|
|
914
1481
|
type: Directive,
|
|
915
1482
|
args: [{
|
|
916
1483
|
selector: 'table[uiTable]',
|
|
@@ -919,6 +1486,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
919
1486
|
},
|
|
920
1487
|
}]
|
|
921
1488
|
}] });
|
|
1489
|
+
/**
|
|
1490
|
+
* Body or group row. `group` draws the muted banner that opens each account inside a list of
|
|
1491
|
+
* holdings: it carries no hover state of its own, unlike an ordinary row.
|
|
1492
|
+
*
|
|
1493
|
+
* @example
|
|
1494
|
+
* <tr uiTr>...
|
|
1495
|
+
* <tr uiTr group>...
|
|
1496
|
+
*/
|
|
1497
|
+
class UiTr {
|
|
1498
|
+
group = input(false, { ...(ngDevMode ? { debugName: "group" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1499
|
+
classes = computed(() => this.group() ? 'min-h-(--row-min) rounded-control bg-(--muted) font-semibold' : 'rounded-control hover:bg-(--glow)', /* @ts-ignore */
|
|
1500
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
1501
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTr, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1502
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiTr, isStandalone: true, selector: "tr[uiTr]", inputs: { group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0 });
|
|
1503
|
+
}
|
|
1504
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTr, decorators: [{
|
|
1505
|
+
type: Directive,
|
|
1506
|
+
args: [{
|
|
1507
|
+
selector: 'tr[uiTr]',
|
|
1508
|
+
host: {
|
|
1509
|
+
'[class]': 'classes()',
|
|
1510
|
+
},
|
|
1511
|
+
}]
|
|
1512
|
+
}], propDecorators: { group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }] } });
|
|
922
1513
|
const cellClasses = (base, numeric, from) => [base, numeric ? NUMERIC_CLASSES : TEXTUAL_CLASSES, from ? FROM_CLASSES[from] : ''].filter(Boolean).join(' ');
|
|
923
1514
|
/**
|
|
924
1515
|
* Column header. `numeric` right-aligns and lines the digits up; `from` holds a
|
|
@@ -933,10 +1524,10 @@ class UiTh {
|
|
|
933
1524
|
...(ngDevMode ? [{ debugName: "from" }] : /* istanbul ignore next */ []));
|
|
934
1525
|
classes = computed(() => cellClasses(TH_CLASSES, this.numeric(), this.from()), /* @ts-ignore */
|
|
935
1526
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
936
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
937
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
1527
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTh, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1528
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiTh, isStandalone: true, selector: "th[uiTh]", inputs: { numeric: { classPropertyName: "numeric", publicName: "numeric", isSignal: true, isRequired: false, transformFunction: null }, from: { classPropertyName: "from", publicName: "from", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0 });
|
|
938
1529
|
}
|
|
939
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1530
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTh, decorators: [{
|
|
940
1531
|
type: Directive,
|
|
941
1532
|
args: [{
|
|
942
1533
|
selector: 'th[uiTh]',
|
|
@@ -952,10 +1543,10 @@ class UiTd {
|
|
|
952
1543
|
...(ngDevMode ? [{ debugName: "from" }] : /* istanbul ignore next */ []));
|
|
953
1544
|
classes = computed(() => cellClasses(TD_CLASSES, this.numeric(), this.from()), /* @ts-ignore */
|
|
954
1545
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
955
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
956
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
1546
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTd, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1547
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: UiTd, isStandalone: true, selector: "td[uiTd]", inputs: { numeric: { classPropertyName: "numeric", publicName: "numeric", isSignal: true, isRequired: false, transformFunction: null }, from: { classPropertyName: "from", publicName: "from", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0 });
|
|
957
1548
|
}
|
|
958
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1549
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiTd, decorators: [{
|
|
959
1550
|
type: Directive,
|
|
960
1551
|
args: [{
|
|
961
1552
|
selector: 'td[uiTd]',
|
|
@@ -965,6 +1556,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
965
1556
|
}]
|
|
966
1557
|
}], propDecorators: { numeric: [{ type: i0.Input, args: [{ isSignal: true, alias: "numeric", required: false }] }], from: [{ type: i0.Input, args: [{ isSignal: true, alias: "from", required: false }] }] } });
|
|
967
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
|
+
|
|
968
1614
|
/*
|
|
969
1615
|
* Public API surface of the ui library.
|
|
970
1616
|
*/
|
|
@@ -973,5 +1619,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
973
1619
|
* Generated bundle index. Do not edit.
|
|
974
1620
|
*/
|
|
975
1621
|
|
|
976
|
-
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 };
|
|
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 };
|
|
977
1623
|
//# sourceMappingURL=joanroucoux-cairn-ui.mjs.map
|