mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 00:01:49 +00:00
* feat: add context menu, multi-select actions, and keyboard shortcuts to flow editor Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review feedback on context menu PR - Revert accidental static import of @scalar/openapi-parser (keep lazy-loaded) - Restore [data-context-menu] in portalDivs for clickOutside compatibility - Make noteDisabled reactive ($derived) in ModuleNode - Use platform-aware shortcut hint (⌫ on Mac, Del on Windows/Linux) - Optimize resolveSelectedModuleIds with single-pass ancestor map Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address additional review feedback on flow context menu PR - Use $derived.by instead of $derived for computed bounds in SelectionBoundingBox - Remove redundant structuredClone wrappers around $state.snapshot - Add null guard for originalModules/targetModules in move handler - Add upper-bound guard (n < 10000) to copyId loop - Fix fragile toggle comparison in moveManager with full array equality Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
75 lines
1.7 KiB
Svelte
75 lines
1.7 KiB
Svelte
<script lang="ts">
|
|
import { stopPropagation, preventDefault } from 'svelte/legacy'
|
|
import { onDestroy } from 'svelte'
|
|
import { Move } from 'lucide-svelte'
|
|
import { twMerge } from 'tailwind-merge'
|
|
import type { MoveManager } from './moveManager.svelte'
|
|
|
|
interface Props {
|
|
moveManager: MoveManager
|
|
moduleId: string
|
|
selectedIds?: string[]
|
|
singleNode?: boolean
|
|
visible?: boolean
|
|
onClickMove?: () => void
|
|
class?: string
|
|
}
|
|
|
|
let {
|
|
moveManager,
|
|
moduleId,
|
|
selectedIds,
|
|
singleNode = false,
|
|
visible = true,
|
|
onClickMove,
|
|
class: extraClass
|
|
}: Props = $props()
|
|
|
|
let dragCleanup: (() => void) | undefined
|
|
|
|
function onPointerDown(e: Event) {
|
|
const pe = e as PointerEvent
|
|
const startX = pe.clientX
|
|
const startY = pe.clientY
|
|
let didDrag = false
|
|
|
|
function onMove(me: PointerEvent) {
|
|
const dx = me.clientX - startX
|
|
const dy = me.clientY - startY
|
|
if (!didDrag && Math.sqrt(dx * dx + dy * dy) > 5) {
|
|
didDrag = true
|
|
moveManager.startDrag(moduleId, startX, startY, singleNode ? undefined : selectedIds)
|
|
}
|
|
}
|
|
|
|
function onUp() {
|
|
document.removeEventListener('pointermove', onMove)
|
|
document.removeEventListener('pointerup', onUp)
|
|
dragCleanup = undefined
|
|
if (!didDrag) {
|
|
onClickMove?.()
|
|
}
|
|
}
|
|
|
|
document.addEventListener('pointermove', onMove)
|
|
document.addEventListener('pointerup', onUp)
|
|
dragCleanup = onUp
|
|
}
|
|
|
|
onDestroy(() => dragCleanup?.())
|
|
</script>
|
|
|
|
<button
|
|
class={twMerge(
|
|
'center-center p-1 text-secondary shadow-sm bg-surface duration-0 hover:bg-surface-tertiary cursor-grab',
|
|
visible ? '' : '!hidden',
|
|
'shadow-md rounded-md',
|
|
'touch-none',
|
|
extraClass
|
|
)}
|
|
onpointerdown={stopPropagation(preventDefault(onPointerDown))}
|
|
title="Move"
|
|
>
|
|
<Move size={12} />
|
|
</button>
|