Files
windmill/frontend/src/lib/components/ResourceEditorDrawer.svelte
T
Ruben Fiszel 9c758df5eb reserve more fixed height for unsaved-changes banner to avoid content shift (#9873)
* fix(frontend): reserve fixed height for unsaved-changes banner to avoid content shift

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): remove border around reserved banner slot and shrink it

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): tighten top padding under the unsaved-changes banner

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): give unsaved-changes banner buttons minimal vertical breathing room

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(frontend): drop redundant Metadata section title in trigger and script editors

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): tuck schedule editor labels under summary to match convention

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* revert(frontend): keep Metadata section title in ScriptBuilder for a separate PR

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): drop leftover header-content margin on headless Section

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): drop top padding above resource editor first field

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style(frontend): match variable editor bottom padding to resource

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): add Path label in new resource form to match edit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): reserve half the banner height to halve the idle gap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): reserve a third of the banner height when idle

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): don't reserve banner slot or tighten top for new entities

Gate the reserved-height slot and the tight content top padding on the banner's baseline (bannerReserved) instead of merely on the banner snippet being present, so new-entity drawers keep normal top spacing and add no empty slot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(frontend): trim banner comments to the 4-line invariant limit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(frontend): describe partial-reserve banner behavior accurately

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:58:31 +02:00

114 lines
2.9 KiB
Svelte

<script lang="ts">
import { Button, Drawer } from './common'
import DrawerContent from './common/drawer/DrawerContent.svelte'
import { Loader2, Save } from 'lucide-svelte'
import WsSpecificVersions from './WsSpecificVersions.svelte'
import { workspaceStore } from '$lib/stores'
import LocalDraftBanner from './LocalDraftBanner.svelte'
let {
workspace = undefined,
disableChatOffset = false
}: { workspace?: string; disableChatOffset?: boolean } = $props()
let drawer: Drawer | undefined = $state()
let canSave = $state(true)
let resource_type: string | undefined = $state(undefined)
let defaultValues: Record<string, any> | undefined = $state(undefined)
let resourceEditor:
| {
save: () => void
localDraftDeployed: () => unknown
localDraftCurrent: () => unknown
discardLocalDraft: () => void
}
| undefined = $state(undefined)
let hasLocalDraft = $state(false)
let canWriteSelected = $state(true)
let path: string | undefined = $state(undefined)
let selected: string | undefined = $state(undefined)
let effectiveWorkspace = $derived(workspace ?? $workspaceStore!)
export async function initEdit(p: string): Promise<void> {
resource_type = undefined
path = p
selected = effectiveWorkspace
drawer?.openDrawer?.()
}
export async function initNew(
resourceType: string,
nDefaultValues?: Record<string, any>
): Promise<void> {
path = undefined
resource_type = resourceType
defaultValues = nDefaultValues
selected = effectiveWorkspace
drawer?.openDrawer?.()
}
let mode: 'edit' | 'new' = $derived(!path ? 'new' : 'edit')
</script>
<Drawer bind:this={drawer} size="50rem" {disableChatOffset}>
<DrawerContent
title={mode == 'edit' ? 'Edit ' + path : 'Add a resource'}
bannerReserved={mode == 'edit'}
on:close={drawer?.closeDrawer}
>
{#await import('./ResourceEditor.svelte')}
<Loader2 class="animate-spin" />
{:then Module}
<Module.default
{path}
{resource_type}
{defaultValues}
{workspace}
on:refresh
bind:this={resourceEditor}
bind:canSave
bind:selected
onDraftStateChange={(v) => (hasLocalDraft = v)}
onCanWriteChange={(v) => (canWriteSelected = v)}
/>
{/await}
{#snippet banner()}
<LocalDraftBanner
show={hasLocalDraft}
reserveSpace={mode == 'edit'}
getDeployed={() => resourceEditor?.localDraftDeployed()}
getCurrent={() => resourceEditor?.localDraftCurrent()}
onDiscard={() => resourceEditor?.discardLocalDraft()}
disabled={!canWriteSelected}
/>
{/snippet}
{#snippet actions()}
{#if mode == 'edit' && path && effectiveWorkspace}
<WsSpecificVersions
kind="resource"
workspaceId={effectiveWorkspace}
initialPath={path}
bind:selected
/>
{/if}
<Button
variant="accent"
unifiedSize="md"
startIcon={{ icon: Save }}
on:click={() => {
resourceEditor?.save()
drawer?.closeDrawer()
}}
disabled={!canSave}
>
Save
</Button>
{/snippet}
</DrawerContent>
</Drawer>