@bagelink/vue 0.0.168 → 0.0.172

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.
@@ -1,86 +1,99 @@
1
1
  <template>
2
- <div class="files-wrapper flex">
3
- <div
4
- ref="dropZoneEl"
5
- class="drop-zone flex-grow"
6
- @click="openFileDialog"
7
- @drop.prevent.stop="onDrop"
8
- @dragleave="draggingOver = false"
9
- >
10
- <Transition name="pop">
11
- <div ref="dropCircle" v-if="draggingOver" class="drop-circle" />
12
- </Transition>
13
- <div class="img-label" v-if="allowUpload">
14
- <p>
15
- {{ dragDropLabel }} <span>{{ browseLabel }}</span>
16
- </p>
17
- </div>
18
- <div class="uploading-wrap">
19
- <div
20
- v-for="(item, index) in files"
21
- :class="{ uploading: item.uploading }"
22
- :key="index"
23
- >
24
- <div class="load-file-bar">
25
- <!-- Preview File if image -->
26
- <img
27
- width="40"
28
- v-if="isImg(item)"
29
- :src="item.dataUrl"
30
- :alt="item.name"
31
- style="margin-inline-end: 10px"
32
- />
33
- <p>
34
- {{ item.name }}
35
- </p>
36
- <div class="flex">
37
- <div
38
- class="pie"
39
- :style="`--p:${item.progress}`"
40
- style="--b: 2px"
41
- :class="{ complete: item.progress === 100 }"
42
- >
43
- <span class="progress" v-if="item.progress < 100">{{
44
- `${item.progress.toFixed(0)}`
45
- }}</span>
46
- <MaterialIcon class="success" icon="check" />
47
- </div>
48
- <Btn
49
- thin
50
- class="delete-btn"
51
- @click.stop="deleteImg(item)"
52
- icon="delete"
53
- v-if="item.progress === 100"
54
- />
55
- </div>
56
- </div>
57
- </div>
58
- </div>
59
- </div>
60
- <input
61
- class="file-input"
62
- type="file"
63
- @change="handleFileInput"
64
- ref="fileInputEl"
65
- :multiple="context?.attrs.multiple || false"
66
- />
67
- </div>
2
+ <div class="files-wrapper flex">
3
+ <div
4
+ ref="dropZoneEl"
5
+ class="drop-zone flex-grow"
6
+ @click="openFileDialog"
7
+ @drop.prevent.stop="onDrop"
8
+ @dragleave="draggingOver = false"
9
+ >
10
+ <Transition name="pop">
11
+ <div
12
+ ref="dropCircle"
13
+ v-if="draggingOver"
14
+ class="drop-circle"
15
+ />
16
+ </Transition>
17
+ <div
18
+ class="img-label"
19
+ v-if="allowUpload"
20
+ >
21
+ <p>
22
+ {{ computedDragDropLabel }} <span>{{ computedDrowseLabel }}</span>
23
+ </p>
24
+ </div>
25
+ <div class="uploading-wrap">
26
+ <div
27
+ v-for="(item, index) in files"
28
+ :class="{ uploading: item.uploading }"
29
+ :key="index"
30
+ >
31
+ <div class="load-file-bar">
32
+ <!-- Preview File if image -->
33
+ <img
34
+ width="40"
35
+ v-if="isImg(item)"
36
+ :src="item.dataUrl"
37
+ :alt="item.name"
38
+ style="margin-inline-end: 10px"
39
+ >
40
+ <p>
41
+ {{ item.name }}
42
+ </p>
43
+ <div class="flex">
44
+ <div
45
+ class="pie"
46
+ :style="`--p:${item.progress}`"
47
+ style="--b: 2px"
48
+ :class="{ complete: item.progress === 100 }"
49
+ >
50
+ <span
51
+ class="progress"
52
+ v-if="item.progress < 100"
53
+ >{{
54
+ `${item.progress.toFixed(0)}`
55
+ }}</span>
56
+ <MaterialIcon
57
+ class="success"
58
+ icon="check"
59
+ />
60
+ </div>
61
+ <Btn
62
+ thin
63
+ class="delete-btn"
64
+ @click.stop="deleteImg(item)"
65
+ icon="delete"
66
+ v-if="item.progress === 100"
67
+ />
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ <input
74
+ class="file-input"
75
+ type="file"
76
+ @change="handleFileInput"
77
+ ref="fileInputEl"
78
+ :multiple="computedMultiple"
79
+ >
80
+ </div>
68
81
  </template>
69
82
 
70
83
  <script setup lang="ts">
71
84
  interface UploadFile {
72
- dataUrl: string;
73
- progress: number;
74
- uploading: boolean;
75
- file?: File;
76
- name: string;
77
- serverFile?: any;
78
- uploaded: boolean;
85
+ dataUrl: string;
86
+ progress: number;
87
+ uploading: boolean;
88
+ file?: File;
89
+ name: string;
90
+ serverFile?: any;
91
+ uploaded: boolean;
79
92
  }
80
93
 
81
- import { onMounted, onUnmounted } from "vue";
94
+ import { onMounted, onUnmounted } from 'vue';
82
95
 
83
- import { MaterialIcon, Btn, useBagel } from "@bagelink/vue";
96
+ import { MaterialIcon, Btn, useBagel } from '@bagelink/vue';
84
97
 
85
98
  const bagel = useBagel();
86
99
 
@@ -92,129 +105,133 @@ const fileInputEl = $ref<HTMLInputElement>();
92
105
  const files = $ref<UploadFile[]>([]);
93
106
 
94
107
  const props = withDefaults(
95
- defineProps<{
96
- context: Record<string, any>;
97
- dragDropLabel?: string;
98
- browseLabel?: string;
99
- }>(),
100
- {
101
- dragDropLabel: "Drag and drop your files here",
102
- browseLabel: "or browse",
103
- }
108
+ defineProps<{
109
+ context: Record<string, any>;
110
+ dragDropLabel?: string;
111
+ browseLabel?: string;
112
+ multiple?: boolean;
113
+ }>(),
114
+ {
115
+ dragDropLabel: 'Drag and drop your files here',
116
+ browseLabel: 'or browse',
117
+ multiple: false,
118
+ },
104
119
  );
105
120
 
121
+ const computedDragDropLabel = $computed(() => props?.context?.attrs?.dragDropLabel || props?.dragDropLabel);
122
+ const computedDrowseLabel = $computed(() => props?.context?.attrs?.browseLabel || props?.browseLabel);
123
+ const computedMultiple = $computed(() => !!(props?.context?.attrs?.multiple || props?.multiple));
124
+
106
125
  const allowUpload = $computed(() => {
107
- const tooManyFiles = !props.context?.attrs.multiple && files.length > 0;
108
- return !tooManyFiles;
126
+ const tooManyFiles = !computedMultiple && files.length > 0;
127
+ return !tooManyFiles;
109
128
  });
110
129
 
111
- const openFileDialog = () => (allowUpload ? fileInputEl?.click() : "");
130
+ const openFileDialog = () => (allowUpload ? fileInputEl?.click() : '');
112
131
 
113
132
  function animateCircle(e: DragEvent) {
114
- e.preventDefault();
115
- const zoneRect = dropZoneEl?.getBoundingClientRect?.();
116
- if (!zoneRect) return;
117
- draggingOver = true;
118
- const left = e.clientX - zoneRect.left;
119
- const top = e.clientY - zoneRect.top;
120
- dropCircle?.style?.setProperty?.("left", `${left}px`);
121
- dropCircle?.style?.setProperty?.("top", `${top}px`);
133
+ e.preventDefault();
134
+ const zoneRect = dropZoneEl?.getBoundingClientRect?.();
135
+ if (!zoneRect) return;
136
+ draggingOver = true;
137
+ const left = e.clientX - zoneRect.left;
138
+ const top = e.clientY - zoneRect.top;
139
+ dropCircle?.style?.setProperty?.('left', `${left}px`);
140
+ dropCircle?.style?.setProperty?.('top', `${top}px`);
122
141
  }
123
142
 
124
143
  function emitValue() {
125
- // const fileValues = files.map((f) => f.serverFile?.id);
126
- // console.log({ files });
127
- if (!props.context?.attrs.multiple)
128
- props.context?.node.input(files[0].serverFile);
129
- // eslint-disable-next-line no-alert
130
- else alert("not implemented");
144
+ // const fileValues = files.map((f) => f.serverFile?.id);
145
+ // console.log({ files });
146
+ if (!computedMultiple) return props.context?.node.input(files[0].serverFile);
147
+ // eslint-disable-next-line no-alert
148
+ return alert('not implemented');
131
149
  }
132
150
 
133
- const isImg = (file: UploadFile) =>
134
- file.file?.type.includes("image") ||
135
- file.serverFile?.mimetype?.match(/image\/|jpg|png|gif|bmp|jpeg/i) ||
136
- file.serverFile.extension?.match(/jpg|png|gif|bmp|jpeg/i);
151
+ const isImg = (file: UploadFile) => file.file?.type.includes('image') ||
152
+ file.serverFile?.mimetype?.match(/image\/|jpg|png|gif|bmp|jpeg/i) ||
153
+ file.serverFile.extension?.match(/jpg|png|gif|bmp|jpeg/i);
137
154
 
138
155
  function uploadFiles() {
139
- files.forEach(async (file) => {
140
- if (file.uploaded || file.uploading || !file.file) return;
141
- file.uploading = true;
142
- file.serverFile = await bagel.uploadFile(file.file, {
143
- onUploadProgress: (e) => (file.progress = e.progress * 100 - 1),
144
- });
145
- file.progress += 1;
146
- emitValue();
147
- file.uploaded = true;
148
- file.uploading = false;
149
- });
156
+ files.forEach(async (file) => {
157
+ if (file.uploaded || file.uploading || !file.file) return;
158
+ file.uploading = true;
159
+ file.serverFile = await bagel.uploadFile(file.file, {
160
+ onUploadProgress: (e) => (file.progress = e.progress * 100 - 1),
161
+ });
162
+ file.progress += 1;
163
+ emitValue();
164
+ file.uploaded = true;
165
+ file.uploading = false;
166
+ });
150
167
  }
151
168
 
152
169
  const deleteImg = async (file: UploadFile) => {
153
- await bagel.delete(`files/${file.serverFile.id}`);
154
- const index = files.indexOf(file);
155
- files.splice(index, 1);
156
- emitValue();
170
+ await bagel.delete(`files/${file.serverFile.id}`);
171
+ const index = files.indexOf(file);
172
+ files.splice(index, 1);
173
+ emitValue();
157
174
  };
158
175
 
159
176
  function readAsDataURL(file: File): Promise<any> {
160
- return new Promise((resolve, reject) => {
161
- const reader = new FileReader();
162
- reader.onload = () => resolve(reader.result);
163
- reader.onerror = reject;
164
- reader.readAsDataURL(file);
165
- });
177
+ return new Promise((resolve, reject) => {
178
+ const reader = new FileReader();
179
+ reader.onload = () => resolve(reader.result);
180
+ reader.onerror = reject;
181
+ reader.readAsDataURL(file);
182
+ });
166
183
  }
167
184
 
168
185
  async function addFiles(addedFiles: FileList) {
169
- if (!allowUpload) return;
170
- for (let i = 0; i < addedFiles.length; i++) {
171
- const f = addedFiles[i];
172
- const file: UploadFile = {
173
- // eslint-disable-next-line no-await-in-loop
174
- dataUrl: await readAsDataURL(f),
175
- file: f,
176
- name: f.name,
177
- progress: 0,
178
- uploading: false,
179
- uploaded: false,
180
- };
181
- files.push(file);
182
- }
183
- uploadFiles();
186
+ if (!allowUpload) return;
187
+ for (let i = 0; i < addedFiles.length; i++) {
188
+ const f = addedFiles[i];
189
+ const file: UploadFile = {
190
+ // eslint-disable-next-line no-await-in-loop
191
+ dataUrl: await readAsDataURL(f),
192
+ file: f,
193
+ name: f.name,
194
+ progress: 0,
195
+ uploading: false,
196
+ uploaded: false,
197
+ };
198
+ files.push(file);
199
+ }
200
+ uploadFiles();
184
201
  }
185
202
 
186
203
  onMounted(() => {
187
- dropZoneEl?.addEventListener("dragover", animateCircle);
188
- if (!props.context?.attrs.multiple && props.context?.value) {
189
- files.push({
190
- serverFile: props.context?.value,
191
- uploaded: true,
192
- uploading: false,
193
- progress: 100,
194
- dataUrl: props.context?.value?.url,
195
- name: props.context?.value?.name,
196
- });
197
- }
204
+ dropZoneEl?.addEventListener('dragover', animateCircle);
205
+ if (!computedMultiple && props.context?.value) {
206
+ files.push({
207
+ serverFile: props.context?.value,
208
+ uploaded: true,
209
+ uploading: false,
210
+ progress: 100,
211
+ dataUrl: props.context?.value?.url,
212
+ name: props.context?.value?.name,
213
+ });
214
+ }
198
215
  });
199
- onUnmounted(() => dropZoneEl?.removeEventListener("dragover", animateCircle));
216
+ onUnmounted(() => dropZoneEl?.removeEventListener('dragover', animateCircle));
200
217
 
201
218
  function handleFileInput(e: Event) {
202
- const filesToAdd: FileList = (e.target as any).files;
203
- void addFiles(filesToAdd); // TODO: Nati: should be awaited?
219
+ const filesToAdd: FileList = (e.target as any).files;
220
+ void addFiles(filesToAdd); // TODO: Nati: should be awaited?
204
221
  }
205
222
 
206
223
  function onDrop(e: DragEvent) {
207
- draggingOver = false;
208
- e.preventDefault();
209
- const filesToAdd = e.dataTransfer?.files;
210
- if (filesToAdd) void addFiles(filesToAdd); // TODO: Nati: should be awaited?
224
+ draggingOver = false;
225
+ e.preventDefault();
226
+ const filesToAdd = e.dataTransfer?.files;
227
+ if (filesToAdd) void addFiles(filesToAdd); // TODO: Nati: should be awaited?
211
228
  }
212
229
  </script>
213
230
  <style>
214
231
  .uploading-wrap {
215
- width: 100%;
216
- padding-right: 5px;
217
- padding-left: 5px;
232
+ width: 100%;
233
+ padding-right: 5px;
234
+ padding-left: 5px;
218
235
  }
219
236
 
220
237
  /*
@@ -222,166 +239,162 @@ function onDrop(e: DragEvent) {
222
239
  */
223
240
 
224
241
  .img-label .icon-font {
225
- font-size: 30px;
226
- margin-bottom: 10px;
242
+ font-size: 30px;
243
+ margin-bottom: 10px;
227
244
  }
228
245
 
229
246
  .img-label span {
230
- text-decoration: underline;
247
+ text-decoration: underline;
231
248
  }
232
249
 
233
250
  .file-input {
234
- display: none;
251
+ display: none;
235
252
  }
236
253
 
237
254
  .files-wrapper {
238
- flex-direction: column;
239
- border-radius: 10px;
240
- /*overflow: hidden;*/
241
- min-height: calc(var(--input-height) * 1.5);
255
+ flex-direction: column;
256
+ border-radius: 10px;
257
+ /*overflow: hidden;*/
258
+ min-height: calc(var(--input-height) * 1.5);
242
259
 
243
- font-size: var(--input-font-size);
260
+ font-size: var(--input-font-size);
244
261
  }
245
262
 
246
263
  .drop-zone {
247
- background-color: var(--input-bg);
248
- height: 100%;
249
- width: 100%;
250
- flex-grow: 1;
251
- position: relative;
252
- overflow: hidden;
253
- cursor: pointer;
254
- transition: var(--bgl-transition);
255
- outline: dashed 1px var(--border-color);
256
- border-radius: 10px;
257
- padding: 1px;
258
- display: flex;
259
- flex-direction: column;
260
- align-items: center;
261
- justify-content: center;
262
- color: var(--input-color);
264
+ background-color: var(--input-bg);
265
+ height: 100%;
266
+ width: 100%;
267
+ flex-grow: 1;
268
+ position: relative;
269
+ overflow: hidden;
270
+ cursor: pointer;
271
+ transition: var(--bgl-transition);
272
+ outline: dashed 1px var(--border-color);
273
+ border-radius: 10px;
274
+ padding: 1px;
275
+ display: flex;
276
+ flex-direction: column;
277
+ align-items: center;
278
+ justify-content: center;
279
+ color: var(--input-color);
263
280
  }
264
281
 
265
282
  .drop-zone:hover {
266
- color: var(--bgl-primary);
267
- outline: dashed 1px var(--bgl-primary-tint);
283
+ color: var(--bgl-primary);
284
+ outline: dashed 1px var(--bgl-primary-tint);
268
285
  }
269
286
 
270
287
  .drop-circle {
271
- pointer-events: none;
272
- position: absolute;
273
- width: 100px;
274
- height: 100px;
275
- border-radius: 50%;
276
- background-color: var(--bgl-primary-tint);
277
- display: flex;
278
- transform: translate(-50%, -50%) scale(1);
279
- transform-origin: left top;
288
+ pointer-events: none;
289
+ position: absolute;
290
+ width: 100px;
291
+ height: 100px;
292
+ border-radius: 50%;
293
+ background-color: var(--bgl-primary-tint);
294
+ display: flex;
295
+ transform: translate(-50%, -50%) scale(1);
296
+ transform-origin: left top;
280
297
  }
281
298
 
282
299
  .load-file-bar {
283
- display: flex;
284
- align-items: center;
285
- justify-content: space-between;
286
- background: var(--bgl-white);
287
- border-radius: var(--btn-border-radius);
288
- width: 100%;
289
- color: var(--input-color);
290
- height: var(--btn-height);
291
- padding: 0.5rem;
292
- height: 46px;
293
- box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
294
- white-space: nowrap;
300
+ display: flex;
301
+ align-items: center;
302
+ justify-content: space-between;
303
+ background: var(--bgl-white);
304
+ border-radius: var(--btn-border-radius);
305
+ width: 100%;
306
+ color: var(--input-color);
307
+ height: var(--btn-height);
308
+ padding: 0.5rem;
309
+ height: 46px;
310
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
311
+ white-space: nowrap;
295
312
  }
296
313
 
297
314
  .load-file-bar p {
298
- width: calc(100% - 40px);
299
- overflow: hidden;
300
- text-overflow: ellipsis;
315
+ width: calc(100% - 40px);
316
+ overflow: hidden;
317
+ text-overflow: ellipsis;
301
318
  }
302
319
 
303
320
  .load-file-bar span {
304
- font-size: 10px;
321
+ font-size: 10px;
305
322
  }
306
323
 
307
324
  .pie {
308
- width: 30px;
309
- height: 30px;
310
- position: relative;
311
- display: flex;
312
- align-items: center;
313
- justify-content: center;
325
+ width: 30px;
326
+ height: 30px;
327
+ position: relative;
328
+ display: flex;
329
+ align-items: center;
330
+ justify-content: center;
314
331
  }
315
332
 
316
333
  .pie:before {
317
- content: "";
318
- position: absolute;
319
- border-radius: 50%;
320
- inset: 0;
321
- transition: all 0.2s ease-in-out;
322
- background: conic-gradient(var(--bgl-primary) calc(var(--p) * 1%), #0000 0);
323
- -webkit-mask: radial-gradient(
324
- farthest-side,
325
- #0000 calc(99% - var(--b)),
326
- #000 calc(100% - var(--b))
327
- );
328
- mask: radial-gradient(
329
- farthest-side,
330
- #0000 calc(99% - var(--b)),
331
- #000 calc(100% - var(--b))
332
- );
334
+ content: "";
335
+ position: absolute;
336
+ border-radius: 50%;
337
+ inset: 0;
338
+ transition: all 0.2s ease-in-out;
339
+ background: conic-gradient(var(--bgl-primary) calc(var(--p) * 1%), #0000 0);
340
+ -webkit-mask: radial-gradient(farthest-side,
341
+ #0000 calc(99% - var(--b)),
342
+ #000 calc(100% - var(--b)));
343
+ mask: radial-gradient(farthest-side,
344
+ #0000 calc(99% - var(--b)),
345
+ #000 calc(100% - var(--b)));
333
346
  }
334
347
 
335
348
  .pie .success {
336
- transform: scale(0);
337
- opacity: 0;
338
- transition: all 0.3s ease-in-out;
349
+ transform: scale(0);
350
+ opacity: 0;
351
+ transition: all 0.3s ease-in-out;
339
352
  }
340
353
 
341
354
  .pie .progress {
342
- position: absolute;
343
- font-size: 10px;
355
+ position: absolute;
356
+ font-size: 10px;
344
357
  }
345
358
 
346
359
  .pie.complete .progress {
347
- display: none;
360
+ display: none;
348
361
  }
349
362
 
350
363
  .pie.complete .success {
351
- transform: scale(1.3);
352
- opacity: 1;
364
+ transform: scale(1.3);
365
+ opacity: 1;
353
366
  }
354
367
 
355
368
  .pie.complete:before {
356
- background: conic-gradient(var(--bgl-green) calc(var(--p) * 1%), #0000 0);
369
+ background: conic-gradient(var(--bgl-green) calc(var(--p) * 1%), #0000 0);
357
370
  }
358
371
 
359
372
  .pie.complete {
360
- color: var(--bgl-green);
373
+ color: var(--bgl-green);
361
374
  }
362
375
  </style>
363
376
  <style>
364
377
  .pop-enter-active {
365
- transition: scale 0.2s cubic-bezier(0.55, 0, 0.1, 1);
378
+ transition: scale 0.2s cubic-bezier(0.55, 0, 0.1, 1);
366
379
  }
367
380
 
368
381
  .pop-leave-active {
369
- transition: scale 0.2s cubic-bezier(0.55, 0, 0.1, 1);
382
+ transition: scale 0.2s cubic-bezier(0.55, 0, 0.1, 1);
370
383
  }
371
384
 
372
385
  .pop-enter-from,
373
386
  .pop-leave-to {
374
- scale: 0;
387
+ scale: 0;
375
388
  }
376
389
 
377
390
  .delete-btn {
378
- margin-inline-end: -30px;
379
- opacity: 0;
380
- margin-inline-start: 10px;
391
+ margin-inline-end: -30px;
392
+ opacity: 0;
393
+ margin-inline-start: 10px;
381
394
  }
382
395
 
383
396
  .uploading-wrap:hover .delete-btn {
384
- opacity: 1;
385
- margin-inline-end: 0;
397
+ opacity: 1;
398
+ margin-inline-end: 0;
386
399
  }
387
400
  </style>