@zambon-dev/ui 1.0.0 → 1.1.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/CHANGELOG.md +173 -0
- package/fesm2022/zambon-dev-ui-framework.mjs +65 -13
- package/fesm2022/zambon-dev-ui-framework.mjs.map +1 -1
- package/fesm2022/zambon-dev-ui-library.mjs +250 -34
- package/fesm2022/zambon-dev-ui-library.mjs.map +1 -1
- package/fesm2022/zambon-dev-ui-shared.mjs +304 -51
- package/fesm2022/zambon-dev-ui-shared.mjs.map +1 -1
- package/fesm2022/zambon-dev-ui.mjs +3 -0
- package/i18n/shared/login/en.json +2 -0
- package/i18n/shared/login/pt.json +2 -0
- package/i18n/shared/top-bar/en.json +3 -0
- package/i18n/shared/top-bar/pt.json +3 -0
- package/package.json +1 -1
- package/styles/common.scss +0 -1
- package/styles/components/card.scss +9 -3
- package/styles/components/dropdown-static.scss +15 -17
- package/styles/components/input-group.scss +7 -3
- package/styles/components/input.scss +38 -17
- package/styles/components/label.scss +4 -1
- package/styles/theme.css +232 -0
- package/styles/variables.scss +79 -53
- package/types/zambon-dev-ui-framework.d.ts +15 -4
- package/types/zambon-dev-ui-library.d.ts +83 -1
- package/types/zambon-dev-ui-shared.d.ts +68 -1
- package/types/zambon-dev-ui.d.ts +4 -1
- package/styles/components/button.scss +0 -101
package/styles/variables.scss
CHANGED
|
@@ -1,60 +1,86 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Component tokens -- the published contract.
|
|
3
|
+
*
|
|
4
|
+
* These names are API: applications override them, and the CHANGELOG documents them. So they stay.
|
|
5
|
+
* What changed is where their values come from. Each one now names a role in the semantic core
|
|
6
|
+
* (see shared/styles/theme.css) instead of repeating a literal, which is what lets a theme move
|
|
7
|
+
* them all at once. The component token is the contract; the core is the implementation.
|
|
8
|
+
*
|
|
9
|
+
* Wrapped in a cascade layer on purpose. Until now an application's override only won if its
|
|
10
|
+
* stylesheet was imported after this one -- same selector, same specificity, later wins. An
|
|
11
|
+
* unlayered declaration beats any layer whatever the order, so an override now wins either way.
|
|
12
|
+
* This can only make an override win more, never less. Override without a layer.
|
|
13
|
+
*
|
|
14
|
+
* A consequence worth knowing: a static override pins that token in both themes. To vary by theme,
|
|
15
|
+
* write `:root[data-theme='dark'] { --sidebar-bg: ... }` -- same specificity as ours, declared
|
|
16
|
+
* later, so it wins. Note that the dark block in theme.css touches core tokens only and never
|
|
17
|
+
* these, so a brand override stays yours in both themes without any work.
|
|
18
|
+
*/
|
|
19
|
+
@layer tokens {
|
|
20
|
+
:root {
|
|
21
|
+
/* Dimensions. Geometry of a component rather than design decisions, so they stay literal. */
|
|
22
|
+
--sidebar-collapsed-width: 70px;
|
|
23
|
+
--sidebar-expanded-width: 220px;
|
|
24
|
+
--sidebar-icon-collapsed-margin: 10px;
|
|
25
|
+
--sidebar-item-height: 2.75rem;
|
|
26
|
+
--sidebar-logo-height: 50px;
|
|
27
|
+
--sidebar-picture-size: 50px;
|
|
10
28
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
/* Surfaces & colors -- translucent "glass" (brand-tinted) */
|
|
30
|
+
--sidebar-animation-duration: var(--duration-long);
|
|
31
|
+
--sidebar-bg: var(--color-surface-brand);
|
|
32
|
+
/* A single 5% rung with no pair in the core. Adding one for one use would be a worse trade,
|
|
33
|
+
and it reads correctly in both themes because the surface beneath it is dark in both. */
|
|
34
|
+
--sidebar-nav-bg: color-mix(in srgb, white 5%, transparent);
|
|
35
|
+
--sidebar-text: var(--color-content-on-brand);
|
|
36
|
+
--sidebar-text-muted: var(--color-content-on-brand-muted);
|
|
37
|
+
--sidebar-item-hover-bg: var(--state-hover-on-brand);
|
|
38
|
+
--sidebar-item-selected-bg: var(--state-selected-on-brand);
|
|
39
|
+
--sidebar-item-selected-text: var(--color-content-on-accent);
|
|
40
|
+
--sidebar-accent: var(--color-content-on-brand);
|
|
41
|
+
--sidebar-tree-line: var(--color-line-on-brand);
|
|
21
42
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
43
|
+
/* Glass */
|
|
44
|
+
--sidebar-blur: var(--blur-glass);
|
|
45
|
+
--sidebar-shadow: var(--shadow-float-strong);
|
|
46
|
+
/* App background the glass sidebar floats over. A gradient, so it has no home in --color-*;
|
|
47
|
+
the dark theme restates it in theme.css. */
|
|
48
|
+
--app-backdrop: linear-gradient(160deg, var(--color-gray-100) 0%, var(--color-gray-300) 100%);
|
|
27
49
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
50
|
+
/* Radii (rounded restyle) */
|
|
51
|
+
--sidebar-radius: var(--radius-surface);
|
|
52
|
+
--sidebar-item-radius: var(--radius-control);
|
|
31
53
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
--tabs-item-bg: color-mix(in srgb, white 45%, transparent);
|
|
36
|
-
--tabs-item-hover-bg: color-mix(in srgb, white 70%, transparent);
|
|
37
|
-
--tabs-item-active-bg: color-mix(in srgb, white 88%, transparent);
|
|
38
|
-
--tabs-item-text: var(--color-gray-600);
|
|
39
|
-
--tabs-item-active-text: var(--color-gray-900);
|
|
40
|
-
--tabs-content-bg: color-mix(in srgb, white 88%, transparent);
|
|
41
|
-
--tabs-border: color-mix(in srgb, white 60%, transparent);
|
|
42
|
-
--tabs-close: var(--color-gray-400);
|
|
43
|
-
--tabs-close-hover-bg: color-mix(in srgb, var(--color-gray-900) 10%, transparent);
|
|
44
|
-
--tabs-close-hover: var(--color-gray-700);
|
|
54
|
+
/* Tabs -- a light glass card the views live in. The sidebar's glass is brand-tinted
|
|
55
|
+
and dark; this one is white-tinted, because the panel holds forms and grids that
|
|
56
|
+
need dark-on-light contrast. Same language (blur, radius, shadow), different hue.
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
That the active tab and the panel now resolve to one token is the point: they are a single
|
|
59
|
+
surface, which is why no seam shows between them. */
|
|
60
|
+
--tabs-item-bg: var(--color-surface-glass-weak);
|
|
61
|
+
--tabs-item-hover-bg: var(--color-surface-glass);
|
|
62
|
+
--tabs-item-active-bg: var(--color-surface-glass-strong);
|
|
63
|
+
--tabs-item-text: var(--color-content-secondary);
|
|
64
|
+
--tabs-item-active-text: var(--color-content-primary);
|
|
65
|
+
--tabs-content-bg: var(--color-surface-glass-strong);
|
|
66
|
+
--tabs-border: var(--color-line-on-glass);
|
|
67
|
+
--tabs-close: var(--color-content-subtle);
|
|
68
|
+
--tabs-close-hover-bg: var(--state-pressed);
|
|
69
|
+
--tabs-close-hover: var(--color-gray-700);
|
|
49
70
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
71
|
+
/* Glass -- softer than the sidebar's, which floats over the backdrop rather than content. */
|
|
72
|
+
--tabs-blur: var(--blur-glass);
|
|
73
|
+
--tabs-shadow: var(--shadow-float);
|
|
74
|
+
|
|
75
|
+
/* Radii & dimensions */
|
|
76
|
+
--tabs-radius: var(--radius-surface);
|
|
77
|
+
--tabs-item-radius: var(--radius-control);
|
|
78
|
+
--tabs-height: 2.75rem;
|
|
79
|
+
--tabs-item-max-width: 14rem;
|
|
80
|
+
/* Tabs compress to this before the strip starts scrolling -- still wide enough for a few
|
|
81
|
+
characters plus the close button, so a compressed tab is identifiable rather than a stub. */
|
|
82
|
+
--tabs-item-min-width: 7rem;
|
|
83
|
+
--tabs-scroll-width: 1.75rem;
|
|
84
|
+
--tabs-fade-width: 1.5rem;
|
|
85
|
+
}
|
|
60
86
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { AfterViewInit, ApplicationRef, EnvironmentProviders, EventEmitter, InjectionToken, InputSignal, ModelSignal, NgZone, OnDestroy, OnInit, OutputEmitterRef, Provider, Signal, TemplateRef, WritableSignal } from "@angular/core";
|
|
3
|
-
import { AngularAssetPattern, DataGridConfigsProvider, DataGridDataset, DataProviderService, DateValidators, FormService, IModal, IRibbonButtonOption, ITabView, ModalComponent, ModalSize, MultiEditorComponent, MultiEditorDataset, MultiSelectComponent, MultiSelectResultDataset, RibbonButtonComponent, RibbonGroupChild } from "@zambon-dev/ui/library";
|
|
3
|
+
import { AngularAssetPattern, ButtonIntent, DataGridConfigsProvider, DataGridDataset, DataProviderService, DateValidators, FormService, IModal, IRibbonButtonOption, ITabView, ModalComponent, ModalSize, MultiEditorComponent, MultiEditorDataset, MultiSelectComponent, MultiSelectResultDataset, RibbonButtonComponent, RibbonGroupChild } from "@zambon-dev/ui/library";
|
|
4
4
|
import { Observable, ReplaySubject, Subject } from "rxjs";
|
|
5
5
|
import { JwtHelperService } from "@auth0/angular-jwt";
|
|
6
6
|
import { Location } from "@angular/common";
|
|
@@ -261,7 +261,12 @@ export declare class ButtonConfirmComponent extends BaseButton {
|
|
|
261
261
|
readonly color: InputSignal<string>;
|
|
262
262
|
readonly icon: InputSignal<string>;
|
|
263
263
|
readonly label: InputSignal<string>;
|
|
264
|
-
|
|
264
|
+
/**
|
|
265
|
+
* What the confirm button is for. Replaces `modalConfirmButtonColor`, which took a Tailwind
|
|
266
|
+
* palette name -- `green-500` -- and so could neither follow a theme nor say what the button
|
|
267
|
+
* meant.
|
|
268
|
+
*/
|
|
269
|
+
readonly modalConfirmButtonIntent: InputSignal<ButtonIntent>;
|
|
265
270
|
readonly modalConfirmButtonLabel: InputSignal<string>;
|
|
266
271
|
readonly modalMessage: InputSignal<string>;
|
|
267
272
|
readonly modalMessageIcon: InputSignal<string>;
|
|
@@ -293,8 +298,8 @@ export declare class ButtonConfirmComponent extends BaseButton {
|
|
|
293
298
|
"required": false;
|
|
294
299
|
"isSignal": true;
|
|
295
300
|
};
|
|
296
|
-
"
|
|
297
|
-
"alias": "
|
|
301
|
+
"modalConfirmButtonIntent": {
|
|
302
|
+
"alias": "modalConfirmButtonIntent";
|
|
298
303
|
"required": false;
|
|
299
304
|
"isSignal": true;
|
|
300
305
|
};
|
|
@@ -1443,6 +1448,8 @@ export interface AppConfigOptions {
|
|
|
1443
1448
|
companyName?: string;
|
|
1444
1449
|
environment?: string;
|
|
1445
1450
|
logoUrl?: string;
|
|
1451
|
+
loginBackgroundUrl?: string;
|
|
1452
|
+
loginLogoUrl?: string;
|
|
1446
1453
|
notificationsEnabled?: boolean;
|
|
1447
1454
|
notificationsUrl?: string;
|
|
1448
1455
|
version?: string;
|
|
@@ -1457,6 +1464,10 @@ export declare class AppConfig {
|
|
|
1457
1464
|
environment: string;
|
|
1458
1465
|
/** Optional brand logo URL shown in the top bar. */
|
|
1459
1466
|
logoUrl?: string;
|
|
1467
|
+
/** Optional photograph behind the sign-in screen. Without one the app backdrop shows. */
|
|
1468
|
+
loginBackgroundUrl?: string;
|
|
1469
|
+
/** Optional logo on the sign-in card. Falls back to `logoUrl`. */
|
|
1470
|
+
loginLogoUrl?: string;
|
|
1460
1471
|
/** Whether the top-bar notifications feature (bell + badge) is enabled. */
|
|
1461
1472
|
notificationsEnabled: boolean;
|
|
1462
1473
|
/** SignalR hub URL the notifications service connects to when enabled. */
|
|
@@ -10,6 +10,87 @@ export declare abstract class BaseComponent implements OnDestroy {
|
|
|
10
10
|
static ɵfac: i0.ɵɵFactoryDeclaration<BaseComponent, never>;
|
|
11
11
|
static ɵcmp: i0.ɵɵComponentDeclaration<BaseComponent, "ng-component", never, {}, {}, never, never, true, never>;
|
|
12
12
|
}
|
|
13
|
+
/** What the button is for, which is what decides how loud it looks. */
|
|
14
|
+
type ButtonIntent = 'primary' | 'secondary' | 'danger' | 'ghost';
|
|
15
|
+
/**
|
|
16
|
+
* The one button.
|
|
17
|
+
*
|
|
18
|
+
* It replaces the global `.btn` classes, which generated 154 variants by interpolating Tailwind
|
|
19
|
+
* colour names -- `btn blue-500`, `btn red-700` -- in a `@for` loop. Those named a palette entry
|
|
20
|
+
* rather than a purpose, so a screen's buttons were a colour decision taken 154 times, and none of
|
|
21
|
+
* them could follow a theme. An intent says what the button is for and lets the theme decide what
|
|
22
|
+
* that looks like.
|
|
23
|
+
*
|
|
24
|
+
* `lib-ribbon-button` is deliberately not folded in here. It is a different shape -- icon above
|
|
25
|
+
* label, large hit area, an options menu -- and merging the two would produce one component with
|
|
26
|
+
* two personalities. They share the tokens instead.
|
|
27
|
+
*
|
|
28
|
+
* There is no click output: the inner element is a real `<button>`, so a `(click)` bound on
|
|
29
|
+
* `<lib-button>` receives the bubbled event, and a disabled button emits nothing to bubble. For the
|
|
30
|
+
* same reason `type="submit"` works from inside a form with no wiring.
|
|
31
|
+
*/
|
|
32
|
+
export declare class ButtonComponent {
|
|
33
|
+
/** What the button is for. Governs fill and text colour. */
|
|
34
|
+
readonly intent: InputSignal<ButtonIntent>;
|
|
35
|
+
/** Native button type. `submit` works because the rendered button is inside the host's form. */
|
|
36
|
+
readonly type: InputSignal<'button' | 'submit' | 'reset'>;
|
|
37
|
+
readonly disabled: InputSignal<boolean>;
|
|
38
|
+
/** Shows a spinner, announces `aria-busy` and blocks activation -- by the keyboard too. */
|
|
39
|
+
readonly loading: InputSignal<boolean>;
|
|
40
|
+
/** Transient confirmation. Blocks activation so the action cannot be fired twice. */
|
|
41
|
+
readonly success: InputSignal<boolean>;
|
|
42
|
+
/** Transient failure. Leaves the button usable so the action can be retried. */
|
|
43
|
+
readonly error: InputSignal<boolean>;
|
|
44
|
+
/** Font Awesome classes for a leading icon, e.g. `fa-solid fa-floppy-disk`. */
|
|
45
|
+
readonly icon: InputSignal<string | undefined>;
|
|
46
|
+
/** Required when the button has an icon and no text. */
|
|
47
|
+
readonly ariaLabel: InputSignal<string | undefined>;
|
|
48
|
+
/** Loading and success both stand for "this action is settled"; error is retryable. */
|
|
49
|
+
protected readonly isBlocked: Signal<boolean>;
|
|
50
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ButtonComponent, never>;
|
|
51
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ButtonComponent, "lib-button", never, {
|
|
52
|
+
"intent": {
|
|
53
|
+
"alias": "intent";
|
|
54
|
+
"required": false;
|
|
55
|
+
"isSignal": true;
|
|
56
|
+
};
|
|
57
|
+
"type": {
|
|
58
|
+
"alias": "type";
|
|
59
|
+
"required": false;
|
|
60
|
+
"isSignal": true;
|
|
61
|
+
};
|
|
62
|
+
"disabled": {
|
|
63
|
+
"alias": "disabled";
|
|
64
|
+
"required": false;
|
|
65
|
+
"isSignal": true;
|
|
66
|
+
};
|
|
67
|
+
"loading": {
|
|
68
|
+
"alias": "loading";
|
|
69
|
+
"required": false;
|
|
70
|
+
"isSignal": true;
|
|
71
|
+
};
|
|
72
|
+
"success": {
|
|
73
|
+
"alias": "success";
|
|
74
|
+
"required": false;
|
|
75
|
+
"isSignal": true;
|
|
76
|
+
};
|
|
77
|
+
"error": {
|
|
78
|
+
"alias": "error";
|
|
79
|
+
"required": false;
|
|
80
|
+
"isSignal": true;
|
|
81
|
+
};
|
|
82
|
+
"icon": {
|
|
83
|
+
"alias": "icon";
|
|
84
|
+
"required": false;
|
|
85
|
+
"isSignal": true;
|
|
86
|
+
};
|
|
87
|
+
"ariaLabel": {
|
|
88
|
+
"alias": "ariaLabel";
|
|
89
|
+
"required": false;
|
|
90
|
+
"isSignal": true;
|
|
91
|
+
};
|
|
92
|
+
}, {}, never, ["*"], true, never>;
|
|
93
|
+
}
|
|
13
94
|
export interface IBatchUpdate<TEntity, TKey> {
|
|
14
95
|
entitiesToDelete: Array<TKey>;
|
|
15
96
|
entitiesToInsertOrUpdate: Array<TEntity>;
|
|
@@ -1672,4 +1753,5 @@ export declare class DateValidators {
|
|
|
1672
1753
|
static lessThanToday(control: AbstractControl): ValidationErrors | null;
|
|
1673
1754
|
static maxDate(maxDate: Date): ValidatorFn;
|
|
1674
1755
|
static minDate(minDate: Date): ValidatorFn;
|
|
1675
|
-
}
|
|
1756
|
+
}
|
|
1757
|
+
export type { ButtonIntent };
|
|
@@ -548,8 +548,11 @@ export declare function createZambonSharedTranslateLoader(httpBackend: HttpBacke
|
|
|
548
548
|
export declare function provideZambonSharedTranslateLoader(appResources?: (string | TranslationResource)[]): Provider;
|
|
549
549
|
export declare function provideZambonSharedTranslateService(config?: ZambonSharedTranslateConfig): EnvironmentProviders;
|
|
550
550
|
export declare class LoginLayoutComponent {
|
|
551
|
+
private config;
|
|
552
|
+
/** Optional photograph behind the card. Absent leaves the app backdrop showing. */
|
|
553
|
+
protected get backgroundUrl(): string | undefined;
|
|
551
554
|
static ɵfac: i0.ɵɵFactoryDeclaration<LoginLayoutComponent, never>;
|
|
552
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<LoginLayoutComponent, "shared-login-layout", never, {}, {}, never,
|
|
555
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LoginLayoutComponent, "shared-login-layout", never, {}, {}, never, ["*"], true, never>;
|
|
553
556
|
}
|
|
554
557
|
export declare class MainLayoutComponent implements OnInit, OnDestroy {
|
|
555
558
|
private readonly logoutModal;
|
|
@@ -672,4 +675,68 @@ export declare class UtcDatePipe implements PipeTransform {
|
|
|
672
675
|
transform(value?: Date | string): Date | null;
|
|
673
676
|
static ɵfac: i0.ɵɵFactoryDeclaration<UtcDatePipe, never>;
|
|
674
677
|
static ɵpipe: i0.ɵɵPipeDeclaration<UtcDatePipe, "utcDate", true>;
|
|
678
|
+
}
|
|
679
|
+
/** What the user chose. `system` follows the operating system and keeps following it. */
|
|
680
|
+
export type ThemePreference = 'light' | 'dark' | 'system';
|
|
681
|
+
/** What the stylesheet is actually asked for. The CSS never sees `system`. */
|
|
682
|
+
export type ResolvedTheme = 'light' | 'dark';
|
|
683
|
+
/** Key the preference is stored under, and the one the pre-paint snippet has to read. */
|
|
684
|
+
export declare const THEME_STORAGE_KEY = "zambon-theme";
|
|
685
|
+
/**
|
|
686
|
+
* Owns the light/dark choice and writes it to `<html>`.
|
|
687
|
+
*
|
|
688
|
+
* The CSS switch is an attribute rather than `prefers-color-scheme` so that both sides keep a
|
|
689
|
+
* choice: an application can leave the attribute unwritten and never have a dark theme, and a user
|
|
690
|
+
* can pick light on a dark machine. Following the operating system is still a supported choice --
|
|
691
|
+
* it is resolved here, in TypeScript, and written out as a concrete `light` or `dark`. That is why
|
|
692
|
+
* `theme.css` holds one dark block instead of two that would have to be kept identical.
|
|
693
|
+
*
|
|
694
|
+
* The attribute is normally written before this service exists, by the snippet in `index.html`
|
|
695
|
+
* documented in the design-tokens guide -- an Angular service cannot run before the first paint,
|
|
696
|
+
* and a frame of the wrong theme is worse than no theme. This picks up from there.
|
|
697
|
+
*/
|
|
698
|
+
export declare class ThemeService {
|
|
699
|
+
private readonly document;
|
|
700
|
+
private readonly preferenceState;
|
|
701
|
+
private readonly systemPrefersDark;
|
|
702
|
+
/** What the user chose, including `system`. */
|
|
703
|
+
readonly preference: import("@angular/core").Signal<ThemePreference>;
|
|
704
|
+
/** What is actually applied right now. `system` is already resolved here. */
|
|
705
|
+
readonly resolved: import("@angular/core").Signal<ResolvedTheme>;
|
|
706
|
+
constructor();
|
|
707
|
+
/** Records a choice and applies it. Persisting is best-effort: a blocked store is not an error. */
|
|
708
|
+
setPreference(preference: ThemePreference): void;
|
|
709
|
+
/** Steps light -> dark -> system -> light, which is what a single-button switch needs. */
|
|
710
|
+
cyclePreference(): void;
|
|
711
|
+
private readStoredPreference;
|
|
712
|
+
private readSystemPrefersDark;
|
|
713
|
+
/**
|
|
714
|
+
* Keeps following the operating system after the first read. Without this, choosing `system` and
|
|
715
|
+
* then switching the machine to dark would leave the application on light until a reload.
|
|
716
|
+
*/
|
|
717
|
+
private watchSystemPreference;
|
|
718
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThemeService, never>;
|
|
719
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* The top bar's light/dark control.
|
|
723
|
+
*
|
|
724
|
+
* One button stepping light -> dark -> system rather than three, because the bar is tight and this
|
|
725
|
+
* is not a setting people change often. The icon shows the choice that is in force, not the one a
|
|
726
|
+
* click would bring: a control that displays its own next state cannot be read at a glance.
|
|
727
|
+
*
|
|
728
|
+
* It has no stylesheet: a ghost `lib-button` is already the icon-button treatment, and a second
|
|
729
|
+
* copy of it here would drift from the one in the top bar beside it.
|
|
730
|
+
*/
|
|
731
|
+
export declare class ThemeSwitchComponent {
|
|
732
|
+
private readonly theme;
|
|
733
|
+
/** The choice in force, including `system`. */
|
|
734
|
+
protected readonly preference: import("@angular/core").Signal<ThemePreference>;
|
|
735
|
+
/** Full Font Awesome classes for the current choice -- the style prefix is not optional. */
|
|
736
|
+
protected readonly icon: import("@angular/core").Signal<string>;
|
|
737
|
+
/** Translation key naming the current choice, used for the label and the tooltip. */
|
|
738
|
+
protected readonly label: import("@angular/core").Signal<string>;
|
|
739
|
+
protected onClick(): void;
|
|
740
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThemeSwitchComponent, never>;
|
|
741
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ThemeSwitchComponent, "shared-theme-switch", never, {}, {}, never, never, true, never>;
|
|
675
742
|
}
|
package/types/zambon-dev-ui.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { ButtonComponent as LibButtonComponent } from "@zambon-dev/ui/library";
|
|
2
|
+
import { ButtonComponent } from "@zambon-dev/ui/framework";
|
|
1
3
|
export * from "@zambon-dev/ui/library";
|
|
2
4
|
export * from "@zambon-dev/ui/framework";
|
|
3
|
-
export * from "@zambon-dev/ui/shared";
|
|
5
|
+
export * from "@zambon-dev/ui/shared";
|
|
6
|
+
export { ButtonComponent, LibButtonComponent };
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
input[type='button'],
|
|
2
|
-
input[type='reset'],
|
|
3
|
-
input[type='submit'],
|
|
4
|
-
button,
|
|
5
|
-
a {
|
|
6
|
-
&.btn {
|
|
7
|
-
@apply relative px-3 py-1.5 inline-block text-center rounded-sm select-none;
|
|
8
|
-
@apply bg-slate-700 text-white;
|
|
9
|
-
@apply transition-all;
|
|
10
|
-
|
|
11
|
-
@apply focus:ring-4 focus:ring-slate-600/30;
|
|
12
|
-
@apply focus:bg-slate-800 hover:bg-slate-800;
|
|
13
|
-
@apply active:bg-slate-900;
|
|
14
|
-
|
|
15
|
-
i:not(:last-child) {
|
|
16
|
-
@apply mr-2;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
&.disabled,
|
|
20
|
-
&:disabled {
|
|
21
|
-
@apply pointer-events-none opacity-[.60] ring-0;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
&.error {
|
|
25
|
-
@apply bg-red-700 focus:ring-red-600/30;
|
|
26
|
-
@apply focus:bg-red-800 hover:bg-red-800;
|
|
27
|
-
@apply active:bg-red-900;
|
|
28
|
-
|
|
29
|
-
i:before {
|
|
30
|
-
@apply content-['\f00d'];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
&.success {
|
|
35
|
-
@apply bg-green-700 ring-4 ring-green-600/30;
|
|
36
|
-
@apply pointer-events-none opacity-[.60];
|
|
37
|
-
|
|
38
|
-
i:before {
|
|
39
|
-
@apply content-['\f00c'];
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
&.loading {
|
|
44
|
-
@apply pointer-events-none opacity-[.60] ring-0 pl-8;
|
|
45
|
-
@apply transition-[margin-left] duration-200 ease-in-out;
|
|
46
|
-
|
|
47
|
-
&:before {
|
|
48
|
-
@apply content-[''] absolute left-2 top-0 bg-center bg-contain bg-no-repeat w-5 h-full animate-spin text-white;
|
|
49
|
-
@apply transition-opacity duration-200 ease-in-out;
|
|
50
|
-
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"%3E%3Ccircle style="opacity:.25" cx="12" cy="12" r="10" stroke="white" stroke-width="4"%3E%3C/circle%3E%3Cpath style="opacity:.75" fill="white" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"%3E%3C/path%3E%3C/svg%3E');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
i {
|
|
54
|
-
@apply invisible;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@mixin button($color) {
|
|
61
|
-
input[type='button'],
|
|
62
|
-
input[type='reset'],
|
|
63
|
-
input[type='submit'],
|
|
64
|
-
button,
|
|
65
|
-
a {
|
|
66
|
-
&.btn {
|
|
67
|
-
@for $i from 1 through 7 {
|
|
68
|
-
&.#{$color + '-' + $i + '00'} {
|
|
69
|
-
@if $i > 4 {
|
|
70
|
-
@apply text-white;
|
|
71
|
-
} @else {
|
|
72
|
-
@apply text-black;
|
|
73
|
-
|
|
74
|
-
&.loading:before {
|
|
75
|
-
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"%3E%3Ccircle style="opacity:.25" cx="12" cy="12" r="10" stroke="black" stroke-width="4"%3E%3C/circle%3E%3Cpath style="opacity:.75" fill="black" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"%3E%3C/path%3E%3C/svg%3E');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
@apply #{'bg-' + $color + '-' + $i + '00'};
|
|
80
|
-
|
|
81
|
-
$ring: $i + '00/30';
|
|
82
|
-
@if $i < 5 {
|
|
83
|
-
$ring: '400/20';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
@apply #{'focus:ring-' + $color + '-' + $ring};
|
|
87
|
-
@apply #{'focus:bg-' + $color + '-' + ($i+1) + '00'};
|
|
88
|
-
@apply #{'hover:bg-' + $color + '-' + ($i+1) + '00'};
|
|
89
|
-
@apply #{'active:bg-' + $color + '-' + ($i+2) + '00'};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
$colors:
|
|
97
|
-
'slate', 'gray', 'zinc', 'neutral', 'stone', 'red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal',
|
|
98
|
-
'cyan', 'sky', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose', 'primary';
|
|
99
|
-
@each $color in $colors {
|
|
100
|
-
@include button($color);
|
|
101
|
-
}
|