vueless 1.3.9-beta.9 → 1.4.0
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.
- package/composables/useUI.ts +187 -155
- package/package.json +2 -2
- package/types.ts +11 -0
- package/ui.button-link/ULink.vue +18 -2
- package/ui.data-table/UTable.vue +568 -198
- package/ui.data-table/UTableRow.vue +372 -168
- package/ui.data-table/config.ts +8 -1
- package/ui.data-table/storybook/stories.ts +90 -0
- package/ui.data-table/tests/UTable.test.ts +317 -20
- package/ui.data-table/tests/UTableRow.test.ts +35 -104
- package/ui.data-table/types.ts +37 -0
- package/ui.data-table/utilTable.ts +16 -10
- package/ui.form-checkbox/UCheckbox.vue +3 -7
- package/ui.text-notify/config.ts +1 -1
- package/ui.text-number/UNumber.vue +36 -2
- package/ui.text-number/config.ts +1 -0
- package/ui.text-number/storybook/stories.ts +11 -0
- package/ui.text-number/tests/UNumber.test.ts +90 -0
- package/ui.text-number/types.ts +5 -0
- package/utils/node/mergeConfigs.d.ts +2 -2
- package/utils/node/mergeConfigs.js +240 -121
- package/utils/node/vuelessConfig.d.ts +1 -1
package/composables/useUI.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ref, watch, getCurrentInstance, toValue, useAttrs, computed } from "vue";
|
|
2
|
-
import { isEqual } from "lodash-es";
|
|
3
2
|
|
|
4
3
|
import { cx, cva, setColor, vuelessConfig, getMergedConfig } from "../utils/ui";
|
|
5
4
|
import {
|
|
@@ -9,7 +8,7 @@ import {
|
|
|
9
8
|
NESTED_COMPONENT_PATTERN_REG_EXP,
|
|
10
9
|
} from "../constants";
|
|
11
10
|
|
|
12
|
-
import type { Ref
|
|
11
|
+
import type { Ref } from "vue";
|
|
13
12
|
import type {
|
|
14
13
|
CVA,
|
|
15
14
|
UseUI,
|
|
@@ -20,11 +19,17 @@ import type {
|
|
|
20
19
|
UnknownObject,
|
|
21
20
|
ComponentNames,
|
|
22
21
|
NestedComponent,
|
|
22
|
+
ConfigDerivedData,
|
|
23
23
|
ComponentDefaults,
|
|
24
24
|
ComponentConfigFull,
|
|
25
25
|
VuelessComponentInstance,
|
|
26
26
|
} from "../types";
|
|
27
27
|
|
|
28
|
+
/* Pre-computed Set for O(1) system key lookups instead of O(n) array scan. */
|
|
29
|
+
const CVA_KEY_SET = new Set(Object.values(CVA_CONFIG_KEY));
|
|
30
|
+
const SYSTEM_KEY_SET = new Set(Object.values(SYSTEM_CONFIG_KEY));
|
|
31
|
+
const TRANSITION_KEY = SYSTEM_CONFIG_KEY.transition;
|
|
32
|
+
|
|
28
33
|
/**
|
|
29
34
|
* Merging component configs in a given sequence (bigger number = bigger priority):
|
|
30
35
|
* 1. Default component config
|
|
@@ -43,11 +48,26 @@ export function useUI<T>(defaultConfig: T, mutatedProps?: MutatedProps, topLevel
|
|
|
43
48
|
|
|
44
49
|
const firstClassKey = Object.keys(defaultConfig || {})[0];
|
|
45
50
|
const config = ref({}) as Ref<ComponentConfigFull<T>>;
|
|
51
|
+
const isDev = import.meta.env?.DEV;
|
|
52
|
+
const isUnstyled = Boolean(vuelessConfig.unstyled);
|
|
53
|
+
|
|
54
|
+
/* Hoist shared reactive primitives — create once, share across all keys. */
|
|
55
|
+
const attrs = useAttrs() as KeyAttrs;
|
|
56
|
+
const reactiveAttrsClass = computed(() => attrs.class);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Reactive wrapper for props — created once per component instead of once per key.
|
|
60
|
+
* Spreads props to create a shallow copy that triggers reactivity on any prop change.
|
|
61
|
+
*/
|
|
62
|
+
const reactiveProps = computed(() => ({ ...props }));
|
|
63
|
+
|
|
64
|
+
/* Cache for CVA resolver functions — only recreated when config changes. */
|
|
65
|
+
const cvaCache = new Map<string, ReturnType<typeof cva>>();
|
|
46
66
|
|
|
47
67
|
watch(
|
|
48
68
|
() => props.config,
|
|
49
69
|
(newVal, oldVal) => {
|
|
50
|
-
if (
|
|
70
|
+
if (newVal === oldVal) return;
|
|
51
71
|
|
|
52
72
|
const propsConfig = props.config as ComponentConfigFull<T>;
|
|
53
73
|
|
|
@@ -55,195 +75,209 @@ export function useUI<T>(defaultConfig: T, mutatedProps?: MutatedProps, topLevel
|
|
|
55
75
|
defaultConfig,
|
|
56
76
|
globalConfig,
|
|
57
77
|
propsConfig,
|
|
58
|
-
unstyled:
|
|
78
|
+
unstyled: isUnstyled,
|
|
59
79
|
}) as ComponentConfigFull<T>;
|
|
80
|
+
|
|
81
|
+
/* Invalidate CVA cache when config changes. */
|
|
82
|
+
cvaCache.clear();
|
|
60
83
|
},
|
|
61
84
|
{ deep: true, immediate: true },
|
|
62
85
|
);
|
|
63
86
|
|
|
64
87
|
/**
|
|
65
|
-
*
|
|
88
|
+
* Pre-computed data per config key that only changes when config changes.
|
|
89
|
+
* Avoids recomputing extends configs, nested component info, and merged configs on every prop change.
|
|
66
90
|
*/
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const mutatedPropsValue = toValue(mutatedProps);
|
|
70
|
-
const value = (config.value as ComponentConfigFull<T>)[key];
|
|
71
|
-
const color = (toValue(mutatedProps || {}).color || props.color) as StateColors;
|
|
72
|
-
|
|
73
|
-
const isNestedComponent = Boolean(getNestedComponent(value));
|
|
74
|
-
|
|
75
|
-
let classes = "";
|
|
76
|
-
|
|
77
|
-
if (typeof value === "object" && isCVA(value)) {
|
|
78
|
-
classes = cva(value)({
|
|
79
|
-
...props,
|
|
80
|
-
...mutatedPropsValue,
|
|
81
|
-
...(color ? { color } : {}),
|
|
82
|
-
});
|
|
83
|
-
}
|
|
91
|
+
const configDerivedData = computed(() => {
|
|
92
|
+
const data: ConfigDerivedData = {};
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
94
|
+
for (const key in config.value) {
|
|
95
|
+
if (isSystemKey(key)) continue;
|
|
88
96
|
|
|
89
|
-
|
|
90
|
-
.
|
|
91
|
-
.replace(NESTED_COMPONENT_PATTERN_REG_EXP, "");
|
|
97
|
+
const keyConfig: NestedComponent =
|
|
98
|
+
typeof config.value[key] === "object" ? (config.value[key] as NestedComponent) : {};
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
const extendsKeyConfig = computeExtendsKeyConfig(key);
|
|
101
|
+
const extendsKeyNestedComponent = getNestedComponent(extendsKeyConfig);
|
|
102
|
+
const keyNestedComponent = getNestedComponent(config.value[key]);
|
|
103
|
+
const nestedComponent = extendsKeyNestedComponent || keyNestedComponent || componentName;
|
|
96
104
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const keysAttrs: KeysAttrs<T> = {};
|
|
105
|
+
const mergedNestedConfig = getMergedConfig({
|
|
106
|
+
defaultConfig: extendsKeyConfig,
|
|
107
|
+
globalConfig: keyConfig,
|
|
108
|
+
propsConfig: attrs["config"] || {},
|
|
109
|
+
unstyled: isUnstyled,
|
|
110
|
+
});
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
const mergedDefaults: ComponentDefaults = {};
|
|
113
|
+
|
|
114
|
+
const defaultAttrs = {
|
|
115
|
+
...(extendsKeyConfig.defaults || {}),
|
|
116
|
+
...(keyConfig.defaults || {}),
|
|
117
|
+
};
|
|
107
118
|
|
|
108
|
-
|
|
119
|
+
for (const defaultKey in defaultAttrs) {
|
|
120
|
+
mergedDefaults[defaultKey] =
|
|
121
|
+
typeof defaultAttrs[defaultKey] === "object"
|
|
122
|
+
? defaultAttrs[defaultKey][String(props[defaultKey])]
|
|
123
|
+
: defaultAttrs[defaultKey];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
data[key] = {
|
|
127
|
+
keyConfig,
|
|
128
|
+
extendsClasses: computeExtendsClasses(key),
|
|
129
|
+
extendsKeyConfig,
|
|
130
|
+
nestedComponent,
|
|
131
|
+
mergedNestedConfig,
|
|
132
|
+
mergedDefaults,
|
|
133
|
+
};
|
|
109
134
|
}
|
|
110
135
|
|
|
111
|
-
return
|
|
112
|
-
}
|
|
136
|
+
return data;
|
|
137
|
+
});
|
|
113
138
|
|
|
114
139
|
/**
|
|
115
|
-
*
|
|
140
|
+
* Compute classes for a given key directly (not as a computed ref).
|
|
141
|
+
* Used inside watchers to avoid creating orphaned computed properties.
|
|
116
142
|
*/
|
|
117
|
-
function
|
|
118
|
-
const
|
|
143
|
+
function computeClassesForKey(key: string) {
|
|
144
|
+
const mutatedPropsValue = toValue(mutatedProps);
|
|
145
|
+
const value = (config.value as ComponentConfigFull<T>)[key];
|
|
146
|
+
const color = (toValue(mutatedProps || {}).color || props.color) as StateColors;
|
|
119
147
|
|
|
120
|
-
const
|
|
148
|
+
const isNestedComponent = Boolean(getNestedComponent(value));
|
|
121
149
|
|
|
122
|
-
|
|
123
|
-
const reactiveClass = computed(() => attrs.class);
|
|
150
|
+
let classes = "";
|
|
124
151
|
|
|
125
|
-
|
|
152
|
+
if (typeof value === "object" && isCVA(value)) {
|
|
153
|
+
let cvaFn = cvaCache.get(key);
|
|
126
154
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (isEqual(newVal, oldVal)) return;
|
|
155
|
+
if (!cvaFn) {
|
|
156
|
+
cvaFn = cva(value);
|
|
157
|
+
cvaCache.set(key, cvaFn);
|
|
158
|
+
}
|
|
132
159
|
|
|
133
|
-
|
|
160
|
+
classes = cvaFn({
|
|
161
|
+
...props,
|
|
162
|
+
...mutatedPropsValue,
|
|
163
|
+
...(color ? { color } : {}),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
134
166
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
167
|
+
if (typeof value === "string") {
|
|
168
|
+
classes = value;
|
|
169
|
+
}
|
|
138
170
|
|
|
139
|
-
|
|
140
|
-
|
|
171
|
+
classes = classes
|
|
172
|
+
.replaceAll(EXTENDS_PATTERN_REG_EXP, "")
|
|
173
|
+
.replace(NESTED_COMPONENT_PATTERN_REG_EXP, "");
|
|
141
174
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const extendsKeyNestedComponent = getNestedComponent(extendsKeyConfig);
|
|
145
|
-
const keyNestedComponent = getNestedComponent(config.value[configKey]);
|
|
146
|
-
const nestedComponent = extendsKeyNestedComponent || keyNestedComponent || componentName;
|
|
175
|
+
return color && !isNestedComponent ? setColor(classes, color) : classes;
|
|
176
|
+
}
|
|
147
177
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"vl-child-key": isDev && attrs["vl-component"] ? configKey : null,
|
|
154
|
-
};
|
|
178
|
+
/**
|
|
179
|
+
* Recursively compute extends classes directly (no orphaned computed refs).
|
|
180
|
+
*/
|
|
181
|
+
function computeExtendsClasses(configKey: string): string[] {
|
|
182
|
+
const extendsKeys = getExtendsKeys(config.value[configKey]);
|
|
155
183
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
globalConfig: keyConfig,
|
|
165
|
-
propsConfig: attrs["config"] || {},
|
|
166
|
-
unstyled: Boolean(vuelessConfig.unstyled),
|
|
167
|
-
}),
|
|
168
|
-
...getDefaults({
|
|
169
|
-
...(extendsKeyConfig.defaults || {}),
|
|
170
|
-
...(keyConfig.defaults || {}),
|
|
171
|
-
}),
|
|
172
|
-
};
|
|
184
|
+
if (!extendsKeys.length) return [];
|
|
185
|
+
|
|
186
|
+
const result: string[] = [];
|
|
187
|
+
|
|
188
|
+
for (const key of extendsKeys) {
|
|
189
|
+
if (key === configKey) continue;
|
|
190
|
+
|
|
191
|
+
result.push(...computeExtendsClasses(key), computeClassesForKey(key));
|
|
173
192
|
}
|
|
174
193
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
*/
|
|
178
|
-
function getExtendsClasses(configKey: string) {
|
|
179
|
-
let extendsClasses: string[] = [];
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
180
196
|
|
|
181
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Merge extends nested component configs.
|
|
199
|
+
* TODO: Add ability to merge multiple keys in one (now works for merging only 1 first key).
|
|
200
|
+
*/
|
|
201
|
+
function computeExtendsKeyConfig(configKey: string): NestedComponent {
|
|
202
|
+
const propsConfig = props.config as ComponentConfigFull<T>;
|
|
203
|
+
const extendsKeys = getExtendsKeys(config.value[configKey]);
|
|
182
204
|
|
|
183
|
-
|
|
184
|
-
extendsKeys.forEach((key) => {
|
|
185
|
-
if (key === configKey) return;
|
|
205
|
+
if (!extendsKeys.length) return {};
|
|
186
206
|
|
|
187
|
-
|
|
188
|
-
...extendsClasses,
|
|
189
|
-
...getExtendsClasses(key),
|
|
190
|
-
toValue(getClasses(key, mutatedProps)),
|
|
191
|
-
];
|
|
192
|
-
});
|
|
193
|
-
}
|
|
207
|
+
const [firstKey] = extendsKeys;
|
|
194
208
|
|
|
195
|
-
|
|
209
|
+
if (config.value[firstKey] === undefined) {
|
|
210
|
+
// eslint-disable-next-line no-console
|
|
211
|
+
console.warn(`[vueless] Missing ${firstKey} extend key.`);
|
|
196
212
|
}
|
|
197
213
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const propsConfig = props.config as ComponentConfigFull<T>;
|
|
206
|
-
const extendsKeys = getExtendsKeys(config.value[configKey]);
|
|
214
|
+
return getMergedConfig({
|
|
215
|
+
defaultConfig: config.value[firstKey] || {},
|
|
216
|
+
globalConfig: globalConfig[firstKey],
|
|
217
|
+
propsConfig: propsConfig[firstKey],
|
|
218
|
+
unstyled: isUnstyled,
|
|
219
|
+
}) as NestedComponent;
|
|
220
|
+
}
|
|
207
221
|
|
|
208
|
-
|
|
209
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Returns an object where:
|
|
224
|
+
* – key: elementKey
|
|
225
|
+
* – value: reactive object of string element attributes (with classes).
|
|
226
|
+
*/
|
|
227
|
+
function getKeysAttrs(mutatedProps?: MutatedProps) {
|
|
228
|
+
const keysAttrs: KeysAttrs<T> = {};
|
|
229
|
+
const attrsRefs: Record<string, Ref<KeyAttrs>> = {};
|
|
210
230
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
console.warn(`[vueless] Missing ${firstKey} extend key.`);
|
|
214
|
-
}
|
|
231
|
+
for (const key in config.value) {
|
|
232
|
+
if (isSystemKey(key)) continue;
|
|
215
233
|
|
|
216
|
-
|
|
217
|
-
defaultConfig: config.value[firstKey] || {},
|
|
218
|
-
globalConfig: globalConfig[firstKey],
|
|
219
|
-
propsConfig: propsConfig[firstKey],
|
|
220
|
-
unstyled: Boolean(vuelessConfig.unstyled),
|
|
221
|
-
}) as NestedComponent;
|
|
222
|
-
}
|
|
234
|
+
const vuelessAttrs = ref({} as KeyAttrs);
|
|
223
235
|
|
|
224
|
-
|
|
236
|
+
attrsRefs[key] = vuelessAttrs;
|
|
237
|
+
keysAttrs[`${key}Attrs`] = vuelessAttrs;
|
|
225
238
|
}
|
|
226
239
|
|
|
227
240
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
* Single consolidated watcher instead of N per-key watchers.
|
|
242
|
+
* Watches: config (for config changes), reactiveProps (for prop changes),
|
|
243
|
+
* mutatedProps (for slot/computed prop changes), reactiveAttrsClass (for class attr changes).
|
|
244
|
+
*/
|
|
245
|
+
watch(
|
|
246
|
+
[config, reactiveProps, mutatedProps || (() => undefined), reactiveAttrsClass],
|
|
247
|
+
() => {
|
|
248
|
+
const derived = configDerivedData.value;
|
|
249
|
+
|
|
250
|
+
for (const key in attrsRefs) {
|
|
251
|
+
const data = derived[key];
|
|
252
|
+
|
|
253
|
+
if (!data) continue;
|
|
254
|
+
|
|
255
|
+
const isTopLevelKey = (topLevelClassKey || firstClassKey) === key;
|
|
256
|
+
const classes = computeClassesForKey(key);
|
|
257
|
+
|
|
258
|
+
const commonAttrs: KeyAttrs = {
|
|
259
|
+
...(isTopLevelKey ? attrs : {}),
|
|
260
|
+
"vl-component": isDev ? attrs["vl-component"] || componentName || null : null,
|
|
261
|
+
"vl-key": isDev ? attrs["vl-key"] || key || null : null,
|
|
262
|
+
"vl-child-component": isDev && attrs["vl-component"] ? data.nestedComponent : null,
|
|
263
|
+
"vl-child-key": isDev && attrs["vl-component"] ? key : null,
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/* Delete value key to prevent v-model overwrite. */
|
|
267
|
+
delete commonAttrs.value;
|
|
268
|
+
|
|
269
|
+
attrsRefs[key].value = {
|
|
270
|
+
...commonAttrs,
|
|
271
|
+
class: cx([...data.extendsClasses, classes, commonAttrs.class]),
|
|
272
|
+
config: data.mergedNestedConfig,
|
|
273
|
+
...data.mergedDefaults,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
{ immediate: true },
|
|
278
|
+
);
|
|
245
279
|
|
|
246
|
-
return
|
|
280
|
+
return keysAttrs;
|
|
247
281
|
}
|
|
248
282
|
|
|
249
283
|
/**
|
|
@@ -275,7 +309,7 @@ function getExtendsKeys(configItemValue?: string | CVA | NestedComponent): strin
|
|
|
275
309
|
const values = getBaseClasses(configItemValue);
|
|
276
310
|
const matches = values.match(EXTENDS_PATTERN_REG_EXP);
|
|
277
311
|
|
|
278
|
-
return matches ? matches
|
|
312
|
+
return matches ? matches.map((pattern) => pattern.slice(2, -1)) : [];
|
|
279
313
|
}
|
|
280
314
|
|
|
281
315
|
/**
|
|
@@ -292,9 +326,7 @@ function getNestedComponent(value?: string | CVA | NestedComponent) {
|
|
|
292
326
|
* Check is config key not contains classes or CVA config object.
|
|
293
327
|
*/
|
|
294
328
|
function isSystemKey(key: string): boolean {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return isExactKey || key.toLowerCase().includes(SYSTEM_CONFIG_KEY.transition.toLowerCase());
|
|
329
|
+
return SYSTEM_KEY_SET.has(key) || key.toLowerCase().includes(TRANSITION_KEY);
|
|
298
330
|
}
|
|
299
331
|
|
|
300
332
|
/**
|
|
@@ -305,7 +337,7 @@ function isCVA(config?: UnknownObject | string): boolean {
|
|
|
305
337
|
return false;
|
|
306
338
|
}
|
|
307
339
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
);
|
|
340
|
+
const keys = Object.keys(config);
|
|
341
|
+
|
|
342
|
+
return keys.some((key) => CVA_KEY_SET.has(key));
|
|
311
343
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vueless",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
|
|
5
5
|
"author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
|
|
6
6
|
"homepage": "https://vueless.com",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
58
58
|
"@vue/test-utils": "^2.4.6",
|
|
59
59
|
"@vue/tsconfig": "^0.7.0",
|
|
60
|
-
"@vueless/storybook": "^1.4.
|
|
60
|
+
"@vueless/storybook": "^1.4.10",
|
|
61
61
|
"eslint": "^9.32.0",
|
|
62
62
|
"eslint-plugin-storybook": "^10.0.2",
|
|
63
63
|
"eslint-plugin-vue": "^10.3.0",
|
package/types.ts
CHANGED
|
@@ -385,6 +385,17 @@ export type UseUI<T> = {
|
|
|
385
385
|
getDataTest: (suffix?: string) => string | null;
|
|
386
386
|
} & KeysAttrs<T>;
|
|
387
387
|
|
|
388
|
+
export interface ConfigDerivedData {
|
|
389
|
+
[key: string]: {
|
|
390
|
+
keyConfig: NestedComponent;
|
|
391
|
+
extendsClasses: string[];
|
|
392
|
+
extendsKeyConfig: NestedComponent;
|
|
393
|
+
nestedComponent: string;
|
|
394
|
+
mergedNestedConfig: unknown;
|
|
395
|
+
mergedDefaults: ComponentDefaults;
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
388
399
|
export type KeysAttrs<T> = Record<
|
|
389
400
|
string,
|
|
390
401
|
Ref<KeyAttrsWithConfig<T>> | ComputedRef<KeyAttrsWithConfig<T>>
|
package/ui.button-link/ULink.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, useSlots, useTemplateRef } from "vue";
|
|
3
|
-
import { RouterLink } from "vue-router";
|
|
3
|
+
import { RouterLink, useRoute, useRouter } from "vue-router";
|
|
4
4
|
|
|
5
5
|
import { useUI } from "../composables/useUI";
|
|
6
6
|
import { hasSlotContent } from "../utils/helper";
|
|
@@ -49,6 +49,8 @@ const emit = defineEmits([
|
|
|
49
49
|
const slots = useSlots();
|
|
50
50
|
|
|
51
51
|
const linkRef = useTemplateRef<HTMLLinkElement>("link");
|
|
52
|
+
const route = useRoute();
|
|
53
|
+
const router = useRouter();
|
|
52
54
|
|
|
53
55
|
const isPresentRoute = computed(() => {
|
|
54
56
|
return typeof props.to === "string" || typeof props.to === "object";
|
|
@@ -58,6 +60,20 @@ const safeTo = computed(() => {
|
|
|
58
60
|
return props.to || "/";
|
|
59
61
|
});
|
|
60
62
|
|
|
63
|
+
const isActive = computed(() => {
|
|
64
|
+
if (!isPresentRoute.value) return false;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const resolved = router.resolve(safeTo.value);
|
|
68
|
+
const currentPath = route.path;
|
|
69
|
+
const targetPath = resolved.path;
|
|
70
|
+
|
|
71
|
+
return currentPath === targetPath || currentPath.startsWith(targetPath + "/");
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
61
77
|
const prefixedHref = computed(() => {
|
|
62
78
|
const types = {
|
|
63
79
|
phone: "tel:",
|
|
@@ -142,7 +158,7 @@ const { getDataTest, linkAttrs } = useUI<Config>(defaultConfig, mutatedProps);
|
|
|
142
158
|
@binding {boolean} is-active
|
|
143
159
|
@binding {boolean} is-exact-active
|
|
144
160
|
-->
|
|
145
|
-
<slot :is-active="
|
|
161
|
+
<slot :is-active="isActive" :is-exact-active="slotProps.isExactActive">
|
|
146
162
|
{{ label }}
|
|
147
163
|
</slot>
|
|
148
164
|
</router-link>
|