vueless 1.2.5-beta.3 → 1.2.5-beta.4

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/constants.d.ts CHANGED
@@ -10,6 +10,7 @@ export const ROUNDING: "rounding";
10
10
  export const DISABLED_OPACITY: "disabled-opacity";
11
11
  export const LETTER_SPACING: "letter-spacing";
12
12
  export const COLOR_MODE_KEY: "vl-color-mode";
13
+ export const AUTO_MODE_KEY: "vl-auto-mode";
13
14
  export const DARK_MODE_CLASS: "vl-dark";
14
15
  export const LIGHT_MODE_CLASS: "vl-light";
15
16
  export const DEFAULT_PRIMARY_COLOR: "grayscale";
package/constants.js CHANGED
@@ -20,6 +20,7 @@ export const LETTER_SPACING = "letter-spacing";
20
20
 
21
21
  /* Vueless color mode keys */
22
22
  export const COLOR_MODE_KEY = "vl-color-mode";
23
+ export const AUTO_MODE_KEY = "vl-auto-mode";
23
24
  export const DARK_MODE_CLASS = "vl-dark";
24
25
  export const LIGHT_MODE_CLASS = "vl-light";
25
26
 
package/index.d.ts CHANGED
@@ -11,9 +11,9 @@ export {
11
11
  createDebounce,
12
12
  hasSlotContent
13
13
  } from "./utils/helper";
14
- export { getStored, getTheme, setTheme, resetTheme, cssVar } from "./utils/theme";
15
14
  export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
16
15
  export { cx, cva, compose, getDefaults, setVuelessConfig, setColor, vuelessConfig } from "./utils/ui";
16
+ export { getTheme, setTheme, resetTheme, normalizeThemeConfig, getStored, cssVar } from "./utils/theme";
17
17
  export { getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
18
18
  /* adapters */
19
19
  export { default as defaultEnLocale } from "./adapter.locale/locales/en";
@@ -130,6 +130,7 @@ export type {
130
130
  ThemeConfigText,
131
131
  ThemeConfigRounding,
132
132
  ThemeConfigOutline,
133
+ MergedThemeConfig,
133
134
  NestedComponent,
134
135
  ComponentConfig,
135
136
  ComponentDefaults,
package/index.ts CHANGED
@@ -17,9 +17,9 @@ export {
17
17
  createDebounce,
18
18
  hasSlotContent
19
19
  } from "./utils/helper";
20
- export { getStored, getTheme, setTheme, resetTheme, cssVar } from "./utils/theme";
21
20
  export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
22
21
  export { cx, cva, compose, getDefaults, setVuelessConfig, setColor, vuelessConfig } from "./utils/ui";
22
+ export { getTheme, setTheme, resetTheme, normalizeThemeConfig, getStored, cssVar } from "./utils/theme";
23
23
  export { getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
24
24
  /* adapters */
25
25
  export { default as defaultEnLocale } from "./adapter.locale/locales/en";
@@ -136,6 +136,7 @@ export type {
136
136
  ThemeConfigText,
137
137
  ThemeConfigRounding,
138
138
  ThemeConfigOutline,
139
+ MergedThemeConfig,
139
140
  NestedComponent,
140
141
  ComponentConfig,
141
142
  ComponentDefaults,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "1.2.5-beta.3",
3
+ "version": "1.2.5-beta.4",
4
4
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
5
5
  "author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
6
6
  "homepage": "https://vueless.com",
package/types.ts CHANGED
@@ -129,6 +129,11 @@ export interface ThemeConfig {
129
129
  */
130
130
  colorMode?: `${ColorMode}`;
131
131
 
132
+ /**
133
+ * Defines the color mode to auto.
134
+ */
135
+ isColorModeAuto?: boolean;
136
+
132
137
  /**
133
138
  * Light theme design system CSS variables.
134
139
  */
@@ -185,6 +190,12 @@ export interface Config extends ThemeConfig {
185
190
  tailwindMerge?: UnknownObject;
186
191
  }
187
192
 
193
+ export type MergedThemeConfig = Omit<ThemeConfig, "text | outline | rounding"> & {
194
+ text: Partial<ThemeConfigText>;
195
+ outline: Partial<ThemeConfigOutline>;
196
+ rounding: Partial<ThemeConfigRounding>;
197
+ };
198
+
188
199
  export type UnknownObject = Record<string, unknown>;
189
200
  export type UnknownArray = unknown[];
190
201
  export type UnknownType = string | number | boolean | UnknownObject | undefined | null;
package/utils/theme.ts CHANGED
@@ -34,6 +34,7 @@ import {
34
34
  DEFAULT_DISABLED_OPACITY,
35
35
  LETTER_SPACING,
36
36
  DEFAULT_LETTER_SPACING,
37
+ AUTO_MODE_KEY,
37
38
  } from "../constants";
38
39
 
39
40
  import type {
@@ -43,6 +44,7 @@ import type {
43
44
  ThemeConfigText,
44
45
  ThemeConfigOutline,
45
46
  ThemeConfigRounding,
47
+ MergedThemeConfig,
46
48
  VuelessCssVariables,
47
49
  } from "../types";
48
50
  import { ColorMode } from "../types";
@@ -59,6 +61,11 @@ declare interface RootCSSVariableOptions {
59
61
  darkTheme: Partial<VuelessCssVariables>;
60
62
  }
61
63
 
64
+ declare interface SetColorMode {
65
+ colorMode: `${ColorMode}`;
66
+ isColorModeAuto: boolean;
67
+ }
68
+
62
69
  /* Creates a media query that checks if the user's system color scheme is set to the dark. */
63
70
  const prefersColorSchemeDark = isCSR && window.matchMedia("(prefers-color-scheme: dark)");
64
71
 
@@ -75,12 +82,18 @@ function toggleColorModeClass() {
75
82
  }
76
83
 
77
84
  /**
78
- * Sets color mode.
79
- * @param {string} mode (dark | light | auto)
80
- * @return {string} current color mode
85
+ * Sets the client-side rendering (CSR) color mode by applying the specified mode,
86
+ * configuring the appropriate event listeners, setting CSS classes, and saving the mode
87
+ * in cookies and local storage.
88
+ *
89
+ * @param {`${ColorMode}`} mode - The desired color mode (dark | light | auto).
90
+ * @return {Object} An object containing:
91
+ * - `colorMode` {string}: The applied color mode (e.g., "light", "dark").
92
+ * - `isColorModeAuto` {boolean}: Indicates whether the color mode is set to auto.
81
93
  */
82
- function setColorMode(mode: `${ColorMode}`): string {
94
+ function setCSRColorMode(mode: `${ColorMode}`): SetColorMode {
83
95
  const colorMode = mode || getStored(COLOR_MODE_KEY) || vuelessConfig.colorMode || ColorMode.Light;
96
+ const isCachedAutoMode = !!Number(getStored(AUTO_MODE_KEY) ?? 0);
84
97
 
85
98
  const isAutoMode = colorMode === ColorMode.Auto;
86
99
  const isSystemDarkMode = isAutoMode && prefersColorSchemeDark && prefersColorSchemeDark?.matches;
@@ -92,7 +105,7 @@ function setColorMode(mode: `${ColorMode}`): string {
92
105
  }
93
106
 
94
107
  /* Adding system color mode change event listener. */
95
- if (isAutoMode && prefersColorSchemeDark) {
108
+ if ((isAutoMode || isCachedAutoMode) && prefersColorSchemeDark) {
96
109
  prefersColorSchemeDark.addEventListener("change", toggleColorModeClass);
97
110
  }
98
111
 
@@ -112,10 +125,35 @@ function setColorMode(mode: `${ColorMode}`): string {
112
125
 
113
126
  if (mode) {
114
127
  setCookie(COLOR_MODE_KEY, currentColorMode);
128
+ setCookie(AUTO_MODE_KEY, String(Number(isAutoMode)));
129
+
115
130
  localStorage.setItem(COLOR_MODE_KEY, currentColorMode);
131
+ localStorage.setItem(AUTO_MODE_KEY, String(Number(isAutoMode)));
116
132
  }
117
133
 
118
- return currentColorMode;
134
+ return {
135
+ colorMode: currentColorMode,
136
+ isColorModeAuto: isAutoMode || isCachedAutoMode,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Gets server-side rendering (SSR) color mode.
142
+ *
143
+ * @param {`${ColorMode}`} mode - The desired color mode (dark | light | auto).
144
+ * @param {boolean} isColorModeAuto - Indicates whether the color mode is set to auto.
145
+ * @return {Object} An object containing:
146
+ * - `colorMode` {string}: The applied color mode (e.g., "light", "dark").
147
+ * - `isColorModeAuto` {boolean}: Indicates whether the color mode is set to auto.
148
+ */
149
+ function getSSRColorMode(mode: `${ColorMode}`, isColorModeAuto: boolean = false): SetColorMode {
150
+ const currentColorMode = mode || vuelessConfig.colorMode || ColorMode.Light;
151
+ const isAutoMode = currentColorMode === ColorMode.Auto;
152
+
153
+ return {
154
+ colorMode: currentColorMode,
155
+ isColorModeAuto: isAutoMode || isColorModeAuto,
156
+ };
119
157
  }
120
158
 
121
159
  /**
@@ -145,6 +183,7 @@ export function resetTheme() {
145
183
  if (!isCSR) return;
146
184
 
147
185
  const themeKeys = [
186
+ AUTO_MODE_KEY,
148
187
  COLOR_MODE_KEY,
149
188
  `vl-${PRIMARY_COLOR}`,
150
189
  `vl-${NEUTRAL_COLOR}`,
@@ -168,26 +207,64 @@ export function resetTheme() {
168
207
  });
169
208
  }
170
209
 
210
+ /**
211
+ * Normalizes the provided theme configuration object into a structured format.
212
+ *
213
+ * @param {object} theme - The theme configuration object to normalize.
214
+ * @return {MergedThemeConfig}
215
+ */
216
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
217
+ export function normalizeThemeConfig(theme: any): MergedThemeConfig {
218
+ return {
219
+ colorMode: theme.colorMode,
220
+ isColorModeAuto: theme.isColorModeAuto,
221
+ primary: theme.primary,
222
+ neutral: theme.neutral,
223
+ text: {
224
+ xs: toNumber(theme.text?.xs),
225
+ sm: toNumber(theme.text?.sm),
226
+ md: toNumber(theme.text?.md),
227
+ lg: toNumber(theme.text?.lg),
228
+ },
229
+ outline: {
230
+ sm: toNumber(theme.outline?.sm),
231
+ md: toNumber(theme.outline?.md),
232
+ lg: toNumber(theme.outline?.lg),
233
+ },
234
+ rounding: {
235
+ sm: toNumber(theme.rounding?.sm),
236
+ md: toNumber(theme.rounding?.md),
237
+ lg: toNumber(theme.rounding?.lg),
238
+ },
239
+ letterSpacing: toNumber(theme.letterSpacing),
240
+ disabledOpacity: toNumber(theme.disabledOpacity),
241
+ };
242
+ }
243
+
171
244
  /**
172
245
  * Retrieves the current theme configuration.
173
246
  * @return ThemeConfig - current theme configuration
174
247
  */
175
- export function getTheme(): ThemeConfig {
176
- const colorMode = getStored(COLOR_MODE_KEY) || vuelessConfig.colorMode || ColorMode.Light;
177
- const primary = getPrimaryColor();
178
- const neutral = getNeutralColor();
248
+ export function getTheme(config?: ThemeConfig): MergedThemeConfig {
249
+ const { colorMode, isColorModeAuto } = isCSR
250
+ ? setCSRColorMode(config?.colorMode as ColorMode)
251
+ : getSSRColorMode(config?.colorMode as ColorMode, config?.isColorModeAuto);
179
252
 
180
- const text = getText();
181
- const outline = getOutlines();
182
- const rounding = getRoundings();
183
- const letterSpacing = getLetterSpacing();
184
- const disabledOpacity = getDisabledOpacity();
253
+ const primary = getPrimaryColor(config?.primary);
254
+ const neutral = getNeutralColor(config?.neutral);
255
+
256
+ const text = getText(config?.text);
257
+ const outline = getOutlines(config?.outline);
258
+ const rounding = getRoundings(config?.rounding);
259
+ const letterSpacing = getLetterSpacing(config?.letterSpacing);
260
+ const disabledOpacity = getDisabledOpacity(config?.disabledOpacity);
185
261
 
186
262
  const lightTheme = merge({}, DEFAULT_LIGHT_THEME, vuelessConfig.lightTheme);
187
263
  const darkTheme = merge({}, DEFAULT_DARK_THEME, vuelessConfig.darkTheme);
188
264
 
189
265
  return {
190
- colorMode: colorMode as `${ColorMode}`,
266
+ colorMode,
267
+ isColorModeAuto,
191
268
  primary,
192
269
  neutral,
193
270
  text,
@@ -206,7 +283,9 @@ export function getTheme(): ThemeConfig {
206
283
  * @return string - CSS variables
207
284
  */
208
285
  export function setTheme(config: ThemeConfig = {}) {
209
- if (isCSR) setColorMode(config.colorMode as ColorMode);
286
+ isCSR
287
+ ? setCSRColorMode(config.colorMode as ColorMode)
288
+ : getSSRColorMode(config.colorMode as ColorMode);
210
289
 
211
290
  const text = getText(config.text);
212
291
  const outline = getOutlines(config.outline);
@@ -664,3 +743,25 @@ function setCSSVariables(
664
743
 
665
744
  return rootVariables;
666
745
  }
746
+
747
+ /**
748
+ * Converts the given value to a number if possible.
749
+ *
750
+ * @param {unknown} value - The value to be converted to a number. Can be of any data type.
751
+ * @return {number | undefined} The numeric representation of the value if conversion is successful; otherwise, undefined.
752
+ */
753
+ function toNumber(value: unknown): number | undefined {
754
+ if (typeof value === "number") {
755
+ return value;
756
+ }
757
+
758
+ if (typeof value === "string" && value.trim() !== "") {
759
+ const number = Number(value);
760
+
761
+ if (!Number.isNaN(number)) {
762
+ return number;
763
+ }
764
+ }
765
+
766
+ return;
767
+ }