@willphan1712000/frontend 1.8.3 → 1.9.1

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/README.md CHANGED
@@ -28,6 +28,7 @@ Reusable React UI components and frontend utilities packaged for application dev
28
28
  - `Transform`
29
29
  - `tools`
30
30
  - `LinearAlgebra`
31
+ - `useThemeState`
31
32
 
32
33
  ### Auth helpers
33
34
  - `useSession`
@@ -249,6 +250,44 @@ function AppProviders({ children }: { children: React.ReactNode }) {
249
250
  - `session`
250
251
  - `auth`
251
252
 
253
+ ## Theme management usage
254
+
255
+ ### `useThemeState`
256
+
257
+ A custom React hook for managing, persisting, and applying the application's theme state (`'light'`, `'dark'`, or `'system'`). It synchronizes state with browser `localStorage` and applies/removes the theme class on `document.body`.
258
+
259
+ ```tsx
260
+ import { useThemeState } from '@willphan1712000/frontend';
261
+
262
+ const Example = () => {
263
+ const { setThemeState, getThemeState } = useThemeState();
264
+
265
+ return (
266
+ <div>
267
+ <p>Current Theme: {getThemeState()}</p>
268
+ <button onClick={() => setThemeState('light')}>Light</button>
269
+ <button onClick={() => setThemeState('dark')}>Dark</button>
270
+ <button onClick={() => setThemeState('system')}>System</button>
271
+ </div>
272
+ );
273
+ };
274
+ ```
275
+
276
+ #### API Reference
277
+
278
+ - `getThemeState(): 'light' | 'dark' | 'system'`
279
+ Retrieves the active theme setting from `localStorage`. Defaults to `'light'`.
280
+ - `setThemeState(mode: 'light' | 'dark' | 'system'): void`
281
+ Sets the active theme setting.
282
+ - `'light'`: Stores `'light'`, removes `'will-dark'` class from `document.body`, and disables OS preference event listeners.
283
+ - `'dark'`: Stores `'dark'`, adds `'will-dark'` class to `document.body`, and disables OS preference event listeners.
284
+ - `'system'`: Stores `'system'`, automatically toggles `'will-dark'` based on OS preferences, and registers a listener to react to future OS preference changes.
285
+
286
+ #### Configuration Details
287
+
288
+ - **Local Storage Key:** `'will-theme'`
289
+ - **CSS Class Applied to `<body>`:** `'will-dark'`
290
+
252
291
  ## Exported utilities
253
292
 
254
293
  ```ts
package/dist/index.d.mts CHANGED
@@ -174,7 +174,10 @@ interface Props$5 {
174
174
  setValue?: (value?: string) => void;
175
175
  label?: string;
176
176
  options?: {
177
- focusColor: string;
177
+ focusColor?: string;
178
+ backgroundColor?: string;
179
+ textColor?: string;
180
+ borderColor?: string;
178
181
  };
179
182
  }
180
183
  /**
@@ -203,7 +206,10 @@ interface Props$4 {
203
206
  setValue?: (value?: string) => void;
204
207
  label?: string;
205
208
  options?: {
206
- focusColor: string;
209
+ focusColor?: string;
210
+ backgroundColor?: string;
211
+ textColor?: string;
212
+ borderColor?: string;
207
213
  };
208
214
  }
209
215
  /**
@@ -774,4 +780,24 @@ declare const LinearAlgebra: {
774
780
  getVectorMagnitude(vector: number[]): number;
775
781
  };
776
782
 
777
- export { type AuthInterface, Avatar, Button, Canvas, ColorPickerSlider, DropdownSelect, Image$1 as Image, ImageEditor, Image as ImageUtilities, InputFile, InputGoogle, LinearAlgebra, Modern as ModernButton, MultiSelect, OptionSlider, type Options$2 as Options, RangeSlider, SessionProvider, type SessionType, type Options$1 as SliderOptions, type StorageInterface, TextArea, Transform, UploadImage, tools, useAuthClient, useSession };
783
+ declare const ThemeStateArray: readonly ["light", "dark", "system"];
784
+ type ThemeState = (typeof ThemeStateArray)[number];
785
+ interface UseThemeState {
786
+ setThemeState: (mode: ThemeState) => void;
787
+ getThemeState: () => ThemeState;
788
+ }
789
+
790
+ /**
791
+ * Custom React hook for managing and modifying the theme state ('light', 'dark', or 'system') on the web.
792
+ * It interacts with the browser's `localStorage` and `document.body` to persist and apply theme choices.
793
+ *
794
+ * @returns {UseThemeState} An object containing the following theme control methods:
795
+ * - `setThemeState(mode: ThemeState)`: Sets the theme to the specified mode.
796
+ * - `'light'`: Sets the storage item to `'light'`, removes the dark theme class from the document body, and disables the OS color scheme change listener.
797
+ * - `'dark'`: Sets the storage item to `'dark'`, adds the dark theme class to the document body, and disables the OS color scheme change listener.
798
+ * - `'system'`: Sets the storage item to `'system'`, matches the document body class to the current OS color scheme preference, and registers a listener to react to future OS color scheme preference changes.
799
+ * - `getThemeState()`: Retrieves the stored theme value from `localStorage`. Defaults to `'light'` if empty or invalid.
800
+ */
801
+ declare function useThemeState(): UseThemeState;
802
+
803
+ export { type AuthInterface, Avatar, Button, Canvas, ColorPickerSlider, DropdownSelect, Image$1 as Image, ImageEditor, Image as ImageUtilities, InputFile, InputGoogle, LinearAlgebra, Modern as ModernButton, MultiSelect, OptionSlider, type Options$2 as Options, RangeSlider, SessionProvider, type SessionType, type Options$1 as SliderOptions, type StorageInterface, TextArea, Transform, UploadImage, tools, useAuthClient, useSession, useThemeState };
package/dist/index.d.ts CHANGED
@@ -174,7 +174,10 @@ interface Props$5 {
174
174
  setValue?: (value?: string) => void;
175
175
  label?: string;
176
176
  options?: {
177
- focusColor: string;
177
+ focusColor?: string;
178
+ backgroundColor?: string;
179
+ textColor?: string;
180
+ borderColor?: string;
178
181
  };
179
182
  }
180
183
  /**
@@ -203,7 +206,10 @@ interface Props$4 {
203
206
  setValue?: (value?: string) => void;
204
207
  label?: string;
205
208
  options?: {
206
- focusColor: string;
209
+ focusColor?: string;
210
+ backgroundColor?: string;
211
+ textColor?: string;
212
+ borderColor?: string;
207
213
  };
208
214
  }
209
215
  /**
@@ -774,4 +780,24 @@ declare const LinearAlgebra: {
774
780
  getVectorMagnitude(vector: number[]): number;
775
781
  };
776
782
 
777
- export { type AuthInterface, Avatar, Button, Canvas, ColorPickerSlider, DropdownSelect, Image$1 as Image, ImageEditor, Image as ImageUtilities, InputFile, InputGoogle, LinearAlgebra, Modern as ModernButton, MultiSelect, OptionSlider, type Options$2 as Options, RangeSlider, SessionProvider, type SessionType, type Options$1 as SliderOptions, type StorageInterface, TextArea, Transform, UploadImage, tools, useAuthClient, useSession };
783
+ declare const ThemeStateArray: readonly ["light", "dark", "system"];
784
+ type ThemeState = (typeof ThemeStateArray)[number];
785
+ interface UseThemeState {
786
+ setThemeState: (mode: ThemeState) => void;
787
+ getThemeState: () => ThemeState;
788
+ }
789
+
790
+ /**
791
+ * Custom React hook for managing and modifying the theme state ('light', 'dark', or 'system') on the web.
792
+ * It interacts with the browser's `localStorage` and `document.body` to persist and apply theme choices.
793
+ *
794
+ * @returns {UseThemeState} An object containing the following theme control methods:
795
+ * - `setThemeState(mode: ThemeState)`: Sets the theme to the specified mode.
796
+ * - `'light'`: Sets the storage item to `'light'`, removes the dark theme class from the document body, and disables the OS color scheme change listener.
797
+ * - `'dark'`: Sets the storage item to `'dark'`, adds the dark theme class to the document body, and disables the OS color scheme change listener.
798
+ * - `'system'`: Sets the storage item to `'system'`, matches the document body class to the current OS color scheme preference, and registers a listener to react to future OS color scheme preference changes.
799
+ * - `getThemeState()`: Retrieves the stored theme value from `localStorage`. Defaults to `'light'` if empty or invalid.
800
+ */
801
+ declare function useThemeState(): UseThemeState;
802
+
803
+ export { type AuthInterface, Avatar, Button, Canvas, ColorPickerSlider, DropdownSelect, Image$1 as Image, ImageEditor, Image as ImageUtilities, InputFile, InputGoogle, LinearAlgebra, Modern as ModernButton, MultiSelect, OptionSlider, type Options$2 as Options, RangeSlider, SessionProvider, type SessionType, type Options$1 as SliderOptions, type StorageInterface, TextArea, Transform, UploadImage, tools, useAuthClient, useSession, useThemeState };
package/dist/index.js CHANGED
@@ -90,7 +90,8 @@ __export(index_exports, {
90
90
  UploadImage: () => UploadImage_default,
91
91
  tools: () => tools_default,
92
92
  useAuthClient: () => useAuthClient_default,
93
- useSession: () => useSession
93
+ useSession: () => useSession,
94
+ useThemeState: () => useThemeState
94
95
  });
95
96
  module.exports = __toCommonJS(index_exports);
96
97
 
@@ -1656,7 +1657,8 @@ var others2 = {
1656
1657
  borderFocus: "#0957d0",
1657
1658
  textRelease: "#000",
1658
1659
  textFocus: "#0957d0",
1659
- border: "solid 1px"
1660
+ border: "solid 1px",
1661
+ backgroundColor: "#fff"
1660
1662
  };
1661
1663
  var styles10 = {
1662
1664
  input: {
@@ -1666,7 +1668,9 @@ var styles10 = {
1666
1668
  border: `${others2.border} ${others2.borderRelease}`,
1667
1669
  borderRadius: "6px",
1668
1670
  padding: "10px",
1669
- outline: "none"
1671
+ outline: "none",
1672
+ backgroundColor: others2.backgroundColor,
1673
+ color: others2.textRelease
1670
1674
  },
1671
1675
  container: {
1672
1676
  width: "100%",
@@ -1680,7 +1684,8 @@ var styles10 = {
1680
1684
  transform: "translateY(-50%)",
1681
1685
  transition: "all .1s linear",
1682
1686
  padding: "0px 5px",
1683
- backgroundColor: "white"
1687
+ backgroundColor: others2.backgroundColor,
1688
+ color: others2.textRelease
1684
1689
  }
1685
1690
  };
1686
1691
  var InputGoogle_styles_default = styles10;
@@ -1703,8 +1708,11 @@ var InputGoogle = (_a) => {
1703
1708
  const [isFocus, setFocus] = (0, import_react13.useState)(false);
1704
1709
  const spanRef = (0, import_react13.useRef)(null);
1705
1710
  const inputRef = (0, import_react13.useRef)(null);
1706
- const borderWhenFocused = isFocus ? `${others2.border} ${options ? options.focusColor : others2.borderFocus}` : `${others2.border} ${others2.borderRelease}`;
1707
- const labelColorWhenFocused = isFocus ? `${options ? options.focusColor : others2.textFocus}` : `${others2.textRelease}`;
1711
+ const backgroundColor = (options == null ? void 0 : options.backgroundColor) ? options.backgroundColor : others2.backgroundColor;
1712
+ const textColor = (options == null ? void 0 : options.textColor) ? options.textColor : others2.textRelease;
1713
+ const borderColor = (options == null ? void 0 : options.borderColor) ? options.borderColor : others2.borderRelease;
1714
+ const borderWhenFocused = isFocus ? `${others2.border} ${options ? options.focusColor : others2.borderFocus}` : `${others2.border} ${borderColor}`;
1715
+ const labelColorWhenFocused = isFocus ? `${(options == null ? void 0 : options.focusColor) ? options.focusColor : others2.textFocus}` : `${textColor}`;
1708
1716
  function spanPositionWhenFocused() {
1709
1717
  if (spanRef.current) {
1710
1718
  spanRef.current.style.top = others2.topFocus;
@@ -1746,7 +1754,9 @@ var InputGoogle = (_a) => {
1746
1754
  },
1747
1755
  type: "text",
1748
1756
  style: __spreadProps(__spreadValues({}, InputGoogle_styles_default.input), {
1749
- border: borderWhenFocused
1757
+ border: borderWhenFocused,
1758
+ backgroundColor,
1759
+ color: textColor
1750
1760
  }),
1751
1761
  onFocus,
1752
1762
  onBlur: offFocus
@@ -1757,7 +1767,8 @@ var InputGoogle = (_a) => {
1757
1767
  {
1758
1768
  ref: spanRef,
1759
1769
  style: __spreadProps(__spreadValues({}, InputGoogle_styles_default.label), {
1760
- color: labelColorWhenFocused
1770
+ color: labelColorWhenFocused,
1771
+ backgroundColor
1761
1772
  }),
1762
1773
  onClick: focus,
1763
1774
  children: label
@@ -1781,7 +1792,8 @@ var others3 = {
1781
1792
  borderFocus: "#0957d0",
1782
1793
  textRelease: "#000",
1783
1794
  textFocus: "#0957d0",
1784
- border: "solid 1px"
1795
+ border: "solid 1px",
1796
+ backgroundColor: "#fff"
1785
1797
  };
1786
1798
  var styles11 = {
1787
1799
  input: {
@@ -1792,7 +1804,9 @@ var styles11 = {
1792
1804
  borderRadius: "6px",
1793
1805
  padding: "10px",
1794
1806
  outline: "none",
1795
- resize: "none"
1807
+ resize: "none",
1808
+ backgroundColor: others3.backgroundColor,
1809
+ color: others3.textRelease
1796
1810
  },
1797
1811
  container: {
1798
1812
  width: "100%",
@@ -1808,7 +1822,8 @@ var styles11 = {
1808
1822
  transform: "translateY(-50%)",
1809
1823
  transition: "all .1s linear",
1810
1824
  padding: "0px 5px",
1811
- backgroundColor: "white"
1825
+ backgroundColor: others3.backgroundColor,
1826
+ color: others3.textRelease
1812
1827
  }
1813
1828
  };
1814
1829
  var TextArea_styles_default = styles11;
@@ -1831,8 +1846,11 @@ var TextArea = (_a) => {
1831
1846
  const [isFocus, setFocus] = (0, import_react14.useState)(false);
1832
1847
  const spanRef = (0, import_react14.useRef)(null);
1833
1848
  const inputRef = (0, import_react14.useRef)(null);
1834
- const inputBorder = isFocus ? `${others3.border} ${options ? options.focusColor : others3.borderFocus}` : `${others3.border} ${others3.borderRelease}`;
1835
- const labelTextColor = isFocus ? `${options ? options.focusColor : others3.textFocus}` : `${others3.textRelease}`;
1849
+ const backgroundColor = (options == null ? void 0 : options.backgroundColor) ? options.backgroundColor : others3.backgroundColor;
1850
+ const textColor = (options == null ? void 0 : options.textColor) ? options.textColor : others3.textRelease;
1851
+ const borderColor = (options == null ? void 0 : options.borderColor) ? options.borderColor : others3.borderRelease;
1852
+ const borderWhenFocused = isFocus ? `${others3.border} ${options ? options.focusColor : others3.borderFocus}` : `${others3.border} ${borderColor}`;
1853
+ const labelColorWhenFocused = isFocus ? `${(options == null ? void 0 : options.focusColor) ? options.focusColor : others3.textFocus}` : `${textColor}`;
1836
1854
  function spanPositionWhenFocused() {
1837
1855
  if (spanRef.current) {
1838
1856
  spanRef.current.style.top = others3.topFocus;
@@ -1871,7 +1889,9 @@ var TextArea = (_a) => {
1871
1889
  value,
1872
1890
  onChange: (e) => setValue(e.target.value),
1873
1891
  style: __spreadProps(__spreadValues({}, TextArea_styles_default.input), {
1874
- border: inputBorder
1892
+ border: borderWhenFocused,
1893
+ backgroundColor,
1894
+ color: textColor
1875
1895
  }),
1876
1896
  onFocus,
1877
1897
  onBlur: offFocus
@@ -1882,7 +1902,8 @@ var TextArea = (_a) => {
1882
1902
  {
1883
1903
  ref: spanRef,
1884
1904
  style: __spreadProps(__spreadValues({}, TextArea_styles_default.label), {
1885
- color: labelTextColor
1905
+ color: labelColorWhenFocused,
1906
+ backgroundColor
1886
1907
  }),
1887
1908
  onClick: focus,
1888
1909
  children: label
@@ -3746,6 +3767,52 @@ var LinearAlgebra = {
3746
3767
  }
3747
3768
  };
3748
3769
  var LinearAlgebra_default = LinearAlgebra;
3770
+
3771
+ // src/utilities/theme/types.ts
3772
+ var ThemeStateArray = ["light", "dark", "system"];
3773
+ var config = {
3774
+ localStorageName: "will-theme",
3775
+ bodyClass: "will-dark"
3776
+ };
3777
+
3778
+ // src/utilities/theme/useThemeState.ts
3779
+ function useThemeState() {
3780
+ const { bodyClass, localStorageName } = config;
3781
+ const htmlClassProcess = (isDark) => {
3782
+ isDark ? document.body.classList.add(bodyClass) : document.body.classList.remove(bodyClass);
3783
+ };
3784
+ const themeCallback = (event) => htmlClassProcess(event.matches);
3785
+ const setLightTheme = () => {
3786
+ window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", themeCallback);
3787
+ localStorage.setItem(localStorageName, ThemeStateArray[0]);
3788
+ document.body.classList.remove(bodyClass);
3789
+ };
3790
+ const setDarkTheme = () => {
3791
+ window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", themeCallback);
3792
+ localStorage.setItem(localStorageName, ThemeStateArray[1]);
3793
+ document.body.classList.add(bodyClass);
3794
+ };
3795
+ const setSystem = () => {
3796
+ localStorage.setItem(localStorageName, ThemeStateArray[2]);
3797
+ htmlClassProcess(
3798
+ window.matchMedia("(prefers-color-scheme: dark)").matches
3799
+ );
3800
+ window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", themeCallback);
3801
+ };
3802
+ return {
3803
+ setThemeState: (mode) => {
3804
+ if (mode === "system") return setSystem();
3805
+ if (mode === "light") return setLightTheme();
3806
+ if (mode === "dark") return setDarkTheme();
3807
+ },
3808
+ getThemeState: () => {
3809
+ const theme = localStorage.getItem(localStorageName);
3810
+ if (!theme) return "light";
3811
+ if (!ThemeStateArray.includes(theme)) return "light";
3812
+ return theme;
3813
+ }
3814
+ };
3815
+ }
3749
3816
  // Annotate the CommonJS export names for ESM import in node:
3750
3817
  0 && (module.exports = {
3751
3818
  Avatar,
@@ -3769,5 +3836,6 @@ var LinearAlgebra_default = LinearAlgebra;
3769
3836
  UploadImage,
3770
3837
  tools,
3771
3838
  useAuthClient,
3772
- useSession
3839
+ useSession,
3840
+ useThemeState
3773
3841
  });
package/dist/index.mjs CHANGED
@@ -1617,7 +1617,8 @@ var others2 = {
1617
1617
  borderFocus: "#0957d0",
1618
1618
  textRelease: "#000",
1619
1619
  textFocus: "#0957d0",
1620
- border: "solid 1px"
1620
+ border: "solid 1px",
1621
+ backgroundColor: "#fff"
1621
1622
  };
1622
1623
  var styles10 = {
1623
1624
  input: {
@@ -1627,7 +1628,9 @@ var styles10 = {
1627
1628
  border: `${others2.border} ${others2.borderRelease}`,
1628
1629
  borderRadius: "6px",
1629
1630
  padding: "10px",
1630
- outline: "none"
1631
+ outline: "none",
1632
+ backgroundColor: others2.backgroundColor,
1633
+ color: others2.textRelease
1631
1634
  },
1632
1635
  container: {
1633
1636
  width: "100%",
@@ -1641,7 +1644,8 @@ var styles10 = {
1641
1644
  transform: "translateY(-50%)",
1642
1645
  transition: "all .1s linear",
1643
1646
  padding: "0px 5px",
1644
- backgroundColor: "white"
1647
+ backgroundColor: others2.backgroundColor,
1648
+ color: others2.textRelease
1645
1649
  }
1646
1650
  };
1647
1651
  var InputGoogle_styles_default = styles10;
@@ -1664,8 +1668,11 @@ var InputGoogle = (_a) => {
1664
1668
  const [isFocus, setFocus] = useState9(false);
1665
1669
  const spanRef = useRef8(null);
1666
1670
  const inputRef = useRef8(null);
1667
- const borderWhenFocused = isFocus ? `${others2.border} ${options ? options.focusColor : others2.borderFocus}` : `${others2.border} ${others2.borderRelease}`;
1668
- const labelColorWhenFocused = isFocus ? `${options ? options.focusColor : others2.textFocus}` : `${others2.textRelease}`;
1671
+ const backgroundColor = (options == null ? void 0 : options.backgroundColor) ? options.backgroundColor : others2.backgroundColor;
1672
+ const textColor = (options == null ? void 0 : options.textColor) ? options.textColor : others2.textRelease;
1673
+ const borderColor = (options == null ? void 0 : options.borderColor) ? options.borderColor : others2.borderRelease;
1674
+ const borderWhenFocused = isFocus ? `${others2.border} ${options ? options.focusColor : others2.borderFocus}` : `${others2.border} ${borderColor}`;
1675
+ const labelColorWhenFocused = isFocus ? `${(options == null ? void 0 : options.focusColor) ? options.focusColor : others2.textFocus}` : `${textColor}`;
1669
1676
  function spanPositionWhenFocused() {
1670
1677
  if (spanRef.current) {
1671
1678
  spanRef.current.style.top = others2.topFocus;
@@ -1707,7 +1714,9 @@ var InputGoogle = (_a) => {
1707
1714
  },
1708
1715
  type: "text",
1709
1716
  style: __spreadProps(__spreadValues({}, InputGoogle_styles_default.input), {
1710
- border: borderWhenFocused
1717
+ border: borderWhenFocused,
1718
+ backgroundColor,
1719
+ color: textColor
1711
1720
  }),
1712
1721
  onFocus,
1713
1722
  onBlur: offFocus
@@ -1718,7 +1727,8 @@ var InputGoogle = (_a) => {
1718
1727
  {
1719
1728
  ref: spanRef,
1720
1729
  style: __spreadProps(__spreadValues({}, InputGoogle_styles_default.label), {
1721
- color: labelColorWhenFocused
1730
+ color: labelColorWhenFocused,
1731
+ backgroundColor
1722
1732
  }),
1723
1733
  onClick: focus,
1724
1734
  children: label
@@ -1742,7 +1752,8 @@ var others3 = {
1742
1752
  borderFocus: "#0957d0",
1743
1753
  textRelease: "#000",
1744
1754
  textFocus: "#0957d0",
1745
- border: "solid 1px"
1755
+ border: "solid 1px",
1756
+ backgroundColor: "#fff"
1746
1757
  };
1747
1758
  var styles11 = {
1748
1759
  input: {
@@ -1753,7 +1764,9 @@ var styles11 = {
1753
1764
  borderRadius: "6px",
1754
1765
  padding: "10px",
1755
1766
  outline: "none",
1756
- resize: "none"
1767
+ resize: "none",
1768
+ backgroundColor: others3.backgroundColor,
1769
+ color: others3.textRelease
1757
1770
  },
1758
1771
  container: {
1759
1772
  width: "100%",
@@ -1769,7 +1782,8 @@ var styles11 = {
1769
1782
  transform: "translateY(-50%)",
1770
1783
  transition: "all .1s linear",
1771
1784
  padding: "0px 5px",
1772
- backgroundColor: "white"
1785
+ backgroundColor: others3.backgroundColor,
1786
+ color: others3.textRelease
1773
1787
  }
1774
1788
  };
1775
1789
  var TextArea_styles_default = styles11;
@@ -1792,8 +1806,11 @@ var TextArea = (_a) => {
1792
1806
  const [isFocus, setFocus] = useState10(false);
1793
1807
  const spanRef = useRef9(null);
1794
1808
  const inputRef = useRef9(null);
1795
- const inputBorder = isFocus ? `${others3.border} ${options ? options.focusColor : others3.borderFocus}` : `${others3.border} ${others3.borderRelease}`;
1796
- const labelTextColor = isFocus ? `${options ? options.focusColor : others3.textFocus}` : `${others3.textRelease}`;
1809
+ const backgroundColor = (options == null ? void 0 : options.backgroundColor) ? options.backgroundColor : others3.backgroundColor;
1810
+ const textColor = (options == null ? void 0 : options.textColor) ? options.textColor : others3.textRelease;
1811
+ const borderColor = (options == null ? void 0 : options.borderColor) ? options.borderColor : others3.borderRelease;
1812
+ const borderWhenFocused = isFocus ? `${others3.border} ${options ? options.focusColor : others3.borderFocus}` : `${others3.border} ${borderColor}`;
1813
+ const labelColorWhenFocused = isFocus ? `${(options == null ? void 0 : options.focusColor) ? options.focusColor : others3.textFocus}` : `${textColor}`;
1797
1814
  function spanPositionWhenFocused() {
1798
1815
  if (spanRef.current) {
1799
1816
  spanRef.current.style.top = others3.topFocus;
@@ -1832,7 +1849,9 @@ var TextArea = (_a) => {
1832
1849
  value,
1833
1850
  onChange: (e) => setValue(e.target.value),
1834
1851
  style: __spreadProps(__spreadValues({}, TextArea_styles_default.input), {
1835
- border: inputBorder
1852
+ border: borderWhenFocused,
1853
+ backgroundColor,
1854
+ color: textColor
1836
1855
  }),
1837
1856
  onFocus,
1838
1857
  onBlur: offFocus
@@ -1843,7 +1862,8 @@ var TextArea = (_a) => {
1843
1862
  {
1844
1863
  ref: spanRef,
1845
1864
  style: __spreadProps(__spreadValues({}, TextArea_styles_default.label), {
1846
- color: labelTextColor
1865
+ color: labelColorWhenFocused,
1866
+ backgroundColor
1847
1867
  }),
1848
1868
  onClick: focus,
1849
1869
  children: label
@@ -3707,6 +3727,52 @@ var LinearAlgebra = {
3707
3727
  }
3708
3728
  };
3709
3729
  var LinearAlgebra_default = LinearAlgebra;
3730
+
3731
+ // src/utilities/theme/types.ts
3732
+ var ThemeStateArray = ["light", "dark", "system"];
3733
+ var config = {
3734
+ localStorageName: "will-theme",
3735
+ bodyClass: "will-dark"
3736
+ };
3737
+
3738
+ // src/utilities/theme/useThemeState.ts
3739
+ function useThemeState() {
3740
+ const { bodyClass, localStorageName } = config;
3741
+ const htmlClassProcess = (isDark) => {
3742
+ isDark ? document.body.classList.add(bodyClass) : document.body.classList.remove(bodyClass);
3743
+ };
3744
+ const themeCallback = (event) => htmlClassProcess(event.matches);
3745
+ const setLightTheme = () => {
3746
+ window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", themeCallback);
3747
+ localStorage.setItem(localStorageName, ThemeStateArray[0]);
3748
+ document.body.classList.remove(bodyClass);
3749
+ };
3750
+ const setDarkTheme = () => {
3751
+ window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", themeCallback);
3752
+ localStorage.setItem(localStorageName, ThemeStateArray[1]);
3753
+ document.body.classList.add(bodyClass);
3754
+ };
3755
+ const setSystem = () => {
3756
+ localStorage.setItem(localStorageName, ThemeStateArray[2]);
3757
+ htmlClassProcess(
3758
+ window.matchMedia("(prefers-color-scheme: dark)").matches
3759
+ );
3760
+ window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", themeCallback);
3761
+ };
3762
+ return {
3763
+ setThemeState: (mode) => {
3764
+ if (mode === "system") return setSystem();
3765
+ if (mode === "light") return setLightTheme();
3766
+ if (mode === "dark") return setDarkTheme();
3767
+ },
3768
+ getThemeState: () => {
3769
+ const theme = localStorage.getItem(localStorageName);
3770
+ if (!theme) return "light";
3771
+ if (!ThemeStateArray.includes(theme)) return "light";
3772
+ return theme;
3773
+ }
3774
+ };
3775
+ }
3710
3776
  export {
3711
3777
  Avatar_default as Avatar,
3712
3778
  Button_default as Button,
@@ -3729,5 +3795,6 @@ export {
3729
3795
  UploadImage_default as UploadImage,
3730
3796
  tools_default as tools,
3731
3797
  useAuthClient_default as useAuthClient,
3732
- useSession
3798
+ useSession,
3799
+ useThemeState
3733
3800
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willphan1712000/frontend",
3
- "version": "1.8.3",
3
+ "version": "1.9.1",
4
4
  "description": "Frontend Library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",