vueless 0.0.576 → 0.0.578
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 +5 -0
- package/ui.button/UButton.vue +3 -3
- package/ui.button/storybook/stories.ts +2 -2
- package/ui.button/types.ts +1 -1
- package/ui.form-calendar/UCalendar.vue +16 -12
- package/ui.form-calendar/utilCalendar.ts +2 -2
- package/ui.form-date-picker/UDatePicker.vue +2 -1
- package/utils/node/dynamicProps.js +190 -0
- package/web-types.json +1 -1
package/package.json
CHANGED
package/plugin-vite.js
CHANGED
|
@@ -10,6 +10,7 @@ import { cacheIcons, removeIconsCache, copyIconsCache } from "./utils/node/loade
|
|
|
10
10
|
import { createTailwindSafelist, clearTailwindSafelist } from "./utils/node/tailwindSafelist.js";
|
|
11
11
|
import { getNuxtFiles, getVueFiles } from "./utils/node/helper.js";
|
|
12
12
|
import { componentResolver, directiveResolver } from "./utils/node/vuelessResolver.js";
|
|
13
|
+
import { setCustomPropTypes, removeCustomPropTypes } from "./utils/node/dynamicProps.js";
|
|
13
14
|
|
|
14
15
|
/* Automatically importing Vueless components on demand */
|
|
15
16
|
export const VuelessUnpluginComponents = (options) =>
|
|
@@ -34,6 +35,8 @@ export const Vueless = function (options = {}) {
|
|
|
34
35
|
|
|
35
36
|
/* if server stopped by developer (Ctrl+C) */
|
|
36
37
|
process.on("SIGINT", async () => {
|
|
38
|
+
await removeCustomPropTypes(isVuelessEnv);
|
|
39
|
+
|
|
37
40
|
/* remove cached icons */
|
|
38
41
|
await removeIconsCache(mirrorCacheDir, debug);
|
|
39
42
|
|
|
@@ -82,6 +85,8 @@ export const Vueless = function (options = {}) {
|
|
|
82
85
|
await cacheIcons({ mode: "vuelessIcons", env, debug, targetFiles });
|
|
83
86
|
/* copy vueless cache folder */
|
|
84
87
|
await copyIconsCache(mirrorCacheDir, debug);
|
|
88
|
+
|
|
89
|
+
await setCustomPropTypes(isVuelessEnv);
|
|
85
90
|
}
|
|
86
91
|
},
|
|
87
92
|
|
package/ui.button/UButton.vue
CHANGED
|
@@ -12,12 +12,12 @@ import UIcon from "../ui.image-icon/UIcon.vue";
|
|
|
12
12
|
import defaultConfig from "./config.ts";
|
|
13
13
|
import { UButton } from "./constants.ts";
|
|
14
14
|
|
|
15
|
-
import type {
|
|
15
|
+
import type { Props, LoaderSize, IconSize, Config } from "./types.ts";
|
|
16
16
|
|
|
17
17
|
defineOptions({ inheritAttrs: false });
|
|
18
18
|
|
|
19
|
-
const props = withDefaults(defineProps<
|
|
20
|
-
...getDefaults<
|
|
19
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
20
|
+
...getDefaults<Props>(defaultConfig, UButton),
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
const slots = useSlots();
|
|
@@ -15,9 +15,9 @@ import UCol from "../../ui.container-col/UCol.vue";
|
|
|
15
15
|
import { useDarkMode } from "../../composables/useDarkMode.ts";
|
|
16
16
|
|
|
17
17
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
18
|
-
import type {
|
|
18
|
+
import type { Props } from "../types.ts";
|
|
19
19
|
|
|
20
|
-
interface UButtonArgs extends
|
|
20
|
+
interface UButtonArgs extends Props {
|
|
21
21
|
slotTemplate?: string;
|
|
22
22
|
enum: "variant" | "size";
|
|
23
23
|
}
|
package/ui.button/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts" generic="TModelValue extends DateValue">
|
|
2
|
-
import { computed, ref, watch, useTemplateRef
|
|
2
|
+
import { computed, ref, watch, useTemplateRef } from "vue";
|
|
3
3
|
import { merge } from "lodash-es";
|
|
4
4
|
|
|
5
5
|
import UButton from "../ui.button/UButton.vue";
|
|
@@ -333,19 +333,23 @@ watch(
|
|
|
333
333
|
},
|
|
334
334
|
);
|
|
335
335
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
336
|
+
watch(
|
|
337
|
+
selectedDate,
|
|
338
|
+
() => {
|
|
339
|
+
if (selectedDate.value && isTimepickerEnabled.value && isInputRefs.value && props.timepicker) {
|
|
340
|
+
hoursRef.value!.value = String(selectedDate.value.getHours()).padStart(2, "0");
|
|
341
|
+
minutesRef.value!.value = String(selectedDate.value.getMinutes()).padStart(2, "0");
|
|
342
|
+
secondsRef.value!.value = String(selectedDate.value.getSeconds()).padStart(2, "0");
|
|
341
343
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
+
emit("userDateChange", userFormattedDate.value);
|
|
345
|
+
}
|
|
344
346
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
347
|
+
if (selectedDate.value) {
|
|
348
|
+
emit("userDateChange", userFormattedDate.value);
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
{ immediate: true },
|
|
352
|
+
);
|
|
349
353
|
|
|
350
354
|
function getCurrentValueType(value: DateValue): DateValue {
|
|
351
355
|
if (props.range && value === null) {
|
|
@@ -59,7 +59,7 @@ export function parseDate<TLocale extends DateLocale>(
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
let timeless = false;
|
|
62
|
-
let parsedDate = null;
|
|
62
|
+
let parsedDate: Date | null = null;
|
|
63
63
|
const originalDate = date;
|
|
64
64
|
|
|
65
65
|
const isDateObject = date instanceof Date;
|
|
@@ -81,7 +81,7 @@ export function parseDate<TLocale extends DateLocale>(
|
|
|
81
81
|
parsedDate = new Date();
|
|
82
82
|
timeless = true;
|
|
83
83
|
} else {
|
|
84
|
-
parsedDate = parseStringDate(date, format, locale);
|
|
84
|
+
parsedDate = parseStringDate(date, format, locale) || null;
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -154,6 +154,7 @@ function onPaste(event: ClipboardEvent) {
|
|
|
154
154
|
try {
|
|
155
155
|
const pasteContent = event.clipboardData ? event.clipboardData.getData("text/plain") : "";
|
|
156
156
|
const userFormat = props.timepicker ? props.userDateTimeFormat : props.userDateFormat;
|
|
157
|
+
const dateFormat = props.timepicker ? props.dateFormat : props.dateTimeFormat;
|
|
157
158
|
const relativeTokensAmount = Number(userFormat.match(/(?<!\\)r/g)?.length);
|
|
158
159
|
|
|
159
160
|
// Amount of tokens used in format string without decimeters.
|
|
@@ -195,7 +196,7 @@ function onPaste(event: ClipboardEvent) {
|
|
|
195
196
|
|
|
196
197
|
if (parsedDate) {
|
|
197
198
|
localValue.value = (
|
|
198
|
-
|
|
199
|
+
dateFormat ? formatDate(parsedDate, dateFormat, locale.value) : parsedDate
|
|
199
200
|
) as TModelValue;
|
|
200
201
|
}
|
|
201
202
|
} catch (error) {
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { vuelessConfig } from "./vuelessConfig.js";
|
|
6
|
+
|
|
7
|
+
import { COMPONENTS, VUELESS_DIR, VUELESS_LOCAL_DIR } from "../../constants.js";
|
|
8
|
+
|
|
9
|
+
const OPTIONAL_MARK = "?";
|
|
10
|
+
const CLOSING_BRACKET = "}";
|
|
11
|
+
const IGNORE_PROP = "@ignore";
|
|
12
|
+
const CUSTOM_PROP = "@custom";
|
|
13
|
+
|
|
14
|
+
const VUELESS_SRC = path.join(VUELESS_DIR, VUELESS_LOCAL_DIR);
|
|
15
|
+
|
|
16
|
+
const PROPS_INTERFACE_REG_EXP = /export\s+interface\s+Props\s*{([^}]*)}/s;
|
|
17
|
+
const UNION_SYMBOLS_REG_EXP = /[?|:"|;]/g;
|
|
18
|
+
const WORD_IN_QUOTE_REG_EXP = /([^"]+)/;
|
|
19
|
+
|
|
20
|
+
async function cacheComponentTypes(filePath) {
|
|
21
|
+
const cacheDir = path.join(filePath, ".cache");
|
|
22
|
+
const sourceFile = path.join(filePath, "types.ts");
|
|
23
|
+
const destFile = path.join(cacheDir, "types.ts");
|
|
24
|
+
|
|
25
|
+
if (existsSync(cacheDir)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (existsSync(sourceFile)) {
|
|
30
|
+
await fs.mkdir(cacheDir);
|
|
31
|
+
await fs.cp(sourceFile, destFile);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function clearComponentTypesCache(filePath) {
|
|
36
|
+
await fs.rm(path.join(filePath, ".cache"), { force: true, recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function restoreComponentTypes(filePath) {
|
|
40
|
+
const cacheDir = path.join(filePath, ".cache");
|
|
41
|
+
const sourceFile = path.join(cacheDir, "types.ts");
|
|
42
|
+
const destFile = path.join(filePath, "types.ts");
|
|
43
|
+
|
|
44
|
+
if (existsSync(sourceFile)) {
|
|
45
|
+
await fs.cp(sourceFile, destFile);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getMultiLineUnionValues(lines, propIndex, propEndIndex) {
|
|
50
|
+
return lines
|
|
51
|
+
.slice(propIndex)
|
|
52
|
+
.slice(1, propEndIndex + 1)
|
|
53
|
+
.map((item) => item.replace(UNION_SYMBOLS_REG_EXP, "").trim());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getInlineUnionValues(lines, propIndex, propEndIndex) {
|
|
57
|
+
const types = lines
|
|
58
|
+
.slice(propIndex)
|
|
59
|
+
.slice(0, propEndIndex + 1)
|
|
60
|
+
.at(0)
|
|
61
|
+
.match(WORD_IN_QUOTE_REG_EXP);
|
|
62
|
+
|
|
63
|
+
return types ? types.map((value) => value.replace(/"/g, "")) : [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Updates or add a prop types dynamically.
|
|
68
|
+
* @param {string} filePath - The path to the TypeScript file.
|
|
69
|
+
* @param {Array} props - Array of prop objects to add or update.
|
|
70
|
+
*/
|
|
71
|
+
async function modifyComponentTypes(filePath, props) {
|
|
72
|
+
try {
|
|
73
|
+
const targetFile = path.join(filePath, "types.ts");
|
|
74
|
+
|
|
75
|
+
/* Read `types.ts` and split it by lines. */
|
|
76
|
+
let fileContent = await fs.readFile(targetFile, "utf-8");
|
|
77
|
+
const propsInterface = fileContent.match(PROPS_INTERFACE_REG_EXP)?.at(0)?.trim();
|
|
78
|
+
|
|
79
|
+
/* Remove props interface and double returns from fileContent */
|
|
80
|
+
fileContent = fileContent.replace(propsInterface, "").replace(/\n\s*\n/g, "\n");
|
|
81
|
+
|
|
82
|
+
const lines = propsInterface.split("\n");
|
|
83
|
+
|
|
84
|
+
for (const prop of props) {
|
|
85
|
+
const { name, type, values = [], description, required, ignore } = prop;
|
|
86
|
+
|
|
87
|
+
if (!name) return;
|
|
88
|
+
|
|
89
|
+
/* Find line with prop. */
|
|
90
|
+
const propRegex = new RegExp(`^\\s*${name}[?:]?\\s*:`);
|
|
91
|
+
const propIndex = lines.findIndex((line) => propRegex.test(line));
|
|
92
|
+
const propEndIndex = lines.slice(propIndex).findIndex((line) => line.endsWith(";"));
|
|
93
|
+
const propTypes = propEndIndex
|
|
94
|
+
? getMultiLineUnionValues(lines, propIndex, propEndIndex)
|
|
95
|
+
: getInlineUnionValues(lines, propIndex, propEndIndex);
|
|
96
|
+
|
|
97
|
+
const defaultUnionType = propTypes.map((value) => `"${value}"`).join(" | ");
|
|
98
|
+
|
|
99
|
+
/* Prepare prop params. */
|
|
100
|
+
const uniqueValues = [...new Set(values)];
|
|
101
|
+
const isAssignableValue = uniqueValues.every((value) => propTypes.includes(value));
|
|
102
|
+
const unionType = uniqueValues.map((value) => `"${value}"`).join(" | ");
|
|
103
|
+
const userOptionalMark = required ? "" : OPTIONAL_MARK;
|
|
104
|
+
const defaultOptionalMark = lines[propIndex]?.includes(OPTIONAL_MARK) ? OPTIONAL_MARK : "";
|
|
105
|
+
const optionalMark = required === undefined ? defaultOptionalMark : userOptionalMark;
|
|
106
|
+
|
|
107
|
+
const propDescription = description?.replaceAll(/[\n\s]+/g, " ").trim() || "–"; // removes new lines and double spaces.
|
|
108
|
+
const propType = unionType.length ? unionType : type;
|
|
109
|
+
|
|
110
|
+
/* Add ignore JSDoc property. */
|
|
111
|
+
if (ignore) {
|
|
112
|
+
const ignoreDefinition = [` * ${IGNORE_PROP}`, ` */`];
|
|
113
|
+
const deleteLinesCount = lines[propIndex - 2].includes(IGNORE_PROP) ? 2 : 1;
|
|
114
|
+
|
|
115
|
+
lines.splice(propIndex - deleteLinesCount, deleteLinesCount, ...ignoreDefinition);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Check if the prop type already exists. */
|
|
119
|
+
if (~propIndex) {
|
|
120
|
+
if (unionType.length && isAssignableValue) {
|
|
121
|
+
// Remove multiline union types;
|
|
122
|
+
lines.splice(propIndex + 1, propEndIndex);
|
|
123
|
+
|
|
124
|
+
lines.splice(propIndex, 1, ` ${name}${defaultOptionalMark}: ${propType};`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (unionType.length && !isAssignableValue) {
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.warn(`${unionType} is not assignable to type ${defaultUnionType}.`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const isCustomProp = lines[propIndex - 2].includes(CUSTOM_PROP);
|
|
133
|
+
|
|
134
|
+
if (!isCustomProp && (type || description || required)) {
|
|
135
|
+
// eslint-disable-next-line no-console, prettier/prettier
|
|
136
|
+
console.warn("Changing of prop type, description or required are not allowed for the default props.");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Add new prop. */
|
|
143
|
+
const closingBracketIndex = lines.findIndex((line) => line.trim() === CLOSING_BRACKET);
|
|
144
|
+
const propDefinition = [
|
|
145
|
+
"",
|
|
146
|
+
` /**`,
|
|
147
|
+
` * ${propDescription}`,
|
|
148
|
+
` * ${CUSTOM_PROP}`,
|
|
149
|
+
` */`,
|
|
150
|
+
` ${name}${optionalMark}: ${type};`,
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
lines.splice(closingBracketIndex, 0, ...propDefinition);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
lines.unshift(fileContent);
|
|
157
|
+
|
|
158
|
+
/* Update `types.ts` file. */
|
|
159
|
+
await fs.writeFile(targetFile, lines.join("\n"), "utf-8");
|
|
160
|
+
} catch (error) {
|
|
161
|
+
// eslint-disable-next-line no-console
|
|
162
|
+
console.error("Error updating file:", error.message);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function setCustomPropTypes(isVuelessEnv) {
|
|
167
|
+
const srcDir = isVuelessEnv ? VUELESS_LOCAL_DIR : VUELESS_SRC;
|
|
168
|
+
|
|
169
|
+
for await (const [componentName, componentDir] of Object.entries(COMPONENTS)) {
|
|
170
|
+
const customProps =
|
|
171
|
+
componentName in vuelessConfig.component && vuelessConfig.component[componentName].props;
|
|
172
|
+
|
|
173
|
+
if (customProps) {
|
|
174
|
+
await cacheComponentTypes(path.join(srcDir, componentDir));
|
|
175
|
+
await modifyComponentTypes(
|
|
176
|
+
path.join(srcDir, componentDir),
|
|
177
|
+
vuelessConfig.component[componentName].props,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function removeCustomPropTypes(isVuelessEnv) {
|
|
184
|
+
const srcDir = isVuelessEnv ? VUELESS_LOCAL_DIR : VUELESS_SRC;
|
|
185
|
+
|
|
186
|
+
for await (const componentDir of Object.values(COMPONENTS)) {
|
|
187
|
+
await restoreComponentTypes(path.join(srcDir, componentDir));
|
|
188
|
+
await clearComponentTypesCache(path.join(srcDir, componentDir));
|
|
189
|
+
}
|
|
190
|
+
}
|