@bagelink/vue 1.6.43 → 1.6.47
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/ModalForm.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts +12 -4
- package/dist/components/Swiper.vue.d.ts.map +1 -1
- package/dist/components/Zoomer.vue.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/index.cjs +10 -10
- package/dist/index.mjs +4178 -4134
- package/dist/plugins/modalTypes.d.ts +1 -8
- package/dist/plugins/modalTypes.d.ts.map +1 -1
- package/dist/plugins/useModal.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/AccordionItem.vue +3 -3
- package/src/components/ModalForm.vue +8 -5
- package/src/components/Swiper.vue +4 -4
- package/src/components/Zoomer.vue +191 -88
- package/src/components/form/inputs/RichText/utils/media.ts +7 -7
- package/src/components/layout/AppContent.vue +6 -3
- package/src/components/layout/AppSidebar.vue +7 -1
- package/src/components/lightbox/Lightbox.vue +270 -120
- package/src/plugins/modalTypes.ts +1 -8
- package/src/plugins/useModal.ts +43 -18
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { LightboxItem } from './lightbox.types'
|
|
3
3
|
|
|
4
|
-
import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL,
|
|
5
|
-
import { watch } from 'vue'
|
|
4
|
+
import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL, Swiper, downloadFile } from '@bagelink/vue'
|
|
5
|
+
import { watch as vueWatch } from 'vue'
|
|
6
6
|
|
|
7
7
|
let isOpen = $ref(false)
|
|
8
8
|
let group = $ref<LightboxItem[]>([])
|
|
9
9
|
let currentIndex = $ref(0)
|
|
10
|
-
|
|
10
|
+
const currentItem = $computed<LightboxItem>(() => group[currentIndex])
|
|
11
|
+
let zoom = $ref(1)
|
|
12
|
+
|
|
13
|
+
const canSwipe = $computed(() => zoom === 1)
|
|
14
|
+
|
|
15
|
+
// Advanced options that update reactively
|
|
16
|
+
const swiperAdvancedOptions = $computed(() => ({
|
|
17
|
+
allowTouchMove: zoom === 1,
|
|
18
|
+
touchRatio: zoom === 1 ? 1 : 0,
|
|
19
|
+
simulateTouch: zoom === 1,
|
|
20
|
+
}))
|
|
11
21
|
|
|
12
22
|
function open(item: LightboxItem, groupItems?: LightboxItem[]) {
|
|
13
23
|
isOpen = true
|
|
14
24
|
group = groupItems || [item]
|
|
15
|
-
currentIndex = group.findIndex((
|
|
25
|
+
currentIndex = group.findIndex((groupItem) => {
|
|
26
|
+
const hasSrcMatch = item.src !== undefined && item.src !== null && item.src !== ''
|
|
27
|
+
&& groupItem.src === item.src
|
|
28
|
+
const hasPathMatch = item.pathKey !== undefined && item.pathKey !== null && item.pathKey !== ''
|
|
29
|
+
&& groupItem.pathKey === item.pathKey
|
|
30
|
+
return hasSrcMatch || hasPathMatch
|
|
31
|
+
})
|
|
32
|
+
if (currentIndex === -1) currentIndex = 0
|
|
33
|
+
zoom = 1
|
|
16
34
|
document.addEventListener('keydown', handleKeydown)
|
|
17
35
|
}
|
|
18
36
|
|
|
@@ -21,107 +39,85 @@ function close() {
|
|
|
21
39
|
document.removeEventListener('keydown', handleKeydown)
|
|
22
40
|
}
|
|
23
41
|
|
|
24
|
-
function next() {
|
|
25
|
-
if (group.length > 1) {
|
|
26
|
-
currentIndex = (currentIndex + 1) % group.length
|
|
27
|
-
currentItem = group[currentIndex]
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function prev() {
|
|
32
|
-
if (group.length > 1) {
|
|
33
|
-
currentIndex = (currentIndex - 1 + group.length) % group.length
|
|
34
|
-
currentItem = group[currentIndex]
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
42
|
function selectItem(index: number) {
|
|
39
43
|
currentIndex = index
|
|
40
|
-
|
|
44
|
+
zoom = 1
|
|
45
|
+
// The v-model will handle updating the swiper
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
if (val) {
|
|
45
|
-
|
|
48
|
+
vueWatch(() => isOpen, (val) => {
|
|
49
|
+
if (val) {
|
|
50
|
+
document.body.style.overflow = 'hidden'
|
|
51
|
+
} else {
|
|
52
|
+
document.body.style.overflow = ''
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
vueWatch(() => currentIndex, () => {
|
|
57
|
+
// Reset zoom when changing slides
|
|
58
|
+
zoom = 1
|
|
46
59
|
})
|
|
47
60
|
|
|
48
61
|
function handleKeydown(event: KeyboardEvent) {
|
|
49
62
|
if (event.key === 'Escape') {
|
|
50
63
|
close()
|
|
51
|
-
} else if (event.key === 'ArrowLeft') {
|
|
52
|
-
prev()
|
|
53
|
-
} else if (event.key === 'ArrowRight') {
|
|
54
|
-
next()
|
|
55
64
|
}
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
const zoom = $ref(1)
|
|
59
|
-
|
|
60
|
-
function clickOutside() {
|
|
61
|
-
if (zoom === 1) { close() }
|
|
62
|
-
}
|
|
63
|
-
|
|
64
67
|
defineExpose({ open, close })
|
|
65
68
|
</script>
|
|
66
69
|
|
|
67
70
|
<template>
|
|
68
71
|
<transition name="fade">
|
|
69
|
-
<div
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class="navigation flex space-between px-3 w-100 absolute m_px-1 m_none z-9"
|
|
77
|
-
>
|
|
78
|
-
<Btn class="oval opacity-8" icon="arrow_back" color="black" @click="prev" />
|
|
79
|
-
|
|
80
|
-
<Btn class="oval opacity-8" icon="arrow_forward" color="black" @click="next" />
|
|
81
|
-
</div>
|
|
82
|
-
<div class="bgl-lightbox relative txt-center" @click.stop>
|
|
83
|
-
<div class="flex start fixed top-1 w-100 space-between px-1 z-9">
|
|
84
|
-
<Btn flat class="color-white" icon="close" @click="close" />
|
|
85
|
-
<div v-if="currentItem?.enableZoom && currentItem?.type === 'image'" class="center">
|
|
86
|
-
<Btn flat class="color-white" icon="remove" :disabled="zoom === 1" @click="zoom--" />
|
|
87
|
-
<Btn flat class="color-white" icon="zoom_in" :disabled="zoom === 1" @click="zoom = 1" />
|
|
88
|
-
<Btn flat class="color-white" icon="add" :disabled="zoom === 3" @click="zoom++" />
|
|
89
|
-
</div>
|
|
90
|
-
<Btn
|
|
91
|
-
v-if="currentItem?.openFile && currentItem?.src" class="color-white" round thin flat
|
|
92
|
-
iconEnd="arrow_outward" value="Open File" :href="currentItem?.src" target="_blank"
|
|
93
|
-
/>
|
|
94
|
-
<Btn
|
|
95
|
-
v-if="currentItem?.download && currentItem?.src" class="color-white" round thin flat
|
|
96
|
-
icon="download" value="Download File" @click="downloadFile(currentItem?.src)"
|
|
97
|
-
/>
|
|
98
|
-
<div v-if="!currentItem?.openFile && !currentItem?.download" />
|
|
72
|
+
<div v-if="isOpen" class="bgl-lightbox-overlay" @keydown.esc="close">
|
|
73
|
+
<div class="flex start w-100 space-between p-025 z-9" @click="close">
|
|
74
|
+
<Btn flat class="color-white" icon="close" @click="close" />
|
|
75
|
+
<div v-if="currentItem?.enableZoom && currentItem?.type === 'image'" class="center">
|
|
76
|
+
<Btn flat class="color-white" icon="remove" :disabled="zoom === 1" @click="zoom--" />
|
|
77
|
+
<Btn flat class="color-white" icon="zoom_in" :disabled="zoom === 1" @click="zoom = 1" />
|
|
78
|
+
<Btn flat class="color-white" icon="add" :disabled="zoom === 3" @click="zoom++" />
|
|
99
79
|
</div>
|
|
80
|
+
<Btn
|
|
81
|
+
v-if="currentItem?.openFile && currentItem?.src" class="color-white" round thin flat
|
|
82
|
+
iconEnd="arrow_outward" value="Open File" :href="currentItem?.src" target="_blank"
|
|
83
|
+
/>
|
|
84
|
+
<Btn
|
|
85
|
+
v-if="currentItem?.download && currentItem?.src" class="color-white" round thin flat
|
|
86
|
+
icon="download" value="Download File" @click="downloadFile(currentItem?.src)"
|
|
87
|
+
/>
|
|
88
|
+
<div v-if="!currentItem?.openFile && !currentItem?.download" />
|
|
89
|
+
</div>
|
|
100
90
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
>
|
|
105
|
-
|
|
91
|
+
<Swiper
|
|
92
|
+
v-if="group && group.length > 0" v-model:index="currentIndex" :items="group"
|
|
93
|
+
:initial-slide="currentIndex" class="bgl-lightbox-swiper" :class="{ zoomed: zoom > 1 }"
|
|
94
|
+
:navigation="group.length > 1" :grab-cursor="canSwipe" :keyboard="true" :loop="false" :speed="400"
|
|
95
|
+
:slides-per-view="1" :space-between="0" effect="slide" :advanced-options="swiperAdvancedOptions"
|
|
96
|
+
@click.stop
|
|
97
|
+
>
|
|
98
|
+
<template #default="{ item }">
|
|
99
|
+
<div class="bgl-lightbox-item">
|
|
106
100
|
<Zoomer
|
|
107
101
|
v-if="item.type === 'image'" v-model:zoom="zoom" :disabled="!item?.enableZoom"
|
|
108
|
-
:mouse-wheel-to-zoom="false"
|
|
102
|
+
:mouse-wheel-to-zoom="false" :double-click-to-zoom="true" :max-scale="5" :min-scale="1"
|
|
103
|
+
:aspect-ratio="0" :limit-translation="true" @click.stop
|
|
109
104
|
>
|
|
110
|
-
<Image :draggable="false" :src="item?.src" alt="Preview" class="
|
|
105
|
+
<Image :draggable="false" :src="item?.src" alt="Preview" class="lightbox-image" />
|
|
111
106
|
</Zoomer>
|
|
112
107
|
|
|
113
108
|
<BglVideo
|
|
114
109
|
v-else-if="item?.type === 'video' && item?.src" :src="item?.src" autoplay controls
|
|
115
|
-
class="
|
|
110
|
+
class="lightbox-video"
|
|
116
111
|
/>
|
|
117
112
|
|
|
118
|
-
<div v-else-if="item?.type === 'pdf' && item?.src" class="
|
|
113
|
+
<div v-else-if="item?.type === 'pdf' && item?.src" class="lightbox-pdf">
|
|
119
114
|
<embed
|
|
120
115
|
:src="normalizeURL(item?.src)" type="application/pdf" width="100%" height="1080"
|
|
121
|
-
:title="item?.name"
|
|
116
|
+
:title="item?.name"
|
|
122
117
|
>
|
|
123
118
|
</div>
|
|
124
|
-
|
|
119
|
+
|
|
120
|
+
<div v-else class="lightbox-file">
|
|
125
121
|
<div class="file-info txt-white flex m_block align-items-start gap-025">
|
|
126
122
|
<Icon class="m-0 m_none" icon="draft" :size="10" weight="12" />
|
|
127
123
|
<Icon class="m-0 none m_block m_-mb-1" icon="draft" :size="4" weight="2" />
|
|
@@ -129,40 +125,39 @@ defineExpose({ open, close })
|
|
|
129
125
|
<div class="txt-start">
|
|
130
126
|
<p class="mx-0 light">
|
|
131
127
|
File:
|
|
132
|
-
<span class="semi word-break-all
|
|
133
|
-
{{ item?.name }}
|
|
134
|
-
</span>
|
|
128
|
+
<span class="semi word-break-all">{{ item?.name }}</span>
|
|
135
129
|
</p>
|
|
136
|
-
<p class="mx-0
|
|
130
|
+
<p class="mx-0">
|
|
137
131
|
Type:
|
|
138
|
-
<span class="semi">
|
|
139
|
-
{{ item?.type }}
|
|
140
|
-
</span>
|
|
132
|
+
<span class="semi">{{ item?.type }}</span>
|
|
141
133
|
</p>
|
|
142
134
|
<Btn :href="item?.src" target="_blank" round thin class="mt-1" value="Open file" />
|
|
143
|
-
<!-- <a :href="currentItem?.src" target="_blank">Open file</a> -->
|
|
144
135
|
</div>
|
|
145
136
|
</div>
|
|
146
137
|
</div>
|
|
147
|
-
</
|
|
148
|
-
</
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
138
|
+
</div>
|
|
139
|
+
</template>
|
|
140
|
+
|
|
141
|
+
<template #prev-button="{ prev }">
|
|
142
|
+
<Btn icon="arrow_back" color="black" @click="prev" />
|
|
143
|
+
</template>
|
|
144
|
+
|
|
145
|
+
<template #next-button="{ next }">
|
|
146
|
+
<Btn icon="arrow_forward" color="black" @click="next" />
|
|
147
|
+
</template>
|
|
148
|
+
</Swiper>
|
|
149
|
+
|
|
150
|
+
<div v-if="group && group.length > 1" class="lightbox-thumbnails" @click.stop>
|
|
151
|
+
<template v-for="(item, index) in group" :key="index">
|
|
152
|
+
<Image
|
|
153
|
+
v-if="item.type === 'image'" class="thumbnail" :src="item.src" alt=""
|
|
154
|
+
:class="{ active: currentIndex === index }" @click.stop="selectItem(index)"
|
|
155
|
+
/>
|
|
156
|
+
<Icon
|
|
157
|
+
v-else class="thumbnail thumbnail-icon" icon="description"
|
|
158
|
+
:class="{ active: currentIndex === index }" @click.stop="selectItem(index)"
|
|
159
|
+
/>
|
|
160
|
+
</template>
|
|
166
161
|
</div>
|
|
167
162
|
</div>
|
|
168
163
|
</transition>
|
|
@@ -189,70 +184,225 @@ defineExpose({ open, close })
|
|
|
189
184
|
}
|
|
190
185
|
|
|
191
186
|
.bgl-lightbox-overlay {
|
|
187
|
+
position: fixed;
|
|
188
|
+
width: 100vw;
|
|
189
|
+
height: 100vh;
|
|
190
|
+
display: grid;
|
|
191
|
+
grid-template-rows: 50px 1fr 100px;
|
|
192
|
+
z-index: 9999;
|
|
193
|
+
inset: 0;
|
|
192
194
|
background: rgba(0, 0, 0, 0.8);
|
|
195
|
+
overflow: hidden;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Top controls row */
|
|
199
|
+
.bgl-lightbox-overlay>.flex:first-child {
|
|
200
|
+
grid-row: 1;
|
|
201
|
+
align-self: start;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Main content row - Swiper */
|
|
205
|
+
.bgl-lightbox-swiper {
|
|
206
|
+
grid-row: 2;
|
|
207
|
+
width: 100vw;
|
|
208
|
+
height: 100%;
|
|
209
|
+
overflow: hidden;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Bottom thumbnails row */
|
|
213
|
+
.lightbox-thumbnails {
|
|
214
|
+
grid-row: 3;
|
|
215
|
+
align-self: end;
|
|
193
216
|
}
|
|
194
217
|
|
|
195
|
-
.bgl-lightbox {
|
|
196
|
-
|
|
218
|
+
.bgl-lightbox-swiper .swiper {
|
|
219
|
+
width: 100%;
|
|
220
|
+
height: 100%;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.bgl-lightbox-swiper .swiper-wrapper {
|
|
224
|
+
align-items: center;
|
|
197
225
|
}
|
|
198
226
|
|
|
199
227
|
.bgl-lightbox-item {
|
|
200
|
-
|
|
228
|
+
display: flex;
|
|
229
|
+
align-items: center;
|
|
230
|
+
justify-content: center;
|
|
231
|
+
width: 100%;
|
|
232
|
+
height: 100%;
|
|
233
|
+
animation: 200ms ease bgl-lightbox-load;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* Navigation button styling */
|
|
237
|
+
.lightbox-nav-btn {
|
|
238
|
+
background: rgba(0, 0, 0, 0.6);
|
|
239
|
+
backdrop-filter: blur(8px);
|
|
240
|
+
border-radius: 50%;
|
|
241
|
+
width: 48px;
|
|
242
|
+
height: 48px;
|
|
243
|
+
display: flex;
|
|
244
|
+
align-items: center;
|
|
245
|
+
justify-content: center;
|
|
246
|
+
color: white;
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
transition: all 0.2s ease;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.lightbox-nav-btn:hover {
|
|
252
|
+
background: rgba(0, 0, 0, 0.8);
|
|
253
|
+
transform: scale(1.05);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.lightbox-nav-btn:active {
|
|
257
|
+
transform: scale(0.95);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Adjust swiper navigation positioning */
|
|
261
|
+
.bgl-lightbox-swiper :deep(.swi-ctrl) {
|
|
262
|
+
padding: 0 1.5rem !important;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@media screen and (max-width: 900px) {
|
|
266
|
+
.bgl-lightbox-swiper :deep(.swi-ctrl) {
|
|
267
|
+
padding: 0 1rem !important;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.lightbox-nav-btn {
|
|
271
|
+
width: 40px;
|
|
272
|
+
height: 40px;
|
|
273
|
+
}
|
|
201
274
|
}
|
|
202
275
|
|
|
203
276
|
@keyframes bgl-lightbox-load {
|
|
204
277
|
from {
|
|
205
|
-
|
|
278
|
+
opacity: 0;
|
|
279
|
+
transform: scale(0.95);
|
|
206
280
|
}
|
|
207
281
|
|
|
208
282
|
to {
|
|
209
|
-
|
|
210
|
-
|
|
283
|
+
opacity: 1;
|
|
284
|
+
transform: scale(1);
|
|
211
285
|
}
|
|
212
286
|
}
|
|
213
287
|
|
|
214
|
-
.
|
|
215
|
-
|
|
288
|
+
.lightbox-image,
|
|
289
|
+
.lightbox-video,
|
|
290
|
+
.lightbox-pdf,
|
|
291
|
+
.lightbox-file {
|
|
292
|
+
max-width: 90vw;
|
|
293
|
+
max-height: calc(100vh - 140px);
|
|
294
|
+
object-fit: contain;
|
|
216
295
|
border-radius: 3px;
|
|
217
296
|
margin: auto;
|
|
218
|
-
animation: 200ms ease bgl-lightbox-load;
|
|
219
|
-
transition: max-height 200ms ease;
|
|
220
297
|
}
|
|
221
298
|
|
|
222
|
-
.
|
|
299
|
+
.lightbox-image {
|
|
300
|
+
width: auto;
|
|
301
|
+
height: auto;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* When zoomed, make image full width */
|
|
305
|
+
.bgl-lightbox-swiper.zoomed .lightbox-image {
|
|
306
|
+
max-width: 100vw;
|
|
307
|
+
width: 100vw;
|
|
308
|
+
max-height: 100vh;
|
|
309
|
+
height: 100vh;
|
|
310
|
+
object-fit: contain;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.bgl-lightbox-swiper.zoomed .vue-zoomer {
|
|
314
|
+
width: 100vw;
|
|
315
|
+
height: 100vh;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.bgl-lightbox-swiper.zoomed .bgl-lightbox-item * {
|
|
223
319
|
max-height: calc(100vh - 90px);
|
|
224
320
|
height: calc(100vh - 90px);
|
|
225
321
|
}
|
|
226
322
|
|
|
227
|
-
.bgl-lightbox-
|
|
323
|
+
.bgl-lightbox-swiper.zoomed {
|
|
228
324
|
pointer-events: none;
|
|
229
325
|
}
|
|
230
326
|
|
|
231
|
-
.bgl-lightbox-
|
|
327
|
+
.bgl-lightbox-swiper.zoomed .vue-zoomer {
|
|
232
328
|
pointer-events: auto;
|
|
233
329
|
}
|
|
234
330
|
|
|
235
|
-
.
|
|
236
|
-
|
|
237
|
-
|
|
331
|
+
.lightbox-thumbnails {
|
|
332
|
+
display: flex;
|
|
333
|
+
justify-content: center;
|
|
334
|
+
align-items: center;
|
|
335
|
+
gap: 0.75rem;
|
|
336
|
+
padding: 1rem;
|
|
337
|
+
overflow-x: auto;
|
|
338
|
+
overflow-y: hidden;
|
|
339
|
+
background: linear-gradient(to top, rgba(0, 0, 0, 0.4) 0%, transparent 100%);
|
|
340
|
+
width: 100%;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.lightbox-thumbnails::-webkit-scrollbar {
|
|
344
|
+
height: 4px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.lightbox-thumbnails::-webkit-scrollbar-track {
|
|
348
|
+
background: transparent;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.lightbox-thumbnails::-webkit-scrollbar-thumb {
|
|
352
|
+
background: rgba(255, 255, 255, 0.3);
|
|
353
|
+
border-radius: 2px;
|
|
238
354
|
}
|
|
239
355
|
|
|
240
356
|
.thumbnail {
|
|
241
|
-
height:
|
|
242
|
-
width:
|
|
357
|
+
height: 60px;
|
|
358
|
+
width: 60px;
|
|
359
|
+
min-width: 60px;
|
|
360
|
+
object-fit: cover;
|
|
361
|
+
border-radius: 6px;
|
|
362
|
+
cursor: pointer;
|
|
363
|
+
opacity: 0.6;
|
|
364
|
+
transition: all 0.2s ease;
|
|
365
|
+
flex-shrink: 0;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.thumbnail-icon {
|
|
369
|
+
display: flex;
|
|
370
|
+
align-items: center;
|
|
371
|
+
justify-content: center;
|
|
372
|
+
background: rgba(255, 255, 255, 0.1);
|
|
243
373
|
}
|
|
244
374
|
|
|
245
375
|
.thumbnail:hover {
|
|
246
376
|
opacity: 1;
|
|
377
|
+
transform: scale(1.05);
|
|
247
378
|
}
|
|
248
379
|
|
|
249
380
|
.thumbnail:active {
|
|
250
|
-
|
|
381
|
+
transform: scale(0.95);
|
|
251
382
|
}
|
|
252
383
|
|
|
253
384
|
.thumbnail.active {
|
|
254
385
|
opacity: 1;
|
|
255
|
-
outline:
|
|
386
|
+
outline: 3px solid white;
|
|
387
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
@media screen and (max-width: 910px) {
|
|
391
|
+
.bgl-lightbox-overlay {
|
|
392
|
+
grid-template-rows: 50px 1fr 70px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.lightbox-thumbnails {
|
|
396
|
+
justify-content: flex-start;
|
|
397
|
+
gap: 0.5rem;
|
|
398
|
+
padding: 0.75rem 1rem;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.thumbnail {
|
|
402
|
+
height: 50px;
|
|
403
|
+
width: 50px;
|
|
404
|
+
min-width: 50px;
|
|
405
|
+
}
|
|
256
406
|
}
|
|
257
407
|
|
|
258
408
|
.file-info {
|
|
@@ -46,7 +46,7 @@ export interface ModalFormComponentProps<T extends { [key: string]: any }> exten
|
|
|
46
46
|
modalOptions: ModalFormOptions<T>
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export interface ModalFormOptions<T> {
|
|
49
|
+
export interface ModalFormOptions<T> extends ModalOptions {
|
|
50
50
|
'schema': MaybeRefOrGetter<BglFormSchemaT<T>>
|
|
51
51
|
'modelValue'?: T
|
|
52
52
|
'onUpdate:modelValue'?: (val: T) => void
|
|
@@ -58,11 +58,4 @@ export interface ModalFormOptions<T> {
|
|
|
58
58
|
'deleteText'?: string
|
|
59
59
|
'duplicateText'?: string
|
|
60
60
|
'onError'?: (err: any) => void
|
|
61
|
-
'side'?: boolean
|
|
62
|
-
'title'?: string
|
|
63
|
-
'width'?: string
|
|
64
|
-
'dismissable'?: boolean
|
|
65
|
-
'visible'?: boolean
|
|
66
|
-
'actions'?: BtnOptions[]
|
|
67
|
-
'class'?: string
|
|
68
61
|
}
|
package/src/plugins/useModal.ts
CHANGED
|
@@ -80,6 +80,11 @@ export const ModalPlugin: Plugin = {
|
|
|
80
80
|
// Make options reactive so updates propagate
|
|
81
81
|
const reactiveOptions = reactive(options) as ModalOptions | ModalFormOptions<T>
|
|
82
82
|
|
|
83
|
+
// Ensure visible is set for modals
|
|
84
|
+
if (!('visible' in reactiveOptions)) {
|
|
85
|
+
reactiveOptions.visible = true
|
|
86
|
+
}
|
|
87
|
+
|
|
83
88
|
const modalComponent = {
|
|
84
89
|
modalOptions: reactiveOptions,
|
|
85
90
|
modalType,
|
|
@@ -147,6 +152,11 @@ export const ModalPlugin: Plugin = {
|
|
|
147
152
|
// Calculate z-index based on stack position
|
|
148
153
|
const zIndex = BASE_Z_INDEX + index * 10
|
|
149
154
|
|
|
155
|
+
if (!modal || !modal.modalOptions) {
|
|
156
|
+
console.error('[BageLink Modal] Invalid modal in stack:', modal)
|
|
157
|
+
return null
|
|
158
|
+
}
|
|
159
|
+
|
|
150
160
|
const props = {
|
|
151
161
|
...modal.modalOptions,
|
|
152
162
|
'visible': true,
|
|
@@ -155,8 +165,14 @@ export const ModalPlugin: Plugin = {
|
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
switch (modal.modalType) {
|
|
158
|
-
case 'modalForm':
|
|
168
|
+
case 'modalForm': {
|
|
169
|
+
const formOptions = modal.modalOptions as ModalFormOptions<any>
|
|
170
|
+
if (!formOptions.schema) {
|
|
171
|
+
console.error('[BageLink Modal] ModalForm requires a schema', modal.modalOptions)
|
|
172
|
+
return null
|
|
173
|
+
}
|
|
159
174
|
return h(ModalForm, props as ComponentProps<typeof ModalForm>, modal.componentSlots)
|
|
175
|
+
}
|
|
160
176
|
case 'confirmModal':
|
|
161
177
|
return h(ModalConfirm, props as ModalConfirmOptions, {})
|
|
162
178
|
default:
|
|
@@ -171,27 +187,36 @@ export const ModalPlugin: Plugin = {
|
|
|
171
187
|
|
|
172
188
|
// Auto-mount modal container to document.body
|
|
173
189
|
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
|
|
174
|
-
|
|
190
|
+
const mountModalContainer = () => {
|
|
191
|
+
const existingContainer = document.getElementById('bagelink-modal-root')
|
|
192
|
+
if (!existingContainer) {
|
|
193
|
+
// Create mount point
|
|
194
|
+
const container = document.createElement('div')
|
|
195
|
+
container.id = 'bagelink-modal-root'
|
|
196
|
+
document.body.appendChild(container)
|
|
197
|
+
|
|
198
|
+
// Create a separate app instance for modals to avoid context issues
|
|
199
|
+
const modalApp = createApp(ModalContainerComponent)
|
|
200
|
+
|
|
201
|
+
// Share the same context/plugins
|
|
202
|
+
modalApp._context = app._context
|
|
203
|
+
|
|
204
|
+
// Mount it
|
|
205
|
+
modalApp.mount(container)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Try to mount immediately if DOM is ready
|
|
210
|
+
if (document.body) {
|
|
211
|
+
mountModalContainer()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Also use mixin as fallback for cases where plugin is installed before DOM ready
|
|
175
215
|
app.mixin({
|
|
176
216
|
mounted() {
|
|
177
217
|
// Only run once on the root component
|
|
178
218
|
if (this.$root === this) {
|
|
179
|
-
|
|
180
|
-
if (!existingContainer) {
|
|
181
|
-
// Create mount point
|
|
182
|
-
const container = document.createElement('div')
|
|
183
|
-
container.id = 'bagelink-modal-root'
|
|
184
|
-
document.body.appendChild(container)
|
|
185
|
-
|
|
186
|
-
// Create a separate app instance for modals to avoid context issues
|
|
187
|
-
const modalApp = createApp(ModalContainerComponent)
|
|
188
|
-
|
|
189
|
-
// Share the same context/plugins
|
|
190
|
-
modalApp._context = app._context
|
|
191
|
-
|
|
192
|
-
// Mount it
|
|
193
|
-
modalApp.mount(container)
|
|
194
|
-
}
|
|
219
|
+
mountModalContainer()
|
|
195
220
|
}
|
|
196
221
|
}
|
|
197
222
|
})
|