mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-18 08:03:11 +00:00
feat: multi-select steps in the simplified view
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
931892d7c1
commit
13d9ec652e
@@ -1335,6 +1335,9 @@
|
||||
{minHeight}
|
||||
{maxHeight}
|
||||
{selectedId}
|
||||
{selectionManager}
|
||||
selectionOverlay={multiSelectEnabled ? selectionBox : undefined}
|
||||
selectionKey={editMode ? modifierKey : undefined}
|
||||
structure={'structureTree' in graph ? graph.structureTree : undefined}
|
||||
{editMode}
|
||||
{insertable}
|
||||
@@ -1422,19 +1425,7 @@
|
||||
{/if}
|
||||
|
||||
{#if multiSelectEnabled}
|
||||
<SelectionBoundingBox
|
||||
selectedNodes={selectionManager.selectedIds.filter((id) =>
|
||||
nodesWithOffset.some((n) => n.id === id)
|
||||
)}
|
||||
allNodes={nodesWithOffset as (Node & { type: string })[]}
|
||||
onDeleteSelected={() => onDeleteMultiple?.(resolvedModuleIds)}
|
||||
onDuplicateSelected={() => onDuplicateMultiple?.(resolvedModuleIds)}
|
||||
onMoveSelected={() => onMoveMultiple?.(resolvedModuleIds)}
|
||||
onCancelMove={() => onMoveMultiple?.(movingIds ?? [])}
|
||||
{canMoveSelected}
|
||||
isMoving={movingIds != null && movingIds.length > 0}
|
||||
{resolvedModuleIds}
|
||||
/>
|
||||
{@render selectionBox(nodesWithOffset)}
|
||||
{/if}
|
||||
|
||||
<GroupOverlay
|
||||
@@ -1548,6 +1539,20 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#snippet selectionBox(allNodes: Node[])}
|
||||
<SelectionBoundingBox
|
||||
selectedNodes={selectionManager.selectedIds.filter((id) => allNodes.some((n) => n.id === id))}
|
||||
allNodes={allNodes as (Node & { type: string })[]}
|
||||
onDeleteSelected={() => onDeleteMultiple?.(resolvedModuleIds)}
|
||||
onDuplicateSelected={() => onDuplicateMultiple?.(resolvedModuleIds)}
|
||||
onMoveSelected={() => onMoveMultiple?.(resolvedModuleIds)}
|
||||
onCancelMove={() => onMoveMultiple?.(movingIds ?? [])}
|
||||
{canMoveSelected}
|
||||
isMoving={movingIds != null && movingIds.length > 0}
|
||||
{resolvedModuleIds}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet simplifiedViewToggle()}
|
||||
<Toggle bind:checked={simplifiedView} size="xs" options={{ right: 'Simplified view' }} />
|
||||
{/snippet}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { SvelteFlow, SvelteFlowProvider, Controls } from '@xyflow/svelte'
|
||||
import {
|
||||
SvelteFlow,
|
||||
SvelteFlowProvider,
|
||||
Controls,
|
||||
SelectionMode,
|
||||
type Node
|
||||
} from '@xyflow/svelte'
|
||||
import '@xyflow/svelte/dist/base.css'
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import type { Snippet } from 'svelte'
|
||||
@@ -7,6 +13,8 @@
|
||||
import { setSimplifiedEditContext } from './simplifiedContext'
|
||||
import type { FlowStructureNode } from '../flowStructure'
|
||||
import type { GraphEventHandlers } from '../graphBuilder.svelte'
|
||||
import type { SelectionManager } from '../selectionUtils.svelte'
|
||||
import SelectionTool from '../SelectionTool.svelte'
|
||||
import SimplifiedNode from './SimplifiedNode.svelte'
|
||||
import SimplifiedBox from './SimplifiedBox.svelte'
|
||||
import SimplifiedEdge from './SimplifiedEdge.svelte'
|
||||
@@ -32,6 +40,12 @@
|
||||
insertable?: boolean
|
||||
eventHandlers?: GraphEventHandlers
|
||||
disableAi?: boolean
|
||||
/** The graph's selection manager; with `selectionOverlay`, several steps can be selected */
|
||||
selectionManager?: SelectionManager
|
||||
/** Drawn over a multi-step selection (its bulk actions), given the laid-out nodes */
|
||||
selectionOverlay?: Snippet<[Node[]]>
|
||||
/** Held while dragging on the canvas to select a rectangle of steps */
|
||||
selectionKey?: string
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -47,7 +61,10 @@
|
||||
editMode = false,
|
||||
insertable = false,
|
||||
eventHandlers,
|
||||
disableAi = false
|
||||
disableAi = false,
|
||||
selectionManager,
|
||||
selectionOverlay,
|
||||
selectionKey
|
||||
}: Props = $props()
|
||||
|
||||
setSimplifiedEditContext({
|
||||
@@ -76,8 +93,11 @@
|
||||
let nodes = $derived(
|
||||
layout.nodes.map((n) => ({
|
||||
...n,
|
||||
// The selection box measures nodes by their `measured` size
|
||||
measured: { width: n.width, height: n.height },
|
||||
selected:
|
||||
n.id === selectedId ||
|
||||
!!selectionManager?.selectedIds.includes(n.id) ||
|
||||
(n.type === 'simplifiedBox' && !!selectedId?.startsWith(`${n.id}-branch-`))
|
||||
}))
|
||||
)
|
||||
@@ -131,17 +151,27 @@
|
||||
maxZoom={1.6}
|
||||
nodesDraggable={false}
|
||||
nodesConnectable={false}
|
||||
elementsSelectable={false}
|
||||
elementsSelectable={!!selectionOverlay}
|
||||
multiSelectionKey="Shift"
|
||||
selectionKey={selectionOverlay ? (selectionKey ?? null) : null}
|
||||
selectionMode={SelectionMode.Partial}
|
||||
zoomOnDoubleClick={false}
|
||||
deleteKey={null}
|
||||
onnodeclick={({ node }) => {
|
||||
onnodeclick={({ node, event }) => {
|
||||
// Adding to a multi-selection is xyflow's, synced by SelectionTool
|
||||
if (event.shiftKey) return
|
||||
// Boxes stand for their loop or branch step; placeholders are not selectable
|
||||
if (node.type === 'simplifiedBox' || node.selectable) onSelect?.(node.id)
|
||||
}}
|
||||
onpaneclick={() => selectionManager?.clearSelection()}
|
||||
{proOptions}
|
||||
--background-color={false}
|
||||
>
|
||||
<div class="absolute inset-0 !bg-surface-secondary h-full"></div>
|
||||
{#if selectionOverlay && selectionManager}
|
||||
<SelectionTool {selectionManager} />
|
||||
{@render selectionOverlay(nodes)}
|
||||
{/if}
|
||||
<Controls position="top-right" orientation="horizontal" showLock={false} />
|
||||
{#if topLeftControls}
|
||||
<Controls
|
||||
|
||||
@@ -839,6 +839,8 @@ export function layoutSimplifiedFlow(
|
||||
position: { x: left, y: b.y },
|
||||
data,
|
||||
draggable: false,
|
||||
// A rectangle drawn over the steps inside would otherwise also select the loop or branch
|
||||
selectable: false,
|
||||
zIndex: -1,
|
||||
// Only the group's label is interactive; SimplifiedBox re-enables pointer events on it.
|
||||
style: 'pointer-events: none;',
|
||||
|
||||
Reference in New Issue
Block a user