@bagelink/vue 1.2.93 → 1.2.99
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.
- package/dist/components/Pill.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts +7 -8
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/Upload/useFileUpload.d.ts +76 -0
- package/dist/components/form/inputs/Upload/useFileUpload.d.ts.map +1 -0
- package/dist/components/form/inputs/index.d.ts +1 -0
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/index.cjs +236 -165
- package/dist/index.mjs +236 -165
- package/dist/style.css +67 -67
- package/package.json +1 -1
- package/src/components/Pill.vue +1 -1
- package/src/components/form/inputs/Upload/UploadInput.vue +26 -101
- package/src/components/form/inputs/Upload/useFileUpload.ts +144 -0
- package/src/components/form/inputs/index.ts +2 -0
- package/src/components/lightbox/Lightbox.vue +41 -26
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { BglFile, QueueFile } from './upload.types'
|
|
2
|
+
import { useBagel } from '@bagelink/vue'
|
|
3
|
+
import { ref, computed, onMounted } from 'vue'
|
|
4
|
+
import { files } from './upload'
|
|
5
|
+
|
|
6
|
+
interface UseFileUploadProps {
|
|
7
|
+
multiple?: boolean
|
|
8
|
+
dirPath?: string
|
|
9
|
+
accept?: string
|
|
10
|
+
disabled?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useFileUpload(props: UseFileUploadProps = {}) {
|
|
14
|
+
files.setBaseUrl(useBagel().host)
|
|
15
|
+
|
|
16
|
+
const fileQueue = ref<QueueFile[]>([])
|
|
17
|
+
const storageFiles = ref<BglFile[]>([])
|
|
18
|
+
const pk = ref<string[]>([])
|
|
19
|
+
|
|
20
|
+
// Computed
|
|
21
|
+
const pathKeys = computed(() => {
|
|
22
|
+
const storagePathKeys = storageFiles.value.map(file => file.path_key)
|
|
23
|
+
return [...pk.value, ...storagePathKeys]
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// File handling methods
|
|
27
|
+
const fileToUrl = (file: File) => URL.createObjectURL(file)
|
|
28
|
+
|
|
29
|
+
const addFile = (file?: File | File[] | FileList | null) => {
|
|
30
|
+
if (!file) return
|
|
31
|
+
|
|
32
|
+
let filesToAdd: File[] = []
|
|
33
|
+
|
|
34
|
+
if (file instanceof File) {
|
|
35
|
+
filesToAdd = [file]
|
|
36
|
+
} else if (file instanceof FileList) {
|
|
37
|
+
filesToAdd = Array.from(file)
|
|
38
|
+
} else if (Array.isArray(file)) {
|
|
39
|
+
filesToAdd = file
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const newQueueFiles = filesToAdd.map(f => ({
|
|
43
|
+
name: f.name,
|
|
44
|
+
file: f,
|
|
45
|
+
progress: 0
|
|
46
|
+
}))
|
|
47
|
+
|
|
48
|
+
fileQueue.value.push(...newQueueFiles)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const removeFile = async (pathKeyOrFile: string | File) => {
|
|
52
|
+
if (typeof pathKeyOrFile === 'string') {
|
|
53
|
+
// Remove from both lists
|
|
54
|
+
storageFiles.value = storageFiles.value.filter(file => file.path_key !== pathKeyOrFile)
|
|
55
|
+
|
|
56
|
+
const pathKeyIndex = pk.value.indexOf(pathKeyOrFile)
|
|
57
|
+
if (pathKeyIndex !== -1) {
|
|
58
|
+
pk.value.splice(pathKeyIndex, 1)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await files.delete(pathKeyOrFile)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error('Error deleting file:', error)
|
|
65
|
+
}
|
|
66
|
+
} else if (pathKeyOrFile) {
|
|
67
|
+
const index = fileQueue.value.findIndex(({ file }) => file.name === pathKeyOrFile.name)
|
|
68
|
+
if (index !== -1) {
|
|
69
|
+
fileQueue.value.splice(index, 1)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const flushQueue = async () => {
|
|
75
|
+
for (const file of fileQueue.value) {
|
|
76
|
+
file.uploading = true
|
|
77
|
+
|
|
78
|
+
// If not multiple, replace the existing file
|
|
79
|
+
if (!props.multiple) {
|
|
80
|
+
pk.value.splice(0, 1)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const { data } = await files.upload(file.file, {
|
|
85
|
+
onUploadProgress: (e: ProgressEvent) => {
|
|
86
|
+
file.progress = (e.loaded / e.total) * 100 - 1
|
|
87
|
+
},
|
|
88
|
+
dirPath: props.dirPath,
|
|
89
|
+
})
|
|
90
|
+
pk.value.push(data.path_key)
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error('Error uploading file:', error)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Clear the queue after processing
|
|
97
|
+
fileQueue.value = []
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// UI interaction
|
|
101
|
+
const browse = (upload = false) => {
|
|
102
|
+
if (props.disabled) return
|
|
103
|
+
|
|
104
|
+
const input = document.createElement('input')
|
|
105
|
+
input.type = 'file'
|
|
106
|
+
input.multiple = !!props.multiple
|
|
107
|
+
input.accept = props.accept || ''
|
|
108
|
+
|
|
109
|
+
input.onchange = (e: Event) => {
|
|
110
|
+
addFile((e.target as HTMLInputElement).files)
|
|
111
|
+
if (upload) {
|
|
112
|
+
flushQueue()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
input.click()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Load initial files
|
|
120
|
+
onMounted(() => {
|
|
121
|
+
if (props.dirPath) {
|
|
122
|
+
files.list(props.dirPath)
|
|
123
|
+
.then((response) => {
|
|
124
|
+
const responseData = Array.isArray(response.data)
|
|
125
|
+
? response.data
|
|
126
|
+
: [response.data]
|
|
127
|
+
storageFiles.value.push(...responseData)
|
|
128
|
+
})
|
|
129
|
+
.catch((error) => { console.error('Error loading files:', error) })
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
fileQueue,
|
|
135
|
+
storageFiles,
|
|
136
|
+
pk,
|
|
137
|
+
pathKeys,
|
|
138
|
+
fileToUrl,
|
|
139
|
+
removeFile,
|
|
140
|
+
flushQueue,
|
|
141
|
+
addFile,
|
|
142
|
+
browse,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -21,3 +21,5 @@ export { default as TelInput } from './TelInput.vue'
|
|
|
21
21
|
export { default as TextInput } from './TextInput.vue'
|
|
22
22
|
export { default as ToggleInput } from './ToggleInput.vue'
|
|
23
23
|
export { default as UploadInput } from './Upload/UploadInput.vue'
|
|
24
|
+
|
|
25
|
+
export { useFileUpload } from './Upload/useFileUpload'
|
|
@@ -63,6 +63,16 @@ function clickOutside() {
|
|
|
63
63
|
|
|
64
64
|
const upgradeHeaders = (url: string) => url.replace(/http:\/\//, '//')
|
|
65
65
|
|
|
66
|
+
function downloadFile() {
|
|
67
|
+
const link = document.createElement('a')
|
|
68
|
+
const src = currentItem.src || ''
|
|
69
|
+
link.target = '_blank'
|
|
70
|
+
link.href = upgradeHeaders(src)
|
|
71
|
+
link.download = src ? src.split('/').pop() || 'download' : 'download'
|
|
72
|
+
document.body.appendChild(link)
|
|
73
|
+
link.click()
|
|
74
|
+
document.body.removeChild(link)
|
|
75
|
+
}
|
|
66
76
|
defineExpose({ open, close })
|
|
67
77
|
</script>
|
|
68
78
|
|
|
@@ -108,8 +118,7 @@ defineExpose({ open, close })
|
|
|
108
118
|
<Btn
|
|
109
119
|
v-if="currentItem?.download && currentItem?.src" class="color-white" round thin flat icon="download"
|
|
110
120
|
value="Download File"
|
|
111
|
-
|
|
112
|
-
download
|
|
121
|
+
@click="downloadFile"
|
|
113
122
|
/>
|
|
114
123
|
<div v-if="!currentItem?.openFile && !currentItem?.download" />
|
|
115
124
|
</div>
|
|
@@ -128,34 +137,40 @@ defineExpose({ open, close })
|
|
|
128
137
|
class="vw90"
|
|
129
138
|
/>
|
|
130
139
|
|
|
131
|
-
<
|
|
140
|
+
<div
|
|
132
141
|
v-else-if="item?.type === 'pdf' && item?.src"
|
|
133
|
-
:src="normalizeURL(item?.src)"
|
|
134
|
-
type="application/pdf"
|
|
135
|
-
width="100%"
|
|
136
|
-
height="1080"
|
|
137
|
-
:title="item?.name"
|
|
138
142
|
class="vw90"
|
|
139
143
|
>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
<embed
|
|
145
|
+
:src="normalizeURL(item?.src)"
|
|
146
|
+
type="application/pdf"
|
|
147
|
+
width="100%"
|
|
148
|
+
height="1080"
|
|
149
|
+
:title="item?.name"
|
|
150
|
+
class="vw90"
|
|
151
|
+
>
|
|
152
|
+
</div>
|
|
153
|
+
<div v-else class="vw90">
|
|
154
|
+
<div class="file-info txt-white flex m_block align-items-start gap-025">
|
|
155
|
+
<Icon class="m-0 m_none" icon="draft" :size="10" weight="12" />
|
|
156
|
+
<Icon class="m-0 none m_block m_-mb-1" icon="draft" :size="4" weight="2" />
|
|
143
157
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
<div class="txt-start">
|
|
159
|
+
<p class="mx-0 light">
|
|
160
|
+
File:
|
|
161
|
+
<span class="semi word-break-all ">
|
|
162
|
+
{{ item?.name }}
|
|
163
|
+
</span>
|
|
164
|
+
</p>
|
|
165
|
+
<p class="mx-0 ">
|
|
166
|
+
Type:
|
|
167
|
+
<span class="semi">
|
|
168
|
+
{{ item?.type }}
|
|
169
|
+
</span>
|
|
170
|
+
</p>
|
|
171
|
+
<Btn :href="item?.src" target="_blank" round thin class="mt-1" value="Open file" />
|
|
172
|
+
<!-- <a :href="currentItem?.src" target="_blank">Open file</a> -->
|
|
173
|
+
</div>
|
|
159
174
|
</div>
|
|
160
175
|
</div>
|
|
161
176
|
</template>
|