@foxeltech/angular-ui 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components.css +2 -17
- package/fesm2022/foxeltech-angular-ui.mjs +509 -243
- package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
- package/package.json +15 -15
- package/src/lib/styles/components.css +2 -17
- package/src/lib/styles/tailwind.css +11 -8
- package/src/lib/styles/tokens.css +131 -0
- package/src/lib/styles/utilities.css +24 -0
- package/src/lib/ui/components/button/button.component.html +4 -1
- package/src/lib/ui/components/checkbox/checkbox.component.html +3 -2
- package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +10 -5
- package/src/lib/ui/components/datetime-picker/datetime-picker.component.scss +3 -0
- package/src/lib/ui/components/drawer/drawer.component.html +78 -61
- package/src/lib/ui/components/drawer/drawer.component.scss +3 -0
- package/src/lib/ui/components/input/input.component.html +15 -3
- package/src/lib/ui/components/kanban-board/kanban-board.component.scss +3 -0
- package/src/lib/ui/components/loading-panel/loading-panel.component.html +53 -53
- package/src/lib/ui/components/master-detail/master-detail.component.css +11 -0
- package/src/lib/ui/components/master-detail/master-detail.component.html +70 -0
- package/src/lib/ui/components/meter/meter.component.html +8 -1
- package/src/lib/ui/components/radio-button/radio-button.component.html +33 -27
- package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +8 -3
- package/src/lib/ui/components/rich-text-area/rich-text-area.component.scss +3 -0
- package/src/lib/ui/components/search-bar/search-bar.component.html +11 -10
- package/src/lib/ui/components/select/select.component.html +17 -10
- package/src/lib/ui/components/severity-badges/severity-badges.component.html +5 -4
- package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +2 -2
- package/src/lib/ui/components/slider/slider.component.html +20 -15
- package/src/lib/ui/components/switch/switch.component.html +11 -6
- package/src/lib/ui/components/tab-group/tab-group.component.html +44 -27
- package/src/lib/ui/components/tab-group/tab-group.component.scss +3 -0
- package/src/lib/ui/components/tag/tag.component.html +6 -1
- package/src/lib/ui/components/toast/toast.component.html +38 -35
- package/src/lib/ui/components/tree-diagram/tree-diagram.component.html +21 -19
- package/src/lib/ui/dialogs/confirmation/confirmation.component.html +1 -1
- package/tailwind.css +11 -8
- package/tokens.css +131 -0
- package/{index.d.ts → types/foxeltech-angular-ui.d.ts} +123 -34
- package/utilities.css +24 -0
- package/tailwind-config.d.ts +0 -4
- package/tailwind.config.js +0 -113
package/package.json
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foxeltech/angular-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"peerDependencies": {
|
|
9
|
-
"tailwindcss": "^
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"@angular/
|
|
13
|
-
"@angular/
|
|
14
|
-
"@angular/
|
|
15
|
-
"@angular/
|
|
16
|
-
"@angular/material": "^20.2.7",
|
|
17
|
-
"@angular/cdk": "^20.2.7",
|
|
9
|
+
"tailwindcss": "^4.0.0",
|
|
10
|
+
"@angular/common": "^20.3.0 || ^21.0.0",
|
|
11
|
+
"@angular/animations": "^20.3.0 || ^21.0.0",
|
|
12
|
+
"@angular/core": "^20.3.0 || ^21.0.0",
|
|
13
|
+
"@angular/forms": "^20.3.0 || ^21.0.0",
|
|
14
|
+
"@angular/material": "^20.2.7 || ^21.0.0",
|
|
15
|
+
"@angular/cdk": "^20.2.7 || ^21.0.0",
|
|
18
16
|
"lodash": "^4.17.21",
|
|
19
17
|
"@ngx-translate/core": "^14.0.0",
|
|
20
18
|
"@ngx-translate/http-loader": "^7.0.0",
|
|
@@ -22,8 +20,8 @@
|
|
|
22
20
|
},
|
|
23
21
|
"dependencies": {
|
|
24
22
|
"tslib": "^2.3.0",
|
|
25
|
-
"@danielmoncada/angular-datetime-picker": "^
|
|
26
|
-
"@danielmoncada/angular-datetime-picker-moment-adapter": "^
|
|
23
|
+
"@danielmoncada/angular-datetime-picker": "^21.0.0",
|
|
24
|
+
"@danielmoncada/angular-datetime-picker-moment-adapter": "^8.0.0",
|
|
27
25
|
"echarts": "^6.0.0",
|
|
28
26
|
"ngx-echarts": "^20.0.2",
|
|
29
27
|
"d3": "^7.9.0",
|
|
@@ -33,18 +31,19 @@
|
|
|
33
31
|
"@microsoft/signalr": "^10.0.0"
|
|
34
32
|
},
|
|
35
33
|
"exports": {
|
|
36
|
-
"./tailwind-config": "./tailwind.config.js",
|
|
37
34
|
"./tailwind.css": "./tailwind.css",
|
|
38
35
|
"./theme.css": "./theme.css",
|
|
39
36
|
"./components.css": "./components.css",
|
|
40
37
|
"./dialogs.css": "./dialogs.css",
|
|
41
38
|
"./fonts.css": "./fonts.css",
|
|
42
39
|
"./animations.css": "./animations.css",
|
|
40
|
+
"./tokens.css": "./tokens.css",
|
|
41
|
+
"./utilities.css": "./utilities.css",
|
|
43
42
|
"./package.json": {
|
|
44
43
|
"default": "./package.json"
|
|
45
44
|
},
|
|
46
45
|
".": {
|
|
47
|
-
"types": "./
|
|
46
|
+
"types": "./types/foxeltech-angular-ui.d.ts",
|
|
48
47
|
"default": "./fesm2022/foxeltech-angular-ui.mjs"
|
|
49
48
|
}
|
|
50
49
|
},
|
|
@@ -52,5 +51,6 @@
|
|
|
52
51
|
"*.css"
|
|
53
52
|
],
|
|
54
53
|
"module": "fesm2022/foxeltech-angular-ui.mjs",
|
|
55
|
-
"typings": "
|
|
54
|
+
"typings": "types/foxeltech-angular-ui.d.ts",
|
|
55
|
+
"type": "module"
|
|
56
56
|
}
|
|
@@ -296,9 +296,6 @@ input[type='password'] {
|
|
|
296
296
|
.txt-card-info-description {
|
|
297
297
|
@apply text-xs font-light text-text-secondary tracking-normal;
|
|
298
298
|
}
|
|
299
|
-
.txt-tag-common {
|
|
300
|
-
@apply font-semibold text-xs;
|
|
301
|
-
}
|
|
302
299
|
.txt-tag-default {
|
|
303
300
|
@apply txt-tag-common text-text-secondary;
|
|
304
301
|
}
|
|
@@ -330,9 +327,6 @@ input[type='password'] {
|
|
|
330
327
|
@apply font-mono text-sm text-text-primary tracking-normal;
|
|
331
328
|
}
|
|
332
329
|
|
|
333
|
-
.btn-common {
|
|
334
|
-
@apply flex text-sm h-10 font-semibold px-xl py-normal items-center justify-center rounded-lg shadow-sm transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed;
|
|
335
|
-
}
|
|
336
330
|
.btn-default {
|
|
337
331
|
@apply btn-common bg-button-default hover:opacity-75 text-text-inverse;
|
|
338
332
|
}
|
|
@@ -345,9 +339,6 @@ input[type='password'] {
|
|
|
345
339
|
.btn-alternative {
|
|
346
340
|
@apply btn-common hover:bg-bg-hover text-text-primary border border-border-default;
|
|
347
341
|
}
|
|
348
|
-
.btn-menu-common {
|
|
349
|
-
@apply flex flex-row items-center px-semi;
|
|
350
|
-
}
|
|
351
342
|
.btn-menu-top {
|
|
352
343
|
@apply btn-menu-common border-b border-border-default pt-small pb-semi;
|
|
353
344
|
}
|
|
@@ -359,7 +350,7 @@ input[type='password'] {
|
|
|
359
350
|
}
|
|
360
351
|
|
|
361
352
|
.input-default {
|
|
362
|
-
@apply block h-[36px] text-text-primary placeholder:text-text-placeholder bg-bg-primary w-full rounded-md border px-semi py-normal border-border-default focus:outline-none focus:border-2 focus:border-gradient-primary-start text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out;
|
|
353
|
+
@apply block h-[36px] text-text-primary placeholder:text-text-placeholder bg-bg-primary w-full rounded-md border px-semi py-normal border-border-default focus:outline-none focus:border-2 focus:border-gradient-primary-start focus-visible:ring-2 focus-visible:ring-primary/50 text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out;
|
|
363
354
|
}
|
|
364
355
|
.input-invalid {
|
|
365
356
|
@apply border-bg-error focus:ring-bg-error focus:border-bg-error;
|
|
@@ -368,12 +359,9 @@ input[type='password'] {
|
|
|
368
359
|
@apply focus:border-2 focus:border-border-hover2 focus:bg-bg-hover2;
|
|
369
360
|
}
|
|
370
361
|
.input-otp {
|
|
371
|
-
@apply bg-bg-primary w-full rounded-md border px-semi py-normal border-border-default focus:outline-none focus:border-2 focus:border-gradient-primary-start text-text-primary ring-0 transition-colors duration-500 ease-in-out;
|
|
362
|
+
@apply bg-bg-primary w-full rounded-md border px-semi py-normal border-border-default focus:outline-none focus:border-2 focus:border-gradient-primary-start focus-visible:ring-2 focus-visible:ring-primary/50 text-text-primary ring-0 transition-colors duration-500 ease-in-out;
|
|
372
363
|
}
|
|
373
364
|
|
|
374
|
-
.card-common {
|
|
375
|
-
@apply text-left px-xl py-large border border-border-default rounded-lg shadow-sm transition-colors duration-500 ease-in-out;
|
|
376
|
-
}
|
|
377
365
|
.card-information {
|
|
378
366
|
@apply card-common hover:opacity-70 hover:shadow-md hover:border-border-selected;
|
|
379
367
|
}
|
|
@@ -384,9 +372,6 @@ input[type='password'] {
|
|
|
384
372
|
@apply flex items-center justify-between rounded-md px-semi py-normal bg-bg-primary mb-normal last:mb-0;
|
|
385
373
|
}
|
|
386
374
|
|
|
387
|
-
.tag-common {
|
|
388
|
-
@apply flex min-w-7 px-[10px] py-[2px] leading-5 hover:opacity-70 items-center justify-center;
|
|
389
|
-
}
|
|
390
375
|
.tag-default {
|
|
391
376
|
@apply tag-common bg-bg-hover;
|
|
392
377
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
+
/* fonts.css is NOT imported here: Tailwind v4 inlines imports and rebases
|
|
2
|
+
font url()s against the entry file, which breaks webpack resolution.
|
|
3
|
+
Consumers add '@foxeltech/angular-ui/fonts.css' to their styles array. */
|
|
4
|
+
@import 'tailwindcss';
|
|
5
|
+
@import './tokens.css';
|
|
6
|
+
@import './utilities.css';
|
|
1
7
|
@import './theme.css';
|
|
2
|
-
@import './components.css';
|
|
3
|
-
@import './dialogs.css';
|
|
4
|
-
@import './fonts.css';
|
|
8
|
+
@import './components.css' layer(components);
|
|
9
|
+
@import './dialogs.css' layer(components);
|
|
5
10
|
@import './animations.css';
|
|
6
11
|
@import '@danielmoncada/angular-datetime-picker/assets/style/picker.min.css';
|
|
7
|
-
@tailwind base;
|
|
8
|
-
@tailwind components;
|
|
9
|
-
@tailwind utilities;
|
|
10
12
|
|
|
11
|
-
html,
|
|
13
|
+
html,
|
|
14
|
+
body {
|
|
12
15
|
font-family: 'Roboto';
|
|
13
16
|
margin: 0;
|
|
14
17
|
padding: 0;
|
|
15
18
|
height: 100%;
|
|
16
19
|
width: 100%;
|
|
17
20
|
background: rgb(var(--bg-primary));
|
|
18
|
-
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tailwind v4 CSS-first theme for @foxeltech/angular-ui.
|
|
3
|
+
* Replaces the old JS preset (tailwind.config.js / lodash.merge sharing).
|
|
4
|
+
*
|
|
5
|
+
* Colour tokens point at the runtime RGB-triplet variables from theme.css
|
|
6
|
+
* (dark default on :root, light via .light_theme) so theme switching keeps
|
|
7
|
+
* working; opacity modifiers like bg-critical/10 are handled by v4's
|
|
8
|
+
* color-mix() and need no <alpha-value> plumbing.
|
|
9
|
+
*/
|
|
10
|
+
/* `inline` is load-bearing: without it the rgb(var(--x)) values are baked
|
|
11
|
+
into :root once, so .light_theme's runtime triplet overrides never reach
|
|
12
|
+
the utilities. inline makes every utility carry rgb(var(--x)) itself and
|
|
13
|
+
resolve at the element that uses it — same behavior as v3. */
|
|
14
|
+
@theme inline {
|
|
15
|
+
/* Colours (names match the v3 preset so every existing class keeps working) */
|
|
16
|
+
--color-gradient-primary-start: rgb(var(--gradient-primary-start));
|
|
17
|
+
--color-gradient-primary-end: rgb(var(--gradient-primary-end));
|
|
18
|
+
--color-gradient-success-start: rgb(var(--gradient-success-start));
|
|
19
|
+
--color-gradient-success-end: rgb(var(--gradient-success-end));
|
|
20
|
+
--color-primary: rgb(var(--primary));
|
|
21
|
+
--color-success: rgb(var(--success));
|
|
22
|
+
--color-danger: rgb(var(--danger));
|
|
23
|
+
--color-warning: rgb(var(--warning));
|
|
24
|
+
--color-critical: rgb(var(--critical));
|
|
25
|
+
--color-discovery: rgb(var(--discovery));
|
|
26
|
+
--color-information: rgb(var(--information));
|
|
27
|
+
--color-bg-primary: rgb(var(--bg-primary));
|
|
28
|
+
--color-bg-canvas: rgb(var(--bg-canvas));
|
|
29
|
+
--color-bg-hover: rgb(var(--bg-primary-hover));
|
|
30
|
+
--color-bg-error: rgb(var(--bg-error-bold));
|
|
31
|
+
--color-bg-hover2: rgb(var(--bg-secondary-hover));
|
|
32
|
+
--color-text-primary: rgb(var(--text-primary));
|
|
33
|
+
--color-text-secondary: rgb(var(--text-secondary));
|
|
34
|
+
--color-text-placeholder: rgb(var(--text-placeholder));
|
|
35
|
+
--color-text-selected: rgb(var(--text-selected-primary));
|
|
36
|
+
--color-text-focus: rgb(var(--text-focus));
|
|
37
|
+
--color-text-inverse: rgb(var(--text-inverse));
|
|
38
|
+
--color-text-error: rgb(var(--text-error));
|
|
39
|
+
--color-text-link: rgb(var(--text-link-primary));
|
|
40
|
+
--color-border-default: rgb(var(--border-default));
|
|
41
|
+
--color-border-interactive: rgb(var(--border-interactive));
|
|
42
|
+
--color-border-selected: rgb(var(--border-selected));
|
|
43
|
+
--color-border-strong: rgb(var(--border-strong));
|
|
44
|
+
--color-border-hover2: rgb(var(--border-secondary-hover));
|
|
45
|
+
--color-button-default: rgb(var(--button-default));
|
|
46
|
+
--color-button-primary: rgb(var(--button-primary));
|
|
47
|
+
|
|
48
|
+
/* Breakpoints (v3 custom screens) */
|
|
49
|
+
--breakpoint-sm: 480px;
|
|
50
|
+
--breakpoint-md: 720px;
|
|
51
|
+
--breakpoint-lg: 1080px;
|
|
52
|
+
--breakpoint-xl: 1920px;
|
|
53
|
+
|
|
54
|
+
/* Shadows — same scale as the v3 preset, incl. the DEFAULT key so the bare
|
|
55
|
+
`shadow` utility keeps its v3 meaning (no shadow→shadow-sm rename sweep). */
|
|
56
|
+
--shadow-sm: 0 1px 2px 0 rgb(var(--shadow-color) / 0.05);
|
|
57
|
+
--shadow: 0 1px 3px 0 rgb(var(--shadow-color) / 0.1), 0 1px 2px -1px rgb(var(--shadow-color) / 0.1);
|
|
58
|
+
--shadow-md: 0 4px 6px -1px rgb(var(--shadow-color) / 0.1), 0 2px 4px -2px rgb(var(--shadow-color) / 0.1);
|
|
59
|
+
--shadow-lg: 0 10px 15px -3px rgb(var(--shadow-color) / 0.1), 0 4px 6px -4px rgb(var(--shadow-color) / 0.1);
|
|
60
|
+
--shadow-xl: 0 20px 25px -5px rgb(var(--shadow-color) / 0.1), 0 10px 10px -5px rgb(var(--shadow-color) / 0.1);
|
|
61
|
+
--shadow-2xl: 0 25px 50px -12px rgb(var(--shadow-color) / 0.25);
|
|
62
|
+
--inset-shadow-sm: inset 0 2px 4px 0 rgb(var(--shadow-color) / 0.05);
|
|
63
|
+
|
|
64
|
+
/* v3 default border radii, incl. DEFAULT so bare `rounded` keeps working */
|
|
65
|
+
--radius-sm: 0.125rem;
|
|
66
|
+
--radius: 0.25rem;
|
|
67
|
+
--radius-md: 0.375rem;
|
|
68
|
+
--radius-lg: 0.5rem;
|
|
69
|
+
--radius-xl: 0.75rem;
|
|
70
|
+
--radius-2xl: 1rem;
|
|
71
|
+
--radius-3xl: 1.5rem;
|
|
72
|
+
|
|
73
|
+
/* Named spacing scale (gap-small, p-semi, mb-large, …) */
|
|
74
|
+
--spacing-xs: 0.125rem;
|
|
75
|
+
--spacing-small: 0.25rem;
|
|
76
|
+
--spacing-normal: 0.5rem;
|
|
77
|
+
--spacing-semi: 0.75rem;
|
|
78
|
+
--spacing-large: 1rem;
|
|
79
|
+
--spacing-xl: 1.25rem;
|
|
80
|
+
--spacing-2xl: 1.5rem;
|
|
81
|
+
|
|
82
|
+
/* Font families */
|
|
83
|
+
--font-spmono: 'SpaceMonoRegular', monospace;
|
|
84
|
+
--font-dmsans: 'DMSans', sans-serif;
|
|
85
|
+
--font-orbitron: 'Orbitron', sans-serif;
|
|
86
|
+
--font-bevn: 'BeVietnamPro', sans-serif;
|
|
87
|
+
--font-roboto: 'Roboto', sans-serif;
|
|
88
|
+
|
|
89
|
+
/* Font-size overrides (v3 preset values) */
|
|
90
|
+
--text-xs: 0.75rem;
|
|
91
|
+
--text-sm: 0.875rem;
|
|
92
|
+
--text-base: 1rem;
|
|
93
|
+
--text-xl: 1.25rem;
|
|
94
|
+
--text-2xl: 1.625rem;
|
|
95
|
+
--text-3xl: 2rem;
|
|
96
|
+
--text-4xl: 2.25rem;
|
|
97
|
+
--text-5xl: 3rem;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* v3 backgroundImage entries — v4 has no background-image theme namespace */
|
|
101
|
+
@utility bg-gradient-primary {
|
|
102
|
+
background-image: linear-gradient(
|
|
103
|
+
135deg,
|
|
104
|
+
rgb(var(--gradient-primary-start)) 0%,
|
|
105
|
+
rgb(var(--gradient-primary-end)) 100%
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
@utility bg-gradient-success {
|
|
109
|
+
background-image: linear-gradient(
|
|
110
|
+
135deg,
|
|
111
|
+
rgb(var(--gradient-success-start)) 0%,
|
|
112
|
+
rgb(var(--gradient-success-end)) 100%
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* v3 behavior compat (per the official upgrade guide) */
|
|
117
|
+
@layer base {
|
|
118
|
+
/* v4 defaults borders to currentColor; restore the theme default */
|
|
119
|
+
*,
|
|
120
|
+
::after,
|
|
121
|
+
::before,
|
|
122
|
+
::backdrop,
|
|
123
|
+
::file-selector-button {
|
|
124
|
+
border-color: rgb(var(--border-default));
|
|
125
|
+
}
|
|
126
|
+
/* v4 preflight switched buttons to cursor: default */
|
|
127
|
+
button:not(:disabled),
|
|
128
|
+
[role='button']:not(:disabled) {
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Shared composable utilities (v4 @utility) — kept OUT of components.css
|
|
3
|
+
* because that file is imported inside layer(components) and @utility must
|
|
4
|
+
* be top-level/unlayered. Usable both in markup and via @apply.
|
|
5
|
+
*/
|
|
6
|
+
@utility txt-tag-common {
|
|
7
|
+
@apply font-semibold text-xs;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@utility btn-common {
|
|
11
|
+
@apply flex text-sm h-10 font-semibold px-xl py-normal items-center justify-center rounded-lg shadow-sm transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@utility btn-menu-common {
|
|
15
|
+
@apply flex flex-row items-center px-semi;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@utility card-common {
|
|
19
|
+
@apply text-left px-xl py-large border border-border-default rounded-lg shadow-sm transition-colors duration-500 ease-in-out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@utility tag-common {
|
|
23
|
+
@apply flex min-w-7 px-[10px] py-[2px] leading-5 hover:opacity-70 items-center justify-center;
|
|
24
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
<button
|
|
2
|
+
[attr.type]="type"
|
|
3
|
+
[attr.aria-label]="ariaLabel || null"
|
|
4
|
+
[attr.aria-busy]="loading || null"
|
|
2
5
|
[class]="class"
|
|
3
6
|
[ngClass]="{
|
|
4
7
|
'btn-default': buttonVariant === 'default',
|
|
@@ -10,7 +13,7 @@
|
|
|
10
13
|
(click)="onClick()"
|
|
11
14
|
>
|
|
12
15
|
@if (loading) {
|
|
13
|
-
<div class="flex w-full items-center justify-center">
|
|
16
|
+
<div class="flex w-full items-center justify-center" aria-hidden="true">
|
|
14
17
|
<svg
|
|
15
18
|
[attr.fill]="
|
|
16
19
|
buttonVariant !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
[class.opacity-50]="disabled"
|
|
4
4
|
[class.cursor-not-allowed]="disabled"
|
|
5
5
|
>
|
|
6
|
+
<!-- sr-only (not `hidden`): keeps the input focusable and in the a11y tree -->
|
|
6
7
|
<input
|
|
7
8
|
type="checkbox"
|
|
8
|
-
class="
|
|
9
|
+
class="peer sr-only"
|
|
9
10
|
[checked]="value"
|
|
10
11
|
[disabled]="disabled"
|
|
11
12
|
(change)="onInputChange($event)"
|
|
12
13
|
(blur)="onTouched()"
|
|
13
14
|
/>
|
|
14
15
|
<span
|
|
15
|
-
class="relative w-4 h-4 border rounded-full transition-colors duration-150 shrink-0"
|
|
16
|
+
class="relative w-4 h-4 border rounded-full transition-colors duration-150 shrink-0 peer-focus-visible:ring-2 peer-focus-visible:ring-primary/50"
|
|
16
17
|
[ngClass]="{
|
|
17
18
|
'rounded-full': rounded,
|
|
18
19
|
'rounded-sm': !rounded,
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<div class="flex flex-col mb-4 w-full text-left">
|
|
2
2
|
@if (label) {
|
|
3
|
-
<label class="txt-field-label">
|
|
3
|
+
<label class="txt-field-label" [attr.for]="fieldId">
|
|
4
4
|
{{ label }}
|
|
5
|
-
|
|
5
|
+
@if (required) {
|
|
6
|
+
<span class="txt-required" aria-hidden="true">*</span>
|
|
7
|
+
}
|
|
6
8
|
</label>
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
<div class="relative">
|
|
10
12
|
<input
|
|
13
|
+
[id]="fieldId"
|
|
11
14
|
[owlDateTimeTrigger]="picker"
|
|
12
15
|
[owlDateTime]="picker"
|
|
13
16
|
[placeholder]="placeholder"
|
|
@@ -18,11 +21,11 @@
|
|
|
18
21
|
[value]="value"
|
|
19
22
|
readonly
|
|
20
23
|
class="input-default pr-9"
|
|
21
|
-
|
|
24
|
+
/>
|
|
22
25
|
<span
|
|
23
26
|
[owlDateTimeTrigger]="picker"
|
|
24
27
|
class="absolute inset-y-0 right-0 flex items-center pr-2.5 cursor-pointer text-text-placeholder hover:text-text-primary transition-colors"
|
|
25
|
-
|
|
28
|
+
>
|
|
26
29
|
<fx-ui-hero-icon icon="calendar" [size]="20" class="text-text-primary"></fx-ui-hero-icon>
|
|
27
30
|
</span>
|
|
28
31
|
</div>
|
|
@@ -32,5 +35,7 @@
|
|
|
32
35
|
[pickerType]="type"
|
|
33
36
|
(afterPickerClosed)="onValueChange($event)"
|
|
34
37
|
></owl-date-time>
|
|
35
|
-
|
|
38
|
+
@if (invalid) {
|
|
39
|
+
<div class="txt-invalid">{{ getErrorMessage(label) }}</div>
|
|
40
|
+
}
|
|
36
41
|
</div>
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
|
|
2
|
+
@reference '../../../styles/tailwind.css';
|
|
3
|
+
|
|
1
4
|
// ─── Owl DateTime Picker Custom Styles ───────────────────────────────────────
|
|
2
5
|
|
|
3
6
|
// Container
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
href="/"
|
|
4
4
|
class="h-[60px]"
|
|
5
5
|
[ngClass]="{ 'flex items-center justify-center': !isExpanded, 'p-4': isExpanded }"
|
|
6
|
-
|
|
6
|
+
>
|
|
7
7
|
@if (logo) {
|
|
8
8
|
<img
|
|
9
9
|
[src]="logo"
|
|
10
|
+
alt="Home"
|
|
10
11
|
class="h-[20px] w-auto transition-all duration-300 ease-in-out"
|
|
11
12
|
[ngClass]="{ 'scale-90': !isExpanded, 'ml-5': isExpanded }"
|
|
12
|
-
|
|
13
|
+
/>
|
|
13
14
|
} @else {
|
|
14
15
|
<div class="h-[30px] w-auto transition-all duration-300 ease-in-out"></div>
|
|
15
16
|
}
|
|
@@ -17,29 +18,37 @@
|
|
|
17
18
|
<div class="drawer-container" [@drawerExpand]="getDrawerState()">
|
|
18
19
|
<!-- Navigation Items -->
|
|
19
20
|
<nav class="drawer-nav">
|
|
20
|
-
|
|
21
|
+
@for (item of items; track item) {
|
|
21
22
|
<ng-container
|
|
22
23
|
*ngTemplateOutlet="drawerItem; context: { $implicit: item, level: 0 }"
|
|
23
24
|
></ng-container>
|
|
24
|
-
|
|
25
|
+
}
|
|
25
26
|
</nav>
|
|
26
27
|
|
|
27
28
|
<!-- Version -->
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
@if (version) {
|
|
30
|
+
<div class="drawer-version" [matTooltip]="!isExpanded ? version : ''" matTooltipPosition="right">
|
|
31
|
+
@if (isExpanded) {
|
|
32
|
+
<span>{{ version }}</span>
|
|
33
|
+
}
|
|
34
|
+
@if (!isExpanded) {
|
|
35
|
+
<span class="drawer-version-dot"></span>
|
|
36
|
+
}
|
|
37
|
+
</div>
|
|
38
|
+
}
|
|
32
39
|
</div>
|
|
33
40
|
|
|
34
41
|
<!-- Floating Toggle Button on right border -->
|
|
35
42
|
<button
|
|
36
43
|
type="button"
|
|
37
44
|
class="toggle-btn"
|
|
45
|
+
[attr.aria-label]="isExpanded ? 'Collapse navigation' : 'Expand navigation'"
|
|
46
|
+
[attr.aria-expanded]="isExpanded"
|
|
38
47
|
(click)="toggleDrawer()"
|
|
39
48
|
[matTooltip]="isExpanded ? 'Collapse' : 'Expand'"
|
|
40
49
|
matTooltipClass="text-text-primary text-xs font-semibold"
|
|
41
50
|
matTooltipPosition="right"
|
|
42
|
-
|
|
51
|
+
>
|
|
43
52
|
<fx-ui-hero-icon
|
|
44
53
|
[icon]="isExpanded ? 'chevron-left' : 'chevron-right'"
|
|
45
54
|
[size]="16"
|
|
@@ -52,60 +61,68 @@
|
|
|
52
61
|
<ng-template #drawerItem let-item let-level="level">
|
|
53
62
|
<div class="drawer-item-wrapper">
|
|
54
63
|
<!-- Item Button/Link -->
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
@if (item.route && !item.children) {
|
|
65
|
+
<a
|
|
66
|
+
[routerLink]="item.route"
|
|
67
|
+
class="drawer-item"
|
|
68
|
+
[class.active]="isActiveRoute(item.route)"
|
|
69
|
+
[class.has-children]="item.children && item.children.length > 0"
|
|
70
|
+
[style.paddingLeft.px]="isExpanded ? 16 + level * 16 : 16"
|
|
71
|
+
[matTooltip]="!isExpanded ? item.label : ''"
|
|
72
|
+
matTooltipPosition="right"
|
|
73
|
+
matTooltipClass="text-text-primary text-xs font-semibold"
|
|
74
|
+
(click)="onItemClick(item, $event)"
|
|
75
|
+
>
|
|
76
|
+
<div class="item-content">
|
|
77
|
+
<fx-ui-hero-icon [icon]="item.icon" [size]="20" class="item-icon"></fx-ui-hero-icon>
|
|
78
|
+
@if (isExpanded) {
|
|
79
|
+
<span class="item-label">{{ item.label }}</span>
|
|
80
|
+
}
|
|
81
|
+
</div>
|
|
82
|
+
</a>
|
|
83
|
+
}
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
@if (!item.route || item.children) {
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
class="drawer-item"
|
|
89
|
+
[class.active]="isActiveRoute(item.route)"
|
|
90
|
+
[class.has-children]="item.children && item.children.length > 0"
|
|
91
|
+
[style.paddingLeft.px]="isExpanded ? 16 + level * 16 : 16"
|
|
92
|
+
[matTooltip]="!isExpanded ? item.label : ''"
|
|
93
|
+
matTooltipPosition="right"
|
|
94
|
+
matTooltipClass="text-text-primary text-sm font-semibold"
|
|
95
|
+
(click)="onItemClick(item, $event)"
|
|
96
|
+
>
|
|
97
|
+
<div class="item-content">
|
|
98
|
+
<fx-ui-hero-icon [icon]="item.icon" [size]="20" class="item-icon"></fx-ui-hero-icon>
|
|
99
|
+
@if (isExpanded) {
|
|
100
|
+
<span class="item-label">{{ item.label }}</span>
|
|
101
|
+
}
|
|
102
|
+
@if (isExpanded && item.children && item.children.length > 0) {
|
|
103
|
+
<fx-ui-hero-icon
|
|
104
|
+
[icon]="'chevron-down'"
|
|
105
|
+
[size]="16"
|
|
106
|
+
class="chevron-icon"
|
|
107
|
+
[class.rotated]="isAccordionExpanded(item.id)"
|
|
108
|
+
></fx-ui-hero-icon>
|
|
109
|
+
}
|
|
110
|
+
</div>
|
|
111
|
+
</button>
|
|
112
|
+
}
|
|
97
113
|
|
|
98
114
|
<!-- Accordion Children -->
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
@if (item.children && item.children.length > 0) {
|
|
116
|
+
<div
|
|
117
|
+
class="accordion-children"
|
|
118
|
+
[@accordionExpand]="getAccordionState(item.id)"
|
|
119
|
+
>
|
|
120
|
+
@for (child of item.children; track child) {
|
|
121
|
+
<ng-container
|
|
122
|
+
*ngTemplateOutlet="drawerItem; context: { $implicit: child, level: level + 1 }"
|
|
123
|
+
></ng-container>
|
|
124
|
+
}
|
|
125
|
+
</div>
|
|
126
|
+
}
|
|
110
127
|
</div>
|
|
111
128
|
</ng-template>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<div class="mb-4 text-left w-full text-text-primary" [class]="class">
|
|
2
2
|
@if (label) {
|
|
3
|
-
<label class="block txt-field-label">
|
|
3
|
+
<label class="block txt-field-label" [attr.for]="fieldId">
|
|
4
4
|
{{ label }}
|
|
5
5
|
@if (required) {
|
|
6
|
-
<span class="txt-required">*</span>
|
|
6
|
+
<span class="txt-required" aria-hidden="true">*</span>
|
|
7
7
|
}
|
|
8
8
|
@if (tooltip) {
|
|
9
9
|
<fx-ui-hero-icon
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
<input
|
|
23
23
|
appTrimOnBlur
|
|
24
24
|
[appTrimOnBlurCollapse]="true"
|
|
25
|
+
[id]="fieldId"
|
|
25
26
|
[type]="displayType"
|
|
26
27
|
[placeholder]="placeholder"
|
|
27
28
|
[disabled]="disabled"
|
|
@@ -30,6 +31,9 @@
|
|
|
30
31
|
(blur)="onBlur()"
|
|
31
32
|
(focus)="onFocus()"
|
|
32
33
|
[attr.maxlength]="maxlength"
|
|
34
|
+
[attr.aria-invalid]="invalid || null"
|
|
35
|
+
[attr.aria-describedby]="invalid ? fieldId + '-error' : null"
|
|
36
|
+
[attr.aria-required]="required ? true : null"
|
|
33
37
|
[class]="type === 'otp' ? 'input-otp' : 'input-default'"
|
|
34
38
|
[class.input-invalid]="invalid"
|
|
35
39
|
[class.pr-8]="type === 'password' || suffixIcon"
|
|
@@ -41,18 +45,26 @@
|
|
|
41
45
|
class="absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary"
|
|
42
46
|
(click)="onSuffixClick()"
|
|
43
47
|
[disabled]="disabled"
|
|
48
|
+
[attr.aria-label]="
|
|
49
|
+
type === 'password'
|
|
50
|
+
? (showPassword ? 'Hide password' : 'Show password')
|
|
51
|
+
: (suffixAriaLabel || null)
|
|
52
|
+
"
|
|
53
|
+
[attr.aria-pressed]="type === 'password' ? showPassword : null"
|
|
44
54
|
>
|
|
45
55
|
@if (type === 'password') {
|
|
46
56
|
<fx-ui-hero-icon
|
|
47
57
|
[icon]="showPassword ? 'eye-slash' : 'eye'"
|
|
48
58
|
[size]="18"
|
|
49
59
|
class="text-text-primary"
|
|
60
|
+
aria-hidden="true"
|
|
50
61
|
></fx-ui-hero-icon>
|
|
51
62
|
} @else if (suffixIcon) {
|
|
52
63
|
<fx-ui-hero-icon
|
|
53
64
|
[icon]="suffixIcon"
|
|
54
65
|
[size]="18"
|
|
55
66
|
class="text-text-primary"
|
|
67
|
+
aria-hidden="true"
|
|
56
68
|
></fx-ui-hero-icon>
|
|
57
69
|
}
|
|
58
70
|
</button>
|
|
@@ -60,7 +72,7 @@
|
|
|
60
72
|
</div>
|
|
61
73
|
|
|
62
74
|
@if (invalid) {
|
|
63
|
-
<div class="txt-invalid">
|
|
75
|
+
<div class="txt-invalid" role="alert" [id]="fieldId + '-error'">
|
|
64
76
|
{{ getErrorMessage(label) }}
|
|
65
77
|
</div>
|
|
66
78
|
}
|