@truenas/ui-components 0.1.22 → 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';
@@ -9643,6 +9643,10 @@ var TnTheme;
9643
9643
  * Default theme used when no theme is set
9644
9644
  */
9645
9645
  const DEFAULT_THEME = TnTheme.Dark;
9646
+ /**
9647
+ * Light theme used when OS preference is light
9648
+ */
9649
+ const LIGHT_THEME = TnTheme.Blue;
9646
9650
  /**
9647
9651
  * localStorage key for storing the current theme name
9648
9652
  */
@@ -9711,7 +9715,8 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
9711
9715
  *
9712
9716
  * Features:
9713
9717
  * - Signal-based reactive theme state
9714
- * - LocalStorage persistence (key: 'tn-theme')
9718
+ * - OS color scheme detection (prefers-color-scheme)
9719
+ * - LocalStorage persistence for explicit user choices (key: 'tn-theme')
9715
9720
  * - Automatic CSS class application to document root
9716
9721
  * - SSR-safe (checks for browser platform)
9717
9722
  *
@@ -9731,9 +9736,12 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
9731
9736
  * // Get current theme (signal)
9732
9737
  * const currentTheme = this.themeService.currentTheme();
9733
9738
  *
9734
- * // Set theme using enum (recommended)
9739
+ * // Set theme using enum (recommended) — persists to localStorage
9735
9740
  * this.themeService.setTheme(TnTheme.Blue);
9736
9741
  *
9742
+ * // Clear user preference and follow OS theme
9743
+ * this.themeService.clearPreference();
9744
+ *
9737
9745
  * // React to theme changes
9738
9746
  * effect(() => {
9739
9747
  * console.log('Theme changed to:', this.themeService.currentTheme()?.label);
@@ -9745,10 +9753,28 @@ const THEME_MAP = new Map(TN_THEME_DEFINITIONS.map((theme) => [theme.name, theme
9745
9753
  class TnThemeService {
9746
9754
  platformId = inject(PLATFORM_ID);
9747
9755
  isBrowser = isPlatformBrowser(this.platformId);
9756
+ destroyRef = inject(DestroyRef);
9748
9757
  /**
9749
9758
  * Internal signal holding the current theme enum value
9750
9759
  */
9751
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
+ };
9752
9778
  /**
9753
9779
  * Computed signal that returns the full theme definition for the current theme
9754
9780
  */
@@ -9762,12 +9788,16 @@ class TnThemeService {
9762
9788
  currentThemeClass = computed(() => {
9763
9789
  return this.currentTheme()?.className ?? TnTheme.Dark;
9764
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" }] : []));
9765
9795
  /**
9766
9796
  * All available theme definitions in the library (readonly array)
9767
9797
  */
9768
9798
  availableThemes = TN_THEME_DEFINITIONS;
9769
9799
  constructor() {
9770
- // Initialize theme from localStorage or default
9800
+ // Initialize theme from localStorage or OS preference
9771
9801
  this.initializeTheme();
9772
9802
  // Effect to apply theme CSS class to document root whenever theme changes
9773
9803
  effect(() => {
@@ -9775,9 +9805,9 @@ class TnThemeService {
9775
9805
  this.applyThemeToDOM(this.currentThemeClass());
9776
9806
  }
9777
9807
  });
9778
- // Effect to persist theme to localStorage whenever it changes
9808
+ // Effect to persist theme to localStorage only for explicit user choices
9779
9809
  effect(() => {
9780
- if (this.isBrowser) {
9810
+ if (this.isBrowser && this.userSelected()) {
9781
9811
  const theme = this.currentTheme();
9782
9812
  if (theme) {
9783
9813
  this.persistThemeToStorage(theme);
@@ -9787,7 +9817,8 @@ class TnThemeService {
9787
9817
  }
9788
9818
  /**
9789
9819
  * Set the current theme.
9790
- * 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.
9791
9822
  *
9792
9823
  * @param theme - The theme to set (use TnTheme enum)
9793
9824
  * @returns true if theme was found and set, false otherwise
@@ -9800,6 +9831,7 @@ class TnThemeService {
9800
9831
  setTheme(theme) {
9801
9832
  const themeDefinition = THEME_MAP.get(theme);
9802
9833
  if (themeDefinition) {
9834
+ this.userSelected.set(true);
9803
9835
  this.currentThemeSignal.set(theme);
9804
9836
  return true;
9805
9837
  }
@@ -9813,10 +9845,27 @@ class TnThemeService {
9813
9845
  return this.currentThemeSignal();
9814
9846
  }
9815
9847
  /**
9816
- * Reset theme to default
9848
+ * Reset theme to default by clearing user preference and reverting to OS detection.
9817
9849
  */
9818
9850
  resetToDefault() {
9819
- 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();
9820
9869
  }
9821
9870
  /**
9822
9871
  * Check if a theme exists
@@ -9825,7 +9874,7 @@ class TnThemeService {
9825
9874
  return THEME_MAP.has(theme);
9826
9875
  }
9827
9876
  /**
9828
- * Initialize theme from localStorage or use default
9877
+ * Initialize theme from localStorage or OS color scheme preference
9829
9878
  */
9830
9879
  initializeTheme() {
9831
9880
  if (!this.isBrowser) {
@@ -9834,18 +9883,54 @@ class TnThemeService {
9834
9883
  try {
9835
9884
  const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
9836
9885
  if (storedTheme && this.hasTheme(storedTheme)) {
9886
+ this.userSelected.set(true);
9837
9887
  this.currentThemeSignal.set(storedTheme);
9838
9888
  }
9839
9889
  else {
9840
- // If no valid stored theme, use default
9841
- this.currentThemeSignal.set(DEFAULT_THEME);
9890
+ // No valid stored theme detect from OS preference
9891
+ this.applySystemTheme();
9892
+ this.listenForColorSchemeChanges();
9842
9893
  }
9843
9894
  }
9844
9895
  catch (error) {
9845
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 {
9846
9913
  this.currentThemeSignal.set(DEFAULT_THEME);
9847
9914
  }
9848
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
+ }
9849
9934
  /**
9850
9935
  * Apply theme CSS class to document root.
9851
9936
  * Removes all other theme classes first to avoid conflicts.
@@ -9904,5 +9989,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
9904
9989
  * Generated bundle index. Do not edit.
9905
9990
  */
9906
9991
 
9907
- 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 };
9908
9993
  //# sourceMappingURL=truenas-ui-components.mjs.map