@truenas/ui-components 0.2.3 → 0.2.5

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": "@truenas/ui-components",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public"
@@ -176,6 +176,21 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
176
176
  disabled: _angular_core.InputSignal<boolean>;
177
177
  /** Require the user to select from the dropdown — reverts on blur if no match */
178
178
  requireSelection: _angular_core.InputSignal<boolean>;
179
+ /**
180
+ * Commit free text as the value: on blur or Enter, an unmatched search term
181
+ * becomes the control value instead of being discarded. For string-valued
182
+ * autocompletes (the typed text IS the value) — e.g. a device path picker
183
+ * where known devices are suggested but any path is acceptable. Mutually
184
+ * exclusive with `requireSelection`, which reverts unmatched text.
185
+ */
186
+ allowCustomValue: _angular_core.InputSignal<boolean>;
187
+ /**
188
+ * Show a loading row in the dropdown panel while options are being fetched.
189
+ * Pair with `searchChange`/`loadMore` for server-driven options.
190
+ */
191
+ loading: _angular_core.InputSignal<boolean>;
192
+ /** Text shown next to the spinner while `loading` is set. */
193
+ loadingText: _angular_core.InputSignal<string>;
179
194
  /** Custom filter function. Defaults to case-insensitive includes on displayWith text */
180
195
  filterFn: _angular_core.InputSignal<((option: T, searchTerm: string) => boolean) | undefined>;
181
196
  /** Text shown when no options match the search */
@@ -199,6 +214,29 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
199
214
  testId: _angular_core.InputSignal<TnTestIdValue>;
200
215
  /** Emits when an option is selected */
201
216
  optionSelected: _angular_core.OutputEmitterRef<T>;
217
+ /**
218
+ * Emits the search term as the user types (not on programmatic writes or
219
+ * option selection). Drive server-side filtering from this: fetch matches
220
+ * and update `options`. The component still applies its client-side filter
221
+ * on top — for pre-filtered server results, pass `[filterFn]` that returns
222
+ * `true`. Emissions are not debounced; debounce in the consumer if the
223
+ * lookup is expensive.
224
+ */
225
+ searchChange: _angular_core.OutputEmitterRef<string>;
226
+ /**
227
+ * Emits when the open dropdown is scrolled near its bottom — append the
228
+ * next page to `options` (and use `loading` while it fetches). Suppressed
229
+ * until the `options` COUNT changes so a slow consumer is not spammed —
230
+ * which also means replacing the array with a same-length page (e.g. a
231
+ * fixed-size window) does not re-arm the emitter; pagination must append.
232
+ */
233
+ loadMore: _angular_core.OutputEmitterRef<void>;
234
+ /**
235
+ * Emits every time the panel opens (focus, click, typing, ArrowDown). For
236
+ * click-to-suggest pickers, prime the first page from here when `options`
237
+ * is still empty — `searchChange` alone never fires until the user types.
238
+ */
239
+ opened: _angular_core.OutputEmitterRef<void>;
202
240
  /** Reference to the input element */
203
241
  inputEl: _angular_core.Signal<ElementRef<HTMLInputElement> | undefined>;
204
242
  /** Template for the dropdown panel, portaled into a CDK overlay on open. */
@@ -225,8 +263,17 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
225
263
  private onTouched;
226
264
  /** Live overlay holding the dropdown panel, or undefined when closed. */
227
265
  private overlayRef?;
266
+ /** Embedded view of the portaled panel — re-rendered before underfill measurement. */
267
+ private panelViewRef?;
228
268
  /** Subscriptions tied to the current overlay; torn down on close. */
229
269
  private overlaySubs;
270
+ /** Set after a loadMore emission; cleared when the options count changes. */
271
+ private loadMorePending;
272
+ /** Options count at the last effect run — length changes re-arm `loadMore`. */
273
+ private lastOptionsCount;
274
+ /** Scroll distance (px) from the panel bottom that triggers `loadMore`. */
275
+ private static readonly loadMoreThresholdPx;
276
+ constructor();
230
277
  ngOnDestroy(): void;
231
278
  writeValue(value: T | null): void;
232
279
  registerOnChange(fn: (value: T | null) => void): void;
@@ -237,6 +284,21 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
237
284
  onBlur(): void;
238
285
  onOptionClick(option: T): void;
239
286
  onKeydown(event: KeyboardEvent): void;
287
+ /**
288
+ * `loadMore` normally fires from a scroll event, which never happens when
289
+ * the current page is too short to overflow the panel — pagination would
290
+ * dead-end with data still available. When new rows land (or the panel
291
+ * opens), emit once more if the panel has no scrollbar.
292
+ */
293
+ private checkUnderfill;
294
+ onPanelScroll(event: Event): void;
295
+ /**
296
+ * Commit the current search term as the value (allowCustomValue mode). A
297
+ * display match (case-insensitive, same as the requireSelection path)
298
+ * commits the matching option instead, so picking an existing entry by
299
+ * typing its full text behaves like clicking it.
300
+ */
301
+ private commitCustomValue;
240
302
  private selectOption;
241
303
  private open;
242
304
  private close;
@@ -253,7 +315,7 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
253
315
  private detachOverlay;
254
316
  private scrollToHighlighted;
255
317
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnAutocompleteComponent<any>, never>;
256
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnAutocompleteComponent<any>, "tn-autocomplete", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "displayWith": { "alias": "displayWith"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "requireSelection": { "alias": "requireSelection"; "required": false; "isSignal": true; }; "filterFn": { "alias": "filterFn"; "required": false; "isSignal": true; }; "noResultsText": { "alias": "noResultsText"; "required": false; "isSignal": true; }; "maxResults": { "alias": "maxResults"; "required": false; "isSignal": true; }; "panelMaxHeight": { "alias": "panelMaxHeight"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; }, { "optionSelected": "optionSelected"; }, never, never, true, never>;
318
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnAutocompleteComponent<any>, "tn-autocomplete", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "displayWith": { "alias": "displayWith"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "requireSelection": { "alias": "requireSelection"; "required": false; "isSignal": true; }; "allowCustomValue": { "alias": "allowCustomValue"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "filterFn": { "alias": "filterFn"; "required": false; "isSignal": true; }; "noResultsText": { "alias": "noResultsText"; "required": false; "isSignal": true; }; "maxResults": { "alias": "maxResults"; "required": false; "isSignal": true; }; "panelMaxHeight": { "alias": "panelMaxHeight"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; }, { "optionSelected": "optionSelected"; "searchChange": "searchChange"; "loadMore": "loadMore"; "opened": "opened"; }, never, never, true, never>;
257
319
  }
258
320
 
259
321
  /**
@@ -426,6 +488,19 @@ declare class TnAutocompleteHarness extends ComponentHarness {
426
488
  * ```
427
489
  */
428
490
  blur(): Promise<void>;
491
+ /**
492
+ * Checks whether the dropdown panel is showing the loading row.
493
+ *
494
+ * @returns Promise resolving to true if the loading indicator is visible.
495
+ *
496
+ * @example
497
+ * ```typescript
498
+ * const ac = await loader.getHarness(TnAutocompleteHarness);
499
+ * await ac.focus();
500
+ * expect(await ac.isLoading()).toBe(true);
501
+ * ```
502
+ */
503
+ isLoading(): Promise<boolean>;
429
504
  }
430
505
  /**
431
506
  * A set of criteria that can be used to filter a list of `TnAutocompleteHarness` instances.
@@ -3394,7 +3469,18 @@ declare class TnFormFieldComponent implements AfterContentInit {
3394
3469
  required: _angular_core.InputSignal<boolean>;
3395
3470
  testId: _angular_core.InputSignal<TnTestIdValue>;
3396
3471
  subscriptSizing: _angular_core.InputSignal<SubscriptSizing>;
3397
- /** Optional tooltip shown via a help icon next to the label. */
3472
+ /**
3473
+ * Optional tooltip shown via a help icon.
3474
+ *
3475
+ * With a `label`, the icon renders next to the label in the label row. Without
3476
+ * one, the icon renders inline after the projected control instead — for
3477
+ * controls that carry their own label (e.g. `tn-checkbox`), where a detached
3478
+ * icon row above the control would look orphaned.
3479
+ *
3480
+ * Inline mode targets compact, self-labeled controls: the wrapper becomes a
3481
+ * flex row, so a full-width control (e.g. a label-less `tn-input`) would
3482
+ * shrink toward its content width. Give such fields a `label` instead.
3483
+ */
3398
3484
  tooltip: _angular_core.InputSignal<string>;
3399
3485
  /** Placement of the tooltip relative to its help icon. */
3400
3486
  tooltipPosition: _angular_core.InputSignal<TooltipPosition>;
@@ -3422,10 +3508,24 @@ declare class TnFormFieldComponent implements AfterContentInit {
3422
3508
  /**
3423
3509
  * Whether the required indicator renders: forced via the `required` input, or
3424
3510
  * inferred from the projected control's validators (mirrors Angular Material's
3425
- * `hasValidator(Validators.required)` approach reference equality, so composed
3426
- * or custom required-like validators need the explicit input).
3511
+ * `hasValidator(Validators.required)` approach, extended to `requiredTrue` for
3512
+ * boolean controls — reference equality, so composed or custom required-like
3513
+ * validators need the explicit input).
3427
3514
  */
3428
3515
  protected showRequired: _angular_core.Signal<boolean>;
3516
+ /**
3517
+ * Whether the tooltip icon renders inline after the projected control rather
3518
+ * than in the label row — true when a tooltip is set but no label is.
3519
+ */
3520
+ protected showInlineTooltip: _angular_core.Signal<boolean>;
3521
+ /**
3522
+ * Whether the required indicator renders inline after the projected control —
3523
+ * with no label there is no label row to host the asterisk, so a required
3524
+ * self-labeled control (e.g. a consent `tn-checkbox`) still gets one.
3525
+ */
3526
+ protected showInlineRequired: _angular_core.Signal<boolean>;
3527
+ /** Whether the wrapper hosts any inline extras and lays out as a flex row. */
3528
+ protected showInlineExtras: _angular_core.Signal<boolean>;
3429
3529
  protected hasError: _angular_core.Signal<boolean>;
3430
3530
  protected errorMessage: _angular_core.Signal<string>;
3431
3531
  ngAfterContentInit(): void;
@@ -3629,6 +3729,19 @@ declare class TnFormFieldHarness extends ComponentHarness {
3629
3729
  * ```
3630
3730
  */
3631
3731
  getTooltip(): Promise<string | null>;
3732
+ /**
3733
+ * Checks whether the tooltip help icon renders inline after the projected
3734
+ * control (label-less mode) rather than in the label row.
3735
+ *
3736
+ * @returns Promise resolving to true if the inline tooltip trigger is present.
3737
+ *
3738
+ * @example
3739
+ * ```typescript
3740
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ testId: 'enable-fxp' }));
3741
+ * expect(await field.isTooltipInline()).toBe(true);
3742
+ * ```
3743
+ */
3744
+ isTooltipInline(): Promise<boolean>;
3632
3745
  /**
3633
3746
  * Checks whether the form field is marked as required.
3634
3747
  *