@truenas/ui-components 0.1.21 → 0.1.23

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, ChangeDetectionStrategy, Component, inject, Injectable, viewChild, signal, effect, computed, ViewEncapsulation, Directive, contentChildren, output, forwardRef, ElementRef, ViewContainerRef, contentChild, ChangeDetectorRef, HostListener, TemplateRef, IterableDiffers, Pipe, model, afterNextRender, PLATFORM_ID } from '@angular/core';
2
+ import { input, ChangeDetectionStrategy, Component, inject, Injectable, viewChild, signal, effect, computed, ViewEncapsulation, Directive, contentChildren, output, forwardRef, ElementRef, ViewContainerRef, contentChild, ChangeDetectorRef, HostListener, TemplateRef, IterableDiffers, Pipe, model, afterNextRender, PLATFORM_ID, DestroyRef } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
4
  import { CommonModule, DOCUMENT, isPlatformBrowser } from '@angular/common';
5
5
  import { mdiCheckCircle, mdiAlertCircle, mdiAlert, mdiInformation, mdiDotsVertical, mdiClose, mdiFolderOpen, mdiLock, mdiLoading, mdiFolderPlus, mdiFolderNetwork, mdiHarddisk, mdiDatabase, mdiFile, mdiFolder } from '@mdi/js';
@@ -477,6 +477,9 @@ class TnIconComponent {
477
477
  // Convert to registry format for Lucide icons
478
478
  effectiveIconName = `lucide:${name}`;
479
479
  }
480
+ else if (library === 'custom' && !name.startsWith('app-')) {
481
+ effectiveIconName = `app-${name}`;
482
+ }
480
483
  // 1. Try icon registry (libraries and custom icons)
481
484
  const iconOptions = {
482
485
  size: this.size(),
@@ -9640,6 +9643,10 @@ var TnTheme;
9640
9643
  * Default theme used when no theme is set
9641
9644
  */
9642
9645
  const DEFAULT_THEME = TnTheme.Dark;
9646
+ /**
9647
+ * Light theme used when OS preference is light
9648
+ */
9649
+ const LIGHT_THEME = TnTheme.Blue;
9643
9650
  /**
9644
9651
  * localStorage key for storing the current theme name
9645
9652
  */
@@ -9708,7 +9715,8 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
9708
9715
  *
9709
9716
  * Features:
9710
9717
  * - Signal-based reactive theme state
9711
- * - LocalStorage persistence (key: 'tn-theme')
9718
+ * - OS color scheme detection (prefers-color-scheme)
9719
+ * - LocalStorage persistence for explicit user choices (key: 'tn-theme')
9712
9720
  * - Automatic CSS class application to document root
9713
9721
  * - SSR-safe (checks for browser platform)
9714
9722
  *
@@ -9728,9 +9736,12 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
9728
9736
  * // Get current theme (signal)
9729
9737
  * const currentTheme = this.themeService.currentTheme();
9730
9738
  *
9731
- * // Set theme using enum (recommended)
9739
+ * // Set theme using enum (recommended) — persists to localStorage
9732
9740
  * this.themeService.setTheme(TnTheme.Blue);
9733
9741
  *
9742
+ * // Clear user preference and follow OS theme
9743
+ * this.themeService.clearPreference();
9744
+ *
9734
9745
  * // React to theme changes
9735
9746
  * effect(() => {
9736
9747
  * console.log('Theme changed to:', this.themeService.currentTheme()?.label);
@@ -9742,10 +9753,28 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
9742
9753
  class TnThemeService {
9743
9754
  platformId = inject(PLATFORM_ID);
9744
9755
  isBrowser = isPlatformBrowser(this.platformId);
9756
+ destroyRef = inject(DestroyRef);
9745
9757
  /**
9746
9758
  * Internal signal holding the current theme enum value
9747
9759
  */
9748
9760
  currentThemeSignal = signal(DEFAULT_THEME, ...(ngDevMode ? [{ debugName: "currentThemeSignal" }] : []));
9761
+ /**
9762
+ * Whether the user has explicitly selected a theme (vs OS-detected default).
9763
+ * When true, theme is persisted to localStorage and OS changes are ignored.
9764
+ */
9765
+ userSelected = signal(false, ...(ngDevMode ? [{ debugName: "userSelected" }] : []));
9766
+ /**
9767
+ * Reference to the prefers-color-scheme media query for cleanup
9768
+ */
9769
+ colorSchemeQuery = null;
9770
+ /**
9771
+ * Bound listener reference for cleanup
9772
+ */
9773
+ colorSchemeListener = (event) => {
9774
+ if (!this.userSelected()) {
9775
+ this.currentThemeSignal.set(event.matches ? DEFAULT_THEME : LIGHT_THEME);
9776
+ }
9777
+ };
9749
9778
  /**
9750
9779
  * Computed signal that returns the full theme definition for the current theme
9751
9780
  */
@@ -9759,12 +9788,16 @@ class TnThemeService {
9759
9788
  currentThemeClass = computed(() => {
9760
9789
  return this.currentTheme()?.className ?? TnTheme.Dark;
9761
9790
  }, ...(ngDevMode ? [{ debugName: "currentThemeClass" }] : []));
9791
+ /**
9792
+ * Whether the current theme is based on OS preference (no explicit user choice)
9793
+ */
9794
+ isUsingSystemTheme = computed(() => !this.userSelected(), ...(ngDevMode ? [{ debugName: "isUsingSystemTheme" }] : []));
9762
9795
  /**
9763
9796
  * All available theme definitions in the library (readonly array)
9764
9797
  */
9765
9798
  availableThemes = TN_THEME_DEFINITIONS;
9766
9799
  constructor() {
9767
- // Initialize theme from localStorage or default
9800
+ // Initialize theme from localStorage or OS preference
9768
9801
  this.initializeTheme();
9769
9802
  // Effect to apply theme CSS class to document root whenever theme changes
9770
9803
  effect(() => {
@@ -9772,9 +9805,9 @@ class TnThemeService {
9772
9805
  this.applyThemeToDOM(this.currentThemeClass());
9773
9806
  }
9774
9807
  });
9775
- // Effect to persist theme to localStorage whenever it changes
9808
+ // Effect to persist theme to localStorage only for explicit user choices
9776
9809
  effect(() => {
9777
- if (this.isBrowser) {
9810
+ if (this.isBrowser && this.userSelected()) {
9778
9811
  const theme = this.currentTheme();
9779
9812
  if (theme) {
9780
9813
  this.persistThemeToStorage(theme);
@@ -9784,7 +9817,8 @@ class TnThemeService {
9784
9817
  }
9785
9818
  /**
9786
9819
  * Set the current theme.
9787
- * Updates the signal, which triggers effects to apply CSS and save to localStorage.
9820
+ * Marks this as an explicit user choice, persists to localStorage,
9821
+ * and stops following OS color scheme changes.
9788
9822
  *
9789
9823
  * @param theme - The theme to set (use TnTheme enum)
9790
9824
  * @returns true if theme was found and set, false otherwise
@@ -9797,6 +9831,7 @@ class TnThemeService {
9797
9831
  setTheme(theme) {
9798
9832
  const themeDefinition = THEME_MAP.get(theme);
9799
9833
  if (themeDefinition) {
9834
+ this.userSelected.set(true);
9800
9835
  this.currentThemeSignal.set(theme);
9801
9836
  return true;
9802
9837
  }
@@ -9810,10 +9845,27 @@ class TnThemeService {
9810
9845
  return this.currentThemeSignal();
9811
9846
  }
9812
9847
  /**
9813
- * Reset theme to default
9848
+ * Reset theme to default by clearing user preference and reverting to OS detection.
9814
9849
  */
9815
9850
  resetToDefault() {
9816
- this.setTheme(DEFAULT_THEME);
9851
+ this.clearPreference();
9852
+ }
9853
+ /**
9854
+ * Clear user preference and revert to OS-based theme detection.
9855
+ * Removes the stored theme from localStorage and follows the OS color scheme.
9856
+ */
9857
+ clearPreference() {
9858
+ if (this.isBrowser) {
9859
+ try {
9860
+ localStorage.removeItem(THEME_STORAGE_KEY);
9861
+ }
9862
+ catch (error) {
9863
+ console.error('[TnThemeService] Error removing from localStorage:', error);
9864
+ }
9865
+ }
9866
+ this.userSelected.set(false);
9867
+ this.applySystemTheme();
9868
+ this.listenForColorSchemeChanges();
9817
9869
  }
9818
9870
  /**
9819
9871
  * Check if a theme exists
@@ -9822,7 +9874,7 @@ class TnThemeService {
9822
9874
  return THEME_MAP.has(theme);
9823
9875
  }
9824
9876
  /**
9825
- * Initialize theme from localStorage or use default
9877
+ * Initialize theme from localStorage or OS color scheme preference
9826
9878
  */
9827
9879
  initializeTheme() {
9828
9880
  if (!this.isBrowser) {
@@ -9831,18 +9883,54 @@ class TnThemeService {
9831
9883
  try {
9832
9884
  const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
9833
9885
  if (storedTheme && this.hasTheme(storedTheme)) {
9886
+ this.userSelected.set(true);
9834
9887
  this.currentThemeSignal.set(storedTheme);
9835
9888
  }
9836
9889
  else {
9837
- // If no valid stored theme, use default
9838
- this.currentThemeSignal.set(DEFAULT_THEME);
9890
+ // No valid stored theme detect from OS preference
9891
+ this.applySystemTheme();
9892
+ this.listenForColorSchemeChanges();
9839
9893
  }
9840
9894
  }
9841
9895
  catch (error) {
9842
9896
  console.error('[TnThemeService] Error reading from localStorage:', error);
9897
+ this.applySystemTheme();
9898
+ this.listenForColorSchemeChanges();
9899
+ }
9900
+ }
9901
+ /**
9902
+ * Detect OS color scheme preference and apply corresponding theme
9903
+ */
9904
+ applySystemTheme() {
9905
+ if (!this.isBrowser) {
9906
+ return;
9907
+ }
9908
+ try {
9909
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
9910
+ this.currentThemeSignal.set(prefersDark ? DEFAULT_THEME : LIGHT_THEME);
9911
+ }
9912
+ catch {
9843
9913
  this.currentThemeSignal.set(DEFAULT_THEME);
9844
9914
  }
9845
9915
  }
9916
+ /**
9917
+ * Listen for OS color scheme changes and apply them when no user preference is set
9918
+ */
9919
+ listenForColorSchemeChanges() {
9920
+ if (!this.isBrowser || this.colorSchemeQuery) {
9921
+ return;
9922
+ }
9923
+ try {
9924
+ this.colorSchemeQuery = window.matchMedia('(prefers-color-scheme: dark)');
9925
+ this.colorSchemeQuery.addEventListener('change', this.colorSchemeListener);
9926
+ this.destroyRef.onDestroy(() => {
9927
+ this.colorSchemeQuery?.removeEventListener('change', this.colorSchemeListener);
9928
+ });
9929
+ }
9930
+ catch {
9931
+ // matchMedia not supported — fall through silently
9932
+ }
9933
+ }
9846
9934
  /**
9847
9935
  * Apply theme CSS class to document root.
9848
9936
  * Removes all other theme classes first to avoid conflicts.
@@ -9901,5 +9989,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
9901
9989
  * Generated bundle index. Do not edit.
9902
9990
  */
9903
9991
 
9904
- 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, TnMenuActivateHoverDirective, 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 };
9992
+ export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, 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, TnMenuActivateHoverDirective, 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 };
9905
9993
  //# sourceMappingURL=truenas-ui-components.mjs.map