Files
windmill/frontend/src/lib/components/SavedInputsPickerViewer.svelte
T

163 lines
4.2 KiB
Svelte

<script lang="ts">
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 } from 'svelte'
export let payloadData: any
export let limitPayloadSize: boolean = false
export let hover: boolean = false
export let viewerOpen: boolean = false
export let maxWidth: number | undefined = undefined
export let editOptions: boolean = true
const dispatch = createEventDispatcher()
const payloadTooBigForPreview = payloadData != 'WINDMILL_TOO_BIG' && isObjectTooBig(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 = editOptions ? 218 : 168 // width of the optional buttons on the right
let popover: Popover | undefined
let popoverOpen = false
let hoverTimeout: ReturnType<typeof setTimeout> | undefined
let objectViewerLoaded = false
let popoverFullyOpened = 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
}
}
}
$: handleHoverChange(hover)
$: ajustedWidth = maxWidth ? Math.abs(maxWidth - xOffset) : undefined
</script>
<Popover
bind:this={popover}
{floatingConfig}
on:openChange={handlePopoverChange}
on:click={(e) => {
e.stopPropagation()
}}
closeOnOtherPopoverOpen
closeOnClickOutside={false}
usePointerDownOutside
>
<svelte:fragment slot="trigger" let:isOpen>
<Button
variant="subtle"
unifiedSize="sm"
btnClasses={hover || isOpen ? 'block -my-2 bg-transparent' : 'hidden'}
wrapperClasses="p-0"
iconOnly
nonCaptureEvent
startIcon={{ icon: Eye }}
/>
</svelte:fragment>
<svelte:fragment slot="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"
on:click={() => {
copyToClipboard(JSON.stringify(payloadData))
}}
on: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>
</svelte:fragment>
</Popover>