vueless 0.0.478-beta.3 → 0.0.479

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.
Files changed (102) hide show
  1. package/composables/useBreakpoint.js +1 -1
  2. package/composables/useUI.js +1 -204
  3. package/constants.js +1 -2
  4. package/directives/clickOutside/vClickOutside.js +2 -2
  5. package/directives/tooltip/storybook/stories.js +5 -5
  6. package/{index.ts → index.js} +7 -10
  7. package/package.json +18 -28
  8. package/preset.tailwind.js +7 -16
  9. package/ui.button/config.js +0 -12
  10. package/ui.button/storybook/stories.js +2 -2
  11. package/ui.button-link/ULink.vue +1 -1
  12. package/ui.button-link/config.js +0 -9
  13. package/ui.button-link/storybook/stories.js +1 -1
  14. package/ui.button-toggle/storybook/stories.js +1 -1
  15. package/ui.container-divider/storybook/stories.js +1 -1
  16. package/ui.container-modal/storybook/stories.js +1 -1
  17. package/ui.container-modal-confirm/storybook/stories.js +1 -1
  18. package/ui.container-row/storybook/stories.js +1 -1
  19. package/ui.data-list/UDataList.vue +4 -4
  20. package/ui.data-table/UTable.vue +1 -1
  21. package/ui.dropdown-badge/config.js +0 -1
  22. package/ui.dropdown-badge/storybook/stories.js +1 -1
  23. package/ui.dropdown-button/config.js +0 -1
  24. package/ui.dropdown-button/storybook/stories.js +2 -2
  25. package/ui.dropdown-link/storybook/stories.js +1 -1
  26. package/ui.dropdown-list/storybook/stories.js +1 -1
  27. package/ui.form-checkbox/config.js +0 -9
  28. package/ui.form-checkbox/storybook/stories.js +1 -1
  29. package/ui.form-checkbox-group/storybook/stories.js +1 -1
  30. package/ui.form-checkbox-multi-state/storybook/stories.js +1 -1
  31. package/ui.form-color-picker/config.js +0 -7
  32. package/ui.form-color-picker/storybook/stories.js +1 -1
  33. package/ui.form-date-picker/storybook/stories.js +2 -2
  34. package/ui.form-date-picker-range/storybook/stories.js +1 -1
  35. package/ui.form-input/UInput.vue +1 -1
  36. package/ui.form-input/storybook/stories.js +1 -1
  37. package/ui.form-input-file/storybook/stories.js +1 -1
  38. package/ui.form-input-money/storybook/stories.js +1 -1
  39. package/ui.form-input-money/useFormatCurrency.js +1 -1
  40. package/ui.form-input-number/UInputNumber.vue +3 -4
  41. package/ui.form-input-number/storybook/stories.js +1 -1
  42. package/ui.form-input-rating/storybook/stories.js +1 -1
  43. package/ui.form-input-search/storybook/stories.js +1 -1
  44. package/ui.form-label/config.js +2 -2
  45. package/ui.form-label/storybook/stories.js +1 -1
  46. package/ui.form-radio/config.js +0 -6
  47. package/ui.form-radio-group/storybook/stories.js +1 -1
  48. package/ui.form-select/storybook/stories.js +1 -1
  49. package/ui.form-switch/config.js +0 -6
  50. package/ui.form-switch/storybook/stories.js +1 -1
  51. package/ui.form-textarea/storybook/stories.js +1 -1
  52. package/ui.image-avatar/config.js +0 -5
  53. package/ui.image-avatar/storybook/stories.js +1 -1
  54. package/ui.image-icon/UIcon.vue +14 -5
  55. package/ui.image-icon/config.js +0 -5
  56. package/ui.image-icon/storybook/stories.js +1 -1
  57. package/ui.loader/config.js +0 -1
  58. package/ui.loader/storybook/stories.js +1 -1
  59. package/ui.loader-overlay/config.js +0 -1
  60. package/ui.loader-progress/config.js +0 -1
  61. package/ui.loader-progress/storybook/stories.js +1 -1
  62. package/ui.navigation-progress/config.js +0 -9
  63. package/ui.navigation-progress/storybook/stories.js +1 -1
  64. package/ui.navigation-tabs/storybook/stories.js +1 -1
  65. package/ui.other-dot/config.js +0 -1
  66. package/ui.other-dot/storybook/stories.js +1 -1
  67. package/ui.text-alert/config.js +0 -7
  68. package/ui.text-alert/storybook/stories.js +1 -1
  69. package/ui.text-badge/config.js +0 -8
  70. package/ui.text-badge/storybook/stories.js +2 -10
  71. package/ui.text-block/UText.vue +62 -18
  72. package/ui.text-block/storybook/Docs.mdx +3 -3
  73. package/ui.text-block/storybook/{stories.ts → stories.js} +8 -13
  74. package/ui.text-block/useAttrs.js +15 -0
  75. package/ui.text-empty/storybook/stories.js +1 -1
  76. package/ui.text-file/UFile.vue +15 -12
  77. package/ui.text-file/config.js +2 -12
  78. package/ui.text-files/config.js +1 -1
  79. package/ui.text-header/config.js +0 -1
  80. package/ui.text-header/storybook/stories.js +1 -1
  81. package/ui.text-money/config.js +0 -1
  82. package/ui.text-money/storybook/stories.js +1 -1
  83. package/ui.text-money/utilMoney.js +2 -2
  84. package/{utilsTs/utilTheme.ts → utils/utilTheme.js} +27 -31
  85. package/utils/utilUI.js +205 -1
  86. package/web-types.json +9580 -0
  87. package/composablesTs/useAutoPosition.ts +0 -115
  88. package/composablesTs/useBreakpoint.ts +0 -106
  89. package/composablesTs/useLocale.ts +0 -25
  90. package/composablesTs/useMutationObserver.ts +0 -50
  91. package/composablesTs/useUI.ts +0 -557
  92. package/constants.ts +0 -73
  93. package/types.ts +0 -223
  94. package/ui.text-block/types.ts +0 -33
  95. package/ui.text-block/useAttrs.ts +0 -20
  96. package/utilsTs/utilHelper.ts +0 -68
  97. package/utilsTs/utilPlatform.ts +0 -53
  98. package/utilsTs/utilStorybook.ts +0 -296
  99. package/utilsTs/utilTailwind.ts +0 -38
  100. package/utilsTs/utilUI.ts +0 -143
  101. /package/ui.text-block/{config.ts → config.js} +0 -0
  102. /package/ui.text-block/{constants.ts → constants.js} +0 -0
@@ -1,27 +1,71 @@
1
- <script lang="ts" setup>
2
- import { getDefault } from "../utilsTs/utilUI.ts";
1
+ <template>
2
+ <div v-bind="wrapperAttrs" :data-test="dataTest">
3
+ <!-- @slot Use it to add something inside. -->
4
+ <div v-if="!hasSlotContent($slots['default'])" v-bind="htmlAttrs" v-html="html" />
5
+ <slot />
6
+ </div>
7
+ </template>
3
8
 
4
- import { UText } from "./constants.ts";
5
- import defaultConfig from "./config.ts";
6
- import useAttrs from "./useAttrs.ts";
9
+ <script setup>
10
+ import { getDefault } from "../utils/utilUI.js";
7
11
 
8
- import type { UTextProps } from "./types.ts";
12
+ import { UText } from "./constants.js";
13
+ import defaultConfig from "./config.js";
14
+ import useAttrs from "./useAttrs.js";
9
15
 
10
16
  defineOptions({ inheritAttrs: false });
11
17
 
12
- const props = withDefaults(defineProps<UTextProps>(), {
13
- size: getDefault<UTextProps>(defaultConfig, UText).size,
14
- align: getDefault<UTextProps>(defaultConfig, UText).align,
15
- line: getDefault<UTextProps>(defaultConfig, UText).line,
18
+ const props = defineProps({
19
+ /**
20
+ * НTML markdown or plain text.
21
+ */
22
+ html: {
23
+ type: String,
24
+ default: undefined,
25
+ },
26
+
27
+ /**
28
+ * Text size.
29
+ * @values sm, md, lg
30
+ */
31
+ size: {
32
+ type: String,
33
+ default: getDefault(defaultConfig, UText).size,
34
+ },
35
+
36
+ /**
37
+ * Text align.
38
+ * @values left, center, right
39
+ */
40
+ align: {
41
+ type: String,
42
+ default: getDefault(defaultConfig, UText).align,
43
+ },
44
+
45
+ /**
46
+ * Remove line height (useful for 1-line text).
47
+ */
48
+ line: {
49
+ type: Boolean,
50
+ default: getDefault(defaultConfig, UText).line,
51
+ },
52
+
53
+ /**
54
+ * Component config object.
55
+ */
56
+ config: {
57
+ type: Object,
58
+ default: () => ({}),
59
+ },
60
+
61
+ /**
62
+ * Data-test attribute for automated testing.
63
+ */
64
+ dataTest: {
65
+ type: String,
66
+ default: "",
67
+ },
16
68
  });
17
69
 
18
70
  const { wrapperAttrs, htmlAttrs, hasSlotContent } = useAttrs(props);
19
71
  </script>
20
-
21
- <template>
22
- <div v-bind="wrapperAttrs" :data-test="dataTest">
23
- <!-- @slot Use it to add something inside. -->
24
- <div v-if="!hasSlotContent($slots['default'])" v-bind="htmlAttrs" v-html="html" />
25
- <slot />
26
- </div>
27
- </template>
@@ -1,8 +1,8 @@
1
1
  import { Meta, Title, Subtitle, Description, Primary, Controls, Stories, Source } from "@storybook/blocks";
2
- import { getSource } from "../../utilsTs/utilStorybook.ts";
2
+ import { getSource } from "../../utils/utilStorybook.js";
3
3
 
4
- import * as stories from "./stories.ts";
5
- import defaultConfig from "../config.ts?raw"
4
+ import * as stories from "./stories.js";
5
+ import defaultConfig from "../config.js?raw"
6
6
 
7
7
  <Meta of={stories} />
8
8
  <Title of={stories} />
@@ -1,16 +1,8 @@
1
- import type { Meta, StoryFn } from "@storybook/vue3";
2
- import { getArgTypes, getSlotNames, getSlotsFragment } from "../../utilsTs/utilStorybook.ts";
1
+ import { getArgTypes, getSlotNames, getSlotsFragment } from "../../utils/utilStorybook.js";
3
2
 
4
3
  import UText from "../../ui.text-block/UText.vue";
5
4
  import URow from "../../ui.container-row/URow.vue";
6
5
 
7
- import type { UTextProps } from "../types.ts";
8
-
9
- interface UTextArgs extends UTextProps {
10
- slotTemplate?: string;
11
- enum: "size";
12
- }
13
-
14
6
  /**
15
7
  * The `UText` component. | [View on GitHub](https://github.com/vuelessjs/vueless/tree/main/src/ui.text-block)
16
8
  */
@@ -22,7 +14,7 @@ export default {
22
14
  ...getArgTypes(UText.__name),
23
15
  },
24
16
  args: {},
25
- } as Meta;
17
+ };
26
18
 
27
19
  const defaultTemplate = `
28
20
  <p>
@@ -32,7 +24,7 @@ const defaultTemplate = `
32
24
  </p>
33
25
  `;
34
26
 
35
- const DefaultTemplate: StoryFn<UTextArgs> = (args: UTextArgs) => ({
27
+ const DefaultTemplate = (args) => ({
36
28
  components: { UText, URow },
37
29
  setup() {
38
30
  const slots = getSlotNames(UText.__name);
@@ -46,12 +38,12 @@ const DefaultTemplate: StoryFn<UTextArgs> = (args: UTextArgs) => ({
46
38
  `,
47
39
  });
48
40
 
49
- const EnumVariantTemplate: StoryFn<UTextArgs> = (args: UTextArgs, { argTypes }) => ({
41
+ const EnumVariantTemplate = (args, { argTypes } = {}) => ({
50
42
  components: { UText, URow },
51
43
  setup() {
52
44
  return {
53
45
  args,
54
- options: argTypes?.[args.enum]?.options || [],
46
+ options: argTypes[args.enum].options,
55
47
  };
56
48
  },
57
49
  template: `
@@ -96,6 +88,7 @@ Paragraphs.args = {
96
88
  rapidly changing landscape and emerge stronger and more competitive.
97
89
  </p>
98
90
  </template>
91
+
99
92
  `,
100
93
  };
101
94
 
@@ -109,6 +102,8 @@ List.args = {
109
102
  <li>Update your profile information</li>
110
103
  <li>Check your email for updates</li>
111
104
  </ul>
105
+
106
+
112
107
  <ol>
113
108
  <li>Create an account by signing up</li>
114
109
  <li>Verify your email address</li>
@@ -0,0 +1,15 @@
1
+ import useUI from "../composables/useUI.js";
2
+
3
+ import defaultConfig from "./config.js";
4
+
5
+ export default function useAttrs(props) {
6
+ const { config, getKeysAttrs, hasSlotContent } = useUI(defaultConfig, () => props.config);
7
+
8
+ const keysAttrs = getKeysAttrs();
9
+
10
+ return {
11
+ config,
12
+ ...keysAttrs,
13
+ hasSlotContent,
14
+ };
15
+ }
@@ -35,7 +35,7 @@ const DefaultTemplate = (args) => ({
35
35
  `,
36
36
  });
37
37
 
38
- const EnumVariantTemplate = (args, { argTypes }) => ({
38
+ const EnumVariantTemplate = (args, { argTypes } = {}) => ({
39
39
  components: { UEmpty, URow },
40
40
  setup() {
41
41
  return {
@@ -23,14 +23,16 @@
23
23
  </slot>
24
24
 
25
25
  <slot name="right" :file="{ elementId, label, url, imageUrl }">
26
- <UIcon
26
+ <UButton
27
27
  v-if="removable"
28
- internal
29
- interactive
30
- color="gray"
31
- :size="removeIconSize"
32
- :name="config.defaults.removeIcon"
33
- v-bind="removeIconAttrs"
28
+ round
29
+ filled
30
+ square
31
+ no-ring
32
+ variant="thirdary"
33
+ :size="removeButtonSize"
34
+ :icon="config.defaults.removeIcon"
35
+ v-bind="removeButtonAttrs"
34
36
  :data-test="`${dataTest}-remove-item`"
35
37
  @click.stop.prevent="onRemove"
36
38
  />
@@ -43,6 +45,7 @@ import { computed, ref, useId } from "vue";
43
45
 
44
46
  import ULink from "../ui.button-link/ULink.vue";
45
47
  import UIcon from "../ui.image-icon/UIcon.vue";
48
+ import UButton from "../ui.button/UButton.vue";
46
49
 
47
50
  import { getDefault } from "../utils/utilUI.js";
48
51
 
@@ -138,20 +141,20 @@ const {
138
141
  fileIconAttrs,
139
142
  fileLabelAttrs,
140
143
  fileImageAttrs,
141
- removeIconAttrs,
144
+ removeButtonAttrs,
142
145
  } = useAttrs(props);
143
146
 
144
147
  const iconSize = computed(() => {
145
148
  const sizes = {
146
- sm: "xs",
147
- md: "sm",
148
- lg: "md",
149
+ sm: "2xs",
150
+ md: "xs",
151
+ lg: "sm",
149
152
  };
150
153
 
151
154
  return sizes[props.size];
152
155
  });
153
156
 
154
- const removeIconSize = computed(() => {
157
+ const removeButtonSize = computed(() => {
155
158
  const sizes = {
156
159
  sm: "2xs",
157
160
  md: "xs",
@@ -1,15 +1,5 @@
1
1
  export default /*tw*/ {
2
- file: {
3
- component: "{ULink}",
4
- base: "flex items-center",
5
- variants: {
6
- size: {
7
- sm: "gap-0.5",
8
- md: "gap-1",
9
- lg: "gap-1.5",
10
- },
11
- },
12
- },
2
+ file: "{ULink}",
13
3
  body: {
14
4
  base: "flex items-center",
15
5
  variants: {
@@ -23,7 +13,7 @@ export default /*tw*/ {
23
13
  fileImage: "rounded-sm max-w-7",
24
14
  fileIcon: "{UIcon}",
25
15
  fileLabel: "{ULink}",
26
- removeIcon: "{UIcon}",
16
+ removeButton: "{UButton} ml-2",
27
17
  defaults: {
28
18
  size: "md",
29
19
  /* icons */
@@ -1,6 +1,6 @@
1
1
  export default /*tw*/ {
2
2
  filesLabel: "{ULabel}",
3
- items: "flex flex-col gap-1.5",
3
+ items: "flex flex-col gap-1",
4
4
  item: "{UFile} block",
5
5
  defaults: {
6
6
  size: "md",
@@ -40,5 +40,4 @@ export default /*tw*/ {
40
40
  line: true,
41
41
  underlined: false,
42
42
  },
43
- safelist: (colors) => [{ pattern: `text-(${colors})-600` }, { pattern: `border-(${colors})-600` }],
44
43
  };
@@ -32,7 +32,7 @@ const DefaultTemplate = (args) => ({
32
32
  `,
33
33
  });
34
34
 
35
- const EnumVariantTemplate = (args, { argTypes }) => ({
35
+ const EnumVariantTemplate = (args, { argTypes } = {}) => ({
36
36
  components: { UHeader, UCol },
37
37
  setup() {
38
38
  return {
@@ -61,5 +61,4 @@ export default /*tw*/ {
61
61
  integer: false,
62
62
  symbolDivided: true,
63
63
  },
64
- safelist: (colors) => [{ pattern: `text-(${colors})-600` }],
65
64
  };
@@ -46,7 +46,7 @@ const DefaultTemplate = (args) => ({
46
46
  `,
47
47
  });
48
48
 
49
- const EnumVariantTemplate = (args, { argTypes }) => ({
49
+ const EnumVariantTemplate = (args, { argTypes } = {}) => ({
50
50
  components: { UMoney, URow },
51
51
  setup() {
52
52
  const slots = getSlotNames(UMoney.__name);
@@ -16,8 +16,8 @@ export function separatedMoney(
16
16
  thousandsSeparator = " ",
17
17
  ) {
18
18
  const options = {
19
- minimumFractionDigits: Number(minFractionDigits),
20
- maximumFractionDigits: Number(maxFractionDigits),
19
+ minimumFractionDigits: minFractionDigits,
20
+ maximumFractionDigits: maxFractionDigits,
21
21
  };
22
22
 
23
23
  const formattedMoney =
@@ -1,26 +1,23 @@
1
- import { fullTailwindConfig } from "./utilTailwind.ts";
2
- import { isSSR, isCSR } from "./utilHelper.ts";
3
- import { vuelessConfig } from "./utilUI.ts";
1
+ import colors from "tailwindcss/colors.js";
2
+
3
+ import { vuelessConfig } from "./utilUI.js";
4
+ import { isSSR, isCSR } from "./utilHelper.js";
4
5
  import {
6
+ GRAY_COLOR,
7
+ COOL_COLOR,
5
8
  BRAND_COLORS,
6
9
  GRAYSCALE_COLOR,
7
10
  DEFAULT_RING,
8
11
  DEFAULT_RING_OFFSET,
12
+ DEFAULT_RING_OFFSET_COLOR_LIGHT,
13
+ DEFAULT_RING_OFFSET_COLOR_DARK,
9
14
  DEFAULT_ROUNDING,
10
15
  DEFAULT_BRAND_COLOR,
11
16
  DEFAULT_GRAY_COLOR,
12
- DEFAULT_RING_OFFSET_COLOR_LIGHT,
13
- DEFAULT_RING_OFFSET_COLOR_DARK,
14
17
  DARK_MODE_SELECTOR,
15
18
  GRAY_COLORS,
16
19
  PX_IN_REM,
17
- } from "../constants.ts";
18
-
19
- import type { ThemeConfig, GrayColors, BrandColors, VuelessCssVariables } from "../types.ts";
20
-
21
- interface InternalThemeConfig extends ThemeConfig {
22
- systemDarkMode?: boolean;
23
- }
20
+ } from "../constants.js";
24
21
 
25
22
  export function themeInit() {
26
23
  if (isSSR) return;
@@ -34,12 +31,8 @@ export function themeInit() {
34
31
  );
35
32
  }
36
33
 
37
- export function setTheme(config: InternalThemeConfig = {}) {
34
+ export function setTheme(config = {}) {
38
35
  const isDarkMode = setDarkMode(config);
39
- const rounding = config?.rounding ?? vuelessConfig.rounding ?? DEFAULT_ROUNDING;
40
- let brand: BrandColors | GrayColors = config?.brand ?? vuelessConfig.brand ?? DEFAULT_BRAND_COLOR;
41
- const gray = config?.gray ?? vuelessConfig.gray ?? DEFAULT_GRAY_COLOR;
42
-
43
36
  const ring = config?.ring ?? vuelessConfig.ring ?? DEFAULT_RING;
44
37
  const ringOffset = config?.ringOffset ?? vuelessConfig.ringOffset ?? DEFAULT_RING_OFFSET;
45
38
 
@@ -53,7 +46,10 @@ export function setTheme(config: InternalThemeConfig = {}) {
53
46
  vuelessConfig.ringOffsetColorLight ??
54
47
  DEFAULT_RING_OFFSET_COLOR_LIGHT;
55
48
 
56
- const colors = fullTailwindConfig.theme.colors;
49
+ const rounding = config?.rounding ?? vuelessConfig.rounding ?? DEFAULT_ROUNDING;
50
+ let brand = config?.brand ?? vuelessConfig.brand ?? DEFAULT_BRAND_COLOR;
51
+ let gray = config?.gray ?? vuelessConfig.gray ?? DEFAULT_GRAY_COLOR;
52
+
57
53
  const isBrandColor = BRAND_COLORS.some((color) => color === brand);
58
54
  const isGrayColor = GRAY_COLORS.some((color) => color === gray);
59
55
 
@@ -73,29 +69,29 @@ export function setTheme(config: InternalThemeConfig = {}) {
73
69
  ? defaultRingOffsetColorDark
74
70
  : defaultRingOffsetColorLight;
75
71
 
72
+ if (gray === COOL_COLOR) {
73
+ gray = GRAY_COLOR;
74
+ }
75
+
76
76
  if (brand === GRAYSCALE_COLOR) {
77
77
  brand = gray;
78
78
  }
79
79
 
80
- const variables: Partial<VuelessCssVariables> = {
81
- "--vl-rounding": `${Number(rounding) / PX_IN_REM}rem`,
80
+ const variables = {
82
81
  "--vl-ring": `${ring}px`,
83
82
  "--vl-ring-offset": `${ringOffset}px`,
84
- "--vl-ring-offset-color": convertHexInRgb(defaultRingOffsetColor),
83
+ "--vl-ring-offset-color": `rgb(${convertHexInRgb(defaultRingOffsetColor)})`,
84
+ "--vl-rounding": `${Number(rounding) / PX_IN_REM}rem`,
85
85
  "--vl-color-gray-default": convertHexInRgb(colors[gray][defaultBrandShade]),
86
86
  "--vl-color-brand-default": convertHexInRgb(colors[brand][defaultGrayShade]),
87
87
  };
88
88
 
89
89
  for (const key in colors[gray]) {
90
- variables[`--vl-color-gray-${key}` as keyof VuelessCssVariables] = convertHexInRgb(
91
- colors[gray][key],
92
- );
90
+ variables[`--vl-color-gray-${key}`] = convertHexInRgb(colors[gray][key]);
93
91
  }
94
92
 
95
93
  for (const key in colors[brand]) {
96
- variables[`--vl-color-brand-${key}` as keyof VuelessCssVariables] = convertHexInRgb(
97
- colors[brand][key],
98
- );
94
+ variables[`--vl-color-brand-${key}`] = convertHexInRgb(colors[brand][key]);
99
95
  }
100
96
 
101
97
  const stringVariables = Object.entries(variables)
@@ -114,14 +110,14 @@ export function setTheme(config: InternalThemeConfig = {}) {
114
110
  return rootVariables;
115
111
  }
116
112
 
117
- function setDarkMode(config: InternalThemeConfig) {
113
+ function setDarkMode(config) {
118
114
  config?.darkMode === undefined
119
115
  ? isCSR && localStorage.removeItem(DARK_MODE_SELECTOR)
120
- : isCSR && localStorage.setItem(DARK_MODE_SELECTOR, Number(config?.darkMode).toString());
116
+ : isCSR && localStorage.setItem(DARK_MODE_SELECTOR, Number(!!config?.darkMode));
121
117
 
122
118
  const storedDarkMode = isCSR ? localStorage.getItem(DARK_MODE_SELECTOR) : null;
123
119
 
124
- const isDarkMode =
120
+ let isDarkMode =
125
121
  storedDarkMode !== null
126
122
  ? !!Number(storedDarkMode)
127
123
  : !!(config?.darkMode ?? vuelessConfig.darkMode ?? config?.systemDarkMode);
@@ -133,7 +129,7 @@ function setDarkMode(config: InternalThemeConfig) {
133
129
  return isDarkMode;
134
130
  }
135
131
 
136
- export function convertHexInRgb(hex: string) {
132
+ export function convertHexInRgb(hex) {
137
133
  const color = hex.replace(/#/g, "");
138
134
 
139
135
  let r, g, b;
package/utils/utilUI.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  GRAYSCALE_COLOR,
8
8
  DEFAULT_BRAND_COLOR,
9
9
  NESTED_COMPONENT_REG_EXP,
10
+ SYSTEM_CONFIG_KEY,
10
11
  } from "../constants.js";
11
12
 
12
13
  /**
@@ -27,7 +28,7 @@ if (isSSR) {
27
28
  if (!vuelessConfig) {
28
29
  vuelessConfig = (await import(/* @vite-ignore */ `${filePath}.ts`)).default;
29
30
  }
30
- } catch {
31
+ } catch (error) {
31
32
  vuelessConfig = {};
32
33
  }
33
34
  })();
@@ -40,6 +41,209 @@ if (isCSR) {
40
41
  )[0] || {};
41
42
  }
42
43
 
44
+ /**
45
+ * Recursively merge config objects with removing tailwind classes duplicates.
46
+ * @param {Object} defaultConfig
47
+ * @param {Object} globalConfig
48
+ * @param {Object} propsConfig
49
+ * @param {Object} config - final merged config.
50
+ * @param {boolean} isReplace - enables class replacement instead of merge.
51
+ * @param {boolean} isVarinants - if true, prevents adding a "base" key into nested objects.
52
+ *
53
+ * @returns {Object}
54
+ */
55
+ export function mergeConfigs({
56
+ defaultConfig,
57
+ globalConfig,
58
+ propsConfig,
59
+ config = {},
60
+ isReplace = false,
61
+ isVariants = false,
62
+ }) {
63
+ globalConfig = cloneDeep(globalConfig || {});
64
+ propsConfig = cloneDeep(propsConfig || {});
65
+
66
+ const isGlobalConfig = Object.keys(globalConfig).length;
67
+ const isPropsConfig = Object.keys(propsConfig).length;
68
+
69
+ // Add unique keys from defaultConfig to composedConfig
70
+ let composedConfig = cloneDeep(defaultConfig);
71
+
72
+ // Add unique keys from globalConfig to composedConfig
73
+ for (let key in globalConfig) {
74
+ if (!Object.keys(composedConfig).includes(key)) {
75
+ composedConfig[key] = globalConfig[key];
76
+ }
77
+ }
78
+
79
+ // Add unique keys from propsConfig to composedConfig
80
+ for (let key in propsConfig) {
81
+ if (!Object.keys(composedConfig).includes(key)) {
82
+ composedConfig[key] = propsConfig[key];
83
+ }
84
+ }
85
+
86
+ const {
87
+ i18n,
88
+ defaults,
89
+ strategy,
90
+ safelist,
91
+ component,
92
+ safelistColors,
93
+ defaultVariants,
94
+ compoundVariants,
95
+ } = SYSTEM_CONFIG_KEY;
96
+
97
+ for (let key in composedConfig) {
98
+ if (isGlobalConfig || isPropsConfig) {
99
+ if (key === safelist || key === safelistColors) {
100
+ if (propsConfig[key]) {
101
+ // eslint-disable-next-line no-console
102
+ console.warn(`Passing '${key}' key in 'config' prop is not allowed.`);
103
+ }
104
+ } else if (key === component) {
105
+ config[key] = propsConfig[key] || defaultConfig[key];
106
+
107
+ if (globalConfig[key]) {
108
+ // eslint-disable-next-line no-console
109
+ console.warn(`Passing '${key}' key in 'config' prop or by global config is not allowed.`);
110
+ }
111
+ } else if (key === strategy) {
112
+ config[key] = propsConfig[key] || globalConfig[key] || defaultConfig[key];
113
+ } else if (key === defaults || key === defaultVariants) {
114
+ config[key] = { ...defaultConfig[key], ...globalConfig[key], ...propsConfig[key] };
115
+ } else if (key === compoundVariants) {
116
+ config[key] = mergeCompoundVariants({
117
+ defaultConfig: composedConfig,
118
+ globalConfig,
119
+ propsConfig,
120
+ isReplace,
121
+ key,
122
+ });
123
+ } else {
124
+ const isObjectComposedConfig = typeof composedConfig[key] === "object";
125
+ const isObjectGlobalConfig = typeof globalConfig[key] === "object";
126
+ const isObjectPropsConfig = typeof propsConfig[key] === "object";
127
+
128
+ const isObject = isObjectComposedConfig || isObjectGlobalConfig || isObjectPropsConfig;
129
+ const isEmpty = composedConfig[key] === null;
130
+ const isI18n = key === i18n;
131
+
132
+ if (key === "variants" && !isVariants) {
133
+ isVariants = true;
134
+ }
135
+
136
+ config[key] =
137
+ isObject && !isEmpty && !isI18n
138
+ ? mergeConfigs({
139
+ defaultConfig: stringToObject(composedConfig[key], { addBase: !isVariants }),
140
+ globalConfig: stringToObject(globalConfig[key], { addBase: !isVariants }),
141
+ propsConfig: stringToObject(propsConfig[key], { addBase: !isVariants }),
142
+ config: stringToObject(composedConfig[key], { addBase: !isVariants }),
143
+ isReplace,
144
+ isVariants,
145
+ })
146
+ : isReplace || isI18n
147
+ ? propsConfig[key] || globalConfig[key] || defaultConfig[key]
148
+ : cx([defaultConfig[key], globalConfig[key], propsConfig[key]]);
149
+ }
150
+ } else {
151
+ config[key] = composedConfig[key];
152
+ }
153
+ }
154
+
155
+ return config;
156
+ }
157
+
158
+ /**
159
+ Turn simplified nested component config to regular config.
160
+ @param {Object | String} value
161
+ @param {Boolean} addBase
162
+ @returns {Object}
163
+ */
164
+ function stringToObject(value, { addBase = false }) {
165
+ if (value === undefined) value = "";
166
+
167
+ return typeof value !== "object" ? addBase && { base: value } : value;
168
+ }
169
+
170
+ /**
171
+ * Merge CVA compound variants arrays.
172
+ * @param {Object} defaultConfig
173
+ * @param {Object} globalConfig
174
+ * @param {Object} propsConfig
175
+ * @param {string} key
176
+ * @param {boolean} isReplace - enables class replacement instead of merge.
177
+ *
178
+ * @returns {Array}
179
+ */
180
+ function mergeCompoundVariants({ defaultConfig, globalConfig, propsConfig, key, isReplace }) {
181
+ if (
182
+ (globalConfig[key] && !Array.isArray(globalConfig[key])) ||
183
+ (propsConfig[key] && !Array.isArray(propsConfig[key])) ||
184
+ (defaultConfig[key] && !Array.isArray(defaultConfig[key]))
185
+ ) {
186
+ // eslint-disable-next-line no-console
187
+ console.error("CompoundVariants should be an array.");
188
+ }
189
+
190
+ let globalConfigUniqueItems = cloneDeep(globalConfig[key] || []);
191
+ let propsConfigUniqueItems = cloneDeep(propsConfig[key] || []);
192
+
193
+ const config = defaultConfig[key].map((defaultConfigItem) => {
194
+ /**
195
+ * Compare two objects by keys for match.
196
+ * @param {Object} configItem
197
+ * @returns {Boolean}
198
+ */
199
+ function isSameItem(configItem) {
200
+ const hasConfigItemKeys = Object.keys(defaultConfigItem)
201
+ .map((key) => defaultConfigItem[key] === configItem[key] || key === "class")
202
+ .every((item) => !!item);
203
+
204
+ const hasDefaultConfigItemKeys = Object.keys(configItem)
205
+ .map((key) => defaultConfigItem[key] === configItem[key] || key === "class")
206
+ .every((item) => !!item);
207
+
208
+ return hasConfigItemKeys && hasDefaultConfigItemKeys;
209
+ }
210
+
211
+ /**
212
+ * Find the same compound variant item in custom config if exist.
213
+ * @param {Object} config
214
+ * @returns {Object|undefined}
215
+ */
216
+ function findItem(config = []) {
217
+ const globalConfigUniqueItemIndex = globalConfigUniqueItems.findIndex(isSameItem);
218
+ const propsConfigUniqueItemIndex = propsConfigUniqueItems.findIndex(isSameItem);
219
+
220
+ if (~globalConfigUniqueItemIndex) {
221
+ globalConfigUniqueItems.splice(globalConfigUniqueItemIndex, 1);
222
+ }
223
+
224
+ if (~propsConfigUniqueItemIndex) {
225
+ propsConfigUniqueItems.splice(propsConfigUniqueItemIndex, 1);
226
+ }
227
+
228
+ return config.find(isSameItem);
229
+ }
230
+
231
+ const globalConfigItem = findItem(globalConfig[key]);
232
+ const propsConfigItem = findItem(propsConfig[key]);
233
+
234
+ return globalConfigItem || propsConfigItem
235
+ ? {
236
+ ...defaultConfigItem,
237
+ class: isReplace
238
+ ? propsConfigItem?.class || globalConfigItem?.class || defaultConfigItem.class
239
+ : cx([defaultConfigItem.class, globalConfigItem?.class, propsConfigItem?.class]),
240
+ }
241
+ : defaultConfigItem;
242
+ });
243
+
244
+ return [...config, ...globalConfigUniqueItems, ...propsConfigUniqueItems];
245
+ }
246
+
43
247
  /**
44
248
  * Extend twMerge (tailwind merge) by vueless and user config:
45
249
  * All list of rules available here: