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.
- package/composables/useBreakpoint.js +1 -1
- package/composables/useUI.js +204 -1
- package/composablesTs/useAutoPosition.ts +115 -0
- package/composablesTs/useBreakpoint.ts +106 -0
- package/composablesTs/useLocale.ts +25 -0
- package/composablesTs/useMutationObserver.ts +50 -0
- package/composablesTs/useUI.ts +562 -0
- package/constants.js +2 -1
- package/constants.ts +73 -0
- package/directives/clickOutside/vClickOutside.js +2 -2
- package/directives/tooltip/storybook/stories.js +5 -5
- package/{index.js → index.ts} +10 -7
- package/package.json +28 -17
- package/preset.tailwind.js +16 -7
- package/types.ts +223 -0
- package/ui.button/config.js +12 -0
- package/ui.button-link/ULink.vue +1 -1
- package/ui.button-link/config.js +9 -0
- package/ui.data-list/UDataList.vue +4 -4
- package/ui.dropdown-badge/config.js +1 -0
- package/ui.dropdown-button/config.js +1 -0
- package/ui.form-checkbox/config.js +9 -0
- package/ui.form-color-picker/config.js +7 -0
- package/ui.form-input/UInput.vue +1 -1
- package/ui.form-input-money/useFormatCurrency.js +1 -1
- package/ui.form-input-number/UInputNumber.vue +4 -3
- package/ui.form-label/config.js +2 -2
- package/ui.form-radio/config.js +6 -0
- package/ui.form-switch/config.js +6 -0
- package/ui.image-avatar/config.js +5 -0
- package/ui.image-icon/config.js +5 -0
- package/ui.loader/config.js +1 -0
- package/ui.loader-overlay/config.js +1 -0
- package/ui.loader-progress/config.js +1 -0
- package/ui.navigation-progress/config.js +9 -0
- package/ui.other-dot/config.js +1 -0
- package/ui.text-alert/config.js +7 -0
- package/ui.text-badge/config.js +8 -0
- package/ui.text-block/UText.vue +18 -62
- package/ui.text-block/storybook/Docs.mdx +3 -3
- package/ui.text-block/storybook/{stories.js → stories.ts} +13 -8
- package/ui.text-block/types.ts +33 -0
- package/ui.text-block/useAttrs.ts +20 -0
- package/ui.text-file/UFile.vue +12 -14
- package/ui.text-file/config.js +12 -2
- package/ui.text-files/config.js +1 -1
- package/ui.text-header/config.js +1 -0
- package/ui.text-money/config.js +1 -0
- package/ui.text-money/utilMoney.js +2 -2
- package/utils/utilUI.js +0 -204
- package/utilsTs/utilHelper.ts +68 -0
- package/utilsTs/utilPlatform.ts +53 -0
- package/utilsTs/utilStorybook.ts +296 -0
- package/utilsTs/utilTailwind.ts +38 -0
- package/{utils/utilTheme.js → utilsTs/utilTheme.ts} +31 -27
- package/utilsTs/utilUI.ts +143 -0
- package/web-types.json +1 -1
- package/ui.text-block/useAttrs.js +0 -15
- /package/ui.text-block/{config.js → config.ts} +0 -0
- /package/ui.text-block/{constants.js → constants.ts} +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isCSR } from "./utilHelper.ts";
|
|
2
|
+
|
|
3
|
+
interface ModernNavigator extends Navigator {
|
|
4
|
+
standalone: string;
|
|
5
|
+
userAgentData: {
|
|
6
|
+
platform: string;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const isWindows = isCSR && checkIsWindows();
|
|
11
|
+
const isMac = isCSR && checkIsMac();
|
|
12
|
+
const isPWA = isCSR && checkIsPWA();
|
|
13
|
+
const isIOS = isCSR && checkIsIOS();
|
|
14
|
+
const isAndroid = isCSR && checkIsAndroid();
|
|
15
|
+
const isMobileApp = isPWA || isIOS || isAndroid;
|
|
16
|
+
|
|
17
|
+
function checkIsWindows() {
|
|
18
|
+
return getPlatform().toUpperCase().indexOf("WINDOWS") >= 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function checkIsMac() {
|
|
22
|
+
return getPlatform().toUpperCase().indexOf("MAC") >= 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function checkIsPWA() {
|
|
26
|
+
return !!(navigator as ModernNavigator).standalone;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function checkIsIOS() {
|
|
30
|
+
const iOSDevices = [
|
|
31
|
+
"iPad Simulator",
|
|
32
|
+
"iPhone Simulator",
|
|
33
|
+
"iPod Simulator",
|
|
34
|
+
"iPad",
|
|
35
|
+
"iPhone",
|
|
36
|
+
"iPod",
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const platform = getPlatform();
|
|
40
|
+
const isIpodIOS13 = platform.includes("Mac") && "ontouchend" in document;
|
|
41
|
+
|
|
42
|
+
return iOSDevices.includes(platform) || isIpodIOS13;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function checkIsAndroid() {
|
|
46
|
+
return getPlatform().toUpperCase().indexOf("ANDROID") >= 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getPlatform() {
|
|
50
|
+
return (navigator as ModernNavigator).userAgentData?.platform || navigator.platform || "unknown";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows };
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
interface WebTypes {
|
|
2
|
+
framework: string;
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
contributions: Contributions;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Contributions {
|
|
9
|
+
html: HtmlContributions;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface HtmlContributions {
|
|
13
|
+
"description-markup": string;
|
|
14
|
+
"types-syntax": string;
|
|
15
|
+
tags: Tag[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Tag {
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
attributes?: Attribute[];
|
|
22
|
+
events?: Event[];
|
|
23
|
+
slots?: Slot[];
|
|
24
|
+
exposes?: Expose[];
|
|
25
|
+
source?: Source;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface Attribute {
|
|
29
|
+
name: string;
|
|
30
|
+
required?: boolean;
|
|
31
|
+
description?: string;
|
|
32
|
+
value: AttributeValue;
|
|
33
|
+
default?: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface AttributeValue {
|
|
37
|
+
kind: string;
|
|
38
|
+
type: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface Event {
|
|
42
|
+
name: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
properties?: EventProperty[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface EventProperty {
|
|
48
|
+
type: string[];
|
|
49
|
+
name: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface Slot {
|
|
54
|
+
name: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
scoped?: boolean;
|
|
57
|
+
bindings?: SlotBinding[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface SlotBinding {
|
|
61
|
+
type: string;
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface Expose {
|
|
67
|
+
name: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
properties: ExposeProperty[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface ExposeProperty {
|
|
73
|
+
type: string;
|
|
74
|
+
description?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface Source {
|
|
78
|
+
module: string;
|
|
79
|
+
symbol: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface Types {
|
|
83
|
+
[key: string]: ArgType | undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface ArgType {
|
|
87
|
+
control?: "text" | "number" | "boolean" | "array" | "select" | false;
|
|
88
|
+
options?: string[];
|
|
89
|
+
table?: TableConfig;
|
|
90
|
+
name?: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
type?: string | null;
|
|
93
|
+
action?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface TableConfig {
|
|
97
|
+
disable?: boolean;
|
|
98
|
+
defaultValue?: { summary: unknown };
|
|
99
|
+
category?: "slots" | "expose" | "Storybook Events";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Load Web-Types from the project root. */
|
|
103
|
+
const [webTypes]: WebTypes[] = Object.values(
|
|
104
|
+
import.meta.glob("/web-types.json", { eager: true, import: "default" }),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const getComponentData = (componentName: string | undefined) => {
|
|
108
|
+
if (!componentName) return;
|
|
109
|
+
|
|
110
|
+
return webTypes.contributions.html.tags.find((item: Tag) => item.name === componentName);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export function getSlotNames(componentName: string | undefined) {
|
|
114
|
+
if (!componentName) return;
|
|
115
|
+
|
|
116
|
+
return getComponentData(componentName)?.slots?.map((item) => item.name);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function getArgTypes(componentName: string | undefined) {
|
|
120
|
+
const component = getComponentData(componentName);
|
|
121
|
+
|
|
122
|
+
if (!component) return;
|
|
123
|
+
|
|
124
|
+
const types: Partial<Types> = {
|
|
125
|
+
// Hide default template arg in docs.
|
|
126
|
+
defaultTemplate: { table: { disable: true } },
|
|
127
|
+
// Hide slot template arg in docs.
|
|
128
|
+
slotTemplate: { table: { disable: true } },
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
component.attributes?.forEach((attribute: Attribute) => {
|
|
132
|
+
const type = attribute.value.type;
|
|
133
|
+
|
|
134
|
+
if (type === "string" || type.includes("string")) {
|
|
135
|
+
types[attribute.name] = {
|
|
136
|
+
control: "text",
|
|
137
|
+
table: {
|
|
138
|
+
defaultValue: { summary: attribute.default || "" },
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (type === "number") {
|
|
144
|
+
types[attribute.name] = {
|
|
145
|
+
control: "number",
|
|
146
|
+
table: {
|
|
147
|
+
defaultValue: { summary: attribute.default || "" },
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (type === "boolean") {
|
|
153
|
+
types[attribute.name] = {
|
|
154
|
+
control: "boolean",
|
|
155
|
+
table: {
|
|
156
|
+
defaultValue: { summary: attribute.default || "" },
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (type === "array") {
|
|
162
|
+
types[attribute.name] = {
|
|
163
|
+
control: "array",
|
|
164
|
+
table: {
|
|
165
|
+
defaultValue: { summary: attribute.default || [] },
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (type.includes("|")) {
|
|
171
|
+
const options = attribute.value.type.replace(/['|]/g, "").split(/\s+/);
|
|
172
|
+
|
|
173
|
+
if (options.length > 1) {
|
|
174
|
+
types[attribute.name] = {
|
|
175
|
+
options,
|
|
176
|
+
control: "select",
|
|
177
|
+
table: {
|
|
178
|
+
defaultValue: { summary: attribute.default || "" },
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
} else {
|
|
182
|
+
types[attribute.name] = {
|
|
183
|
+
control: type.split("|")[0] as ArgType["control"],
|
|
184
|
+
table: {
|
|
185
|
+
defaultValue: { summary: attribute.default || "" },
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (attribute.description?.includes("@ignore")) {
|
|
192
|
+
types[attribute.name] = {
|
|
193
|
+
table: {
|
|
194
|
+
disable: true,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
component.slots?.forEach((slot) => {
|
|
201
|
+
const bindings: string[] = [];
|
|
202
|
+
|
|
203
|
+
slot.bindings?.forEach((binding: SlotBinding) => {
|
|
204
|
+
if (binding.name === "name") return;
|
|
205
|
+
|
|
206
|
+
const description = binding.description ? ` (${binding.description})` : "";
|
|
207
|
+
|
|
208
|
+
bindings.push(`${binding.name}: ${binding.type}${description}`);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
types[`${slot.name}Slot`] = {
|
|
212
|
+
name: slot.name,
|
|
213
|
+
description: slot.description,
|
|
214
|
+
type: slot.bindings ? `{ ${bindings.join(", ")} }` : null,
|
|
215
|
+
control: "text",
|
|
216
|
+
table: { category: "slots" },
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Hide autogenerated slot docs, but keep props with the same name
|
|
220
|
+
if (!component.attributes?.map((item) => item.name)?.includes(slot.name)) {
|
|
221
|
+
types[slot.name] = {
|
|
222
|
+
table: {
|
|
223
|
+
disable: true,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
component.exposes?.forEach((expose) => {
|
|
230
|
+
const properties: string[] = [];
|
|
231
|
+
|
|
232
|
+
expose.properties?.forEach((property: ExposeProperty) => {
|
|
233
|
+
const description = property.description ? ` (${property.description})` : "";
|
|
234
|
+
|
|
235
|
+
properties.push(`${property.type}${description}`);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
types[`${expose.name}Expose`] = {
|
|
239
|
+
type: expose.properties ? properties.join(", ") : null,
|
|
240
|
+
name: expose.name,
|
|
241
|
+
description: expose.description,
|
|
242
|
+
control: false,
|
|
243
|
+
table: { category: "expose" },
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Hide autogenerated expose docs, but keep props with the same name
|
|
247
|
+
if (!component.attributes?.map((item) => item.name)?.includes(expose.name)) {
|
|
248
|
+
types[expose.name] = {
|
|
249
|
+
table: {
|
|
250
|
+
disable: true,
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
component.events?.forEach((event) => {
|
|
257
|
+
const properties: string[] = [];
|
|
258
|
+
|
|
259
|
+
event.properties?.forEach((property: EventProperty) => {
|
|
260
|
+
const description = property.description ? ` (${property.description})` : "";
|
|
261
|
+
|
|
262
|
+
properties.push(`${property.name}: ${property.type}${description}`);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
types[event.name] = {
|
|
266
|
+
type: event.properties ? properties.join(", ") : null,
|
|
267
|
+
name: event.name,
|
|
268
|
+
description: event.description,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
if (import.meta.env.STORYBOOK_FULL) {
|
|
272
|
+
const eventName = "on" + event.name.charAt(0).toUpperCase() + event.name.slice(1);
|
|
273
|
+
|
|
274
|
+
types[eventName] = {
|
|
275
|
+
action: event.name,
|
|
276
|
+
table: { category: "Storybook Events" },
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
return types;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function getSource(defaultConfig: string) {
|
|
285
|
+
return defaultConfig.replace("export default /*tw*/ ", "").replace(";", "");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function getSlotsFragment(defaultTemplate: string) {
|
|
289
|
+
return `
|
|
290
|
+
<template v-for="(slot, index) of slots" :key="index" v-slot:[slot]>
|
|
291
|
+
<template v-if="slot === 'default' && !args['defaultSlot']">${defaultTemplate || ""}</template>
|
|
292
|
+
<template v-else-if="slot === 'default' && args['defaultSlot']">{{ args['defaultSlot'] }}</template>
|
|
293
|
+
<template v-else-if="args[slot + 'Slot']">{{ args[slot + 'Slot'] }}</template>
|
|
294
|
+
</template>
|
|
295
|
+
`;
|
|
296
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
import resolveConfig from "tailwindcss/resolveConfig";
|
|
4
|
+
import { isSSR, isCSR } from "./utilHelper.ts";
|
|
5
|
+
|
|
6
|
+
import type { Config } from "tailwindcss";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Load Tailwind config from the project root.
|
|
10
|
+
* Both for server and client side renderings.
|
|
11
|
+
* IIFE for SSR is used to prevent top level await issue.
|
|
12
|
+
*/
|
|
13
|
+
export let fullTailwindConfig: any;
|
|
14
|
+
|
|
15
|
+
if (isSSR) {
|
|
16
|
+
/* Load Tailwind config from the project root in IIFE (no top-level await). */
|
|
17
|
+
(async () => {
|
|
18
|
+
try {
|
|
19
|
+
const filePath = `${process.cwd()}/tailwind.config`;
|
|
20
|
+
|
|
21
|
+
let tailwindConfig = (await import(/* @vite-ignore */ `${filePath}.js`)).default;
|
|
22
|
+
|
|
23
|
+
if (!tailwindConfig) {
|
|
24
|
+
tailwindConfig = (await import(/* @vite-ignore */ `${filePath}.ts`)).default;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fullTailwindConfig = resolveConfig(tailwindConfig);
|
|
28
|
+
} catch {}
|
|
29
|
+
})();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isCSR) {
|
|
33
|
+
const tailwindConfig = Object.values(
|
|
34
|
+
import.meta.glob("/tailwind.config.{js,ts}", { eager: true, import: "default" }),
|
|
35
|
+
)[0] as Config;
|
|
36
|
+
|
|
37
|
+
fullTailwindConfig = resolveConfig(tailwindConfig);
|
|
38
|
+
}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { vuelessConfig } from "./utilUI.
|
|
4
|
-
import { isSSR, isCSR } from "./utilHelper.js";
|
|
1
|
+
import { fullTailwindConfig } from "./utilTailwind.ts";
|
|
2
|
+
import { isSSR, isCSR } from "./utilHelper.ts";
|
|
3
|
+
import { vuelessConfig } from "./utilUI.ts";
|
|
5
4
|
import {
|
|
6
|
-
GRAY_COLOR,
|
|
7
|
-
COOL_COLOR,
|
|
8
5
|
BRAND_COLORS,
|
|
9
6
|
GRAYSCALE_COLOR,
|
|
10
7
|
DEFAULT_RING,
|
|
11
8
|
DEFAULT_RING_OFFSET,
|
|
12
|
-
DEFAULT_RING_OFFSET_COLOR_LIGHT,
|
|
13
|
-
DEFAULT_RING_OFFSET_COLOR_DARK,
|
|
14
9
|
DEFAULT_ROUNDING,
|
|
15
10
|
DEFAULT_BRAND_COLOR,
|
|
16
11
|
DEFAULT_GRAY_COLOR,
|
|
12
|
+
DEFAULT_RING_OFFSET_COLOR_LIGHT,
|
|
13
|
+
DEFAULT_RING_OFFSET_COLOR_DARK,
|
|
17
14
|
DARK_MODE_SELECTOR,
|
|
18
15
|
GRAY_COLORS,
|
|
19
16
|
PX_IN_REM,
|
|
20
|
-
} from "../constants.
|
|
17
|
+
} from "../constants.ts";
|
|
18
|
+
|
|
19
|
+
import type { ThemeConfig, GrayColors, BrandColors, VuelessCssVariables } from "../types.ts";
|
|
20
|
+
|
|
21
|
+
interface InternalThemeConfig extends ThemeConfig {
|
|
22
|
+
systemDarkMode?: boolean;
|
|
23
|
+
}
|
|
21
24
|
|
|
22
25
|
export function themeInit() {
|
|
23
26
|
if (isSSR) return;
|
|
@@ -31,8 +34,12 @@ export function themeInit() {
|
|
|
31
34
|
);
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
export function setTheme(config = {}) {
|
|
37
|
+
export function setTheme(config: InternalThemeConfig = {}) {
|
|
35
38
|
const isDarkMode = setDarkMode(config);
|
|
39
|
+
const rounding = config?.rounding ?? vuelessConfig.rounding ?? DEFAULT_ROUNDING;
|
|
40
|
+
let brand: BrandColors | GrayColors = config?.brand ?? vuelessConfig.brand ?? DEFAULT_BRAND_COLOR;
|
|
41
|
+
const gray = config?.gray ?? vuelessConfig.gray ?? DEFAULT_GRAY_COLOR;
|
|
42
|
+
|
|
36
43
|
const ring = config?.ring ?? vuelessConfig.ring ?? DEFAULT_RING;
|
|
37
44
|
const ringOffset = config?.ringOffset ?? vuelessConfig.ringOffset ?? DEFAULT_RING_OFFSET;
|
|
38
45
|
|
|
@@ -46,10 +53,7 @@ export function setTheme(config = {}) {
|
|
|
46
53
|
vuelessConfig.ringOffsetColorLight ??
|
|
47
54
|
DEFAULT_RING_OFFSET_COLOR_LIGHT;
|
|
48
55
|
|
|
49
|
-
const
|
|
50
|
-
let brand = config?.brand ?? vuelessConfig.brand ?? DEFAULT_BRAND_COLOR;
|
|
51
|
-
let gray = config?.gray ?? vuelessConfig.gray ?? DEFAULT_GRAY_COLOR;
|
|
52
|
-
|
|
56
|
+
const colors = fullTailwindConfig.theme.colors;
|
|
53
57
|
const isBrandColor = BRAND_COLORS.some((color) => color === brand);
|
|
54
58
|
const isGrayColor = GRAY_COLORS.some((color) => color === gray);
|
|
55
59
|
|
|
@@ -69,29 +73,29 @@ export function setTheme(config = {}) {
|
|
|
69
73
|
? defaultRingOffsetColorDark
|
|
70
74
|
: defaultRingOffsetColorLight;
|
|
71
75
|
|
|
72
|
-
if (gray === COOL_COLOR) {
|
|
73
|
-
gray = GRAY_COLOR;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
76
|
if (brand === GRAYSCALE_COLOR) {
|
|
77
77
|
brand = gray;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
const variables = {
|
|
80
|
+
const variables: Partial<VuelessCssVariables> = {
|
|
81
|
+
"--vl-rounding": `${Number(rounding) / PX_IN_REM}rem`,
|
|
81
82
|
"--vl-ring": `${ring}px`,
|
|
82
83
|
"--vl-ring-offset": `${ringOffset}px`,
|
|
83
|
-
"--vl-ring-offset-color":
|
|
84
|
-
"--vl-rounding": `${Number(rounding) / PX_IN_REM}rem`,
|
|
84
|
+
"--vl-ring-offset-color": convertHexInRgb(defaultRingOffsetColor),
|
|
85
85
|
"--vl-color-gray-default": convertHexInRgb(colors[gray][defaultBrandShade]),
|
|
86
86
|
"--vl-color-brand-default": convertHexInRgb(colors[brand][defaultGrayShade]),
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
for (const key in colors[gray]) {
|
|
90
|
-
variables[`--vl-color-gray-${key}`] = convertHexInRgb(
|
|
90
|
+
variables[`--vl-color-gray-${key}` as keyof VuelessCssVariables] = convertHexInRgb(
|
|
91
|
+
colors[gray][key],
|
|
92
|
+
);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
for (const key in colors[brand]) {
|
|
94
|
-
variables[`--vl-color-brand-${key}`] = convertHexInRgb(
|
|
96
|
+
variables[`--vl-color-brand-${key}` as keyof VuelessCssVariables] = convertHexInRgb(
|
|
97
|
+
colors[brand][key],
|
|
98
|
+
);
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
const stringVariables = Object.entries(variables)
|
|
@@ -110,14 +114,14 @@ export function setTheme(config = {}) {
|
|
|
110
114
|
return rootVariables;
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
function setDarkMode(config) {
|
|
117
|
+
function setDarkMode(config: InternalThemeConfig) {
|
|
114
118
|
config?.darkMode === undefined
|
|
115
119
|
? isCSR && localStorage.removeItem(DARK_MODE_SELECTOR)
|
|
116
|
-
: isCSR && localStorage.setItem(DARK_MODE_SELECTOR, Number(
|
|
120
|
+
: isCSR && localStorage.setItem(DARK_MODE_SELECTOR, Number(config?.darkMode).toString());
|
|
117
121
|
|
|
118
122
|
const storedDarkMode = isCSR ? localStorage.getItem(DARK_MODE_SELECTOR) : null;
|
|
119
123
|
|
|
120
|
-
|
|
124
|
+
const isDarkMode =
|
|
121
125
|
storedDarkMode !== null
|
|
122
126
|
? !!Number(storedDarkMode)
|
|
123
127
|
: !!(config?.darkMode ?? vuelessConfig.darkMode ?? config?.systemDarkMode);
|
|
@@ -129,7 +133,7 @@ function setDarkMode(config) {
|
|
|
129
133
|
return isDarkMode;
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
export function convertHexInRgb(hex) {
|
|
136
|
+
export function convertHexInRgb(hex: string) {
|
|
133
137
|
const color = hex.replace(/#/g, "");
|
|
134
138
|
|
|
135
139
|
let r, g, b;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { merge } from "lodash-es";
|
|
2
|
+
import { defineConfig } from "cva";
|
|
3
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
4
|
+
import { cloneDeep, isCSR, isSSR } from "./utilHelper.ts";
|
|
5
|
+
import {
|
|
6
|
+
BRAND_COLOR,
|
|
7
|
+
GRAYSCALE_COLOR,
|
|
8
|
+
DEFAULT_BRAND_COLOR,
|
|
9
|
+
NESTED_COMPONENT_REG_EXP,
|
|
10
|
+
} from "../constants.ts";
|
|
11
|
+
|
|
12
|
+
import type { BrandColors, Config, ComponentNames, Component, Defaults } from "../types.ts";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Load Vueless config from the project root.
|
|
16
|
+
* Both for server and client side renderings.
|
|
17
|
+
* IIFE for SSR is used to prevent top level await issue.
|
|
18
|
+
*/
|
|
19
|
+
export let vuelessConfig: Config = {};
|
|
20
|
+
|
|
21
|
+
if (isSSR) {
|
|
22
|
+
/* Load Vueless config from the project root in IIFE (no top-level await). */
|
|
23
|
+
(async () => {
|
|
24
|
+
try {
|
|
25
|
+
const filePath = `${process.cwd()}/vueless.config`;
|
|
26
|
+
|
|
27
|
+
vuelessConfig = (await import(/* @vite-ignore */ `${filePath}.js`)).default;
|
|
28
|
+
|
|
29
|
+
if (!vuelessConfig) {
|
|
30
|
+
vuelessConfig = (await import(/* @vite-ignore */ `${filePath}.ts`)).default;
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
vuelessConfig = {};
|
|
34
|
+
}
|
|
35
|
+
})();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isCSR) {
|
|
39
|
+
vuelessConfig =
|
|
40
|
+
Object.values(
|
|
41
|
+
import.meta.glob("/vueless.config.{js,ts}", { eager: true, import: "default" }),
|
|
42
|
+
)[0] || {};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Extend twMerge (tailwind merge) by vueless and user config:
|
|
47
|
+
* All list of rules available here:
|
|
48
|
+
* https://github.com/dcastil/tailwind-merge/blob/v2.3.0/src/lib/default-config.ts
|
|
49
|
+
*/
|
|
50
|
+
const twMerge = extendTailwindMerge(
|
|
51
|
+
merge(
|
|
52
|
+
{
|
|
53
|
+
extend: {
|
|
54
|
+
theme: {
|
|
55
|
+
spacing: ["safe-top", "safe-bottom", "safe-left", "safe-right"],
|
|
56
|
+
},
|
|
57
|
+
classGroups: {
|
|
58
|
+
"ring-w": [{ ring: ["dynamic"] }],
|
|
59
|
+
"ring-offset-w": [{ "ring-offset": ["dynamic"] }],
|
|
60
|
+
"ring-offset-color": [{ "ring-offset": ["dynamic"] }],
|
|
61
|
+
"font-size": [{ text: ["2xs"] }],
|
|
62
|
+
rounded: [{ rounded: ["dynamic"] }],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
vuelessConfig.tailwindMerge,
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Export cva (class variance authority) methods:
|
|
72
|
+
* – extended with tailwind-merge
|
|
73
|
+
* – remove all Vueless nested component names ({U...} strings) from class list string.
|
|
74
|
+
* Learn more here: https://beta.cva.style
|
|
75
|
+
*/
|
|
76
|
+
export const {
|
|
77
|
+
cx,
|
|
78
|
+
compose,
|
|
79
|
+
cva: classVarianceAuthority,
|
|
80
|
+
} = defineConfig({
|
|
81
|
+
hooks: {
|
|
82
|
+
onComplete: (classNames) => twMerge(classNames).replace(NESTED_COMPONENT_REG_EXP, ""),
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
/* This allows skipping some CVA config keys in vueless config. */
|
|
87
|
+
export const cva = ({ base = "", variants = {}, compoundVariants = [], defaultVariants = {} }) =>
|
|
88
|
+
classVarianceAuthority({
|
|
89
|
+
base,
|
|
90
|
+
variants,
|
|
91
|
+
compoundVariants,
|
|
92
|
+
defaultVariants,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Return default values for component props, icons, etc..
|
|
97
|
+
*/
|
|
98
|
+
export function getDefault<T>(defaultConfig: Component, name: ComponentNames): T {
|
|
99
|
+
const componentDefaults = cloneDeep(defaultConfig.defaults) || {};
|
|
100
|
+
const globalDefaults = cloneDeep(vuelessConfig.component?.[name]?.defaults) || {};
|
|
101
|
+
|
|
102
|
+
const defaults = merge(componentDefaults, globalDefaults) as T & Defaults;
|
|
103
|
+
|
|
104
|
+
if (defaults.color) {
|
|
105
|
+
defaults.color = getColor(defaults.color as BrandColors);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return defaults;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Return `grayscale` color if in component config it `brand` but in vueless config it `grayscale`
|
|
113
|
+
* Otherwise return given color.
|
|
114
|
+
*/
|
|
115
|
+
export function getColor(color: string) {
|
|
116
|
+
const isBrandColorGrayscale = (vuelessConfig.brand ?? DEFAULT_BRAND_COLOR) === GRAYSCALE_COLOR;
|
|
117
|
+
const isComponentColorBrand = color === BRAND_COLOR;
|
|
118
|
+
|
|
119
|
+
return isBrandColorGrayscale && isComponentColorBrand ? GRAYSCALE_COLOR : color;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Replace in tailwind classes `{color}` variable into given color.
|
|
124
|
+
*/
|
|
125
|
+
export function setColor(classes: string, color: string) {
|
|
126
|
+
return classes?.replace(/{color}/g, color);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generates simple unique identifier.
|
|
131
|
+
*/
|
|
132
|
+
export function getRandomId(length = 15) {
|
|
133
|
+
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
134
|
+
const charactersLength = characters.length;
|
|
135
|
+
|
|
136
|
+
let id = "";
|
|
137
|
+
|
|
138
|
+
while (id.length < length) {
|
|
139
|
+
id += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return id;
|
|
143
|
+
}
|
package/web-types.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import useUI from "../composables/useUI.js";
|
|
2
|
-
|
|
3
|
-
import defaultConfig from "./config.js";
|
|
4
|
-
|
|
5
|
-
export default function useAttrs(props) {
|
|
6
|
-
const { config, getKeysAttrs, hasSlotContent } = useUI(defaultConfig, () => props.config);
|
|
7
|
-
|
|
8
|
-
const keysAttrs = getKeysAttrs();
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
config,
|
|
12
|
-
...keysAttrs,
|
|
13
|
-
hasSlotContent,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
File without changes
|
|
File without changes
|