vueless 0.0.477 → 0.0.478-beta.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.
Files changed (60) hide show
  1. package/composables/useBreakpoint.js +1 -1
  2. package/composables/useUI.js +204 -1
  3. package/composablesTs/useAutoPosition.ts +115 -0
  4. package/composablesTs/useBreakpoint.ts +106 -0
  5. package/composablesTs/useLocale.ts +25 -0
  6. package/composablesTs/useMutationObserver.ts +50 -0
  7. package/composablesTs/useUI.ts +562 -0
  8. package/constants.js +2 -1
  9. package/constants.ts +73 -0
  10. package/directives/clickOutside/vClickOutside.js +2 -2
  11. package/directives/tooltip/storybook/stories.js +5 -5
  12. package/{index.js → index.ts} +10 -7
  13. package/package.json +28 -17
  14. package/preset.tailwind.js +16 -7
  15. package/types.ts +223 -0
  16. package/ui.button/config.js +12 -0
  17. package/ui.button-link/ULink.vue +1 -1
  18. package/ui.button-link/config.js +9 -0
  19. package/ui.data-list/UDataList.vue +4 -4
  20. package/ui.dropdown-badge/config.js +1 -0
  21. package/ui.dropdown-button/config.js +1 -0
  22. package/ui.form-checkbox/config.js +9 -0
  23. package/ui.form-color-picker/config.js +7 -0
  24. package/ui.form-input/UInput.vue +1 -1
  25. package/ui.form-input-money/useFormatCurrency.js +1 -1
  26. package/ui.form-input-number/UInputNumber.vue +4 -3
  27. package/ui.form-label/config.js +2 -2
  28. package/ui.form-radio/config.js +6 -0
  29. package/ui.form-switch/config.js +6 -0
  30. package/ui.image-avatar/config.js +5 -0
  31. package/ui.image-icon/config.js +5 -0
  32. package/ui.loader/config.js +1 -0
  33. package/ui.loader-overlay/config.js +1 -0
  34. package/ui.loader-progress/config.js +1 -0
  35. package/ui.navigation-progress/config.js +9 -0
  36. package/ui.other-dot/config.js +1 -0
  37. package/ui.text-alert/config.js +7 -0
  38. package/ui.text-badge/config.js +8 -0
  39. package/ui.text-block/UText.vue +18 -62
  40. package/ui.text-block/storybook/Docs.mdx +3 -3
  41. package/ui.text-block/storybook/{stories.js → stories.ts} +13 -8
  42. package/ui.text-block/types.ts +33 -0
  43. package/ui.text-block/useAttrs.ts +20 -0
  44. package/ui.text-file/UFile.vue +12 -14
  45. package/ui.text-file/config.js +12 -2
  46. package/ui.text-files/config.js +1 -1
  47. package/ui.text-header/config.js +1 -0
  48. package/ui.text-money/config.js +1 -0
  49. package/ui.text-money/utilMoney.js +2 -2
  50. package/utils/utilUI.js +0 -204
  51. package/utilsTs/utilHelper.ts +68 -0
  52. package/utilsTs/utilPlatform.ts +53 -0
  53. package/utilsTs/utilStorybook.ts +296 -0
  54. package/utilsTs/utilTailwind.ts +38 -0
  55. package/{utils/utilTheme.js → utilsTs/utilTheme.ts} +31 -27
  56. package/utilsTs/utilUI.ts +143 -0
  57. package/web-types.json +1 -1
  58. package/ui.text-block/useAttrs.js +0 -15
  59. /package/ui.text-block/{config.js → config.ts} +0 -0
  60. /package/ui.text-block/{constants.js → constants.ts} +0 -0
@@ -107,4 +107,13 @@ export default /*tw*/ {
107
107
  variant: "progress",
108
108
  indicator: false,
109
109
  },
110
+ safelist: (colors) => [
111
+ { pattern: `text-(${colors})-600` },
112
+ { pattern: `text-(${colors})-100` },
113
+ { pattern: `stroke-(${colors})-100` },
114
+ {
115
+ pattern: `bg-(${colors})-50`,
116
+ variants: ["[&::-webkit-progress-bar]", "[@supports(selector(&::-moz-progress-bar))]"],
117
+ },
118
+ ],
110
119
  };
@@ -19,4 +19,5 @@ export default /*tw*/ {
19
19
  color: "brand",
20
20
  size: "md",
21
21
  },
22
+ safelist: (colors) => [{ pattern: `bg-(${colors})-600` }],
22
23
  };
@@ -54,4 +54,11 @@ export default /*tw*/ {
54
54
  /* icons */
55
55
  closeIcon: "close",
56
56
  },
57
+ safelist: (colors) => [
58
+ { pattern: `bg-(${colors})-50` },
59
+ { pattern: `bg-(${colors})-600` },
60
+ { pattern: `text-(${colors})-600` },
61
+ { pattern: `border-(${colors})-100` },
62
+ { pattern: `border-(${colors})-600` },
63
+ ],
57
64
  };
@@ -63,4 +63,12 @@ export default /*tw*/ {
63
63
  round: false,
64
64
  bordered: false,
65
65
  },
66
+ safelist: (colors) => [
67
+ { pattern: `bg-(${colors})-50` },
68
+ { pattern: `bg-(${colors})-600` },
69
+ { pattern: `border-(${colors})-100` },
70
+ { pattern: `border-(${colors})-600` },
71
+ { pattern: `text-(${colors})-600` },
72
+ { pattern: `ring-(${colors})-700`, variants: ["focus", "focus-within"] },
73
+ ],
66
74
  };
@@ -1,71 +1,27 @@
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>
1
+ <script lang="ts" setup>
2
+ import { getDefault } from "../utilsTs/utilUI.ts";
8
3
 
9
- <script setup>
10
- import { getDefault } from "../utils/utilUI.js";
4
+ import { UText } from "./constants.ts";
5
+ import defaultConfig from "./config.ts";
6
+ import useAttrs from "./useAttrs.ts";
11
7
 
12
- import { UText } from "./constants.js";
13
- import defaultConfig from "./config.js";
14
- import useAttrs from "./useAttrs.js";
8
+ import type { UTextProps } from "./types.ts";
15
9
 
16
10
  defineOptions({ inheritAttrs: false });
17
11
 
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
- },
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,
68
16
  });
69
17
 
70
18
  const { wrapperAttrs, htmlAttrs, hasSlotContent } = useAttrs(props);
71
19
  </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 "../../utils/utilStorybook.js";
2
+ import { getSource } from "../../utilsTs/utilStorybook.ts";
3
3
 
4
- import * as stories from "./stories.js";
5
- import defaultConfig from "../config.js?raw"
4
+ import * as stories from "./stories.ts";
5
+ import defaultConfig from "../config.ts?raw"
6
6
 
7
7
  <Meta of={stories} />
8
8
  <Title of={stories} />
@@ -1,8 +1,16 @@
1
- import { getArgTypes, getSlotNames, getSlotsFragment } from "../../utils/utilStorybook.js";
1
+ import type { Meta, StoryFn } from "@storybook/vue3";
2
+ import { getArgTypes, getSlotNames, getSlotsFragment } from "../../utilsTs/utilStorybook.ts";
2
3
 
3
4
  import UText from "../../ui.text-block/UText.vue";
4
5
  import URow from "../../ui.container-row/URow.vue";
5
6
 
7
+ import type { UTextProps } from "../types.ts";
8
+
9
+ interface UTextArgs extends UTextProps {
10
+ slotTemplate?: string;
11
+ enum: "size";
12
+ }
13
+
6
14
  /**
7
15
  * The `UText` component. | [View on GitHub](https://github.com/vuelessjs/vueless/tree/main/src/ui.text-block)
8
16
  */
@@ -14,7 +22,7 @@ export default {
14
22
  ...getArgTypes(UText.__name),
15
23
  },
16
24
  args: {},
17
- };
25
+ } as Meta;
18
26
 
19
27
  const defaultTemplate = `
20
28
  <p>
@@ -24,7 +32,7 @@ const defaultTemplate = `
24
32
  </p>
25
33
  `;
26
34
 
27
- const DefaultTemplate = (args) => ({
35
+ const DefaultTemplate: StoryFn<UTextArgs> = (args) => ({
28
36
  components: { UText, URow },
29
37
  setup() {
30
38
  const slots = getSlotNames(UText.__name);
@@ -38,12 +46,12 @@ const DefaultTemplate = (args) => ({
38
46
  `,
39
47
  });
40
48
 
41
- const EnumVariantTemplate = (args, { argTypes } = {}) => ({
49
+ const EnumVariantTemplate: StoryFn<UTextArgs> = (args, { argTypes }) => ({
42
50
  components: { UText, URow },
43
51
  setup() {
44
52
  return {
45
53
  args,
46
- options: argTypes[args.enum].options,
54
+ options: argTypes?.[args.enum]?.options || [],
47
55
  };
48
56
  },
49
57
  template: `
@@ -88,7 +96,6 @@ Paragraphs.args = {
88
96
  rapidly changing landscape and emerge stronger and more competitive.
89
97
  </p>
90
98
  </template>
91
-
92
99
  `,
93
100
  };
94
101
 
@@ -102,8 +109,6 @@ List.args = {
102
109
  <li>Update your profile information</li>
103
110
  <li>Check your email for updates</li>
104
111
  </ul>
105
-
106
-
107
112
  <ol>
108
113
  <li>Create an account by signing up</li>
109
114
  <li>Verify your email address</li>
@@ -0,0 +1,33 @@
1
+ import defaultConfig from "./config.ts";
2
+
3
+ export interface UTextProps {
4
+ /**
5
+ * HTML markdown or plain text.
6
+ */
7
+ html?: string;
8
+
9
+ /**
10
+ * Text size.
11
+ */
12
+ size?: "sm" | "md" | "lg";
13
+
14
+ /**
15
+ * Text align.
16
+ */
17
+ align?: "left" | "center" | "right";
18
+
19
+ /**
20
+ * Remove line height (useful for 1-line text).
21
+ */
22
+ line?: boolean;
23
+
24
+ /**
25
+ * Component config object.
26
+ */
27
+ config?: Partial<typeof defaultConfig>;
28
+
29
+ /**
30
+ * Data-test attribute for automated testing.
31
+ */
32
+ dataTest?: string;
33
+ }
@@ -0,0 +1,20 @@
1
+ import useUI from "../composablesTs/useUI.ts";
2
+
3
+ import defaultConfig from "./config.ts";
4
+
5
+ import type { UseAttrs } from "../types.ts";
6
+ import type { UTextProps } from "./types.ts";
7
+
8
+ type Config = Partial<typeof defaultConfig>;
9
+
10
+ export default function useAttrs(props: UTextProps): UseAttrs {
11
+ const { config, getKeysAttrs, hasSlotContent } = useUI<Config>(defaultConfig, () => props.config);
12
+
13
+ const keysAttrs = getKeysAttrs();
14
+
15
+ return {
16
+ config,
17
+ ...keysAttrs,
18
+ hasSlotContent,
19
+ };
20
+ }
@@ -23,16 +23,14 @@
23
23
  </slot>
24
24
 
25
25
  <slot name="right" :file="{ elementId, label, url, imageUrl }">
26
- <UButton
26
+ <UIcon
27
27
  v-if="removable"
28
- round
29
- filled
30
- square
31
- no-ring
32
- variant="thirdary"
33
- :size="removeButtonSize"
34
- :icon="config.defaults.removeIcon"
35
- v-bind="removeButtonAttrs"
28
+ internal
29
+ interactive
30
+ color="gray"
31
+ :size="removeIconSize"
32
+ :name="config.defaults.removeIcon"
33
+ v-bind="removeIconAttrs"
36
34
  :data-test="`${dataTest}-remove-item`"
37
35
  @click.stop.prevent="onRemove"
38
36
  />
@@ -141,20 +139,20 @@ const {
141
139
  fileIconAttrs,
142
140
  fileLabelAttrs,
143
141
  fileImageAttrs,
144
- removeButtonAttrs,
142
+ removeIconAttrs,
145
143
  } = useAttrs(props);
146
144
 
147
145
  const iconSize = computed(() => {
148
146
  const sizes = {
149
- sm: "2xs",
150
- md: "xs",
151
- lg: "sm",
147
+ sm: "xs",
148
+ md: "sm",
149
+ lg: "md",
152
150
  };
153
151
 
154
152
  return sizes[props.size];
155
153
  });
156
154
 
157
- const removeButtonSize = computed(() => {
155
+ const removeIconSize = computed(() => {
158
156
  const sizes = {
159
157
  sm: "2xs",
160
158
  md: "xs",
@@ -1,5 +1,15 @@
1
1
  export default /*tw*/ {
2
- file: "{ULink}",
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
+ },
3
13
  body: {
4
14
  base: "flex items-center",
5
15
  variants: {
@@ -13,7 +23,7 @@ export default /*tw*/ {
13
23
  fileImage: "rounded-sm max-w-7",
14
24
  fileIcon: "{UIcon}",
15
25
  fileLabel: "{ULink}",
16
- removeButton: "{UButton} ml-2",
26
+ removeIcon: "{UIcon}",
17
27
  defaults: {
18
28
  size: "md",
19
29
  /* icons */
@@ -1,6 +1,6 @@
1
1
  export default /*tw*/ {
2
2
  filesLabel: "{ULabel}",
3
- items: "flex flex-col gap-1",
3
+ items: "flex flex-col gap-1.5",
4
4
  item: "{UFile} block",
5
5
  defaults: {
6
6
  size: "md",
@@ -40,4 +40,5 @@ export default /*tw*/ {
40
40
  line: true,
41
41
  underlined: false,
42
42
  },
43
+ safelist: (colors) => [{ pattern: `text-(${colors})-600` }, { pattern: `border-(${colors})-600` }],
43
44
  };
@@ -61,4 +61,5 @@ export default /*tw*/ {
61
61
  integer: false,
62
62
  symbolDivided: true,
63
63
  },
64
+ safelist: (colors) => [{ pattern: `text-(${colors})-600` }],
64
65
  };
@@ -16,8 +16,8 @@ export function separatedMoney(
16
16
  thousandsSeparator = " ",
17
17
  ) {
18
18
  const options = {
19
- minimumFractionDigits: minFractionDigits,
20
- maximumFractionDigits: maxFractionDigits,
19
+ minimumFractionDigits: Number(minFractionDigits),
20
+ maximumFractionDigits: Number(maxFractionDigits),
21
21
  };
22
22
 
23
23
  const formattedMoney =
package/utils/utilUI.js CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  GRAYSCALE_COLOR,
8
8
  DEFAULT_BRAND_COLOR,
9
9
  NESTED_COMPONENT_REG_EXP,
10
- SYSTEM_CONFIG_KEY,
11
10
  } from "../constants.js";
12
11
 
13
12
  /**
@@ -41,209 +40,6 @@ if (isCSR) {
41
40
  )[0] || {};
42
41
  }
43
42
 
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
-
247
43
  /**
248
44
  * Extend twMerge (tailwind merge) by vueless and user config:
249
45
  * All list of rules available here:
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Deeply clone given object (same as lodash.cloneDeep).
3
+ */
4
+ export function cloneDeep(entity: unknown, cache = new WeakMap()): unknown {
5
+ // primitives
6
+ if (Object(entity) !== entity || !entity) {
7
+ return entity;
8
+ }
9
+
10
+ // cyclic reference
11
+ if (cache.has(entity)) {
12
+ return cache.get(entity);
13
+ }
14
+
15
+ const result =
16
+ entity instanceof Set
17
+ ? new Set(entity)
18
+ : entity instanceof Map
19
+ ? new Map(Array.from(entity, ([key, val]) => [key, cloneDeep(val, cache)]))
20
+ : entity instanceof Date
21
+ ? new Date(entity)
22
+ : entity instanceof RegExp
23
+ ? new RegExp(entity.source, entity.flags)
24
+ : entity.constructor
25
+ ? new (entity.constructor as { new (): typeof entity })()
26
+ : Object.create(null);
27
+
28
+ cache.set(entity, result);
29
+
30
+ return Object.assign(
31
+ result,
32
+ ...Object.keys(entity).map((key) => ({
33
+ [key]: cloneDeep((entity as Record<string | number, unknown>)[key], cache),
34
+ })),
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Creates a debounced function with delay (same as lodash.debounce).
40
+ */
41
+ export function createDebounce(func: () => void, ms: number) {
42
+ let timeout: ReturnType<typeof setTimeout>;
43
+
44
+ return function (...args: []) {
45
+ clearTimeout(timeout);
46
+ // @ts-expect-error - this implicitly has type any because it does not have a type annotation.
47
+ timeout = setTimeout(() => func.apply(this as unknown, args), ms);
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Change page title in runtime by provided config.
53
+ */
54
+ export function setTitle({ title = "", separator = " / ", suffix = "" }) {
55
+ if (isCSR) {
56
+ document.title = title ? title + separator + suffix : suffix;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Check is code rendering on the server side.
62
+ */
63
+ export const isSSR: boolean = typeof window === "undefined";
64
+
65
+ /**
66
+ * Check is code rendering on the client side.
67
+ */
68
+ export const isCSR: boolean = typeof window !== "undefined";