@volverjs/ui-vue 0.0.10-beta.11 → 0.0.10-beta.13

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.
Files changed (47) hide show
  1. package/auto-imports.d.ts +1 -0
  2. package/dist/components/VvInputFile/VvInputFile.es.js +1529 -0
  3. package/dist/components/VvInputFile/VvInputFile.umd.js +1 -0
  4. package/dist/components/VvInputFile/VvInputFile.vue.d.ts +141 -0
  5. package/dist/components/VvInputFile/index.d.ts +52 -0
  6. package/dist/components/VvTab/VvTab.es.js +2 -2
  7. package/dist/components/VvTab/VvTab.umd.js +1 -1
  8. package/dist/components/VvTab/VvTab.vue.d.ts +1 -0
  9. package/dist/components/index.d.ts +1 -0
  10. package/dist/components/index.es.js +635 -312
  11. package/dist/components/index.umd.js +1 -1
  12. package/dist/composables/index.d.ts +1 -0
  13. package/dist/composables/index.es.js +77 -1
  14. package/dist/composables/index.umd.js +1 -1
  15. package/dist/composables/useBlurhash.d.ts +7 -0
  16. package/dist/icons.es.js +3 -3
  17. package/dist/icons.umd.js +1 -1
  18. package/dist/stories/AlertGroup/AlertGroupWithComposable.stories.d.ts +1 -1
  19. package/dist/stories/Blurhash/BlurhashComposable.stories.d.ts +4 -0
  20. package/dist/stories/InputFile/InputFile.settings.d.ts +56 -0
  21. package/dist/stories/InputFile/InputFile.stories.d.ts +12 -0
  22. package/dist/stories/InputFile/InputFileModifiers.stories.d.ts +9 -0
  23. package/dist/stories/InputFile/InputFileSlots.stories.d.ts +6 -0
  24. package/dist/types/blurhash.d.ts +12 -0
  25. package/dist/types/index.d.ts +2 -0
  26. package/dist/types/input-file.d.ts +14 -0
  27. package/dist/workers/blurhash.d.ts +1 -0
  28. package/package.json +13 -1
  29. package/src/assets/icons/detailed.json +1 -1
  30. package/src/assets/icons/normal.json +1 -1
  31. package/src/assets/icons/simple.json +1 -1
  32. package/src/components/VvInputFile/VvInputFile.vue +274 -0
  33. package/src/components/VvInputFile/index.ts +36 -0
  34. package/src/components/VvTab/VvTab.vue +2 -2
  35. package/src/components/index.ts +1 -0
  36. package/src/composables/index.ts +1 -0
  37. package/src/composables/useBlurhash.ts +76 -0
  38. package/src/stories/AlertGroup/AlertGroupWithComposable.stories.ts +2 -2
  39. package/src/stories/Blurhash/BlurhashComposable.stories.ts +195 -0
  40. package/src/stories/InputFile/InputFile.settings.ts +36 -0
  41. package/src/stories/InputFile/InputFile.stories.ts +90 -0
  42. package/src/stories/InputFile/InputFileModifiers.stories.ts +51 -0
  43. package/src/stories/InputFile/InputFileSlots.stories.ts +25 -0
  44. package/src/types/blurhash.ts +21 -0
  45. package/src/types/index.ts +2 -0
  46. package/src/types/input-file.ts +16 -0
  47. package/src/workers/blurhash.ts +9 -0
@@ -0,0 +1,274 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: 'VvInputFile',
4
+ }
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ import { useVModel } from '@vueuse/core'
9
+ import type { UploadedFile } from '../../types'
10
+ import { computed, onBeforeUnmount, ref } from 'vue'
11
+ import VvButton from '../VvButton/VvButton.vue'
12
+ import VvIcon from '../VvIcon/VvIcon.vue'
13
+ import HintSlotFactory from '../common/HintSlot'
14
+ import { VvInputFileProps, type VvInputFileEvents } from '.'
15
+
16
+ // props, emit, slots and attrs
17
+ const props = defineProps(VvInputFileProps)
18
+ const emit = defineEmits<VvInputFileEvents>()
19
+ const slots = useSlots()
20
+
21
+ // props merged with volver defaults (now only for labels)
22
+ const propsDefaults = useDefaults<typeof VvInputFileProps>(
23
+ 'VvInputFile',
24
+ VvInputFileProps,
25
+ props,
26
+ )
27
+
28
+ const { modifiers, id } = toRefs(props)
29
+
30
+ const hasId = useUniqueId(id)
31
+ const hasHintId = computed(() => `${hasId.value}-hint`)
32
+
33
+ // styles
34
+ const bemCssClasses = useModifiers(
35
+ 'vv-input-file',
36
+ modifiers,
37
+ computed(() => ({
38
+ dragging: isDragging.value,
39
+ loading: props.loading,
40
+ valid: props.valid === true,
41
+ invalid: props.invalid === true,
42
+ 'icon-before': !!props.iconLeft,
43
+ 'icon-after': !!props.iconRight,
44
+ })),
45
+ )
46
+
47
+ const {
48
+ HintSlot,
49
+ hasHintLabelOrSlot,
50
+ hasInvalidLabelOrSlot,
51
+ hintSlotScope,
52
+ } = HintSlotFactory(propsDefaults, slots)
53
+
54
+ const localModelValue = useVModel(props, 'modelValue', emit)
55
+ const files = computed(() => {
56
+ if (
57
+ !localModelValue.value ||
58
+ (!Array.isArray(localModelValue.value) &&
59
+ !(localModelValue.value as File)?.name)
60
+ ) {
61
+ return []
62
+ }
63
+ return Array.isArray(localModelValue.value)
64
+ ? localModelValue.value
65
+ : [localModelValue.value]
66
+ })
67
+
68
+ const hasMax = computed(() => {
69
+ return typeof props.max === 'string' ? parseInt(props.max) : props.max
70
+ })
71
+
72
+ const hasDropArea = computed(() => {
73
+ return modifiers?.value?.includes('drop-area')
74
+ })
75
+
76
+ const isMultiple = computed(() => {
77
+ if (!props.multiple) {
78
+ return false
79
+ }
80
+ if (!hasMax.value) {
81
+ return true
82
+ }
83
+ return hasMax.value - files.value.length > 1
84
+ })
85
+
86
+ const isDragging = ref(false)
87
+
88
+ const inputEl = ref<HTMLInputElement>()
89
+ const onDragenter = () => {
90
+ isDragging.value = true
91
+ }
92
+
93
+ const onDragleave = () => {
94
+ isDragging.value = false
95
+ }
96
+
97
+ const onDrop = (event: DragEvent) => {
98
+ if (!event.dataTransfer?.files) {
99
+ return
100
+ }
101
+ isDragging.value = false
102
+ addFiles(event.dataTransfer?.files)
103
+ }
104
+
105
+ const onChange = () => {
106
+ if (!inputEl.value?.files) {
107
+ return
108
+ }
109
+ addFiles(inputEl.value.files)
110
+ inputEl.value.value = ''
111
+ }
112
+
113
+ const addFiles = (uploadedFiles: FileList) => {
114
+ if (!props.multiple) {
115
+ if (Array.isArray(localModelValue.value)) {
116
+ localModelValue.value = [...uploadedFiles]
117
+ return
118
+ }
119
+ localModelValue.value = uploadedFiles[0]
120
+ return
121
+ }
122
+ let toReturn: (File | UploadedFile)[] = []
123
+ if (!Array.isArray(localModelValue.value) && localModelValue.value) {
124
+ toReturn = [localModelValue.value]
125
+ } else {
126
+ toReturn =
127
+ localModelValue.value && Array.isArray(localModelValue.value)
128
+ ? [...localModelValue.value]
129
+ : toReturn
130
+ }
131
+ for (const file of uploadedFiles) {
132
+ if (hasMax.value && toReturn.length >= hasMax.value) {
133
+ break
134
+ }
135
+ toReturn.push(file)
136
+ }
137
+
138
+ localModelValue.value = toReturn
139
+ }
140
+
141
+ const onClick = () => {
142
+ if (!inputEl.value) {
143
+ return
144
+ }
145
+ inputEl.value.click()
146
+ }
147
+
148
+ const onClickRemoveFile = (index: number) => {
149
+ if (!Array.isArray(localModelValue.value)) {
150
+ localModelValue.value = undefined
151
+ return
152
+ }
153
+ const toReturn = [...localModelValue.value]
154
+ toReturn.splice(index, 1)
155
+ localModelValue.value = toReturn
156
+ }
157
+
158
+ const previewSrc = computed(() => {
159
+ if (files.value.length === 0) {
160
+ return
161
+ }
162
+ if (files.value[0] instanceof File) {
163
+ return URL.createObjectURL(files.value[0])
164
+ }
165
+ return files.value[0].url
166
+ })
167
+
168
+ onBeforeUnmount(() => {
169
+ if (previewSrc.value) {
170
+ URL.revokeObjectURL(previewSrc.value)
171
+ }
172
+ })
173
+
174
+ const sizeInKiB = (size?: number) => {
175
+ if (!size) {
176
+ return
177
+ }
178
+ return Math.floor(size / 1024)
179
+ }
180
+ </script>
181
+
182
+ <template>
183
+ <div :class="bemCssClasses">
184
+ <label v-if="label" :for="hasId">
185
+ {{ label }}
186
+ </label>
187
+ <div
188
+ v-if="hasDropArea"
189
+ class="vv-input-file__drop-area"
190
+ @dragenter.prevent.stop="onDragenter"
191
+ @dragleave.prevent.stop="onDragleave"
192
+ @drop.prevent.stop="onDrop"
193
+ @dragover.prevent.stop
194
+ @click.stop="onClick"
195
+ >
196
+ <slot name="drop-area">
197
+ <VvButton
198
+ modifiers="action"
199
+ aria-label="upload"
200
+ :label="!previewSrc ? labelButton : undefined"
201
+ :class="{
202
+ 'absolute top-8 right-8': previewSrc,
203
+ }"
204
+ :icon="!previewSrc ? 'image' : 'edit'"
205
+ class="z-1"
206
+ @click.stop="onClick"
207
+ />
208
+ <picture class="vv-input-file__preview">
209
+ <img
210
+ v-if="previewSrc"
211
+ :src="previewSrc"
212
+ :alt="files[0].name"
213
+ />
214
+ </picture>
215
+ </slot>
216
+ </div>
217
+ <div class="vv-input-file__wrapper">
218
+ <VvIcon v-if="iconLeft" :name="iconLeft" />
219
+ <input
220
+ :id="hasId"
221
+ ref="inputEl"
222
+ :placeholder="placeholder"
223
+ :aria-describedby="hasHintLabelOrSlot ? hasHintId : undefined"
224
+ :aria-invalid="invalid"
225
+ :aria-errormessage="
226
+ hasInvalidLabelOrSlot ? hasHintId : undefined
227
+ "
228
+ :multiple="isMultiple"
229
+ :accept="accept"
230
+ type="file"
231
+ :name="name"
232
+ @change="onChange"
233
+ />
234
+ <VvIcon v-if="iconRight" :name="iconRight" />
235
+ </div>
236
+ <ul class="vv-input-file__list">
237
+ <li
238
+ v-for="(file, index) in files"
239
+ :key="index"
240
+ class="vv-input-file__item"
241
+ >
242
+ <VvIcon
243
+ class="vv-input-file__item-icon"
244
+ name="akar-icons:file"
245
+ />
246
+ <div class="vv-input-file__item-name">{{ file.name }}</div>
247
+ <small class="vv-input-file__item-info">
248
+ {{ sizeInKiB(file.size) }} KB
249
+ </small>
250
+ <button
251
+ type="button"
252
+ class="vv-input-file__item-remove"
253
+ title="Remove"
254
+ aria-label="remove-file"
255
+ @click.stop="onClickRemoveFile(index)"
256
+ />
257
+ </li>
258
+ </ul>
259
+ <HintSlot :id="hasHintId" class="vv-input-file__hint">
260
+ <template v-if="$slots.hint" #hint>
261
+ <slot name="hint" v-bind="hintSlotScope" />
262
+ </template>
263
+ <template v-if="$slots.loading" #loading>
264
+ <slot name="loading" v-bind="hintSlotScope" />
265
+ </template>
266
+ <template v-if="$slots.valid" #valid>
267
+ <slot name="valid" v-bind="hintSlotScope" />
268
+ </template>
269
+ <template v-if="$slots.invalid" #invalid>
270
+ <slot name="invalid" v-bind="hintSlotScope" />
271
+ </template>
272
+ </HintSlot>
273
+ </div>
274
+ </template>
@@ -0,0 +1,36 @@
1
+ import type { UploadedFile } from '@/types'
2
+ import {
3
+ ModifiersProps,
4
+ ValidProps,
5
+ InvalidProps,
6
+ HintProps,
7
+ LabelProps,
8
+ LoadingProps,
9
+ } from '../../props'
10
+
11
+ export type VvInputFileEvents = {
12
+ 'update:modelValue': [File | undefined]
13
+ }
14
+
15
+ export const VvInputFileProps = {
16
+ ...ModifiersProps,
17
+ ...ValidProps,
18
+ ...InvalidProps,
19
+ ...HintProps,
20
+ ...LabelProps,
21
+ ...LoadingProps,
22
+ name: { type: String },
23
+ id: { type: String },
24
+ modelValue: {
25
+ type: Object as PropType<File | (File | UploadedFile)[] | UploadedFile>,
26
+ required: true,
27
+ },
28
+ max: [Number, String],
29
+ labelButton: { type: String, default: 'Image' },
30
+ loading: Boolean,
31
+ accept: String,
32
+ placeholder: String,
33
+ multiple: Boolean,
34
+ iconLeft: String,
35
+ iconRight: String,
36
+ }
@@ -60,8 +60,8 @@
60
60
  }"
61
61
  @click="onNavClick"
62
62
  >
63
- <template #item="{ item, index }">
64
- <slot name="nav-item" v-bind="{ item, index }" />
63
+ <template #item="{ item, data, index }">
64
+ <slot name="nav-item" v-bind="{ item, data, index }" />
65
65
  </template>
66
66
  </VvNav>
67
67
  <!-- #endregion -->
@@ -31,3 +31,4 @@ export { default as VvSelect } from './VvSelect/VvSelect.vue'
31
31
  export { default as VvTab } from './VvTab/VvTab.vue'
32
32
  export { default as VvTextarea } from './VvTextarea/VvTextarea.vue'
33
33
  export { default as VvTooltip } from './VvTooltip/VvTooltip.vue'
34
+ export { default as VvInputFile } from './VvInputFile/VvInputFile.vue'
@@ -1 +1,2 @@
1
1
  export { useAlert } from './alert/useAlert'
2
+ export { useBlurhash } from './useBlurhash'
@@ -0,0 +1,76 @@
1
+ import { wrap } from 'comlink'
2
+ import pica from 'pica'
3
+ import type { BlurhashWorkerType } from '@/types/blurhash'
4
+ import BlurhashWorker from '@/workers/blurhash?worker&inline'
5
+
6
+ const remoteFunction = wrap<BlurhashWorkerType>(new BlurhashWorker())
7
+
8
+ function loadImage(src: string): Promise<CanvasImageSource> {
9
+ return new Promise((resolve, reject) => {
10
+ const img = new Image()
11
+ img.onload = () => resolve(img)
12
+ img.onerror = (...args) => reject(args)
13
+ img.src = src
14
+ })
15
+ }
16
+
17
+ const getWidthHeightFromMaxSize = (
18
+ width: number,
19
+ height: number,
20
+ maxSize: number,
21
+ ) => {
22
+ if (width > height) {
23
+ return {
24
+ width: maxSize,
25
+ height: Math.round(maxSize * (height / width)),
26
+ }
27
+ }
28
+ return {
29
+ width: Math.round(maxSize * (width / height)),
30
+ height: maxSize,
31
+ }
32
+ }
33
+
34
+ const resizeImage = async (
35
+ image: ImageBitmap | HTMLImageElement | HTMLCanvasElement | File | Blob,
36
+ width: number,
37
+ height: number,
38
+ ) => {
39
+ const resizer = new pica()
40
+ const canvas = document.createElement('canvas')
41
+ canvas.width = width
42
+ canvas.height = height
43
+ const result = await resizer.resize(image, canvas)
44
+ return result.getContext('2d')?.getImageData(0, 0, width, height).data
45
+ }
46
+
47
+ export const useBlurhash = () => {
48
+ async function encode(file: File) {
49
+ const imageUrl = URL.createObjectURL(file)
50
+ const image = await loadImage(imageUrl)
51
+ if ('width' in image && 'height' in image) {
52
+ const { width: newWidth, height: newHeight } =
53
+ getWidthHeightFromMaxSize(
54
+ image.width as number,
55
+ image.height as number,
56
+ 32,
57
+ )
58
+ const imageData = await resizeImage(
59
+ image as ImageBitmap,
60
+ newWidth,
61
+ newHeight,
62
+ )
63
+ if (imageData) {
64
+ return remoteFunction.encode(
65
+ imageData,
66
+ newWidth,
67
+ newHeight,
68
+ 4,
69
+ 4,
70
+ )
71
+ }
72
+ }
73
+ }
74
+
75
+ return { encode, decode: remoteFunction.decode, loadImage }
76
+ }
@@ -6,7 +6,7 @@ import { Default as DefaultStory, type Story } from './AlertGroup.stories'
6
6
  import { useAlert } from '@/composables/alert/useAlert'
7
7
 
8
8
  const meta: Meta<typeof VvAlertGroup> = {
9
- title: 'Components/AlertGroup/UseComposable',
9
+ title: 'Composables/useAlert',
10
10
  component: VvAlertGroup,
11
11
  args: defaultArgs,
12
12
  argTypes,
@@ -15,7 +15,7 @@ const meta: Meta<typeof VvAlertGroup> = {
15
15
 
16
16
  export default meta
17
17
 
18
- export const UseComposable: Story = {
18
+ export const Default: Story = {
19
19
  ...DefaultStory,
20
20
  parameters: {
21
21
  docs: {
@@ -0,0 +1,195 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { ref } from 'vue'
3
+ import VvInputFile from '@/components/VvInputFile/VvInputFile.vue'
4
+ import { useBlurhash } from '@/composables/useBlurhash'
5
+
6
+ const meta: Meta = {
7
+ title: 'Composables/useBlurhash',
8
+ tags: ['autodocs'],
9
+ }
10
+
11
+ export default meta
12
+
13
+ export const Default: StoryObj = {
14
+ render: (args) => ({
15
+ components: { VvInputFile },
16
+ setup() {
17
+ const isLoading = ref(false)
18
+ const { encode, decode, loadImage } = useBlurhash()
19
+ const file = ref({})
20
+ const canvas = ref()
21
+ const isImgLoaded = ref(false)
22
+ const blurhash = ref('')
23
+ const imageUrl = ref('')
24
+ const image = ref()
25
+
26
+ return {
27
+ args,
28
+ isLoading,
29
+ canvas,
30
+ encode,
31
+ decode,
32
+ file,
33
+ blurhash,
34
+ isImgLoaded,
35
+ loadImage,
36
+ image,
37
+ imageUrl,
38
+ }
39
+ },
40
+ watch: {
41
+ file: {
42
+ immediate: true,
43
+ async handler(newValue) {
44
+ if (newValue?.size) {
45
+ this.imageUrl = URL.createObjectURL(newValue)
46
+ this.image = await this.loadImage(this.imageUrl)
47
+ this.blurhash = await this.encode(newValue)
48
+ } else {
49
+ this.image = null
50
+ this.imageUrl = ''
51
+ this.blurhash = ''
52
+ }
53
+ },
54
+ },
55
+ blurhash: {
56
+ async handler(newValue) {
57
+ if (this.image) {
58
+ const blurhashDecoded = await this.decode(
59
+ newValue,
60
+ this.image.width,
61
+ this.image.height,
62
+ )
63
+
64
+ if (this.canvas) {
65
+ this.canvas.width = this.image.width
66
+ this.canvas.height = this.image.height
67
+ const ctx = this.canvas.getContext('2d')
68
+ const imageData = ctx.createImageData(
69
+ this.canvas.width,
70
+ this.canvas.height,
71
+ )
72
+ imageData.data.set(blurhashDecoded)
73
+ ctx.putImageData(imageData, 0, 0)
74
+ }
75
+ }
76
+ },
77
+ },
78
+ },
79
+ template: /* html */ `
80
+ <div class="w-full grid gap-md grid-cols-3 h-150" :class="{ 'vv-skeleton': isLoading }">
81
+ <div class="w-150 h-150 col-span-1">
82
+ <div class="text-20 font-semibold mb-md">Upload image</div>
83
+ <vv-input-file v-model="file" name="input-file" modifiers="drop-area square hidden" accept=".gif,.jpg,.jpeg,.png,image/gif,image/jpeg,image/png" />
84
+ </div>
85
+ <div v-show="blurhash" class="h-150 col-span-2">
86
+ <picture class="flex gap-md justify-center">
87
+ <div>
88
+ <div class="text-20 font-semibold mb-md">Blurhash</div>
89
+ <canvas
90
+ ref="canvas"
91
+ class="w-150 h-150 block object-cover" />
92
+ </div>
93
+ <div>
94
+ <div class="text-20 font-semibold mb-md">Image</div>
95
+ <img
96
+ v-if="image"
97
+ class="w-150 h-150 block object-cover"
98
+ :class="{ 'vv-skeleton__item': isLoading }"
99
+ :src="imageUrl"
100
+ alt="image"
101
+ :width="image.width"
102
+ :height="image.height" />
103
+ </div>
104
+ </picture>
105
+ </div>
106
+ </div>
107
+ `,
108
+ }),
109
+
110
+ parameters: {
111
+ docs: {
112
+ source: {
113
+ type: 'code',
114
+ language: 'html',
115
+ code: /* html */ `
116
+ <div class="w-full grid gap-md grid-cols-3 h-150" :class="{ 'vv-skeleton': isLoading }">
117
+ <div class="w-150 h-150 col-span-1">
118
+ <div class="text-20 font-semibold mb-md">Upload image</div>
119
+ <vv-input-file v-model="file" name="input-file" modifiers="drop-area square hidden" accept=".gif,.jpg,.jpeg,.png,image/gif,image/jpeg,image/png" />
120
+ </div>
121
+ <div v-show="blurhash" class="h-150 col-span-2">
122
+ <picture class="flex gap-md justify-center">
123
+ <div>
124
+ <div class="text-20 font-semibold mb-md">Blurhash</div>
125
+ <canvas
126
+ ref="canvas"
127
+ class="w-150 h-150 block object-cover" />
128
+ </div>
129
+ <div>
130
+ <div class="text-20 font-semibold mb-md">Image</div>
131
+ <img
132
+ v-if="image"
133
+ class="w-150 h-150 block object-cover"
134
+ :class="{ 'vv-skeleton__item': isLoading }"
135
+ :src="imageUrl"
136
+ alt="image"
137
+ :width="image.width"
138
+ :height="image.height" />
139
+ </div>
140
+ </picture>
141
+ </div>
142
+ </div>
143
+
144
+ <script setup lang='ts'>
145
+ import { useBlurhash } from '@volverjs/ui-vue/composables'
146
+
147
+ const { encode, decode, loadImage } = useBlurhash()
148
+
149
+ const isLoading = ref(false)
150
+ const file = ref({})
151
+ const canvas = ref()
152
+ const isImgLoaded = ref(false)
153
+ const blurhash = ref('')
154
+ const imageUrl = ref('')
155
+ const image = ref()
156
+
157
+ watch(file, async (newValue) => {
158
+ if (newValue?.size) {
159
+ this.imageUrl = URL.createObjectURL(newValue)
160
+ this.image = await this.loadImage(this.imageUrl)
161
+ this.blurhash = await this.encode(newValue)
162
+ } else {
163
+ this.image = null
164
+ this.imageUrl = ''
165
+ this.blurhash = ''
166
+ }
167
+ }, { immediate: true })
168
+
169
+ watch(blurhash, async (newValue) => {
170
+ if (this.image) {
171
+ const blurhashDecoded = await this.decode(
172
+ newValue,
173
+ this.image.width,
174
+ this.image.height,
175
+ )
176
+
177
+ if (this.canvas) {
178
+ this.canvas.width = this.image.width
179
+ this.canvas.height = this.image.height
180
+ const ctx = this.canvas.getContext('2d')
181
+ const imageData = ctx.createImageData(
182
+ this.canvas.width,
183
+ this.canvas.height,
184
+ )
185
+ imageData.data.set(blurhashDecoded)
186
+ ctx.putImageData(imageData, 0, 0)
187
+ }
188
+ }
189
+ })
190
+ </script>
191
+ `,
192
+ },
193
+ },
194
+ },
195
+ }
@@ -0,0 +1,36 @@
1
+ import { HintArgTypes, ModifiersArgTypes } from '@/stories/argTypes'
2
+ import { VvInputFileProps } from '@/components/VvInputFile'
3
+
4
+ export const defaultArgs = {
5
+ ...propsToObject(VvInputFileProps),
6
+ name: 'vv-input-file',
7
+ label: 'Upload file',
8
+ }
9
+
10
+ export const argTypes = {
11
+ ...HintArgTypes,
12
+ modifiers: {
13
+ ...ModifiersArgTypes.modifiers,
14
+ options: ['drop-area', 'hidden', 'square', 'circle'],
15
+ },
16
+ 'drop-area': {
17
+ description: 'Drop area slot',
18
+ control: {
19
+ type: 'text',
20
+ },
21
+ table: {
22
+ category: 'Slots',
23
+ type: {
24
+ summary: 'html',
25
+ },
26
+ },
27
+ },
28
+ 'update:model-value': {
29
+ table: {
30
+ category: 'Events',
31
+ type: {
32
+ summary: 'File | File[]',
33
+ },
34
+ },
35
+ },
36
+ }