mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 08:02:18 +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>
107 lines
2.8 KiB
Svelte
107 lines
2.8 KiB
Svelte
<script lang="ts">
|
|
import Popover from '$lib/components/Popover.svelte'
|
|
import Badge from '$lib/components/common/badge/Badge.svelte'
|
|
import type { FlowNodeColorClasses } from '$lib/components/graph'
|
|
import { Pencil } from 'lucide-svelte'
|
|
import { slide } from 'svelte/transition'
|
|
import { twMerge } from 'tailwind-merge'
|
|
|
|
let iconWidth: number = $state(0)
|
|
let idBadgeWidth: number | undefined = $state(undefined)
|
|
interface Props {
|
|
label?: string
|
|
path?: string
|
|
id?: string
|
|
deletable?: boolean
|
|
bold?: boolean
|
|
editId?: boolean
|
|
disableEditId?: boolean
|
|
hover?: boolean
|
|
colorClasses?: FlowNodeColorClasses
|
|
icon?: import('svelte').Snippet
|
|
onclick?: () => void
|
|
}
|
|
|
|
let {
|
|
label = '',
|
|
path = '',
|
|
id = '',
|
|
deletable = false,
|
|
bold = false,
|
|
editId = $bindable(false),
|
|
disableEditId = false,
|
|
hover = false,
|
|
colorClasses,
|
|
icon,
|
|
onclick
|
|
}: Props = $props()
|
|
|
|
let marginLeft = $derived(Math.max(iconWidth ?? 0, idBadgeWidth ?? 0) * 2 + 32)
|
|
</script>
|
|
|
|
<div
|
|
id={id || undefined}
|
|
class="relative flex gap-1 justify-between items-center w-full overflow-hidden rounded-sm
|
|
p-2 text-2xs module text-primary"
|
|
>
|
|
{#if icon && true}
|
|
<div class="flex-none" bind:clientWidth={iconWidth}>
|
|
{@render icon?.()}
|
|
</div>
|
|
{/if}
|
|
|
|
<Popover
|
|
class="absolute left-1/2 transform -translate-x-1/2 center-center"
|
|
style="max-width: calc(100% - {marginLeft}px)"
|
|
>
|
|
<div class="text-center {colorClasses?.text} truncate {bold ? '!font-bold' : 'font-normal'}">
|
|
{label}
|
|
</div>
|
|
{#snippet text()}
|
|
<div>
|
|
<div>{label}</div>
|
|
{#if path != ''}<div>{path}</div>{/if}
|
|
</div>
|
|
{/snippet}
|
|
</Popover>
|
|
|
|
<div class="flex items-center space-x-2 relative" bind:clientWidth={idBadgeWidth}>
|
|
{#if id && id !== 'preprocessor' && !id.startsWith('failure') && !id.startsWith('subflow:')}
|
|
<Badge
|
|
color="transparent"
|
|
class="border-none"
|
|
wrapperClass={twMerge(
|
|
'max-w-full rounded-md hover:opacity-60 transition-opacity',
|
|
colorClasses?.badge
|
|
)}
|
|
baseClass={twMerge('!px-1')}
|
|
title={id}
|
|
clickable={!disableEditId}
|
|
onclick={disableEditId
|
|
? undefined
|
|
: (e) => {
|
|
e?.preventDefault()
|
|
e?.stopPropagation()
|
|
editId = !editId
|
|
onclick?.()
|
|
}}
|
|
>
|
|
<span class="max-w-full text-2xs truncate flex items-center">
|
|
{#if !disableEditId && (editId || (hover && deletable))}
|
|
<span transition:slide={{ axis: 'x', duration: 100 }}>
|
|
<Pencil size={10} class="mr-1" />
|
|
</span>
|
|
{/if}
|
|
<span class="max-w-12 truncate">
|
|
{id}
|
|
</span>
|
|
</span>
|
|
</Badge>
|
|
{:else if id?.startsWith('subflow:')}
|
|
<Badge color="blue" wrapperClass="max-w-full" baseClass="!px-1" title={id}>
|
|
<span class="max-w-full text-2xs truncate">{id.substring('subflow:'.length)}</span></Badge
|
|
>
|
|
{/if}
|
|
</div>
|
|
</div>
|