vueless 1.2.14 → 1.2.15-beta.2

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.
@@ -15,9 +15,9 @@ import type {
15
15
  UseUI,
16
16
  KeyAttrs,
17
17
  KeysAttrs,
18
+ StateColors,
18
19
  MutatedProps,
19
20
  UnknownObject,
20
- PrimaryColors,
21
21
  ComponentNames,
22
22
  NestedComponent,
23
23
  ComponentDefaults,
@@ -72,7 +72,7 @@ export default function useUI<T>(
72
72
  return computed(() => {
73
73
  const mutatedPropsValue = toValue(mutatedProps);
74
74
  const value = (config.value as ComponentConfigFull<T>)[key];
75
- const color = (toValue(mutatedProps || {}).color || props.color) as PrimaryColors;
75
+ const color = (toValue(mutatedProps || {}).color || props.color) as StateColors;
76
76
 
77
77
  const isNestedComponent = Boolean(getNestedComponent(value));
78
78
 
package/index.d.ts CHANGED
@@ -149,8 +149,11 @@ export type {
149
149
  CreateVuelessOptions,
150
150
  /* Color and theme types */
151
151
  StateColors,
152
+ ColorShades,
152
153
  PrimaryColors,
153
154
  NeutralColors,
155
+ PrimaryColorName,
156
+ NeutralColorName,
154
157
  VuelessCssVariables,
155
158
  /* Component and Directive types */
156
159
  Directives,
package/index.ts CHANGED
@@ -155,8 +155,11 @@ export type {
155
155
  CreateVuelessOptions,
156
156
  /* Color and theme types */
157
157
  StateColors,
158
+ ColorShades,
158
159
  PrimaryColors,
159
160
  NeutralColors,
161
+ PrimaryColorName,
162
+ NeutralColorName,
160
163
  VuelessCssVariables,
161
164
  /* Component and Directive types */
162
165
  Directives,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "1.2.14",
3
+ "version": "1.2.15-beta.2",
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
@@ -220,8 +220,22 @@ export type StateColors =
220
220
  | "grayscale"
221
221
  | string;
222
222
 
223
- export type NeutralColors = "slate" | "gray" | "zinc" | "neutral" | "stone" | string;
224
- export type PrimaryColors =
223
+ export interface ColorShades {
224
+ 50: string;
225
+ 100: string;
226
+ 200: string;
227
+ 300: string;
228
+ 400: string;
229
+ 500: string;
230
+ 600: string;
231
+ 700: string;
232
+ 800: string;
233
+ 900: string;
234
+ 950: string;
235
+ }
236
+
237
+ export type NeutralColorName = "slate" | "gray" | "zinc" | "neutral" | "stone" | string;
238
+ export type PrimaryColorName =
225
239
  | "red"
226
240
  | "orange"
227
241
  | "amber"
@@ -241,6 +255,9 @@ export type PrimaryColors =
241
255
  | "rose"
242
256
  | string;
243
257
 
258
+ export type NeutralColors = NeutralColorName | Partial<ColorShades>;
259
+ export type PrimaryColors = PrimaryColorName | Partial<ColorShades>;
260
+
244
261
  export interface Directives {
245
262
  tooltip?: Partial<Props>;
246
263
  clickOutside?: Partial<Props>;
@@ -1,6 +1,6 @@
1
1
  import defaultConfig from "./config";
2
2
 
3
- import type { PrimaryColors, NeutralColors, ComponentConfig } from "../types";
3
+ import type { PrimaryColorName, NeutralColorName, ComponentConfig } from "../types";
4
4
 
5
5
  export type Config = typeof defaultConfig;
6
6
 
@@ -18,12 +18,12 @@ export interface Props {
18
18
  /**
19
19
  * Color list.
20
20
  */
21
- colors?: Record<PrimaryColors, string>;
21
+ colors?: Record<PrimaryColorName, string>;
22
22
 
23
23
  /**
24
24
  * Color labels.
25
25
  */
26
- labels?: Record<NeutralColors, string>;
26
+ labels?: Record<NeutralColorName, string>;
27
27
 
28
28
  /**
29
29
  * Unique element id.
@@ -1,6 +1,6 @@
1
1
  import defaultConfig from "./config";
2
2
 
3
- import type { PrimaryColors, NeutralColors, ComponentConfig } from "../types";
3
+ import type { PrimaryColorName, NeutralColorName, ComponentConfig } from "../types";
4
4
 
5
5
  export type Config = typeof defaultConfig;
6
6
 
@@ -8,12 +8,12 @@ export interface Props {
8
8
  /**
9
9
  * Selected primary color (v-model).
10
10
  */
11
- primary?: PrimaryColors;
11
+ primary?: PrimaryColorName;
12
12
 
13
13
  /**
14
14
  * Selected neutral color (v-model).
15
15
  */
16
- neutral?: NeutralColors;
16
+ neutral?: NeutralColorName;
17
17
 
18
18
  /**
19
19
  * Component size.
@@ -23,22 +23,22 @@ export interface Props {
23
23
  /**
24
24
  * Primary color list.
25
25
  */
26
- primaryColors?: Record<PrimaryColors, string>;
26
+ primaryColors?: Record<PrimaryColorName, string>;
27
27
 
28
28
  /**
29
29
  * Gray color list.
30
30
  */
31
- neutralColors?: Record<NeutralColors, string>;
31
+ neutralColors?: Record<NeutralColorName, string>;
32
32
 
33
33
  /**
34
34
  * Primary color labels.
35
35
  */
36
- primaryLabels?: Record<PrimaryColors, string>;
36
+ primaryLabels?: Record<PrimaryColorName, string>;
37
37
 
38
38
  /**
39
39
  * Gray color labels.
40
40
  */
41
- neutralLabels?: Record<NeutralColors, string>;
41
+ neutralLabels?: Record<NeutralColorName, string>;
42
42
 
43
43
  /**
44
44
  * Unique element id.
package/utils/theme.ts CHANGED
@@ -45,6 +45,7 @@ import type {
45
45
  NeutralColors,
46
46
  PrimaryColors,
47
47
  ThemeConfig,
48
+ ColorShades,
48
49
  ThemeConfigText,
49
50
  ThemeConfigOutline,
50
51
  ThemeConfigRounding,
@@ -359,6 +360,40 @@ export function normalizeThemeConfig(theme: any): MergedThemeConfig {
359
360
  };
360
361
  }
361
362
 
363
+ function isColorShadesObject(color: unknown): color is Partial<ColorShades> {
364
+ return typeof color === "object" && color !== null && !Array.isArray(color);
365
+ }
366
+
367
+ function validateColorShades(color: Partial<ColorShades>, colorType: string) {
368
+ const validShades = COLOR_SHADES;
369
+ const providedShades = Object.keys(color);
370
+
371
+ const invalidShades = providedShades.filter((shade) => !validShades.includes(Number(shade)));
372
+ const missingShades = validShades.filter((shade) => !(String(shade) in color));
373
+
374
+ if (invalidShades.length > 0) {
375
+ const invalidShadesStr = invalidShades.join(", ");
376
+ const validShadesStr = validShades.join(", ");
377
+
378
+ // eslint-disable-next-line no-console
379
+ console.warn(
380
+ `[vueless] Invalid shade keys found in ${colorType} color object: ${invalidShadesStr}. ` +
381
+ `Valid shades are: ${validShadesStr}.`,
382
+ );
383
+ }
384
+
385
+ if (missingShades.length > 0) {
386
+ const missingShadesStr = missingShades.join(", ");
387
+ const validShadesStr = validShades.join(", ");
388
+
389
+ // eslint-disable-next-line no-console
390
+ console.warn(
391
+ `[vueless] Missing shade keys in ${colorType} color object: ${missingShadesStr}. ` +
392
+ `All shades (${validShadesStr}) should be defined.`,
393
+ );
394
+ }
395
+ }
396
+
362
397
  /**
363
398
  * Determines if the provided color mode configuration has a primary color
364
399
  * that differs from the default color mode configuration.
@@ -377,13 +412,35 @@ function hasPrimaryColor(
377
412
 
378
413
  /**
379
414
  * Retrieve primary color value and save them to cookie and localStorage.
380
- * @return string - primary color.
415
+ * @return string | Partial<ColorShades> - primary color.
381
416
  */
382
417
  function getPrimaryColor(primary?: PrimaryColors) {
383
418
  const storageKey = `vl-${PRIMARY_COLOR}`;
384
419
 
420
+ const storedValue = getStored(storageKey);
421
+ let parsedStoredValue: PrimaryColors | undefined;
422
+
423
+ if (storedValue) {
424
+ try {
425
+ parsedStoredValue = JSON.parse(storedValue);
426
+ } catch {
427
+ parsedStoredValue = storedValue;
428
+ }
429
+ }
430
+
385
431
  let primaryColor: PrimaryColors =
386
- primary ?? getStored(storageKey) ?? vuelessConfig.primary ?? DEFAULT_PRIMARY_COLOR;
432
+ primary ?? parsedStoredValue ?? vuelessConfig.primary ?? DEFAULT_PRIMARY_COLOR;
433
+
434
+ if (isColorShadesObject(primaryColor)) {
435
+ validateColorShades(primaryColor, PRIMARY_COLOR);
436
+
437
+ if (isCSR && primary) {
438
+ setCookie(storageKey, JSON.stringify(primaryColor));
439
+ localStorage.setItem(storageKey, JSON.stringify(primaryColor));
440
+ }
441
+
442
+ return primaryColor;
443
+ }
387
444
 
388
445
  const isPrimaryColor =
389
446
  PRIMARY_COLORS.some((color) => color === primaryColor) || primaryColor === GRAYSCALE_COLOR;
@@ -405,13 +462,35 @@ function getPrimaryColor(primary?: PrimaryColors) {
405
462
 
406
463
  /**
407
464
  * Retrieve neutral color value and save them to cookie and localStorage.
408
- * @return string - neutral color.
465
+ * @return string | Partial<ColorShades> - neutral color.
409
466
  */
410
467
  function getNeutralColor(neutral?: NeutralColors) {
411
468
  const storageKey = `vl-${NEUTRAL_COLOR}`;
412
469
 
470
+ const storedValue = getStored(storageKey);
471
+ let parsedStoredValue: NeutralColors | undefined;
472
+
473
+ if (storedValue) {
474
+ try {
475
+ parsedStoredValue = JSON.parse(storedValue);
476
+ } catch {
477
+ parsedStoredValue = storedValue;
478
+ }
479
+ }
480
+
413
481
  let neutralColor: NeutralColors =
414
- neutral ?? getStored(storageKey) ?? vuelessConfig.neutral ?? DEFAULT_NEUTRAL_COLOR;
482
+ neutral ?? parsedStoredValue ?? vuelessConfig.neutral ?? DEFAULT_NEUTRAL_COLOR;
483
+
484
+ if (isColorShadesObject(neutralColor)) {
485
+ validateColorShades(neutralColor, NEUTRAL_COLOR);
486
+
487
+ if (isCSR && neutral) {
488
+ setCookie(storageKey, JSON.stringify(neutralColor));
489
+ localStorage.setItem(storageKey, JSON.stringify(neutralColor));
490
+ }
491
+
492
+ return neutralColor;
493
+ }
415
494
 
416
495
  const isNeutralColor = NEUTRAL_COLORS.some((color) => color === neutralColor);
417
496
 
@@ -733,14 +812,34 @@ export function setRootCSSVariables(vars: MergedThemeConfig) {
733
812
  "--vl-disabled-opacity": `${vars.disabledOpacity}%`,
734
813
  };
735
814
 
736
- for (const shade of COLOR_SHADES) {
737
- variables[`--vl-${PRIMARY_COLOR}-${shade}` as keyof VuelessCssVariables] =
738
- `var(--color-${vars.primary}-${shade})`;
815
+ if (isColorShadesObject(vars.primary)) {
816
+ for (const shade of COLOR_SHADES) {
817
+ const shadeValue = vars.primary[shade as keyof ColorShades];
818
+
819
+ if (shadeValue) {
820
+ variables[`--vl-${PRIMARY_COLOR}-${shade}` as keyof VuelessCssVariables] = shadeValue;
821
+ }
822
+ }
823
+ } else {
824
+ for (const shade of COLOR_SHADES) {
825
+ variables[`--vl-${PRIMARY_COLOR}-${shade}` as keyof VuelessCssVariables] =
826
+ `var(--color-${vars.primary}-${shade})`;
827
+ }
739
828
  }
740
829
 
741
- for (const shade of COLOR_SHADES) {
742
- variables[`--vl-${NEUTRAL_COLOR}-${shade}` as keyof VuelessCssVariables] =
743
- `var(--color-${vars.neutral}-${shade})`;
830
+ if (isColorShadesObject(vars.neutral)) {
831
+ for (const shade of COLOR_SHADES) {
832
+ const shadeValue = vars.neutral[shade as keyof ColorShades];
833
+
834
+ if (shadeValue) {
835
+ variables[`--vl-${NEUTRAL_COLOR}-${shade}` as keyof VuelessCssVariables] = shadeValue;
836
+ }
837
+ }
838
+ } else {
839
+ for (const shade of COLOR_SHADES) {
840
+ variables[`--vl-${NEUTRAL_COLOR}-${shade}` as keyof VuelessCssVariables] =
841
+ `var(--color-${vars.neutral}-${shade})`;
842
+ }
744
843
  }
745
844
 
746
845
  const [light, dark] = generateCSSColorVariables(vars.lightTheme ?? {}, vars.darkTheme ?? {});
@@ -799,10 +898,14 @@ function setCSSVariables(
799
898
  `;
800
899
 
801
900
  if (isCSR) {
901
+ const firstStyleOrLink = document.querySelector("link[rel='stylesheet'], style");
802
902
  const style = document.createElement("style");
803
903
 
804
904
  style.innerHTML = rootVariables;
805
- document.head.appendChild(style);
905
+
906
+ firstStyleOrLink && firstStyleOrLink.parentNode
907
+ ? firstStyleOrLink.parentNode.insertBefore(style, firstStyleOrLink)
908
+ : document.head.appendChild(style);
806
909
  }
807
910
 
808
911
  return rootVariables;