mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 16:02:23 +00:00
Add connexion animated border effect
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
import type { PropPickerWrapperContext } from './flows/propPicker/PropPickerWrapper.svelte'
|
||||
import { codeToStaticTemplate, getDefaultExpr } from './flows/utils'
|
||||
import SimpleEditor from './SimpleEditor.svelte'
|
||||
import { Button } from './common'
|
||||
import { Button } from '$lib/components/common'
|
||||
import AnimatedButton from '$lib/components/common/button/AnimatedButton.svelte'
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
@@ -187,6 +188,9 @@
|
||||
let stepInputGen: StepInputGen | undefined = undefined
|
||||
|
||||
loadResourceTypes()
|
||||
|
||||
$: connecting =
|
||||
$propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'connect'
|
||||
</script>
|
||||
|
||||
{#if arg != undefined}
|
||||
@@ -331,22 +335,23 @@
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
title="Connect to another node's output"
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs2"
|
||||
on:click={() => {
|
||||
focusProp(argName, 'connect', (path) => {
|
||||
connectProperty(path)
|
||||
dispatch('change', { argName })
|
||||
return true
|
||||
})
|
||||
}}
|
||||
id="flow-editor-plug"
|
||||
>
|
||||
<Plug size={16} /> →
|
||||
</Button>
|
||||
<AnimatedButton animate={connecting} baseRadius="6px" animationDuration="1s">
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs2"
|
||||
btnClasses={connecting ? 'text-blue-500' : 'text-primary'}
|
||||
on:click={() => {
|
||||
focusProp(argName, 'connect', (path) => {
|
||||
connectProperty(path)
|
||||
dispatch('change', { argName })
|
||||
return true
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Plug size={16} /> →
|
||||
</Button>
|
||||
</AnimatedButton>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -354,13 +359,13 @@
|
||||
<div class="max-w-xs" />
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="relative" on:keyup={stepInputGen?.onKeyUp}>
|
||||
{#if $propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'connect'}
|
||||
<!-- {#if $propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'connect'}
|
||||
<span
|
||||
class={'text-white z-50 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit absolute top-0 right-0 bg-blue-500'}
|
||||
>
|
||||
Connect input →
|
||||
</span>
|
||||
{/if}
|
||||
{/if} -->
|
||||
<!-- {inputCat}
|
||||
{propertyType} -->
|
||||
<div class="relative flex flex-row items-top gap-2 justify-between">
|
||||
@@ -471,7 +476,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if $propPickerConfig?.propName == argName}
|
||||
{#if $propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'insert'}
|
||||
<div class="text-blue-500 mt-2" in:fade={{ duration: 200 }}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let marginWidth = '2px'
|
||||
export let animationDuration = '2s'
|
||||
export let baseRadius = '4px'
|
||||
export let animate = true
|
||||
export let wrapperClasses = ''
|
||||
export let ringColor = 'transparent'
|
||||
|
||||
let clientWidth = 0
|
||||
let clientHeight = 0
|
||||
|
||||
$: circleRadius = Math.sqrt(clientWidth * clientWidth + clientHeight * clientHeight) / 2
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={twMerge('gradient-button', wrapperClasses)}
|
||||
style="--margin-width: {marginWidth}; --animation-duration: {animationDuration}; --base-radius: {baseRadius}; --circle-radius: {circleRadius}; --ring-color: {ringColor}"
|
||||
class:animate
|
||||
bind:clientWidth
|
||||
bind:clientHeight
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.gradient-button {
|
||||
position: relative;
|
||||
padding: var(--margin-width, 2px);
|
||||
font-size: inherit;
|
||||
border: none;
|
||||
border-radius: calc(var(--base-radius) + var(--margin-width, 2px));
|
||||
color: currentColor;
|
||||
background: inherit;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Circular gradient */
|
||||
.gradient-button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: calc(var(--circle-radius, 300px) * 2px);
|
||||
height: calc(var(--circle-radius, 300px) * 2px);
|
||||
background: var(--ring-color, transparent);
|
||||
border-radius: 50%;
|
||||
z-index: -1;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.gradient-button.animate::before {
|
||||
background: conic-gradient(from 0deg, #63a9ff, #0c79fd, #033b80, #00142c, #63a9ff);
|
||||
animation: rotate var(--animation-duration, 2s) linear infinite;
|
||||
}
|
||||
|
||||
/* inner background */
|
||||
.gradient-button::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: var(--margin-width, 2px);
|
||||
right: var(--margin-width, 2px);
|
||||
bottom: var(--margin-width, 2px);
|
||||
left: var(--margin-width, 2px);
|
||||
background: inherit;
|
||||
border-radius: var(--base-radius);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.gradient-button.animate:hover::before {
|
||||
animation-duration: 1s;
|
||||
}
|
||||
</style>
|
||||
@@ -25,6 +25,7 @@
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import type { PickableProperties } from '../previousResults'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import AnimatedButton from '$lib/components/common/button/AnimatedButton.svelte'
|
||||
|
||||
export let pickableProperties: PickableProperties | undefined
|
||||
export let result: any = undefined
|
||||
@@ -62,41 +63,44 @@
|
||||
<Pane class={twMerge('relative !transition-none ', noPadding ? '' : 'p-2')}>
|
||||
<slot />
|
||||
</Pane>
|
||||
<Pane
|
||||
minSize={20}
|
||||
size={40}
|
||||
class="pt-2 relative ml-[-1px] border-4 !transition-none z-1000 {$propPickerConfig
|
||||
? 'rounded-l-md border-blue-500'
|
||||
: 'border-transparent'}"
|
||||
>
|
||||
{#if result}
|
||||
<PropPickerResult
|
||||
{result}
|
||||
{extraResults}
|
||||
{flow_input}
|
||||
allowCopy={!notSelectable && !$propPickerConfig}
|
||||
on:select={({ detail }) => {
|
||||
dispatch('select', detail)
|
||||
if ($propPickerConfig?.onSelect(detail)) {
|
||||
propPickerConfig.set(undefined)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{:else if pickableProperties}
|
||||
<PropPicker
|
||||
{displayContext}
|
||||
{error}
|
||||
{pickableProperties}
|
||||
{notSelectable}
|
||||
allowCopy={!notSelectable && !$propPickerConfig}
|
||||
on:select={({ detail }) => {
|
||||
dispatch('select', detail)
|
||||
if ($propPickerConfig?.onSelect(detail)) {
|
||||
propPickerConfig.set(undefined)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<Pane minSize={20} size={40} class="!transition-none z-1000 ml-[-1px]">
|
||||
<AnimatedButton
|
||||
animate={$propPickerConfig?.insertionMode == 'connect'}
|
||||
baseRadius="4px"
|
||||
wrapperClasses="h-full w-full pt-2"
|
||||
marginWidth="4px"
|
||||
ringColor={$propPickerConfig?.insertionMode == 'insert' ? '#3b82f6' : 'transparent'}
|
||||
animationDuration="1s"
|
||||
>
|
||||
{#if result}
|
||||
<PropPickerResult
|
||||
{result}
|
||||
{extraResults}
|
||||
{flow_input}
|
||||
allowCopy={!notSelectable && !$propPickerConfig}
|
||||
on:select={({ detail }) => {
|
||||
dispatch('select', detail)
|
||||
if ($propPickerConfig?.onSelect(detail)) {
|
||||
propPickerConfig.set(undefined)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{:else if pickableProperties}
|
||||
<PropPicker
|
||||
{displayContext}
|
||||
{error}
|
||||
{pickableProperties}
|
||||
{notSelectable}
|
||||
allowCopy={!notSelectable && !$propPickerConfig}
|
||||
on:select={({ detail }) => {
|
||||
dispatch('select', detail)
|
||||
if ($propPickerConfig?.onSelect(detail)) {
|
||||
propPickerConfig.set(undefined)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</AnimatedButton>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
|
||||
@@ -63,12 +63,19 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full !bg-surface">
|
||||
<div class="flex flex-col h-full !bg-surface rounded overflow-hidden">
|
||||
<div class="px-2">
|
||||
{#if !notSelectable}
|
||||
<div class="flex flex-row space-x-1">
|
||||
{#if $propPickerConfig}
|
||||
<!-- <Badge large color="blue">
|
||||
{`Selected: ${$propPickerConfig?.propName}`}
|
||||
</Badge> -->
|
||||
<Badge large color="blue">
|
||||
{`Mode: ${$propPickerConfig?.insertionMode}`}
|
||||
</Badge>
|
||||
{:else}
|
||||
<Badge large color="blue">← Edit or connect an input</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<ClearableInput bind:value={search} placeholder="Search prop..." wrapperClass="py-2" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user