@foxeltech/angular-ui 0.7.124 → 0.7.133
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 +5 -4
- package/fesm2022/foxeltech-angular-ui-flow.mjs +42 -11
- package/fesm2022/foxeltech-angular-ui-flow.mjs.map +1 -1
- package/fesm2022/foxeltech-angular-ui.mjs +33 -11
- package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/styles/components.css +5 -4
- package/src/lib/styles/utilities.css +9 -4
- package/src/lib/ui/components/button/button.component.html +37 -225
- package/src/lib/ui/components/button/button.component.scss +30 -0
- package/src/lib/ui/components/drawer/drawer.component.html +4 -4
- package/src/lib/ui/components/drawer/drawer.component.scss +26 -19
- package/src/lib/ui/components/master-detail/master-detail.component.css +36 -8
- package/src/lib/ui/components/master-detail/master-detail.component.html +4 -2
- package/src/lib/ui/components/stat-cards/stat-cards.component.css +18 -0
- package/types/foxeltech-angular-ui-flow.d.ts +24 -2
- package/types/foxeltech-angular-ui.d.ts +22 -3
- package/utilities.css +9 -4
package/package.json
CHANGED
|
@@ -361,11 +361,12 @@ input[type='password'] {
|
|
|
361
361
|
inset 0 0 0 1px rgb(0 0 0 / 0.08),
|
|
362
362
|
0 1px 2px rgb(0 0 0 / 0.12);
|
|
363
363
|
}
|
|
364
|
-
/* Icon-only button (no label): square
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
/* Icon-only button (no label): square, no side padding, so the glyph sits
|
|
365
|
+
dead-centre (anh Tú 04/09). The width tracks `btn-common`'s height — 27px
|
|
366
|
+
since 17/09 — so it stays square; consumers may still size it with
|
|
367
|
+
utilities (`w-9 h-9`), which win in the utilities layer. */
|
|
367
368
|
.fx-btn--icon {
|
|
368
|
-
width:
|
|
369
|
+
width: 27px;
|
|
369
370
|
padding-left: 0;
|
|
370
371
|
padding-right: 0;
|
|
371
372
|
}
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
@utility btn-common {
|
|
11
|
-
/*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
/* The demo's `.og-button--sm` (anh Tú 17/09: compact như New Analysis) —
|
|
12
|
+
27px tall, 11.5px at weight 400, 12px sides, 7px radius. MEASURED off the
|
|
13
|
+
reference's own `.og-button--sm.bi-newbtn`, which is the topbar CTA: it
|
|
14
|
+
wore this shape through `!` overrides because the shared button was a size
|
|
15
|
+
above it. The h-8 / 13px / 600 this replaces was itself a step down from
|
|
16
|
+
pre-V2 h-10 (29/08); against V2 panel density it still read one notch
|
|
17
|
+
heavy. leading-none: an arbitrary font-size inherits lh 1.5 and Archivo's
|
|
18
|
+
uneven leading floats the ink ~1px above centre. */
|
|
19
|
+
@apply flex text-[length:var(--og-fs-xs)] leading-none h-[27px] font-normal px-[12px] py-[4px] items-center justify-center rounded-control shadow-sm transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
@utility btn-menu-common {
|
|
@@ -14,229 +14,41 @@
|
|
|
14
14
|
[disabled]="disabled() || loading()"
|
|
15
15
|
(click)="onClick()"
|
|
16
16
|
>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
fill="freeze"
|
|
55
|
-
/>
|
|
56
|
-
<animate
|
|
57
|
-
id="spinner_lo66"
|
|
58
|
-
begin="spinner_Aguh.end"
|
|
59
|
-
attributeName="r"
|
|
60
|
-
calcMode="spline"
|
|
61
|
-
dur="0.5s"
|
|
62
|
-
keySplines=".36,.6,.31,1"
|
|
63
|
-
values="3;0"
|
|
64
|
-
fill="freeze"
|
|
65
|
-
/>
|
|
66
|
-
<animate
|
|
67
|
-
id="spinner_z0Or"
|
|
68
|
-
begin="spinner_lo66.end"
|
|
69
|
-
attributeName="cx"
|
|
70
|
-
dur="0.001s"
|
|
71
|
-
values="20;4"
|
|
72
|
-
fill="freeze"
|
|
73
|
-
/>
|
|
74
|
-
</circle>
|
|
75
|
-
<circle cx="4" cy="12" r="3">
|
|
76
|
-
<animate
|
|
77
|
-
begin="0;spinner_z0Or.end"
|
|
78
|
-
attributeName="cx"
|
|
79
|
-
calcMode="spline"
|
|
80
|
-
dur="0.5s"
|
|
81
|
-
keySplines=".36,.6,.31,1"
|
|
82
|
-
values="4;12"
|
|
83
|
-
fill="freeze"
|
|
84
|
-
/>
|
|
85
|
-
<animate
|
|
86
|
-
begin="spinner_OLMs.end"
|
|
87
|
-
attributeName="cx"
|
|
88
|
-
calcMode="spline"
|
|
89
|
-
dur="0.5s"
|
|
90
|
-
keySplines=".36,.6,.31,1"
|
|
91
|
-
values="12;20"
|
|
92
|
-
fill="freeze"
|
|
93
|
-
/>
|
|
94
|
-
<animate
|
|
95
|
-
id="spinner_JsnR"
|
|
96
|
-
begin="spinner_UHR2.end"
|
|
97
|
-
attributeName="r"
|
|
98
|
-
calcMode="spline"
|
|
99
|
-
dur="0.5s"
|
|
100
|
-
keySplines=".36,.6,.31,1"
|
|
101
|
-
values="3;0"
|
|
102
|
-
fill="freeze"
|
|
103
|
-
/>
|
|
104
|
-
<animate
|
|
105
|
-
id="spinner_Aguh"
|
|
106
|
-
begin="spinner_JsnR.end"
|
|
107
|
-
attributeName="cx"
|
|
108
|
-
dur="0.001s"
|
|
109
|
-
values="20;4"
|
|
110
|
-
fill="freeze"
|
|
111
|
-
/>
|
|
112
|
-
<animate
|
|
113
|
-
begin="spinner_Aguh.end"
|
|
114
|
-
attributeName="r"
|
|
115
|
-
calcMode="spline"
|
|
116
|
-
dur="0.5s"
|
|
117
|
-
keySplines=".36,.6,.31,1"
|
|
118
|
-
values="0;3"
|
|
119
|
-
fill="freeze"
|
|
120
|
-
/>
|
|
121
|
-
</circle>
|
|
122
|
-
<circle cx="12" cy="12" r="3">
|
|
123
|
-
<animate
|
|
124
|
-
begin="0;spinner_z0Or.end"
|
|
125
|
-
attributeName="cx"
|
|
126
|
-
calcMode="spline"
|
|
127
|
-
dur="0.5s"
|
|
128
|
-
keySplines=".36,.6,.31,1"
|
|
129
|
-
values="12;20"
|
|
130
|
-
fill="freeze"
|
|
131
|
-
/>
|
|
132
|
-
<animate
|
|
133
|
-
id="spinner_hSjk"
|
|
134
|
-
begin="spinner_OLMs.end"
|
|
135
|
-
attributeName="r"
|
|
136
|
-
calcMode="spline"
|
|
137
|
-
dur="0.5s"
|
|
138
|
-
keySplines=".36,.6,.31,1"
|
|
139
|
-
values="3;0"
|
|
140
|
-
fill="freeze"
|
|
141
|
-
/>
|
|
142
|
-
<animate
|
|
143
|
-
id="spinner_UHR2"
|
|
144
|
-
begin="spinner_hSjk.end"
|
|
145
|
-
attributeName="cx"
|
|
146
|
-
dur="0.001s"
|
|
147
|
-
values="20;4"
|
|
148
|
-
fill="freeze"
|
|
149
|
-
/>
|
|
150
|
-
<animate
|
|
151
|
-
begin="spinner_UHR2.end"
|
|
152
|
-
attributeName="r"
|
|
153
|
-
calcMode="spline"
|
|
154
|
-
dur="0.5s"
|
|
155
|
-
keySplines=".36,.6,.31,1"
|
|
156
|
-
values="0;3"
|
|
157
|
-
fill="freeze"
|
|
158
|
-
/>
|
|
159
|
-
<animate
|
|
160
|
-
begin="spinner_Aguh.end"
|
|
161
|
-
attributeName="cx"
|
|
162
|
-
calcMode="spline"
|
|
163
|
-
dur="0.5s"
|
|
164
|
-
keySplines=".36,.6,.31,1"
|
|
165
|
-
values="4;12"
|
|
166
|
-
fill="freeze"
|
|
167
|
-
/>
|
|
168
|
-
</circle>
|
|
169
|
-
<circle cx="20" cy="12" r="3">
|
|
170
|
-
<animate
|
|
171
|
-
id="spinner_4v5M"
|
|
172
|
-
begin="0;spinner_z0Or.end"
|
|
173
|
-
attributeName="r"
|
|
174
|
-
calcMode="spline"
|
|
175
|
-
dur="0.5s"
|
|
176
|
-
keySplines=".36,.6,.31,1"
|
|
177
|
-
values="3;0"
|
|
178
|
-
fill="freeze"
|
|
179
|
-
/>
|
|
180
|
-
<animate
|
|
181
|
-
id="spinner_OLMs"
|
|
182
|
-
begin="spinner_4v5M.end"
|
|
183
|
-
attributeName="cx"
|
|
184
|
-
dur="0.001s"
|
|
185
|
-
values="20;4"
|
|
186
|
-
fill="freeze"
|
|
187
|
-
/>
|
|
188
|
-
<animate
|
|
189
|
-
begin="spinner_OLMs.end"
|
|
190
|
-
attributeName="r"
|
|
191
|
-
calcMode="spline"
|
|
192
|
-
dur="0.5s"
|
|
193
|
-
keySplines=".36,.6,.31,1"
|
|
194
|
-
values="0;3"
|
|
195
|
-
fill="freeze"
|
|
196
|
-
/>
|
|
197
|
-
<animate
|
|
198
|
-
begin="spinner_UHR2.end"
|
|
199
|
-
attributeName="cx"
|
|
200
|
-
calcMode="spline"
|
|
201
|
-
dur="0.5s"
|
|
202
|
-
keySplines=".36,.6,.31,1"
|
|
203
|
-
values="4;12"
|
|
204
|
-
fill="freeze"
|
|
205
|
-
/>
|
|
206
|
-
<animate
|
|
207
|
-
begin="spinner_Aguh.end"
|
|
208
|
-
attributeName="cx"
|
|
209
|
-
calcMode="spline"
|
|
210
|
-
dur="0.5s"
|
|
211
|
-
keySplines=".36,.6,.31,1"
|
|
212
|
-
values="12;20"
|
|
213
|
-
fill="freeze"
|
|
214
|
-
/>
|
|
215
|
-
</circle>
|
|
216
|
-
</svg>
|
|
217
|
-
</div>
|
|
218
|
-
} @else {
|
|
219
|
-
<ng-container>
|
|
220
|
-
<div class="flex items-center justify-center gap-small">
|
|
221
|
-
@if (icon) {
|
|
222
|
-
<fx-ui-hero-icon
|
|
223
|
-
[icon]="icon"
|
|
224
|
-
[size]="16"
|
|
225
|
-
class="flex"
|
|
226
|
-
[ngClass]="{
|
|
227
|
-
'text-text-inverse': buttonVariant() === 'default',
|
|
228
|
-
'text-text-primary': buttonVariant() === 'alternative',
|
|
229
|
-
'text-accent-contrast': buttonVariant() === 'primary',
|
|
230
|
-
'text-surface': buttonVariant() === 'success',
|
|
231
|
-
'text-white': buttonVariant() === 'cta',
|
|
232
|
-
}"
|
|
233
|
-
></fx-ui-hero-icon>
|
|
234
|
-
}
|
|
235
|
-
@if (label) {
|
|
236
|
-
<!-- text-box: optical centering — Archivo's leading floats ink up otherwise -->
|
|
237
|
-
<div class="[text-box:trim-both_cap_alphabetic]">{{ label }}</div>
|
|
238
|
-
}
|
|
239
|
-
</div>
|
|
240
|
-
</ng-container>
|
|
241
|
-
}
|
|
17
|
+
<!-- Loading keeps the LABEL, as the sign-in button does: a button that
|
|
18
|
+
swaps its text for a spinner collapses to a stub, and the row jumps.
|
|
19
|
+
The ring is that button's `.li-spin`, in the foreground the variant
|
|
20
|
+
already uses for its icon so it reads on a coloured ground. -->
|
|
21
|
+
<!-- 6px, the reference button's own gap; `gap-small` is 4px. -->
|
|
22
|
+
<div class="flex items-center justify-center gap-[6px]">
|
|
23
|
+
@if (loading()) {
|
|
24
|
+
<span
|
|
25
|
+
class="fx-btn__spin"
|
|
26
|
+
aria-hidden="true"
|
|
27
|
+
[ngClass]="{
|
|
28
|
+
'text-text-inverse': buttonVariant() === 'default',
|
|
29
|
+
'text-text-primary': buttonVariant() === 'alternative',
|
|
30
|
+
'text-accent-contrast': buttonVariant() === 'primary',
|
|
31
|
+
'text-surface': buttonVariant() === 'success',
|
|
32
|
+
'text-white': buttonVariant() === 'cta',
|
|
33
|
+
}"
|
|
34
|
+
></span>
|
|
35
|
+
} @else if (icon) {
|
|
36
|
+
<fx-ui-hero-icon
|
|
37
|
+
[icon]="icon"
|
|
38
|
+
[size]="13"
|
|
39
|
+
class="flex"
|
|
40
|
+
[ngClass]="{
|
|
41
|
+
'text-text-inverse': buttonVariant() === 'default',
|
|
42
|
+
'text-text-primary': buttonVariant() === 'alternative',
|
|
43
|
+
'text-accent-contrast': buttonVariant() === 'primary',
|
|
44
|
+
'text-surface': buttonVariant() === 'success',
|
|
45
|
+
'text-white': buttonVariant() === 'cta',
|
|
46
|
+
}"
|
|
47
|
+
></fx-ui-hero-icon>
|
|
48
|
+
}
|
|
49
|
+
@if (label) {
|
|
50
|
+
<!-- text-box: optical centering — Archivo's leading floats ink up otherwise -->
|
|
51
|
+
<div class="[text-box:trim-both_cap_alphabetic]">{{ label }}</div>
|
|
52
|
+
}
|
|
53
|
+
</div>
|
|
242
54
|
</button>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/* Loading ring — the sign-in button's `.li-spin`, sized to sit beside a label.
|
|
4
|
+
`currentColor` so the variant's own foreground class colours it. */
|
|
5
|
+
.fx-btn__spin {
|
|
6
|
+
display: inline-block;
|
|
7
|
+
/* 12px, matching the button's 13px icon — 14 crowded the 27px box. */
|
|
8
|
+
width: 12px;
|
|
9
|
+
height: 12px;
|
|
10
|
+
flex: 0 0 auto;
|
|
11
|
+
border-radius: 50%;
|
|
12
|
+
border: 2px solid currentColor;
|
|
13
|
+
border-top-color: transparent;
|
|
14
|
+
opacity: 0.9;
|
|
15
|
+
animation: fx-btn-spin 0.8s linear infinite;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes fx-btn-spin {
|
|
19
|
+
to {
|
|
20
|
+
transform: rotate(360deg);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* A ring that has stopped still marks the button as busy, and `aria-busy`
|
|
25
|
+
carries the state regardless. */
|
|
26
|
+
@media (prefers-reduced-motion: reduce) {
|
|
27
|
+
.fx-btn__spin {
|
|
28
|
+
animation: none;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -105,12 +105,12 @@
|
|
|
105
105
|
@if (item.iconData) {
|
|
106
106
|
<lucide-icon
|
|
107
107
|
[img]="item.iconData"
|
|
108
|
-
[size]="isExpanded ?
|
|
108
|
+
[size]="isExpanded ? 15 : 20"
|
|
109
109
|
class="item-icon"
|
|
110
110
|
[class.item-icon--solid]="item.iconSolid"
|
|
111
111
|
/>
|
|
112
112
|
} @else {
|
|
113
|
-
<fx-ui-hero-icon [icon]="item.icon" [size]="isExpanded ?
|
|
113
|
+
<fx-ui-hero-icon [icon]="item.icon" [size]="isExpanded ? 15 : 20" class="item-icon"></fx-ui-hero-icon>
|
|
114
114
|
}
|
|
115
115
|
@if (isExpanded) {
|
|
116
116
|
<span class="item-label">{{ item.label }}</span>
|
|
@@ -136,12 +136,12 @@
|
|
|
136
136
|
@if (item.iconData) {
|
|
137
137
|
<lucide-icon
|
|
138
138
|
[img]="item.iconData"
|
|
139
|
-
[size]="isExpanded ?
|
|
139
|
+
[size]="isExpanded ? 15 : 20"
|
|
140
140
|
class="item-icon"
|
|
141
141
|
[class.item-icon--solid]="item.iconSolid"
|
|
142
142
|
/>
|
|
143
143
|
} @else {
|
|
144
|
-
<fx-ui-hero-icon [icon]="item.icon" [size]="isExpanded ?
|
|
144
|
+
<fx-ui-hero-icon [icon]="item.icon" [size]="isExpanded ? 15 : 20" class="item-icon"></fx-ui-hero-icon>
|
|
145
145
|
}
|
|
146
146
|
@if (isExpanded) {
|
|
147
147
|
<span class="item-label">{{ item.label }}</span>
|
|
@@ -184,7 +184,9 @@
|
|
|
184
184
|
.drawer-section-label {
|
|
185
185
|
/* Demo (.og-sidebar__section sau override): 10.5px, text-faint, giãn chữ
|
|
186
186
|
.08em và KHÔNG viết hoa — override đặt `text-transform: none`. */
|
|
187
|
-
|
|
187
|
+
/* 11.5px since 17/09, up from the demo's 10.5 — it moves with the rows it
|
|
188
|
+
captions, or the caption reads as a different scale from the nav. */
|
|
189
|
+
@apply px-large pt-4 pb-1 text-[length:var(--og-fs-xs)]/[1.4] font-semibold text-faint select-none;
|
|
188
190
|
letter-spacing: var(--og-tracking-label);
|
|
189
191
|
}
|
|
190
192
|
|
|
@@ -204,9 +206,13 @@
|
|
|
204
206
|
8px in, not 16. */
|
|
205
207
|
@apply flex items-center w-full border-none bg-transparent cursor-pointer no-underline relative;
|
|
206
208
|
margin-top: 1px;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
/* +12% on the reference's 12.5px/30px row (anh Tú 17/09: bump the font size
|
|
210
|
+
of the sidebar a bit like 10-20%). The row has to grow with the text or
|
|
211
|
+
the 20px leading exceeds `min-height` and the declared height stops being
|
|
212
|
+
the shipped one — the mistake the 0.7.107 note below records. */
|
|
213
|
+
min-height: 34px;
|
|
214
|
+
padding: 7px 8px;
|
|
215
|
+
font-size: 14px;
|
|
210
216
|
color: rgb(var(--og-text-body));
|
|
211
217
|
border-radius: var(--og-radius-control);
|
|
212
218
|
transition: background-color 130ms ease, color 130ms ease;
|
|
@@ -268,8 +274,8 @@
|
|
|
268
274
|
carries the row on its own and goes back up to 18px (anh Tú 16/09). Expanded
|
|
269
275
|
it stays at the reference's 13px mark. */
|
|
270
276
|
.drawer-item.is-collapsed .item-icon {
|
|
271
|
-
width:
|
|
272
|
-
height:
|
|
277
|
+
width: 20px;
|
|
278
|
+
height: 20px;
|
|
273
279
|
margin-right: 0;
|
|
274
280
|
}
|
|
275
281
|
|
|
@@ -297,21 +303,22 @@
|
|
|
297
303
|
.item-icon {
|
|
298
304
|
@apply flex-shrink-0;
|
|
299
305
|
color: inherit;
|
|
300
|
-
/* The reference's mark is a 12.5px glyph
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
306
|
+
/* The reference's mark is a 12.5px glyph in an 18px slot — icon 13px plus
|
|
307
|
+
5px to restore the slot, i.e. 13 + 5 + the container's 9px gap = 27px from
|
|
308
|
+
the icon's left edge to the label. Scaled with the 14px label (17/09):
|
|
309
|
+
15px + 6px keeps the same proportion at 30px. Sizing the icon itself 18px
|
|
310
|
+
drew it half again too large. */
|
|
311
|
+
width: 15px;
|
|
312
|
+
height: 15px;
|
|
313
|
+
margin-right: 6px;
|
|
307
314
|
}
|
|
308
315
|
|
|
309
316
|
.item-label {
|
|
310
|
-
/* Size comes from .drawer-item (
|
|
311
|
-
Leading
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
@apply flex-1 whitespace-nowrap overflow-hidden text-ellipsis leading-[
|
|
317
|
+
/* Size comes from .drawer-item (14px since 17/09) — no second scale here.
|
|
318
|
+
Leading tracks it: 20px, and the row's 7px padding either side makes 34,
|
|
319
|
+
which is what `min-height` now declares. Raising one without the other is
|
|
320
|
+
how the row's stated height stopped matching the shipped one before. */
|
|
321
|
+
@apply flex-1 whitespace-nowrap overflow-hidden text-ellipsis leading-[20px] text-left;
|
|
315
322
|
font-size: inherit;
|
|
316
323
|
}
|
|
317
324
|
|
|
@@ -329,7 +336,7 @@
|
|
|
329
336
|
}
|
|
330
337
|
|
|
331
338
|
.drawer-version {
|
|
332
|
-
@apply flex items-center justify-center py-3 px-4 text-text-placeholder text-[
|
|
339
|
+
@apply flex items-center justify-center py-3 px-4 text-text-placeholder text-[12px] border-t border-border-default select-none;
|
|
333
340
|
font-family: var(--og-font-mono);
|
|
334
341
|
}
|
|
335
342
|
|
|
@@ -73,9 +73,21 @@
|
|
|
73
73
|
min-height: 420px;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/*
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
/* Track widths are the DESIGN'S own: every three-column screen in the demo
|
|
77
|
+
bundle is `204px minmax(0,1fr) 340px` — measured on Results, License and
|
|
78
|
+
Relationships, 17/09. So the rail and the DETAIL are the fixed tracks and
|
|
79
|
+
the LIST takes what is left.
|
|
80
|
+
|
|
81
|
+
It used to be the other way round: the list was capped at 420px and the
|
|
82
|
+
detail was `flex: 1 1 360px`, so every pixel of a wide window went to the
|
|
83
|
+
reading pane — ~930px of it at 1920, against a list that never grew past
|
|
84
|
+
420 (anh Tú 17/09: kéo rộng cột list ra và thu nhỏ cột detail lại).
|
|
85
|
+
|
|
86
|
+
All three still SHRINK below their track (`flex-basis` + a floor, not a
|
|
87
|
+
hard width): with the rail open the three floors add up past the 720px
|
|
88
|
+
where this query starts, and a pane that refuses to give way would push
|
|
89
|
+
the frame into a horizontal scrollbar instead. Collapsed, the rail is
|
|
90
|
+
40px and 40 + 260 + 300 fits. */
|
|
79
91
|
/* Hairline to the RIGHT of rail and list instead of below. */
|
|
80
92
|
.fx-md-facets,
|
|
81
93
|
.fx-md-list {
|
|
@@ -91,10 +103,10 @@
|
|
|
91
103
|
max-width: 40px;
|
|
92
104
|
}
|
|
93
105
|
.fx-md-facets--open {
|
|
94
|
-
flex: 0 1
|
|
106
|
+
flex: 0 1 204px;
|
|
95
107
|
width: auto;
|
|
96
|
-
min-width:
|
|
97
|
-
max-width:
|
|
108
|
+
min-width: 180px;
|
|
109
|
+
max-width: 204px;
|
|
98
110
|
}
|
|
99
111
|
.fx-md-rail-toggle {
|
|
100
112
|
height: 100%;
|
|
@@ -109,14 +121,30 @@
|
|
|
109
121
|
|
|
110
122
|
.fx-md-list {
|
|
111
123
|
width: auto;
|
|
112
|
-
flex:
|
|
124
|
+
flex: 1 1 0;
|
|
113
125
|
min-width: 260px;
|
|
114
|
-
|
|
126
|
+
/* Uncapped unless a caller asks for one — see the `listWidth` input. */
|
|
127
|
+
max-width: var(--fx-md-list-width, none);
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
.fx-md-detail {
|
|
131
|
+
width: auto;
|
|
132
|
+
flex: 0 1 var(--fx-md-detail-width, 340px);
|
|
133
|
+
min-width: 300px;
|
|
134
|
+
max-width: var(--fx-md-detail-width, 340px);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* `detailWidth="auto"`: the pre-0.7.130 split, kept for the panes that need
|
|
138
|
+
the room (Binaries, Remediation — anh Tú 17/09). The detail takes the
|
|
139
|
+
spare width and the LIST goes back to being the capped one. */
|
|
140
|
+
.fx-md-row--detail-fluid .fx-md-list {
|
|
141
|
+
flex: 0 1 var(--fx-md-list-width, 420px);
|
|
142
|
+
max-width: var(--fx-md-list-width, 420px);
|
|
143
|
+
}
|
|
144
|
+
.fx-md-row--detail-fluid .fx-md-detail {
|
|
118
145
|
flex: 1 1 360px;
|
|
119
146
|
min-width: 360px;
|
|
147
|
+
max-width: none;
|
|
120
148
|
}
|
|
121
149
|
}
|
|
122
150
|
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
class="fx-md-row flex flex-col items-stretch"
|
|
7
7
|
[class.fx-md-row--rail-open]="railOpen()"
|
|
8
8
|
[style.--fx-md-list-width]="listWidth()"
|
|
9
|
+
[class.fx-md-row--detail-fluid]="detailFluid()"
|
|
10
|
+
[style.--fx-md-detail-width]="detailFluid() ? null : detailWidth()"
|
|
9
11
|
[style.--fx-md-pane-h]="paneHeight()"
|
|
10
12
|
>
|
|
11
13
|
<!-- Facets rail — collapsible (» / «), collapsed = 40px strip with the label
|
|
@@ -108,7 +110,7 @@
|
|
|
108
110
|
<div class="flex gap-small">
|
|
109
111
|
<button
|
|
110
112
|
type="button"
|
|
111
|
-
class="btn-alternative
|
|
113
|
+
class="btn-alternative"
|
|
112
114
|
[disabled]="!paginator().hasPreviousPage()"
|
|
113
115
|
(click)="paginator().previousPage()"
|
|
114
116
|
>
|
|
@@ -116,7 +118,7 @@
|
|
|
116
118
|
</button>
|
|
117
119
|
<button
|
|
118
120
|
type="button"
|
|
119
|
-
class="btn-alternative
|
|
121
|
+
class="btn-alternative"
|
|
120
122
|
[disabled]="!paginator().hasNextPage()"
|
|
121
123
|
(click)="paginator().nextPage()"
|
|
122
124
|
>
|
|
@@ -58,6 +58,24 @@
|
|
|
58
58
|
margin-top: 6px;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/* The figure follows the BOX, not just the grid (anh Tú 18/09: show all the
|
|
62
|
+
stats numbers much smaller on the mobile screen). The 32px reference figure
|
|
63
|
+
is sized for a quarter of a desktop page; on a phone the strip is already
|
|
64
|
+
2-up, so each cell is ~175px and a six-digit count like 52,311 filled it
|
|
65
|
+
edge to edge. Same two thresholds the columns use, so the type steps down
|
|
66
|
+
with the layout rather than at some other width. */
|
|
67
|
+
@container (width < 720px) {
|
|
68
|
+
.fx-stat-cell__figure {
|
|
69
|
+
font-size: 22px;
|
|
70
|
+
margin-top: 4px;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
@container (width < 360px) {
|
|
74
|
+
.fx-stat-cell__figure {
|
|
75
|
+
font-size: 20px;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
.fx-stat-cell__sub {
|
|
62
80
|
font-size: var(--og-fs-xs);
|
|
63
81
|
font-weight: 400;
|
|
@@ -51,14 +51,36 @@ declare class FlowCanvasComponent {
|
|
|
51
51
|
readonly height: _angular_core.InputSignal<string>;
|
|
52
52
|
/** Caller-translated hint shown when the graph is empty. */
|
|
53
53
|
readonly emptyText: _angular_core.InputSignal<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Work is running against this chain — validation, typically.
|
|
56
|
+
*
|
|
57
|
+
* The edges become a travelling dashed line ("marching ants") so the canvas
|
|
58
|
+
* says something is in flight without a spinner sitting on top of the graph.
|
|
59
|
+
* Purely presentational: the caller owns what "busy" means and when it ends.
|
|
60
|
+
*/
|
|
61
|
+
readonly busy: _angular_core.InputSignal<boolean>;
|
|
54
62
|
readonly nodeClick: _angular_core.OutputEmitterRef<string>;
|
|
55
63
|
private readonly flow;
|
|
56
64
|
private readonly host;
|
|
57
65
|
private readonly destroyRef;
|
|
58
66
|
constructor();
|
|
59
67
|
private fitHandle;
|
|
60
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Coalesces fit requests, then fits TWICE — one frame apart.
|
|
70
|
+
*
|
|
71
|
+
* vflow frames the graph from `getNodesBounds`, which sums each node's
|
|
72
|
+
* `point()` and its `size()`. For an html-template node that size is the
|
|
73
|
+
* MEASURED box and is still 0 on the frame the nodes are created, so the
|
|
74
|
+
* first fit sees a bounds box running origin-to-origin: five 176px cards
|
|
75
|
+
* measured 864px instead of 1040px, and the graph was framed 88px — half a
|
|
76
|
+
* card — too far right, clipping the last one (MEASURED 16/09).
|
|
77
|
+
*
|
|
78
|
+
* The second pass runs once the cards have been laid out and measured, and
|
|
79
|
+
* is what actually frames them. The first is kept so a slow frame still
|
|
80
|
+
* shows something sensible rather than the graph pinned top-left.
|
|
81
|
+
*/
|
|
61
82
|
private scheduleFit;
|
|
83
|
+
private fit;
|
|
62
84
|
/** Column index per node: pinned value, else longest path from a source. */
|
|
63
85
|
private readonly columns;
|
|
64
86
|
/** ngx-vflow nodes: html-template cards positioned by the derived layout. */
|
|
@@ -86,7 +108,7 @@ declare class FlowCanvasComponent {
|
|
|
86
108
|
isEdgeActive(edge: Edge): boolean;
|
|
87
109
|
isEdgeDimmed(edge: Edge): boolean;
|
|
88
110
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FlowCanvasComponent, never>;
|
|
89
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FlowCanvasComponent, "fx-ui-flow-canvas", never, { "nodes": { "alias": "nodes"; "required": false; "isSignal": true; }; "edges": { "alias": "edges"; "required": false; "isSignal": true; }; "activeId": { "alias": "activeId"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; }, { "nodeClick": "nodeClick"; }, never, never, true, never>;
|
|
111
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FlowCanvasComponent, "fx-ui-flow-canvas", never, { "nodes": { "alias": "nodes"; "required": false; "isSignal": true; }; "edges": { "alias": "edges"; "required": false; "isSignal": true; }; "activeId": { "alias": "activeId"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "busy": { "alias": "busy"; "required": false; "isSignal": true; }; }, { "nodeClick": "nodeClick"; }, never, never, true, never>;
|
|
90
112
|
}
|
|
91
113
|
|
|
92
114
|
declare class FlowConnection {
|