@bagelink/vue 0.0.1025 → 0.0.1031

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 (70) hide show
  1. package/README.md +1 -0
  2. package/dist/components/Image.vue.d.ts.map +1 -1
  3. package/dist/components/ToolBar.vue.d.ts +3 -0
  4. package/dist/components/ToolBar.vue.d.ts.map +1 -0
  5. package/dist/components/form/inputs/NumberInput.vue.d.ts.map +1 -1
  6. package/dist/components/form/inputs/RangeInput.vue.d.ts +2 -2
  7. package/dist/components/form/inputs/RangeInput.vue.d.ts.map +1 -1
  8. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts +12 -0
  9. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -0
  10. package/dist/components/form/inputs/RichText/composables/useCommands.d.ts +9 -0
  11. package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -0
  12. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +40 -20
  13. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
  14. package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
  15. package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
  16. package/dist/components/form/inputs/RichText/index.vue.d.ts +1 -0
  17. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
  18. package/dist/components/form/inputs/RichText/utils/commands.d.ts +17 -0
  19. package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -0
  20. package/dist/components/form/inputs/RichText/utils/debug.d.ts +48 -0
  21. package/dist/components/form/inputs/RichText/utils/debug.d.ts.map +1 -0
  22. package/dist/components/form/inputs/RichText/utils/formatting.d.ts +3 -1
  23. package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -1
  24. package/dist/components/form/inputs/RichText/utils/selection.d.ts +13 -0
  25. package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
  26. package/dist/components/form/inputs/RichText/utils/table.d.ts +4 -0
  27. package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -1
  28. package/dist/components/form/inputs/SignaturePad.vue.d.ts +2 -2
  29. package/dist/components/form/inputs/SignaturePad.vue.d.ts.map +1 -1
  30. package/dist/components/form/inputs/Upload/UploadFile.vue.d.ts +86 -0
  31. package/dist/components/form/inputs/Upload/UploadFile.vue.d.ts.map +1 -0
  32. package/dist/components/form/inputs/Upload/upload.d.ts +13 -0
  33. package/dist/components/form/inputs/Upload/upload.d.ts.map +1 -0
  34. package/dist/components/form/inputs/index.d.ts +1 -0
  35. package/dist/components/form/inputs/index.d.ts.map +1 -1
  36. package/dist/components/index.d.ts +1 -0
  37. package/dist/components/index.d.ts.map +1 -1
  38. package/dist/editor-B3mMCQSg.cjs +4 -0
  39. package/dist/editor-BKPRpAjr.js +4 -0
  40. package/dist/index.cjs +10059 -6970
  41. package/dist/index.mjs +10065 -6976
  42. package/dist/plugins/bagel.d.ts +1 -0
  43. package/dist/plugins/bagel.d.ts.map +1 -1
  44. package/dist/style.css +666 -458
  45. package/package.json +1 -1
  46. package/src/components/Image.vue +1 -2
  47. package/src/components/ToolBar.vue +9 -0
  48. package/src/components/form/inputs/NumberInput.vue +8 -1
  49. package/src/components/form/inputs/RangeInput.vue +6 -5
  50. package/src/components/form/inputs/RichText/composables/useCommands.ts +114 -0
  51. package/src/components/form/inputs/RichText/composables/useEditor.ts +257 -129
  52. package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +64 -19
  53. package/src/components/form/inputs/RichText/config.ts +18 -2
  54. package/src/components/form/inputs/RichText/editor.css +17 -15
  55. package/src/components/form/inputs/RichText/index.vue +67 -13
  56. package/src/components/form/inputs/RichText/richTextTypes.d.ts +2 -0
  57. package/src/components/form/inputs/RichText/utils/commands.ts +37 -0
  58. package/src/components/form/inputs/RichText/utils/debug.ts +196 -0
  59. package/src/components/form/inputs/RichText/utils/formatting.ts +168 -288
  60. package/src/components/form/inputs/RichText/utils/selection.ts +77 -0
  61. package/src/components/form/inputs/RichText/utils/table.ts +66 -0
  62. package/src/components/form/inputs/SignaturePad.vue +2 -2
  63. package/src/components/form/inputs/Upload/UploadFile.vue +357 -0
  64. package/src/components/form/inputs/Upload/upload.css +232 -0
  65. package/src/components/form/inputs/Upload/upload.ts +22 -0
  66. package/src/components/form/inputs/Upload/upload.types.d.ts +43 -0
  67. package/src/components/form/inputs/index.ts +1 -0
  68. package/src/components/index.ts +2 -0
  69. package/src/plugins/bagel.ts +2 -2
  70. /package/src/components/form/inputs/RichText/components/{Toolbar.vue → EditorToolbar.vue} +0 -0
@@ -4,7 +4,7 @@ import { Btn } from '@bagelink/vue'
4
4
  import SignaturePad from 'signature_pad'
5
5
  import { onMounted, onUnmounted, watch } from 'vue'
6
6
 
7
- interface WaterMark {
7
+ export interface WaterMark {
8
8
  text: string
9
9
  x: number
10
10
  y: number
@@ -16,7 +16,7 @@ interface WaterMark {
16
16
  style: string
17
17
  }
18
18
 
19
- type FormatType = 'image/png' | 'image/jpg' | 'image/jpeg' | 'image/svg+xml'
19
+ export type FormatType = 'image/png' | 'image/jpg' | 'image/jpeg' | 'image/svg+xml'
20
20
 
21
21
  const props = withDefaults(defineProps<{
22
22
  sigOption?: SignaturePadOptions
@@ -0,0 +1,357 @@
1
+ <script setup lang="ts">
2
+ import type { MaterialIcons } from '@bagelink/vue'
3
+ import type { BglFile, QueueFile } from './upload.types'
4
+ import { Btn, IMAGE_FORMATS_REGEXP, Icon, useBagel, Card, Image } from '@bagelink/vue'
5
+ import { watch } from 'vue'
6
+ import { files } from './upload'
7
+
8
+ const props = withDefaults(defineProps<{
9
+ label?: string
10
+ multiple?: boolean
11
+ modelValue?: string | string[]
12
+ width?: string
13
+ height?: string | 'auto'
14
+ dirPath?: string
15
+ fill?: boolean
16
+ oval?: boolean
17
+ theme?: 'dropzone' | 'basic'
18
+ accept?: string
19
+ required?: boolean
20
+ disabled?: boolean
21
+ baseURL?: string
22
+ }>(), {
23
+ height: '215px',
24
+ theme: 'dropzone',
25
+ accept: '*'
26
+ })
27
+
28
+ const emit = defineEmits(['update:modelValue', 'addFileStart'])
29
+
30
+ const bagel = useBagel()
31
+ const fileBaseUrl = $computed(() => (props.baseURL || bagel.fileBaseUrl || 'https://files.bagel.design/').replace(/\/$/, ''))
32
+
33
+ files.setBaseUrl(bagel.host)
34
+
35
+ const fileQueue = $ref<QueueFile[]>([])
36
+ let storageFiles = $ref<BglFile[]>([])
37
+ const pk = $ref<string[]>([props.modelValue].flat().filter(Boolean) as string[])
38
+
39
+ const pathKeys = $computed<string[]>(() => {
40
+ const sf = storageFiles.map(file => file.path_key)
41
+ return [...pk, ...sf]
42
+ })
43
+
44
+ watch(() => pk, (value) => {
45
+ if (props.multiple) emit('update:modelValue', value)
46
+ else emit('update:modelValue', value[0] || undefined)
47
+ }, { deep: true })
48
+
49
+ const isImage = (str: string) => IMAGE_FORMATS_REGEXP.test(str)
50
+ const pathToUrl = (path_key: string) => `${fileBaseUrl}/${path_key}`
51
+ const fileToUrl = (file: File) => URL.createObjectURL(file)
52
+
53
+ let isDragOver = $ref(false)
54
+
55
+ // File handling methods
56
+ async function removeFile(path_key: string) {
57
+ storageFiles = storageFiles.filter(file => file.path_key !== path_key)
58
+ const pki = pk.indexOf(path_key)
59
+ if (pki !== -1) pk.splice(pki, 1)
60
+
61
+ try {
62
+ await files.delete(path_key)
63
+ } catch (error) {
64
+ console.error(error)
65
+ }
66
+ }
67
+
68
+ async function flushQueue() {
69
+ emit('addFileStart')
70
+ for (const file of fileQueue) {
71
+ file.uploading = true
72
+ if (!props.multiple) pk.splice(0, 1)
73
+ try {
74
+ const { data } = await files.upload(file.file, {
75
+ onUploadProgress: (e: ProgressEvent) => {
76
+ file.progress = (e.loaded / e.total) * 100 - 1
77
+ },
78
+ dirPath: props.dirPath,
79
+ })
80
+ pk.push(data.path_key)
81
+ } catch (error) {
82
+ console.error(error)
83
+ }
84
+ }
85
+ fileQueue.splice(0, fileQueue.length)
86
+ }
87
+
88
+ // Event handlers
89
+ function browse() {
90
+ if (props.disabled) return
91
+ const input = document.createElement('input')
92
+ input.type = 'file'
93
+ input.multiple = props.multiple
94
+ input.accept = props.accept
95
+ input.onchange = (e: Event) => {
96
+ const files = Array.from((e.target as HTMLInputElement).files || [])
97
+ fileQueue.push(...files.map(file => ({ name: file.name, file, progress: 0 })))
98
+ flushQueue()
99
+ }
100
+ input.click()
101
+ }
102
+
103
+ function handleDrag(e: DragEvent, isDragging = false) {
104
+ e.preventDefault()
105
+ e.stopPropagation()
106
+ if (!props.disabled) isDragOver = isDragging
107
+ }
108
+
109
+ function handleDrop(e: DragEvent) {
110
+ if (props.disabled) return
111
+ e.preventDefault()
112
+ e.stopPropagation()
113
+
114
+ if (e.dataTransfer?.files) {
115
+ fileQueue.push(...Array.from(e.dataTransfer.files)
116
+ .map(file => ({ name: file.name, file, progress: 0 })))
117
+ flushQueue()
118
+ }
119
+ isDragOver = false
120
+ }
121
+
122
+ if (props.dirPath) {
123
+ files.get(props.dirPath)
124
+ .then(response => storageFiles.push(...[response.data].flat()))
125
+ .catch(console.error)
126
+ }
127
+
128
+ watch(() => props.dirPath, () => {
129
+ if (props.dirPath) {
130
+ files.get(props.dirPath)
131
+ .then(response => storageFiles.push(...[response.data].flat()))
132
+ .catch(console.error)
133
+ }
134
+ })
135
+ </script>
136
+
137
+ <template>
138
+ <div class="bagel-input">
139
+ <label v-if="label">{{ label }}</label>
140
+ <input
141
+ v-if="required && !pathKeys.length"
142
+ placeholder="required"
143
+ type="text"
144
+ required
145
+ class="pixel"
146
+ >
147
+
148
+ <Card
149
+ v-if="theme === 'basic'"
150
+ outline
151
+ class="flex p-05 gap-1"
152
+ @dragover="(e) => handleDrag(e, true)"
153
+ @drop="handleDrop"
154
+ >
155
+ <Btn
156
+ v-if="!pathKeys.length && !fileQueue.length"
157
+ class="px-1-5"
158
+ icon="upload"
159
+ outline
160
+ @click="browse"
161
+ >
162
+ Upload
163
+ </Btn>
164
+
165
+ <template v-for="path_key in pathKeys" :key="path_key">
166
+ <div class="txt-gray txt-12 flex">
167
+ <div class="m-05 flex opacity-7 z-99">
168
+ <Btn
169
+ v-for="action in [
170
+ { tooltip: 'Delete', icon: 'delete' as MaterialIcons, onClick: () => removeFile(path_key) },
171
+ { tooltip: 'Replace', icon: 'autorenew' as MaterialIcons, onClick: browse },
172
+ { tooltip: 'Download', icon: 'download' as MaterialIcons, href: pathToUrl(path_key), download: path_key.split('/').pop() },
173
+ ]"
174
+ :key="action.tooltip"
175
+ v-tooltip="action.tooltip"
176
+ color="gray"
177
+ thin
178
+ :icon="action.icon"
179
+ v-bind="action.href ? { href: action.href, download: action.download, target: '_blank' } : {}"
180
+ @click.stop="action.onClick"
181
+ />
182
+ <p
183
+ v-lightbox="{ src: pathToUrl(path_key), download: true }"
184
+ class="ellipsis-1 word-break-all h-20 m-0"
185
+ >
186
+ {{ path_key.split('/').pop() }}
187
+ </p>
188
+ </div>
189
+ </div>
190
+ </template>
191
+
192
+ <span
193
+ v-if="!pathKeys.length && !fileQueue.length"
194
+ class="txt-gray txt-12"
195
+ >
196
+ No file selected
197
+ </span>
198
+ </Card>
199
+
200
+ <div
201
+ v-else
202
+ class="fileUploadWrap"
203
+ :class="{
204
+ 'fileDropZone': !pathKeys.length && !fileQueue.length,
205
+ 'dragover': isDragOver,
206
+ 'bgl_oval-upload': oval,
207
+ }"
208
+ :style="{ width, height }"
209
+ @click="browse"
210
+ @dragover="(e) => handleDrag(e, true)"
211
+ @drop="handleDrop"
212
+ @dragleave="(e) => handleDrag(e)"
213
+ >
214
+ <slot name="files" :files="pathKeys" :fileQueue>
215
+ <div v-if="multiple" class="bgl-multi-preview">
216
+ <div
217
+ v-for="path_key in pathKeys"
218
+ :key="path_key"
219
+ v-lightbox="{ src: pathToUrl(path_key), download: true }"
220
+ class="multi-image-item-preview"
221
+ :class="{ 'bgl_fill-image': fill }"
222
+ >
223
+ <Image
224
+ v-if="isImage(path_key)"
225
+ class="multi-preview"
226
+ :src="pathToUrl(path_key)"
227
+ alt=""
228
+ />
229
+ <Icon v-else icon="description" class="multi-preview" />
230
+ <p class="m-0">
231
+ {{ path_key.split('/').pop() }}
232
+ </p>
233
+ <Btn
234
+ thin
235
+ flat
236
+ icon="delete"
237
+ color="red"
238
+ @click.stop="removeFile(path_key)"
239
+ />
240
+ </div>
241
+ <div
242
+ v-for="file in fileQueue"
243
+ :key="file.name"
244
+ class="multi-image-item-preview"
245
+ :class="{ 'bgl_fill-image': fill }"
246
+ >
247
+ <Image
248
+ v-if="isImage(file.file.type)"
249
+ class="multi-preview"
250
+ :src="fileToUrl(file.file)"
251
+ alt=""
252
+ />
253
+ <Icon v-else icon="description" class="multi-preview" />
254
+ <p class="no-margin multi-preview-txt">
255
+ {{ file.name }}
256
+ </p>
257
+ <div
258
+ class="pie"
259
+ :style="{ '--p': file.progress }"
260
+ style="--b: 2px"
261
+ :class="{ complete: file.progress === 100 }"
262
+ >
263
+ <span v-if="file.progress < 100" class="progress">
264
+ {{ file.progress.toFixed(0) }}
265
+ </span>
266
+ <Icon class="success" icon="check_circle" />
267
+ </div>
268
+ </div>
269
+ </div>
270
+
271
+ <div
272
+ v-else-if="pathKeys.length > 0 || fileQueue.length > 0"
273
+ class="bgl-single-preview"
274
+ >
275
+ <div
276
+ v-for="path_key in pathKeys"
277
+ :key="path_key"
278
+ class="single-image-item-preview"
279
+ :class="{ 'bgl_fill-image': fill }"
280
+ >
281
+ <div class="position-start m-05 flex opacity-7 z-99 gap-025">
282
+ <Btn
283
+ v-for="action in [
284
+ { tooltip: 'Delete', icon: 'delete' as MaterialIcons, onClick: () => removeFile(path_key) },
285
+ { tooltip: 'Replace', icon: 'autorenew' as MaterialIcons, onClick: browse },
286
+ { tooltip: 'Download', icon: 'download' as MaterialIcons, href: pathToUrl(path_key), download: path_key.split('/').pop() },
287
+ ]"
288
+ :key="action.tooltip"
289
+ v-tooltip="action.tooltip"
290
+ color="gray"
291
+ thin
292
+ :icon="action.icon"
293
+ v-bind="action.href ? { href: action.href, download: action.download, target: '_blank' } : {}"
294
+ @click.stop="action.onClick"
295
+ />
296
+ </div>
297
+ <div v-if="isImage(path_key)" class="h-100">
298
+ <Image
299
+ v-lightbox="{ src: pathToUrl(path_key), download: true }"
300
+ class="single-preview"
301
+ :src="pathToUrl(path_key)"
302
+ alt=""
303
+ />
304
+ </div>
305
+ <Icon
306
+ v-else
307
+ v-lightbox="{ src: pathToUrl(path_key), download: true }"
308
+ :size="4"
309
+ weight="2"
310
+ icon="description"
311
+ class="color-primary w-100"
312
+ />
313
+ </div>
314
+ <div
315
+ v-for="file in fileQueue"
316
+ :key="file.name"
317
+ class="single-image-item-preview"
318
+ :class="{ 'bgl_fill-image': fill }"
319
+ >
320
+ <div
321
+ class="pie"
322
+ :style="{ '--p': file.progress }"
323
+ style="--b: 2px"
324
+ :class="{ complete: file.progress === 100 }"
325
+ >
326
+ <span v-if="file.progress < 100" class="progress">
327
+ {{ file.progress.toFixed(0) }}
328
+ </span>
329
+ <Icon class="success" icon="check_circle" />
330
+ </div>
331
+ <Image
332
+ v-if="isImage(file.file.type)"
333
+ class="single-preview"
334
+ :src="fileToUrl(file.file)"
335
+ alt=""
336
+ />
337
+ </div>
338
+ </div>
339
+ </slot>
340
+
341
+ <slot
342
+ v-if="(!pathKeys.length && !fileQueue.length) || multiple"
343
+ name="placeholder"
344
+ :files="pathKeys"
345
+ :fileQueue
346
+ :browse
347
+ >
348
+ <p class="p-1 flex column hover fileUploadPlaceHolder justify-content-center mb-05">
349
+ <Icon icon="upload_2" />
350
+ Drop files here or click to upload
351
+ </p>
352
+ </slot>
353
+ </div>
354
+ </div>
355
+ </template>
356
+
357
+ <style src="./upload.css" scoped></style>
@@ -0,0 +1,232 @@
1
+ .fileUploadWrap {
2
+ outline: 1px solid var(--border-color);
3
+ border-radius: var(--input-border-radius);
4
+ text-align: center;
5
+ cursor: pointer;
6
+ transition: var(--bgl-transition);
7
+ position: relative;
8
+ font-size: var(--input-font-size);
9
+ overflow-y: auto;
10
+ background: var(--input-bg);
11
+ height: 215px;
12
+ }
13
+
14
+ .bagel-input .fileUploadWrap.fileDropZone {
15
+ background: var(--input-bg);
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ color: var(--bgl-gray);
20
+ flex-direction: column;
21
+ }
22
+
23
+ .fileUploadWrap.dragover,
24
+ .fileUploadWrap:hover {
25
+ box-shadow: inset 0 0 10px #00000012;
26
+ }
27
+
28
+ .fileUploadWrap[style*='height: auto;'] {
29
+ min-height: 215px;
30
+ }
31
+
32
+ .multi-image-item-preview {
33
+ border: 1px solid var(--border-color) !important;
34
+ border-radius: var(--input-border-radius);
35
+ margin: 0.5rem !important;
36
+ background: var(--bgl-popup-bg);
37
+ padding: 0;
38
+ padding-inline-end: 0.75rem;
39
+ text-align: start;
40
+ color: var(--input-color);
41
+ display: grid;
42
+ grid-template-columns: auto 1fr 22px;
43
+ gap: 1rem;
44
+ align-items: center;
45
+ }
46
+
47
+ .multi-image-item-preview p {
48
+ overflow: hidden;
49
+ text-overflow: ellipsis;
50
+ white-space: nowrap;
51
+ }
52
+
53
+ .multi-preview {
54
+ width: 40px;
55
+ height: 40px;
56
+ border-radius: var(--input-border-radius);
57
+ object-fit: cover;
58
+ background: var(--bgl-gray-light);
59
+ text-align: center !important;
60
+ justify-content: center;
61
+ align-items: center;
62
+ display: flex;
63
+ }
64
+
65
+ .bgl-single-preview {
66
+ height: 100%;
67
+ position: relative;
68
+ }
69
+
70
+ .bgl-single-preview + .fileUploadPlaceHolder {
71
+ position: absolute;
72
+ inset: 0;
73
+ margin: auto;
74
+ top: calc(50% - 2rem);
75
+ }
76
+
77
+ .single-image-item-preview {
78
+ height: 100%;
79
+ min-height: 100%;
80
+ position: relative;
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ }
85
+
86
+ .fileUploadWrap[style*='height: auto'] .single-image-item-preview {
87
+ min-height: 215px;
88
+ }
89
+
90
+ .fileUploadWrap[style*='height: auto'] .single-preview {
91
+ margin: 0rem !important;
92
+ }
93
+
94
+ .single-preview {
95
+ border-radius: var(--input-border-radius);
96
+ margin: 1rem;
97
+ padding: 0px;
98
+ min-height: calc(100% - 2rem);
99
+ height: calc(100% - 2rem);
100
+ object-fit: cover;
101
+ background: var(--bgl-gray-light);
102
+ width: 90%;
103
+ }
104
+
105
+ .single-image-item-preview:hover::after {
106
+ content: 'zoom_in';
107
+ font-size: 32px;
108
+ font-family: 'Material Symbols Outlined', serif;
109
+ position: absolute;
110
+ border-radius: 100%;
111
+ color: var(--bgl-white);
112
+ z-index: 9;
113
+ pointer-events: none;
114
+ }
115
+
116
+ .single-image-item-preview:hover img {
117
+ filter: brightness(70%);
118
+ }
119
+
120
+ .bgl_fill-image.single-image-item-preview {
121
+ height: 100%;
122
+ }
123
+
124
+ .bgl_fill-image.single-image-item-preview .single-preview {
125
+ border-radius: unset;
126
+ object-fit: cover;
127
+ box-shadow: unset;
128
+ width: 100%;
129
+ height: auto;
130
+ }
131
+
132
+ .single-image-item-preview .pie {
133
+ transform-origin: top;
134
+ transform: scale(1.4);
135
+ position: absolute;
136
+ text-align: center;
137
+ inset: 0;
138
+ margin: auto;
139
+ display: flex;
140
+ justify-content: center;
141
+ align-items: center;
142
+ background: transparent;
143
+ border: none !important;
144
+ padding: 0 !important;
145
+ }
146
+
147
+ .bgl_oval-upload {
148
+ border-radius: 100% !important;
149
+ overflow: hidden;
150
+ }
151
+
152
+ .bgl_oval-upload p {
153
+ padding: 0.75rem !important;
154
+ font-size: 12px;
155
+ }
156
+
157
+ .bgl_oval-upload .fileUploadPlaceHolder {
158
+ top: 0;
159
+ }
160
+
161
+ .bgl_oval-upload .pie {
162
+ transform: scale(1);
163
+ }
164
+
165
+ .bgl_oval-upload span.bgl_icon-font.color-primary {
166
+ transform: scale(0.4) !important;
167
+ }
168
+
169
+ .bgl_oval-upload .single-image-item-preview {
170
+ height: 100%;
171
+ }
172
+
173
+ .bgl_oval-upload .single-preview {
174
+ margin: 0;
175
+ height: 100% !important;
176
+ }
177
+
178
+ .pie {
179
+ width: 30px;
180
+ height: 30px;
181
+ position: relative;
182
+ display: flex;
183
+ align-items: center;
184
+ justify-content: center;
185
+ }
186
+
187
+ .pie:before {
188
+ content: '';
189
+ position: absolute;
190
+ border-radius: 50%;
191
+ inset: 0;
192
+ transition: all 0.2s ease-in-out;
193
+ background: conic-gradient(var(--bgl-primary) calc(var(--p) * 1%), #0000 0);
194
+ -webkit-mask: radial-gradient(
195
+ farthest-side,
196
+ #0000 calc(99% - var(--b)),
197
+ #000 calc(100% - var(--b))
198
+ );
199
+ mask: radial-gradient(
200
+ farthest-side,
201
+ #0000 calc(99% - var(--b)),
202
+ #000 calc(100% - var(--b))
203
+ );
204
+ }
205
+
206
+ .pie .success {
207
+ transform: scale(0);
208
+ opacity: 0;
209
+ transition: all 0.3s ease-in-out;
210
+ }
211
+
212
+ .pie .progress {
213
+ position: absolute;
214
+ font-size: 10px;
215
+ }
216
+
217
+ .pie.complete .progress {
218
+ display: none;
219
+ }
220
+
221
+ .pie.complete .success {
222
+ transform: scale(1.3);
223
+ opacity: 1;
224
+ }
225
+
226
+ .pie.complete:before {
227
+ background: conic-gradient(var(--bgl-green) calc(var(--p) * 1%), #0000 0);
228
+ }
229
+
230
+ .pie.complete {
231
+ color: var(--bgl-green);
232
+ }
@@ -0,0 +1,22 @@
1
+ import type { AxiosResponse } from 'axios'
2
+ import type { BglFile, BglFilePatch, UploadOptions } from './upload.types.d.ts'
3
+ import axios from 'axios'
4
+
5
+ export const files = {
6
+ setBaseUrl: (baseUrl?: string) => {
7
+ if (!baseUrl) return
8
+ baseUrl = baseUrl.replace(/\/$/, '')
9
+ axios.defaults.baseURL = baseUrl
10
+ },
11
+ get: async (pathKey?: string): Promise<AxiosResponse<BglFile>> => { return axios.get('/files/', { params: { path_key: pathKey } }) },
12
+ put: async (bglFilePatch: BglFilePatch, pathKey?: string): Promise<AxiosResponse<BglFile>> => { return axios.put('/files/', bglFilePatch, { params: { path_key: pathKey } }) },
13
+ delete: async (pathKey?: string) => { return axios.delete('/files/', { params: { path_key: pathKey } }) },
14
+ upload: async (file: File, options?: UploadOptions & { dirPath?: string, tags?: string[] }): Promise<AxiosResponse<BglFile>> => {
15
+ const formData = new FormData()
16
+ formData.append('upload', file)
17
+ return axios.post('/files/upload', formData, {
18
+ headers: { 'Content-Type': 'multipart/form-data' },
19
+ onUploadProgress: options?.onUploadProgress,
20
+ params: { dir_path: options?.dirPath, tags: options?.tags },
21
+ }) }
22
+ }
@@ -0,0 +1,43 @@
1
+ export interface BglFile {
2
+ created_at?: string
3
+ updated_at?: string
4
+ path_key: string
5
+ base_url: string
6
+ name: string
7
+ mimetype?: string
8
+ size?: number
9
+ extension?: string
10
+ tags: string[]
11
+ url: string
12
+ }
13
+
14
+ export interface BglFilePatch {
15
+ name?: string
16
+ tags: string[]
17
+ mimetype?: string
18
+ extension?: string
19
+ size?: number
20
+ base_url?: string
21
+ }
22
+
23
+ export interface UploadFilesBglStaticFilesUpload {
24
+ upload: string
25
+ }
26
+
27
+ export interface UploadFilesS3FilesUpload {
28
+ upload: string
29
+ }
30
+
31
+ export interface UploadOptions {
32
+ onUploadProgress?: (progress: any) => void
33
+ dirPath?: string
34
+ tags?: string[]
35
+ }
36
+
37
+ interface QueueFile {
38
+ file: File
39
+ progress: number
40
+ uploading?: boolean
41
+ name?: string
42
+ uploaded?: boolean
43
+ }
@@ -19,3 +19,4 @@ export { default as TableField } from './TableField.vue'
19
19
  export { default as TelInput } from './TelInput.vue'
20
20
  export { default as TextInput } from './TextInput.vue'
21
21
  export { default as ToggleInput } from './ToggleInput.vue'
22
+ export { default as Upload } from './Upload/UploadFile.vue'
@@ -32,5 +32,7 @@ export { default as Pill } from './Pill.vue'
32
32
  export { default as RouterWrapper } from './RouterWrapper.vue'
33
33
  export { default as TableSchema } from './TableSchema.vue'
34
34
  export { default as Title } from './Title.vue'
35
+ export { default as ToolBar } from './ToolBar.vue'
35
36
  export { default as TopBar } from './TopBar.vue'
37
+
36
38
  export { default as Zoomer } from './Zoomer.vue'
@@ -29,14 +29,14 @@ export interface BagelOptions {
29
29
  defaultLang?: string
30
30
  language?: string
31
31
  host?: string
32
-
32
+ fileBaseUrl?: string
33
33
  onError?: (err: Error) => void
34
34
 
35
35
  i18nT?: (key: string) => string
36
36
  }
37
37
  export const BagelVue: Plugin = {
38
38
  install: (app, options: BagelOptions) => {
39
- const bagel = new Bagel({ host: options.host, onError: options.onError })
39
+ const bagel = new Bagel({ host: options.host, fileBaseUrl: options.fileBaseUrl, onError: options.onError })
40
40
  app.directive('click-outside', clickOutside)
41
41
  app.directive('ripple', ripple)
42
42
  app.directive('pattern', pattern)