@truenas/ui-components 0.8.14 → 0.8.16

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.8.14",
3
+ "version": "0.8.16",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public"
@@ -15,7 +15,7 @@
15
15
  * which is the whole promise being made here — that a mock goes in.
16
16
  */
17
17
  import * as i0 from "@angular/core";
18
- import { AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, ComponentRef, DoCheck, ElementRef, InjectionToken, OnChanges, OnDestroy, OnInit, PipeTransform, Provider, Renderer2, Signal, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef } from "@angular/core";
18
+ import { AfterContentInit, AfterViewInit, ChangeDetectorRef, ComponentRef, DoCheck, ElementRef, InjectionToken, OnChanges, OnDestroy, OnInit, PipeTransform, Provider, Renderer2, Signal, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef } from "@angular/core";
19
19
  import { AbstractControl, ControlValueAccessor, NgControl, ValidationErrors } from "@angular/forms";
20
20
  import { BehaviorSubject, Observable } from "rxjs";
21
21
  import { BaseHarnessFilters, ComponentHarness, HarnessLoader, HarnessPredicate, ModifierKeys as ModifierKeys$1, TestKey } from "@angular/cdk/testing";
@@ -18214,14 +18214,33 @@ export interface FileSystemItem {
18214
18214
  disabled?: boolean;
18215
18215
  }
18216
18216
  export interface FilePickerCallbacks {
18217
+ /**
18218
+ * Lists a directory. Reject with an Error to fail the navigation: the popup
18219
+ * shows its message above the listing (and `error` emits a `navigation`
18220
+ * error) until a later listing succeeds.
18221
+ */
18217
18222
  getChildren?: (path: string) => Promise<FileSystemItem[]>;
18218
18223
  validatePath?: (path: string) => Promise<boolean>;
18219
18224
  }
18225
+ /**
18226
+ * Everything that decides what the picker browses and how its value is
18227
+ * expressed, as one object — for consumers that build it from their own data
18228
+ * source and would otherwise bind the same four inputs at every call site.
18229
+ * Each field mirrors the input of the same name; an input that is bound
18230
+ * explicitly wins over the field.
18231
+ */
18232
+ export interface FilePickerSource {
18233
+ callbacks?: FilePickerCallbacks;
18234
+ rootPath?: string;
18235
+ startPath?: string;
18236
+ valueRoot?: string;
18237
+ }
18220
18238
  /**
18221
18239
  * A consumer-defined creation flow surfaced as a button in the picker footer,
18222
18240
  * e.g. creating a dataset or zvol through a dialog. The creation itself
18223
- * always happens outside the picker — either in a fully consumer-owned flow
18224
- * (`createAction` event) or through the pluggable inline flow (`create`).
18241
+ * always happens outside the picker — through the pluggable inline flow
18242
+ * (`create`), a consumer-owned flow the picker awaits (`run`), or, with
18243
+ * neither, a fully consumer-owned flow driven by the `createAction` event.
18225
18244
  */
18226
18245
  export interface FilePickerCreateAction {
18227
18246
  /** Identifies the action in `createAction` events, e.g. 'dataset'. */
@@ -18251,6 +18270,23 @@ export interface FilePickerCreateAction {
18251
18270
  * that (e.g. creation is cheap to leave behind, or idempotent).
18252
18271
  */
18253
18272
  create?: (parentPath: string, name: string) => Promise<string>;
18273
+ /**
18274
+ * A consumer-owned creation flow the picker awaits, e.g. one that opens a
18275
+ * dialog or side panel. Called with the browsed directory when the button is
18276
+ * pressed (and `create` is not set). Resolve with the created path to have
18277
+ * the picker refresh the listing and select it, exactly as after an inline
18278
+ * `create`; resolve with `null` when the user cancelled. Reject with an
18279
+ * Error to show its message in the popup (and emit a `creation` error).
18280
+ * Without `create` or `run`, pressing the button emits `createAction`.
18281
+ */
18282
+ run?: (parentPath: string) => Promise<string | null>;
18283
+ /**
18284
+ * Whether the action applies in the directory being browsed. Called with
18285
+ * that directory's path and its listed item — `null` when the picker has not
18286
+ * listed it, e.g. the start directory or the root. The button is hidden
18287
+ * where this returns false. Omit it to show the action everywhere.
18288
+ */
18289
+ canCreateAt?: (parentPath: string, directory: FileSystemItem | null) => boolean;
18254
18290
  }
18255
18291
  export interface FilePickerCreateActionEvent {
18256
18292
  actionId: string;
@@ -18328,6 +18364,13 @@ export declare class TnFilePickerComponent implements ControlValueAccessor, OnIn
18328
18364
  openOnClick: import("@angular/core").InputSignal<boolean>;
18329
18365
  placeholder: import("@angular/core").InputSignal<string>;
18330
18366
  disabled: import("@angular/core").InputSignal<boolean>;
18367
+ /**
18368
+ * Shows the value without letting the user change it: the input cannot be
18369
+ * edited and the popup cannot be opened. Unlike `disabled` (or a disabled
18370
+ * form control) the field stays focusable and its value stays part of the
18371
+ * form — e.g. for a user who may view a setting but not edit it.
18372
+ */
18373
+ readonly: import("@angular/core").InputSignal<boolean>;
18331
18374
  /**
18332
18375
  * Test-id base applied to the file-picker container (`file-picker-` prefixed)
18333
18376
  * and derived onto the inner chrome: the path input (`input-<base>`), the
@@ -18337,7 +18380,13 @@ export declare class TnFilePickerComponent implements ControlValueAccessor, OnIn
18337
18380
  * (default `data-testid`).
18338
18381
  */
18339
18382
  testId: import("@angular/core").InputSignal<TnTestIdValue>;
18340
- startPath: import("@angular/core").InputSignal<string>;
18383
+ /**
18384
+ * Bundles `callbacks`, `rootPath`, `startPath` and `valueRoot` into one
18385
+ * object. Each of those inputs, when bound, wins over its field here.
18386
+ */
18387
+ source: import("@angular/core").InputSignal<FilePickerSource | undefined>;
18388
+ /** Directory the popup opens in. Defaults to `source.startPath`, else `/mnt`. */
18389
+ startPath: import("@angular/core").InputSignal<string | undefined>;
18341
18390
  /** Restricts navigation — users cannot navigate above this path. */
18342
18391
  rootPath: import("@angular/core").InputSignal<string | undefined>;
18343
18392
  /**
@@ -18359,7 +18408,10 @@ export declare class TnFilePickerComponent implements ControlValueAccessor, OnIn
18359
18408
  */
18360
18409
  itemSelectionChange: import("@angular/core").OutputEmitterRef<FilePickerSelection>;
18361
18410
  pathChange: import("@angular/core").OutputEmitterRef<string>;
18362
- /** Re-emitted from the popup when one of the `createActions` buttons is pressed. */
18411
+ /**
18412
+ * Emitted when one of the `createActions` buttons with neither `create` nor
18413
+ * `run` is pressed.
18414
+ */
18363
18415
  createAction: import("@angular/core").OutputEmitterRef<FilePickerCreateActionEvent>;
18364
18416
  error: import("@angular/core").OutputEmitterRef<FilePickerError>;
18365
18417
  wrapperEl: import("@angular/core").Signal<ElementRef<HTMLDivElement>>;
@@ -18374,6 +18426,11 @@ export declare class TnFilePickerComponent implements ControlValueAccessor, OnIn
18374
18426
  selectedItems: import("@angular/core").WritableSignal<string[]>;
18375
18427
  loading: import("@angular/core").WritableSignal<boolean>;
18376
18428
  hasError: import("@angular/core").WritableSignal<boolean>;
18429
+ /**
18430
+ * Message of the last failed listing or creation, shown in the popup until
18431
+ * a listing succeeds or the popup closes.
18432
+ */
18433
+ popupError: import("@angular/core").WritableSignal<string | null>;
18377
18434
  private formDisabled;
18378
18435
  /**
18379
18436
  * Field-derived ARIA for the path input when projected into a
@@ -18390,6 +18447,11 @@ export declare class TnFilePickerComponent implements ControlValueAccessor, OnIn
18390
18447
  */
18391
18448
  private listedItems;
18392
18449
  isDisabled: import("@angular/core").Signal<boolean>;
18450
+ private effectiveCallbacks;
18451
+ private effectiveStartPath;
18452
+ private effectiveValueRoot;
18453
+ /** `createActions` that apply in the directory being browsed (`canCreateAt`). */
18454
+ protected availableCreateActions: import("@angular/core").Signal<FilePickerCreateAction[]>;
18393
18455
  /** Explicit `testId` wins; otherwise the bound control's name (formControlName etc.). */
18394
18456
  protected resolvedTestId: import("@angular/core").Signal<TnTestIdValue>;
18395
18457
  /**
@@ -18466,6 +18528,23 @@ export declare class TnFilePickerComponent implements ControlValueAccessor, OnIn
18466
18528
  navigateToPath(path: string): void;
18467
18529
  /** An inline `create` flow finished — show the new item selected and applied. */
18468
18530
  onCreated(path: string): void;
18531
+ /**
18532
+ * A footer button for an action without `create` was pressed: await its
18533
+ * `run` flow and select what it created, or hand the action to the consumer
18534
+ * through `createAction`.
18535
+ *
18536
+ * Deliberately not named `onCreateAction`: Storybook copies a story's props
18537
+ * onto the rendered component, and stories commonly name their
18538
+ * `(createAction)` handler that way, which would replace this method.
18539
+ */
18540
+ runCreateAction(event: FilePickerCreateActionEvent): Promise<void>;
18541
+ /**
18542
+ * `selectPath` for an item a create action just made. The user is looking at
18543
+ * the popup, so a path outside `rootPath` — which `selectPath` would only
18544
+ * report as a `validation` error, never shown there — is told as a
18545
+ * `creation` error in the popup instead of the press seeming to do nothing.
18546
+ */
18547
+ private selectCreatedPath;
18469
18548
  /**
18470
18549
  * Re-fetches the listing of the directory currently being browsed. Call it
18471
18550
  * when the directory changed outside the picker, e.g. after a
@@ -18569,11 +18648,21 @@ export declare class TnFilePickerComponent implements ControlValueAccessor, OnIn
18569
18648
  "required": false;
18570
18649
  "isSignal": true;
18571
18650
  };
18651
+ "readonly": {
18652
+ "alias": "readonly";
18653
+ "required": false;
18654
+ "isSignal": true;
18655
+ };
18572
18656
  "testId": {
18573
18657
  "alias": "testId";
18574
18658
  "required": false;
18575
18659
  "isSignal": true;
18576
18660
  };
18661
+ "source": {
18662
+ "alias": "source";
18663
+ "required": false;
18664
+ "isSignal": true;
18665
+ };
18577
18666
  "startPath": {
18578
18667
  "alias": "startPath";
18579
18668
  "required": false;
@@ -18899,7 +18988,7 @@ interface DisplayedFileItem extends FileSystemItem {
18899
18988
  /** Marks the synthetic first row hosting the inline creation input. */
18900
18989
  inlineCreate?: boolean;
18901
18990
  }
18902
- export declare class TnFilePickerPopupComponent implements AfterViewChecked {
18991
+ export declare class TnFilePickerPopupComponent {
18903
18992
  /**
18904
18993
  * What can be selected: a single mode, or an explicit list of selectable
18905
18994
  * item types, e.g. ['folder', 'dataset'].
@@ -18908,8 +18997,8 @@ export declare class TnFilePickerPopupComponent implements AfterViewChecked {
18908
18997
  multiSelect: import("@angular/core").InputSignal<boolean>;
18909
18998
  /**
18910
18999
  * Consumer-defined creation flows rendered as buttons in the footer, next to the
18911
- * Select button. Pressing one emits `createAction` with the action id and the
18912
- * currently browsed path.
19000
+ * Select button. Pressing one without `create` emits `createAction` with the
19001
+ * action id and the currently browsed path; the owning picker runs its `run`.
18913
19002
  */
18914
19003
  createActions: import("@angular/core").InputSignal<FilePickerCreateAction[]>;
18915
19004
  currentPath: import("@angular/core").InputSignal<string>;
@@ -18922,6 +19011,8 @@ export declare class TnFilePickerPopupComponent implements AfterViewChecked {
18922
19011
  fileItems: import("@angular/core").InputSignal<FileSystemItem[]>;
18923
19012
  selectedItems: import("@angular/core").InputSignal<string[]>;
18924
19013
  loading: import("@angular/core").InputSignal<boolean>;
19014
+ /** Message of a failed listing or creation, shown above the listing. */
19015
+ errorMessage: import("@angular/core").InputSignal<string | null>;
18925
19016
  fileExtensions: import("@angular/core").InputSignal<string[] | undefined>;
18926
19017
  /**
18927
19018
  * Test-id base the owning picker resolved (explicit `testId`, else the bound
@@ -18935,22 +19026,14 @@ export declare class TnFilePickerPopupComponent implements AfterViewChecked {
18935
19026
  private elementRef;
18936
19027
  constructor();
18937
19028
  /**
18938
- * Requests a focus of the inline creation input on the next view check.
19029
+ * Requests a focus of the inline creation input after the next render.
18939
19030
  * Set when the row opens and when a failed attempt re-enables the input —
18940
19031
  * the two moments the input either just entered the DOM or just lost focus
18941
- * to the `disabled` attribute. Cleared once the focus is scheduled, so
18942
- * focus is NOT permanently forced back: after an error the user can still
18943
- * click elsewhere without being trapped in the input.
18944
- */
18945
- private pendingCreationFocus;
18946
- /**
18947
- * Focus runs here rather than at the call sites because the input may not
18948
- * be in the DOM (or may still be disabled) until a later view check, and a
18949
- * re-render can replace the element after focus() was scheduled — render
18950
- * scheduling differs between hosts. So the request keeps retrying on each
18951
- * check and is cleared only once the input actually holds focus.
19032
+ * to the `disabled` attribute. Cleared once focus is applied, so focus is
19033
+ * NOT permanently forced back: after an error the user can still click
19034
+ * elsewhere without being trapped in the input.
18952
19035
  */
18953
- ngAfterViewChecked(): void;
19036
+ private readonly creationFocusRequested;
18954
19037
  itemClick: import("@angular/core").OutputEmitterRef<FileSystemItem>;
18955
19038
  itemDoubleClick: import("@angular/core").OutputEmitterRef<FileSystemItem>;
18956
19039
  pathNavigate: import("@angular/core").OutputEmitterRef<string>;
@@ -18963,6 +19046,12 @@ export declare class TnFilePickerPopupComponent implements AfterViewChecked {
18963
19046
  submit: import("@angular/core").OutputEmitterRef<void>;
18964
19047
  cancel: import("@angular/core").OutputEmitterRef<void>;
18965
19048
  displayedColumns: string[];
19049
+ /**
19050
+ * The empty directory is the table's own empty state, worded for a listing —
19051
+ * a second empty-state block here would render under it, so the popup only
19052
+ * words and decorates the table's.
19053
+ */
19054
+ protected readonly emptyIcon: string;
18966
19055
  /**
18967
19056
  * Whether an empty selection stands for the directory being browsed: true
18968
19057
  * whenever the selectable types include a directory-like type. The footer
@@ -18984,6 +19073,7 @@ export declare class TnFilePickerPopupComponent implements AfterViewChecked {
18984
19073
  */
18985
19074
  protected readonly selectTestId: import("@angular/core").Signal<(string | number | null | undefined)[]>;
18986
19075
  protected readonly clearSelectionTestId: import("@angular/core").Signal<(string | number | null | undefined)[]>;
19076
+ protected readonly errorTestId: import("@angular/core").Signal<(string | number | null | undefined)[]>;
18987
19077
  /** Role-first segments for the inline creation input: `input-create[-<base>]`. */
18988
19078
  protected readonly inlineCreateTestId: import("@angular/core").Signal<(string | number | null | undefined)[]>;
18989
19079
  /**
@@ -19087,6 +19177,11 @@ export declare class TnFilePickerPopupComponent implements AfterViewChecked {
19087
19177
  "required": false;
19088
19178
  "isSignal": true;
19089
19179
  };
19180
+ "errorMessage": {
19181
+ "alias": "errorMessage";
19182
+ "required": false;
19183
+ "isSignal": true;
19184
+ };
19090
19185
  "fileExtensions": {
19091
19186
  "alias": "fileExtensions";
19092
19187
  "required": false;