@foxeltech/angular-ui 0.7.75 → 0.7.83
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 +26 -2
- package/fesm2022/foxeltech-angular-ui.mjs +85 -21
- package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/styles/components.css +26 -2
- package/src/lib/styles/utilities.css +6 -1
- package/src/lib/ui/components/pending-badge/pending-badge.component.html +2 -2
- package/src/lib/ui/components/project-card/project-card.component.html +7 -3
- package/src/lib/ui/components/sample-frame/sample-frame.component.html +6 -7
- package/src/lib/ui/components/section-card/section-card.component.html +3 -1
- package/src/lib/ui/components/segmented/segmented.component.html +3 -1
- package/types/foxeltech-angular-ui.d.ts +41 -7
- package/utilities.css +6 -1
package/package.json
CHANGED
|
@@ -339,14 +339,27 @@ input[type='password'] {
|
|
|
339
339
|
@apply font-mono text-sm text-text-primary tracking-normal;
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
/* ---- Nút: viền hairline + bóng mờ ở MỌI biến thể (anh Tú 04/09) --------
|
|
343
|
+
Demo `.og-button`: border `--og-rule`, hover `0 2px 7px rgba(0,0,0,.12)`;
|
|
344
|
+
`.bi-newbtn`: viền tối hơn nền 1 bậc + `0 1px 2px rgba(0,0,0,.15)`. Nút nền
|
|
345
|
+
đặc dùng viền inset tối 8% để không phải khai màu viền riêng cho gradient. */
|
|
342
346
|
.btn-default {
|
|
343
347
|
@apply btn-common bg-button-default hover:opacity-75 text-text-inverse;
|
|
348
|
+
box-shadow:
|
|
349
|
+
inset 0 0 0 1px rgb(0 0 0 / 0.08),
|
|
350
|
+
0 1px 2px rgb(0 0 0 / 0.12);
|
|
344
351
|
}
|
|
345
352
|
.btn-primary {
|
|
346
353
|
@apply btn-common bg-gradient-primary hover:opacity-75 text-text-focus;
|
|
354
|
+
box-shadow:
|
|
355
|
+
inset 0 0 0 1px rgb(0 0 0 / 0.08),
|
|
356
|
+
0 1px 2px rgb(0 0 0 / 0.12);
|
|
347
357
|
}
|
|
348
358
|
.btn-success {
|
|
349
359
|
@apply btn-common bg-gradient-success hover:opacity-75 text-surface;
|
|
360
|
+
box-shadow:
|
|
361
|
+
inset 0 0 0 1px rgb(0 0 0 / 0.08),
|
|
362
|
+
0 1px 2px rgb(0 0 0 / 0.12);
|
|
350
363
|
}
|
|
351
364
|
/* Nút hành động chính theo demo (`.bi-newbtn`): màu cố định, không phải accent. */
|
|
352
365
|
/* ---- Tông severity theo ĐÚNG mô hình demo (đo 03/09) -----------------
|
|
@@ -604,13 +617,24 @@ input[type='password'] {
|
|
|
604
617
|
}
|
|
605
618
|
|
|
606
619
|
.btn-cta {
|
|
607
|
-
@apply btn-common bg-cta hover:bg-cta-hover text-white;
|
|
620
|
+
@apply btn-common bg-cta hover:bg-cta-hover text-white border border-cta-hover;
|
|
608
621
|
box-shadow:
|
|
609
622
|
inset 0 1px 0 rgb(255 255 255 / 0.18),
|
|
610
623
|
0 1px 2px rgb(0 0 0 / 0.15);
|
|
611
624
|
}
|
|
625
|
+
.btn-cta:hover {
|
|
626
|
+
box-shadow:
|
|
627
|
+
inset 0 1px 0 rgb(255 255 255 / 0.18),
|
|
628
|
+
0 2px 6px rgb(0 0 0 / 0.2);
|
|
629
|
+
}
|
|
630
|
+
/* Outline = `.og-button--outline`: nền surface, viền --og-border, bóng 1px;
|
|
631
|
+
hover nổi lên 0 2px 7px như demo. */
|
|
612
632
|
.btn-alternative {
|
|
613
|
-
@apply btn-common hover:bg-bg-hover text-text-primary border border-border-default;
|
|
633
|
+
@apply btn-common bg-surface hover:bg-bg-hover text-text-primary border border-border-default;
|
|
634
|
+
box-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
|
|
635
|
+
}
|
|
636
|
+
.btn-alternative:hover {
|
|
637
|
+
box-shadow: 0 2px 7px rgb(0 0 0 / 0.12);
|
|
614
638
|
}
|
|
615
639
|
.btn-menu-top {
|
|
616
640
|
@apply btn-menu-common border-b border-border-default pt-small pb-semi;
|
|
@@ -31,7 +31,12 @@
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
@utility card-common {
|
|
34
|
-
@apply text-left px-xl py-large border border-border-default rounded-lg
|
|
34
|
+
@apply text-left px-xl py-large border border-border-default rounded-lg transition-colors duration-500 ease-in-out;
|
|
35
|
+
/* Demo `.og-panel` / `.og-statgrid`: hairline border + two soft layers.
|
|
36
|
+
Replaces Tailwind shadow-sm, which reads harder at the edges. */
|
|
37
|
+
box-shadow:
|
|
38
|
+
0 1px 2px rgb(0 0 0 / 0.04),
|
|
39
|
+
0 3px 8px rgb(0 0 0 / 0.05);
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
@utility tag-common {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
@if (variant() === 'dot') {
|
|
2
2
|
<span
|
|
3
|
-
class="inline-block w-[7px] h-[7px] rounded-full bg-
|
|
3
|
+
class="inline-block w-[7px] h-[7px] rounded-full bg-muted/60 align-middle"
|
|
4
4
|
[attr.title]="tooltip"
|
|
5
5
|
[attr.aria-label]="tooltip"
|
|
6
6
|
></span>
|
|
7
7
|
} @else {
|
|
8
8
|
<span
|
|
9
|
-
class="inline-flex items-center gap-xs px-small py-[1px] rounded-pill border border-
|
|
9
|
+
class="inline-flex items-center gap-xs px-small py-[1px] rounded-pill border border-border-subtle bg-neutral-soft text-muted txt-tag-label whitespace-nowrap align-middle"
|
|
10
10
|
[attr.title]="tooltip"
|
|
11
11
|
>
|
|
12
12
|
<fx-ui-hero-icon icon="clock" [size]="11" class="flex" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<button
|
|
2
2
|
type="button"
|
|
3
|
-
class="card-common bg-surface w-full flex flex-col gap-normal hover:border-accent hover:shadow-md transition-all"
|
|
3
|
+
class="card-common bg-surface w-full flex-1 flex flex-col gap-normal hover:border-accent hover:shadow-md transition-all"
|
|
4
4
|
(click)="open.emit(card())"
|
|
5
5
|
>
|
|
6
6
|
<!-- Header: avatar + name/sub + score chip (design 12) -->
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
>{{ card().avatar }}</span
|
|
14
14
|
>
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
<!-- min-h 46px = name (24) + mt-xs (2) + sub (20): reserves the two-line
|
|
17
|
+
header even when a card has no sub line, so trend/metric rows line
|
|
18
|
+
up across the grid row (measured 04/09). -->
|
|
19
|
+
<div class="min-w-0 min-h-[46px]">
|
|
17
20
|
<div class="txt-heading-06 truncate text-left">{{ card().name }}</div>
|
|
18
21
|
@if (card().sub; as sub) {
|
|
19
22
|
<div class="flex items-center gap-xs txt-utility-description mt-xs">
|
|
@@ -88,8 +91,9 @@
|
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
@if (card().meta || card().openLabel) {
|
|
94
|
+
<!-- mt-auto: footers line up across a grid row whatever each card holds -->
|
|
91
95
|
<div
|
|
92
|
-
class="flex items-center justify-between w-full pt-normal border-t border-border-default"
|
|
96
|
+
class="flex items-center justify-between w-full pt-normal mt-auto border-t border-border-default"
|
|
93
97
|
>
|
|
94
98
|
<span class="txt-utility-description text-left">{{ card().meta }}</span>
|
|
95
99
|
@if (card().openLabel) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
<div class="relative
|
|
2
|
-
<!--
|
|
3
|
-
|
|
4
|
-
<div class="flex flex-wrap items-center gap-small mb-
|
|
5
|
-
<span class="txt-tag-label text-warning uppercase tracking-wide whitespace-nowrap">{{ label() }}</span>
|
|
1
|
+
<div class="relative flex flex-col flex-1 min-h-0">
|
|
2
|
+
<!-- One quiet chip marks the block as sample data; the tooltip names the
|
|
3
|
+
missing field. flex-wrap so the chip drops a line in narrow rails. -->
|
|
4
|
+
<div class="flex flex-wrap items-center gap-small mb-small">
|
|
6
5
|
<span
|
|
7
|
-
class="inline-flex items-center gap-xs px-small py-[1px] rounded-pill border border-
|
|
6
|
+
class="inline-flex items-center gap-xs px-small py-[1px] rounded-pill border border-border-subtle bg-neutral-soft text-muted txt-tag-label whitespace-nowrap"
|
|
8
7
|
[attr.title]="tooltip"
|
|
9
8
|
>
|
|
10
9
|
<fx-ui-hero-icon icon="clock" [size]="11" class="flex" />
|
|
@@ -12,7 +11,7 @@
|
|
|
12
11
|
</span>
|
|
13
12
|
</div>
|
|
14
13
|
<!-- Sample content is non-interactive: nothing here reflects real state. -->
|
|
15
|
-
<div class="pointer-events-none select-none opacity-90">
|
|
14
|
+
<div class="pointer-events-none select-none opacity-90 flex-1 min-h-0 flex flex-col">
|
|
16
15
|
<ng-content></ng-content>
|
|
17
16
|
</div>
|
|
18
17
|
</div>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- flex column filling the host: cards in one grid row share a height and a
|
|
2
|
+
projected footer can sit at the bottom with `mt-auto` (design 01). -->
|
|
3
|
+
<div class="card-common bg-surface flex flex-col flex-1 min-h-0">
|
|
2
4
|
@if (titleStyle() === 'eyebrow') {
|
|
3
5
|
<div class="flex items-baseline justify-between gap-normal mb-large">
|
|
4
6
|
<span class="txt-eyebrow">{{ title() }}</span>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<div
|
|
2
|
-
class="relative inline-grid max-w-full grid-flow-col
|
|
2
|
+
class="relative inline-grid max-w-full grid-flow-col items-stretch p-[3px] bg-surface-sunken border border-border rounded-full"
|
|
3
|
+
[class]="gridClass()"
|
|
3
4
|
role="group"
|
|
4
5
|
[attr.aria-label]="ariaLabel()"
|
|
5
6
|
>
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
}
|
|
16
17
|
@for (option of options(); track option.value) {
|
|
17
18
|
<button
|
|
19
|
+
#opt
|
|
18
20
|
type="button"
|
|
19
21
|
(click)="select(option)"
|
|
20
22
|
[disabled]="option.disabled"
|
|
@@ -719,7 +719,23 @@ declare function toneBadge(tone: Tone | string | null | undefined): string;
|
|
|
719
719
|
* nơi cần nhấn mạnh nhất; đừng dùng cho badge có chấm bên trong (chấm sẽ chìm).
|
|
720
720
|
*/
|
|
721
721
|
declare function toneBadgeSolid(tone: Tone | string | null | undefined): string;
|
|
722
|
-
/**
|
|
722
|
+
/**
|
|
723
|
+
* Chỉ MÀU của tông, không kèm hình dạng pill — cho ô icon, chip, ô nền.
|
|
724
|
+
*
|
|
725
|
+
* ⚠️ Đây là họ SOLID: `tone-tint-critical` đặt `background: --og-text`
|
|
726
|
+
* (gần đen) VÀ `color: --og-surface` (trắng) trên CÙNG element.
|
|
727
|
+
*
|
|
728
|
+
* Hệ quả bắt buộc nhớ:
|
|
729
|
+
* 1. Chỉ dùng cho **badge một phần tử**. Với hộp nhiều dòng thì con sẽ cần màu
|
|
730
|
+
* chữ riêng, và nếu con ghi đè bằng `toneFg()` (đỏ sẫm) thì thành đỏ sẫm
|
|
731
|
+
* trên gần đen — không đọc được. Đã bị đúng vậy ở hộp RISK của
|
|
732
|
+
* `info-binary-detail` (anh Tú 03/09).
|
|
733
|
+
* 2. **KHÔNG** đặt `toneFg()` lên phần tử con của một phần tử đã có
|
|
734
|
+
* `toneTint()` — nó phá màu tương phản mà `toneTint` vừa đặt.
|
|
735
|
+
*
|
|
736
|
+
* Hộp/khối nhiều dòng thì dùng `toneScale()`: nền nhạt (color-mix 12%) + chữ
|
|
737
|
+
* đúng tông, con chỉ cần THỪA HƯỞNG.
|
|
738
|
+
*/
|
|
723
739
|
declare function toneTint(tone: Tone | string | null | undefined): string;
|
|
724
740
|
/**
|
|
725
741
|
* CHỮ theo tông — dùng thành viên dễ đọc nhất của mỗi họ severity, KHÔNG phải
|
|
@@ -1837,6 +1853,10 @@ declare class MeterComponent {
|
|
|
1837
1853
|
* - titleStyle 'heading' (default): icon + heading, the pre-V2 look
|
|
1838
1854
|
* - titleStyle 'eyebrow': the V2 design's panel header — small tracked
|
|
1839
1855
|
* caption on the left, muted [aside] text on the right
|
|
1856
|
+
*
|
|
1857
|
+
* The host is a flex column (not the custom element's inline default) so that
|
|
1858
|
+
* in a CSS grid row every card stretches to the row height and panels line up
|
|
1859
|
+
* (anh Tú 04/09); a footer projected with `mt-auto` sticks to the bottom.
|
|
1840
1860
|
*/
|
|
1841
1861
|
declare class SectionCardComponent {
|
|
1842
1862
|
/** Caller-translated heading. */
|
|
@@ -2023,6 +2043,7 @@ declare class ThemePickerComponent {
|
|
|
2023
2043
|
* <fx-ui-pending-badge [field]="'confirmed-chains weekly series'" />
|
|
2024
2044
|
*
|
|
2025
2045
|
* Keep the copy factual: it states that data is missing, never fakes a value.
|
|
2046
|
+
* Styling is neutral on purpose (anh Tú 04/09): a hint, not a warning.
|
|
2026
2047
|
*/
|
|
2027
2048
|
declare class PendingBadgeComponent {
|
|
2028
2049
|
/** Caller-translated chip text. */
|
|
@@ -2044,10 +2065,11 @@ declare class PendingBadgeComponent {
|
|
|
2044
2065
|
* reviewed before its endpoint exists (anh Tú 29/08: "muốn có full giao diện
|
|
2045
2066
|
* trước để review").
|
|
2046
2067
|
*
|
|
2047
|
-
* The
|
|
2048
|
-
*
|
|
2049
|
-
*
|
|
2050
|
-
*
|
|
2068
|
+
* The marker is deliberately quiet (anh Tú 04/09: no yellow frame, just a
|
|
2069
|
+
* subtle "Pending API" chip): a neutral chip above the block, with the label
|
|
2070
|
+
* and missing field in its tooltip. The content itself is non-interactive so
|
|
2071
|
+
* sample shapes are never mistaken for live state. Drop the wrapper (and the
|
|
2072
|
+
* sample input) the moment the API lands.
|
|
2051
2073
|
*
|
|
2052
2074
|
* The pending chip is inlined rather than composed from fx-ui-pending-badge:
|
|
2053
2075
|
* one lib component importing another inside the same barrel tripped NG0919
|
|
@@ -2058,7 +2080,7 @@ declare class PendingBadgeComponent {
|
|
|
2058
2080
|
* </fx-ui-sample-frame>
|
|
2059
2081
|
*/
|
|
2060
2082
|
declare class SampleFrameComponent {
|
|
2061
|
-
/** Caller-translated
|
|
2083
|
+
/** Caller-translated block label — surfaces in the chip tooltip only. */
|
|
2062
2084
|
readonly label: _angular_core.InputSignal<string>;
|
|
2063
2085
|
/** Field the backend still owes — shown on the pending badge tooltip. */
|
|
2064
2086
|
readonly field: _angular_core.InputSignal<string>;
|
|
@@ -2156,17 +2178,29 @@ declare class SegmentedComponent {
|
|
|
2156
2178
|
* (design refs 04/05).
|
|
2157
2179
|
*/
|
|
2158
2180
|
readonly variant: _angular_core.InputSignal<"default" | "pill">;
|
|
2181
|
+
/**
|
|
2182
|
+
* 'equal' (default): every segment takes the same width (view toggles such
|
|
2183
|
+
* as Catalog | Table). 'content': each segment is as wide as its label —
|
|
2184
|
+
* the demo's detail-pane tabs ("Affected software · Summary · Evidence"),
|
|
2185
|
+
* where equal columns would truncate the long label in a narrow pane.
|
|
2186
|
+
*/
|
|
2187
|
+
readonly fit: _angular_core.InputSignal<"equal" | "content">;
|
|
2188
|
+
private readonly optionEls;
|
|
2189
|
+
/** Measured box of the active segment — only used when fit === 'content'. */
|
|
2190
|
+
private readonly thumbBox;
|
|
2191
|
+
constructor();
|
|
2159
2192
|
/** Index the sliding thumb sits on; -1 hides it (no/unknown selection). */
|
|
2160
2193
|
readonly activeIndex: _angular_core.Signal<number>;
|
|
2161
2194
|
/** Equal-width tracks → the thumb is 1/n of the inner width and slides by
|
|
2162
2195
|
* multiples of its own width. Pure CSS transform, nothing to measure. */
|
|
2163
2196
|
readonly thumbWidth: _angular_core.Signal<string>;
|
|
2164
2197
|
readonly thumbTransform: _angular_core.Signal<string>;
|
|
2198
|
+
readonly gridClass: _angular_core.Signal<"auto-cols-auto" | "auto-cols-fr">;
|
|
2165
2199
|
select(option: FxSegmentedOption): void;
|
|
2166
2200
|
thumbClass(): string;
|
|
2167
2201
|
optionClass(option: FxSegmentedOption): string;
|
|
2168
2202
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SegmentedComponent, never>;
|
|
2169
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SegmentedComponent, "fx-ui-segmented", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
|
|
2203
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SegmentedComponent, "fx-ui-segmented", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "fit": { "alias": "fit"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
|
|
2170
2204
|
}
|
|
2171
2205
|
|
|
2172
2206
|
interface FxListRowBadge {
|
package/utilities.css
CHANGED
|
@@ -31,7 +31,12 @@
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
@utility card-common {
|
|
34
|
-
@apply text-left px-xl py-large border border-border-default rounded-lg
|
|
34
|
+
@apply text-left px-xl py-large border border-border-default rounded-lg transition-colors duration-500 ease-in-out;
|
|
35
|
+
/* Demo `.og-panel` / `.og-statgrid`: hairline border + two soft layers.
|
|
36
|
+
Replaces Tailwind shadow-sm, which reads harder at the edges. */
|
|
37
|
+
box-shadow:
|
|
38
|
+
0 1px 2px rgb(0 0 0 / 0.04),
|
|
39
|
+
0 3px 8px rgb(0 0 0 / 0.05);
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
@utility tag-common {
|