@bagelink/vue 0.0.753 → 0.0.757
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/Zoomer.vue.d.ts +40 -0
- package/dist/components/Zoomer.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/Tabs.vue.d.ts +2 -0
- package/dist/components/layout/Tabs.vue.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/components/lightbox/lightbox.types.d.ts +2 -0
- package/dist/components/lightbox/lightbox.types.d.ts.map +1 -1
- package/dist/index.cjs +732 -255
- package/dist/index.mjs +732 -255
- package/dist/style.css +141 -97
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/tapDetector.d.ts +30 -0
- package/dist/utils/tapDetector.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/Btn.vue +1 -1
- package/src/components/Zoomer.vue +377 -0
- package/src/components/form/inputs/RadioGroup.vue +2 -2
- package/src/components/form/inputs/RichText2/Toolbar.vue +1 -1
- package/src/components/form/inputs/RichText2/index.vue +3 -3
- package/src/components/form/inputs/ToggleInput.vue +1 -1
- package/src/components/formkit/Toggle.vue +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/SidebarMenu.vue +1 -1
- package/src/components/layout/Tabs.vue +6 -3
- package/src/components/lightbox/Lightbox.vue +30 -6
- package/src/components/lightbox/index.ts +2 -2
- package/src/components/lightbox/lightbox.types.ts +2 -0
- package/src/styles/layout.css +18 -2
- package/src/styles/mobilLayout.css +16 -2
- package/src/utils/BagelFormUtils.ts +1 -1
- package/src/utils/index.ts +12 -4
- package/src/utils/tapDetector.ts +119 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { debounce } from '@bagelink/vue'
|
|
3
|
+
import { onMounted, onUnmounted, watch } from 'vue'
|
|
4
|
+
import TapDetector from '../utils/tapDetector'
|
|
5
|
+
|
|
6
|
+
// Props interface
|
|
7
|
+
interface Props {
|
|
8
|
+
minScale?: number
|
|
9
|
+
maxScale?: number
|
|
10
|
+
zoom?: number
|
|
11
|
+
resetTrigger?: number
|
|
12
|
+
aspectRatio?: number
|
|
13
|
+
backgroundColor?: string
|
|
14
|
+
pivot?: string // 'cursor' | 'image-center'
|
|
15
|
+
zoomingElastic?: boolean
|
|
16
|
+
limitTranslation?: boolean
|
|
17
|
+
doubleClickToZoom?: boolean
|
|
18
|
+
mouseWheelToZoom?: boolean
|
|
19
|
+
disabled?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
minScale = 1,
|
|
24
|
+
maxScale = 5,
|
|
25
|
+
zoom,
|
|
26
|
+
disabled = false,
|
|
27
|
+
aspectRatio = 1,
|
|
28
|
+
backgroundColor = 'transparent',
|
|
29
|
+
pivot = 'cursor',
|
|
30
|
+
zoomingElastic = true,
|
|
31
|
+
limitTranslation = true,
|
|
32
|
+
doubleClickToZoom = true,
|
|
33
|
+
mouseWheelToZoom = true,
|
|
34
|
+
} = defineProps<Props>()
|
|
35
|
+
|
|
36
|
+
const emit = defineEmits(['update:zoom'])
|
|
37
|
+
// Reactive state using $ref (vue-macros)
|
|
38
|
+
const zoomElement = $ref<HTMLElement | null>()
|
|
39
|
+
let containerWidth = $ref(1)
|
|
40
|
+
let containerHeight = $ref(1)
|
|
41
|
+
let containerLeft = $ref(0)
|
|
42
|
+
let containerTop = $ref(0)
|
|
43
|
+
let translateX = $ref(0)
|
|
44
|
+
let animTranslateX = $ref(0)
|
|
45
|
+
let translateY = $ref(0)
|
|
46
|
+
let animTranslateY = $ref(0)
|
|
47
|
+
let _zoom = $ref(zoom || 1)
|
|
48
|
+
let scale = $computed({
|
|
49
|
+
get: () => zoom === undefined ? _zoom : zoom,
|
|
50
|
+
set: (val) => {
|
|
51
|
+
_zoom = val
|
|
52
|
+
emit('update:zoom', _zoom)
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
let animScale = $ref(1)
|
|
57
|
+
let lastFullWheelTime = $ref(0)
|
|
58
|
+
let lastWheelTime = $ref(0)
|
|
59
|
+
let lastWheelDirection = $ref<'x' | 'y'>('y')
|
|
60
|
+
let isPointerDown = $ref(false)
|
|
61
|
+
let pointerPosX = $ref(-1)
|
|
62
|
+
let pointerPosY = $ref(-1)
|
|
63
|
+
let twoFingerInitDist = $ref(0)
|
|
64
|
+
let panLocked = $ref(true)
|
|
65
|
+
let raf = $ref<number | null>(null)
|
|
66
|
+
let tapDetector = $ref<TapDetector | null>(null)
|
|
67
|
+
|
|
68
|
+
const wrapperStyle = $computed(() => {
|
|
69
|
+
const translateXValue = containerWidth * animTranslateX
|
|
70
|
+
const translateYValue = containerHeight * animTranslateY
|
|
71
|
+
return {
|
|
72
|
+
transform: [
|
|
73
|
+
`translate(${translateXValue}px, ${translateYValue}px)`,
|
|
74
|
+
`scale(${animScale})`,
|
|
75
|
+
].join(' '),
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
watch(() => scale, (newScale) => {
|
|
80
|
+
const { x, y } = calcTranslateLimit()
|
|
81
|
+
translateX = Math.max(-x, Math.min(x, translateX))
|
|
82
|
+
translateY = Math.max(-y, Math.min(y, translateY))
|
|
83
|
+
if (newScale !== 1) {
|
|
84
|
+
panLocked = false
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
function reset() {
|
|
89
|
+
scale = 1
|
|
90
|
+
panLocked = true
|
|
91
|
+
translateX = 0
|
|
92
|
+
translateY = 0
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
defineExpose({ reset })
|
|
96
|
+
|
|
97
|
+
function tryToScale(scaleDelta: number) {
|
|
98
|
+
if (disabled) return
|
|
99
|
+
let newScale = scale * scaleDelta
|
|
100
|
+
if (zoomingElastic) {
|
|
101
|
+
if (newScale < minScale! || newScale > maxScale!) {
|
|
102
|
+
let log = Math.log2(scaleDelta)
|
|
103
|
+
log *= 0.2
|
|
104
|
+
scaleDelta = 2 ** log
|
|
105
|
+
newScale = scale * scaleDelta
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
if (newScale < minScale!) newScale = minScale!
|
|
109
|
+
else if (newScale > maxScale!) newScale = maxScale!
|
|
110
|
+
}
|
|
111
|
+
scaleDelta = newScale / scale
|
|
112
|
+
scale = newScale
|
|
113
|
+
if (pivot !== 'image-center') {
|
|
114
|
+
const normMousePosX = (pointerPosX - containerLeft) / containerWidth
|
|
115
|
+
const normMousePosY = (pointerPosY - containerTop) / containerHeight
|
|
116
|
+
translateX = (0.5 + translateX - normMousePosX) * scaleDelta + normMousePosX - 0.5
|
|
117
|
+
translateY = (0.5 + translateY - normMousePosY) * scaleDelta + normMousePosY - 0.5
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function setPointerPosCenter() {
|
|
122
|
+
pointerPosX = containerLeft + containerWidth / 2
|
|
123
|
+
pointerPosY = containerTop + containerHeight / 2
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function onPointerMove(newMousePosX: number, newMousePosY: number) {
|
|
127
|
+
if (isPointerDown) {
|
|
128
|
+
const pixelDeltaX = newMousePosX - pointerPosX
|
|
129
|
+
const pixelDeltaY = newMousePosY - pointerPosY
|
|
130
|
+
if (!panLocked) {
|
|
131
|
+
const translateLimit = calcTranslateLimit()
|
|
132
|
+
const maxTranslateX = translateLimit.x
|
|
133
|
+
const maxTranslateY = translateLimit.y
|
|
134
|
+
const newTranslateX = translateX + pixelDeltaX / containerWidth
|
|
135
|
+
const newTranslateY = translateY + pixelDeltaY / containerHeight
|
|
136
|
+
translateX = Math.max(-maxTranslateX, Math.min(maxTranslateX, newTranslateX))
|
|
137
|
+
translateY = Math.max(-maxTranslateY, Math.min(maxTranslateY, newTranslateY))
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
pointerPosX = newMousePosX
|
|
141
|
+
pointerPosY = newMousePosY
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function onInteractionEnd() {
|
|
145
|
+
debounce(() => {
|
|
146
|
+
limit()
|
|
147
|
+
panLocked = scale === 1
|
|
148
|
+
}, 100)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function limit() {
|
|
152
|
+
if (scale < minScale!) {
|
|
153
|
+
scale = minScale!
|
|
154
|
+
} else if (scale > maxScale!) {
|
|
155
|
+
tryToScale(maxScale! / scale)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (limitTranslation) {
|
|
159
|
+
const translateLimit = calcTranslateLimit()
|
|
160
|
+
if (Math.abs(translateX) > translateLimit.x) {
|
|
161
|
+
translateX *= translateLimit.x / Math.abs(translateX)
|
|
162
|
+
}
|
|
163
|
+
if (Math.abs(translateY) > translateLimit.y) {
|
|
164
|
+
translateY *= translateLimit.y / Math.abs(translateY)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function calcTranslateLimit() {
|
|
170
|
+
if (getMarginDirection() === 'y') {
|
|
171
|
+
const imageToContainerRatio = containerWidth / aspectRatio / containerHeight
|
|
172
|
+
let translateLimitY = (scale * imageToContainerRatio - 1) / 2
|
|
173
|
+
if (translateLimitY < 0) translateLimitY = 0
|
|
174
|
+
return { x: (scale - 1) / 2, y: translateLimitY }
|
|
175
|
+
} else {
|
|
176
|
+
const imageToContainerRatio = containerHeight * aspectRatio / containerWidth
|
|
177
|
+
let translateLimitX = (scale * imageToContainerRatio - 1) / 2
|
|
178
|
+
if (translateLimitX < 0) translateLimitX = 0
|
|
179
|
+
return { x: translateLimitX, y: (scale - 1) / 2 }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getMarginDirection() {
|
|
184
|
+
const containerRatio = containerWidth / containerHeight
|
|
185
|
+
return containerRatio > aspectRatio ? 'x' : 'y'
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function onMouseWheel(ev: WheelEvent) {
|
|
189
|
+
if (!mouseWheelToZoom) return
|
|
190
|
+
ev.preventDefault()
|
|
191
|
+
const currTime = Date.now()
|
|
192
|
+
if (Math.abs(ev.deltaY) === 120) {
|
|
193
|
+
if (currTime - lastFullWheelTime > 50) {
|
|
194
|
+
onMouseWheelDo(ev.deltaY)
|
|
195
|
+
lastFullWheelTime = currTime
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
if (currTime - lastWheelTime > 50) {
|
|
199
|
+
lastWheelDirection = ev.deltaX > ev.deltaY ? 'x' : 'y'
|
|
200
|
+
if (lastWheelDirection === 'y') {
|
|
201
|
+
onMouseWheelDo(ev.deltaY)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
lastWheelTime = currTime
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function onMouseWheelDo(wheelDelta: number) {
|
|
209
|
+
const scaleDelta = 1.25 ** (wheelDelta / 120)
|
|
210
|
+
tryToScale(scaleDelta)
|
|
211
|
+
onInteractionEnd()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function onMouseDown(ev: MouseEvent) {
|
|
215
|
+
refreshContainerPos()
|
|
216
|
+
isPointerDown = true
|
|
217
|
+
pointerPosX = ev.clientX
|
|
218
|
+
pointerPosY = ev.clientY
|
|
219
|
+
document.addEventListener('mousemove', onMouseMove)
|
|
220
|
+
document.addEventListener('mouseup', onMouseUp)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function onMouseUp() {
|
|
224
|
+
isPointerDown = false
|
|
225
|
+
onInteractionEnd()
|
|
226
|
+
document.removeEventListener('mouseup', onMouseUp)
|
|
227
|
+
document.removeEventListener('mousemove', onMouseMove)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function onMouseMove(ev: MouseEvent) {
|
|
231
|
+
onPointerMove(ev.clientX, ev.clientY)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function onTouchStart(ev: TouchEvent) {
|
|
235
|
+
if (ev.touches.length === 1) {
|
|
236
|
+
refreshContainerPos()
|
|
237
|
+
pointerPosX = ev.touches[0].clientX
|
|
238
|
+
pointerPosY = ev.touches[0].clientY
|
|
239
|
+
isPointerDown = true
|
|
240
|
+
} else if (ev.touches.length === 2) {
|
|
241
|
+
isPointerDown = true
|
|
242
|
+
pointerPosX = (ev.touches[0].clientX + ev.touches[1].clientX) / 2
|
|
243
|
+
pointerPosY = (ev.touches[0].clientY + ev.touches[1].clientY) / 2
|
|
244
|
+
const distX = ev.touches[0].clientX - ev.touches[1].clientX
|
|
245
|
+
const distY = ev.touches[0].clientY - ev.touches[1].clientY
|
|
246
|
+
twoFingerInitDist = Math.sqrt(distX * distX + distY * distY)
|
|
247
|
+
}
|
|
248
|
+
document.addEventListener('touchend', onTouchEnd)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function onTouchEnd(ev: TouchEvent) {
|
|
252
|
+
if (ev.touches.length === 0) {
|
|
253
|
+
isPointerDown = false
|
|
254
|
+
if (Math.abs(scale - 1) < 0.1) scale = 1
|
|
255
|
+
onInteractionEnd()
|
|
256
|
+
} else if (ev.touches.length === 1) {
|
|
257
|
+
pointerPosX = ev.touches[0].clientX
|
|
258
|
+
pointerPosY = ev.touches[0].clientY
|
|
259
|
+
}
|
|
260
|
+
document.removeEventListener('touchend', onTouchEnd)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function onTouchMove(ev: TouchEvent) {
|
|
264
|
+
if (ev.touches.length === 1) {
|
|
265
|
+
onPointerMove(ev.touches[0].clientX, ev.touches[0].clientY)
|
|
266
|
+
} else if (ev.touches.length
|
|
267
|
+
|
|
268
|
+
=== 2) {
|
|
269
|
+
const distX = ev.touches[0].clientX - ev.touches[1].clientX
|
|
270
|
+
const distY = ev.touches[0].clientY - ev.touches[1].clientY
|
|
271
|
+
const newTwoFingerDist = Math.sqrt(distX * distX + distY * distY)
|
|
272
|
+
tryToScale(newTwoFingerDist / twoFingerInitDist)
|
|
273
|
+
twoFingerInitDist = newTwoFingerDist
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function refreshContainerPos() {
|
|
278
|
+
if (zoomElement) {
|
|
279
|
+
const rect = zoomElement.getBoundingClientRect()
|
|
280
|
+
containerLeft = rect.left
|
|
281
|
+
containerTop = rect.top
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function loop() {
|
|
286
|
+
animScale = gainOn(animScale, scale)
|
|
287
|
+
animTranslateX = gainOn(animTranslateX, translateX)
|
|
288
|
+
animTranslateY = gainOn(animTranslateY, translateY)
|
|
289
|
+
raf = window.requestAnimationFrame(loop)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function gainOn(from: number, to: number) {
|
|
293
|
+
const delta = (to - from) * 0.3
|
|
294
|
+
return Math.abs(delta) > 1e-5 ? from + delta : to
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Lifecycle hooks
|
|
298
|
+
onMounted(() => {
|
|
299
|
+
tapDetector = new TapDetector()
|
|
300
|
+
if (zoomElement) tapDetector.attach(zoomElement)
|
|
301
|
+
if (doubleClickToZoom) {
|
|
302
|
+
tapDetector.onDoubleTap(onDoubleTap as any)
|
|
303
|
+
}
|
|
304
|
+
window.addEventListener('resize', onWindowResize)
|
|
305
|
+
onWindowResize()
|
|
306
|
+
refreshContainerPos()
|
|
307
|
+
loop()
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
onUnmounted(() => {
|
|
311
|
+
if (zoomElement) tapDetector?.detach(zoomElement)
|
|
312
|
+
window.removeEventListener('resize', onWindowResize)
|
|
313
|
+
if (raf) window.cancelAnimationFrame(raf)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
function onDoubleTap(ev: MouseEvent) {
|
|
317
|
+
if (scale === 1) {
|
|
318
|
+
if (ev.clientX > 0) {
|
|
319
|
+
pointerPosX = ev.clientX
|
|
320
|
+
pointerPosY = ev.clientY
|
|
321
|
+
}
|
|
322
|
+
tryToScale(Math.min(3, maxScale!))
|
|
323
|
+
} else {
|
|
324
|
+
reset()
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function onWindowResize() {
|
|
329
|
+
if (zoomElement) {
|
|
330
|
+
const styles = window.getComputedStyle(zoomElement)
|
|
331
|
+
containerWidth = Number.parseFloat(styles.width)
|
|
332
|
+
containerHeight = Number.parseFloat(styles.height)
|
|
333
|
+
setPointerPosCenter()
|
|
334
|
+
limit()
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
</script>
|
|
338
|
+
|
|
339
|
+
<template>
|
|
340
|
+
<div
|
|
341
|
+
ref="zoomElement"
|
|
342
|
+
class="vue-zoomer"
|
|
343
|
+
:style="{ backgroundColor }"
|
|
344
|
+
@mousewheel="onMouseWheel"
|
|
345
|
+
@DOMMouseScroll="onMouseWheel"
|
|
346
|
+
@mousedown="onMouseDown"
|
|
347
|
+
@mouseout="setPointerPosCenter"
|
|
348
|
+
@focusout="setPointerPosCenter"
|
|
349
|
+
@touchstart="onTouchStart"
|
|
350
|
+
@touchmove="onTouchMove"
|
|
351
|
+
>
|
|
352
|
+
<div class="zoomer" :style="wrapperStyle">
|
|
353
|
+
<slot />
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
</template>
|
|
357
|
+
|
|
358
|
+
<style scoped>
|
|
359
|
+
.vue-zoomer {
|
|
360
|
+
overflow: hidden;
|
|
361
|
+
}
|
|
362
|
+
.zoomer {
|
|
363
|
+
transform-origin: 50% 50%;
|
|
364
|
+
width: 100%;
|
|
365
|
+
height: 100%;
|
|
366
|
+
}
|
|
367
|
+
.zoomer > img {
|
|
368
|
+
vertical-align: top;
|
|
369
|
+
user-select: none;
|
|
370
|
+
-moz-user-drag: none;
|
|
371
|
+
-webkit-user-select: none;
|
|
372
|
+
-moz-user-select: none;
|
|
373
|
+
-ms-user-select: none;
|
|
374
|
+
-webkit-user-drag: none;
|
|
375
|
+
-moz-user-drag: none;
|
|
376
|
+
}
|
|
377
|
+
</style>
|
|
@@ -26,7 +26,7 @@ const selectedOption = defineModel('modelValue')
|
|
|
26
26
|
<label
|
|
27
27
|
v-for="opt in options"
|
|
28
28
|
:key="opt.id"
|
|
29
|
-
class="border
|
|
29
|
+
class="border rounded p-1 flex bg-gray-light mb-05 gap-075 active-list-item hover"
|
|
30
30
|
:for="opt.id"
|
|
31
31
|
>
|
|
32
32
|
<input
|
|
@@ -40,7 +40,7 @@ const selectedOption = defineModel('modelValue')
|
|
|
40
40
|
<div class="flex w-100 gap-1 flex-wrap m_gap-05 m_gap-row-025">
|
|
41
41
|
<img
|
|
42
42
|
v-if="opt.imgSrc"
|
|
43
|
-
class="bg-popup shadow-light py-025
|
|
43
|
+
class="bg-popup shadow-light py-025 radius-05 m_w40"
|
|
44
44
|
width="60"
|
|
45
45
|
:src="opt.imgSrc"
|
|
46
46
|
:alt="opt.imgAlt"
|
|
@@ -134,10 +134,10 @@ function handleKeyDown(event: KeyboardEvent) {
|
|
|
134
134
|
</script>
|
|
135
135
|
|
|
136
136
|
<template>
|
|
137
|
-
<div class="rich-text-editor
|
|
137
|
+
<div class="rich-text-editor rounded pt-05 px-1 pb-1">
|
|
138
138
|
<Toolbar :config @action="handleToolbarAction" />
|
|
139
139
|
<div class="editor-container flex flex-stretch gap-1 m_column">
|
|
140
|
-
<div class="content-area
|
|
140
|
+
<div class="content-area radius-05 p-1 shadow-light w-100 grid">
|
|
141
141
|
<textarea v-if="isCodeView" v-model="contentHtml" @input="updateContent" />
|
|
142
142
|
<div
|
|
143
143
|
v-else
|
|
@@ -151,7 +151,7 @@ function handleKeyDown(event: KeyboardEvent) {
|
|
|
151
151
|
@keydown="handleKeyDown"
|
|
152
152
|
/>
|
|
153
153
|
</div>
|
|
154
|
-
<code v-if="isSplitView" class="preview-area w-100
|
|
154
|
+
<code v-if="isSplitView" class="preview-area w-100 radius-05 p-1">{{ contentHtml }}</code>
|
|
155
155
|
</div>
|
|
156
156
|
</div>
|
|
157
157
|
</template>
|
|
@@ -14,7 +14,7 @@ const inputVal = defineModel('modelValue', {
|
|
|
14
14
|
<div class="bagel-input checkbox" :title="label">
|
|
15
15
|
<label class="switch">
|
|
16
16
|
<input :id="id" v-model="inputVal" type="checkbox">
|
|
17
|
-
<span class="slider
|
|
17
|
+
<span class="slider rounded" />
|
|
18
18
|
</label>
|
|
19
19
|
</div>
|
|
20
20
|
</template>
|
package/src/components/index.ts
CHANGED
|
@@ -30,3 +30,4 @@ export { default as RouterWrapper } from './RouterWrapper.vue'
|
|
|
30
30
|
export { default as TableSchema } from './TableSchema.vue'
|
|
31
31
|
export { default as Title } from './Title.vue'
|
|
32
32
|
export { default as TopBar } from './TopBar.vue'
|
|
33
|
+
export { default as Zoomer } from './Zoomer.vue'
|
|
@@ -32,7 +32,7 @@ function toggleMenu() {
|
|
|
32
32
|
@click="toggleMenu"
|
|
33
33
|
/>
|
|
34
34
|
<Card
|
|
35
|
-
class="py-1 px-05 h-100 flex column gap-05
|
|
35
|
+
class="py-1 px-05 h-100 flex column gap-05 rounded relative bg-primary font-light overflow-y "
|
|
36
36
|
>
|
|
37
37
|
<slot v-if="!isOpen || !slots['brand-open']" name="brand" />
|
|
38
38
|
<slot v-if="isOpen" name="brand-open" />
|
|
@@ -8,6 +8,7 @@ import { useTabs } from './tabsManager'
|
|
|
8
8
|
const props = defineProps<{
|
|
9
9
|
tabs: Tab[]
|
|
10
10
|
modelValue?: string
|
|
11
|
+
flat?: boolean
|
|
11
12
|
}>()
|
|
12
13
|
|
|
13
14
|
const emit = defineEmits(['update:modelValue'])
|
|
@@ -19,13 +20,15 @@ const tabValue = (tab: Tab) => (typeof tab === 'string' ? tab : tab.id)
|
|
|
19
20
|
|
|
20
21
|
const slctTab = $computed({
|
|
21
22
|
get: () => props.modelValue || tabValue(props.tabs[0]),
|
|
22
|
-
set:
|
|
23
|
+
set: value => {
|
|
24
|
+
emit('update:modelValue', value)
|
|
25
|
+
},
|
|
23
26
|
})
|
|
24
27
|
|
|
25
28
|
const tabComponent = defineComponent({
|
|
26
29
|
render() {
|
|
27
30
|
const currentTabIndex = props.tabs.findIndex(
|
|
28
|
-
tab => tabValue(tab) === currentTab.value
|
|
31
|
+
tab => tabValue(tab) === currentTab.value
|
|
29
32
|
)
|
|
30
33
|
if (currentTabIndex === -1) return null
|
|
31
34
|
const slotChildren = slots.default?.()[1]?.children
|
|
@@ -37,7 +40,7 @@ const tabComponent = defineComponent({
|
|
|
37
40
|
|
|
38
41
|
z
|
|
39
42
|
<template>
|
|
40
|
-
<TabsNav v-model="slctTab" :tabs :group class="mb-05" />
|
|
43
|
+
<TabsNav v-model="slctTab" :flat :tabs :group class="mb-05" />
|
|
41
44
|
<div v-if="currentTab">
|
|
42
45
|
<template v-if="slots[currentTab]">
|
|
43
46
|
<slot :name="currentTab" />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { BglVideo, Btn, Icon } from '@bagelink/vue'
|
|
2
|
+
import { BglVideo, Btn, Icon, Zoomer } from '@bagelink/vue'
|
|
3
3
|
import { watch } from 'vue'
|
|
4
|
+
|
|
4
5
|
import type { LightboxItem } from './lightbox.types'
|
|
5
6
|
|
|
6
7
|
let isOpen = $ref(false)
|
|
@@ -62,6 +63,12 @@ function handleKeydown(event: KeyboardEvent) {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
const zoom = $ref(1)
|
|
67
|
+
|
|
68
|
+
function clickOutside() {
|
|
69
|
+
if (zoom === 1) close()
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
defineExpose({ open, close })
|
|
66
73
|
</script>
|
|
67
74
|
|
|
@@ -73,7 +80,7 @@ defineExpose({ open, close })
|
|
|
73
80
|
@keydown.esc="close"
|
|
74
81
|
@keydown.left="prev"
|
|
75
82
|
@keydown.right="next"
|
|
76
|
-
@click="
|
|
83
|
+
@click="clickOutside"
|
|
77
84
|
>
|
|
78
85
|
<div v-if="group && group.length > 1" class="navigation flex space-between px-3 w-100 absolute">
|
|
79
86
|
<Btn
|
|
@@ -81,6 +88,7 @@ defineExpose({ open, close })
|
|
|
81
88
|
icon="arrow_back"
|
|
82
89
|
@click="prev"
|
|
83
90
|
/>
|
|
91
|
+
|
|
84
92
|
<Btn
|
|
85
93
|
class="navigation-btn oval"
|
|
86
94
|
icon="arrow_forward"
|
|
@@ -88,10 +96,26 @@ defineExpose({ open, close })
|
|
|
88
96
|
/>
|
|
89
97
|
</div>
|
|
90
98
|
<div class="bgl-lightbox relative txt-center" @click.stop>
|
|
91
|
-
<
|
|
99
|
+
<div class="flex start fixed top-1 w-100 space-between">
|
|
100
|
+
<Btn flat class="color-white" icon="close" @click="close" />
|
|
101
|
+
<div v-if="currentItem?.enableZoom && currentItem?.type === 'image'" class="center">
|
|
102
|
+
<Btn flat class="color-white" icon="remove" :disabled="zoom === 1" @click="zoom--" />
|
|
103
|
+
<Btn flat class="color-white" icon="zoom_in" :disabled="zoom === 1" @click="zoom = 1" />
|
|
104
|
+
<Btn flat class="color-white" icon="add" :disabled="zoom === 3" @click="zoom++" />
|
|
105
|
+
</div>
|
|
106
|
+
<Btn
|
|
107
|
+
v-if="currentItem?.openFile" class="color-white" round thin flat icon.end="arrow_outward"
|
|
108
|
+
value="Open File"
|
|
109
|
+
:href="currentItem?.src"
|
|
110
|
+
target="_blank"
|
|
111
|
+
/>
|
|
112
|
+
<div v-else />
|
|
113
|
+
</div>
|
|
92
114
|
<div class="bgl-lightbox-item">
|
|
93
115
|
<template v-if="currentItem?.type === 'image'">
|
|
94
|
-
<
|
|
116
|
+
<Zoomer v-model:zoom="zoom" :disabled="!currentItem?.enableZoom" :mouse-wheel-to-zoom="false">
|
|
117
|
+
<img :draggable="false" :src="currentItem?.src" alt="Preview" class="vw90 lightbox-image">
|
|
118
|
+
</Zoomer>
|
|
95
119
|
</template>
|
|
96
120
|
<template v-else-if="currentItem?.type === 'video'">
|
|
97
121
|
<BglVideo
|
|
@@ -131,7 +155,7 @@ defineExpose({ open, close })
|
|
|
131
155
|
<img
|
|
132
156
|
v-if="item.type === 'image'"
|
|
133
157
|
class="thumbnail object-fit-cover hover
|
|
134
|
-
opacity-5
|
|
158
|
+
opacity-5 rounded flex bg-popup justify-content-center align-items-center flex-shrink-0"
|
|
135
159
|
:src="item.src"
|
|
136
160
|
alt=""
|
|
137
161
|
:class="{ active: currentIndex === index }"
|
|
@@ -140,7 +164,7 @@ defineExpose({ open, close })
|
|
|
140
164
|
<Icon
|
|
141
165
|
v-else
|
|
142
166
|
class="thumbnail object-fit-cover hover
|
|
143
|
-
opacity-5
|
|
167
|
+
opacity-5 ed flex bg-popup justify-content-center align-items-center flex-shrink-0"
|
|
144
168
|
icon="description"
|
|
145
169
|
:class="{ active: currentIndex === index }"
|
|
146
170
|
@click="selectItem(index)"
|
|
@@ -43,10 +43,10 @@ function openClickHandler(e: MouseEvent, el: HTMLElement, binding: DirectiveBind
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function bindingToItem(binding: DirectiveBinding, el: HTMLElement): LightboxItem {
|
|
46
|
-
let { group, src, type, name, thumbnail } = binding.value || {}
|
|
46
|
+
let { group, src, type, name, thumbnail, enableZoom, openFile } = binding.value || {}
|
|
47
47
|
src = src || binding.value || el.getAttribute('src') || ''
|
|
48
48
|
type = type || determineFileType(src)
|
|
49
|
-
return { src, type, name, thumbnail, group }
|
|
49
|
+
return { src, type, name, thumbnail, group, enableZoom, openFile }
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const youtubeRegex = /youtube\.com|youtu\.be/
|
package/src/styles/layout.css
CHANGED
|
@@ -11,18 +11,34 @@
|
|
|
11
11
|
height: 100vh;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
.round {
|
|
16
|
+
border-radius: 1000px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.rounded,
|
|
20
|
+
.radius,
|
|
21
|
+
.radius-1 {
|
|
15
22
|
border-radius: var(--btn-border-radius) !important;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
|
-
.
|
|
25
|
+
.radius-05 {
|
|
19
26
|
border-radius: calc(var(--btn-border-radius) / 2) !important;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
.
|
|
29
|
+
.radius-2 {
|
|
23
30
|
border-radius: calc(var(--btn-border-radius) * 2) !important;
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
.radius-3 {
|
|
34
|
+
border-radius: calc(var(--btn-border-radius) * 3) !important;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.radius-4 {
|
|
38
|
+
border-radius: calc(var(--btn-border-radius) * 4) !important;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
26
42
|
.round-none {
|
|
27
43
|
border-radius: 0;
|
|
28
44
|
}
|
|
@@ -35,17 +35,31 @@
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.m_round {
|
|
38
|
+
border-radius: 1000px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.m_rounded,
|
|
42
|
+
.m_radius,
|
|
43
|
+
.m_radius-1 {
|
|
38
44
|
border-radius: var(--btn-border-radius) !important;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
.
|
|
47
|
+
.m_radius-05 {
|
|
42
48
|
border-radius: calc(var(--btn-border-radius) / 2) !important;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
.
|
|
51
|
+
.m_radius-2 {
|
|
46
52
|
border-radius: calc(var(--btn-border-radius) * 2) !important;
|
|
47
53
|
}
|
|
48
54
|
|
|
55
|
+
.m_radius-3 {
|
|
56
|
+
border-radius: calc(var(--btn-border-radius) * 3) !important;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.m_radius-4 {
|
|
60
|
+
border-radius: calc(var(--btn-border-radius) * 4) !important;
|
|
61
|
+
}
|
|
62
|
+
|
|
49
63
|
.m_round-none {
|
|
50
64
|
border-radius: 0 !important;
|
|
51
65
|
}
|