mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
Fix browser text selection during icon drag
- Add e.preventDefault() in handlePointerDown to prevent text selection - Add @dragstart.prevent to prevent native browser drag - Add select-none and @selectstart.prevent to workspace - Add mouseleave event listener to reset drag state when leaving window - Refactor grid positioning to use consistent iconPadding constant
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "haex-hub",
|
"name": "haex-hub",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.9",
|
"version": "0.1.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
@pointerdown.left="handlePointerDown"
|
@pointerdown.left="handlePointerDown"
|
||||||
@pointermove="handlePointerMove"
|
@pointermove="handlePointerMove"
|
||||||
@pointerup="handlePointerUp"
|
@pointerup="handlePointerUp"
|
||||||
|
@dragstart.prevent
|
||||||
@click.left="handleClick"
|
@click.left="handleClick"
|
||||||
@dblclick="handleDoubleClick"
|
@dblclick="handleDoubleClick"
|
||||||
>
|
>
|
||||||
@ -176,6 +177,9 @@ const style = computed(() => ({
|
|||||||
const handlePointerDown = (e: PointerEvent) => {
|
const handlePointerDown = (e: PointerEvent) => {
|
||||||
if (!draggableEl.value || !draggableEl.value.parentElement) return
|
if (!draggableEl.value || !draggableEl.value.parentElement) return
|
||||||
|
|
||||||
|
// Prevent any text selection during drag
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
isDragging.value = true
|
isDragging.value = true
|
||||||
emit('dragStart', props.id, props.itemType, props.referenceId, iconWidth.value, iconHeight.value, x.value, y.value)
|
emit('dragStart', props.id, props.itemType, props.referenceId, iconWidth.value, iconHeight.value, x.value, y.value)
|
||||||
|
|
||||||
|
|||||||
@ -25,12 +25,13 @@
|
|||||||
>
|
>
|
||||||
<UContextMenu :items="getWorkspaceContextMenuItems(workspace.id)">
|
<UContextMenu :items="getWorkspaceContextMenuItems(workspace.id)">
|
||||||
<div
|
<div
|
||||||
class="w-full h-full relative"
|
class="w-full h-full relative select-none"
|
||||||
:style="getWorkspaceBackgroundStyle(workspace)"
|
:style="getWorkspaceBackgroundStyle(workspace)"
|
||||||
@click.self.stop="handleDesktopClick"
|
@click.self.stop="handleDesktopClick"
|
||||||
@mousedown.left.self="handleAreaSelectStart"
|
@mousedown.left.self="handleAreaSelectStart"
|
||||||
@dragover.prevent="handleDragOver"
|
@dragover.prevent="handleDragOver"
|
||||||
@drop.prevent="handleDrop($event, workspace.id)"
|
@drop.prevent="handleDrop($event, workspace.id)"
|
||||||
|
@selectstart.prevent
|
||||||
>
|
>
|
||||||
<!-- Drop Target Zone (visible during drag) -->
|
<!-- Drop Target Zone (visible during drag) -->
|
||||||
<div
|
<div
|
||||||
@ -735,6 +736,21 @@ watch(currentWorkspace, async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Reset drag state when mouse leaves the document (fixes stuck dropzone)
|
||||||
|
useEventListener(document, 'mouseleave', () => {
|
||||||
|
if (isDragging.value) {
|
||||||
|
isDragging.value = false
|
||||||
|
currentDraggedItem.id = ''
|
||||||
|
currentDraggedItem.itemType = ''
|
||||||
|
currentDraggedItem.referenceId = ''
|
||||||
|
currentDraggedItem.width = 0
|
||||||
|
currentDraggedItem.height = 0
|
||||||
|
currentDraggedItem.x = 0
|
||||||
|
currentDraggedItem.y = 0
|
||||||
|
allowSwipe.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// Load workspaces first
|
// Load workspaces first
|
||||||
await workspaceStore.loadWorkspacesAsync()
|
await workspaceStore.loadWorkspacesAsync()
|
||||||
|
|||||||
@ -27,24 +27,26 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
const deviceStore = useDeviceStore()
|
const deviceStore = useDeviceStore()
|
||||||
const settingsStore = useVaultSettingsStore()
|
const settingsStore = useVaultSettingsStore()
|
||||||
|
|
||||||
$i18n.setLocaleMessage('de', {
|
$i18n.setLocaleMessage('de', { desktop: de })
|
||||||
desktop: de,
|
|
||||||
})
|
|
||||||
$i18n.setLocaleMessage('en', { desktop: en })
|
$i18n.setLocaleMessage('en', { desktop: en })
|
||||||
|
|
||||||
const desktopItems = ref<IDesktopItem[]>([])
|
const desktopItems = ref<IDesktopItem[]>([])
|
||||||
const selectedItemIds = ref<Set<string>>(new Set())
|
const selectedItemIds = ref<Set<string>>(new Set())
|
||||||
|
|
||||||
// Desktop Grid Settings (stored in DB per device)
|
// Desktop Grid Settings (stored in DB per device)
|
||||||
const iconSizePreset = ref<DesktopIconSizePreset>(DesktopIconSizePreset.medium)
|
const iconSizePreset = ref<DesktopIconSizePreset>(
|
||||||
|
DesktopIconSizePreset.medium,
|
||||||
|
)
|
||||||
|
|
||||||
// Get device internal ID from DB
|
// Get device internal ID from DB
|
||||||
const getDeviceInternalIdAsync = async () => {
|
const getDeviceInternalIdAsync = async () => {
|
||||||
if (!deviceStore.deviceId || !currentVault.value?.drizzle) return undefined
|
if (!deviceStore.deviceId || !currentVault.value?.drizzle) return undefined
|
||||||
|
|
||||||
const device = await currentVault.value.drizzle.query.haexDevices.findFirst({
|
const device = await currentVault.value.drizzle.query.haexDevices.findFirst(
|
||||||
where: eq(haexDevices.deviceId, deviceStore.deviceId),
|
{
|
||||||
})
|
where: eq(haexDevices.deviceId, deviceStore.deviceId),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return device?.id ? device.id : undefined
|
return device?.id ? device.id : undefined
|
||||||
}
|
}
|
||||||
@ -54,7 +56,8 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
const deviceInternalId = await getDeviceInternalIdAsync()
|
const deviceInternalId = await getDeviceInternalIdAsync()
|
||||||
if (!deviceInternalId) return
|
if (!deviceInternalId) return
|
||||||
|
|
||||||
const preset = await settingsStore.syncDesktopIconSizeAsync(deviceInternalId)
|
const preset =
|
||||||
|
await settingsStore.syncDesktopIconSizeAsync(deviceInternalId)
|
||||||
iconSizePreset.value = preset
|
iconSizePreset.value = preset
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,22 +74,26 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
return iconSizePresetValues[iconSizePreset.value]
|
return iconSizePresetValues[iconSizePreset.value]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const iconPadding = 30
|
||||||
|
|
||||||
// Calculate grid cell size based on icon size
|
// Calculate grid cell size based on icon size
|
||||||
const gridCellSize = computed(() => {
|
const gridCellSize = computed(() => {
|
||||||
// Add padding around icon (30px extra for spacing)
|
// Add padding around icon (30px extra for spacing)
|
||||||
return effectiveIconSize.value + 30
|
return effectiveIconSize.value + iconPadding
|
||||||
})
|
})
|
||||||
|
|
||||||
// Grid offsets for start position (negative = move grid up)
|
|
||||||
const gridOffsetY = -30 // Start grid 30px higher
|
|
||||||
|
|
||||||
// Snap position to grid (centers icon in cell)
|
// Snap position to grid (centers icon in cell)
|
||||||
// iconWidth and iconHeight are optional - if provided, they're used for centering
|
// iconWidth and iconHeight are optional - if provided, they're used for centering
|
||||||
const snapToGrid = (x: number, y: number, iconWidth?: number, iconHeight?: number) => {
|
const snapToGrid = (
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
iconWidth?: number,
|
||||||
|
iconHeight?: number,
|
||||||
|
) => {
|
||||||
const cellSize = gridCellSize.value
|
const cellSize = gridCellSize.value
|
||||||
|
|
||||||
// Adjust y for grid offset
|
// Adjust y for grid offset
|
||||||
const adjustedY = Math.max(0, y - gridOffsetY)
|
const adjustedY = Math.max(0, y + iconPadding)
|
||||||
|
|
||||||
// Calculate which grid cell the position falls into
|
// Calculate which grid cell the position falls into
|
||||||
const col = Math.floor(x / cellSize)
|
const col = Math.floor(x / cellSize)
|
||||||
@ -107,8 +114,8 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
const paddingY = (totalHeight - actualIconHeight) / 2
|
const paddingY = (totalHeight - actualIconHeight) / 2
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: col * cellSize + paddingX,
|
x: col * cellSize + paddingX - iconPadding,
|
||||||
y: row * cellSize + paddingY + gridOffsetY,
|
y: row * cellSize + paddingY - iconPadding,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,9 +136,12 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
.from(haexDesktopItems)
|
.from(haexDesktopItems)
|
||||||
.where(eq(haexDesktopItems.workspaceId, currentWorkspace.value.id))
|
.where(eq(haexDesktopItems.workspaceId, currentWorkspace.value.id))
|
||||||
|
|
||||||
desktopItems.value = items.map(item => ({
|
desktopItems.value = items.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
referenceId: item.itemType === 'extension' ? item.extensionId! : item.systemWindowId!,
|
referenceId:
|
||||||
|
item.itemType === 'extension'
|
||||||
|
? item.extensionId!
|
||||||
|
: item.systemWindowId!,
|
||||||
}))
|
}))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Desktop-Items:', error)
|
console.error('Fehler beim Laden der Desktop-Items:', error)
|
||||||
@ -160,7 +170,10 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
workspaceId: targetWorkspaceId,
|
workspaceId: targetWorkspaceId,
|
||||||
itemType: itemType,
|
itemType: itemType,
|
||||||
extensionId: itemType === 'extension' ? referenceId : null,
|
extensionId: itemType === 'extension' ? referenceId : null,
|
||||||
systemWindowId: itemType === 'system' || itemType === 'file' || itemType === 'folder' ? referenceId : null,
|
systemWindowId:
|
||||||
|
itemType === 'system' || itemType === 'file' || itemType === 'folder'
|
||||||
|
? referenceId
|
||||||
|
: null,
|
||||||
positionX: positionX,
|
positionX: positionX,
|
||||||
positionY: positionY,
|
positionY: positionY,
|
||||||
}
|
}
|
||||||
@ -173,7 +186,10 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
if (result.length > 0 && result[0]) {
|
if (result.length > 0 && result[0]) {
|
||||||
const itemWithRef = {
|
const itemWithRef = {
|
||||||
...result[0],
|
...result[0],
|
||||||
referenceId: itemType === 'extension' ? result[0].extensionId! : result[0].systemWindowId!,
|
referenceId:
|
||||||
|
itemType === 'extension'
|
||||||
|
? result[0].extensionId!
|
||||||
|
: result[0].systemWindowId!,
|
||||||
}
|
}
|
||||||
desktopItems.value.push(itemWithRef)
|
desktopItems.value.push(itemWithRef)
|
||||||
return itemWithRef
|
return itemWithRef
|
||||||
@ -184,7 +200,7 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
itemType,
|
itemType,
|
||||||
referenceId,
|
referenceId,
|
||||||
workspaceId: targetWorkspaceId,
|
workspaceId: targetWorkspaceId,
|
||||||
position: { x: positionX, y: positionY }
|
position: { x: positionX, y: positionY },
|
||||||
})
|
})
|
||||||
|
|
||||||
// Log full error details
|
// Log full error details
|
||||||
@ -221,7 +237,10 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
const item = result[0]
|
const item = result[0]
|
||||||
desktopItems.value[index] = {
|
desktopItems.value[index] = {
|
||||||
...item,
|
...item,
|
||||||
referenceId: item.itemType === 'extension' ? item.extensionId! : item.systemWindowId!,
|
referenceId:
|
||||||
|
item.itemType === 'extension'
|
||||||
|
? item.extensionId!
|
||||||
|
: item.systemWindowId!,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,16 +273,14 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
itemType: DesktopItemType,
|
itemType: DesktopItemType,
|
||||||
referenceId: string,
|
referenceId: string,
|
||||||
) => {
|
) => {
|
||||||
return desktopItems.value.find(
|
return desktopItems.value.find((item) => {
|
||||||
(item) => {
|
if (item.itemType !== itemType) return false
|
||||||
if (item.itemType !== itemType) return false
|
if (itemType === 'extension') {
|
||||||
if (itemType === 'extension') {
|
return item.extensionId === referenceId
|
||||||
return item.extensionId === referenceId
|
} else {
|
||||||
} else {
|
return item.systemWindowId === referenceId
|
||||||
return item.systemWindowId === referenceId
|
}
|
||||||
}
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const openDesktopItem = (
|
const openDesktopItem = (
|
||||||
@ -274,9 +291,9 @@ export const useDesktopStore = defineStore('desktopStore', () => {
|
|||||||
const windowManager = useWindowManagerStore()
|
const windowManager = useWindowManagerStore()
|
||||||
|
|
||||||
if (itemType === 'system') {
|
if (itemType === 'system') {
|
||||||
const systemWindow = windowManager.getAllSystemWindows().find(
|
const systemWindow = windowManager
|
||||||
(win) => win.id === referenceId,
|
.getAllSystemWindows()
|
||||||
)
|
.find((win) => win.id === referenceId)
|
||||||
|
|
||||||
if (systemWindow) {
|
if (systemWindow) {
|
||||||
windowManager.openWindowAsync({
|
windowManager.openWindowAsync({
|
||||||
|
|||||||
Reference in New Issue
Block a user