@bagelink/vue 0.0.466 → 0.0.473

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 (180) hide show
  1. package/dist/components/Accordion.vue.d.ts.map +1 -1
  2. package/dist/components/AccordionItem.vue.d.ts.map +1 -1
  3. package/dist/components/Alert.vue.d.ts +2 -2
  4. package/dist/components/Alert.vue.d.ts.map +1 -1
  5. package/dist/components/Avatar.vue.d.ts.map +1 -1
  6. package/dist/components/Badge.vue.d.ts +2 -2
  7. package/dist/components/Badge.vue.d.ts.map +1 -1
  8. package/dist/components/BglVideo.vue.d.ts +2 -2
  9. package/dist/components/BglVideo.vue.d.ts.map +1 -1
  10. package/dist/components/Btn.vue.d.ts +2 -0
  11. package/dist/components/Btn.vue.d.ts.map +1 -1
  12. package/dist/components/Card.vue.d.ts.map +1 -1
  13. package/dist/components/Carousel.vue.d.ts +4 -2
  14. package/dist/components/Carousel.vue.d.ts.map +1 -1
  15. package/dist/components/DataPreview.vue.d.ts.map +1 -1
  16. package/dist/components/Drop.vue.d.ts +34 -0
  17. package/dist/components/Drop.vue.d.ts.map +1 -0
  18. package/dist/components/FileUploader.vue.d.ts +60 -0
  19. package/dist/components/FileUploader.vue.d.ts.map +1 -0
  20. package/dist/components/Flag.vue.d.ts +2 -2
  21. package/dist/components/Flag.vue.d.ts.map +1 -1
  22. package/dist/components/ListItem.vue.d.ts.map +1 -1
  23. package/dist/components/ListView.vue.d.ts.map +1 -1
  24. package/dist/components/MapEmbed.vue.d.ts +2 -2
  25. package/dist/components/MapEmbed.vue.d.ts.map +1 -1
  26. package/dist/components/MaterialIcon.vue.d.ts.map +1 -1
  27. package/dist/components/Modal.vue.d.ts +2 -1
  28. package/dist/components/Modal.vue.d.ts.map +1 -1
  29. package/dist/components/ModalConfirm.vue.d.ts.map +1 -1
  30. package/dist/components/ModalForm.vue.d.ts.map +1 -1
  31. package/dist/components/NavBar.vue.d.ts.map +1 -1
  32. package/dist/components/PageTitle.vue.d.ts.map +1 -1
  33. package/dist/components/TableSchema.vue.d.ts.map +1 -1
  34. package/dist/components/Title.vue.d.ts.map +1 -1
  35. package/dist/components/dashboard/Lineart.vue.d.ts.map +1 -1
  36. package/dist/components/dashboard/index.d.ts.map +1 -1
  37. package/dist/components/form/BglField.vue.d.ts.map +1 -1
  38. package/dist/components/form/BglForm.vue.d.ts +4 -2
  39. package/dist/components/form/BglForm.vue.d.ts.map +1 -1
  40. package/dist/components/form/ItemRef.vue.d.ts +1 -0
  41. package/dist/components/form/ItemRef.vue.d.ts.map +1 -1
  42. package/dist/components/form/index.d.ts.map +1 -1
  43. package/dist/components/form/inputs/CheckInput.vue.d.ts.map +1 -1
  44. package/dist/components/form/inputs/Checkbox.vue.d.ts.map +1 -1
  45. package/dist/components/form/inputs/ColorPicker.vue.d.ts.map +1 -1
  46. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  47. package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
  48. package/dist/components/form/inputs/FileUpload.vue.d.ts +2 -1
  49. package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
  50. package/dist/components/form/inputs/JSONInput.vue.d.ts.map +1 -1
  51. package/dist/components/form/inputs/RadioPillsInput.vue.d.ts.map +1 -1
  52. package/dist/components/form/inputs/RichText.vue.d.ts.map +1 -1
  53. package/dist/components/form/inputs/SelectField.vue.d.ts +1 -4
  54. package/dist/components/form/inputs/SelectField.vue.d.ts.map +1 -1
  55. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  56. package/dist/components/form/inputs/SignaturePad.vue.d.ts +56 -0
  57. package/dist/components/form/inputs/SignaturePad.vue.d.ts.map +1 -0
  58. package/dist/components/form/inputs/TableField.vue.d.ts.map +1 -1
  59. package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
  60. package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
  61. package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
  62. package/dist/components/form/inputs/ToggleInput.vue.d.ts.map +1 -1
  63. package/dist/components/form/inputs/index.d.ts +1 -0
  64. package/dist/components/form/inputs/index.d.ts.map +1 -1
  65. package/dist/components/index.d.ts.map +1 -1
  66. package/dist/components/layout/BottomMenu.vue.d.ts.map +1 -1
  67. package/dist/components/layout/Layout.vue.d.ts +2 -2
  68. package/dist/components/layout/Layout.vue.d.ts.map +1 -1
  69. package/dist/components/layout/SidebarMenu.vue.d.ts.map +1 -1
  70. package/dist/components/layout/TabbedLayout.vue.d.ts.map +1 -1
  71. package/dist/components/layout/Tabs.vue.d.ts.map +1 -1
  72. package/dist/components/layout/TabsBody.vue.d.ts.map +1 -1
  73. package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
  74. package/dist/components/layout/index.d.ts.map +1 -1
  75. package/dist/index.cjs +17617 -20818
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.mjs +17617 -20818
  78. package/dist/plugins/bagel.d.ts +1 -1
  79. package/dist/plugins/bagel.d.ts.map +1 -1
  80. package/dist/plugins/modal.d.ts +14 -14
  81. package/dist/plugins/modal.d.ts.map +1 -1
  82. package/dist/style.css +333 -309
  83. package/dist/types/BagelForm.d.ts +16 -16
  84. package/dist/types/BagelForm.d.ts.map +1 -1
  85. package/dist/types/BtnOptions.d.ts +1 -1
  86. package/dist/types/BtnOptions.d.ts.map +1 -1
  87. package/dist/types/NavLink.d.ts +2 -2
  88. package/dist/types/NavLink.d.ts.map +1 -1
  89. package/dist/types/file.d.ts +2 -2
  90. package/dist/types/file.d.ts.map +1 -1
  91. package/dist/types/index.d.ts.map +1 -1
  92. package/dist/types/materialIcons.d.ts.map +1 -1
  93. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  94. package/dist/utils/allCountries.d.ts +2 -2
  95. package/dist/utils/allCountries.d.ts.map +1 -1
  96. package/dist/utils/clickOutside.d.ts.map +1 -1
  97. package/dist/utils/index.d.ts +5 -5
  98. package/dist/utils/index.d.ts.map +1 -1
  99. package/package.json +19 -17
  100. package/src/components/Accordion.vue +10 -10
  101. package/src/components/AccordionItem.vue +47 -47
  102. package/src/components/Alert.vue +21 -21
  103. package/src/components/Avatar.vue +15 -15
  104. package/src/components/Badge.vue +17 -16
  105. package/src/components/BglVideo.vue +43 -43
  106. package/src/components/Btn.vue +67 -65
  107. package/src/components/Card.vue +26 -26
  108. package/src/components/Carousel.vue +130 -134
  109. package/src/components/DataPreview.vue +30 -30
  110. package/src/components/Flag.vue +26 -26
  111. package/src/components/ListItem.vue +13 -13
  112. package/src/components/ListView.vue +2 -2
  113. package/src/components/MapEmbed.vue +33 -33
  114. package/src/components/MaterialIcon.vue +12 -12
  115. package/src/components/Modal.vue +73 -66
  116. package/src/components/ModalConfirm.vue +18 -18
  117. package/src/components/ModalForm.vue +79 -76
  118. package/src/components/NavBar.vue +33 -30
  119. package/src/components/PageTitle.vue +10 -8
  120. package/src/components/TableSchema.vue +126 -113
  121. package/src/components/Title.vue +8 -8
  122. package/src/components/dashboard/Lineart.vue +103 -96
  123. package/src/components/dashboard/index.ts +1 -1
  124. package/src/components/form/BglField.vue +61 -61
  125. package/src/components/form/BglFieldSet.vue +9 -8
  126. package/src/components/form/BglForm.vue +71 -70
  127. package/src/components/form/index.ts +4 -4
  128. package/src/components/form/inputs/CheckInput.vue +25 -23
  129. package/src/components/form/inputs/Checkbox.vue +10 -10
  130. package/src/components/form/inputs/ColorPicker.vue +26 -24
  131. package/src/components/form/inputs/DateInput.vue +39 -39
  132. package/src/components/form/inputs/DatePicker.vue +37 -36
  133. package/src/components/form/inputs/FileUpload.vue +165 -161
  134. package/src/components/form/inputs/FileUploadURL.vue +134 -132
  135. package/src/components/form/inputs/JSONInput.vue +29 -29
  136. package/src/components/form/inputs/RadioPillsInput.vue +49 -49
  137. package/src/components/form/inputs/RichText.vue +62 -63
  138. package/src/components/form/inputs/SelectInput.vue +173 -167
  139. package/src/components/form/inputs/SignaturePad.vue +150 -0
  140. package/src/components/form/inputs/TableField.vue +77 -76
  141. package/src/components/form/inputs/TelInput.vue +280 -285
  142. package/src/components/form/inputs/TextInput.vue +81 -76
  143. package/src/components/form/inputs/ToggleInput.vue +13 -13
  144. package/src/components/form/inputs/index.ts +17 -15
  145. package/src/components/formkit/FileUploader.vue +162 -158
  146. package/src/components/formkit/MiscFields.vue +20 -20
  147. package/src/components/formkit/Toggle.vue +13 -13
  148. package/src/components/index.ts +28 -28
  149. package/src/components/layout/BottomMenu.vue +25 -21
  150. package/src/components/layout/Layout.vue +29 -29
  151. package/src/components/layout/SidebarMenu.vue +31 -27
  152. package/src/components/layout/TabbedLayout.vue +47 -45
  153. package/src/components/layout/Tabs.vue +32 -32
  154. package/src/components/layout/TabsBody.vue +10 -10
  155. package/src/components/layout/TabsNav.vue +44 -44
  156. package/src/components/layout/index.ts +7 -7
  157. package/src/components/layout/tabsManager.ts +6 -6
  158. package/src/index.ts +13 -8
  159. package/src/plugins/bagel.ts +37 -37
  160. package/src/plugins/modal.ts +66 -67
  161. package/src/styles/layout.css +5 -3
  162. package/src/styles/mobilLayout.css +7 -0
  163. package/src/types/BagelForm.ts +27 -27
  164. package/src/types/BtnOptions.ts +13 -13
  165. package/src/types/NavLink.ts +8 -8
  166. package/src/types/file.ts +11 -11
  167. package/src/types/index.ts +16 -12
  168. package/src/types/materialIcons.ts +3249 -3249
  169. package/src/utils/BagelFormUtils.ts +23 -23
  170. package/src/utils/allCountries.ts +11 -12
  171. package/src/utils/clickOutside.ts +6 -6
  172. package/src/utils/index.ts +39 -41
  173. package/src/utils/lang.ts +13 -13
  174. package/src/utils/strings.ts +9 -9
  175. package/tsconfig.json +0 -1
  176. package/vite.config.ts +8 -8
  177. package/dist/components/Popover.vue.d.ts +0 -10
  178. package/dist/components/Popover.vue.d.ts.map +0 -1
  179. package/dist/types/materialIcon.d.ts +0 -2
  180. package/dist/types/materialIcon.d.ts.map +0 -1
@@ -1,49 +1,201 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, watch } from 'vue'
3
+ import {
4
+ Btn,
5
+ MaterialIcon,
6
+ type StorageFile,
7
+ useBagel,
8
+ } from '@bagelink/vue'
9
+
10
+ const props = defineProps<{
11
+ label?: string
12
+ multiple?: boolean
13
+ files?: StorageFile | StorageFile[]
14
+ deleteEndpoint?: string
15
+ bindkey?: StrKey
16
+ width?: string
17
+ }>()
18
+
19
+ const bagel = useBagel()
20
+
21
+ interface QueueFile {
22
+ file: File
23
+ progress: number
24
+ uploading?: boolean
25
+ name?: string
26
+ uploaded?: boolean
27
+ }
28
+
29
+ type StrKey = keyof StorageFile
30
+
31
+ type FSValue = string[] | string | number
32
+
33
+ const bindKey: StrKey = props.bindkey || 'id'
34
+
35
+ const file_bindkeys = defineModel<FSValue>('modelValue')
36
+ const storageFiles = $ref<StorageFile[]>([])
37
+
38
+ const compareIds = (v1?: FSValue, v2?: FSValue) => [v1].flat().join(',') === [v2].flat().join(',')
39
+
40
+ onMounted(() => {
41
+ if (!props.files && [file_bindkeys.value].flat().length) {
42
+ const ids = [file_bindkeys.value].flat()
43
+ if (props.multiple) {
44
+ ids.forEach((id) => {
45
+ void bagel.get<StorageFile>(`/files/${id}`).then((file) => {
46
+ storageFiles.push(file)
47
+ })
48
+ })
49
+ } else {
50
+ void bagel.get<StorageFile>(`/files/${ids[0]}`).then((file) => {
51
+ storageFiles.push(file)
52
+ })
53
+ }
54
+ }
55
+ })
56
+
57
+ watch(
58
+ () => props.files,
59
+ (newFiles) => {
60
+ if (newFiles) {
61
+ const filesToAdd = [newFiles]
62
+ .flat()
63
+ .filter(f => !storageFiles.find(sf => sf[bindKey] === f[bindKey]))
64
+ for (const file of filesToAdd) {
65
+ storageFiles.push(file)
66
+ }
67
+ }
68
+ },
69
+ { immediate: true },
70
+ )
71
+
72
+ watch(
73
+ () => storageFiles,
74
+ (newFiles) => {
75
+ let idValue: FSValue
76
+ if (props.multiple) { idValue = newFiles.map(f => f[bindKey]) as FSValue
77
+ }
78
+ else {
79
+ idValue = (newFiles[0][bindKey] as string) || ''
80
+ }
81
+ if (!compareIds(file_bindkeys.value, idValue)) {
82
+ file_bindkeys.value = idValue
83
+ }
84
+ },
85
+ { deep: true },
86
+ )
87
+
88
+ const fileQueue = $ref<QueueFile[]>([])
89
+ let isDragOver = $ref(false)
90
+
91
+ function preventDefault(e: Event) {
92
+ e.preventDefault()
93
+ e.stopPropagation()
94
+ }
95
+
96
+ const fileToUrl = (file: File) => URL.createObjectURL(file)
97
+ function removeFile(file: StorageFile) {
98
+ const index = storageFiles.indexOf(file)
99
+ storageFiles.splice(index, 1)
100
+
101
+ if (props.deleteEndpoint) {
102
+ void bagel.delete(`${props.deleteEndpoint}/${file.id}`)
103
+ }
104
+ }
105
+
106
+ function flushQueue() {
107
+ fileQueue.forEach(async (file, i) => {
108
+ if (!props.multiple) storageFiles.splice(0, 1)
109
+ const serverFile = await bagel.uploadFile<StorageFile>(file.file, {
110
+ onUploadProgress: (e: any) => (file.progress = e.progress * 100 - 1),
111
+ })
112
+ storageFiles.push(serverFile)
113
+ fileQueue.splice(i, 1)
114
+ })
115
+ }
116
+
117
+ function browse() {
118
+ const input = document.createElement('input')
119
+ input.type = 'file'
120
+ input.multiple = props.multiple
121
+ input.onchange = (e: Event) => {
122
+ const target = e.target as HTMLInputElement
123
+ if (target.files) {
124
+ Array.from(target.files).forEach((file: File) => fileQueue.push({ name: file.name, file, progress: 0 }))
125
+ }
126
+ flushQueue()
127
+ }
128
+ input.click()
129
+ }
130
+
131
+ function dragleave(e: DragEvent) {
132
+ preventDefault(e)
133
+ if (e.dataTransfer) isDragOver = false
134
+ else isDragOver = false
135
+ }
136
+
137
+ function dragover(e: DragEvent) {
138
+ preventDefault(e)
139
+ if (e.dataTransfer) isDragOver = true
140
+ else isDragOver = false
141
+ }
142
+
143
+ function drop(e: DragEvent) {
144
+ preventDefault(e)
145
+ if (e.dataTransfer) {
146
+ Array.from(e.dataTransfer.files).forEach((file: File) => fileQueue.push({ name: file.name, file, progress: 0 }))
147
+ }
148
+ isDragOver = false
149
+ flushQueue()
150
+ }
151
+ </script>
152
+
1
153
  <template>
2
154
  <div class="bagel-input">
3
155
  <label>
4
156
  {{ label }}
5
157
  </label>
6
158
  <div
7
- @click="browse"
8
- @dragover="dragover"
9
- @drop="drop"
10
- @dragleave="dragleave"
11
159
  class="fileUploadWrap"
12
160
  :class="{
13
161
  fileDropZone: !storageFiles.length && !fileQueue.length,
14
162
  dragover: isDragOver,
15
163
  }"
164
+ @click="browse"
165
+ @dragover="dragover"
166
+ @drop="drop"
167
+ @dragleave="dragleave"
16
168
  >
17
169
  <slot name="files" :files="storageFiles" :fileQueue>
18
170
  <template v-for="file in storageFiles" :key="file.id">
19
- <div class="single-image" v-if="!multiple">
20
- <img class="single preview" :src="file.url" alt="" >
171
+ <div v-if="!multiple" class="single-image">
172
+ <img class="single preview" :src="file.url" alt="">
21
173
  <!-- <p class="no-margin">
22
174
  {{ file.name }}
23
175
  </p> -->
24
176
  </div>
25
- <div class="multi-image-item previewName" v-if="multiple">
26
- <img class="preview" :src="file.url" alt="" >
177
+ <div v-if="multiple" class="multi-image-item previewName">
178
+ <img class="preview" :src="file.url" alt="">
27
179
  <p class="no-margin">
28
180
  {{ file.name }}
29
181
  </p>
30
182
  <Btn
31
183
  thin
32
- @click.stop="removeFile(file)"
33
184
  flat
34
185
  icon="delete"
35
186
  color="red"
187
+ @click.stop="removeFile(file)"
36
188
  />
37
189
  </div>
38
190
  </template>
39
191
  <template v-for="fileQ in fileQueue" :key="fileQ.file">
40
- <div class="imagePreviewWrap" v-if="!multiple">
41
- <img class="preview" :src="fileToUrl(fileQ.file)" alt="" >
192
+ <div v-if="!multiple" class="imagePreviewWrap">
193
+ <img class="preview" :src="fileToUrl(fileQ.file)" alt="">
42
194
  </div>
43
195
  <div class="previewName">
44
196
  <img
45
- :width="width || '220'"
46
197
  v-if="multiple"
198
+ :width="width || '220'"
47
199
  class="preview"
48
200
  :src="fileToUrl(fileQ.file)"
49
201
  alt=""
@@ -57,7 +209,7 @@
57
209
  style="--b: 2px"
58
210
  :class="{ complete: fileQ.progress === 100 }"
59
211
  >
60
- <span class="progress" v-if="fileQ.progress < 100">
212
+ <span v-if="fileQ.progress < 100" class="progress">
61
213
  {{ `${fileQ.progress.toFixed(0)}` }}
62
214
  </span>
63
215
  <MaterialIcon class="success" icon="check" />
@@ -72,154 +224,6 @@
72
224
  </div>
73
225
  </template>
74
226
 
75
- <script setup lang="ts">
76
- import { onMounted, watch } from 'vue';
77
- import {
78
- Btn, type StorageFile, useBagel, MaterialIcon,
79
- } from '@bagelink/vue';
80
-
81
- const bagel = useBagel();
82
-
83
- type QueueFile = {
84
- file: File;
85
- progress: number;
86
- uploading?: boolean;
87
- name?: string;
88
- uploaded?: boolean;
89
- };
90
-
91
- type StrKey = keyof StorageFile;
92
-
93
- type FSValue = string[] | string | number;
94
-
95
- const props = defineProps<{
96
- label?: string;
97
- multiple?: boolean;
98
- files?: StorageFile | StorageFile[];
99
- deleteEndpoint?: string;
100
- bindkey?: StrKey;
101
- width?: string;
102
- }>();
103
-
104
- const bindKey: StrKey = props.bindkey || 'id';
105
-
106
- const file_bindkeys = defineModel<FSValue>('modelValue');
107
- const storageFiles = $ref<StorageFile[]>([]);
108
-
109
- const compareIds = (v1?: FSValue, v2?: FSValue) => [v1].flat().join(',') === [v2].flat().join(',');
110
-
111
- onMounted(() => {
112
- if (!props.files && [file_bindkeys.value].flat().length) {
113
- const ids = [file_bindkeys.value].flat();
114
- if (props.multiple) {
115
- ids.forEach((id) => {
116
- void bagel.get<StorageFile>(`/files/${id}`).then((file) => {
117
- storageFiles.push(file);
118
- });
119
- });
120
- } else {
121
- void bagel.get<StorageFile>(`/files/${ids[0]}`).then((file) => {
122
- storageFiles.push(file);
123
- });
124
- }
125
- }
126
- });
127
-
128
- watch(
129
- () => props.files,
130
- (newFiles) => {
131
- if (newFiles) {
132
- const filesToAdd = [newFiles]
133
- .flat()
134
- .filter((f) => !storageFiles.find((sf) => sf[bindKey] === f[bindKey]));
135
- for (const file of filesToAdd) {
136
- storageFiles.push(file);
137
- }
138
- }
139
- },
140
- { immediate: true },
141
- );
142
-
143
- watch(
144
- () => storageFiles,
145
- (newFiles) => {
146
- let idValue: FSValue;
147
- if (props.multiple) idValue = newFiles.map((f) => f[bindKey]) as FSValue;
148
- else {
149
- idValue = (newFiles[0]?.[bindKey] as string) || '';
150
- }
151
- if (!compareIds(file_bindkeys?.value, idValue)) {
152
- file_bindkeys.value = idValue;
153
- }
154
- },
155
- { deep: true },
156
- );
157
-
158
- const fileQueue = $ref<QueueFile[]>([]);
159
- let isDragOver = $ref(false);
160
-
161
- const preventDefault = (e: Event) => {
162
- e.preventDefault();
163
- e.stopPropagation();
164
- };
165
-
166
- const fileToUrl = (file: File) => URL.createObjectURL(file);
167
- const removeFile = (file: StorageFile) => {
168
- const index = storageFiles.indexOf(file);
169
- storageFiles.splice(index, 1);
170
-
171
- if (props.deleteEndpoint) {
172
- void bagel.delete(`${props.deleteEndpoint}/${file.id}`);
173
- }
174
- };
175
-
176
- const flushQueue = () => {
177
- fileQueue.forEach(async (file, i) => {
178
- if (!props.multiple) storageFiles.splice(0, 1);
179
- const serverFile = await bagel.uploadFile<StorageFile>(file.file, {
180
- onUploadProgress: (e: any) => (file.progress = e.progress * 100 - 1),
181
- });
182
- storageFiles.push(serverFile);
183
- fileQueue.splice(i, 1);
184
- });
185
- };
186
-
187
- const browse = () => {
188
- const input = document.createElement('input');
189
- input.type = 'file';
190
- input.multiple = props.multiple;
191
- input.onchange = (e: Event) => {
192
- const target = e?.target as HTMLInputElement;
193
- if (target?.files) {
194
- Array.from(target.files).forEach((file: File) => fileQueue.push({ name: file.name, file, progress: 0 }));
195
- }
196
- flushQueue();
197
- };
198
- input.click();
199
- };
200
-
201
- const dragleave = (e: DragEvent) => {
202
- preventDefault(e);
203
- if (e.dataTransfer) isDragOver = false;
204
- else isDragOver = false;
205
- };
206
-
207
- const dragover = (e: DragEvent) => {
208
- preventDefault(e);
209
- if (e.dataTransfer) isDragOver = true;
210
- else isDragOver = false;
211
- };
212
-
213
- const drop = (e: DragEvent) => {
214
- preventDefault(e);
215
- if (e.dataTransfer) {
216
- Array.from(e.dataTransfer.files).forEach((file: File) => fileQueue.push({ name: file.name, file, progress: 0 }));
217
- }
218
- isDragOver = false;
219
- flushQueue();
220
- };
221
- </script>
222
-
223
227
  <style scoped>
224
228
  .bagel-input .fileUploadWrap {
225
229
  --width: 320px;
@@ -1,51 +1,167 @@
1
+ <script setup lang="ts">
2
+ import { watch } from 'vue'
3
+ import { Btn, MaterialIcon, type StorageFile, useBagel } from '@bagelink/vue'
4
+
5
+ const props = defineProps<{
6
+ label: string
7
+ multiple?: boolean
8
+ files?: StorageFile | StorageFile[]
9
+ deleteEndpoint?: string
10
+ }>()
11
+
12
+ const bagel = useBagel()
13
+
14
+ interface QueueFile {
15
+ file: File
16
+ progress: number
17
+ uploading?: boolean
18
+ name?: string
19
+ uploaded?: boolean
20
+ }
21
+
22
+ const fileURLs = defineModel<string[] | string>('modelValue')
23
+ const storageFiles = $ref<StorageFile[]>([])
24
+
25
+ const compareIds = (v1?: string[] | string, v2?: string[] | string) => [v1].flat().join(',') === [v2].flat().join(',')
26
+
27
+ watch(
28
+ () => props.files,
29
+ (newFiles) => {
30
+ if (newFiles) storageFiles.push(...[newFiles].flat())
31
+ },
32
+ { immediate: true }
33
+ )
34
+
35
+ watch(
36
+ () => storageFiles,
37
+ (newFiles) => {
38
+ let idValue: string[] | string = ''
39
+ if (props.multiple) idValue = newFiles.map(f => f.url || '')
40
+ else idValue = newFiles[0].url || ''
41
+ if (!compareIds(fileURLs.value, idValue)) fileURLs.value = idValue
42
+ },
43
+ { deep: true }
44
+ )
45
+
46
+ const fileQueue = $ref<QueueFile[]>([])
47
+ let isDragOver = $ref(false)
48
+
49
+ function preventDefault(e: Event) {
50
+ e.preventDefault()
51
+ e.stopPropagation()
52
+ }
53
+
54
+ const fileToUrl = (file: File) => URL.createObjectURL(file)
55
+ function removeFile(file: StorageFile) {
56
+ const index = storageFiles.indexOf(file)
57
+ storageFiles.splice(index, 1)
58
+
59
+ if (props.deleteEndpoint) {
60
+ void bagel.delete(`${props.deleteEndpoint}/${file.id}`)
61
+ }
62
+ }
63
+
64
+ function flushQueue() {
65
+ fileQueue.forEach(async (file, i) => {
66
+ if (!props.multiple) storageFiles.splice(0, 1)
67
+ const serverFile = await bagel.uploadFile<StorageFile>(file.file, {
68
+ onUploadProgress: (e: any) => (file.progress = e.progress * 100 - 1),
69
+ })
70
+ storageFiles.push(serverFile)
71
+ fileQueue.splice(i, 1)
72
+ })
73
+ }
74
+
75
+ function browse() {
76
+ const input = document.createElement('input')
77
+ input.type = 'file'
78
+ input.multiple = props.multiple
79
+ input.onchange = (e: Event) => {
80
+ const target = e.target as HTMLInputElement
81
+ if (target.files) {
82
+ Array.from(target.files).forEach((file: File) => fileQueue.push({ name: file.name, file, progress: 0 })
83
+ )
84
+ }
85
+ flushQueue()
86
+ }
87
+ input.click()
88
+ }
89
+
90
+ function dragleave(e: DragEvent) {
91
+ preventDefault(e)
92
+ if (e.dataTransfer) isDragOver = false
93
+ else isDragOver = false
94
+ }
95
+
96
+ function dragover(e: DragEvent) {
97
+ preventDefault(e)
98
+ if (e.dataTransfer) isDragOver = true
99
+ else isDragOver = false
100
+ }
101
+
102
+ function drop(e: DragEvent) {
103
+ preventDefault(e)
104
+ if (e.dataTransfer) {
105
+ Array.from(e.dataTransfer.files).forEach((file: File) => fileQueue.push({ name: file.name, file, progress: 0 })
106
+ )
107
+ }
108
+ isDragOver = false
109
+ flushQueue()
110
+ }
111
+ </script>
112
+
1
113
  <template>
2
114
  <div class="bagel-input">
3
115
  <label>
4
116
  {{ label }}
5
117
  </label>
6
118
  <div
7
- @click="browse"
8
- @dragover="dragover"
9
- @drop="drop"
10
- @dragleave="dragleave"
11
119
  class="fileUploadWrap"
12
120
  :class="{
13
121
  fileDropZone: !storageFiles.length && !fileQueue.length,
14
122
  dragover: isDragOver,
15
- }">
123
+ }"
124
+ @click="browse"
125
+ @dragover="dragover"
126
+ @drop="drop"
127
+ @dragleave="dragleave"
128
+ >
16
129
  <template v-for="file in storageFiles" :key="file.id">
17
- <div class="imagePreviewWrap" v-if="!multiple">
18
- <img class="preview" :src="file.url" alt="" />
130
+ <div v-if="!multiple" class="imagePreviewWrap">
131
+ <img class="preview" :src="file.url" alt="">
19
132
  </div>
20
133
  <div class="previewName">
21
134
  <img
22
- height="60"
23
135
  v-if="multiple"
136
+ height="60"
24
137
  class="preview"
25
138
  :src="file.url"
26
- alt="" />
139
+ alt=""
140
+ >
27
141
  <p class="no-margin">
28
142
  {{ file.name }}
29
143
  </p>
30
144
  <Btn
31
145
  thin
32
- @click.stop="removeFile(file)"
33
146
  flat
34
147
  icon="delete"
35
- color="red" />
148
+ color="red"
149
+ @click.stop="removeFile(file)"
150
+ />
36
151
  </div>
37
152
  </template>
38
153
  <template v-for="fileQ in fileQueue" :key="fileQ.file">
39
- <div class="imagePreviewWrap" v-if="!multiple">
40
- <img class="preview" :src="fileToUrl(fileQ.file)" alt="" />
154
+ <div v-if="!multiple" class="imagePreviewWrap">
155
+ <img class="preview" :src="fileToUrl(fileQ.file)" alt="">
41
156
  </div>
42
157
  <div class="previewName">
43
158
  <img
44
- height="60"
45
159
  v-if="multiple"
160
+ height="60"
46
161
  class="preview"
47
162
  :src="fileToUrl(fileQ.file)"
48
- alt="" />
163
+ alt=""
164
+ >
49
165
  <p class="no-margin">
50
166
  {{ fileQ.name }}
51
167
  </p>
@@ -53,8 +169,9 @@
53
169
  class="pie"
54
170
  :style="`--p:${fileQ.progress}`"
55
171
  style="--b: 2px"
56
- :class="{ complete: fileQ.progress === 100 }">
57
- <span class="progress" v-if="fileQ.progress < 100">
172
+ :class="{ complete: fileQ.progress === 100 }"
173
+ >
174
+ <span v-if="fileQ.progress < 100" class="progress">
58
175
  {{ `${fileQ.progress.toFixed(0)}` }}
59
176
  </span>
60
177
  <MaterialIcon class="success" icon="check" />
@@ -65,121 +182,6 @@
65
182
  </div>
66
183
  </template>
67
184
 
68
- <script setup lang="ts">
69
- import { watch } from 'vue';
70
- import { Btn, type StorageFile, useBagel, MaterialIcon } from '@bagelink/vue';
71
-
72
- const bagel = useBagel();
73
-
74
- type QueueFile = {
75
- file: File;
76
- progress: number;
77
- uploading?: boolean;
78
- name?: string;
79
- uploaded?: boolean;
80
- };
81
-
82
- const props = defineProps<{
83
- label: string;
84
- multiple?: boolean;
85
- files?: StorageFile | StorageFile[];
86
- deleteEndpoint?: string;
87
- }>();
88
-
89
- const fileURLs = defineModel<string[] | string>('modelValue');
90
- const storageFiles = $ref<StorageFile[]>([]);
91
-
92
- const compareIds = (v1?: string[] | string, v2?: string[] | string) =>
93
- [v1].flat().join(',') === [v2].flat().join(',');
94
-
95
- watch(
96
- () => props.files,
97
- (newFiles) => {
98
- if (newFiles) storageFiles.push(...[newFiles].flat());
99
- },
100
- { immediate: true }
101
- );
102
-
103
- watch(
104
- () => storageFiles,
105
- (newFiles) => {
106
- let idValue: string[] | string = '';
107
- if (props.multiple) idValue = newFiles.map((f) => f.url || '');
108
- else idValue = newFiles[0]?.url || '';
109
- if (!compareIds(fileURLs?.value, idValue)) fileURLs.value = idValue;
110
- },
111
- { deep: true }
112
- );
113
-
114
- const fileQueue = $ref<QueueFile[]>([]);
115
- let isDragOver = $ref(false);
116
-
117
- const preventDefault = (e: Event) => {
118
- e.preventDefault();
119
- e.stopPropagation();
120
- };
121
-
122
- const fileToUrl = (file: File) => URL.createObjectURL(file);
123
- const removeFile = (file: StorageFile) => {
124
- const index = storageFiles.indexOf(file);
125
- storageFiles.splice(index, 1);
126
-
127
- if (props.deleteEndpoint) {
128
- void bagel.delete(`${props.deleteEndpoint}/${file.id}`);
129
- }
130
- };
131
-
132
- const flushQueue = () => {
133
- fileQueue.forEach(async (file, i) => {
134
- if (!props.multiple) storageFiles.splice(0, 1);
135
- const serverFile = await bagel.uploadFile<StorageFile>(file.file, {
136
- onUploadProgress: (e: any) => (file.progress = e.progress * 100 - 1),
137
- });
138
- storageFiles.push(serverFile);
139
- fileQueue.splice(i, 1);
140
- });
141
- };
142
-
143
- const browse = () => {
144
- const input = document.createElement('input');
145
- input.type = 'file';
146
- input.multiple = props.multiple;
147
- input.onchange = (e: Event) => {
148
- const target = e?.target as HTMLInputElement;
149
- if (target?.files) {
150
- Array.from(target.files).forEach((file: File) =>
151
- fileQueue.push({ name: file.name, file, progress: 0 })
152
- );
153
- }
154
- flushQueue();
155
- };
156
- input.click();
157
- };
158
-
159
- const dragleave = (e: DragEvent) => {
160
- preventDefault(e);
161
- if (e.dataTransfer) isDragOver = false;
162
- else isDragOver = false;
163
- };
164
-
165
- const dragover = (e: DragEvent) => {
166
- preventDefault(e);
167
- if (e.dataTransfer) isDragOver = true;
168
- else isDragOver = false;
169
- };
170
-
171
- const drop = (e: DragEvent) => {
172
- preventDefault(e);
173
- if (e.dataTransfer) {
174
- Array.from(e.dataTransfer.files).forEach((file: File) =>
175
- fileQueue.push({ name: file.name, file, progress: 0 })
176
- );
177
- }
178
- isDragOver = false;
179
- flushQueue();
180
- };
181
- </script>
182
-
183
185
  <style scoped>
184
186
  .bagel-input .fileUploadWrap {
185
187
  outline: 1px solid var(--border-color);