@bagelink/vue 1.2.128 → 1.2.132

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 (37) hide show
  1. package/dist/components/AddressSearch.vue.d.ts.map +1 -1
  2. package/dist/components/Carousel2.vue.d.ts +89 -0
  3. package/dist/components/Carousel2.vue.d.ts.map +1 -0
  4. package/dist/components/DragOver.vue.d.ts.map +1 -1
  5. package/dist/components/Dropdown.vue.d.ts.map +1 -1
  6. package/dist/components/ImportData.vue.d.ts.map +1 -1
  7. package/dist/components/Pill.vue.d.ts +2 -0
  8. package/dist/components/Pill.vue.d.ts.map +1 -1
  9. package/dist/components/calendar/views/CalendarPopover.vue.d.ts +2 -2
  10. package/dist/components/calendar/views/CalendarPopover.vue.d.ts.map +1 -1
  11. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  12. package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts +5 -1
  13. package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
  14. package/dist/components/form/inputs/Upload/upload.types.d.ts +2 -0
  15. package/dist/components/form/inputs/Upload/upload.types.d.ts.map +1 -1
  16. package/dist/components/form/inputs/Upload/useFileUpload.d.ts +8 -1
  17. package/dist/components/form/inputs/Upload/useFileUpload.d.ts.map +1 -1
  18. package/dist/components/form/inputs/index.d.ts +0 -1
  19. package/dist/components/form/inputs/index.d.ts.map +1 -1
  20. package/dist/index.cjs +445 -915
  21. package/dist/index.mjs +445 -915
  22. package/dist/style.css +1665 -1849
  23. package/package.json +1 -1
  24. package/src/components/DragOver.vue +0 -2
  25. package/src/components/ImportData.vue +1 -1
  26. package/src/components/Loading.vue +1 -1
  27. package/src/components/Pill.vue +28 -5
  28. package/src/components/form/inputs/Upload/UploadInput.vue +35 -47
  29. package/src/components/form/inputs/Upload/upload.css +12 -9
  30. package/src/components/form/inputs/Upload/upload.types.ts +2 -0
  31. package/src/components/form/inputs/Upload/useFileUpload.ts +35 -9
  32. package/src/components/form/inputs/index.ts +0 -1
  33. package/src/styles/layout.css +1466 -1460
  34. package/src/styles/theme.css +2 -1
  35. package/dist/types/timeago.d.ts +0 -23
  36. package/dist/types/timeago.d.ts.map +0 -1
  37. package/src/components/form/inputs/FileUpload.vue +0 -724
@@ -1,724 +0,0 @@
1
- <script setup lang="ts">
2
- import type { StorageFile } from '@bagelink/vue'
3
- import {
4
- Btn,
5
- IMAGE_FORMATS_REGEXP,
6
- Icon,
7
-
8
- useBagel,
9
- Card,
10
- Image
11
- } from '@bagelink/vue'
12
- import { onMounted, watch } from 'vue'
13
-
14
- type StrKey = keyof StorageFile
15
-
16
- const {
17
- label,
18
- multiple,
19
- files,
20
- bindkey = 'id',
21
- modelValue,
22
- width,
23
- height = '215px',
24
- topic,
25
- fill,
26
- oval,
27
- theme = 'dropzone',
28
- accept = '*',
29
- required,
30
- disabled,
31
- } = defineProps<{
32
- label?: string
33
- multiple?: boolean
34
- files?: StorageFile | StorageFile[]
35
- bindkey?: StrKey
36
- modelValue?: string[] | string
37
- width?: string
38
- height?: string | 'auto'
39
- topic?: string
40
- fill?: boolean
41
- oval?: boolean
42
- theme?: 'dropzone' | 'basic'
43
- accept?: string
44
- required?: boolean
45
- disabled?: boolean
46
- dropPlaceholder?: string
47
- noFilePlaceholder?: string
48
- btnPlaceholder?: string
49
- }>()
50
-
51
- const emit = defineEmits(['update:modelValue', 'addFileStart'])
52
-
53
- const bindKey: StrKey = bindkey
54
-
55
- const bagel = useBagel()
56
-
57
- interface QueueFile {
58
- file: File
59
- progress: number
60
- uploading?: boolean
61
- name?: string
62
- uploaded?: boolean
63
- }
64
-
65
- const isImage = (str: string) => IMAGE_FORMATS_REGEXP.test(str)
66
-
67
- const file_bindkeys = defineModel<string[] | string>('modelValue', {
68
- default: () => [],
69
- })
70
- const storageFiles = $ref<StorageFile[]>([])
71
-
72
- async function fetchTopicFiles() {
73
- const files = await bagel.get<StorageFile[]>(`/files?topic=${topic}`)
74
- storageFiles.push(...files)
75
- }
76
-
77
- function loadFiles() {
78
- const ids = [file_bindkeys.value].flat().filter(Boolean) as unknown as string[]
79
- if (!ids.length) {
80
- storageFiles.splice(0, storageFiles.length)
81
- return
82
- }
83
-
84
- const currentIds = storageFiles.map(file => file[bindKey])
85
- const idsToAdd = ids.filter(id => !currentIds.includes(id))
86
- const idsToRemove = currentIds.filter((id): id is string => (typeof id === 'string') && !ids.includes(id))
87
-
88
- idsToRemove.forEach((id) => {
89
- const index = storageFiles.findIndex(file => file[bindKey] === id)
90
- if (index !== -1) storageFiles.splice(index, 1)
91
- })
92
-
93
- if (bindKey === 'url') {
94
- idsToAdd.forEach((url) => {
95
- storageFiles.push({ url } as StorageFile)
96
- })
97
- return
98
- }
99
-
100
- if (multiple) {
101
- idsToAdd.forEach((id) => {
102
- void bagel
103
- .get<StorageFile>(`/files/${id}`)
104
- .then((file) => {
105
- storageFiles.push(file)
106
- })
107
- .catch(console.error)
108
- })
109
- } else {
110
- void bagel
111
- .get<StorageFile>(`/files/${idsToAdd[0]}`)
112
- .then((file) => {
113
- storageFiles.push(file)
114
- })
115
- .catch(console.error)
116
- }
117
- }
118
-
119
- onMounted(() => {
120
- if (topic) fetchTopicFiles()
121
- if (!files && [file_bindkeys.value].flat().length > 0) {
122
- loadFiles()
123
- }
124
- })
125
-
126
- watch(() => topic, fetchTopicFiles)
127
- watch(() => modelValue, loadFiles)
128
-
129
- watch(
130
- () => files,
131
- (newFiles) => {
132
- if (newFiles) {
133
- const filesToAdd = [newFiles]
134
- .flat()
135
- .filter(f => !storageFiles.some(sf => sf[bindKey] === f[bindKey]))
136
- for (const file of filesToAdd) {
137
- storageFiles.push(file)
138
- }
139
- }
140
- },
141
- { immediate: true }
142
- )
143
-
144
- function compareLists(a: any, b: any) {
145
- return (
146
- [a].flat().every((id: any) => [b].flat().includes(id))
147
- && [b].flat().every(id => [a].flat().includes(id))
148
- )
149
- }
150
-
151
- function updateModelValue() {
152
- let idValue: string | string[]
153
- if (multiple) {
154
- idValue = storageFiles.map(f => f[bindKey]) as string[]
155
- } else {
156
- idValue = (storageFiles[0][bindKey] as string) || ''
157
- }
158
- const isSame = compareLists(file_bindkeys.value, idValue)
159
- if (!isSame) {
160
- file_bindkeys.value = idValue
161
- }
162
- }
163
-
164
- const fileQueue = $ref<QueueFile[]>([])
165
- let isDragOver = $ref(false)
166
-
167
- function preventDefault(e: Event) {
168
- e.preventDefault()
169
- e.stopPropagation()
170
- }
171
-
172
- const fileToUrl = (file: File) => URL.createObjectURL(file)
173
- function removeFile(file: StorageFile) {
174
- const index = storageFiles.indexOf(file)
175
- storageFiles.splice(index, 1)
176
- void bagel.delete(`/static_files/${file.id}`)
177
- updateModelValue()
178
- }
179
-
180
- async function flushQueue() {
181
- emit('addFileStart')
182
- for (const file of fileQueue) {
183
- file.uploading = true
184
- if (!multiple) storageFiles.splice(0, 1)
185
- const serverFile = await bagel.uploadFile<StorageFile>(file.file, {
186
- onUploadProgress: (e: any) => (file.progress = e.progress * 100 - 1),
187
- topic,
188
- })
189
- storageFiles.push(serverFile)
190
- }
191
- fileQueue.splice(0, fileQueue.length)
192
- updateModelValue()
193
- }
194
-
195
- function browse() {
196
- if (disabled) return
197
- const input = document.createElement('input')
198
- input.type = 'file'
199
- input.multiple = multiple
200
- input.accept = accept
201
- input.onchange = (e: Event) => {
202
- const target = e.target as HTMLInputElement
203
- if (target.files) {
204
- const files = Array.from(target.files)
205
- for (const file of files) {
206
- fileQueue.push({ name: file.name, file, progress: 0 })
207
- }
208
- }
209
- flushQueue()
210
- }
211
- console.log(fileQueue)
212
- input.click()
213
- }
214
-
215
- function dragleave(e: DragEvent) {
216
- preventDefault(e)
217
- if (e.dataTransfer) isDragOver = false
218
- else isDragOver = false
219
- }
220
-
221
- function dragover(e: DragEvent) {
222
- if (disabled) return
223
- preventDefault(e)
224
- if (e.dataTransfer) isDragOver = true
225
- else isDragOver = false
226
- }
227
-
228
- function drop(e: DragEvent) {
229
- if (disabled) return
230
- preventDefault(e)
231
- if (e.dataTransfer) {
232
- Array.from(e.dataTransfer.files).forEach((file: File) => fileQueue.push({ name: file.name, file, progress: 0 })
233
- )
234
- }
235
- isDragOver = false
236
- flushQueue()
237
- }
238
- </script>
239
-
240
- <template>
241
- <div class="bagel-input">
242
- <label>
243
- {{ label }}
244
- </label>
245
- <input
246
- v-if="required && !storageFiles.length"
247
- placeholder="required"
248
- type="text"
249
- required
250
- class="pixel"
251
- >
252
- <Card
253
- v-if="theme === 'basic'"
254
- outline
255
- class="flex p-05 gap-1"
256
- @dragover="dragover"
257
- @drop="drop"
258
- >
259
- <Btn
260
- v-if="!storageFiles.length && !fileQueue.length"
261
- class="px-1-5"
262
- icon="upload"
263
- outline
264
- :value="btnPlaceholder || 'Upload'"
265
- @click="browse"
266
- />
267
-
268
- <div
269
- v-for="file in storageFiles"
270
- :key="file.id"
271
- class="txt-gray txt-12 flex"
272
- >
273
- <div class="m-05 flex opacity-7 z-99">
274
- <Btn
275
- v-tooltip="'Delete'"
276
- color="gray"
277
- thin
278
- icon="delete"
279
- @click.stop="removeFile(file)"
280
- />
281
- <Btn
282
- v-tooltip="'Replace'"
283
- color="gray"
284
- thin
285
- icon="autorenew"
286
- @click="browse"
287
- />
288
- <Btn
289
- v-tooltip="'Download'"
290
- :href="file.url"
291
- :download="file.name"
292
- color="gray"
293
- thin
294
- icon="download"
295
- target="_blank"
296
- @click.stop
297
- />
298
- <p
299
- v-lightbox="{ src: file.url, download: true }"
300
- class="ellipsis-1 word-break-all h-20 m-0 color-black"
301
- >
302
- {{ file.name }}
303
- </p>
304
-
305
- <div class="flex gap-025 rounded pe-1 ps-05 py-025 bg-gray-80 -my-1 ">
306
- <Icon icon="draft" :size="1.5" />
307
- <p
308
- v-lightbox="{ src: file.url, download: true }"
309
- class="ellipsis-1 word-break-all h-20 m-0 color-black txt16"
310
- >
311
- {{ file.name }}
312
- </p>
313
- </div>
314
-
315
- <Btn
316
- thin
317
- flat
318
- icon="delete"
319
- color="red"
320
- @click.stop="removeFile(file)"
321
- />
322
- </div>
323
- </div>
324
- <span
325
- v-if="!storageFiles.length && !fileQueue.length"
326
- class="txt-gray txt-12"
327
- >
328
- {{ noFilePlaceholder || 'No file selected' }}
329
- </span>
330
- </Card>
331
- <div
332
- v-else
333
- class="fileUploadWrap"
334
- :class="{
335
- 'fileDropZone': !storageFiles.length && !fileQueue.length,
336
- 'dragover': isDragOver,
337
- 'bgl_oval-upload': oval,
338
- }"
339
- :style="{ width, height }"
340
- @click="browse"
341
- @dragover="dragover"
342
- @drop="drop"
343
- @dragleave="dragleave"
344
- >
345
- <slot name="files" :files="storageFiles" :fileQueue>
346
- <div v-if="multiple" class="bgl-multi-preview">
347
- <div
348
- v-for="file in storageFiles"
349
- :key="file.id"
350
- v-lightbox="{ src: file.url, download: true }"
351
- class="multi-image-item-preview"
352
- >
353
- <Image
354
- v-if="isImage(file.extension || file.url)"
355
- class="multi-preview"
356
- :src="file.url"
357
- alt=""
358
- />
359
- <Icon v-else icon="draft" class="multi-preview" />
360
- <p class="m-0">
361
- {{ file.name }}
362
- </p>
363
- <Btn
364
- thin
365
- flat
366
- icon="delete"
367
- color="red"
368
- @click.stop="removeFile(file)"
369
- />
370
- </div>
371
- <div
372
- v-for="fileQ in fileQueue"
373
- :key="fileQ.file.name"
374
- class="multi-image-item-preview"
375
- >
376
- <template v-if="isImage(fileQ.file.type)">
377
- <Image
378
- v-if="multiple"
379
- :width="width || '220'"
380
- class="multi-preview"
381
- :src="fileToUrl(fileQ.file)"
382
- alt=""
383
- />
384
- </template>
385
-
386
- <Icon v-else icon="draft" class="multi-preview" />
387
-
388
- <p class="no-margin multi-preview-txt">
389
- {{ fileQ.name }}
390
- </p>
391
- <div
392
- class="pie"
393
- :style="`--p:${fileQ.progress}`"
394
- style="--b: 2px"
395
- :class="{ complete: fileQ.progress === 100 }"
396
- >
397
- <span v-if="fileQ.progress < 100" class="progress">
398
- {{ `${fileQ.progress.toFixed(0)}` }}
399
- </span>
400
- <Icon class="success" icon="check" />
401
- </div>
402
- </div>
403
- </div>
404
-
405
- <div
406
- v-if="!multiple && (storageFiles.length > 0 || fileQueue.length > 0)"
407
- class="bgl-single-preview"
408
- >
409
- <div
410
- v-for="file in storageFiles"
411
- :key="file.id"
412
- class="single-image-item-preview"
413
- :class="{ 'bgl_fill-image': fill }"
414
- >
415
- <div class="position-start m-05 flex opacity-7 z-99 gap-025">
416
- <Btn
417
- v-tooltip="'Delete'"
418
- color="gray"
419
- thin
420
- icon="delete"
421
- @click.stop="removeFile(file)"
422
- />
423
- <Btn
424
- v-tooltip="'Replace'"
425
- color="gray"
426
- thin
427
- icon="autorenew"
428
- @click="browse"
429
- />
430
- <Btn
431
- v-tooltip="'Download'"
432
- :href="file.url"
433
- :download="file.name"
434
- color="gray"
435
- thin
436
- icon="download"
437
- target="_blank"
438
- @click.stop
439
- />
440
- </div>
441
- <div v-if="isImage(file.extension || file.url)" class="h-100">
442
- <Image
443
- v-lightbox="{ src: file.url, download: true }"
444
- class="single-preview"
445
- :src="file.url"
446
- alt=""
447
- />
448
- </div>
449
- <Icon
450
- v-else
451
- v-lightbox="{ src: file.url, download: true }"
452
- :size="4"
453
- weight="2"
454
- icon="draft"
455
- class="color-primary w-100"
456
- />
457
- </div>
458
-
459
- <div
460
- v-for="fileQ in fileQueue"
461
- :key="fileQ.file.name"
462
- class="single-image-item-preview"
463
- :class="{ 'bgl_fill-image': fill }"
464
- >
465
- <div
466
- class="pie"
467
- :style="`--p:${fileQ.progress}`"
468
- style="--b: 2px"
469
- :class="{ complete: fileQ.progress === 100 }"
470
- >
471
- <span v-if="fileQ.progress < 100" class="progress">
472
- {{ `${fileQ.progress.toFixed(0)}` }}
473
- </span>
474
- <Icon class="success" icon="check" />
475
- </div>
476
- <Image
477
- v-if="isImage(fileQ.file.type)"
478
- v-lightbox="{ src: fileToUrl(fileQ.file), download: true }"
479
- class="single-preview"
480
- :src="fileToUrl(fileQ.file)"
481
- alt=""
482
- />
483
- <!-- <Icon v-else :size="8" weight="2" icon="draft" class="color-primary" /> -->
484
- </div>
485
- </div>
486
- </slot>
487
- <slot
488
- v-if="(!storageFiles.length && !fileQueue.length) || multiple"
489
- name="placeholder"
490
- :files="storageFiles"
491
- :fileQueue
492
- :browse
493
- >
494
- <p
495
- class="p-1 flex column hover fileUploadPlaceHolder justify-content-center mb-05"
496
- >
497
- <Icon icon="upload_2" />
498
- <span class=" pretty balance">
499
- {{ dropPlaceholder || 'Drag and Drop files here or click to upload' }}
500
- </span>
501
- </p>
502
- </slot>
503
- </div>
504
- </div>
505
- </template>
506
-
507
- <style scoped>
508
- .fileUploadWrap {
509
- outline: 1px solid var(--border-color);
510
- border-radius: var(--input-border-radius);
511
- text-align: center;
512
- cursor: pointer;
513
- transition: var(--bgl-transition);
514
- position: relative;
515
- font-size: var(--input-font-size);
516
- overflow-y: auto;
517
- background: var(--input-bg);
518
- height: 215px;
519
- }
520
- .bagel-input .fileUploadWrap.fileDropZone {
521
- background: var(--input-bg);
522
- display: flex;
523
- align-items: center;
524
- justify-content: center;
525
- color: var(--bgl-gray);
526
- flex-direction: column;
527
- }
528
-
529
- .fileUploadWrap.dragover,
530
- .fileUploadWrap:hover {
531
- box-shadow: inset 0 0 10px #00000012;
532
- }
533
-
534
- .fileUploadWrap[style*='height: auto;'] {
535
- min-height: 215px;
536
- }
537
-
538
- .multi-image-item-preview {
539
- border: 1px solid var(--border-color) !important;
540
- border-radius: var(--input-border-radius);
541
- margin: 0.5rem !important;
542
- background: var(--bgl-popup-bg);
543
- padding: 0;
544
- padding-inline-end: 0.75rem;
545
- text-align: start;
546
- color: var(--input-color);
547
- display: grid;
548
- grid-template-columns: auto 1fr 22px;
549
- gap: 1rem;
550
- align-items: center;
551
- }
552
- .multi-image-item-preview p {
553
- overflow: hidden;
554
- text-overflow: ellipsis;
555
- white-space: nowrap;
556
- }
557
- .multi-preview {
558
- width: 40px;
559
- height: 40px;
560
- border-radius: var(--input-border-radius);
561
- object-fit: cover;
562
- background: var(--bgl-gray-light);
563
- text-align: center !important;
564
- justify-content: center;
565
- align-items: center;
566
- display: flex;
567
- }
568
- .bgl-single-preview {
569
- height: 100%;
570
- position: relative;
571
- }
572
- .bgl-single-preview + .fileUploadPlaceHolder {
573
- position: absolute;
574
- inset: 0;
575
- margin: auto;
576
- top: calc(50% - 2rem);
577
- }
578
- .single-image-item-preview {
579
- height: 100%;
580
- min-height: 100%;
581
- position: relative;
582
- display: flex;
583
- align-items: center;
584
- justify-content: center;
585
- }
586
- .fileUploadWrap[style*='height: auto'] .single-image-item-preview {
587
- min-height: 215px;
588
- }
589
-
590
- .fileUploadWrap[style*='height: auto'] .single-preview {
591
- margin: 0rem !important;
592
- }
593
- .single-preview {
594
- border-radius: var(--input-border-radius);
595
- margin: 1rem;
596
- padding: 0px;
597
- min-height: calc(100% - 2rem);
598
- height: calc(100% - 2rem);
599
- object-fit: cover;
600
- background: var(--bgl-gray-light);
601
- width: calc(90% - 2rem);
602
- min-width: 5rem;
603
- }
604
- .single-image-item-preview:hover::after {
605
- content: 'zoom_in';
606
- font-size: 32px;
607
- font-family: 'Material Symbols Outlined', serif;
608
- position: absolute;
609
- border-radius: 100%;
610
- color: var(--bgl-white);
611
- z-index: 9;
612
- pointer-events: none;
613
- }
614
- .single-image-item-preview:hover img {
615
- filter: brightness(70%);
616
- }
617
-
618
- .bgl_fill-image.single-image-item-preview {
619
- height: 100%;
620
- }
621
- .bgl_fill-image.single-image-item-preview .single-preview {
622
- border-radius: unset;
623
- object-fit: cover;
624
- box-shadow: unset;
625
- width: 100%;
626
- height: auto;
627
- }
628
-
629
- .single-image-item-preview .pie {
630
- transform-origin: top;
631
- transform: scale(1.4);
632
- position: absolute;
633
- text-align: center;
634
- inset: 0;
635
- margin: auto;
636
- display: flex;
637
- justify-content: center;
638
- align-items: center;
639
- background: transparent;
640
- border: none !important;
641
- padding: 0 !important;
642
- }
643
-
644
- .bgl_oval-upload {
645
- border-radius: 100% !important;
646
- overflow: hidden;
647
- }
648
- .bgl_oval-upload p {
649
- padding: 0.75rem !important;
650
- font-size: 12px;
651
- }
652
- .bgl_oval-upload .fileUploadPlaceHolder {
653
- top: 0;
654
- }
655
- .bgl_oval-upload .pie {
656
- transform: scale(1);
657
- }
658
- .bgl_oval-upload span.bgl_icon-font.color-primary {
659
- transform: scale(0.4) !important;
660
- }
661
- .bgl_oval-upload .single-image-item-preview {
662
- height: 100%;
663
- }
664
- .bgl_oval-upload .single-preview {
665
- margin: 0;
666
- height: 100% !important;
667
- }
668
-
669
- .pie {
670
- width: 30px;
671
- height: 30px;
672
- position: relative;
673
- display: flex;
674
- align-items: center;
675
- justify-content: center;
676
- }
677
-
678
- .pie:before {
679
- content: '';
680
- position: absolute;
681
- border-radius: 50%;
682
- inset: 0;
683
- transition: all 0.2s ease-in-out;
684
- background: conic-gradient(var(--bgl-primary) calc(var(--p) * 1%), #0000 0);
685
- -webkit-mask: radial-gradient(
686
- farthest-side,
687
- #0000 calc(99% - var(--b)),
688
- #000 calc(100% - var(--b))
689
- );
690
- mask: radial-gradient(
691
- farthest-side,
692
- #0000 calc(99% - var(--b)),
693
- #000 calc(100% - var(--b))
694
- );
695
- }
696
-
697
- .pie .success {
698
- transform: scale(0);
699
- opacity: 0;
700
- transition: all 0.3s ease-in-out;
701
- }
702
-
703
- .pie .progress {
704
- position: absolute;
705
- font-size: 10px;
706
- }
707
-
708
- .pie.complete .progress {
709
- display: none;
710
- }
711
-
712
- .pie.complete .success {
713
- transform: scale(1.3);
714
- opacity: 1;
715
- }
716
-
717
- .pie.complete:before {
718
- background: conic-gradient(var(--bgl-green) calc(var(--p) * 1%), #0000 0);
719
- }
720
-
721
- .pie.complete {
722
- color: var(--bgl-green);
723
- }
724
- </style>