mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-09 16:05:42 +00:00
* Remove $$props.field usage * Rename slots to ensure no hyphen * _props * _trigger * OnSelectedIteration type correct capitalization * rename _content * Remove afterUpdate * Migrate everything to svelte 5 * array bind * Fix popover * type never * nit fixes * Fixed many trivial errors * onClick * Fix errors * use let: * nit typing * fix: wrap state_referenced_locally vars with untrack() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add untrack import * Fix all syntax errors due to untrack migration * Fix undefined errors * Fix more undefined errors * untrack(() => initialOpen) * svelte-ignore * Fix state_descriptors_fixed error in Chart.svelte Use $state.snapshot() to pass plain copies of data/options to Chart.js instead of $state proxies. Chart.js's listenArrayEvents tries to define property descriptors on data arrays, which Svelte 5 proxies reject. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * nit typing * Merge issue * Fix "path is not set" error in resource picker / editor * Fix InputTransformForm error when rerunning some flows * fix npm run check --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
179 lines
4.5 KiB
Svelte
179 lines
4.5 KiB
Svelte
<script lang="ts">
|
|
import { run, createBubbler } from 'svelte/legacy'
|
|
|
|
const bubble = createBubbler()
|
|
import { Button } from '$lib/components/common'
|
|
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
|
import ObjectViewerWrapper from '$lib/components/propertyPicker/ObjectViewerWrapper.svelte'
|
|
import { copyToClipboard, isObjectTooBig } from '$lib/utils'
|
|
import { Eye, CopyIcon, Loader2 } from 'lucide-svelte'
|
|
import { createEventDispatcher, untrack } from 'svelte'
|
|
|
|
interface Props {
|
|
payloadData: any
|
|
limitPayloadSize?: boolean
|
|
hover?: boolean
|
|
viewerOpen?: boolean
|
|
maxWidth?: number | undefined
|
|
editOptions?: boolean
|
|
}
|
|
|
|
let {
|
|
payloadData,
|
|
limitPayloadSize = false,
|
|
hover = false,
|
|
viewerOpen = false,
|
|
maxWidth = undefined,
|
|
editOptions = true
|
|
}: Props = $props()
|
|
|
|
const dispatch = createEventDispatcher()
|
|
const payloadTooBigForPreview = untrack(() => payloadData) != 'WINDMILL_TOO_BIG' && isObjectTooBig(untrack(() => payloadData))
|
|
const buttonWidth = 34
|
|
const floatingConfig = {
|
|
placement: 'bottom-end',
|
|
strategy: 'fixed',
|
|
offset: { mainAxis: 8, crossAxis: -buttonWidth },
|
|
gutter: 0,
|
|
middleware: [
|
|
{
|
|
name: 'flip',
|
|
options: {
|
|
fallbackPlacements: ['bottom-top']
|
|
}
|
|
}
|
|
]
|
|
}
|
|
const xOffset = untrack(() => editOptions) ? 218 : 168 // width of the optional buttons on the right
|
|
|
|
let popover: Popover | undefined = $state()
|
|
let popoverOpen = false
|
|
let hoverTimeout: ReturnType<typeof setTimeout> | undefined
|
|
let objectViewerLoaded = $state(false)
|
|
let popoverFullyOpened = $state(false)
|
|
|
|
function handlePopoverChange(event: CustomEvent<boolean>) {
|
|
const isOpen = event.detail
|
|
popoverOpen = isOpen
|
|
|
|
if (!isOpen) {
|
|
objectViewerLoaded = false
|
|
popoverFullyOpened = false
|
|
} else {
|
|
// Set a small delay to ensure the popover is fully rendered before
|
|
// marking it as fully opened
|
|
setTimeout(() => {
|
|
popoverFullyOpened = true
|
|
}, 50)
|
|
}
|
|
|
|
dispatch('openChange', isOpen)
|
|
}
|
|
|
|
function handleHoverChange(isHovering: boolean) {
|
|
if (isHovering) {
|
|
// Start debounce timer when hovering starts
|
|
hoverTimeout = setTimeout(() => {
|
|
if (viewerOpen && popover && !popoverOpen) {
|
|
popover.open()
|
|
}
|
|
}, 100)
|
|
} else {
|
|
// Clear timeout if hover ends before debounce period
|
|
if (hoverTimeout) {
|
|
clearTimeout(hoverTimeout)
|
|
hoverTimeout = undefined
|
|
}
|
|
}
|
|
}
|
|
|
|
run(() => {
|
|
handleHoverChange(hover)
|
|
})
|
|
let ajustedWidth = $derived(maxWidth ? Math.abs(maxWidth - xOffset) : undefined)
|
|
</script>
|
|
|
|
<Popover
|
|
bind:this={popover}
|
|
{floatingConfig}
|
|
on:openChange={handlePopoverChange}
|
|
on:click={(e) => {
|
|
e.stopPropagation()
|
|
}}
|
|
closeOnOtherPopoverOpen
|
|
closeOnOutsideClick={false}
|
|
usePointerDownOutside
|
|
>
|
|
{#snippet trigger({ isOpen })}
|
|
<Button
|
|
variant="subtle"
|
|
unifiedSize="sm"
|
|
btnClasses={hover || isOpen ? 'block -my-2 bg-transparent' : 'hidden'}
|
|
wrapperClasses="p-0"
|
|
iconOnly
|
|
nonCaptureEvent
|
|
startIcon={{ icon: Eye }}
|
|
/>
|
|
{/snippet}
|
|
{#snippet content()}
|
|
<div
|
|
class="p-2 overflow-auto"
|
|
style="width: {ajustedWidth ? ajustedWidth + 'px' : '50vh'}; max-height: 50vh"
|
|
>
|
|
{#if payloadData === 'WINDMILL_TOO_BIG'}
|
|
<div class="text-center text-primary text-xs">
|
|
{#if limitPayloadSize}
|
|
Payload too big to be used
|
|
{:else}
|
|
Payload too big to preview but can still be loaded
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<div
|
|
class="relative p-2"
|
|
role="button"
|
|
tabindex="0"
|
|
aria-label="Copy JSON payload to clipboard"
|
|
onclick={() => {
|
|
copyToClipboard(JSON.stringify(payloadData))
|
|
}}
|
|
onkeydown={bubble('keydown')}
|
|
>
|
|
{#if !objectViewerLoaded && payloadTooBigForPreview}
|
|
<div class="flex justify-center items-center py-4">
|
|
<Loader2 size={20} class="animate-spin text-primary" />
|
|
<span class="ml-2 text-xs text-primary">Loading data...</span>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if popoverFullyOpened || !payloadTooBigForPreview}
|
|
<div
|
|
class={!objectViewerLoaded && payloadTooBigForPreview
|
|
? 'invisible h-0 overflow-hidden'
|
|
: ''}
|
|
>
|
|
<ObjectViewerWrapper
|
|
json={payloadData}
|
|
allowCopy
|
|
pureViewer
|
|
on:mounted={() => (objectViewerLoaded = true)}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="absolute top-2 right-2 w-full h-full">
|
|
<Button
|
|
variant="subtle"
|
|
unifiedSize="sm"
|
|
class="absolute top-0 right-0"
|
|
iconOnly
|
|
startIcon={{ icon: CopyIcon }}
|
|
nonCaptureEvent
|
|
/>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/snippet}
|
|
</Popover>
|