vueless 0.0.325 → 0.0.327

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.
@@ -10,17 +10,15 @@ import {
10
10
  Fragment,
11
11
  } from "vue";
12
12
 
13
- import {
14
- cx,
15
- setColor,
16
- getColor,
17
- strategy,
18
- nestedComponentRegEx,
19
- globalComponentConfig,
20
- } from "../service.ui";
13
+ import { cx, setColor, getColor, vuelessConfig } from "../service.ui/index.js";
21
14
 
22
- import { cloneDeep } from "../service.helper";
23
- import { STRATEGY_TYPE, CVA_CONFIG_KEY, SYSTEM_CONFIG_KEY } from "../constants";
15
+ import { cloneDeep, isCSR } from "../service.helper/index.js";
16
+ import {
17
+ STRATEGY_TYPE,
18
+ CVA_CONFIG_KEY,
19
+ SYSTEM_CONFIG_KEY,
20
+ NESTED_COMPONENT_REG_EXP,
21
+ } from "../constants/index.js";
24
22
 
25
23
  /**
26
24
  Merging component configs in a given sequence (bigger number = bigger priority):
@@ -32,10 +30,11 @@ import { STRATEGY_TYPE, CVA_CONFIG_KEY, SYSTEM_CONFIG_KEY } from "../constants";
32
30
  export default function useUI(defaultConfig = {}, propsConfigGetter = null, topLevelClassKey) {
33
31
  const { type, props } = getCurrentInstance();
34
32
  const componentName = type.name;
35
- const globalConfig = globalComponentConfig[componentName];
33
+ const globalConfig = vuelessConfig?.component ? vuelessConfig?.component[componentName] : {};
36
34
 
37
- const isStrategyValid = strategy && Object.values(STRATEGY_TYPE).includes(strategy);
38
- const vuelessStrategy = isStrategyValid ? strategy : STRATEGY_TYPE.merge;
35
+ const isStrategyValid =
36
+ vuelessConfig.strategy && Object.values(STRATEGY_TYPE).includes(vuelessConfig.strategy);
37
+ const vuelessStrategy = isStrategyValid ? vuelessConfig.strategy : STRATEGY_TYPE.merge;
39
38
 
40
39
  const [firstClassKey] = Object.keys(defaultConfig);
41
40
  const config = ref({});
@@ -58,7 +57,7 @@ export default function useUI(defaultConfig = {}, propsConfigGetter = null, topL
58
57
  const nestedComponent = getNestedComponent(defaultConfig[configKey]);
59
58
 
60
59
  const attrs = useAttrs();
61
- const isDev = import.meta.env.DEV;
60
+ const isDev = isCSR && import.meta.env?.DEV;
62
61
  const vuelessAttrs = ref({});
63
62
  const isTopLevelKey = (topLevelClassKey || firstClassKey) === configKey;
64
63
 
@@ -199,7 +198,6 @@ function mergeConfigs({
199
198
  safelistColors,
200
199
  defaultVariants,
201
200
  compoundVariants,
202
- iconName,
203
201
  } = SYSTEM_CONFIG_KEY;
204
202
 
205
203
  for (let key in composedConfig) {
@@ -235,7 +233,6 @@ function mergeConfigs({
235
233
 
236
234
  const isObject = isObjectComposedConfig || isObjectGlobalConfig || isObjectPropsConfig;
237
235
  const isEmpty = composedConfig[key] === null;
238
- const isIconName = key.toLowerCase().includes(iconName.toLowerCase());
239
236
  const isI18n = key === i18n;
240
237
 
241
238
  if (key === "variants" && !isVariants) {
@@ -252,7 +249,7 @@ function mergeConfigs({
252
249
  isReplace,
253
250
  isVariants,
254
251
  })
255
- : isReplace || isIconName || isI18n
252
+ : isReplace || isI18n
256
253
  ? propsConfig[key] || globalConfig[key] || defaultConfig[key]
257
254
  : cx([defaultConfig[key], globalConfig[key], propsConfig[key]]);
258
255
  }
@@ -388,7 +385,8 @@ function getBaseClasses(value) {
388
385
  function getNestedComponent(value) {
389
386
  const classes = getBaseClasses(value);
390
387
  const component = value?.component || "";
391
- const match = classes.match(nestedComponentRegEx) || component.match(nestedComponentRegEx);
388
+ const match =
389
+ classes.match(NESTED_COMPONENT_REG_EXP) || component.match(NESTED_COMPONENT_REG_EXP);
392
390
 
393
391
  return match ? match[1] : "";
394
392
  }
@@ -401,11 +399,7 @@ function getNestedComponent(value) {
401
399
  function isSystemKey(key) {
402
400
  const isExactKey = Object.values(SYSTEM_CONFIG_KEY).some((value) => value === key);
403
401
 
404
- return (
405
- isExactKey ||
406
- key.toLowerCase().includes(SYSTEM_CONFIG_KEY.iconName.toLowerCase()) ||
407
- key.toLowerCase().includes(SYSTEM_CONFIG_KEY.transition.toLowerCase())
408
- );
402
+ return isExactKey || key.toLowerCase().includes(SYSTEM_CONFIG_KEY.transition.toLowerCase());
409
403
  }
410
404
 
411
405
  /**
@@ -62,6 +62,10 @@ export const SYSTEM_CONFIG_KEY = {
62
62
  component: "component",
63
63
  transition: "transition",
64
64
  safelistColors: "safelistColors",
65
- iconName: "iconName",
66
65
  ...CVA_CONFIG_KEY,
67
66
  };
67
+
68
+ /* Other */
69
+ export const PX_IN_REM = 16;
70
+ export const HYPHEN_SYMBOL = "-";
71
+ export const NESTED_COMPONENT_REG_EXP = /\{U[^}]*}/g;
@@ -1,14 +1,13 @@
1
1
  import tippy from "tippy.js";
2
2
  import { merge } from "lodash-es";
3
3
 
4
+ // Fix for SSR
4
5
  import "tippy.js/dist/tippy.css";
5
6
  import "tippy.js/themes/light.css";
6
7
  import "tippy.js/animations/shift-away.css";
7
8
 
8
- /* Load Vueless config from the project root. */
9
- const [vuelessConfig] = Object.values(
10
- import.meta.glob("/vueless.config.js", { eager: true, import: "default" }),
11
- );
9
+ import { vuelessConfig } from "../service.ui/index.js";
10
+ import { isCSR, isSSR } from "../service.helper/index.js";
12
11
 
13
12
  const globalSettings = vuelessConfig?.directive?.tooltip || {};
14
13
  const defaultSettings = {
@@ -19,21 +18,21 @@ const defaultSettings = {
19
18
 
20
19
  const mergedSettings = merge(defaultSettings, globalSettings);
21
20
 
22
- tippy.setDefaultProps(mergedSettings);
21
+ isCSR && tippy.setDefaultProps(mergedSettings);
23
22
 
24
23
  export default {
25
24
  mounted(el, bindings) {
26
- tippy(el, merge(mergedSettings, bindings.value || {}));
25
+ isCSR && tippy(el, merge(mergedSettings, bindings.value || {}));
27
26
  },
28
27
 
29
28
  updated(el, bindings) {
30
- if (!el._tippy) return;
29
+ if (!el._tippy || isSSR) return;
31
30
 
32
31
  el._tippy.setProps(merge(mergedSettings, bindings.value || {}));
33
32
  },
34
33
 
35
34
  unmounted(el) {
36
- if (!el._tippy) return;
35
+ if (!el._tippy || isSSR) return;
37
36
 
38
37
  el._tippy.destroy();
39
38
  },
package/index.js CHANGED
@@ -2,8 +2,10 @@
2
2
  import { createLocale, LocaleSymbol } from "./composable.locale";
3
3
  import { createLoaderRendering, LoaderRenderingSymbol } from "./ui.loader-rendering/composables/useLoaderRendering";
4
4
  import { createLoaderTop, LoaderTopSymbol } from "./ui.loader-top/composables/useLoaderTop";
5
+ import { themeInit } from "./service.theme";
5
6
 
6
- export { setTheme } from "./service.ui";
7
+ export { setTitle } from "./service.helper";
8
+ export { setTheme } from "./service.theme";
7
9
  export { default as createVueI18nAdapter } from "./adatper.locale/vue-i18n";
8
10
  export { default as defaultEnLocale } from "./adatper.locale/locales/en";
9
11
  export { useLocale } from "./composable.locale";
@@ -33,6 +35,8 @@ export function createVueless(options = {}) {
33
35
  app.provide(LoaderTopSymbol, loaderTop);
34
36
  };
35
37
 
38
+ themeInit();
39
+
36
40
  return {
37
41
  install,
38
42
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.325",
3
+ "version": "0.0.327",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless Component Framework.",
6
6
  "homepage": "https://vueless.com",
@@ -37,6 +37,13 @@ export function cloneDeep(entity, cache = new WeakMap()) {
37
37
  );
38
38
  }
39
39
 
40
+ /**
41
+ Invoke function with delay (same as lodash.debounce).
42
+ @param {Function} func
43
+ @param {Number} wait
44
+
45
+ @returns {Function}
46
+ */
40
47
  export function debounce(func, wait) {
41
48
  let timeout;
42
49
 
@@ -47,3 +54,28 @@ export function debounce(func, wait) {
47
54
  timeout = setTimeout(() => func.apply(context, args), wait);
48
55
  };
49
56
  }
57
+
58
+ /**
59
+ Change page title in runtime by provided config.
60
+ @param {Object} config
61
+ @param {string} config.title
62
+ @param {string} config.separator
63
+ @param {string} config.suffix
64
+
65
+ @returns {VoidFunction}
66
+ */
67
+ export function setTitle({ title, separator = " / ", suffix = "" }) {
68
+ document.title = title ? title + separator + suffix : suffix;
69
+ }
70
+
71
+ /**
72
+ Check is code rendering on the server side.
73
+ @returns {boolean}
74
+ */
75
+ export const isSSR = typeof window === "undefined";
76
+
77
+ /**
78
+ Check is code rendering on the client side.
79
+ @returns {boolean}
80
+ */
81
+ export const isCSR = typeof window !== "undefined";
@@ -0,0 +1,124 @@
1
+ import colors from "tailwindcss/colors.js";
2
+
3
+ import { vuelessConfig } from "../service.ui";
4
+ import {
5
+ GRAY_COLOR,
6
+ COOL_COLOR,
7
+ BRAND_COLORS,
8
+ GRAYSCALE_COLOR,
9
+ DEFAULT_RING,
10
+ DEFAULT_RING_OFFSET,
11
+ DEFAULT_ROUNDING,
12
+ DEFAULT_BRAND_COLOR,
13
+ DEFAULT_GRAY_COLOR,
14
+ DARK_MODE_SELECTOR,
15
+ GRAY_COLORS,
16
+ PX_IN_REM,
17
+ } from "../constants/index.js";
18
+
19
+ export function themeInit() {
20
+ const prefersColorSchemeDark = window && window.matchMedia("(prefers-color-scheme: dark)");
21
+
22
+ setTheme({ systemDarkMode: prefersColorSchemeDark.matches });
23
+
24
+ prefersColorSchemeDark.addEventListener("change", (event) =>
25
+ setTheme({ systemDarkMode: event.matches }),
26
+ );
27
+ }
28
+
29
+ export function setTheme(config = {}) {
30
+ const isDarkMode = setDarkMode(config);
31
+ const ring = config?.ring ?? vuelessConfig?.ring ?? DEFAULT_RING;
32
+ const ringOffset = config?.ringOffset ?? vuelessConfig?.ringOffset ?? DEFAULT_RING_OFFSET;
33
+ const rounding = config?.rounding ?? vuelessConfig?.rounding ?? DEFAULT_ROUNDING;
34
+ let brand = config?.brand ?? vuelessConfig?.brand ?? DEFAULT_BRAND_COLOR;
35
+ let gray = config?.gray ?? vuelessConfig?.gray ?? DEFAULT_GRAY_COLOR;
36
+
37
+ const isBrandColor = BRAND_COLORS.some((color) => color === brand);
38
+ const isGrayColor = GRAY_COLORS.some((color) => color === gray);
39
+
40
+ if (!isBrandColor) {
41
+ // eslint-disable-next-line no-console
42
+ console.warn(`Brand color '${brand}' is incorrect.`);
43
+ }
44
+
45
+ if (!isGrayColor) {
46
+ // eslint-disable-next-line no-console
47
+ console.warn(`Gray color '${gray}' is incorrect.`);
48
+ }
49
+
50
+ const defaultBrandShade = isDarkMode ? 400 : 600;
51
+ const defaultGrayShade = isDarkMode ? 400 : 600;
52
+
53
+ if (gray === COOL_COLOR) {
54
+ gray = GRAY_COLOR;
55
+ }
56
+
57
+ if (brand === GRAYSCALE_COLOR) {
58
+ brand = gray;
59
+ }
60
+
61
+ const variables = {
62
+ "--vl-ring": `${ring}px`,
63
+ "--vl-ring-offset": `${ringOffset}px`,
64
+ "--vl-rounding": `${Number(rounding) / PX_IN_REM}rem`,
65
+ "--vl-color-gray-default": convertHexInRgb(colors[gray][defaultBrandShade]),
66
+ "--vl-color-brand-default": convertHexInRgb(colors[brand][defaultGrayShade]),
67
+ };
68
+
69
+ for (const key in colors[gray]) {
70
+ variables[`--vl-color-gray-${key}`] = convertHexInRgb(colors[gray][key]);
71
+ }
72
+
73
+ for (const key in colors[brand]) {
74
+ variables[`--vl-color-brand-${key}`] = convertHexInRgb(colors[brand][key]);
75
+ }
76
+
77
+ const style = document.createElement("style");
78
+ const stringVariables = Object.entries(variables)
79
+ .map(([key, value]) => `${key}: ${value};`)
80
+ .join(" ");
81
+
82
+ style.innerHTML = `:root {${stringVariables}`;
83
+
84
+ document.head.appendChild(style);
85
+ }
86
+
87
+ function setDarkMode(config) {
88
+ config?.darkMode === undefined
89
+ ? localStorage.removeItem(DARK_MODE_SELECTOR)
90
+ : localStorage.setItem(DARK_MODE_SELECTOR, Number(!!config?.darkMode));
91
+
92
+ const storedDarkMode = localStorage.getItem(DARK_MODE_SELECTOR);
93
+
94
+ let isDarkMode =
95
+ storedDarkMode !== null
96
+ ? !!Number(storedDarkMode)
97
+ : !!(config?.darkMode ?? vuelessConfig?.darkMode ?? config?.systemDarkMode);
98
+
99
+ isDarkMode
100
+ ? document.documentElement.classList.add(DARK_MODE_SELECTOR)
101
+ : document.documentElement.classList.remove(DARK_MODE_SELECTOR);
102
+
103
+ return isDarkMode;
104
+ }
105
+
106
+ function convertHexInRgb(hex) {
107
+ const color = hex.replace(/#/g, "");
108
+
109
+ let r, g, b;
110
+
111
+ if (color.length === 6) {
112
+ r = parseInt(color.substring(0, 2), 16);
113
+ g = parseInt(color.substring(2, 4), 16);
114
+ b = parseInt(color.substring(4, 6), 16);
115
+ }
116
+
117
+ if (color.length === 3) {
118
+ r = parseInt(color.substring(0, 1).repeat(2), 16);
119
+ g = parseInt(color.substring(1, 2).repeat(2), 16);
120
+ b = parseInt(color.substring(2, 3).repeat(2), 16);
121
+ }
122
+
123
+ return color.length === 6 || color.length === 3 ? `${r}, ${g}, ${b}` : "";
124
+ }
@@ -1,46 +1,36 @@
1
1
  import { merge } from "lodash-es";
2
2
  import { defineConfig } from "cva";
3
3
  import { extendTailwindMerge } from "tailwind-merge";
4
- import colors from "tailwindcss/colors";
5
-
6
- import { cloneDeep } from "../service.helper";
4
+ import { cloneDeep, isCSR, isSSR } from "../service.helper/index.js";
7
5
  import {
8
- GRAY_COLOR,
9
- COOL_COLOR,
10
6
  BRAND_COLOR,
11
- BRAND_COLORS,
12
7
  GRAYSCALE_COLOR,
13
- DEFAULT_RING,
14
- DEFAULT_RING_OFFSET,
15
- DEFAULT_ROUNDING,
16
8
  DEFAULT_BRAND_COLOR,
17
- DEFAULT_GRAY_COLOR,
18
- DARK_MODE_SELECTOR,
19
- GRAY_COLORS,
20
- } from "../constants";
9
+ NESTED_COMPONENT_REG_EXP,
10
+ } from "../constants/index.js";
21
11
 
22
- /* Load Vueless config from the project root. */
23
- const [vuelessConfig] = Object.values(
24
- import.meta.glob("/vueless.config.js", { eager: true, import: "default" }),
25
- );
12
+ /**
13
+ Load Vueless config from the project root.
14
+ Both for server and client side renderings.
15
+ IIFE is used to cache the results.
16
+ */
17
+ export const vuelessConfig = (() => {
18
+ let config = {};
26
19
 
27
- /*
28
- Export global config settings for the current library.
29
- Did as a separate variables for more comfortable usage.
30
- */
31
- export const {
32
- layout,
33
- strategy,
34
- rounding,
35
- gray,
36
- brand,
37
- tailwindMerge: globalTailwindMergeConfig,
38
- component: globalComponentConfig,
39
- } = vuelessConfig;
20
+ if (isSSR) {
21
+ // TODO: test it in SSR, maybe `await` is needed
22
+ config = import(/* @vite-ignore */ `${process.cwd()}/vueless.config.js`).default;
23
+ }
40
24
 
41
- export const nestedComponentRegEx = /\{U[^}]*}/g;
25
+ if (isCSR) {
26
+ config = Object.values(
27
+ import.meta.glob("/vueless.config.js", { eager: true, import: "default" }),
28
+ )[0];
29
+ }
30
+
31
+ return config;
32
+ })();
42
33
 
43
- //
44
34
  /**
45
35
  Extend twMerge (tailwind merge) by vueless and user config:
46
36
  All list of rules available here:
@@ -61,7 +51,7 @@ const twMerge = extendTailwindMerge(
61
51
  },
62
52
  },
63
53
  },
64
- globalTailwindMergeConfig,
54
+ vuelessConfig.tailwindMerge,
65
55
  ),
66
56
  );
67
57
 
@@ -69,83 +59,52 @@ const twMerge = extendTailwindMerge(
69
59
  Export cva (class variance authority) methods:
70
60
  * extended with tailwind-merge
71
61
  * remove all Vueless nested component names ({U...} strings) from class list string.
72
- It helps to make class variation switchers and removes class duplications.
73
62
  https://beta.cva.style
74
63
  */
75
- export const {
76
- cva: classVarianceAuthority,
77
- cx,
78
- compose,
79
- } = defineConfig({
64
+ export const { cva, cx, compose } = defineConfig({
80
65
  hooks: {
81
- onComplete: (classNames) => twMerge(classNames).replace(nestedComponentRegEx, ""),
66
+ onComplete: (classNames) => twMerge(classNames).replace(NESTED_COMPONENT_REG_EXP, ""),
82
67
  },
83
68
  });
84
69
 
85
- export const cva = ({ base = "", variants = {}, compoundVariants = [], defaultVariants = {} }) =>
86
- classVarianceAuthority({
87
- base,
88
- variants,
89
- compoundVariants,
90
- defaultVariants,
91
- });
92
-
93
- const PX_IN_REM = 16;
94
- const HYPHEN_SYMBOL = "-";
95
-
96
- (() => {
97
- const prefersColorSchemeDark = window && window.matchMedia("(prefers-color-scheme: dark)");
98
-
99
- setTheme({ systemDarkMode: prefersColorSchemeDark.matches });
70
+ /**
71
+ Return default values for component props, icons, etc..
72
+ @param { Object } defaultConfig
73
+ @param { String } name
100
74
 
101
- prefersColorSchemeDark.addEventListener("change", (event) =>
102
- setTheme({ systemDarkMode: event.matches }),
75
+ @returns { Object }
76
+ */
77
+ export function getDefault(defaultConfig, name) {
78
+ const defaults = merge(
79
+ cloneDeep(defaultConfig.defaults),
80
+ vuelessConfig?.component ? vuelessConfig?.component[name]?.defaults : {},
103
81
  );
104
- })();
105
-
106
- function getRandomId(length = 15) {
107
- const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
108
- const charactersLength = characters.length;
109
- let id = "";
110
-
111
- while (id.length < length) {
112
- id += characters.charAt(Math.floor(Math.random() * charactersLength));
113
- }
114
-
115
- return id;
116
- }
117
-
118
- function setTitle({ title, separator = " / ", suffix = "" }) {
119
- document.title = title ? title + separator + suffix : suffix;
120
- }
121
-
122
- function setFavicon(faviconPath) {
123
- if (!faviconPath) return;
124
-
125
- const head = document.querySelector("head");
126
- const faviconTag = document.createElement("link");
127
-
128
- faviconTag.setAttribute("rel", "shortcut icon");
129
- faviconTag.setAttribute("href", `${faviconPath}?${Math.random()}`);
130
-
131
- head.appendChild(faviconTag);
132
- }
133
-
134
- function getDefault(defaultConfig, name) {
135
- const defaults = merge(cloneDeep(defaultConfig.defaults), globalComponentConfig[name]?.defaults);
136
82
 
137
83
  defaults.color = getColor(defaults.color);
138
84
 
139
85
  return defaults;
140
86
  }
141
87
 
142
- function getColor(color) {
143
- return (brand ?? DEFAULT_BRAND_COLOR) === GRAYSCALE_COLOR && color === BRAND_COLOR
88
+ /**
89
+ Return `grayscale` color if in component config it `brand` but in vueless config it `grayscale`
90
+ Otherwise return given color.
91
+ @param { String } color
92
+ @returns { String }
93
+ */
94
+ export function getColor(color) {
95
+ return (vuelessConfig.brand ?? DEFAULT_BRAND_COLOR) === GRAYSCALE_COLOR && color === BRAND_COLOR
144
96
  ? GRAYSCALE_COLOR
145
97
  : color;
146
98
  }
147
99
 
148
- function setColor(classes, color) {
100
+ /**
101
+ Replace in tailwind classes `{color}` variable into given color.
102
+ @param { String } classes
103
+ @param { String } color
104
+
105
+ @returns { String }
106
+ */
107
+ export function setColor(classes, color) {
149
108
  if (typeof classes !== "string") {
150
109
  return "";
151
110
  }
@@ -153,113 +112,20 @@ function setColor(classes, color) {
153
112
  return classes?.replaceAll("{color}", color);
154
113
  }
155
114
 
156
- function setDarkMode(config) {
157
- config?.darkMode === undefined
158
- ? localStorage.removeItem(DARK_MODE_SELECTOR)
159
- : localStorage.setItem(DARK_MODE_SELECTOR, Number(!!config?.darkMode));
160
-
161
- const storedDarkMode = localStorage.getItem(DARK_MODE_SELECTOR);
162
-
163
- let isDarkMode =
164
- storedDarkMode !== null
165
- ? !!Number(storedDarkMode)
166
- : !!(config?.darkMode ?? vuelessConfig?.darkMode ?? config?.systemDarkMode);
167
-
168
- isDarkMode
169
- ? document.documentElement.classList.add(DARK_MODE_SELECTOR)
170
- : document.documentElement.classList.remove(DARK_MODE_SELECTOR);
171
-
172
- return isDarkMode;
173
- }
174
-
175
- function setTheme(config = {}) {
176
- const isDarkMode = setDarkMode(config);
177
- const ring = config?.ring ?? vuelessConfig?.ring ?? DEFAULT_RING;
178
- const ringOffset = config?.ringOffset ?? vuelessConfig?.ringOffset ?? DEFAULT_RING_OFFSET;
179
- const rounding = config?.rounding ?? vuelessConfig?.rounding ?? DEFAULT_ROUNDING;
180
- let brand = config?.brand ?? vuelessConfig?.brand ?? DEFAULT_BRAND_COLOR;
181
- let gray = config?.gray ?? vuelessConfig?.gray ?? DEFAULT_GRAY_COLOR;
182
-
183
- const isBrandColor = BRAND_COLORS.some((color) => color === brand);
184
- const isGrayColor = GRAY_COLORS.some((color) => color === gray);
185
-
186
- if (!isBrandColor) {
187
- // eslint-disable-next-line no-console
188
- console.warn(`Brand color '${brand}' is incorrect.`);
189
- }
190
-
191
- if (!isGrayColor) {
192
- // eslint-disable-next-line no-console
193
- console.warn(`Gray color '${gray}' is incorrect.`);
194
- }
195
-
196
- const defaultBrandShade = isDarkMode ? 400 : 600;
197
- const defaultGrayShade = isDarkMode ? 400 : 600;
198
-
199
- if (gray === COOL_COLOR) {
200
- gray = GRAY_COLOR;
201
- }
202
-
203
- if (brand === GRAYSCALE_COLOR) {
204
- brand = gray;
205
- }
206
-
207
- const variables = {
208
- "--vl-ring": `${ring}px`,
209
- "--vl-ring-offset": `${ringOffset}px`,
210
- "--vl-rounding": `${Number(rounding) / PX_IN_REM}rem`,
211
- "--vl-color-gray-default": convertHexInRgb(colors[gray][defaultBrandShade]),
212
- "--vl-color-brand-default": convertHexInRgb(colors[brand][defaultGrayShade]),
213
- };
214
-
215
- for (const key in colors[gray]) {
216
- variables[`--vl-color-gray-${key}`] = convertHexInRgb(colors[gray][key]);
217
- }
218
-
219
- for (const key in colors[brand]) {
220
- variables[`--vl-color-brand-${key}`] = convertHexInRgb(colors[brand][key]);
221
- }
222
-
223
- const style = document.createElement("style");
224
- const stringVariables = Object.entries(variables)
225
- .map(([key, value]) => `${key}: ${value};`)
226
- .join(" ");
227
-
228
- style.innerHTML = `:root {${stringVariables}`;
229
-
230
- document.head.appendChild(style);
231
- }
232
-
233
- function convertHexInRgb(hex) {
234
- const color = hex.replace(/#/g, "");
235
-
236
- let r, g, b;
115
+ /**
116
+ Generates unique #id.
117
+ @param { Number } length
118
+ @returns { String }
119
+ */
120
+ export function getRandomId(length = 15) {
121
+ const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
122
+ const charactersLength = characters.length;
237
123
 
238
- if (color.length === 6) {
239
- r = parseInt(color.substring(0, 2), 16);
240
- g = parseInt(color.substring(2, 4), 16);
241
- b = parseInt(color.substring(4, 6), 16);
242
- }
124
+ let id = "";
243
125
 
244
- if (color.length === 3) {
245
- r = parseInt(color.substring(0, 1).repeat(2), 16);
246
- g = parseInt(color.substring(1, 2).repeat(2), 16);
247
- b = parseInt(color.substring(2, 3).repeat(2), 16);
126
+ while (id.length < length) {
127
+ id += characters.charAt(Math.floor(Math.random() * charactersLength));
248
128
  }
249
129
 
250
- return color.length === 6 || color.length === 3 ? `${r}, ${g}, ${b}` : "";
130
+ return id;
251
131
  }
252
-
253
- export {
254
- PX_IN_REM,
255
- HYPHEN_SYMBOL,
256
- getRandomId,
257
- setTitle,
258
- setFavicon,
259
- getDefault,
260
- getColor,
261
- setColor,
262
- setDarkMode,
263
- setTheme,
264
- convertHexInRgb,
265
- };
@@ -46,6 +46,7 @@
46
46
  <div
47
47
  v-bind="attrs.bodyCellPrimaryAttrs"
48
48
  ref="cellRef"
49
+ :title="isElementOverflown(cellRef?.[index]) ? value.primary : ''"
49
50
  :data-test="`${dataTest}-${key}-cell`"
50
51
  >
51
52
  {{ value.primary || HYPHEN_SYMBOL }}
@@ -53,14 +54,23 @@
53
54
 
54
55
  <div v-bind="attrs.bodyCellSecondaryAttrs">
55
56
  <template v-if="Array.isArray(value.secondary)">
56
- <div v-for="(secondary, idx) in value.secondary" ref="cellRef" :key="idx">
57
+ <div
58
+ v-for="(secondary, idx) in value.secondary"
59
+ ref="cellRef"
60
+ :key="idx"
61
+ :title="isElementOverflown(cellRef?.[index]) ? value.secondary : ''"
62
+ >
57
63
  <span v-bind="attrs.bodyCellSecondaryEmptyAttrs">
58
64
  {{ secondary }}
59
65
  </span>
60
66
  </div>
61
67
  </template>
62
68
 
63
- <div v-else ref="cellRef">
69
+ <div
70
+ v-else
71
+ ref="cellRef"
72
+ :title="isElementOverflown(cellRef?.[index]) ? value.secondary : ''"
73
+ >
64
74
  {{ value.secondary }}
65
75
  </div>
66
76
  </div>
@@ -72,6 +82,7 @@
72
82
  <div
73
83
  v-bind="attrs.bodyCellPrimaryAttrs"
74
84
  ref="cellRef"
85
+ :title="isElementOverflown(cellRef?.[index]) ? value : ''"
75
86
  :data-test="`${dataTest}-${key}-cell`"
76
87
  >
77
88
  {{ value || value === 0 ? value : HYPHEN_SYMBOL }}
@@ -100,7 +111,7 @@
100
111
  <script setup>
101
112
  import { computed, ref } from "vue";
102
113
 
103
- import { HYPHEN_SYMBOL } from "../../service.ui";
114
+ import { HYPHEN_SYMBOL } from "../../constants";
104
115
  import { getFilteredRow } from "../services/table.service.js";
105
116
 
106
117
  import { useMutationObserver } from "../../composable.mutationObserver/index.js";
@@ -185,8 +196,7 @@ function setCellTitle(mutations) {
185
196
  mutations.forEach((mutation) => {
186
197
  const { target } = mutation;
187
198
 
188
- const isOverflown =
189
- target.clientWidth < target.scrollWidth || target.clientHeight < target.scrollHeight;
199
+ const isOverflown = isElementOverflown(target);
190
200
 
191
201
  if (isOverflown) {
192
202
  target.setAttribute("title", target.textContent);
@@ -197,4 +207,10 @@ function setCellTitle(mutations) {
197
207
  }
198
208
  });
199
209
  }
210
+
211
+ function isElementOverflown(element) {
212
+ if (!cellRef.value) return false;
213
+
214
+ return element.clientWidth < element.scrollWidth || element.clientHeight < element.scrollHeight;
215
+ }
200
216
  </script>
@@ -292,7 +292,7 @@ import {
292
292
  getFlatRows,
293
293
  } from "./services/table.service";
294
294
 
295
- import { PX_IN_REM } from "../service.ui";
295
+ import { PX_IN_REM } from "../constants";
296
296
  import { UTable } from "./constants";
297
297
  import useAttrs from "./composables/attrs.composable";
298
298
  import { useLocale } from "../composable.locale";
@@ -72,7 +72,7 @@
72
72
  @binding {string} emptyStyles
73
73
  -->
74
74
  <slot name="empty" :empty-styles="optionClasses">
75
- <span v-if="!options.length" v-bind="optionAttrs()">
75
+ <span v-if="!options.length" v-bind="optionAttrs">
76
76
  <span v-bind="optionContentAttrs" v-text="currentLocale.noDataToShow" />
77
77
  </span>
78
78
  </slot>
@@ -98,6 +98,8 @@
98
98
  </template>
99
99
  </ul>
100
100
  </div>
101
+
102
+ <!-- {{ !options.length }} -->
101
103
  </template>
102
104
 
103
105
  <script setup>
@@ -249,6 +251,8 @@ const addOptionKeyCombination = computed(() => {
249
251
  });
250
252
 
251
253
  const wrapperHeight = computed(() => {
254
+ if (!optionsRef.value.length) return "auto";
255
+
252
256
  const maxHeight = optionsRef.value
253
257
  .slice(0, props.visibleOptions)
254
258
  .map((el) => el.getBoundingClientRect().height)
@@ -1,7 +1,7 @@
1
1
  import useUI from "../../composable.ui";
2
2
  import { cva, cx } from "../../service.ui";
3
3
 
4
- import defaultConfig from "../configs/default.config";
4
+ import defaultConfig from "../configs/default.config.js";
5
5
  import { computed } from "vue";
6
6
 
7
7
  export default function useAttrs(props) {
@@ -80,8 +80,7 @@
80
80
  import { computed, onBeforeUnmount, onMounted, ref } from "vue";
81
81
  import { merge } from "lodash-es";
82
82
 
83
- import { globalComponentConfig, cx, getDefault } from "../service.ui";
84
-
83
+ import { cx, getDefault, vuelessConfig } from "../service.ui";
85
84
  import { useLocale } from "../composable.locale";
86
85
  import useAttrs from "./composables/attrs.composable";
87
86
 
@@ -193,7 +192,7 @@ function getOffsetWidth(selector) {
193
192
  }
194
193
 
195
194
  function setPosition() {
196
- const positionClasses = globalComponentConfig.UNotify?.positionClasses;
195
+ const positionClasses = vuelessConfig.component?.UNotify?.positionClasses;
197
196
  const pageClass = positionClasses?.page || config.value.positionClasses.page;
198
197
  const asideClass = positionClasses?.aside || config.value.positionClasses.aside;
199
198
  const pageWidth = getOffsetWidth(`${pageClass}`);
@@ -1,7 +1,7 @@
1
- import { globalComponentConfig, getRandomId } from "../../service.ui";
1
+ import { getRandomId, vuelessConfig } from "../../service.ui";
2
2
  import { DELAY_BETWEEN_CLONES, DURATION, LOCAL_STORAGE_ID, NOTIFY_TYPE } from "../constants";
3
3
 
4
- const globalNotifyDuration = globalComponentConfig.UNotify?.duration;
4
+ const globalNotifyDuration = vuelessConfig.component?.UNotify?.duration;
5
5
  const notifyClearAllEvent = new Event("notifyClearAll");
6
6
 
7
7
  let lastMessageTime = undefined;
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "vueless",
4
- "version": "0.0.325",
4
+ "version": "0.0.327",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",