vueless 0.0.103 → 0.0.104

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.103",
3
+ "version": "0.0.104",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless Component Framework.",
6
6
  "homepage": "https://vueless.com",
@@ -6,7 +6,7 @@ import defaultConfig from "../configs/default.config";
6
6
 
7
7
  export function useAttrs(props) {
8
8
  const { config, getAttrs } = useUI(defaultConfig, () => props.config);
9
- const { dropzoneWrapper, placeholder } = config.value;
9
+ const { dropzoneWrapper, placeholder, selectedItem } = config.value;
10
10
 
11
11
  const cvaDropzoneWrapper = cva({
12
12
  base: dropzoneWrapper.base,
@@ -20,6 +20,12 @@ export function useAttrs(props) {
20
20
  compoundVariants: placeholder.compoundVariants,
21
21
  });
22
22
 
23
+ const cvaSelectedItem = cva({
24
+ base: selectedItem.base,
25
+ variants: selectedItem.variants,
26
+ compoundVariants: selectedItem.compoundVariants,
27
+ });
28
+
23
29
  const dropzoneWrapperClasses = computed(() =>
24
30
  cvaDropzoneWrapper({
25
31
  error: Boolean(props.error),
@@ -35,20 +41,25 @@ export function useAttrs(props) {
35
41
  }),
36
42
  );
37
43
 
44
+ const selectedItemClasses = computed(() =>
45
+ cvaSelectedItem({
46
+ size: props.size,
47
+ }),
48
+ );
49
+
38
50
  const labelAttrs = getAttrs("label", { isComponent: true });
39
51
  const buttonAttrs = getAttrs("button", { isComponent: true });
40
52
  const dropzoneWrapperAttrs = getAttrs("dropzoneWrapper", { classes: dropzoneWrapperClasses });
41
53
  const descriptionAttrs = getAttrs("description", { isComponent: true });
54
+ const contentWrapperAttrs = getAttrs("contentWrapper");
42
55
  const buttonWrapperAttrs = getAttrs("buttonWrapper");
43
- const placeholderWrapperAttrs = getAttrs("placeholderWrapper");
44
- const placeholderAttrs = getAttrs("placeholder", {
45
- classes: placeholderClasses,
46
- isComponent: true,
47
- });
48
- const placeholderIconAttrs = getAttrs("placeholderIcon", { isComponent: true });
56
+ const placeholderAttrs = getAttrs("placeholder", { classes: placeholderClasses });
49
57
  const clearIconAttrs = getAttrs("clearIcon", { isComponent: true });
58
+ const chooseFileIconAttrs = getAttrs("chooseFileIcon", { isComponent: true });
50
59
  const chooseFileIconNameAttrs = getAttrs("chooseFileIconName", { isComponent: true });
51
60
  const inputAttrs = getAttrs("input");
61
+ const fileListAttrs = getAttrs("fileList");
62
+ const selectedItemAttrs = getAttrs("selectedItem", { classes: selectedItemClasses });
52
63
 
53
64
  return {
54
65
  config,
@@ -57,11 +68,13 @@ export function useAttrs(props) {
57
68
  buttonAttrs,
58
69
  dropzoneWrapperAttrs,
59
70
  descriptionAttrs,
60
- buttonWrapperAttrs,
61
- placeholderWrapperAttrs,
62
- placeholderIconAttrs,
71
+ contentWrapperAttrs,
63
72
  clearIconAttrs,
64
73
  chooseFileIconNameAttrs,
65
74
  placeholderAttrs,
75
+ fileListAttrs,
76
+ buttonWrapperAttrs,
77
+ selectedItemAttrs,
78
+ chooseFileIconAttrs,
66
79
  };
67
80
  }
@@ -26,10 +26,20 @@ export default /*tw*/ {
26
26
  ],
27
27
  },
28
28
  description: "text-gray-700",
29
- buttonWrapper: "relative mt-3 flex w-full gap-52 justify-between rounded bg-brand-50 p-4",
30
- placeholderWrapper: "flex items-center gap-2",
29
+ contentWrapper: "relative mt-3 flex w-full gap-6 justify-between items-start rounded bg-brand-50 p-4",
30
+ fileList: "pr-4 shrink-0 text-gray-700 flex-grow flex flex-col gap-4",
31
31
  placeholder: {
32
- base: "pr-4 shrink-0 text-ellipsis overflow-hidden text-gray-700 text-nowrap",
32
+ base: "pr-4 shrink-0 text-gray-700 flex-grow",
33
+ variants: {
34
+ size: {
35
+ sm: "text-sm",
36
+ md: "text-base",
37
+ lg: "text-lg",
38
+ },
39
+ },
40
+ },
41
+ selectedItem: {
42
+ base: "pr-4 shrink-0 text-gray-700 flex-grow",
33
43
  variants: {
34
44
  size: {
35
45
  sm: "text-sm",
@@ -39,25 +49,25 @@ export default /*tw*/ {
39
49
  },
40
50
  },
41
51
  button: "hover:cursor-pointer",
42
- placeholderIcon: "-rotate-45",
43
- placeholderIconName: "attach_file",
44
52
  chooseFileIcon: "",
45
- chooseFileIconName: "upload_file",
53
+ chooseFileIconName: "attach_file",
46
54
  clearIcon: "",
47
55
  clearIconName: "close",
48
- dropzoneWrapperHover: "border-gray-400 bg-gray-50",
56
+ dropzoneWrapperHover: "border-gray-400",
49
57
  dropzoneWrapperError: "hover:border-red-400 border-red-300",
50
58
  input: "sr-only pointer-events-none size-0 opacity-0",
59
+ buttonWrapper: "flex gap-4 items-center",
51
60
  i18n: {
52
61
  sizeError: "File size is too big.",
53
62
  formatError: "Format is not supported.",
54
63
  noFile: "No file selected",
55
- uploadFile: "Upload file",
64
+ uploadFile: "Choose file",
56
65
  },
57
66
  defaultVariants: {
58
67
  size: "md",
59
68
  labelAlign: "topInside",
60
69
  allowedFileTypes: [],
61
- maxFileSize: 100,
70
+ multiple: false,
71
+ maxFileSize: 0,
62
72
  },
63
73
  };
@@ -3,58 +3,64 @@
3
3
  <div ref="dropZoneRef" :ondrop="onDrop" v-bind="dropzoneWrapperAttrs">
4
4
  <UText :size="nestedComponentSize" v-bind="descriptionAttrs" :html="description" />
5
5
 
6
- <div v-bind="buttonWrapperAttrs">
7
- <div v-bind="placeholderWrapperAttrs">
6
+ <div v-bind="contentWrapperAttrs">
7
+ <slot name="left" />
8
+ <span v-if="!isValue" v-bind="placeholderAttrs" v-text="currentLocale.noFile" />
9
+ <div v-else v-bind="fileListAttrs">
10
+ <template v-if="props.multiple">
11
+ <span
12
+ v-for="(file, idx) in currentFiles"
13
+ v-bind="selectedItemAttrs"
14
+ :key="idx"
15
+ v-text="file.name"
16
+ />
17
+ </template>
18
+ <span v-else v-bind="selectedItemAttrs" v-text="currentFiles.name" />
19
+ </div>
20
+
21
+ <div v-bind="buttonWrapperAttrs">
22
+ <template v-if="Array.isArray(currentFiles) || !currentFiles">
23
+ <UButton
24
+ class="hover:cursor-pointer"
25
+ :label="currentLocale.uploadFile"
26
+ variant="thirdary"
27
+ filled
28
+ :size="nestedComponentSize"
29
+ v-bind="buttonAttrs"
30
+ tag="label"
31
+ :for="id"
32
+ >
33
+ <template #right>
34
+ <UIcon
35
+ internal
36
+ :size="nestedComponentSize"
37
+ :name="config.chooseFileIconName"
38
+ v-bind="chooseFileIconAttrs"
39
+ />
40
+ </template>
41
+ </UButton>
42
+
43
+ <input
44
+ :id="id"
45
+ ref="fileInputRef"
46
+ :multiple="multiple"
47
+ type="file"
48
+ :accept="accept"
49
+ v-bind="inputAttrs"
50
+ @change="onChangeFile"
51
+ />
52
+ </template>
8
53
  <UIcon
9
- pill
54
+ v-if="isValue"
55
+ interactive
10
56
  internal
11
57
  :size="nestedComponentSize"
12
- :name="config.placeholderIconName"
13
- v-bind="placeholderIconAttrs"
58
+ :name="config.clearIconName"
59
+ pill
60
+ v-bind="clearIconAttrs"
61
+ @click="onClickResetFiles"
14
62
  />
15
- <span v-bind="placeholderAttrs" v-text="placeholder" />
16
63
  </div>
17
-
18
- <template v-if="!currentFiles.length">
19
- <UButton
20
- class="hover:cursor-pointer"
21
- :label="currentLocale.uploadFile"
22
- variant="thirdary"
23
- filled
24
- :size="nestedComponentSize"
25
- v-bind="buttonAttrs"
26
- tag="label"
27
- :for="id"
28
- >
29
- <template #right>
30
- <UIcon
31
- internal
32
- :size="nestedComponentSize"
33
- :name="config.chooseFileIconName"
34
- v-bind="chooseFileIconAttrs"
35
- />
36
- </template>
37
- </UButton>
38
-
39
- <input
40
- :id="id"
41
- ref="fileInputRef"
42
- type="file"
43
- :accept="accept"
44
- v-bind="inputAttrs"
45
- @change="onChangeFile"
46
- />
47
- </template>
48
- <UIcon
49
- v-else
50
- interactive
51
- internal
52
- :size="nestedComponentSize"
53
- :name="config.clearIconName"
54
- pill
55
- v-bind="clearIconAttrs"
56
- @click="onClickResetFiles"
57
- />
58
64
  </div>
59
65
  </div>
60
66
  </ULabel>
@@ -87,7 +93,7 @@ const props = defineProps({
87
93
  */
88
94
  label: {
89
95
  type: String,
90
- default: "Label",
96
+ default: "",
91
97
  },
92
98
 
93
99
  /**
@@ -95,7 +101,7 @@ const props = defineProps({
95
101
  */
96
102
  description: {
97
103
  type: String,
98
- default: "Some description here",
104
+ default: "",
99
105
  },
100
106
 
101
107
  /**
@@ -108,8 +114,16 @@ const props = defineProps({
108
114
  },
109
115
 
110
116
  modelValue: {
111
- type: Array,
112
- default: () => [],
117
+ type: [Array, File],
118
+ default: null,
119
+ },
120
+
121
+ /**
122
+ * Allow select multiple files.
123
+ */
124
+ multiple: {
125
+ type: Boolean,
126
+ default: UIService.get(defaultConfig, UInputFile).default.multiple,
113
127
  },
114
128
 
115
129
  /**
@@ -175,13 +189,14 @@ const {
175
189
  buttonAttrs,
176
190
  dropzoneWrapperAttrs,
177
191
  descriptionAttrs,
178
- buttonWrapperAttrs,
179
- placeholderWrapperAttrs,
180
- placeholderIconAttrs,
192
+ contentWrapperAttrs,
181
193
  clearIconAttrs,
182
194
  chooseFileIconAttrs,
183
195
  placeholderAttrs,
184
196
  inputAttrs,
197
+ fileListAttrs,
198
+ buttonWrapperAttrs,
199
+ selectedItemAttrs,
185
200
  } = useAttrs(props);
186
201
 
187
202
  const i18nGlobal = tm(UInputFile);
@@ -189,7 +204,11 @@ const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props
189
204
 
190
205
  const currentFiles = computed({
191
206
  get: () => props.modelValue,
192
- set: (newValue) => emit("update:modelValue", newValue),
207
+ set: (newValue) => {
208
+ const fallbackValue = props.multiple ? [] : null;
209
+
210
+ emit("update:modelValue", newValue || fallbackValue);
211
+ },
193
212
  });
194
213
 
195
214
  const currentError = computed({
@@ -197,16 +216,23 @@ const currentError = computed({
197
216
  set: (newValue) => emit("update:error", newValue),
198
217
  });
199
218
 
200
- const accept = computed(() => {
201
- return props.allowedFileTypes.join(",");
202
- });
203
-
204
219
  const extensionNames = computed(() => {
205
220
  return props.allowedFileTypes.map((type) => type.replace(".", ""));
206
221
  });
207
222
 
208
- const placeholder = computed(() => {
209
- return currentFiles.value.length ? currentFiles.value[0].name : currentLocale.value.noFile;
223
+ const allowedFileTypeFormats = computed(() => {
224
+ return props.allowedFileTypes.map((type) => (type.startsWith(".") ? type : `.${type}`));
225
+ });
226
+
227
+ const accept = computed(() => {
228
+ return allowedFileTypeFormats.value.join(",");
229
+ });
230
+
231
+ const isValue = computed(() => {
232
+ return (
233
+ (Array.isArray(currentFiles.value) && currentFiles.value.length) ||
234
+ (!Array.isArray(currentFiles.value) && currentFiles.value)
235
+ );
210
236
  });
211
237
 
212
238
  const nestedComponentSize = computed(() => {
@@ -238,7 +264,7 @@ function validate(file) {
238
264
 
239
265
  const isValidSize = targetFileSize <= props.maxFileSize;
240
266
 
241
- if (!isValidSize) {
267
+ if (!isValidSize && props.maxFileSize) {
242
268
  currentError.value = currentLocale.value.sizeError;
243
269
  }
244
270
 
@@ -256,11 +282,15 @@ function onChangeFile(event) {
256
282
  return;
257
283
  }
258
284
 
259
- currentFiles.value = Array.from(event.target.files);
285
+ currentFiles.value = props.multiple
286
+ ? [...currentFiles.value, Array.from(event.target.files).at(0)]
287
+ : Array.from(event.target.files).at(0);
288
+
289
+ if (fileInputRef.value) fileInputRef.value.value = "";
260
290
  }
261
291
 
262
292
  function onClickResetFiles() {
263
- currentFiles.value = [];
293
+ currentFiles.value = null;
264
294
 
265
295
  if (fileInputRef.value) fileInputRef.value.value = "";
266
296
  }
@@ -268,13 +298,13 @@ function onClickResetFiles() {
268
298
  function onDragOver(event) {
269
299
  event.preventDefault();
270
300
 
271
- dropZoneRef.value.classList.add(config.value.dropzoneWrapperHover.split(" "));
301
+ dropZoneRef.value.classList.add(...config.value.dropzoneWrapperHover.split(" "));
272
302
  }
273
303
 
274
304
  function onDragLeave(event) {
275
305
  event.preventDefault();
276
306
 
277
- dropZoneRef.value.classList.remove(config.value.dropzoneWrapperHover.split(" "));
307
+ dropZoneRef.value.classList.remove(...config.value.dropzoneWrapperHover.split(" "));
278
308
  }
279
309
 
280
310
  function onDrop(event) {
@@ -297,7 +327,7 @@ function onDrop(event) {
297
327
  return;
298
328
  }
299
329
 
300
- currentFiles.value = [targetFile];
330
+ currentFiles.value = props.multiple ? [...currentFiles.value, targetFile] : targetFile;
301
331
  });
302
332
  }
303
333
  </script>
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "vueless",
4
- "version": "0.0.103",
4
+ "version": "0.0.104",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",
@@ -3448,7 +3448,7 @@
3448
3448
  "kind": "expression",
3449
3449
  "type": "string"
3450
3450
  },
3451
- "default": "\"Label\""
3451
+ "default": "\"\""
3452
3452
  },
3453
3453
  {
3454
3454
  "name": "description",
@@ -3457,7 +3457,7 @@
3457
3457
  "kind": "expression",
3458
3458
  "type": "string"
3459
3459
  },
3460
- "default": "\"Some description here\""
3460
+ "default": "\"\""
3461
3461
  },
3462
3462
  {
3463
3463
  "name": "labelAlign",
@@ -3472,9 +3472,18 @@
3472
3472
  "name": "modelValue",
3473
3473
  "value": {
3474
3474
  "kind": "expression",
3475
- "type": "array"
3475
+ "type": "array|File"
3476
3476
  },
3477
- "default": "[]"
3477
+ "default": "null"
3478
+ },
3479
+ {
3480
+ "name": "multiple",
3481
+ "description": "Allow select multiple files.",
3482
+ "value": {
3483
+ "kind": "expression",
3484
+ "type": "boolean"
3485
+ },
3486
+ "default": "false"
3478
3487
  },
3479
3488
  {
3480
3489
  "name": "maxFileSize",
@@ -3483,7 +3492,7 @@
3483
3492
  "kind": "expression",
3484
3493
  "type": "number"
3485
3494
  },
3486
- "default": "100"
3495
+ "default": "0"
3487
3496
  },
3488
3497
  {
3489
3498
  "name": "allowedFileTypes",
@@ -3539,6 +3548,11 @@
3539
3548
  "name": "update:error"
3540
3549
  }
3541
3550
  ],
3551
+ "slots": [
3552
+ {
3553
+ "name": "left"
3554
+ }
3555
+ ],
3542
3556
  "source": {
3543
3557
  "module": "vueless/ui.form-input-file/index.vue",
3544
3558
  "symbol": "default"
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 -960 960 960"><path d="M452-203.43h60v-201.96l82.48 82.48 42-42L480-517.15 325.76-362.91l42 42L452-405.39v201.96ZM222.15-74.02q-27.6 0-47.86-20.27-20.27-20.26-20.27-47.86v-675.7q0-27.7 20.27-48.03 20.26-20.34 47.86-20.34h361.48l222.59 222.59v521.48q0 27.6-20.34 47.86-20.33 20.27-48.03 20.27h-515.7Zm326.7-557.83v-186h-326.7v675.7h515.7v-489.7h-189Zm-326.7-186v186-186 675.7-675.7Z"/></svg>