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:
2026-08-19 10:44:02 +00:00
co-authored by Claude Opus 5
parent 80f952a9a6
commit ad41bafd59
+15 -17
View File
@@ -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 (
<div className="flex flex-col gap-2.5">
<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>
)}
<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" />
<input
value={draftTags}
onChange={(ev) => setDraftTags(ev.target.value)}
onKeyDown={(ev) => {
if (ev.key === 'Enter' && tagsDirty) void run(() => setTags.mutateAsync({ id: node.id, tags: nextTags }));
if (ev.key === 'Escape') setDraftTags(node.tags.join(' '));
}}
placeholder="tag:server tag:eu — space separated"
@@ -164,19 +169,16 @@ const Ownership = ({ node, busy, onError }: OwnershipProps) => {
/>
{tagsDirty && (
<>
<Button
onClick={() => void run(() => setTags.mutateAsync({ id: node.id, tags: nextTags }))}
disabled={busy || pending}
>
<Button type="submit" disabled={busy || pending}>
<Check className="h-3.5 w-3.5" />
Apply tags
</Button>
<Button onClick={() => setDraftTags(node.tags.join(' '))} disabled={busy || pending}>
<Button type="button" onClick={() => setDraftTags(node.tags.join(' '))} disabled={busy || pending}>
Revert
</Button>
</>
)}
</div>
</form>
<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
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();
setRenaming(false);
if (!name || name === node.name) return;
@@ -218,29 +221,24 @@ const NodeCard = ({ node, onError }: NodeCardProps) => {
<Dot tone={node.online ? 'ok' : 'idle'} />
<div className="min-w-0 flex-1">
{renaming ? (
<div className="flex items-center gap-1.5">
<form onSubmit={submitRename} className="flex items-center gap-1.5">
<input
value={draftName}
onChange={(ev) => setDraftName(ev.target.value)}
onKeyDown={(ev) => {
if (ev.key === 'Enter') void submitRename();
if (ev.key === 'Escape') setRenaming(false);
}}
autoFocus
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"
/>
<button
type="button"
onClick={() => void submitRename()}
className="cursor-pointer p-1 text-emerald-400"
>
<button type="submit" className="cursor-pointer p-1 text-emerald-400">
<Check className="h-3.5 w-3.5" />
</button>
<button type="button" onClick={() => setRenaming(false)} className="cursor-pointer p-1 text-zinc-500">
<X className="h-3.5 w-3.5" />
</button>
</div>
</form>
) : (
<div className="flex flex-wrap items-center gap-2">
<span className="truncate text-sm font-medium text-zinc-100">