@truenas/ui-components 0.1.17 → 0.1.19
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.
|
@@ -22,7 +22,8 @@ import * as i1$2 from '@angular/cdk/tree';
|
|
|
22
22
|
import { CdkTree, CdkTreeModule, CdkTreeNode, CDK_TREE_NODE_OUTLET_NODE, CdkTreeNodeOutlet, CdkNestedTreeNode } from '@angular/cdk/tree';
|
|
23
23
|
export { FlatTreeControl } from '@angular/cdk/tree';
|
|
24
24
|
import { map } from 'rxjs/operators';
|
|
25
|
-
import {
|
|
25
|
+
import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
|
|
26
|
+
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
|
26
27
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
27
28
|
|
|
28
29
|
var DiskType;
|
|
@@ -7657,78 +7658,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
7657
7658
|
args: ['keydown', ['$event']]
|
|
7658
7659
|
}] } });
|
|
7659
7660
|
|
|
7660
|
-
const defaults = {
|
|
7661
|
-
panelClass: ['tn-dialog-panel'],
|
|
7662
|
-
maxWidth: '90vw',
|
|
7663
|
-
maxHeight: '90vh',
|
|
7664
|
-
role: 'dialog',
|
|
7665
|
-
};
|
|
7666
|
-
class TnDialog {
|
|
7667
|
-
dialog = inject(Dialog);
|
|
7668
|
-
/**
|
|
7669
|
-
* Open a dialog with the given component or template.
|
|
7670
|
-
* Applies default configuration for panel class, max dimensions, and focus behavior.
|
|
7671
|
-
*/
|
|
7672
|
-
open(target, config) {
|
|
7673
|
-
const merged = {
|
|
7674
|
-
...defaults,
|
|
7675
|
-
...config,
|
|
7676
|
-
panelClass: [
|
|
7677
|
-
...defaults.panelClass,
|
|
7678
|
-
...(Array.isArray(config?.panelClass) ? config.panelClass : config?.panelClass ? [config.panelClass] : [])
|
|
7679
|
-
],
|
|
7680
|
-
autoFocus: config?.autoFocus ?? true,
|
|
7681
|
-
restoreFocus: config?.restoreFocus ?? true,
|
|
7682
|
-
};
|
|
7683
|
-
return this.dialog.open(target, merged);
|
|
7684
|
-
}
|
|
7685
|
-
/**
|
|
7686
|
-
* Open a fullscreen dialog that takes over the entire viewport.
|
|
7687
|
-
* Automatically applies fullscreen styling and dimensions.
|
|
7688
|
-
*/
|
|
7689
|
-
openFullscreen(target, config) {
|
|
7690
|
-
const merged = {
|
|
7691
|
-
...defaults,
|
|
7692
|
-
...config,
|
|
7693
|
-
maxWidth: '100vw',
|
|
7694
|
-
maxHeight: '100vh',
|
|
7695
|
-
width: '100vw',
|
|
7696
|
-
height: '100vh',
|
|
7697
|
-
panelClass: [
|
|
7698
|
-
...defaults.panelClass,
|
|
7699
|
-
...(Array.isArray(config?.panelClass) ? config.panelClass : config?.panelClass ? [config.panelClass] : []),
|
|
7700
|
-
'tn-dialog--fullscreen'
|
|
7701
|
-
],
|
|
7702
|
-
autoFocus: config?.autoFocus ?? true,
|
|
7703
|
-
restoreFocus: config?.restoreFocus ?? true,
|
|
7704
|
-
};
|
|
7705
|
-
return this.dialog.open(target, merged);
|
|
7706
|
-
}
|
|
7707
|
-
/**
|
|
7708
|
-
* Open a confirmation dialog with customizable title, message, and button labels.
|
|
7709
|
-
* Returns a promise that resolves to an Observable of the user's choice.
|
|
7710
|
-
*/
|
|
7711
|
-
confirm(opts) {
|
|
7712
|
-
// Import the confirm dialog component dynamically to avoid circular dependencies
|
|
7713
|
-
return Promise.resolve().then(function () { return confirmDialog_component; }).then(m => {
|
|
7714
|
-
const dialogRef = this.open(m.TnConfirmDialogComponent, {
|
|
7715
|
-
data: opts,
|
|
7716
|
-
width: '488px',
|
|
7717
|
-
role: 'alertdialog',
|
|
7718
|
-
disableClose: true,
|
|
7719
|
-
panelClass: [opts.destructive ? 'tn-dialog--destructive' : ''].filter(Boolean),
|
|
7720
|
-
});
|
|
7721
|
-
return dialogRef.closed;
|
|
7722
|
-
});
|
|
7723
|
-
}
|
|
7724
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnDialog, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7725
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnDialog, providedIn: 'root' });
|
|
7726
|
-
}
|
|
7727
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnDialog, decorators: [{
|
|
7728
|
-
type: Injectable,
|
|
7729
|
-
args: [{ providedIn: 'root' }]
|
|
7730
|
-
}] });
|
|
7731
|
-
|
|
7732
7661
|
class TnDialogShellComponent {
|
|
7733
7662
|
title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
7734
7663
|
showFullscreenButton = input(false, ...(ngDevMode ? [{ debugName: "showFullscreenButton" }] : []));
|
|
@@ -7817,10 +7746,281 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
7817
7746
|
}, template: "<tn-dialog-shell [title]=\"data.title\">\n <p style=\"margin: 0;\">{{ data.message }}</p>\n <div tnDialogAction>\n <tn-button\n type=\"button\"\n variant=\"outline\"\n [label]=\"data.cancelText || 'Cancel'\"\n (click)=\"ref.close(false)\" />\n <tn-button\n type=\"button\"\n [color]=\"data.destructive ? 'warn' : 'primary'\"\n [label]=\"data.confirmText || 'OK'\"\n (click)=\"ref.close(true)\" />\n </div>\n</tn-dialog-shell>\n" }]
|
|
7818
7747
|
}] });
|
|
7819
7748
|
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7749
|
+
const defaults = {
|
|
7750
|
+
panelClass: ['tn-dialog-panel'],
|
|
7751
|
+
maxWidth: '90vw',
|
|
7752
|
+
maxHeight: '90vh',
|
|
7753
|
+
role: 'dialog',
|
|
7754
|
+
};
|
|
7755
|
+
class TnDialog {
|
|
7756
|
+
dialog = inject(Dialog);
|
|
7757
|
+
/**
|
|
7758
|
+
* Open a dialog with the given component or template.
|
|
7759
|
+
* Applies default configuration for panel class, max dimensions, and focus behavior.
|
|
7760
|
+
*/
|
|
7761
|
+
open(target, config) {
|
|
7762
|
+
const merged = {
|
|
7763
|
+
...defaults,
|
|
7764
|
+
...config,
|
|
7765
|
+
panelClass: [
|
|
7766
|
+
...defaults.panelClass,
|
|
7767
|
+
...(Array.isArray(config?.panelClass) ? config.panelClass : config?.panelClass ? [config.panelClass] : [])
|
|
7768
|
+
],
|
|
7769
|
+
autoFocus: config?.autoFocus ?? true,
|
|
7770
|
+
restoreFocus: config?.restoreFocus ?? true,
|
|
7771
|
+
};
|
|
7772
|
+
return this.dialog.open(target, merged);
|
|
7773
|
+
}
|
|
7774
|
+
/**
|
|
7775
|
+
* Open a fullscreen dialog that takes over the entire viewport.
|
|
7776
|
+
* Automatically applies fullscreen styling and dimensions.
|
|
7777
|
+
*/
|
|
7778
|
+
openFullscreen(target, config) {
|
|
7779
|
+
const merged = {
|
|
7780
|
+
...defaults,
|
|
7781
|
+
...config,
|
|
7782
|
+
maxWidth: '100vw',
|
|
7783
|
+
maxHeight: '100vh',
|
|
7784
|
+
width: '100vw',
|
|
7785
|
+
height: '100vh',
|
|
7786
|
+
panelClass: [
|
|
7787
|
+
...defaults.panelClass,
|
|
7788
|
+
...(Array.isArray(config?.panelClass) ? config.panelClass : config?.panelClass ? [config.panelClass] : []),
|
|
7789
|
+
'tn-dialog--fullscreen'
|
|
7790
|
+
],
|
|
7791
|
+
autoFocus: config?.autoFocus ?? true,
|
|
7792
|
+
restoreFocus: config?.restoreFocus ?? true,
|
|
7793
|
+
};
|
|
7794
|
+
return this.dialog.open(target, merged);
|
|
7795
|
+
}
|
|
7796
|
+
/**
|
|
7797
|
+
* Open a confirmation dialog. Resolves to true if confirmed, false otherwise.
|
|
7798
|
+
*/
|
|
7799
|
+
async confirm(opts) {
|
|
7800
|
+
const dialogRef = this.open(TnConfirmDialogComponent, {
|
|
7801
|
+
data: opts,
|
|
7802
|
+
width: '488px',
|
|
7803
|
+
role: 'alertdialog',
|
|
7804
|
+
disableClose: true,
|
|
7805
|
+
panelClass: [opts.destructive ? 'tn-dialog--destructive' : ''].filter(Boolean),
|
|
7806
|
+
});
|
|
7807
|
+
const result = await firstValueFrom(dialogRef.closed);
|
|
7808
|
+
return !!result;
|
|
7809
|
+
}
|
|
7810
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnDialog, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7811
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnDialog, providedIn: 'root' });
|
|
7812
|
+
}
|
|
7813
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnDialog, decorators: [{
|
|
7814
|
+
type: Injectable,
|
|
7815
|
+
args: [{ providedIn: 'root' }]
|
|
7816
|
+
}] });
|
|
7817
|
+
|
|
7818
|
+
/**
|
|
7819
|
+
* Harness for interacting with `tn-dialog-shell` in tests.
|
|
7820
|
+
*
|
|
7821
|
+
* Dialogs are portaled into the CDK overlay outside the component tree.
|
|
7822
|
+
* Use `TnDialogTesting.rootLoader(fixture)` to get a loader that can find them.
|
|
7823
|
+
*
|
|
7824
|
+
* @example
|
|
7825
|
+
* ```typescript
|
|
7826
|
+
* const dialogLoader = TnDialogTesting.rootLoader(fixture);
|
|
7827
|
+
*
|
|
7828
|
+
* // Find dialog by title
|
|
7829
|
+
* const dialog = await dialogLoader.getHarness(
|
|
7830
|
+
* TnDialogHarness.with({ title: 'Delete Dataset?' })
|
|
7831
|
+
* );
|
|
7832
|
+
*
|
|
7833
|
+
* // Click an action button and close
|
|
7834
|
+
* await dialog.clickActionButton('Delete');
|
|
7835
|
+
* ```
|
|
7836
|
+
*/
|
|
7837
|
+
class TnDialogHarness extends ComponentHarness {
|
|
7838
|
+
/**
|
|
7839
|
+
* The selector for the host element of a dialog shell instance.
|
|
7840
|
+
*/
|
|
7841
|
+
static hostSelector = 'tn-dialog-shell';
|
|
7842
|
+
_title = this.locatorFor('.tn-dialog__title');
|
|
7843
|
+
_closeButton = this.locatorFor('.tn-dialog__close');
|
|
7844
|
+
_fullscreenButton = this.locatorForOptional('.tn-dialog__fullscreen');
|
|
7845
|
+
_content = this.locatorFor('.tn-dialog__content');
|
|
7846
|
+
/**
|
|
7847
|
+
* Gets a `HarnessPredicate` that can be used to search for a dialog
|
|
7848
|
+
* with specific attributes.
|
|
7849
|
+
*
|
|
7850
|
+
* @param options Options for filtering which dialog instances are considered a match.
|
|
7851
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
7852
|
+
*
|
|
7853
|
+
* @example
|
|
7854
|
+
* ```typescript
|
|
7855
|
+
* const dialog = await dialogLoader.getHarness(
|
|
7856
|
+
* TnDialogHarness.with({ title: 'Edit User' })
|
|
7857
|
+
* );
|
|
7858
|
+
*
|
|
7859
|
+
* // With regex
|
|
7860
|
+
* const dialog = await dialogLoader.getHarness(
|
|
7861
|
+
* TnDialogHarness.with({ title: /delete/i })
|
|
7862
|
+
* );
|
|
7863
|
+
* ```
|
|
7864
|
+
*/
|
|
7865
|
+
static with(options = {}) {
|
|
7866
|
+
return new HarnessPredicate(TnDialogHarness, options)
|
|
7867
|
+
.addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title));
|
|
7868
|
+
}
|
|
7869
|
+
/**
|
|
7870
|
+
* Gets the dialog's title text.
|
|
7871
|
+
*
|
|
7872
|
+
* @returns Promise resolving to the dialog title.
|
|
7873
|
+
*
|
|
7874
|
+
* @example
|
|
7875
|
+
* ```typescript
|
|
7876
|
+
* const dialog = await dialogLoader.getHarness(TnDialogHarness);
|
|
7877
|
+
* expect(await dialog.getTitle()).toBe('Edit User');
|
|
7878
|
+
* ```
|
|
7879
|
+
*/
|
|
7880
|
+
async getTitle() {
|
|
7881
|
+
const title = await this._title();
|
|
7882
|
+
return (await title.text()).trim();
|
|
7883
|
+
}
|
|
7884
|
+
/**
|
|
7885
|
+
* Gets the text content of the dialog's scrollable content area.
|
|
7886
|
+
*
|
|
7887
|
+
* @returns Promise resolving to the content text.
|
|
7888
|
+
*
|
|
7889
|
+
* @example
|
|
7890
|
+
* ```typescript
|
|
7891
|
+
* const dialog = await dialogLoader.getHarness(TnDialogHarness);
|
|
7892
|
+
* expect(await dialog.getContentText()).toContain('Are you sure?');
|
|
7893
|
+
* ```
|
|
7894
|
+
*/
|
|
7895
|
+
async getContentText() {
|
|
7896
|
+
const content = await this._content();
|
|
7897
|
+
return (await content.text()).trim();
|
|
7898
|
+
}
|
|
7899
|
+
/**
|
|
7900
|
+
* Clicks the close button (X) in the dialog header.
|
|
7901
|
+
*
|
|
7902
|
+
* @returns Promise that resolves when the click action is complete.
|
|
7903
|
+
*
|
|
7904
|
+
* @example
|
|
7905
|
+
* ```typescript
|
|
7906
|
+
* const dialog = await dialogLoader.getHarness(TnDialogHarness);
|
|
7907
|
+
* await dialog.close();
|
|
7908
|
+
* ```
|
|
7909
|
+
*/
|
|
7910
|
+
async close() {
|
|
7911
|
+
const closeBtn = await this._closeButton();
|
|
7912
|
+
await closeBtn.click();
|
|
7913
|
+
}
|
|
7914
|
+
/**
|
|
7915
|
+
* Clicks an action button in the dialog footer by its label.
|
|
7916
|
+
* Only matches buttons inside the `tnDialogAction` footer area, not buttons in content.
|
|
7917
|
+
*
|
|
7918
|
+
* @param label The button label to match. Supports string or regex.
|
|
7919
|
+
* @throws Error if no matching button is found in the actions area.
|
|
7920
|
+
*
|
|
7921
|
+
* @example
|
|
7922
|
+
* ```typescript
|
|
7923
|
+
* const dialog = await dialogLoader.getHarness(TnDialogHarness);
|
|
7924
|
+
* await dialog.clickActionButton('Save');
|
|
7925
|
+
* await dialog.clickActionButton(/cancel/i);
|
|
7926
|
+
* ```
|
|
7927
|
+
*/
|
|
7928
|
+
async clickActionButton(label) {
|
|
7929
|
+
const buttons = await this.locatorForAll(TnButtonHarness.with({ label, ancestor: '.tn-dialog__actions' }))();
|
|
7930
|
+
if (buttons.length === 0) {
|
|
7931
|
+
throw new Error(`No action button found with label matching: ${label}`);
|
|
7932
|
+
}
|
|
7933
|
+
await buttons[0].click();
|
|
7934
|
+
}
|
|
7935
|
+
/**
|
|
7936
|
+
* Gets all action button harnesses in the dialog footer.
|
|
7937
|
+
*
|
|
7938
|
+
* @returns Promise resolving to an array of `TnButtonHarness` instances.
|
|
7939
|
+
*
|
|
7940
|
+
* @example
|
|
7941
|
+
* ```typescript
|
|
7942
|
+
* const dialog = await dialogLoader.getHarness(TnDialogHarness);
|
|
7943
|
+
* const buttons = await dialog.getActionButtons();
|
|
7944
|
+
* expect(buttons).toHaveLength(2);
|
|
7945
|
+
* ```
|
|
7946
|
+
*/
|
|
7947
|
+
async getActionButtons() {
|
|
7948
|
+
return this.locatorForAll(TnButtonHarness.with({ ancestor: '.tn-dialog__actions' }))();
|
|
7949
|
+
}
|
|
7950
|
+
/**
|
|
7951
|
+
* Whether the dialog has a fullscreen toggle button.
|
|
7952
|
+
*
|
|
7953
|
+
* @returns Promise resolving to true if the fullscreen button is present.
|
|
7954
|
+
*/
|
|
7955
|
+
async hasFullscreenButton() {
|
|
7956
|
+
return (await this._fullscreenButton()) !== null;
|
|
7957
|
+
}
|
|
7958
|
+
/**
|
|
7959
|
+
* Clicks the fullscreen toggle button.
|
|
7960
|
+
*
|
|
7961
|
+
* @throws Error if the dialog does not have a fullscreen button.
|
|
7962
|
+
*/
|
|
7963
|
+
async toggleFullscreen() {
|
|
7964
|
+
const btn = await this._fullscreenButton();
|
|
7965
|
+
if (!btn) {
|
|
7966
|
+
throw new Error('Dialog does not have a fullscreen button');
|
|
7967
|
+
}
|
|
7968
|
+
await btn.click();
|
|
7969
|
+
}
|
|
7970
|
+
/**
|
|
7971
|
+
* Whether the dialog is currently in fullscreen mode.
|
|
7972
|
+
* Checks the aria-label of the fullscreen button.
|
|
7973
|
+
*
|
|
7974
|
+
* @returns Promise resolving to true if fullscreen, false if not or if no fullscreen button.
|
|
7975
|
+
*/
|
|
7976
|
+
async isFullscreen() {
|
|
7977
|
+
const btn = await this._fullscreenButton();
|
|
7978
|
+
if (!btn) {
|
|
7979
|
+
return false;
|
|
7980
|
+
}
|
|
7981
|
+
const ariaLabel = await btn.getAttribute('aria-label');
|
|
7982
|
+
return ariaLabel === 'Exit fullscreen';
|
|
7983
|
+
}
|
|
7984
|
+
}
|
|
7985
|
+
|
|
7986
|
+
/**
|
|
7987
|
+
* Test utilities for working with `TnDialogHarness`.
|
|
7988
|
+
*
|
|
7989
|
+
* Dialogs are portaled into the CDK overlay outside the component tree,
|
|
7990
|
+
* so a regular `TestbedHarnessEnvironment.loader()` won't find them.
|
|
7991
|
+
* Use `TnDialogTesting.rootLoader()` to get a loader that can.
|
|
7992
|
+
*
|
|
7993
|
+
* @example
|
|
7994
|
+
* ```typescript
|
|
7995
|
+
* import { TnDialogTesting, TnDialogHarness } from '@truenas/ui-components';
|
|
7996
|
+
*
|
|
7997
|
+
* let dialogLoader: HarnessLoader;
|
|
7998
|
+
*
|
|
7999
|
+
* beforeEach(() => {
|
|
8000
|
+
* fixture = TestBed.createComponent(TestHostComponent);
|
|
8001
|
+
* dialogLoader = TnDialogTesting.rootLoader(fixture);
|
|
8002
|
+
* });
|
|
8003
|
+
*
|
|
8004
|
+
* it('should open a confirm dialog', async () => {
|
|
8005
|
+
* const dialog = await dialogLoader.getHarness(
|
|
8006
|
+
* TnDialogHarness.with({ title: 'Delete?' })
|
|
8007
|
+
* );
|
|
8008
|
+
* await dialog.clickActionButton('Delete');
|
|
8009
|
+
* });
|
|
8010
|
+
* ```
|
|
8011
|
+
*/
|
|
8012
|
+
class TnDialogTesting {
|
|
8013
|
+
/**
|
|
8014
|
+
* Creates a `HarnessLoader` that searches the entire document,
|
|
8015
|
+
* including the CDK overlay where dialogs are rendered.
|
|
8016
|
+
*
|
|
8017
|
+
* @param fixture The component fixture for the test.
|
|
8018
|
+
* @returns A `HarnessLoader` capable of finding dialog harnesses.
|
|
8019
|
+
*/
|
|
8020
|
+
static rootLoader(fixture) {
|
|
8021
|
+
return TestbedHarnessEnvironment.documentRootLoader(fixture);
|
|
8022
|
+
}
|
|
8023
|
+
}
|
|
7824
8024
|
|
|
7825
8025
|
/**
|
|
7826
8026
|
* Directive to mark an element as a side-panel footer action.
|
|
@@ -8987,6 +9187,134 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
8987
9187
|
}, template: "<div class=\"tn-file-picker-container\">\n <div #wrapper ixInput class=\"tn-file-picker-wrapper\" style=\"padding-right: 40px;\">\n <input\n type=\"text\"\n class=\"tn-file-picker-input\"\n [class.error]=\"hasError()\"\n [value]=\"selectedPath() | tnStripMntPrefix\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"!allowManualInput()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onPathInput($event)\">\n\n <button\n type=\"button\"\n class=\"tn-file-picker-toggle\"\n aria-label=\"Open file picker\"\n [disabled]=\"isDisabled()\"\n (click)=\"openFilePicker()\">\n <tn-icon name=\"folder\" library=\"mdi\" />\n </button>\n </div>\n \n <ng-template #filePickerTemplate>\n <tn-file-picker-popup\n class=\"tn-file-picker-popup\"\n [mode]=\"mode()\"\n [multiSelect]=\"multiSelect()\"\n [allowCreate]=\"allowCreate()\"\n [allowDatasetCreate]=\"allowDatasetCreate()\"\n [allowZvolCreate]=\"allowZvolCreate()\"\n [currentPath]=\"currentPath()\"\n [fileItems]=\"fileItems()\"\n [selectedItems]=\"selectedItems()\"\n [loading]=\"loading()\"\n [creationLoading]=\"creationLoading()\"\n [fileExtensions]=\"fileExtensions()\"\n (itemClick)=\"onItemClick($event)\"\n (itemDoubleClick)=\"onItemDoubleClick($event)\"\n (pathNavigate)=\"navigateToPath($event)\"\n (createFolder)=\"onCreateFolder()\"\n (submitFolderName)=\"onSubmitFolderName($event.name, $event.tempId)\"\n (cancelFolderCreation)=\"onCancelFolderCreation($event)\"\n (clearSelection)=\"onClearSelection()\"\n (submit)=\"onSubmit()\"\n (cancel)=\"onCancel()\"\n (close)=\"close()\" />\n </ng-template>\n</div>", styles: [":host{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-file-picker-container{position:relative;display:flex;align-items:center;width:100%}.tn-file-picker-wrapper{display:flex;align-items:center;width:100%;position:relative}.tn-file-picker-input{display:block;width:100%;min-height:2.5rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--tn-fg1, #212529);background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;outline:none;box-sizing:border-box;font-family:inherit}.tn-file-picker-input::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-file-picker-input:focus{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}.tn-file-picker-input:disabled{background-color:var(--tn-alt-bg1, #f8f9fa);color:var(--tn-fg2, #6c757d);cursor:not-allowed;opacity:.6}.tn-file-picker-input.error{border-color:var(--tn-error, #dc3545)}.tn-file-picker-input.error:focus{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}.tn-file-picker-toggle{position:absolute;right:8px;z-index:2;pointer-events:auto;background:transparent;border:none;cursor:pointer;padding:4px;color:var(--tn-fg1);border-radius:4px}.tn-file-picker-toggle:hover{background:var(--tn-bg2, #f0f0f0)}.tn-file-picker-toggle:focus{outline:2px solid var(--tn-primary);outline-offset:2px}.tn-file-picker-toggle:disabled{cursor:not-allowed;opacity:.5}.tn-file-picker-toggle tn-icon{font-size:var(--tn-icon-md, 20px)}:host:focus-within .tn-file-picker-input{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}:host.error .tn-file-picker-input{border-color:var(--tn-error, #dc3545)}:host.error .tn-file-picker-input:focus{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}@media(prefers-reduced-motion:reduce){.tn-file-picker-input,.tn-file-picker-toggle,.file-item,.breadcrumb-segment{transition:none}.tn-file-picker-loading tn-icon{animation:none}}@media(prefers-contrast:high){.tn-file-picker-input{border-width:2px}.file-item:hover,.file-item.selected{border:2px solid var(--tn-fg1)}.zfs-badge{border:1px solid var(--tn-fg1)}}@media(max-width:768px){:host ::ng-deep .tn-file-picker-overlay .tn-file-picker-dialog{min-width:300px;max-width:calc(100vw - 32px);max-height:calc(100vh - 64px)}.tn-file-picker-header{flex-direction:column;gap:12px;align-items:stretch}.tn-file-picker-breadcrumb{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.tn-file-picker-breadcrumb::-webkit-scrollbar{display:none}.file-item{padding:12px;min-height:56px}.file-info{font-size:.875rem}}\n"] }]
|
|
8988
9188
|
}], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], multiSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSelect", required: false }] }], allowCreate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCreate", required: false }] }], allowDatasetCreate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDatasetCreate", required: false }] }], allowZvolCreate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowZvolCreate", required: false }] }], allowManualInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowManualInput", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], startPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "startPath", required: false }] }], rootPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootPath", required: false }] }], fileExtensions: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileExtensions", required: false }] }], callbacks: [{ type: i0.Input, args: [{ isSignal: true, alias: "callbacks", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], pathChange: [{ type: i0.Output, args: ["pathChange"] }], createFolder: [{ type: i0.Output, args: ["createFolder"] }], error: [{ type: i0.Output, args: ["error"] }], wrapperEl: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }], filePickerTemplate: [{ type: i0.ViewChild, args: ['filePickerTemplate', { isSignal: true }] }] } });
|
|
8989
9189
|
|
|
9190
|
+
class TnEmptyComponent {
|
|
9191
|
+
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
9192
|
+
description = input(...(ngDevMode ? [undefined, { debugName: "description" }] : []));
|
|
9193
|
+
icon = input(...(ngDevMode ? [undefined, { debugName: "icon" }] : []));
|
|
9194
|
+
iconLibrary = input('mdi', ...(ngDevMode ? [{ debugName: "iconLibrary" }] : []));
|
|
9195
|
+
actionText = input(...(ngDevMode ? [undefined, { debugName: "actionText" }] : []));
|
|
9196
|
+
size = input('default', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
9197
|
+
onAction = output();
|
|
9198
|
+
hasAction = computed(() => !!this.actionText(), ...(ngDevMode ? [{ debugName: "hasAction" }] : []));
|
|
9199
|
+
iconSize = computed(() => {
|
|
9200
|
+
return this.size() === 'compact' ? 'lg' : 'xl';
|
|
9201
|
+
}, ...(ngDevMode ? [{ debugName: "iconSize" }] : []));
|
|
9202
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnEmptyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9203
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnEmptyComponent, isStandalone: true, selector: "tn-empty", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconLibrary: { classPropertyName: "iconLibrary", publicName: "iconLibrary", isSignal: true, isRequired: false, transformFunction: null }, actionText: { classPropertyName: "actionText", publicName: "actionText", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onAction: "onAction" }, host: { attributes: { "role": "status" }, properties: { "class.tn-empty--compact": "size() === \"compact\"" }, classAttribute: "tn-empty" }, ngImport: i0, template: "@if (icon()) {\n <div class=\"tn-empty__icon\">\n <tn-icon\n aria-hidden=\"true\"\n [name]=\"icon()!\"\n [library]=\"iconLibrary()\"\n [size]=\"iconSize()\"\n />\n </div>\n}\n\n<div class=\"tn-empty__title\">\n {{ title() }}\n</div>\n\n@if (description()) {\n <div class=\"tn-empty__description\">\n {{ description() }}\n </div>\n}\n\n@if (hasAction()) {\n <div class=\"tn-empty__action\">\n <tn-button\n color=\"primary\"\n variant=\"outline\"\n [label]=\"actionText()!\"\n (onClick)=\"onAction.emit()\"\n />\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:8px;padding:48px 24px}:host(.tn-empty--compact){padding:24px 16px}:host(.tn-empty--compact) .tn-empty__title{font-size:1rem}:host(.tn-empty--compact) .tn-empty__description{font-size:.8125rem}:host(.tn-empty--compact) .tn-empty__icon{margin-bottom:4px}:host(.tn-empty--compact) .tn-empty__action{margin-top:4px}.tn-empty__icon{color:var(--tn-fg2, #6b7280);margin-bottom:8px}.tn-empty__title{font-size:1.125rem;font-weight:600;color:var(--tn-fg1, #e5e7eb);line-height:1.4}.tn-empty__description{font-size:.875rem;color:var(--tn-fg2, #6b7280);line-height:1.5;max-width:420px}.tn-empty__action{margin-top:8px}\n"], dependencies: [{ kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "fullSize", "customSize"] }, { kind: "component", type: TnButtonComponent, selector: "tn-button", inputs: ["primary", "color", "variant", "backgroundColor", "label", "disabled"], outputs: ["onClick"] }] });
|
|
9204
|
+
}
|
|
9205
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnEmptyComponent, decorators: [{
|
|
9206
|
+
type: Component,
|
|
9207
|
+
args: [{ selector: 'tn-empty', standalone: true, imports: [TnIconComponent, TnButtonComponent], host: {
|
|
9208
|
+
'class': 'tn-empty',
|
|
9209
|
+
'[class.tn-empty--compact]': 'size() === "compact"',
|
|
9210
|
+
'role': 'status',
|
|
9211
|
+
}, template: "@if (icon()) {\n <div class=\"tn-empty__icon\">\n <tn-icon\n aria-hidden=\"true\"\n [name]=\"icon()!\"\n [library]=\"iconLibrary()\"\n [size]=\"iconSize()\"\n />\n </div>\n}\n\n<div class=\"tn-empty__title\">\n {{ title() }}\n</div>\n\n@if (description()) {\n <div class=\"tn-empty__description\">\n {{ description() }}\n </div>\n}\n\n@if (hasAction()) {\n <div class=\"tn-empty__action\">\n <tn-button\n color=\"primary\"\n variant=\"outline\"\n [label]=\"actionText()!\"\n (onClick)=\"onAction.emit()\"\n />\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:8px;padding:48px 24px}:host(.tn-empty--compact){padding:24px 16px}:host(.tn-empty--compact) .tn-empty__title{font-size:1rem}:host(.tn-empty--compact) .tn-empty__description{font-size:.8125rem}:host(.tn-empty--compact) .tn-empty__icon{margin-bottom:4px}:host(.tn-empty--compact) .tn-empty__action{margin-top:4px}.tn-empty__icon{color:var(--tn-fg2, #6b7280);margin-bottom:8px}.tn-empty__title{font-size:1.125rem;font-weight:600;color:var(--tn-fg1, #e5e7eb);line-height:1.4}.tn-empty__description{font-size:.875rem;color:var(--tn-fg2, #6b7280);line-height:1.5;max-width:420px}.tn-empty__action{margin-top:8px}\n"] }]
|
|
9212
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconLibrary: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconLibrary", required: false }] }], actionText: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionText", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], onAction: [{ type: i0.Output, args: ["onAction"] }] } });
|
|
9213
|
+
|
|
9214
|
+
/**
|
|
9215
|
+
* Harness for interacting with tn-empty in tests.
|
|
9216
|
+
* Provides text-based querying for verifying empty state content.
|
|
9217
|
+
*
|
|
9218
|
+
* @example
|
|
9219
|
+
* ```typescript
|
|
9220
|
+
* // Check for existence
|
|
9221
|
+
* const empty = await loader.getHarness(TnEmptyHarness);
|
|
9222
|
+
*
|
|
9223
|
+
* // Find empty state by title
|
|
9224
|
+
* const noItems = await loader.getHarness(
|
|
9225
|
+
* TnEmptyHarness.with({ title: 'No items found' })
|
|
9226
|
+
* );
|
|
9227
|
+
*
|
|
9228
|
+
* // Check if empty state exists with specific text
|
|
9229
|
+
* const hasEmpty = await loader.hasHarness(
|
|
9230
|
+
* TnEmptyHarness.with({ title: /no.*found/i })
|
|
9231
|
+
* );
|
|
9232
|
+
* ```
|
|
9233
|
+
*/
|
|
9234
|
+
class TnEmptyHarness extends ComponentHarness {
|
|
9235
|
+
/**
|
|
9236
|
+
* The selector for the host element of a `TnEmptyComponent` instance.
|
|
9237
|
+
*/
|
|
9238
|
+
static hostSelector = 'tn-empty';
|
|
9239
|
+
_title = this.locatorFor('.tn-empty__title');
|
|
9240
|
+
_description = this.locatorForOptional('.tn-empty__description');
|
|
9241
|
+
/**
|
|
9242
|
+
* Gets a `HarnessPredicate` that can be used to search for an empty state
|
|
9243
|
+
* with specific attributes.
|
|
9244
|
+
*
|
|
9245
|
+
* @param options Options for filtering which empty state instances are considered a match.
|
|
9246
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
9247
|
+
*
|
|
9248
|
+
* @example
|
|
9249
|
+
* ```typescript
|
|
9250
|
+
* // Find by title
|
|
9251
|
+
* const empty = await loader.getHarness(
|
|
9252
|
+
* TnEmptyHarness.with({ title: 'No results' })
|
|
9253
|
+
* );
|
|
9254
|
+
*
|
|
9255
|
+
* // Find by title regex
|
|
9256
|
+
* const empty = await loader.getHarness(
|
|
9257
|
+
* TnEmptyHarness.with({ title: /empty/i })
|
|
9258
|
+
* );
|
|
9259
|
+
* ```
|
|
9260
|
+
*/
|
|
9261
|
+
static with(options = {}) {
|
|
9262
|
+
return new HarnessPredicate(TnEmptyHarness, options)
|
|
9263
|
+
.addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title));
|
|
9264
|
+
}
|
|
9265
|
+
/**
|
|
9266
|
+
* Gets the title text.
|
|
9267
|
+
*
|
|
9268
|
+
* @returns Promise resolving to the empty state's title text.
|
|
9269
|
+
*
|
|
9270
|
+
* @example
|
|
9271
|
+
* ```typescript
|
|
9272
|
+
* const empty = await loader.getHarness(TnEmptyHarness);
|
|
9273
|
+
* const title = await empty.getTitle();
|
|
9274
|
+
* expect(title).toBe('No items found');
|
|
9275
|
+
* ```
|
|
9276
|
+
*/
|
|
9277
|
+
async getTitle() {
|
|
9278
|
+
const title = await this._title();
|
|
9279
|
+
return (await title.text()).trim();
|
|
9280
|
+
}
|
|
9281
|
+
/**
|
|
9282
|
+
* Gets the description text, or null if no description is rendered.
|
|
9283
|
+
*
|
|
9284
|
+
* @returns Promise resolving to the description text, or null.
|
|
9285
|
+
*
|
|
9286
|
+
* @example
|
|
9287
|
+
* ```typescript
|
|
9288
|
+
* const empty = await loader.getHarness(TnEmptyHarness);
|
|
9289
|
+
* const desc = await empty.getDescription();
|
|
9290
|
+
* expect(desc).toBe('Try adjusting your filters');
|
|
9291
|
+
* ```
|
|
9292
|
+
*/
|
|
9293
|
+
async getDescription() {
|
|
9294
|
+
const desc = await this._description();
|
|
9295
|
+
if (!desc) {
|
|
9296
|
+
return null;
|
|
9297
|
+
}
|
|
9298
|
+
return (await desc.text()).trim();
|
|
9299
|
+
}
|
|
9300
|
+
/**
|
|
9301
|
+
* Gets all text content from the empty state (title + description combined).
|
|
9302
|
+
*
|
|
9303
|
+
* @returns Promise resolving to the full text content, trimmed of whitespace.
|
|
9304
|
+
*
|
|
9305
|
+
* @example
|
|
9306
|
+
* ```typescript
|
|
9307
|
+
* const empty = await loader.getHarness(TnEmptyHarness);
|
|
9308
|
+
* const text = await empty.getText();
|
|
9309
|
+
* expect(text).toContain('No items');
|
|
9310
|
+
* ```
|
|
9311
|
+
*/
|
|
9312
|
+
async getText() {
|
|
9313
|
+
const host = await this.host();
|
|
9314
|
+
return (await host.text()).trim();
|
|
9315
|
+
}
|
|
9316
|
+
}
|
|
9317
|
+
|
|
8990
9318
|
class TnKeyboardShortcutService {
|
|
8991
9319
|
shortcuts = new Map();
|
|
8992
9320
|
globalEnabled = true;
|
|
@@ -9505,5 +9833,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
9505
9833
|
* Generated bundle index. Do not edit.
|
|
9506
9834
|
*/
|
|
9507
9835
|
|
|
9508
|
-
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_THEME_DEFINITIONS, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCellDefDirective, TnCheckboxComponent, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateRangeInputComponent, TnDialog, TnDialogShellComponent, TnDividerComponent, TnDividerDirective, TnExpansionPanelComponent, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuComponent, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTabsComponent, TnTabsHarness, TnTheme, TnThemeService, TnTimeInputComponent, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
|
|
9836
|
+
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_THEME_DEFINITIONS, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCellDefDirective, TnCheckboxComponent, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateRangeInputComponent, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuComponent, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTabsComponent, TnTabsHarness, TnTheme, TnThemeService, TnTimeInputComponent, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
|
|
9509
9837
|
//# sourceMappingURL=truenas-ui-components.mjs.map
|