@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxeltech/angular-ui",
3
- "version": "0.7.124",
3
+ "version": "0.7.133",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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 h-8 × w-8, no side padding, so the
365
- glyph sits dead-centre (anh Tú 04/09). Consumers may still size it with
366
- utilities (`w-9 h-9`) those win in the utilities layer. */
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/09so 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: 2rem;
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
- /* Compact size (anh Tú 29/08): h-8 + 13px label the pre-V2 h-10 read too
12
- large against the V2 density. leading-none: arbitrary text-[13px] inherits
13
- lh 1.5 and Archivo's uneven leading floats the ink ~1px above center. */
14
- @apply flex text-[13px] leading-none h-8 font-semibold px-large py-small items-center justify-center rounded-lg shadow-sm transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed;
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
- @if (loading()) {
18
- <div class="flex w-full items-center justify-center" aria-hidden="true">
19
- <svg
20
- [attr.fill]="
21
- buttonVariant() !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'
22
- "
23
- width="16"
24
- height="16"
25
- viewBox="0 0 24 24"
26
- xmlns="http://www.w3.org/2000/svg"
27
- >
28
- <circle cx="4" cy="12" r="0">
29
- <animate
30
- begin="0;spinner_z0Or.end"
31
- attributeName="r"
32
- calcMode="spline"
33
- dur="0.5s"
34
- keySplines=".36,.6,.31,1"
35
- values="0;3"
36
- fill="freeze"
37
- />
38
- <animate
39
- begin="spinner_OLMs.end"
40
- attributeName="cx"
41
- calcMode="spline"
42
- dur="0.5s"
43
- keySplines=".36,.6,.31,1"
44
- values="4;12"
45
- fill="freeze"
46
- />
47
- <animate
48
- begin="spinner_UHR2.end"
49
- attributeName="cx"
50
- calcMode="spline"
51
- dur="0.5s"
52
- keySplines=".36,.6,.31,1"
53
- values="12;20"
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 ? 13 : 18"
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 ? 13 : 18" class="item-icon"></fx-ui-hero-icon>
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 ? 13 : 18"
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 ? 13 : 18" class="item-icon"></fx-ui-hero-icon>
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
- @apply px-large pt-4 pb-1 text-[10.5px]/[1.4] font-semibold text-faint select-none;
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
- min-height: 30px;
208
- padding: 6px 8px;
209
- font-size: var(--og-fs-sm);
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: 18px;
272
- height: 18px;
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 sitting in an 18px slot, so the
301
- ICON is 13px and the 5px below restores the slot: 13 + 5 + the container's
302
- 9px gap = 27px from the icon's left edge to the label, exactly as in the
303
- design. Sizing the icon itself 18px drew it half again too large. */
304
- width: 13px;
305
- height: 13px;
306
- margin-right: 5px;
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 (12.5px, 0.7.107) — no second scale here.
311
- Leading is 18px, not `leading-5`'s 20px: the label is the tallest thing in
312
- the row, so 20 + 6 + 6 padding made every row 32px and the declared
313
- `min-height: 30px` never applied. The reference's row is 30. */
314
- @apply flex-1 whitespace-nowrap overflow-hidden text-ellipsis leading-[18px] text-left;
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-[11px] border-t border-border-default select-none;
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
- /* Panes CO GIÃN: facets/list nhường chỗ để cụm detail luôn sàn 400px.
77
- `--fx-md-list-width` (input `listWidth`) giờ bề rộng MONG MUỐN kiêm
78
- tối đa của cụm list, không còn con số cứng. */
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 230px;
106
+ flex: 0 1 204px;
95
107
  width: auto;
96
- min-width: 200px;
97
- max-width: 260px;
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: 0 1 var(--fx-md-list-width, 420px);
124
+ flex: 1 1 0;
113
125
  min-width: 260px;
114
- max-width: var(--fx-md-list-width, 420px);
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 !h-8 !px-semi text-sm"
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 !h-8 !px-semi text-sm"
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
- /** Coalesces fit requests into one frame after vflow has measured its nodes. */
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 {