@bagelink/vue 0.0.751 → 0.0.755

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 (183) hide show
  1. package/dist/components/AddressSaerch.vue.d.ts +7 -0
  2. package/dist/components/AddressSaerch.vue.d.ts.map +1 -0
  3. package/dist/components/ComboBox.vue.d.ts +3 -3
  4. package/dist/components/Comments.vue.d.ts +2 -2
  5. package/dist/components/ContactSubmissions.vue.d.ts +2 -2
  6. package/dist/components/FormSchema.vue.d.ts +4 -5
  7. package/dist/components/LangText.vue.d.ts +2 -2
  8. package/dist/components/ModalBglForm.vue.d.ts +20 -21
  9. package/dist/components/PersonPreview.vue.d.ts +4 -5
  10. package/dist/components/PersonPreviewFormkit.vue.d.ts +3 -4
  11. package/dist/components/Popover.vue.d.ts +10 -0
  12. package/dist/components/Popover.vue.d.ts.map +1 -0
  13. package/dist/components/RTXEditor.vue.d.ts +3 -3
  14. package/dist/components/TabbedLayout.vue.d.ts +4 -5
  15. package/dist/components/Zoomer.vue.d.ts +40 -0
  16. package/dist/components/Zoomer.vue.d.ts.map +1 -0
  17. package/dist/components/charts/BarChart.vue.d.ts +2 -2
  18. package/dist/components/form/ItemRef.vue.d.ts +3 -5
  19. package/dist/components/form/ItemRef.vue.d.ts.map +1 -1
  20. package/dist/components/form/MaterialIcon.vue.d.ts +3 -4
  21. package/dist/components/form/PlainInputField.vue.d.ts +3 -3
  22. package/dist/components/form/inputs/CurrencyInput.vue.d.ts +3 -3
  23. package/dist/components/form/inputs/DatetimeInput.vue.d.ts +3 -3
  24. package/dist/components/form/inputs/DurationInput.vue.d.ts +3 -3
  25. package/dist/components/form/inputs/DynamicLinkField.vue.d.ts +3 -3
  26. package/dist/components/form/inputs/EmailInput.vue.d.ts +3 -3
  27. package/dist/components/form/inputs/FloatInput.vue.d.ts +3 -3
  28. package/dist/components/form/inputs/IntInput.vue.d.ts +3 -3
  29. package/dist/components/form/inputs/LinkField.vue.d.ts +3 -3
  30. package/dist/components/form/inputs/Password.vue.d.ts +3 -3
  31. package/dist/components/form/inputs/PlainText.vue.d.ts +3 -3
  32. package/dist/components/form/inputs/ReadOnlyInput.vue.d.ts +2 -2
  33. package/dist/components/form/inputs/RichTextEditor.vue.d.ts +3 -3
  34. package/dist/components/form/inputs/SelectField.vue.d.ts +8 -6
  35. package/dist/components/form/inputs/SelectField.vue.d.ts.map +1 -1
  36. package/dist/components/form/inputs/TelInput.vue.d.ts +6 -6
  37. package/dist/components/form/inputs/TextArea.vue.d.ts +3 -3
  38. package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
  39. package/dist/components/formkit/AddressArray.vue.d.ts +2 -2
  40. package/dist/components/formkit/BankDetailsArray.vue.d.ts +2 -2
  41. package/dist/components/formkit/ContactArrayFormKit.vue.d.ts +2 -2
  42. package/dist/components/formkit/FileUploader.vue.d.ts +2 -2
  43. package/dist/components/formkit/MiscFields.vue.d.ts +2 -2
  44. package/dist/components/formkit/Toggle.vue.d.ts +2 -2
  45. package/dist/components/index.d.ts +1 -0
  46. package/dist/components/index.d.ts.map +1 -1
  47. package/dist/components/layout/SidebarMenu.vue.d.ts.map +1 -1
  48. package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
  49. package/dist/components/lightbox/lightbox.types.d.ts +2 -0
  50. package/dist/components/lightbox/lightbox.types.d.ts.map +1 -1
  51. package/dist/components/whatsapp/form/MsgTemplate.vue.d.ts +3 -4
  52. package/dist/components/whatsapp/form/TextVariableExamples.vue.d.ts +2 -2
  53. package/dist/index.cjs +812 -333
  54. package/dist/index.mjs +812 -333
  55. package/dist/style.css +89 -81
  56. package/dist/types/materialIcon.d.ts +2 -0
  57. package/dist/types/materialIcon.d.ts.map +1 -0
  58. package/dist/utils/index.d.ts +1 -1
  59. package/dist/utils/index.d.ts.map +1 -1
  60. package/dist/utils/objects.d.ts +0 -1
  61. package/dist/utils/tapDetector.d.ts +30 -0
  62. package/dist/utils/tapDetector.d.ts.map +1 -0
  63. package/package.json +1 -1
  64. package/src/components/Zoomer.vue +377 -0
  65. package/src/components/form/inputs/TextInput.vue +14 -12
  66. package/src/components/index.ts +1 -0
  67. package/src/components/layout/SidebarMenu.vue +49 -78
  68. package/src/components/lightbox/Lightbox.vue +28 -4
  69. package/src/components/lightbox/index.ts +2 -2
  70. package/src/components/lightbox/lightbox.types.ts +2 -0
  71. package/src/styles/bagel.css +6 -0
  72. package/src/styles/buttons.css +4 -0
  73. package/src/styles/theme.css +6 -1
  74. package/src/utils/index.ts +12 -4
  75. package/src/utils/tapDetector.ts +119 -0
  76. package/dist/components/Accordion.d.ts +0 -12
  77. package/dist/components/Accordion.d.ts.map +0 -1
  78. package/dist/components/AccordionItem.d.ts +0 -34
  79. package/dist/components/AccordionItem.d.ts.map +0 -1
  80. package/dist/components/Alert.d.ts +0 -34
  81. package/dist/components/Alert.d.ts.map +0 -1
  82. package/dist/components/Avatar.d.ts +0 -36
  83. package/dist/components/Avatar.d.ts.map +0 -1
  84. package/dist/components/Badge.d.ts +0 -22
  85. package/dist/components/Badge.d.ts.map +0 -1
  86. package/dist/components/BglVideo.d.ts +0 -20
  87. package/dist/components/BglVideo.d.ts.map +0 -1
  88. package/dist/components/Btn.d.ts +0 -99
  89. package/dist/components/Btn.d.ts.map +0 -1
  90. package/dist/components/Card.d.ts +0 -39
  91. package/dist/components/Card.d.ts.map +0 -1
  92. package/dist/components/Carousel.d.ts +0 -74
  93. package/dist/components/Carousel.d.ts.map +0 -1
  94. package/dist/components/DataPreview.d.ts +0 -42
  95. package/dist/components/DataPreview.d.ts.map +0 -1
  96. package/dist/components/Drop.vue.d.ts +0 -34
  97. package/dist/components/Drop.vue.d.ts.map +0 -1
  98. package/dist/components/FileUploader.vue.d.ts +0 -60
  99. package/dist/components/FileUploader.vue.d.ts.map +0 -1
  100. package/dist/components/Flag.d.ts +0 -20
  101. package/dist/components/Flag.d.ts.map +0 -1
  102. package/dist/components/ListItem.d.ts +0 -34
  103. package/dist/components/ListItem.d.ts.map +0 -1
  104. package/dist/components/ListView.d.ts +0 -13
  105. package/dist/components/ListView.d.ts.map +0 -1
  106. package/dist/components/MapEmbed.d.ts +0 -3
  107. package/dist/components/MapEmbed.d.ts.map +0 -1
  108. package/dist/components/MaterialIcon.d.ts +0 -26
  109. package/dist/components/MaterialIcon.d.ts.map +0 -1
  110. package/dist/components/Modal.d.ts +0 -46
  111. package/dist/components/Modal.d.ts.map +0 -1
  112. package/dist/components/ModalConfirm.d.ts +0 -24
  113. package/dist/components/ModalConfirm.d.ts.map +0 -1
  114. package/dist/components/ModalForm.d.ts +0 -78
  115. package/dist/components/ModalForm.d.ts.map +0 -1
  116. package/dist/components/NavBar.d.ts +0 -64
  117. package/dist/components/NavBar.d.ts.map +0 -1
  118. package/dist/components/PageTitle.d.ts +0 -24
  119. package/dist/components/PageTitle.d.ts.map +0 -1
  120. package/dist/components/RouterWrapper.d.ts +0 -3
  121. package/dist/components/RouterWrapper.d.ts.map +0 -1
  122. package/dist/components/TableSchema.d.ts +0 -35
  123. package/dist/components/TableSchema.d.ts.map +0 -1
  124. package/dist/components/Title.d.ts +0 -42
  125. package/dist/components/Title.d.ts.map +0 -1
  126. package/dist/components/TopBar.d.ts +0 -12
  127. package/dist/components/TopBar.d.ts.map +0 -1
  128. package/dist/components/dashboard/Lineart.d.ts +0 -20
  129. package/dist/components/dashboard/Lineart.d.ts.map +0 -1
  130. package/dist/components/form/BglField.d.ts +0 -25
  131. package/dist/components/form/BglField.d.ts.map +0 -1
  132. package/dist/components/form/BglForm.d.ts +0 -75
  133. package/dist/components/form/BglForm.d.ts.map +0 -1
  134. package/dist/components/form/inputs/CheckInput.d.ts +0 -56
  135. package/dist/components/form/inputs/CheckInput.d.ts.map +0 -1
  136. package/dist/components/form/inputs/Checkbox.d.ts +0 -16
  137. package/dist/components/form/inputs/Checkbox.d.ts.map +0 -1
  138. package/dist/components/form/inputs/ColorPicker.d.ts +0 -48
  139. package/dist/components/form/inputs/ColorPicker.d.ts.map +0 -1
  140. package/dist/components/form/inputs/DateInput.d.ts +0 -64
  141. package/dist/components/form/inputs/DateInput.d.ts.map +0 -1
  142. package/dist/components/form/inputs/DatePicker.d.ts +0 -33
  143. package/dist/components/form/inputs/DatePicker.d.ts.map +0 -1
  144. package/dist/components/form/inputs/FileUpload.d.ts +0 -108
  145. package/dist/components/form/inputs/FileUpload.d.ts.map +0 -1
  146. package/dist/components/form/inputs/JSONInput.d.ts +0 -53
  147. package/dist/components/form/inputs/JSONInput.d.ts.map +0 -1
  148. package/dist/components/form/inputs/RadioGroup.d.ts +0 -42
  149. package/dist/components/form/inputs/RadioGroup.d.ts.map +0 -1
  150. package/dist/components/form/inputs/RadioPillsInput.d.ts +0 -48
  151. package/dist/components/form/inputs/RadioPillsInput.d.ts.map +0 -1
  152. package/dist/components/form/inputs/RichText.d.ts +0 -20
  153. package/dist/components/form/inputs/RichText.d.ts.map +0 -1
  154. package/dist/components/form/inputs/RichText2/Toolbar.d.ts +0 -22
  155. package/dist/components/form/inputs/RichText2/Toolbar.d.ts.map +0 -1
  156. package/dist/components/form/inputs/RichText2/index.d.ts +0 -24
  157. package/dist/components/form/inputs/RichText2/index.d.ts.map +0 -1
  158. package/dist/components/form/inputs/SelectInput.d.ts +0 -55
  159. package/dist/components/form/inputs/SelectInput.d.ts.map +0 -1
  160. package/dist/components/form/inputs/SignaturePad.d.ts +0 -72
  161. package/dist/components/form/inputs/SignaturePad.d.ts.map +0 -1
  162. package/dist/components/form/inputs/TableField.d.ts +0 -45
  163. package/dist/components/form/inputs/TableField.d.ts.map +0 -1
  164. package/dist/components/form/inputs/TelInput.d.ts +0 -241
  165. package/dist/components/form/inputs/TelInput.d.ts.map +0 -1
  166. package/dist/components/form/inputs/TextInput.d.ts +0 -90
  167. package/dist/components/form/inputs/TextInput.d.ts.map +0 -1
  168. package/dist/components/form/inputs/ToggleInput.d.ts +0 -58
  169. package/dist/components/form/inputs/ToggleInput.d.ts.map +0 -1
  170. package/dist/components/layout/BottomMenu.d.ts +0 -27
  171. package/dist/components/layout/BottomMenu.d.ts.map +0 -1
  172. package/dist/components/layout/Layout.d.ts +0 -58
  173. package/dist/components/layout/Layout.d.ts.map +0 -1
  174. package/dist/components/layout/SidebarMenu.d.ts +0 -38
  175. package/dist/components/layout/SidebarMenu.d.ts.map +0 -1
  176. package/dist/components/layout/TabbedLayout.d.ts +0 -42
  177. package/dist/components/layout/TabbedLayout.d.ts.map +0 -1
  178. package/dist/components/layout/Tabs.d.ts +0 -31
  179. package/dist/components/layout/Tabs.d.ts.map +0 -1
  180. package/dist/components/layout/TabsBody.d.ts +0 -23
  181. package/dist/components/layout/TabsBody.d.ts.map +0 -1
  182. package/dist/components/layout/TabsNav.d.ts +0 -35
  183. package/dist/components/layout/TabsNav.d.ts.map +0 -1
@@ -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>
@@ -186,24 +186,26 @@ onMounted(() => {
186
186
  font-size: var(--label-font-size);
187
187
  }
188
188
 
189
- .textInputIconWrap {
190
- position: relative;
191
- }
192
-
193
189
  .textInputIconWrap .bgl_icon-font {
194
- position: absolute;
195
- inset-inline-end: 0.7rem;
196
- bottom: 50%;
197
- line-height: 0;
198
190
  color: var(--bgl-gray);
191
+ position: absolute;
192
+ bottom: 0px;
193
+ inset-inline-end: 0.25rem;
194
+ margin-block: calc(var(--input-height) / 2 - 16px);
195
+ }
196
+ .textInputIconWrap input{
197
+ padding-inline-end: 2rem;
199
198
  }
200
199
 
201
200
  .txtInputIconStart .iconStart {
202
- position: absolute;
203
- inset-inline-start: 0.7rem;
204
- top: 50%;
205
- line-height: 0;
206
201
  color: var(--bgl-gray);
202
+ position: absolute;
203
+ bottom: 0px;
204
+ inset-inline-start: 0.25rem;
205
+ margin-block: calc(var(--input-height) / 2 - 16px);
206
+ }
207
+ .txtInputIconStart input{
208
+ padding-inline-start: 2rem;
207
209
  }
208
210
 
209
211
  .txtInputIconStart textarea {
@@ -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'
@@ -22,43 +22,45 @@ function toggleMenu() {
22
22
  </script>
23
23
 
24
24
  <template>
25
- <Card
26
- class="flex column gap-05 pt-1 bgl_sidebar"
27
- :class="{ wideNav: isOpen }"
28
- >
29
- <div class="w-100 px-075">
30
- <Btn
31
- thin
32
- color="light"
33
- class="toggleNav mb-05"
34
- icon="keyboard_arrow_right"
35
- @click="toggleMenu"
36
- />
37
- </div>
38
- <slot v-if="!isOpen || !slots['brand-open']" name="brand" />
39
- <slot v-if="isOpen" name="brand-open" />
40
- <slot v-if="!navLinks" />
25
+ <div class="vh-100 relative p-05 bgl_sidebar" :class="{ wideNav: isOpen }">
41
26
  <Btn
42
- v-for="(nav, i) in navLinks"
43
- :key="i"
44
- v-tooltip.right="{
45
- content: nav.label,
46
- disabled: open,
47
- class: ['nav-tooltip'],
48
- }"
49
- :to="nav.to"
50
- class="nav-button px-075"
27
+ thin
28
+ color="white"
29
+ class="mb-05 absolute toggleNav
30
+ z-2 border"
31
+ icon="keyboard_arrow_right"
32
+ @click="toggleMenu"
33
+ />
34
+ <Card
35
+ class="py-1 px-05 h-100 flex column gap-05 round relative bg-primary font-light overflow-y "
51
36
  >
52
- <template #default>
53
- <Icon :icon="nav.icon" :size="1.4" />
54
- <p v-if="open">
55
- {{ nav.label }}
56
- </p>
57
- </template>
58
- </Btn>
59
- <div style="flex-grow: 1" />
60
- <slot name="footer" />
61
- </Card>
37
+ <slot v-if="!isOpen || !slots['brand-open']" name="brand" />
38
+ <slot v-if="isOpen" name="brand-open" />
39
+ <slot v-if="!navLinks" />
40
+ <Btn
41
+ v-for="(nav, i) in navLinks"
42
+ :key="i"
43
+ v-tooltip.right="{
44
+ content: nav.label,
45
+ disabled: open,
46
+ class: ['nav-tooltip'],
47
+ }"
48
+ :to="nav.to"
49
+ class="nav-button px-075 me-auto w-100"
50
+ >
51
+ <template #default>
52
+ <Icon :icon="nav.icon" :size="1.4" />
53
+ <transition name="showP">
54
+ <p v-if="open">
55
+ {{ nav.label }}
56
+ </p>
57
+ </transition>
58
+ </template>
59
+ </Btn>
60
+ <div style="flex-grow: 1" />
61
+ <slot name="footer" />
62
+ </Card>
63
+ </div>
62
64
  </template>
63
65
 
64
66
  <style>
@@ -70,34 +72,24 @@ function toggleMenu() {
70
72
  box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
71
73
  }
72
74
 
73
- .wideNav .nav-button .bgl_btn-flex {
74
- justify-content: flex-start !important;
75
- }
76
-
77
75
  .nav-button.router-link-active {
78
76
  background: var(--bgl-popup-bg) !important;
79
77
  color: var(--bgl-primary);
80
78
  }
79
+ .nav-button .bgl_btn-flex{
80
+ justify-content: flex-start !important;
81
+ }
81
82
  </style>
82
83
 
83
84
  <style scoped>
84
85
  .bgl_sidebar {
85
- background-color: var(--bgl-primary);
86
- color: var(--bgl-white);
87
- overflow: hidden;
88
- border-radius: var(--card-border-radius);
89
- margin: 0.5rem;
90
- width: calc(100% - 1rem);
91
- padding: 0.5rem;
92
- overflow-y: auto;
93
- /* scrollbar-gutter: stable both-edges; */
94
-
95
86
  }
96
87
  .toggleNav {
97
88
  height: 22px !important;
98
89
  width: 22px !important;
99
- opacity: 0.4;
100
90
  transition: var(--bgl-transition);
91
+ top: 2rem;
92
+ inset-inline-end: -3px;
101
93
  }
102
94
  .toggleNav:hover {
103
95
  opacity: 1;
@@ -107,11 +99,6 @@ function toggleMenu() {
107
99
  transform: rotate(180deg);
108
100
  }
109
101
 
110
- .wideNav .nav-button {
111
- padding-inline-start: 1rem !important;
112
- width: 100%;
113
- }
114
-
115
102
  [dir='rtl'] .toggleNav {
116
103
  transform: rotate(180deg);
117
104
  }
@@ -120,30 +107,14 @@ function toggleMenu() {
120
107
  transform: rotate(0deg);
121
108
  }
122
109
 
123
- .nav-button.bgl_btn-icon {
124
- border-radius: var(--btn-border-radius) !important;
110
+ .showP-enter-active,
111
+ .showP-leave-active {
112
+ transition: all 0.5s ease;
125
113
  }
126
114
 
127
- .nav-button p {
128
- transition: var(--nav-tran);
129
- animation: fade 2s;
130
- animation-duration: 900ms;
131
- }
132
-
133
- @keyframes fade {
134
- 0% {
135
- opacity: 0;
136
- transform: translateX(-20px);
137
- }
138
-
139
- 30% {
140
- opacity: 0;
141
- transform: translateX(-20px);
142
- }
143
-
144
- 100% {
145
- opacity: 1;
146
- transform: translateX(0);
147
- }
115
+ .showP-enter-from,
116
+ .showP-leave-to {
117
+ opacity: 0;
118
+ transform: translateX(-20px);
148
119
  }
149
120
  </style>
@@ -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="close"
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
- <Btn flat class="fixed top-1 start-1 color-white" icon="close" @click="close" />
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
- <img :src="currentItem?.src" alt="Preview" class="vw90 lightbox-image">
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
@@ -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/
@@ -4,4 +4,6 @@ export interface LightboxItem {
4
4
  name: string
5
5
  thumbnail?: string
6
6
  group?: string
7
+ enableZoom?: boolean
8
+ openFile?: boolean
7
9
  }
@@ -85,6 +85,12 @@ select {
85
85
  font: inherit;
86
86
  }
87
87
 
88
+ button::-moz-focus-inner,
89
+ input::-moz-focus-inner {
90
+ border: 0;
91
+ padding: 0;
92
+ }
93
+
88
94
 
89
95
  @media screen and (max-width: 910px) {
90
96
 
@@ -69,6 +69,10 @@
69
69
  border: 1px solid var(--border-color);
70
70
  }
71
71
 
72
+ .border-primary {
73
+ border: 1px solid var(--bgl-primary);
74
+ }
75
+
72
76
  .rotate-180 {
73
77
  transform: rotate(180deg);
74
78
  }