achery-ui 0.18.0 → 0.19.0

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,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
+ var reactNative = require('react-native');
4
5
  var tokens = require('achery-ui/tokens');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
- var reactNative = require('react-native');
7
7
  var reactNativeSafeAreaContext = require('react-native-safe-area-context');
8
8
 
9
9
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -2789,8 +2789,16 @@ var require_yen = __commonJS({
2789
2789
  module.exports = "../yen-ZW6HM35W.svg";
2790
2790
  }
2791
2791
  });
2792
+ var MODE_STORAGE_KEY = "achery-theme-mode";
2793
+ var ACCENT_STORAGE_KEY = "achery-accent";
2794
+ var DIAL_STORAGE_KEY = "achery-dial";
2795
+ var MATERIAL_STORAGE_KEY = "achery-material";
2792
2796
  var ThemeContext = react.createContext({
2793
2797
  tokens: tokens.lightTokens,
2798
+ mode: "light",
2799
+ theme: "light",
2800
+ setTheme: () => {
2801
+ },
2794
2802
  dark: false,
2795
2803
  toggle: () => {
2796
2804
  },
@@ -2803,52 +2811,153 @@ var ThemeContext = react.createContext({
2803
2811
  material: "none",
2804
2812
  setMaterial: () => {
2805
2813
  },
2806
- surfaceOrigin: "native-only"
2814
+ surfaceOrigin: "native-only",
2815
+ hydrated: true
2807
2816
  });
2817
+ var isThemeMode = (v) => v === "light" || v === "dark" || v === "system";
2818
+ var isAccentColor = (v) => typeof v === "string" && Object.prototype.hasOwnProperty.call(tokens.accentColors, v);
2819
+ var isAccentDial = (v) => v === "underline" || v === "chrome" || v === "surface";
2820
+ var isMaterial = (v) => v === "none" || v === "leather" || v === "wood" || v === "copper";
2808
2821
  function NativeThemeProvider({
2809
2822
  children,
2823
+ defaultMode,
2810
2824
  defaultDark = false,
2811
2825
  defaultAccent = "terracotta",
2812
2826
  defaultDial = "chrome",
2813
2827
  defaultMaterial = "none",
2814
- defaultSurfaceOrigin = "native-only"
2828
+ defaultSurfaceOrigin = "native-only",
2829
+ storage
2815
2830
  }) {
2816
- const [dark, setDark] = react.useState(defaultDark);
2831
+ const initialMode = defaultMode ?? (defaultDark ? "dark" : "system");
2832
+ const [mode, setModeState] = react.useState(initialMode);
2817
2833
  const [accent, setAccentState] = react.useState(defaultAccent);
2818
2834
  const [dial, setDialState] = react.useState(defaultDial);
2819
2835
  const [material, setMaterialState] = react.useState(defaultMaterial);
2820
2836
  const [surfaceOrigin] = react.useState(defaultSurfaceOrigin);
2821
- const toggle = react.useCallback(() => setDark((d) => !d), []);
2822
- const setAccent = react.useCallback((a) => setAccentState(a), []);
2823
- const setDial = react.useCallback((d) => setDialState(d), []);
2824
- const setMaterial = react.useCallback((m) => setMaterialState(m), []);
2837
+ const [hydrated, setHydrated] = react.useState(!storage);
2838
+ const systemScheme = reactNative.useColorScheme();
2839
+ const resolvedTheme = mode === "system" ? systemScheme === "dark" ? "dark" : "light" : mode;
2840
+ const dark = resolvedTheme === "dark";
2841
+ const userSet = react.useRef({ mode: false, accent: false, dial: false, material: false });
2842
+ const storageRef = react.useRef(storage);
2843
+ react.useEffect(() => {
2844
+ const store = storageRef.current;
2845
+ if (!store) return;
2846
+ let cancelled = false;
2847
+ void (async () => {
2848
+ try {
2849
+ const [storedMode, storedAccent, storedDial, storedMaterial] = await Promise.all([
2850
+ store.getItem(MODE_STORAGE_KEY),
2851
+ store.getItem(ACCENT_STORAGE_KEY),
2852
+ store.getItem(DIAL_STORAGE_KEY),
2853
+ store.getItem(MATERIAL_STORAGE_KEY)
2854
+ ]);
2855
+ if (cancelled) return;
2856
+ if (isThemeMode(storedMode) && !userSet.current.mode) setModeState(storedMode);
2857
+ if (isAccentColor(storedAccent) && !userSet.current.accent) setAccentState(storedAccent);
2858
+ if (isAccentDial(storedDial) && !userSet.current.dial) setDialState(storedDial);
2859
+ if (isMaterial(storedMaterial) && !userSet.current.material)
2860
+ setMaterialState(storedMaterial);
2861
+ } catch {
2862
+ } finally {
2863
+ if (!cancelled) setHydrated(true);
2864
+ }
2865
+ })();
2866
+ return () => {
2867
+ cancelled = true;
2868
+ };
2869
+ }, []);
2870
+ const persist = react.useCallback((key, value2) => {
2871
+ const store = storageRef.current;
2872
+ if (!store) return;
2873
+ try {
2874
+ const result = store.setItem(key, value2);
2875
+ if (result && typeof result.catch === "function") {
2876
+ void result.catch(() => {
2877
+ });
2878
+ }
2879
+ } catch {
2880
+ }
2881
+ }, []);
2882
+ const setTheme = react.useCallback(
2883
+ (next) => {
2884
+ userSet.current.mode = true;
2885
+ setModeState(next);
2886
+ persist(MODE_STORAGE_KEY, next);
2887
+ },
2888
+ [persist]
2889
+ );
2890
+ const toggle = react.useCallback(() => {
2891
+ setTheme(resolvedTheme === "dark" ? "light" : "dark");
2892
+ }, [resolvedTheme, setTheme]);
2893
+ const setAccent = react.useCallback(
2894
+ (a) => {
2895
+ userSet.current.accent = true;
2896
+ setAccentState(a);
2897
+ persist(ACCENT_STORAGE_KEY, a);
2898
+ },
2899
+ [persist]
2900
+ );
2901
+ const setDial = react.useCallback(
2902
+ (d) => {
2903
+ userSet.current.dial = true;
2904
+ setDialState(d);
2905
+ persist(DIAL_STORAGE_KEY, d);
2906
+ },
2907
+ [persist]
2908
+ );
2909
+ const setMaterial = react.useCallback(
2910
+ (m) => {
2911
+ userSet.current.material = true;
2912
+ setMaterialState(m);
2913
+ persist(MATERIAL_STORAGE_KEY, m);
2914
+ },
2915
+ [persist]
2916
+ );
2825
2917
  const tokens$1 = react.useMemo(() => {
2826
2918
  const base = dark ? tokens.darkTokens : tokens.lightTokens;
2827
- const a = tokens.accentColors[accent];
2919
+ const a = tokens.accentColors[accent] ?? tokens.accentColors.terracotta;
2828
2920
  return {
2829
2921
  ...base,
2830
2922
  accent: dark ? a.light : a.main,
2831
2923
  accentFg: dark ? a.fgDark : a.fg
2832
2924
  };
2833
2925
  }, [dark, accent]);
2834
- return /* @__PURE__ */ jsxRuntime.jsx(
2835
- ThemeContext.Provider,
2836
- {
2837
- value: {
2838
- tokens: tokens$1,
2839
- dark,
2840
- toggle,
2841
- accent,
2842
- setAccent,
2843
- dial,
2844
- setDial,
2845
- material,
2846
- setMaterial,
2847
- surfaceOrigin
2848
- },
2849
- children
2850
- }
2926
+ const value = react.useMemo(
2927
+ () => ({
2928
+ tokens: tokens$1,
2929
+ mode,
2930
+ theme: resolvedTheme,
2931
+ setTheme,
2932
+ dark,
2933
+ toggle,
2934
+ accent,
2935
+ setAccent,
2936
+ dial,
2937
+ setDial,
2938
+ material,
2939
+ setMaterial,
2940
+ surfaceOrigin,
2941
+ hydrated
2942
+ }),
2943
+ [
2944
+ tokens$1,
2945
+ mode,
2946
+ resolvedTheme,
2947
+ setTheme,
2948
+ dark,
2949
+ toggle,
2950
+ accent,
2951
+ setAccent,
2952
+ dial,
2953
+ setDial,
2954
+ material,
2955
+ setMaterial,
2956
+ surfaceOrigin,
2957
+ hydrated
2958
+ ]
2851
2959
  );
2960
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value, children });
2852
2961
  }
2853
2962
  function useTheme() {
2854
2963
  return react.useContext(ThemeContext);