@bagelink/vue 0.0.168 → 0.0.170

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