feat: show path in flow script picker (#4574)
* Add popover with path to flow modules * Add path to items in the flow picker * fix virtual item display issue * fix min size * open popover on keyboard selection --------- Co-authored-by: Guilhem <guilhem@mbp-de-windmill.home> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { createPopperActions, type PopperOptions } from 'svelte-popperjs'
|
||||
import type { PopoverPlacement } from './Popover.model'
|
||||
import Portal from '$lib/components/Portal.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import { ExternalLink } from 'lucide-svelte'
|
||||
|
||||
@@ -13,6 +14,7 @@
|
||||
export let appearTimeout = 300
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let style: string | undefined = undefined
|
||||
export let forceOpen = false
|
||||
|
||||
const [popperRef, popperContent] = createPopperActions({ placement })
|
||||
|
||||
@@ -47,6 +49,8 @@
|
||||
inTimeout = undefined
|
||||
timeout = setTimeout(() => (showTooltip = false), disappearTimeout)
|
||||
}
|
||||
|
||||
$: forceOpen ? open() : close()
|
||||
</script>
|
||||
|
||||
{#if notClickable}
|
||||
@@ -73,7 +77,10 @@
|
||||
use:popperContent={popperOptions}
|
||||
on:mouseenter={open}
|
||||
on:mouseleave={close}
|
||||
class="z-[5001] py-2 px-3 rounded-md text-sm font-normal !text-gray-300 bg-gray-800 whitespace-normal text-left {popupClass}"
|
||||
class={twMerge(
|
||||
'z-[5001] py-2 px-3 rounded-md text-sm font-normal !text-gray-300 bg-gray-800 whitespace-normal text-left',
|
||||
popupClass
|
||||
)}
|
||||
>
|
||||
<div class="max-w-sm break-words">
|
||||
<slot name="text" />
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
export let kind: 'trigger' | 'script' | 'preprocessor' | 'failure' | 'approval'
|
||||
export let selectedKind: 'script' | 'flow' | 'approval' | 'trigger' | 'preprocessor' | 'failure' =
|
||||
kind
|
||||
export let displayPath = false
|
||||
|
||||
let lang: FlowCopilotModule['lang'] = undefined
|
||||
let selectedCompletion: FlowCopilotModule['selectedCompletion'] = undefined
|
||||
@@ -424,6 +425,7 @@
|
||||
selected={selectedByKeyboard - inlineScripts?.length - aiLength - topLevelNodes.length}
|
||||
on:pickScript
|
||||
on:pickFlow
|
||||
{displayPath}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -446,6 +448,7 @@
|
||||
topLevelNodes.length}
|
||||
on:pickScript
|
||||
bind:loading
|
||||
{displayPath}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
export let bold: boolean = false
|
||||
export let id: string | undefined = undefined
|
||||
export let label: string
|
||||
export let path: string = ''
|
||||
export let modType: string | undefined = undefined
|
||||
export let bgColor: string = ''
|
||||
export let concurrency: boolean = false
|
||||
@@ -248,11 +249,22 @@
|
||||
<slot name="icon" />
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="absolute left-1/2 transform -translate-x-1/2 text-center truncate"
|
||||
class:font-bold={bold}
|
||||
style="max-width: calc(100% - {marginLeft}px)">{label}</div
|
||||
|
||||
<Popover
|
||||
class="absolute left-1/2 transform -translate-x-1/2 center-center"
|
||||
style="max-width: calc(100% - {marginLeft}px)"
|
||||
>
|
||||
<div class="text-center truncate {bold ? '!font-bold' : 'font-normal'}">
|
||||
{label}
|
||||
</div>
|
||||
<svelte:fragment slot="text">
|
||||
<div>
|
||||
<div>{label}</div>
|
||||
{#if path != ''}<div>{path}</div>{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
<div class="flex items-center space-x-2 relative max-w-[25%]" bind:clientWidth={idBadgeWidth}>
|
||||
{#if id && id !== 'preprocessor' && !id.startsWith('failure')}
|
||||
<Badge color="indigo" wrapperClass="max-w-full" baseClass="max-w-full truncate" title={id}>
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
let small = false
|
||||
let open = false
|
||||
|
||||
let width = 0
|
||||
let height = 0
|
||||
|
||||
$: displayPath = width > 650 || height > 400
|
||||
|
||||
$: small = kind === 'preprocessor' || kind === 'failure'
|
||||
</script>
|
||||
|
||||
@@ -90,11 +95,17 @@ shouldUsePortal={true} -->
|
||||
</svelte:fragment>
|
||||
<div
|
||||
id="flow-editor-insert-module"
|
||||
class="flex flex-col h-[400px] {small ? 'w-[450px]' : 'w-[650px]'} pt-1 pr-1 pl-1 gap-1.5"
|
||||
class="flex flex-col h-[400px] {small
|
||||
? 'w-[450px]'
|
||||
: 'w-[650px]'} pt-1 pr-1 pl-1 gap-1.5 resize overflow-auto {small
|
||||
? 'min-w-[450px]'
|
||||
: 'min-w-[650px]'} min-h-[400px]"
|
||||
on:wheel={(e) => {
|
||||
e.stopPropagation()
|
||||
}}
|
||||
role="none"
|
||||
bind:clientWidth={width}
|
||||
bind:clientHeight={height}
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<StepGenQuick
|
||||
@@ -203,6 +214,7 @@ shouldUsePortal={true} -->
|
||||
on:pickFlow
|
||||
{preFilter}
|
||||
{small}
|
||||
{displayPath}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
(mod.value.type === 'rawscript'
|
||||
? `Inline ${prettyLanguage(mod.value.language)}`
|
||||
: 'To be defined')}
|
||||
path={`path` in mod.value && mod.summary ? mod.value.path : ''}
|
||||
isTrigger={isTriggerStep(mod)}
|
||||
>
|
||||
<div slot="icon">
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
>
|
||||
<div
|
||||
style={borderColor ? `border-color: ${borderColor};` : 'border: 0'}
|
||||
class="flex gap-1 justify-between {center
|
||||
class="flex flex-row gap-1 justify-between {center
|
||||
? 'items-center'
|
||||
: 'items-baseline'} w-full overflow-hidden rounded-sm border p-2 text-2xs module text-primary border-gray-400 dark:border-gray-600"
|
||||
>
|
||||
@@ -38,8 +38,7 @@
|
||||
<slot name="icon" />
|
||||
<span class="mr-2" />
|
||||
{/if}
|
||||
<div />
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex flex-col flex-grow shrink-0 max-w-full min-w-0">
|
||||
{#if label}
|
||||
<div class="truncate text-center">{label}</div>
|
||||
{/if}
|
||||
@@ -47,10 +46,12 @@
|
||||
<div class="truncate text-2xs text-center"><pre>{preLabel}</pre></div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
{#if id && !hideId}
|
||||
<Badge color="indigo">{id}</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
{#if id && !hideId}
|
||||
<div class="flex items-center shrink min-w-0">
|
||||
<Badge color="indigo" wrapperClass="w-full" baseClass="max-w-full" title={id}>
|
||||
<span class="max-w-full text-2xs truncate">{id}</span>
|
||||
</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</VirtualItemWrapper>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { APP_TO_ICON_COMPONENT } from '$lib/components/icons'
|
||||
import { IntegrationService, ScriptService, type HubScriptKind } from '$lib/gen'
|
||||
import { Circle } from 'lucide-svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
|
||||
export let kind: HubScriptKind & string = 'script'
|
||||
export let filter = ''
|
||||
@@ -25,6 +26,7 @@
|
||||
app: string
|
||||
kind: HubScriptKind
|
||||
}[] = []
|
||||
export let displayPath = false
|
||||
|
||||
export let apps: string[] = []
|
||||
let allApps: string[] = []
|
||||
@@ -138,32 +140,56 @@
|
||||
<ul>
|
||||
{#each items as item, index (item.path)}
|
||||
<li class="w-full">
|
||||
<button
|
||||
class="px-3 py-2 gap-2 flex flex-row w-full hover:bg-surface-hover transition-all items-center rounded-md {index ===
|
||||
selected
|
||||
? 'bg-surface-hover'
|
||||
: ''}"
|
||||
on:click={() => dispatch('pickScript', item)}
|
||||
>
|
||||
<div class={classNames('flex justify-center items-center')}>
|
||||
{#if item['app'] in APP_TO_ICON_COMPONENT}
|
||||
<svelte:component this={APP_TO_ICON_COMPONENT[item['app']]} height={14} width={14} />
|
||||
{:else}
|
||||
<div
|
||||
class="w-[14px] h-[14px] text-gray-400 flex flex-row items-center justify-center"
|
||||
<Popover class="w-full" placement="right" forceOpen={index === selected}>
|
||||
<svelte:fragment slot="text">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-left text-xs font-normal leading-tight py-0"
|
||||
>{item.summary ?? ''}</div
|
||||
>
|
||||
<Circle size="12" />
|
||||
<div class="text-left text-2xs font-normal">
|
||||
{item.path ?? ''}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<button
|
||||
class="px-3 py-2 gap-2 flex flex-row w-full hover:bg-surface-hover transition-all items-center rounded-md {index ===
|
||||
selected
|
||||
? 'bg-surface-hover'
|
||||
: ''}"
|
||||
on:click={() => dispatch('pickScript', item)}
|
||||
>
|
||||
<div class={classNames('flex justify-center items-center')}>
|
||||
{#if item['app'] in APP_TO_ICON_COMPONENT}
|
||||
<svelte:component
|
||||
this={APP_TO_ICON_COMPONENT[item['app']]}
|
||||
height={14}
|
||||
width={14}
|
||||
/>
|
||||
{:else}
|
||||
<div
|
||||
class="w-[14px] h-[14px] text-gray-400 flex flex-row items-center justify-center"
|
||||
>
|
||||
<Circle size="12" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<span class="grow truncate text-left text-2xs text-primary font-normal">
|
||||
{item.summary ?? ''}
|
||||
</span>
|
||||
{#if index === selected}
|
||||
<kbd class="!text-xs">↵</kbd>
|
||||
{/if}
|
||||
</button>
|
||||
<div class="flex flex-col grow min-w-0">
|
||||
<div
|
||||
class="grow truncate text-left text-2xs text-primary font-normal leading-tight py-0.5"
|
||||
>{item.summary ?? ''}</div
|
||||
>
|
||||
{#if displayPath && item.path}
|
||||
<div class="grow truncate text-left text-2xs text-secondary font-[220]">
|
||||
{item.path}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if index === selected}
|
||||
<kbd class="!text-xs">↵</kbd>
|
||||
{/if}
|
||||
</button>
|
||||
</Popover>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { Code2 } from 'lucide-svelte'
|
||||
import BarsStaggered from '$lib/components/icons/BarsStaggered.svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
|
||||
export let kind: 'script' | 'trigger' | 'approval' | 'failure' | 'flow' | 'preprocessor' =
|
||||
'script'
|
||||
export let isTemplate: boolean | undefined = undefined
|
||||
export let selected: number | undefined = undefined
|
||||
export let displayPath = false
|
||||
|
||||
type Item = {
|
||||
path: string
|
||||
@@ -105,35 +107,52 @@
|
||||
<ul>
|
||||
{#each filteredWithOwner ?? [] as { path, hash, summary, marked }, index}
|
||||
<li class="w-full">
|
||||
<button
|
||||
class="px-3 py-2 gap-2 flex flex-row w-full hover:bg-surface-hover transition-all items-center rounded-md {index ===
|
||||
selected
|
||||
? 'bg-surface-hover'
|
||||
: ''}"
|
||||
on:click={() => {
|
||||
if (kind == 'flow') {
|
||||
dispatch('pickFlow', { path: path })
|
||||
} else {
|
||||
dispatch('pickScript', { path: path, hash: lockHash ? hash : undefined, kind })
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if kind == 'flow'}
|
||||
<BarsStaggered size={14} class="shrink-0" />
|
||||
{:else}
|
||||
<Code2 size={14} />
|
||||
{/if}
|
||||
<span class="grow min-w-0 truncate text-left text-2xs text-primary font-normal">
|
||||
{#if marked}
|
||||
{@html marked}
|
||||
{:else}
|
||||
{!summary || summary.length == 0 ? path : summary}
|
||||
{/if}</span
|
||||
<Popover class="w-full " placement="right" forceOpen={index === selected}>
|
||||
<svelte:fragment slot="text">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-left text-xs font-normal leading-tight py-0">{summary ?? ''}</div>
|
||||
<div class="text-left text-2xs font-normal">
|
||||
{path ?? ''}
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<button
|
||||
class="px-3 py-2 gap-2 flex flex-row w-full hover:bg-surface-hover transition-all items-center rounded-md {index ===
|
||||
selected
|
||||
? 'bg-surface-hover'
|
||||
: ''}"
|
||||
on:click={() => {
|
||||
if (kind == 'flow') {
|
||||
dispatch('pickFlow', { path: path })
|
||||
} else {
|
||||
dispatch('pickScript', { path: path, hash: lockHash ? hash : undefined, kind })
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if index === selected}
|
||||
<kbd class="!text-xs">↵</kbd>
|
||||
{/if}
|
||||
</button>
|
||||
{#if kind == 'flow'}
|
||||
<BarsStaggered size={14} class="shrink-0" />
|
||||
{:else}
|
||||
<Code2 size={14} />
|
||||
{/if}
|
||||
<div class="flex flex-col grow min-w-0">
|
||||
<div class="grow min-w-0 truncate text-left text-2xs text-primary font-normal">
|
||||
{#if marked}
|
||||
{@html marked}
|
||||
{:else}
|
||||
{!summary || summary.length == 0 ? path : summary}
|
||||
{/if}
|
||||
</div>
|
||||
{#if displayPath && path}
|
||||
<div class="grow min-w-0 truncate text-left text-2xs text-secondary font-[220]">
|
||||
{path}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if index === selected}
|
||||
<kbd class="!text-xs">↵</kbd>
|
||||
{/if}
|
||||
</button>
|
||||
</Popover>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user