vueless 0.0.603 → 0.0.604

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.
@@ -93,6 +93,8 @@ export default function useUI<T>(
93
93
  classes = cx([classes, attrs.class]);
94
94
  }
95
95
 
96
+ classes = classes.replaceAll(EXTENDS_PATTERN_REG_EXP, "");
97
+
96
98
  return color ? setColor(classes, color) : classes;
97
99
  });
98
100
  }
@@ -118,12 +120,12 @@ export default function useUI<T>(
118
120
  * Get an element attributes for a given key.
119
121
  */
120
122
  function getAttrs(configKey: string, classes: ComputedRef<string>) {
121
- const nestedComponent = getNestedComponent(config.value[configKey] || "");
123
+ const vuelessAttrs = ref({});
122
124
 
123
125
  const attrs = useAttrs() as KeyAttrs;
124
126
  const isDev = isCSR && import.meta.env?.DEV;
125
- const vuelessAttrs = ref({});
126
127
  const isTopLevelKey = (topLevelClassKey || firstClassKey) === configKey;
128
+ const nestedComponent = getNestedComponent(config.value[configKey] || "");
127
129
 
128
130
  const commonAttrs: KeyAttrs = {
129
131
  ...(isTopLevelKey ? attrs : {}),
@@ -139,32 +141,25 @@ export default function useUI<T>(
139
141
 
140
142
  watch(config, updateVuelessAttrs, { immediate: true });
141
143
  watch(props, updateVuelessAttrs);
144
+ watch(classes, updateVuelessAttrs);
142
145
 
143
- if (classes?.value) {
144
- watch(classes, updateVuelessAttrs);
145
- }
146
-
146
+ /**
147
+ * Updating Vueless attributes.
148
+ */
147
149
  function updateVuelessAttrs() {
148
150
  let configAttr: NestedComponent = {};
149
- let extendsConfigAttr: NestedComponent = {};
150
- let extendsClasses: string[] = [];
151
-
152
- const baseClasses = getBaseClasses(config.value[configKey]);
153
- const extendsKeys = getExtendsKeys(baseClasses);
154
151
 
155
152
  if (typeof config.value[configKey] === "object") {
156
153
  configAttr = config.value[configKey] as NestedComponent;
157
154
  }
158
155
 
159
- if (extendsKeys.length) {
160
- extendsClasses = extendsKeys.map((key) => toValue(getClasses(key, mutatedProps)));
161
- extendsConfigAttr = getExtendsConfig(extendsKeys);
162
- }
156
+ const extendsClasses = getExtendsClasses(configKey);
157
+ const extendsConfigAttr = getExtendsConfigAttr(configKey);
163
158
 
164
159
  vuelessAttrs.value = {
165
160
  ...commonAttrs,
166
- class: cx([...extendsClasses, toValue(classes).replaceAll(EXTENDS_PATTERN_REG_EXP, "")]),
167
- config: merge(configAttr, extendsConfigAttr),
161
+ class: cx([...extendsClasses, toValue(classes)]),
162
+ config: merge({}, configAttr, extendsConfigAttr),
168
163
  ...getDefaults({
169
164
  ...(configAttr.defaults || {}),
170
165
  ...(extendsConfigAttr.defaults || {}),
@@ -172,21 +167,51 @@ export default function useUI<T>(
172
167
  };
173
168
  }
174
169
 
170
+ /**
171
+ * Recursively get extends classes.
172
+ */
173
+ function getExtendsClasses(configKey: string) {
174
+ let extendsClasses: string[] = [];
175
+
176
+ const extendsKeys = getExtendsKeys(config.value[configKey]);
177
+
178
+ if (extendsKeys.length) {
179
+ extendsKeys.forEach((key) => {
180
+ extendsClasses = [
181
+ ...extendsClasses,
182
+ ...getExtendsClasses(key),
183
+ toValue(getClasses(key, mutatedProps)),
184
+ ];
185
+ });
186
+ }
187
+
188
+ return extendsClasses;
189
+ }
190
+
175
191
  /**
176
192
  * Merge extends nested component configs.
177
193
  * TODO: Add ability to merge multiple keys in one (now works for merging only 1 first key).
178
194
  */
179
- function getExtendsConfig(extendsKeys: string[]) {
180
- const [firstKey] = extendsKeys;
181
-
182
- return getMergedConfig({
183
- defaultConfig: config.value[firstKey],
184
- globalConfig: globalConfig[firstKey],
185
- propsConfig: propsConfig[firstKey],
186
- }) as NestedComponent;
195
+ function getExtendsConfigAttr(configKey: string) {
196
+ let extendsConfigAttr: NestedComponent = {};
197
+
198
+ const extendsKeys = getExtendsKeys(config.value[configKey]);
199
+
200
+ if (extendsKeys.length) {
201
+ const [firstKey] = extendsKeys;
202
+
203
+ extendsConfigAttr = getMergedConfig({
204
+ defaultConfig: config.value[firstKey],
205
+ globalConfig: globalConfig[firstKey],
206
+ propsConfig: propsConfig[firstKey],
207
+ }) as NestedComponent;
208
+ }
209
+
210
+ return extendsConfigAttr;
187
211
  }
188
212
 
189
213
  /**
214
+ * Get component prop default value.
190
215
  * Conditionally set props default value for nested components based on parent component prop value.
191
216
  * For example, set icon size for the nested component based on the size of the parent component.
192
217
  * Use an object where key = parent component prop value, value = nested component prop value.
@@ -221,7 +246,8 @@ function getBaseClasses(value: string | CVA | undefined) {
221
246
  * Retrieves extends keys from patterns:
222
247
  * Example: `{>someKey} {>someOtherKey}` >>> `["someKey", "someOtherKey"]`.
223
248
  */
224
- function getExtendsKeys(values: string = ""): string[] {
249
+ function getExtendsKeys(configItemValue?: CVA | string): string[] {
250
+ const values = getBaseClasses(configItemValue);
225
251
  const matches = values.match(EXTENDS_PATTERN_REG_EXP);
226
252
 
227
253
  return matches ? matches?.map((pattern) => pattern.slice(2, -1)) : [];
@@ -249,7 +275,11 @@ function isSystemKey(key: string): boolean {
249
275
  /**
250
276
  * Check is config contains default CVA keys.
251
277
  */
252
- function isCVA(config: UnknownObject): boolean {
278
+ function isCVA(config?: UnknownObject | string): boolean {
279
+ if (typeof config !== "object") {
280
+ return false;
281
+ }
282
+
253
283
  return Object.values(CVA_CONFIG_KEY).some((value) =>
254
284
  Object.keys(config).some((key) => key === value),
255
285
  );
@@ -24,7 +24,6 @@ if (isCSR) {
24
24
  animation: "shift-away",
25
25
  };
26
26
 
27
- settings = merge(defaultSettings, {});
28
27
  settings = merge(defaultSettings, vuelessConfig.directive?.tooltip || {}) as DefaultProps;
29
28
  tippy.setDefaultProps(settings);
30
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.603",
3
+ "version": "0.0.604",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -49,7 +49,9 @@ const isShownModal = computed({
49
49
  });
50
50
 
51
51
  const i18nGlobal = tm(UModalConfirm);
52
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props?.config?.i18n));
52
+ const currentLocale = computed(() =>
53
+ merge({}, defaultConfig.i18n, i18nGlobal, props?.config?.i18n),
54
+ );
53
55
 
54
56
  function closeModal() {
55
57
  isShownModal.value = false;
@@ -48,7 +48,7 @@ const emit = defineEmits([
48
48
  const { tm } = useLocale();
49
49
 
50
50
  const i18nGlobal = tm(UDataListName);
51
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
51
+ const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n));
52
52
 
53
53
  const iconSize = computed(() => {
54
54
  const sizes = {
@@ -96,7 +96,7 @@ const stickyActionHeaderRowRef = useTemplateRef<HTMLDivElement>("sticky-action-h
96
96
  const actionHeaderRowRef = useTemplateRef<HTMLDivElement>("action-header-row");
97
97
 
98
98
  const i18nGlobal = tm(UTable);
99
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
99
+ const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n));
100
100
 
101
101
  const sortedRows: ComputedRef<Row[]> = computed(() => {
102
102
  const headerKeys = props.columns.map((column) =>
@@ -40,14 +40,7 @@ export default /*tw*/ {
40
40
  },
41
41
  headerCellCheckbox: "{>headerCellBase} w-10 pr-2",
42
42
  headerCheckbox: "{UCheckbox}",
43
- headerCounter: {
44
- base: "{>headerCounterBase} absolute top-4 mt-px left-11 ml-px",
45
- variants: {
46
- compact: {
47
- true: "top-3",
48
- },
49
- },
50
- },
43
+ headerCounter: "{>stickyHeaderCounter} mt-px ml-px",
51
44
  headerLoader: "{ULoaderProgress} absolute !top-auto",
52
45
  body: "group/body divide-none",
53
46
  bodyRow: "hover:bg-gray-50",
@@ -108,7 +101,6 @@ export default /*tw*/ {
108
101
  },
109
102
  defaults: {
110
103
  emptyCellLabel: "—",
111
- nesting: false,
112
104
  compact: false,
113
105
  selectable: false,
114
106
  dateDivider: false,
@@ -39,12 +39,12 @@ export default {
39
39
  argTypes: {
40
40
  ...getArgTypes(UTable.__name),
41
41
  row: {
42
- description:
43
- "The row of the table. It's not a prop (it created for ease of work with storybook).",
42
+ table: {
43
+ disable: true,
44
+ },
44
45
  },
45
46
  numberOfRows: {
46
- description:
47
- "The number of table rows. It's not a prop (it created for ease of work with storybook).",
47
+ description: "The number of table rows (not a component prop).",
48
48
  },
49
49
  },
50
50
  args: {
@@ -5,16 +5,7 @@ import type { ComponentConfig, UnknownObject } from "../types.ts";
5
5
 
6
6
  export type Config = typeof defaultConfig;
7
7
 
8
- type RowKeys =
9
- | number
10
- | string
11
- | boolean
12
- | undefined
13
- | Date
14
- | Row
15
- | Row[]
16
- | string
17
- | ((row: Row) => string);
8
+ type RowKeys = number | string | boolean | undefined | Date | Row | Row[] | ((row: Row) => string);
18
9
 
19
10
  export interface CellObject {
20
11
  contentClasses?: string | ((value: unknown | string, row: Row) => string);
@@ -54,7 +54,7 @@ const elementId = props.id || useId();
54
54
  const { tm } = useLocale();
55
55
 
56
56
  const i18nGlobal = tm(UDropdownList);
57
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
57
+ const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n));
58
58
 
59
59
  const addOptionKeyCombination = computed(() => {
60
60
  return isMac ? "(⌘ + Enter)" : "(Ctrl + Enter)";
@@ -129,7 +129,7 @@ const isCurrentView = computed(() => ({
129
129
  const i18nGlobal = tm<DefaultLocale>(UCalendar);
130
130
 
131
131
  const currentLocale: ComputedRef<Locale> = computed(() =>
132
- merge(defaultConfig.i18n, i18nGlobal, props.config?.i18n),
132
+ merge({}, defaultConfig.i18n, i18nGlobal, props.config?.i18n),
133
133
  );
134
134
 
135
135
  const locale = computed(() => {
@@ -82,7 +82,7 @@ const localValue = computed({
82
82
  });
83
83
 
84
84
  const currentLocale: ComputedRef<Locale> = computed(() =>
85
- merge(defaultConfig.i18n, i18nGlobal, props.config.i18n),
85
+ merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n),
86
86
  );
87
87
 
88
88
  const clickOutsideOptions = computed(() => ({ ignore: [datepickerInputRef.value?.inputRef] }));
@@ -268,7 +268,7 @@ watchEffect(() => {
268
268
  const calendarConfig = datepickerCalendarAttrs.value.config as unknown as UCalendarConfig;
269
269
 
270
270
  if (!calendarConfig?.i18n || props.config?.i18n) {
271
- calendarConfig.i18n = merge(calendarConfig.i18n, config.value.i18n);
271
+ calendarConfig.i18n = merge({}, calendarConfig.i18n, config.value.i18n);
272
272
  }
273
273
  });
274
274
  </script>
@@ -573,7 +573,7 @@ watchEffect(() => {
573
573
  const calendarConfig = datepickerCalendarAttrs.value.config as unknown as UCalendarConfig;
574
574
 
575
575
  if (!calendarConfig.i18n || props.config?.i18n) {
576
- calendarConfig.i18n = merge(calendarConfig.i18n, config.value.i18n);
576
+ calendarConfig.i18n = merge({}, calendarConfig.i18n, config.value.i18n);
577
577
  }
578
578
  });
579
579
  </script>
@@ -15,7 +15,9 @@ export function useLocale(props: UDatePickerRangeProps<unknown>) {
15
15
 
16
16
  const i18nGlobal = tm<Locale>(UDatePickerRange);
17
17
 
18
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config?.i18n));
18
+ const currentLocale = computed(() =>
19
+ merge({}, defaultConfig.i18n, i18nGlobal, props.config?.i18n),
20
+ );
19
21
 
20
22
  const locale = computed(() => {
21
23
  const { months, weekdays } = currentLocale.value;
@@ -50,7 +50,7 @@ const fileInputRef = ref<HTMLInputElement | null>(null);
50
50
  const elementId = props.id || useId();
51
51
 
52
52
  const i18nGlobal = tm(UInputFile);
53
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
53
+ const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n));
54
54
 
55
55
  const currentFiles = computed<File | File[] | null>({
56
56
  get: () => props.modelValue,
@@ -97,7 +97,7 @@ const innerWrapperRef = ref<HTMLDivElement | null>(null);
97
97
  const elementId = props.id || useId();
98
98
 
99
99
  const i18nGlobal = tm(USelect);
100
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
100
+ const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n));
101
101
 
102
102
  const isTop = computed(() => {
103
103
  if (props.openDirection === DIRECTION.top) return true;
@@ -33,7 +33,7 @@ const emit = defineEmits([
33
33
  const { tm } = useLocale();
34
34
 
35
35
  const i18nGlobal = tm(USwitch);
36
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config?.i18n));
36
+ const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config?.i18n));
37
37
 
38
38
  const checkedValue = computed({
39
39
  get: () => props.modelValue,
@@ -213,6 +213,6 @@ const { stripeAttrs } = useUI<Config>(defaultConfig);
213
213
 
214
214
  <template>
215
215
  <Transition :css="false" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
216
- <div v-if="show" v-bind="stripeAttrs" :style="barStyle" />
216
+ <div v-if="show" v-bind="stripeAttrs" :data-test="dataTest" :style="barStyle" />
217
217
  </Transition>
218
218
  </template>
@@ -49,4 +49,9 @@ export interface Props {
49
49
  * Component config object.
50
50
  */
51
51
  config?: ComponentConfig<Config>;
52
+
53
+ /**
54
+ * Data-test attribute for automated testing.
55
+ */
56
+ dataTest?: string;
52
57
  }
@@ -59,7 +59,6 @@ export default /*tw*/ {
59
59
  decimalSeparator: ",",
60
60
  thousandsSeparator: " ",
61
61
  planned: false,
62
- integer: false,
63
62
  symbolDivided: true,
64
63
  },
65
64
  };
@@ -27,7 +27,7 @@ const notifyPositionStyles = ref({});
27
27
  const notificationsWrapperRef = ref<NotificationsWrapperRef | null>(null);
28
28
 
29
29
  const i18nGlobal = tm(UNotify);
30
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
30
+ const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n));
31
31
 
32
32
  onMounted(() => {
33
33
  window.addEventListener("resize", setPosition, { passive: true });
@@ -311,5 +311,5 @@ async function getDefaults() {
311
311
  const defaultConfigPath = path.join(cwd, defaultIconsDir, COMPONENTS[U_ICON], "config.ts");
312
312
  const uIconDefaultConfig = await getComponentDefaultConfig(U_ICON, defaultConfigPath);
313
313
 
314
- return merge(uIconDefaultConfig?.defaults, vuelessConfig?.component?.[U_ICON]?.defaults);
314
+ return merge({}, uIconDefaultConfig?.defaults, vuelessConfig?.component?.[U_ICON]?.defaults);
315
315
  }
package/utils/ui.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { merge } from "lodash-es";
2
2
  import { defineConfig } from "cva";
3
3
  import { extendTailwindMerge } from "tailwind-merge";
4
- import { cloneDeep, isCSR, isSSR } from "./helper.ts";
4
+ import { isCSR, isSSR } from "./helper.ts";
5
5
  import { createGetMergedConfig } from "./node/mergeConfigs.js";
6
6
  import { UIcon } from "../ui.image-icon/constants.ts";
7
7
  import {
@@ -99,10 +99,10 @@ export const cva = ({ base = "", variants = {}, compoundVariants = [], defaultVa
99
99
  * Return default values for component props, icons, etc..
100
100
  */
101
101
  export function getDefaults<Props, Config>(defaultConfig: Config, name: ComponentNames) {
102
- const componentDefaults = cloneDeep((defaultConfig as UnknownObject).defaults) || {};
103
- const globalDefaults = cloneDeep(vuelessConfig.component?.[name]?.defaults) || {};
102
+ const componentDefaults = (defaultConfig as UnknownObject).defaults || {};
103
+ const globalDefaults = vuelessConfig.component?.[name]?.defaults || {};
104
104
 
105
- const defaults = merge(componentDefaults, globalDefaults) as Props & Defaults;
105
+ const defaults = merge({}, componentDefaults, globalDefaults) as Props & Defaults;
106
106
 
107
107
  if (defaults.color) {
108
108
  defaults.color = getColor(defaults.color as BrandColors);
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "vueless",
4
- "version": "0.0.603",
4
+ "version": "0.0.604",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",
@@ -7858,6 +7858,15 @@
7858
7858
  "kind": "expression",
7859
7859
  "type": "ComponentConfig"
7860
7860
  }
7861
+ },
7862
+ {
7863
+ "name": "dataTest",
7864
+ "required": false,
7865
+ "description": "Data-test attribute for automated testing.",
7866
+ "value": {
7867
+ "kind": "expression",
7868
+ "type": "string"
7869
+ }
7861
7870
  }
7862
7871
  ],
7863
7872
  "source": {