@bagelink/vue 1.16.0 → 1.17.6
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/assetTransport.d.ts +28 -0
- package/dist/assetTransport.d.ts.map +1 -0
- package/dist/assetTransport.test.d.ts +2 -0
- package/dist/assetTransport.test.d.ts.map +1 -0
- package/dist/assets.d.ts +65 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.test.d.ts +2 -0
- package/dist/assets.test.d.ts.map +1 -0
- package/dist/components/AddressSearch.vue.d.ts +40 -36
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/ContextMenu.vue.d.ts +518 -0
- package/dist/components/ContextMenu.vue.d.ts.map +1 -0
- package/dist/components/Dropdown.types.d.ts +25 -0
- package/dist/components/Dropdown.types.d.ts.map +1 -0
- package/dist/components/Dropdown.vue.d.ts +20 -15
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/FilterQuery.vue.d.ts +1 -1
- package/dist/components/FilterQuery.vue.d.ts.map +1 -1
- package/dist/components/Icon/Icon.vue.d.ts +0 -1
- package/dist/components/Icon/Icon.vue.d.ts.map +1 -1
- package/dist/components/Icon/constants.d.ts +0 -1
- package/dist/components/Icon/constants.d.ts.map +1 -1
- package/dist/components/Icon/types.d.ts +2 -3
- package/dist/components/Icon/types.d.ts.map +1 -1
- package/dist/components/Image.vue.d.ts +7 -1
- package/dist/components/Image.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts +1 -1
- package/dist/components/draggable/useDraggable.d.ts.map +1 -1
- package/dist/components/form/inputs/IconPickerInput.vue.d.ts +48 -37
- package/dist/components/form/inputs/IconPickerInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +3 -0
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/selection.d.ts +2 -0
- package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/table.d.ts +34 -8
- package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +71 -63
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
- package/dist/components/form/inputs/Upload/AssetUploadInput.vue.d.ts +11 -0
- package/dist/components/form/inputs/Upload/AssetUploadInput.vue.d.ts.map +1 -0
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts +2 -2
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/lightbox/AssetPreview.vue.d.ts +9 -0
- package/dist/components/lightbox/AssetPreview.vue.d.ts.map +1 -0
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/components/lightbox/LightboxImage.vue.d.ts +2 -1
- package/dist/components/lightbox/LightboxImage.vue.d.ts.map +1 -1
- package/dist/components/lightbox/index.d.ts.map +1 -1
- package/dist/components/lightbox/lightbox.types.d.ts +7 -0
- package/dist/components/lightbox/lightbox.types.d.ts.map +1 -1
- package/dist/composables/index.d.ts +1 -1
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useBoundedRectDrag.d.ts.map +1 -1
- package/dist/composables/useImageSrc.d.ts +8 -4
- package/dist/composables/useImageSrc.d.ts.map +1 -1
- package/dist/composables/usePointerSession.d.ts +14 -3
- package/dist/composables/usePointerSession.d.ts.map +1 -1
- package/dist/core.css +3 -3
- package/dist/directives/vResize.d.ts.map +1 -1
- package/dist/form-flow/FormFlowPlayer.vue.d.ts +3 -3
- package/dist/form-flow/FormFlowPlayer.vue.d.ts.map +1 -1
- package/dist/form-flow/form-flow.d.ts +13 -6
- package/dist/form-flow/form-flow.d.ts.map +1 -1
- package/dist/form-flow/schema-fields.d.ts +6 -0
- package/dist/form-flow/schema-fields.d.ts.map +1 -1
- package/dist/index.cjs +240 -153
- package/dist/index.css +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +24167 -23417
- package/dist/style.css +3 -3
- package/dist/types/BagelForm.d.ts +5 -1
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/assetTransport.test.ts +75 -0
- package/src/assetTransport.ts +98 -0
- package/src/assets.test.ts +82 -0
- package/src/assets.ts +162 -0
- package/src/components/ContextMenu.vue +113 -0
- package/src/components/Dropdown.types.ts +27 -0
- package/src/components/Dropdown.vue +129 -64
- package/src/components/FilterQuery.vue +1 -1
- package/src/components/Icon/Icon.vue +8 -30
- package/src/components/Icon/constants.ts +0 -166
- package/src/components/Icon/types.ts +2 -3
- package/src/components/Image.vue +19 -3
- package/src/components/draggable/useDraggable.ts +67 -100
- package/src/components/form/inputs/IconPickerInput.vue +14 -9
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +43 -111
- package/src/components/form/inputs/RichText/composables/useCommands.ts +32 -54
- package/src/components/form/inputs/RichText/composables/useEditor.ts +45 -171
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +90 -14
- package/src/components/form/inputs/RichText/index.vue +1195 -1210
- package/src/components/form/inputs/RichText/utils/commands.ts +689 -1028
- package/src/components/form/inputs/RichText/utils/selection.ts +66 -41
- package/src/components/form/inputs/RichText/utils/table.ts +798 -136
- package/src/components/form/inputs/SelectInput.vue +1 -1
- package/src/components/form/inputs/Upload/AssetUploadInput.vue +67 -0
- package/src/components/form/inputs/Upload/UploadInput.vue +5 -1
- package/src/components/index.ts +3 -1
- package/src/components/lightbox/AssetPreview.vue +16 -0
- package/src/components/lightbox/Lightbox.vue +8 -3
- package/src/components/lightbox/LightboxImage.vue +4 -3
- package/src/components/lightbox/index.ts +29 -4
- package/src/components/lightbox/lightbox.types.ts +5 -1
- package/src/composables/index.ts +1 -1
- package/src/composables/useBoundedRectDrag.ts +9 -12
- package/src/composables/useImageSrc.ts +47 -37
- package/src/composables/usePointerSession.ts +57 -12
- package/src/directives/vResize.ts +6 -7
- package/src/form-flow/form-flow.ts +31 -7
- package/src/form-flow/schema-fields.ts +28 -1
- package/src/index.ts +3 -0
- package/src/styles/core.css +1 -18
- package/src/types/BagelForm.ts +5 -1
- package/dist/components/form/inputs/RichText/utils/formatting.d.ts +0 -10
- package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +0 -1
- package/src/components/form/inputs/RichText/components/gridBox.vue +0 -58
- package/src/components/form/inputs/RichText/utils/formatting.ts +0 -331
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MATERIAL_ICONS, FONT_AWESOME_BRANDS_ICONS } from './constants'
|
|
2
2
|
|
|
3
|
-
export type FontAwesomeIcons = (typeof FONT_AWESOME_ICONS)[number]
|
|
4
3
|
export type FontAwesomeBrandsIcons = (typeof FONT_AWESOME_BRANDS_ICONS)[number]
|
|
5
4
|
export type MaterialIcons = (typeof MATERIAL_ICONS)[number]
|
|
6
5
|
|
|
7
|
-
export type IconType = MaterialIcons |
|
|
6
|
+
export type IconType = MaterialIcons | FontAwesomeBrandsIcons
|
package/src/components/Image.vue
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
defineOptions({ name: 'BglImage', inheritAttrs: false })
|
|
3
3
|
import { Skeleton, normalizeDimension, Icon, useImageSrc } from '../index'
|
|
4
|
+
import type { AssetDeliveryRequest, AssetPurpose } from '../assets'
|
|
4
5
|
import { Comment, computed, ref, useSlots, watch, type StyleValue, type VNode } from 'vue'
|
|
5
6
|
|
|
6
7
|
interface ImageProps {
|
|
7
|
-
src?: string
|
|
8
|
+
src?: string | AssetDeliveryRequest
|
|
9
|
+
/** Stable asset UUID; requires an injected asset client. */
|
|
10
|
+
assetId?: string
|
|
11
|
+
versionId?: string
|
|
12
|
+
/** Server preset; width/height below control layout only. */
|
|
13
|
+
purpose?: AssetPurpose
|
|
8
14
|
pathKey?: string
|
|
9
15
|
alt?: string
|
|
10
16
|
width?: string | number
|
|
@@ -25,10 +31,17 @@ interface ImageProps {
|
|
|
25
31
|
|
|
26
32
|
const props = defineProps<ImageProps>()
|
|
27
33
|
|
|
28
|
-
const { imageSrc, loadingError } = useImageSrc(() => props.
|
|
34
|
+
const { imageSrc, loadingError, onError } = useImageSrc(() => props.assetId
|
|
35
|
+
? { asset_id: props.assetId, version_id: props.versionId, representation: props.purpose }
|
|
36
|
+
: typeof props.src === 'object'
|
|
37
|
+
? { ...props.src, representation: props.purpose ?? props.src.representation }
|
|
38
|
+
: props.src ?? props.pathKey ?? props.modelValue)
|
|
29
39
|
const imageLoaded = ref(false)
|
|
30
40
|
|
|
31
|
-
watch(imageSrc,
|
|
41
|
+
watch(imageSrc, value => {
|
|
42
|
+
if (!value) imageLoaded.value = false
|
|
43
|
+
})
|
|
44
|
+
watch(() => [props.assetId, props.versionId, props.purpose, props.src, props.pathKey, props.modelValue], () => {
|
|
32
45
|
imageLoaded.value = false
|
|
33
46
|
})
|
|
34
47
|
|
|
@@ -101,6 +114,7 @@ const imgFit = computed(() => props.fit ?? 'cover')
|
|
|
101
114
|
:class="[frameHasHeight ? 'absolute-fill' : 'bgl-image-frame-img--flow', { 'opacity-0': !imageLoaded }]"
|
|
102
115
|
:style="{ objectFit: imgFit, mixBlendMode: blend } as StyleValue"
|
|
103
116
|
@load="onImageLoad"
|
|
117
|
+
@error="onError"
|
|
104
118
|
>
|
|
105
119
|
<Skeleton v-if="!imageLoaded && !loadingError" class="absolute-fill" />
|
|
106
120
|
<div v-else-if="loadingError && !gradientCss" class="flex-center absolute-fill error-image">
|
|
@@ -120,6 +134,7 @@ const imgFit = computed(() => props.fit ?? 'cover')
|
|
|
120
134
|
:height="normalizeDimension(height)"
|
|
121
135
|
:class="{ 'opacity-0': !imageLoaded }"
|
|
122
136
|
@load="onImageLoad"
|
|
137
|
+
@error="onError"
|
|
123
138
|
>
|
|
124
139
|
<Skeleton
|
|
125
140
|
v-if="!imageLoaded && !loadingError"
|
|
@@ -144,6 +159,7 @@ const imgFit = computed(() => props.fit ?? 'cover')
|
|
|
144
159
|
:class="{ 'opacity-0': !imageLoaded }"
|
|
145
160
|
:style="{ objectFit: imgFit }"
|
|
146
161
|
@load="onImageLoad"
|
|
162
|
+
@error="onError"
|
|
147
163
|
>
|
|
148
164
|
<div v-if="loadingError" class="flex-center absolute-fill error-image">
|
|
149
165
|
<Icon name="broken_image" />
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ref, computed, toValue, getCurrentInstance, onUnmounted, type MaybeRefOrGetter } from 'vue'
|
|
2
|
+
import { createPointerSession } from '../../composables/usePointerSession'
|
|
2
3
|
import { insertionIndexToFinalIndex } from './reorder'
|
|
3
4
|
|
|
4
5
|
export interface DraggableEvent extends MouseEvent {
|
|
@@ -37,10 +38,9 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
37
38
|
const dropIndicator = ref<HTMLElement | null>(null)
|
|
38
39
|
const dropTarget = ref<DraggableElement | null>(null)
|
|
39
40
|
|
|
40
|
-
// Only run in browser environment
|
|
41
41
|
const isBrowser = typeof window !== 'undefined'
|
|
42
|
+
const pointerSession = createPointerSession()
|
|
42
43
|
|
|
43
|
-
// Move style injection into setup
|
|
44
44
|
if (isBrowser) {
|
|
45
45
|
const existingStyle = document.getElementById('draggable-style')
|
|
46
46
|
if (!existingStyle) {
|
|
@@ -83,8 +83,6 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
83
83
|
function appendToTopLayer(element: HTMLElement) {
|
|
84
84
|
document.body.appendChild(element)
|
|
85
85
|
element.setAttribute('popover', 'manual')
|
|
86
|
-
// The browser's popover UA styles use `inset: 0; margin: auto`, which
|
|
87
|
-
// recentres fixed-position drag helpers and offsets them from the cursor.
|
|
88
86
|
element.style.inset = 'auto'
|
|
89
87
|
element.style.margin = '0'
|
|
90
88
|
element.style.padding = '0'
|
|
@@ -128,9 +126,8 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
128
126
|
line.style.background = 'var(--primary-color, #0066ff)'
|
|
129
127
|
line.style.boxShadow = '0 0 4px rgba(0, 102, 255, 0.5)'
|
|
130
128
|
line.style.transition = 'all 0.15s ease'
|
|
131
|
-
line.style.display = 'none'
|
|
129
|
+
line.style.display = 'none'
|
|
132
130
|
|
|
133
|
-
// Add the dot indicator
|
|
134
131
|
const dot = document.createElement('div')
|
|
135
132
|
dot.style.position = 'absolute'
|
|
136
133
|
dot.style.left = '-4px'
|
|
@@ -153,7 +150,6 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
153
150
|
dropIndicator.value.style.width = `${rect.width}px`
|
|
154
151
|
dropIndicator.value.style.left = `${rect.left}px`
|
|
155
152
|
|
|
156
|
-
// Move indicator to match the actual drop position
|
|
157
153
|
if (isAfter) {
|
|
158
154
|
dropIndicator.value.style.top = `${rect.bottom}px`
|
|
159
155
|
} else {
|
|
@@ -180,15 +176,6 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
180
176
|
return 2
|
|
181
177
|
}
|
|
182
178
|
|
|
183
|
-
// function resetDragStyles(dragEl: DraggableElement) {
|
|
184
|
-
// if (defaultOptions.value.mode === 'ghost') {
|
|
185
|
-
// dragEl.style.opacity = ''
|
|
186
|
-
// dragEl.style.transform = ''
|
|
187
|
-
// } else {
|
|
188
|
-
// dragEl.style.opacity = ''
|
|
189
|
-
// }
|
|
190
|
-
// }
|
|
191
|
-
|
|
192
179
|
function cacheElementPositions(draggedList: DraggableElement[]) {
|
|
193
180
|
POSITION_CACHE.clear()
|
|
194
181
|
draggedList.forEach((el, index) => {
|
|
@@ -203,11 +190,9 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
203
190
|
function calculateDropIndex(e: MouseEvent, draggedList: DraggableElement[], dragIndex: number, newDropTarget: DraggableElement) {
|
|
204
191
|
const now = Date.now()
|
|
205
192
|
if (now - lastUpdateTime < UPDATE_THRESHOLD) {
|
|
206
|
-
// Return last calculated index if we're within threshold
|
|
207
193
|
return lastDropIndex !== -1 ? lastDropIndex : dragIndex
|
|
208
194
|
}
|
|
209
195
|
|
|
210
|
-
// Use cached positions if available, otherwise calculate new ones
|
|
211
196
|
if (POSITION_CACHE.size === 0) {
|
|
212
197
|
cacheElementPositions(draggedList)
|
|
213
198
|
}
|
|
@@ -221,18 +206,14 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
221
206
|
const elementHeight = targetPos.bottom - targetPos.top
|
|
222
207
|
const relativeY = mouseY - targetPos.top
|
|
223
208
|
|
|
224
|
-
// Calculate sticky zones
|
|
225
209
|
const upperSticky = elementHeight * 0.3
|
|
226
210
|
const lowerSticky = elementHeight * 0.7
|
|
227
211
|
|
|
228
212
|
let dropIndex = targetIndex
|
|
229
213
|
|
|
230
|
-
// Different behavior based on context
|
|
231
214
|
if (targetIndex === dragIndex) {
|
|
232
|
-
// On original element - require significant movement
|
|
233
215
|
if (relativeY > lowerSticky) { dropIndex += 1 }
|
|
234
216
|
} else if (targetIndex === lastDropIndex) {
|
|
235
|
-
// On previously targeted element - use sticky zones
|
|
236
217
|
if (targetIndex > dragIndex) {
|
|
237
218
|
if (relativeY < upperSticky) { dropIndex = targetIndex }
|
|
238
219
|
else { dropIndex = targetIndex + 1 }
|
|
@@ -241,18 +222,16 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
241
222
|
else { dropIndex = targetIndex }
|
|
242
223
|
}
|
|
243
224
|
} else {
|
|
244
|
-
// On new element - use 60/40 split
|
|
245
225
|
if (relativeY > elementHeight * 0.6) { dropIndex += 1 }
|
|
246
226
|
}
|
|
247
227
|
|
|
248
|
-
// Update state
|
|
249
228
|
lastDropIndex = dropIndex
|
|
250
229
|
lastUpdateTime = now
|
|
251
230
|
|
|
252
231
|
return dropIndex
|
|
253
232
|
}
|
|
254
233
|
|
|
255
|
-
function
|
|
234
|
+
function resetVisualState() {
|
|
256
235
|
if (!isBrowser) { return }
|
|
257
236
|
|
|
258
237
|
document.removeEventListener('mousemove', onDragMove)
|
|
@@ -268,7 +247,6 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
268
247
|
ghostElement.value = null
|
|
269
248
|
dropIndicator.value = null
|
|
270
249
|
|
|
271
|
-
// Reset all elements
|
|
272
250
|
if (dragElement.value) {
|
|
273
251
|
const list = dragElement.value.__drag_list
|
|
274
252
|
list?.forEach((el) => {
|
|
@@ -299,58 +277,85 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
299
277
|
POSITION_CACHE.clear()
|
|
300
278
|
}
|
|
301
279
|
|
|
280
|
+
function cleanup() {
|
|
281
|
+
pointerSession.cancel()
|
|
282
|
+
resetVisualState()
|
|
283
|
+
}
|
|
284
|
+
|
|
302
285
|
function onKeyDown(e: KeyboardEvent) {
|
|
303
286
|
if (e.key === 'Escape' && isDragging.value) {
|
|
304
287
|
cleanup()
|
|
305
288
|
}
|
|
306
289
|
}
|
|
307
290
|
|
|
308
|
-
function
|
|
309
|
-
if (!isBrowser || isDragging.value || defaultOptions.value.disabled) { return }
|
|
310
|
-
if (e.button !== 0) { return } // Only left mouse button
|
|
311
|
-
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) { return }
|
|
312
|
-
|
|
313
|
-
const dragEl = findDraggableParent(e.target as Element)
|
|
314
|
-
if (!dragEl || dragEl.dataset.draggable === 'false' || e.currentTarget !== dragEl) { return }
|
|
315
|
-
|
|
316
|
-
// If handle is specified, check if clicked element matches
|
|
317
|
-
if (defaultOptions.value.handle) {
|
|
318
|
-
const handleEl = (e.target as Element).closest(defaultOptions.value.handle)
|
|
319
|
-
if (!handleEl || !dragEl.contains(handleEl)) { return }
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
e.preventDefault()
|
|
291
|
+
function beginDrag(dragEl: DraggableElement, clientX: number, clientY: number, rawEvent: MouseEvent) {
|
|
323
292
|
isDragging.value = true
|
|
324
293
|
dragElement.value = dragEl
|
|
325
294
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
startY = e.clientY
|
|
295
|
+
startX = clientX
|
|
296
|
+
startY = clientY
|
|
329
297
|
const rect = dragEl.getBoundingClientRect()
|
|
330
298
|
initialX = rect.left
|
|
331
299
|
initialY = rect.top
|
|
332
300
|
|
|
333
|
-
// Create ghost element that follows mouse
|
|
334
301
|
ghostElement.value = createGhost(dragEl)
|
|
335
302
|
if (ghostElement.value) {
|
|
336
303
|
ghostElement.value.style.left = `${initialX}px`
|
|
337
304
|
ghostElement.value.style.top = `${initialY}px`
|
|
338
305
|
}
|
|
339
306
|
|
|
340
|
-
// Only create drop indicator in line mode
|
|
341
307
|
if (defaultOptions.value.mode === 'line') {
|
|
342
308
|
dropIndicator.value = createDropIndicator()
|
|
343
309
|
}
|
|
344
310
|
|
|
345
|
-
// Apply mode-specific styles and get shift amount
|
|
346
311
|
dragEl.classList.add(defaultOptions.value.dragClass!)
|
|
347
312
|
shiftAmount = defaultOptions.value.mode === 'ghost'
|
|
348
313
|
? handleGhostMode(dragEl)
|
|
349
314
|
: handleLineMode(dragEl)
|
|
350
315
|
|
|
351
|
-
defaultOptions.value.onStart?.(
|
|
316
|
+
defaultOptions.value.onStart?.(rawEvent)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function onPointerStart(e: PointerEvent) {
|
|
320
|
+
if (!isBrowser || isDragging.value || defaultOptions.value.disabled) { return }
|
|
321
|
+
if (e.button !== 0 || e.isPrimary === false) { return }
|
|
322
|
+
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) { return }
|
|
323
|
+
|
|
324
|
+
const dragEl = findDraggableParent(e.target as Element)
|
|
325
|
+
if (!dragEl || dragEl.dataset.draggable === 'false' || e.currentTarget !== dragEl) { return }
|
|
326
|
+
|
|
327
|
+
if (defaultOptions.value.handle) {
|
|
328
|
+
const handleEl = (e.target as Element).closest(defaultOptions.value.handle)
|
|
329
|
+
if (!handleEl || !dragEl.contains(handleEl)) { return }
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
beginDrag(dragEl, e.clientX, e.clientY, e)
|
|
333
|
+
|
|
334
|
+
pointerSession.start(e, {
|
|
335
|
+
cursor: 'grabbing',
|
|
336
|
+
target: dragEl,
|
|
337
|
+
onMove: next => onDragMove(next),
|
|
338
|
+
onEnd: next => onDragEnd(next),
|
|
339
|
+
onCancel: () => resetVisualState(),
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function onDragStart(e: MouseEvent) {
|
|
344
|
+
if (!isBrowser || isDragging.value || defaultOptions.value.disabled) { return }
|
|
345
|
+
if (e.button !== 0) { return }
|
|
346
|
+
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) { return }
|
|
347
|
+
|
|
348
|
+
const dragEl = findDraggableParent(e.target as Element)
|
|
349
|
+
if (!dragEl || dragEl.dataset.draggable === 'false' || e.currentTarget !== dragEl) { return }
|
|
350
|
+
|
|
351
|
+
if (defaultOptions.value.handle) {
|
|
352
|
+
const handleEl = (e.target as Element).closest(defaultOptions.value.handle)
|
|
353
|
+
if (!handleEl || !dragEl.contains(handleEl)) { return }
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
e.preventDefault()
|
|
357
|
+
beginDrag(dragEl, e.clientX, e.clientY, e)
|
|
352
358
|
|
|
353
|
-
// Prevent text selection during drag
|
|
354
359
|
if (isBrowser) {
|
|
355
360
|
document.body.style.userSelect = 'none'
|
|
356
361
|
document.body.style.webkitUserSelect = 'none'
|
|
@@ -383,18 +388,15 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
383
388
|
const scrollParent = findScrollParent(dragElement.value!) as HTMLElement
|
|
384
389
|
const rect = scrollParent.getBoundingClientRect()
|
|
385
390
|
const margin = 50
|
|
386
|
-
const speed = Math.min(20, rect.height * 0.1)
|
|
391
|
+
const speed = Math.min(20, rect.height * 0.1)
|
|
387
392
|
|
|
388
393
|
const { scrollTop } = scrollParent
|
|
389
394
|
const maxScroll = scrollParent.scrollHeight - scrollParent.clientHeight
|
|
390
395
|
|
|
391
|
-
// Use requestAnimationFrame for smoother scrolling
|
|
392
396
|
if (e.clientY - rect.top < margin) {
|
|
393
|
-
|
|
394
|
-
scrollParent.scrollTop = newScroll
|
|
397
|
+
scrollParent.scrollTop = Math.max(0, scrollTop - speed)
|
|
395
398
|
} else if (rect.bottom - e.clientY < margin) {
|
|
396
|
-
|
|
397
|
-
scrollParent.scrollTop = newScroll
|
|
399
|
+
scrollParent.scrollTop = Math.min(maxScroll, scrollTop + speed)
|
|
398
400
|
}
|
|
399
401
|
})
|
|
400
402
|
}
|
|
@@ -402,23 +404,19 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
402
404
|
function onDragMove(e: MouseEvent) {
|
|
403
405
|
if (!isDragging.value || !dragElement.value) { return }
|
|
404
406
|
|
|
405
|
-
// Move ghost element with cursor
|
|
406
407
|
if (ghostElement.value) {
|
|
407
408
|
const dx = e.clientX - startX
|
|
408
409
|
const dy = e.clientY - startY
|
|
409
410
|
ghostElement.value.style.transform = `translate3d(${dx}px, ${dy}px, 0)`
|
|
410
411
|
}
|
|
411
412
|
|
|
412
|
-
// Get the draggable list and current positions
|
|
413
413
|
const list = dragElement.value.__drag_list!
|
|
414
414
|
const draggedList = list.filter(el => el.dataset.draggable !== 'false')
|
|
415
415
|
const dragIndex = draggedList.indexOf(dragElement.value)
|
|
416
416
|
|
|
417
|
-
// Find the element at cursor position
|
|
418
417
|
const elementAtPoint = document.elementFromPoint(e.clientX, e.clientY)
|
|
419
418
|
const newDropTarget = findDraggableParent(elementAtPoint)
|
|
420
419
|
|
|
421
|
-
// Validate drop target
|
|
422
420
|
if (!newDropTarget || !draggedList.includes(newDropTarget)) {
|
|
423
421
|
dropTarget.value = null
|
|
424
422
|
if (dropIndicator.value) {
|
|
@@ -429,23 +427,19 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
429
427
|
|
|
430
428
|
const dropIndex = calculateDropIndex(e, draggedList, dragIndex, newDropTarget)
|
|
431
429
|
|
|
432
|
-
// Only update if position actually changed
|
|
433
430
|
if (dropTarget.value !== newDropTarget || dropTarget.value.__intended_index !== dropIndex) {
|
|
434
431
|
dropTarget.value = newDropTarget
|
|
435
432
|
dropTarget.value.__intended_index = dropIndex
|
|
436
433
|
|
|
437
|
-
// Update line indicator
|
|
438
434
|
if (dropIndicator.value && defaultOptions.value.mode === 'line') {
|
|
439
435
|
const targetIndex = draggedList.indexOf(newDropTarget)
|
|
440
436
|
const isAfter = dropIndex > targetIndex
|
|
441
437
|
updateDropIndicator(newDropTarget, isAfter)
|
|
442
438
|
}
|
|
443
439
|
|
|
444
|
-
// Update element positions
|
|
445
440
|
const finalIndex = insertionIndexToFinalIndex(dragIndex, dropIndex, draggedList.length)
|
|
446
441
|
const shift = shiftAmount
|
|
447
442
|
draggedList.forEach((el) => {
|
|
448
|
-
// Use transform3d for better performance
|
|
449
443
|
el.style.transition = `transform ${defaultOptions.value.animation}ms cubic-bezier(0.2, 0, 0, 1)`
|
|
450
444
|
el.style.transform = 'translate3d(0, 0, 0)'
|
|
451
445
|
|
|
@@ -473,19 +467,19 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
473
467
|
}
|
|
474
468
|
|
|
475
469
|
function onDragEnd(e: MouseEvent) {
|
|
476
|
-
if (defaultOptions.value.disabled) {
|
|
470
|
+
if (defaultOptions.value.disabled) { resetVisualState(); return }
|
|
477
471
|
if (isDragging.value) {
|
|
478
472
|
lastUpdateTime = 0
|
|
479
473
|
onDragMove(e)
|
|
480
474
|
}
|
|
481
475
|
if (!isDragging.value || !dragElement.value || !dropTarget.value) {
|
|
482
|
-
|
|
476
|
+
resetVisualState()
|
|
483
477
|
return
|
|
484
478
|
}
|
|
485
479
|
|
|
486
480
|
const list = dragElement.value.__drag_list
|
|
487
481
|
if (!list || !list.length) {
|
|
488
|
-
|
|
482
|
+
resetVisualState()
|
|
489
483
|
return
|
|
490
484
|
}
|
|
491
485
|
|
|
@@ -506,12 +500,12 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
506
500
|
dragEvent.newIndex = actualDropIndex
|
|
507
501
|
dragEvent.item = defaultOptions.value.items?.[actualDragIndex] ?? dragElement.value.__drag_data
|
|
508
502
|
|
|
509
|
-
|
|
503
|
+
resetVisualState()
|
|
510
504
|
defaultOptions.value.onEnd?.(dragEvent)
|
|
511
505
|
return
|
|
512
506
|
}
|
|
513
507
|
|
|
514
|
-
|
|
508
|
+
resetVisualState()
|
|
515
509
|
}
|
|
516
510
|
|
|
517
511
|
function onTouchStart(e: TouchEvent) {
|
|
@@ -528,38 +522,10 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
528
522
|
const { handle } = defaultOptions.value
|
|
529
523
|
if (handle && !target.closest(handle)) { return }
|
|
530
524
|
|
|
531
|
-
e.preventDefault()
|
|
532
|
-
|
|
533
|
-
dragElement.value = draggableParent
|
|
534
|
-
|
|
535
|
-
startX = touch.clientX
|
|
536
|
-
startY = touch.clientY
|
|
537
|
-
const rect = draggableParent.getBoundingClientRect()
|
|
538
|
-
initialX = rect.left
|
|
539
|
-
initialY = rect.top
|
|
540
|
-
|
|
541
|
-
// Rest of the setup same as onDragStart...
|
|
542
|
-
ghostElement.value = createGhost(draggableParent)
|
|
543
|
-
if (ghostElement.value) {
|
|
544
|
-
ghostElement.value.style.left = `${initialX}px`
|
|
545
|
-
ghostElement.value.style.top = `${initialY}px`
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Only create drop indicator in line mode
|
|
549
|
-
if (defaultOptions.value.mode === 'line') {
|
|
550
|
-
dropIndicator.value = createDropIndicator()
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Apply mode-specific styles and get shift amount
|
|
554
|
-
dragElement.value.classList.add(defaultOptions.value.dragClass!)
|
|
555
|
-
shiftAmount = defaultOptions.value.mode === 'ghost'
|
|
556
|
-
? handleGhostMode(dragElement.value)
|
|
557
|
-
: handleLineMode(dragElement.value)
|
|
558
|
-
|
|
559
|
-
defaultOptions.value.onStart?.(e as unknown as MouseEvent)
|
|
525
|
+
e.preventDefault()
|
|
526
|
+
beginDrag(draggableParent, touch.clientX, touch.clientY, e as unknown as MouseEvent)
|
|
560
527
|
|
|
561
528
|
if (isBrowser) {
|
|
562
|
-
// Prevent text selection during drag
|
|
563
529
|
document.body.style.userSelect = 'none'
|
|
564
530
|
document.body.style.webkitUserSelect = 'none'
|
|
565
531
|
document.body.style.cursor = 'grabbing'
|
|
@@ -573,7 +539,7 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
573
539
|
}
|
|
574
540
|
|
|
575
541
|
function onTouchMove(e: TouchEvent) {
|
|
576
|
-
e.preventDefault()
|
|
542
|
+
e.preventDefault()
|
|
577
543
|
const touch = e.touches[0]
|
|
578
544
|
if (!touch) { return }
|
|
579
545
|
|
|
@@ -607,7 +573,6 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
607
573
|
if (!isBrowser) { return }
|
|
608
574
|
cleanupFn?.()
|
|
609
575
|
|
|
610
|
-
// Reset state
|
|
611
576
|
isDragging.value = false
|
|
612
577
|
dragElement.value = null
|
|
613
578
|
ghostElement.value = null
|
|
@@ -633,6 +598,7 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
633
598
|
|
|
634
599
|
el.__drag_data = defaultOptions.value.items?.[el.__drag_index]
|
|
635
600
|
|
|
601
|
+
el.addEventListener('pointerdown', onPointerStart as EventListener)
|
|
636
602
|
el.addEventListener('mousedown', onDragStart)
|
|
637
603
|
el.addEventListener('touchstart', onTouchStart)
|
|
638
604
|
}
|
|
@@ -641,6 +607,7 @@ export function useDraggable(options: MaybeRefOrGetter<DraggableOptions> = {}) {
|
|
|
641
607
|
cleanupFn = () => {
|
|
642
608
|
cleanup()
|
|
643
609
|
elements.forEach((el) => {
|
|
610
|
+
el.removeEventListener('pointerdown', onPointerStart as EventListener)
|
|
644
611
|
el.removeEventListener('mousedown', onDragStart)
|
|
645
612
|
el.removeEventListener('touchstart', onTouchStart)
|
|
646
613
|
delete el.__drag_group
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
|
3
3
|
import { Dropdown, EmptyState, Icon, TabsNav, TextInput } from '../../../index'
|
|
4
|
-
import { FONT_AWESOME_BRANDS_ICONS,
|
|
4
|
+
import { FONT_AWESOME_BRANDS_ICONS, MATERIAL_ICONS } from '../../Icon/constants'
|
|
5
5
|
|
|
6
|
-
defineProps<{
|
|
6
|
+
withDefaults(defineProps<{
|
|
7
7
|
modelValue?: string
|
|
8
8
|
label?: string
|
|
9
9
|
placeholder?: string
|
|
10
|
-
|
|
10
|
+
emptyLabel?: string
|
|
11
|
+
searchPlaceholder?: string
|
|
12
|
+
noResultsText?: string
|
|
13
|
+
}>(), {
|
|
14
|
+
emptyLabel: 'Choose an icon',
|
|
15
|
+
searchPlaceholder: 'Search icons',
|
|
16
|
+
noResultsText: 'No icons found',
|
|
17
|
+
})
|
|
11
18
|
|
|
12
19
|
const emit = defineEmits<{
|
|
13
20
|
'update:modelValue': [value: string | undefined]
|
|
@@ -15,20 +22,18 @@ const emit = defineEmits<{
|
|
|
15
22
|
|
|
16
23
|
const dropdown = ref<InstanceType<typeof Dropdown> | null>(null)
|
|
17
24
|
const search = ref('')
|
|
18
|
-
const activeTab = ref<'material' | '
|
|
25
|
+
const activeTab = ref<'material' | 'brands'>('material')
|
|
19
26
|
const visibleCount = ref(80)
|
|
20
27
|
const tabsGroup = `icon-picker-${Math.random().toString(36).slice(2)}`
|
|
21
28
|
let batchTimer: ReturnType<typeof setTimeout> | undefined
|
|
22
29
|
|
|
23
30
|
const tabs = [
|
|
24
31
|
{ label: 'Material', value: 'material' },
|
|
25
|
-
{ label: 'FA', value: 'fa' },
|
|
26
32
|
{ label: 'Brands', value: 'brands' },
|
|
27
33
|
]
|
|
28
34
|
|
|
29
35
|
const iconSets = {
|
|
30
36
|
material: MATERIAL_ICONS,
|
|
31
|
-
fa: FONT_AWESOME_ICONS,
|
|
32
37
|
brands: FONT_AWESOME_BRANDS_ICONS,
|
|
33
38
|
}
|
|
34
39
|
|
|
@@ -86,7 +91,7 @@ onBeforeUnmount(() => {
|
|
|
86
91
|
<Icon v-if="modelValue" :icon="modelValue" :size="1.1" />
|
|
87
92
|
<span v-else class="txt11 opacity-4">—</span>
|
|
88
93
|
</span>
|
|
89
|
-
<span class="txt12 flex-grow opacity-6 ellipsis-1">{{ modelValue || placeholder ||
|
|
94
|
+
<span class="txt12 flex-grow opacity-6 ellipsis-1">{{ modelValue || placeholder || emptyLabel }}</span>
|
|
90
95
|
<Icon v-if="modelValue" icon="close" :size="0.9" class="opacity-5 hover-opacity-10" @click.stop="clear" />
|
|
91
96
|
<Icon :icon="shown ? 'expand_less' : 'expand_more'" :size="1" class="opacity-4" />
|
|
92
97
|
</div>
|
|
@@ -94,7 +99,7 @@ onBeforeUnmount(() => {
|
|
|
94
99
|
|
|
95
100
|
<div class="w-300px overflow-hidden">
|
|
96
101
|
<TabsNav v-model="activeTab" :tabs="tabs" :group="tabsGroup" flat class="round-none w-100p mx-0 txt-center" />
|
|
97
|
-
<TextInput v-model="search" icon-start="search" placeholder="
|
|
102
|
+
<TextInput v-model="search" icon-start="search" :placeholder="searchPlaceholder" class="mb-0" />
|
|
98
103
|
<div class="grid grid-wrap-7 gap-025 p-05 overflow max-h-300px">
|
|
99
104
|
<div
|
|
100
105
|
v-for="icon in visibleIcons"
|
|
@@ -107,7 +112,7 @@ onBeforeUnmount(() => {
|
|
|
107
112
|
<Icon :icon="icon" :size="1.2" />
|
|
108
113
|
</div>
|
|
109
114
|
</div>
|
|
110
|
-
<EmptyState v-if="visibleIcons.length === 0" compact icon="none" description="
|
|
115
|
+
<EmptyState v-if="visibleIcons.length === 0" compact icon="none" :description="noResultsText" />
|
|
111
116
|
</div>
|
|
112
117
|
</Dropdown>
|
|
113
118
|
</div>
|