rename and tags fields submit on enter, not just the buttons
The node rename input and the owner/tags editor's tags field sat in a div, so pressing Enter did nothing and the check/apply button was the only way in. Both are now real forms with the right button wrapped as type="submit" and their manual Enter handling removed so there's no double-submit; Escape handling stays since a form has no native equivalent for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+15
-17
@@ -103,6 +103,12 @@ const Ownership = ({ node, busy, onError }: OwnershipProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const applyTags = (ev?: React.FormEvent) => {
|
||||||
|
ev?.preventDefault();
|
||||||
|
if (!tagsDirty) return;
|
||||||
|
void run(() => setTags.mutateAsync({ id: node.id, tags: nextTags }));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2.5">
|
<div className="flex flex-col gap-2.5">
|
||||||
<div className="text-[10px] font-semibold uppercase tracking-wide text-zinc-500">Owner and tags</div>
|
<div className="text-[10px] font-semibold uppercase tracking-wide text-zinc-500">Owner and tags</div>
|
||||||
@@ -148,13 +154,12 @@ const Ownership = ({ node, busy, onError }: OwnershipProps) => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<form onSubmit={applyTags} className="flex flex-wrap items-center gap-2">
|
||||||
<TagIcon className="h-3.5 w-3.5 shrink-0 text-zinc-500" />
|
<TagIcon className="h-3.5 w-3.5 shrink-0 text-zinc-500" />
|
||||||
<input
|
<input
|
||||||
value={draftTags}
|
value={draftTags}
|
||||||
onChange={(ev) => setDraftTags(ev.target.value)}
|
onChange={(ev) => setDraftTags(ev.target.value)}
|
||||||
onKeyDown={(ev) => {
|
onKeyDown={(ev) => {
|
||||||
if (ev.key === 'Enter' && tagsDirty) void run(() => setTags.mutateAsync({ id: node.id, tags: nextTags }));
|
|
||||||
if (ev.key === 'Escape') setDraftTags(node.tags.join(' '));
|
if (ev.key === 'Escape') setDraftTags(node.tags.join(' '));
|
||||||
}}
|
}}
|
||||||
placeholder="tag:server tag:eu — space separated"
|
placeholder="tag:server tag:eu — space separated"
|
||||||
@@ -164,19 +169,16 @@ const Ownership = ({ node, busy, onError }: OwnershipProps) => {
|
|||||||
/>
|
/>
|
||||||
{tagsDirty && (
|
{tagsDirty && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button type="submit" disabled={busy || pending}>
|
||||||
onClick={() => void run(() => setTags.mutateAsync({ id: node.id, tags: nextTags }))}
|
|
||||||
disabled={busy || pending}
|
|
||||||
>
|
|
||||||
<Check className="h-3.5 w-3.5" />
|
<Check className="h-3.5 w-3.5" />
|
||||||
Apply tags
|
Apply tags
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => setDraftTags(node.tags.join(' '))} disabled={busy || pending}>
|
<Button type="button" onClick={() => setDraftTags(node.tags.join(' '))} disabled={busy || pending}>
|
||||||
Revert
|
Revert
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</form>
|
||||||
<p className="text-[11px] leading-snug text-zinc-600">
|
<p className="text-[11px] leading-snug text-zinc-600">
|
||||||
Tags are what the access policy targets. A tag no rule mentions does nothing; removing one a rule depends on
|
Tags are what the access policy targets. A tag no rule mentions does nothing; removing one a rule depends on
|
||||||
cuts the node off from it. The <span className="text-zinc-500">tag:</span> prefix is added for you.
|
cuts the node off from it. The <span className="text-zinc-500">tag:</span> prefix is added for you.
|
||||||
@@ -204,7 +206,8 @@ const NodeCard = ({ node, onError }: NodeCardProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitRename = async () => {
|
const submitRename = async (ev?: React.FormEvent) => {
|
||||||
|
ev?.preventDefault();
|
||||||
const name = draftName.trim();
|
const name = draftName.trim();
|
||||||
setRenaming(false);
|
setRenaming(false);
|
||||||
if (!name || name === node.name) return;
|
if (!name || name === node.name) return;
|
||||||
@@ -218,29 +221,24 @@ const NodeCard = ({ node, onError }: NodeCardProps) => {
|
|||||||
<Dot tone={node.online ? 'ok' : 'idle'} />
|
<Dot tone={node.online ? 'ok' : 'idle'} />
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
{renaming ? (
|
{renaming ? (
|
||||||
<div className="flex items-center gap-1.5">
|
<form onSubmit={submitRename} className="flex items-center gap-1.5">
|
||||||
<input
|
<input
|
||||||
value={draftName}
|
value={draftName}
|
||||||
onChange={(ev) => setDraftName(ev.target.value)}
|
onChange={(ev) => setDraftName(ev.target.value)}
|
||||||
onKeyDown={(ev) => {
|
onKeyDown={(ev) => {
|
||||||
if (ev.key === 'Enter') void submitRename();
|
|
||||||
if (ev.key === 'Escape') setRenaming(false);
|
if (ev.key === 'Escape') setRenaming(false);
|
||||||
}}
|
}}
|
||||||
autoFocus
|
autoFocus
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="min-w-0 flex-1 rounded-md border border-white/10 bg-black/40 px-2 py-1 text-sm text-zinc-100 outline-none focus:border-primary/50"
|
className="min-w-0 flex-1 rounded-md border border-white/10 bg-black/40 px-2 py-1 text-sm text-zinc-100 outline-none focus:border-primary/50"
|
||||||
/>
|
/>
|
||||||
<button
|
<button type="submit" className="cursor-pointer p-1 text-emerald-400">
|
||||||
type="button"
|
|
||||||
onClick={() => void submitRename()}
|
|
||||||
className="cursor-pointer p-1 text-emerald-400"
|
|
||||||
>
|
|
||||||
<Check className="h-3.5 w-3.5" />
|
<Check className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
<button type="button" onClick={() => setRenaming(false)} className="cursor-pointer p-1 text-zinc-500">
|
<button type="button" onClick={() => setRenaming(false)} className="cursor-pointer p-1 text-zinc-500">
|
||||||
<X className="h-3.5 w-3.5" />
|
<X className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<span className="truncate text-sm font-medium text-zinc-100">
|
<span className="truncate text-sm font-medium text-zinc-100">
|
||||||
|
|||||||
Reference in New Issue
Block a user