fix: grey the draft banner's Discard while a command is in flight, like Save

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-09-15 10:31:53 +02:00
co-authored by Claude Opus 5
parent 8a6fa9b8a4
commit a68b19130f
5 changed files with 33 additions and 12 deletions
@@ -19,6 +19,12 @@
onDiscard: () => void | Promise<void>
/** When true (e.g. no write access), hide the Discard action. */
disabled?: boolean
/**
* A command is in flight on the item. What a discard would revert to is whatever that
* command leaves behind, which is not what the user is looking at, so it waits, greyed
* rather than hidden, like Save.
*/
busy?: boolean
/** Diff drawer title. */
title?: string
/**
@@ -35,6 +41,7 @@
getCurrent,
onDiscard,
disabled = false,
busy = false,
title = 'Deployed <> Local changes',
reserveSpace
}: Props = $props()
@@ -85,23 +92,24 @@
const original = $state.snapshot(deployed) as Value
const current = $state.snapshot(getCurrent()) as Value
diffDrawer?.openDrawer()
// Mirror the inline Discard's `disabled` gate inside the diff drawer —
// otherwise a read-only user could still trigger onDiscard via the
// drawer button even though we hid the banner's inline action.
// Mirror the inline Discard's gates inside the diff drawer — otherwise a read-only user
// could still trigger onDiscard via the drawer button even though we hid the banner's
// inline action.
diffDrawer?.setDiff({
mode: 'simple',
original,
current,
title,
button: disabled
? undefined
: {
text: 'Discard changes',
onClick: async () => {
await onDiscard()
diffDrawer?.closeDrawer()
button:
disabled || busy
? undefined
: {
text: 'Discard changes',
onClick: async () => {
await onDiscard()
diffDrawer?.closeDrawer()
}
}
}
})
}
</script>
@@ -142,6 +150,7 @@
<Button
unifiedSize="sm"
variant="subtle"
disabled={busy}
btnClasses={classes.warning.titleClass}
on:click={onDiscard}>Discard</Button
>
@@ -52,6 +52,9 @@
* so it can hide the banner's Discard button in read-only mode (matches
* the trigger editors' `disabled={!can_write}` wiring). */
onCanWriteChange?: (canWrite: boolean) => void
/** Notifies the parent drawer that a command is in flight on one of the resource's
* items, so it can grey the banner's Discard the way Save is greyed. */
onBusyChange?: (busy: boolean) => void
}
let {
@@ -65,7 +68,8 @@
selected: selectedProp = $bindable(),
viewJsonSchema = $bindable(),
onDraftStateChange,
onCanWriteChange
onCanWriteChange,
onBusyChange
}: Props = $props()
type ResourceState = {
@@ -349,6 +353,9 @@
$effect(() => {
onCanWriteChange?.(can_write === true)
})
$effect(() => {
onBusyChange?.(Object.values(items).some((it) => it.busy))
})
export function localDraftDeployed(): ResourceState | undefined {
return selectedItem?.deployed
@@ -58,6 +58,7 @@
let session = $state(0)
let hasLocalDraft = $state(false)
let canWriteSelected = $state(true)
let editorBusy = $state(false)
let path: string | undefined = $state(undefined)
let selected: string | undefined = $state(undefined)
@@ -169,6 +170,7 @@
bind:viewJsonSchema
onDraftStateChange={(v) => (hasLocalDraft = v)}
onCanWriteChange={(v) => (canWriteSelected = v)}
onBusyChange={(v) => (editorBusy = v)}
/>
{/key}
{/await}
@@ -191,6 +193,7 @@
else dispatch('refresh', removedPath)
}}
disabled={!canWriteSelected}
busy={editorBusy}
/>
{/snippet}
{#snippet actions()}
@@ -354,6 +354,7 @@
if ((await shown?.discard())?.removed && shown !== selectedItem) dispatch('create')
}}
disabled={!can_write}
busy={anyBusy}
/>
{/snippet}
<div class="flex flex-col gap-8 pb-2">
@@ -1346,6 +1346,7 @@
if ((await shown?.discard())?.removed && shown !== item.current) onUpdate?.(target)
}}
disabled={!can_write}
busy={item.busy}
/>
{/snippet}
{@render content()}