vueless 1.5.2-beta.2 → 1.5.2-beta.4
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/package.json +1 -1
- package/plugin-vite.js +27 -17
- package/ui.form-date-picker/storybook/stories.ts +2 -2
- package/ui.form-date-picker-range/storybook/stories.ts +2 -2
- package/ui.form-input/UInput.vue +2 -12
- package/ui.form-input/config.ts +4 -4
- package/ui.form-input/storybook/stories.ts +1 -1
- package/ui.form-input-number/storybook/stories.ts +1 -1
- package/ui.form-input-password/storybook/stories.ts +2 -2
- package/ui.form-input-search/config.ts +2 -2
- package/ui.form-input-search/storybook/stories.ts +1 -1
- package/ui.form-label/config.ts +1 -1
- package/ui.form-textarea/UTextarea.vue +2 -12
- package/ui.form-textarea/config.ts +8 -8
- package/utils/node/loaderIcon.d.ts +1 -0
- package/utils/node/loaderIcon.js +30 -13
- package/utils/tests/ui.test.ts +8 -30
- package/utils/ui.ts +7 -28
package/package.json
CHANGED
package/plugin-vite.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
removeIconsCache,
|
|
13
13
|
copyIconsCache,
|
|
14
14
|
generateIconExports,
|
|
15
|
+
hasIconsCache,
|
|
15
16
|
reloadServerOnIconsCacheUpdate,
|
|
16
17
|
extractIconLines,
|
|
17
18
|
isIconChanged,
|
|
@@ -81,21 +82,28 @@ export const Vueless = function (options = {}) {
|
|
|
81
82
|
// Cache to store previous icon content for change detection
|
|
82
83
|
const iconContentCache = new Map();
|
|
83
84
|
|
|
85
|
+
let isProductionBuild = false;
|
|
86
|
+
|
|
84
87
|
const targetFiles = [
|
|
85
88
|
...(include || []),
|
|
86
89
|
...getVuelessAppDirs(),
|
|
87
90
|
...(isNuxtModuleEnv ? getNuxtDirs() : getVueDirs()),
|
|
88
91
|
];
|
|
89
92
|
|
|
93
|
+
async function cacheIcons(files) {
|
|
94
|
+
await createIconsCache({ env, targetFiles: files, basePath, debug });
|
|
95
|
+
|
|
96
|
+
if (isNuxtModuleEnv) {
|
|
97
|
+
await copyIconsCache(basePath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
/* if server stopped by developer (Ctrl+C) */
|
|
91
102
|
process.on("SIGINT", async () => {
|
|
92
103
|
/* remove `.cache` folder in components and restore changes */
|
|
93
104
|
await removeCustomPropTypes(vuelessSrcDir);
|
|
94
105
|
await restoreComponents(vuelessSrcDir);
|
|
95
106
|
|
|
96
|
-
/* remove cached icons */
|
|
97
|
-
await removeIconsCache(basePath);
|
|
98
|
-
|
|
99
107
|
/* clear tailwind safelist */
|
|
100
108
|
await clearTailwindSafelist(debug);
|
|
101
109
|
|
|
@@ -130,6 +138,8 @@ export const Vueless = function (options = {}) {
|
|
|
130
138
|
}),
|
|
131
139
|
|
|
132
140
|
configResolved: async (config) => {
|
|
141
|
+
isProductionBuild = config.command === "build" && !config.build.watch;
|
|
142
|
+
|
|
133
143
|
if (!isNuxtModuleEnv) {
|
|
134
144
|
/* auto import user configs */
|
|
135
145
|
await autoImportUserConfigs(basePath);
|
|
@@ -152,14 +162,13 @@ export const Vueless = function (options = {}) {
|
|
|
152
162
|
/* collect used in project colors for tailwind safelist */
|
|
153
163
|
await createTailwindSafelist({ env, srcDir: vuelessSrcDir, targetFiles, basePath, debug });
|
|
154
164
|
|
|
155
|
-
/* cache vueless built-in and project icons */
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (isNuxtModuleEnv) {
|
|
160
|
-
await copyIconsCache(basePath);
|
|
165
|
+
/* cache vueless built-in and project icons (the dev cache is shared between processes) */
|
|
166
|
+
if (isProductionBuild) {
|
|
167
|
+
await removeIconsCache(basePath);
|
|
161
168
|
}
|
|
162
169
|
|
|
170
|
+
await cacheIcons(targetFiles);
|
|
171
|
+
|
|
163
172
|
/* suppress SOURCEMAP_BROKEN warning from Tailwind */
|
|
164
173
|
for (const key of ["rollupOptions", "rolldownOptions"]) {
|
|
165
174
|
const opts = config.build?.[key];
|
|
@@ -197,11 +206,7 @@ export const Vueless = function (options = {}) {
|
|
|
197
206
|
|
|
198
207
|
if (hasIconChanges && currentIconLines.length) {
|
|
199
208
|
/* cache vueless built-in and project icons */
|
|
200
|
-
await
|
|
201
|
-
|
|
202
|
-
if (isNuxtModuleEnv) {
|
|
203
|
-
await copyIconsCache(basePath);
|
|
204
|
-
}
|
|
209
|
+
await cacheIcons([file]);
|
|
205
210
|
|
|
206
211
|
reloadServerOnIconsCacheUpdate(server);
|
|
207
212
|
|
|
@@ -219,6 +224,11 @@ export const Vueless = function (options = {}) {
|
|
|
219
224
|
/* load SVG images as a Vue components */
|
|
220
225
|
load: async (id) => {
|
|
221
226
|
if (id === RESOLVED_ICONS_VIRTUAL_MODULE_ID) {
|
|
227
|
+
/* restore icons cache if it was removed by another process */
|
|
228
|
+
if (!isProductionBuild && !(await hasIconsCache())) {
|
|
229
|
+
await cacheIcons(targetFiles);
|
|
230
|
+
}
|
|
231
|
+
|
|
222
232
|
return generateIconExports();
|
|
223
233
|
}
|
|
224
234
|
|
|
@@ -226,9 +236,9 @@ export const Vueless = function (options = {}) {
|
|
|
226
236
|
},
|
|
227
237
|
|
|
228
238
|
/* remove cached icons */
|
|
229
|
-
buildEnd: async
|
|
230
|
-
/* skip in watch mode — configResolved won't re-run */
|
|
231
|
-
if (
|
|
239
|
+
buildEnd: async () => {
|
|
240
|
+
/* skip in dev and watch mode — the cache is shared and configResolved won't re-run */
|
|
241
|
+
if (!isProductionBuild) return;
|
|
232
242
|
|
|
233
243
|
await removeIconsCache(basePath);
|
|
234
244
|
},
|
|
@@ -379,7 +379,7 @@ export const Slots: StoryFn<DefaultUDatePickerArgs> = (args) => ({
|
|
|
379
379
|
v-bind="args"
|
|
380
380
|
v-model="args.leftModel"
|
|
381
381
|
class="w-full"
|
|
382
|
-
:config="{ datepickerInput: {
|
|
382
|
+
:config="{ datepickerInput: { leftSlot: 'pl-0' } }"
|
|
383
383
|
>
|
|
384
384
|
<template #left>
|
|
385
385
|
<UButton
|
|
@@ -396,7 +396,7 @@ export const Slots: StoryFn<DefaultUDatePickerArgs> = (args) => ({
|
|
|
396
396
|
v-bind="args"
|
|
397
397
|
v-model="args.modelValue"
|
|
398
398
|
class="w-full"
|
|
399
|
-
:config="{ datepickerInput: {
|
|
399
|
+
:config="{ datepickerInput: { rightSlot: 'pr-0' } }"
|
|
400
400
|
>
|
|
401
401
|
<template #right>
|
|
402
402
|
<UButton
|
|
@@ -391,7 +391,7 @@ export const Slots: StoryFn<DefaultUDatePickerRangeArgs> = (args) => ({
|
|
|
391
391
|
v-model="leftModel"
|
|
392
392
|
variant="input"
|
|
393
393
|
class="w-full"
|
|
394
|
-
:config="{ datepickerInput: {
|
|
394
|
+
:config="{ datepickerInput: { leftSlot: 'pl-0' } }"
|
|
395
395
|
>
|
|
396
396
|
<template #left>
|
|
397
397
|
<UButton
|
|
@@ -408,7 +408,7 @@ export const Slots: StoryFn<DefaultUDatePickerRangeArgs> = (args) => ({
|
|
|
408
408
|
v-model="args.modelValue"
|
|
409
409
|
variant="input"
|
|
410
410
|
class="w-full"
|
|
411
|
-
:config="{ datepickerInput: {
|
|
411
|
+
:config="{ datepickerInput: { rightSlot: 'pr-0' } }"
|
|
412
412
|
>
|
|
413
413
|
<template #right>
|
|
414
414
|
<UButton
|
package/ui.form-input/UInput.vue
CHANGED
|
@@ -222,19 +222,9 @@ function setLabelPosition() {
|
|
|
222
222
|
|
|
223
223
|
if (leftSlotWrapperRef.value && inputRef.value && labelComponentRef.value?.labelElement) {
|
|
224
224
|
const leftSlotOrIconWidth = leftSlotWrapperRef.value.getBoundingClientRect().width;
|
|
225
|
-
const
|
|
225
|
+
const inputLeftPadding = parseFloat(getComputedStyle(inputRef.value).paddingLeft);
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
let wrapperLeftPadding = 0;
|
|
229
|
-
|
|
230
|
-
if (wrapperElement) {
|
|
231
|
-
wrapperGap = parseFloat(getComputedStyle(wrapperElement).gap);
|
|
232
|
-
wrapperLeftPadding = parseFloat(getComputedStyle(wrapperElement).paddingLeft);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (labelComponentRef.value?.labelElement) {
|
|
236
|
-
labelComponentRef.value.labelElement.style.left = `${leftSlotOrIconWidth + wrapperLeftPadding + wrapperGap}px`;
|
|
237
|
-
}
|
|
227
|
+
labelComponentRef.value.labelElement.style.left = `${leftSlotOrIconWidth + inputLeftPadding}px`;
|
|
238
228
|
}
|
|
239
229
|
}
|
|
240
230
|
|
package/ui.form-input/config.ts
CHANGED
|
@@ -11,7 +11,7 @@ export default /*tw*/ {
|
|
|
11
11
|
},
|
|
12
12
|
wrapper: {
|
|
13
13
|
base: `
|
|
14
|
-
flex
|
|
14
|
+
flex w-full relative bg-default transition
|
|
15
15
|
border border-solid rounded-medium border-default outline-solid outline-small outline-transparent
|
|
16
16
|
hover:border-lifted hover:focus-within:border-primary focus-within:border-primary
|
|
17
17
|
focus-within:outline focus-within:outline-small focus-within:outline-primary focus-within:transition
|
|
@@ -37,11 +37,11 @@ export default /*tw*/ {
|
|
|
37
37
|
},
|
|
38
38
|
leftIcon: "{UIcon} {>inputIcon} -mr-0.5",
|
|
39
39
|
rightIcon: "{UIcon} {>inputIcon} -ml-0.5",
|
|
40
|
-
leftSlot: "flex items-center",
|
|
41
|
-
rightSlot: "flex items-center",
|
|
40
|
+
leftSlot: "flex items-center shrink-0 pl-3",
|
|
41
|
+
rightSlot: "flex items-center shrink-0 pr-3",
|
|
42
42
|
input: {
|
|
43
43
|
base: `
|
|
44
|
-
block w-full py-2 font-normal leading-none! bg-transparent
|
|
44
|
+
block w-full min-w-0 px-3 py-2 font-normal leading-none! bg-transparent
|
|
45
45
|
border-none rounded-inherit transition focus:outline-none
|
|
46
46
|
placeholder:font-normal placeholder:text-muted
|
|
47
47
|
disabled:text-accented disabled:cursor-not-allowed
|
|
@@ -211,7 +211,7 @@ export const Slots: StoryFn<UInputArgs> = (args) => ({
|
|
|
211
211
|
<UInput
|
|
212
212
|
label="Phone Number"
|
|
213
213
|
placeholder="Enter your phone number"
|
|
214
|
-
:config="{
|
|
214
|
+
:config="{ leftSlot: 'pl-0' }"
|
|
215
215
|
>
|
|
216
216
|
<template #label="{ label }">
|
|
217
217
|
{{ label }}
|
|
@@ -221,7 +221,7 @@ export const Slots: StoryFn<UInputNumberArgs> = (args) => ({
|
|
|
221
221
|
<UInputNumber
|
|
222
222
|
label="Left slot"
|
|
223
223
|
placeholder="Enter discount amount"
|
|
224
|
-
:config="{ numberInput: {
|
|
224
|
+
:config="{ numberInput: { leftSlot: 'pl-0' } }"
|
|
225
225
|
>
|
|
226
226
|
<template #left>
|
|
227
227
|
<UDropdownButton
|
|
@@ -161,7 +161,7 @@ export const Slots: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
|
161
161
|
v-bind="args"
|
|
162
162
|
v-model="args.modelValue"
|
|
163
163
|
label="Enter your password"
|
|
164
|
-
:config="{ passwordInput: {
|
|
164
|
+
:config="{ passwordInput: { leftSlot: 'pl-0' } }"
|
|
165
165
|
>
|
|
166
166
|
<template #label="{ label }">
|
|
167
167
|
{{ label }}
|
|
@@ -184,7 +184,7 @@ export const Slots: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
|
184
184
|
v-bind="args"
|
|
185
185
|
v-model="args.modelValue"
|
|
186
186
|
label="Enter your password"
|
|
187
|
-
:config="{ passwordInput: {
|
|
187
|
+
:config="{ passwordInput: { rightSlot: 'pr-0' } }"
|
|
188
188
|
>
|
|
189
189
|
<template #label="{ label }">
|
|
190
190
|
{{ label }}
|
|
@@ -2,7 +2,7 @@ export default /*tw*/ {
|
|
|
2
2
|
searchInput: "{UInput}",
|
|
3
3
|
searchInputWithButton: {
|
|
4
4
|
base: "{UInput} {>searchInput}",
|
|
5
|
-
|
|
5
|
+
rightSlot: "pr-0",
|
|
6
6
|
},
|
|
7
7
|
inputIcon: {
|
|
8
8
|
base: "{UIcon}",
|
|
@@ -17,7 +17,7 @@ export default /*tw*/ {
|
|
|
17
17
|
clearIcon: "{>inputIcon}",
|
|
18
18
|
searchIcon: "{>inputIcon} -mr-1",
|
|
19
19
|
searchButton: {
|
|
20
|
-
base: "{UButton} rounded-l-none ml-2",
|
|
20
|
+
base: "{UButton} self-stretch py-0 rounded-l-none ml-2",
|
|
21
21
|
defaults: {
|
|
22
22
|
size: {
|
|
23
23
|
sm: "xs",
|
|
@@ -179,7 +179,7 @@ export const Slots: StoryFn<UInputSearchArgs> = (args) => ({
|
|
|
179
179
|
|
|
180
180
|
<UInputSearch
|
|
181
181
|
placeholder="Ask something..."
|
|
182
|
-
:config="{ searchInput: {
|
|
182
|
+
:config="{ searchInput: { leftSlot: 'pl-0' } }"
|
|
183
183
|
>
|
|
184
184
|
<template #left>
|
|
185
185
|
<UDropdownButton
|
package/ui.form-label/config.ts
CHANGED
|
@@ -146,19 +146,9 @@ function setLabelPosition() {
|
|
|
146
146
|
|
|
147
147
|
if (leftSlotWrapperRef.value && textareaRef.value && labelComponentRef.value?.labelElement) {
|
|
148
148
|
const leftSlotWidth = leftSlotWrapperRef.value.getBoundingClientRect().width;
|
|
149
|
-
const
|
|
149
|
+
const textareaPaddingLeft = parseFloat(getComputedStyle(textareaRef.value).paddingLeft);
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
let wrapperLeftPadding = 0;
|
|
153
|
-
|
|
154
|
-
if (wrapperElement) {
|
|
155
|
-
wrapperGap = parseFloat(getComputedStyle(wrapperElement).gap);
|
|
156
|
-
wrapperLeftPadding = parseFloat(getComputedStyle(wrapperElement).paddingLeft);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (labelComponentRef.value?.labelElement) {
|
|
160
|
-
labelComponentRef.value.labelElement.style.left = `${leftSlotWidth + wrapperLeftPadding + wrapperGap}px`;
|
|
161
|
-
}
|
|
151
|
+
labelComponentRef.value.labelElement.style.left = `${leftSlotWidth + textareaPaddingLeft}px`;
|
|
162
152
|
}
|
|
163
153
|
}
|
|
164
154
|
|
|
@@ -9,12 +9,12 @@ export default /*tw*/ {
|
|
|
9
9
|
},
|
|
10
10
|
},
|
|
11
11
|
},
|
|
12
|
-
slot: "flex items-center justify-center whitespace-nowrap gap-1 rounded-medium",
|
|
13
|
-
leftSlot: "{>slot} rounded-r-none",
|
|
14
|
-
rightSlot: "{>slot} rounded-l-none",
|
|
12
|
+
slot: "flex shrink-0 items-center justify-center whitespace-nowrap gap-1 rounded-medium",
|
|
13
|
+
leftSlot: "{>slot} pl-3 rounded-r-none",
|
|
14
|
+
rightSlot: "{>slot} pr-3 rounded-l-none",
|
|
15
15
|
wrapper: {
|
|
16
16
|
base: `
|
|
17
|
-
flex
|
|
17
|
+
flex w-full bg-default transition
|
|
18
18
|
rounded-medium border border-solid border-default outline-solid outline-small outline-transparent
|
|
19
19
|
hover:border-lifted hover:focus-within:border-primary focus-within:border-primary
|
|
20
20
|
focus-within:outline focus-within:outline-small focus-within:outline-primary focus-within:transition
|
|
@@ -30,7 +30,7 @@ export default /*tw*/ {
|
|
|
30
30
|
},
|
|
31
31
|
textarea: {
|
|
32
32
|
base: `
|
|
33
|
-
block w-full bg-transparent border-none font-normal
|
|
33
|
+
block w-full min-w-0 px-3 py-2 bg-transparent border-none font-normal
|
|
34
34
|
placeholder:text-muted placeholder:font-normal placeholder:leading-none
|
|
35
35
|
focus:outline-none disabled:cursor-not-allowed
|
|
36
36
|
`,
|
|
@@ -51,9 +51,9 @@ export default /*tw*/ {
|
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
53
|
compoundVariants: [
|
|
54
|
-
{ labelAlign: "topInside", label: true, size: "sm", class: "
|
|
55
|
-
{ labelAlign: "topInside", label: true, size: "md", class: "
|
|
56
|
-
{ labelAlign: "topInside", label: true, size: "lg", class: "
|
|
54
|
+
{ labelAlign: "topInside", label: true, size: "sm", class: "pt-5" },
|
|
55
|
+
{ labelAlign: "topInside", label: true, size: "md", class: "pt-6" },
|
|
56
|
+
{ labelAlign: "topInside", label: true, size: "lg", class: "pt-7" },
|
|
57
57
|
],
|
|
58
58
|
},
|
|
59
59
|
defaults: {
|
|
@@ -2,6 +2,7 @@ export function createIconsCache({ env, debug, targetFiles, basePath }?: string)
|
|
|
2
2
|
export function removeIconsCache(basePath: string, isVuelessEnv?: boolean): Promise<void>;
|
|
3
3
|
export function copyIconsCache(basePath: string, isVuelessEnv?: boolean): Promise<void>;
|
|
4
4
|
export function generateIconExports(): string;
|
|
5
|
+
export function hasIconsCache(): Promise<boolean>;
|
|
5
6
|
export function reloadServerOnIconsCacheUpdate(server: Object): void;
|
|
6
7
|
export function extractIconLines(content: string): string[];
|
|
7
8
|
export function isIconChanged(currentLines: string[], previousLines: string[]): boolean;
|
package/utils/node/loaderIcon.js
CHANGED
|
@@ -126,10 +126,7 @@ export async function copyIconsCache(basePath, isVuelessEnv = false) {
|
|
|
126
126
|
* @return {string} A string containing the export statement for the cached SVG icons as an array to be used in Vite.
|
|
127
127
|
*/
|
|
128
128
|
export async function generateIconExports() {
|
|
129
|
-
const
|
|
130
|
-
const files = await getDirFiles(cachePath, ".svg", {
|
|
131
|
-
recursive: true,
|
|
132
|
-
});
|
|
129
|
+
const files = await getCachedIconFiles();
|
|
133
130
|
|
|
134
131
|
const entries = files.map((filePath) => {
|
|
135
132
|
const fullImportPath = path.resolve(filePath).replace(/\\/g, "/");
|
|
@@ -142,6 +139,22 @@ export async function generateIconExports() {
|
|
|
142
139
|
return `export const cachedIcons = [\n${entries.join(",\n")}\n];`;
|
|
143
140
|
}
|
|
144
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Check if the icons cache contains at least one SVG icon.
|
|
144
|
+
* @returns {Promise<boolean>}
|
|
145
|
+
*/
|
|
146
|
+
export async function hasIconsCache() {
|
|
147
|
+
return (await getCachedIconFiles()).length > 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get paths of all cached SVG icons.
|
|
152
|
+
* @returns {Promise<string[]>}
|
|
153
|
+
*/
|
|
154
|
+
function getCachedIconFiles() {
|
|
155
|
+
return getDirFiles(path.join(cwd(), ICONS_CACHED_DIR), ".svg", { recursive: true });
|
|
156
|
+
}
|
|
157
|
+
|
|
145
158
|
/**
|
|
146
159
|
* Reloads the server when the icons cache is updated. This function sets up a file system watcher
|
|
147
160
|
* on the icons cache directory and triggers a full server reload whenever files are added or removed.
|
|
@@ -193,9 +206,11 @@ async function findAndCopyIcons(files, library, vuelessConfig, debug) {
|
|
|
193
206
|
const validIconNamesRegex = /^(?!icon$|name$)[a-z0-9_-]+$/;
|
|
194
207
|
const ternaryRegex = /\?.*:/;
|
|
195
208
|
|
|
209
|
+
const iconNames = new Set();
|
|
210
|
+
|
|
196
211
|
safelistIcons?.forEach((iconName) => {
|
|
197
212
|
if (validIconNamesRegex.test(iconName)) {
|
|
198
|
-
|
|
213
|
+
iconNames.add(iconName);
|
|
199
214
|
}
|
|
200
215
|
});
|
|
201
216
|
|
|
@@ -212,11 +227,7 @@ async function findAndCopyIcons(files, library, vuelessConfig, debug) {
|
|
|
212
227
|
const iconName = iconNameMatch && iconNameMatch[2];
|
|
213
228
|
|
|
214
229
|
if (validIconNamesRegex.test(iconName)) {
|
|
215
|
-
|
|
216
|
-
iconName && copyIcon(iconName, library);
|
|
217
|
-
} catch (error) {
|
|
218
|
-
debug && console.log(error);
|
|
219
|
-
}
|
|
230
|
+
iconNames.add(iconName);
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
iconNameRegex.lastIndex = 0;
|
|
@@ -249,10 +260,10 @@ async function findAndCopyIcons(files, library, vuelessConfig, debug) {
|
|
|
249
260
|
continue;
|
|
250
261
|
}
|
|
251
262
|
|
|
252
|
-
|
|
253
|
-
|
|
263
|
+
iconNames.add(trueName);
|
|
264
|
+
iconNames.add(falseName);
|
|
254
265
|
} else {
|
|
255
|
-
|
|
266
|
+
iconNames.add(iconName);
|
|
256
267
|
}
|
|
257
268
|
} catch (error) {
|
|
258
269
|
debug && console.log(error);
|
|
@@ -260,6 +271,12 @@ async function findAndCopyIcons(files, library, vuelessConfig, debug) {
|
|
|
260
271
|
}
|
|
261
272
|
}
|
|
262
273
|
});
|
|
274
|
+
|
|
275
|
+
await Promise.all(
|
|
276
|
+
[...iconNames].map((iconName) =>
|
|
277
|
+
copyIcon(iconName, library).catch((error) => debug && console.log(error)),
|
|
278
|
+
),
|
|
279
|
+
);
|
|
263
280
|
}
|
|
264
281
|
|
|
265
282
|
/**
|
package/utils/tests/ui.test.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { describe, it, expect, vi
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { COLOR_MODE_KEY } from "../../constants.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import type { Config } from "../../types";
|
|
4
|
+
import { setVuelessConfig } from "../ui";
|
|
7
5
|
|
|
8
6
|
/* jsdom doesn't implement matchMedia, which theme.ts evaluates on import. */
|
|
9
7
|
const prefersDark = { matches: false, addEventListener: vi.fn(), removeEventListener: vi.fn() };
|
|
@@ -12,37 +10,17 @@ window.matchMedia = vi.fn(() => prefersDark) as unknown as typeof window.matchMe
|
|
|
12
10
|
|
|
13
11
|
const { getThemeCookieName } = await import("../theme");
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe("loadSSRVuelessConfig", () => {
|
|
20
|
-
it.each([
|
|
21
|
-
["resolves", () => ({ cookiePrefix: "from-file" })],
|
|
22
|
-
[
|
|
23
|
-
"fails",
|
|
24
|
-
() => {
|
|
25
|
-
throw new Error("Cannot find module '/vueless.config'");
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
])("keeps the injected config when a deferred import %s", async (_, settle: () => Config) => {
|
|
29
|
-
let release!: () => void;
|
|
30
|
-
const released = new Promise<void>((resolve) => (release = resolve));
|
|
31
|
-
|
|
32
|
-
const loading = loadSSRVuelessConfig(() => released.then(() => ({ default: settle() })));
|
|
33
|
-
|
|
13
|
+
describe("setVuelessConfig", () => {
|
|
14
|
+
it("uses the injected config", () => {
|
|
34
15
|
setVuelessConfig({ cookiePrefix: "app-a" });
|
|
35
|
-
expect(getThemeCookieName(COLOR_MODE_KEY)).toBe(`app-a-${COLOR_MODE_KEY}`);
|
|
36
|
-
|
|
37
|
-
release();
|
|
38
|
-
await loading;
|
|
39
16
|
|
|
40
17
|
expect(getThemeCookieName(COLOR_MODE_KEY)).toBe(`app-a-${COLOR_MODE_KEY}`);
|
|
41
18
|
});
|
|
42
19
|
|
|
43
|
-
it("
|
|
44
|
-
|
|
20
|
+
it("keeps the current config when an empty one is injected", () => {
|
|
21
|
+
setVuelessConfig({ cookiePrefix: "app-a" });
|
|
22
|
+
setVuelessConfig({});
|
|
45
23
|
|
|
46
|
-
expect(getThemeCookieName(COLOR_MODE_KEY)).toBe(`
|
|
24
|
+
expect(getThemeCookieName(COLOR_MODE_KEY)).toBe(`app-a-${COLOR_MODE_KEY}`);
|
|
47
25
|
});
|
|
48
26
|
});
|
package/utils/ui.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { cx as cvaCx } from "cva";
|
|
|
3
3
|
import { defineConfig } from "cva/config";
|
|
4
4
|
import { extendTailwindMerge } from "tailwind-merge";
|
|
5
5
|
|
|
6
|
-
import { isCSR, isSSR } from "./helper";
|
|
7
6
|
import { createGetMergedConfig } from "./node/mergeConfigs";
|
|
8
7
|
import { COMPONENT_NAME as U_ICON } from "../ui.image-icon/constants";
|
|
9
8
|
import { ICON_NON_PROPS_DEFAULTS, TAILWIND_MERGE_EXTENSION } from "../constants";
|
|
@@ -30,7 +29,13 @@ type GetMergedConfig = (options: MergedConfigOptions) => unknown;
|
|
|
30
29
|
* Load Vueless config from the project root.
|
|
31
30
|
* Both for server and client side renderings.
|
|
32
31
|
*/
|
|
33
|
-
export let vuelessConfig: Config =
|
|
32
|
+
export let vuelessConfig: Config =
|
|
33
|
+
Object.values(
|
|
34
|
+
import.meta.glob(["/vueless.config.{js,ts}"], {
|
|
35
|
+
eager: true,
|
|
36
|
+
import: "default",
|
|
37
|
+
}),
|
|
38
|
+
)[0] || {};
|
|
34
39
|
|
|
35
40
|
export function setVuelessConfig(config?: Config) {
|
|
36
41
|
config = config || {};
|
|
@@ -38,32 +43,6 @@ export function setVuelessConfig(config?: Config) {
|
|
|
38
43
|
vuelessConfig = Object.keys(config).length ? config : vuelessConfig;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
if (isCSR) {
|
|
42
|
-
vuelessConfig =
|
|
43
|
-
Object.values(
|
|
44
|
-
import.meta.glob(["/vueless.config.{js,ts}"], {
|
|
45
|
-
eager: true,
|
|
46
|
-
import: "default",
|
|
47
|
-
}),
|
|
48
|
-
)[0] || {};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Load Vueless config on the server; a config injected via `createVueless` meanwhile wins. */
|
|
52
|
-
export async function loadSSRVuelessConfig(
|
|
53
|
-
// @ts-expect-error: vueless.config.{js,ts} is optional
|
|
54
|
-
importConfig = () => import(/* @vite-ignore */ "/vueless.config"),
|
|
55
|
-
) {
|
|
56
|
-
try {
|
|
57
|
-
const { default: config } = await importConfig();
|
|
58
|
-
|
|
59
|
-
if (!Object.keys(vuelessConfig).length) vuelessConfig = config;
|
|
60
|
-
} catch {
|
|
61
|
-
/* No config file: keep the current config. */
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (isSSR) loadSSRVuelessConfig();
|
|
66
|
-
|
|
67
46
|
/**
|
|
68
47
|
* Extend twMerge (tailwind merge) by vueless and user config:
|
|
69
48
|
*/
|