swarm-ui: drop the redundant outer dialog card, move close into the panel header
Dialog and Panel both drew their own bordered/backgrounded card in the same --bg-elev, so a Panel-based dialog (create-agent, link-matrix-account) rendered as two concentric cards with a floating close button on the outer one and no purpose to it. Give Dialog a "plain" mode that drops its own card chrome (border, background, padding) and floating close button, and give Panel an optional onClose that renders a close button at the end of its own header row instead. AgentsPage's two Panel-backed dialogs now use plain + Panel's onClose, so the Panel is the dialog's only visible card. ConfirmDialog (no Panel of its own) is unaffected — plain defaults to false, unchanged card + floating close button. Added a ComponentsPage sample demonstrating the plain + onClose pairing. Verified both dialog modes via a real headless-chromium screenshot (plain dialog: single card, close button in the header bar; default dialog: unchanged floating close button).
This commit is contained in:
parent
09d502ea34
commit
8a4c613e4e
8 changed files with 158 additions and 18 deletions
|
|
@ -219,6 +219,36 @@ function DialogSample() {
|
|||
);
|
||||
}
|
||||
|
||||
// `plain`: a `Panel`'s own header bar/border is the dialog's
|
||||
// only card, not a second one nested inside `Dialog`'s own — pair
|
||||
// `plain` with `Panel`'s `onClose` so the close button still exists,
|
||||
// just moved into that header instead of floating over the whole thing.
|
||||
function PlainDialogSample() {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>open plain dialog</Button>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
label="sample plain dialog"
|
||||
plain
|
||||
>
|
||||
<Panel
|
||||
title="a form-shaped dialog"
|
||||
icon="🔗"
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<p>
|
||||
One card, one header bar — the close button lives here instead of
|
||||
floating over a second, purposeless outer one.
|
||||
</p>
|
||||
</Panel>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Own state for the same reason `TextFieldSample` needs it — a
|
||||
// controlled sample has to actually respond to interaction.
|
||||
function MultiselectFilterSample() {
|
||||
|
|
@ -458,6 +488,9 @@ export function ComponentsPage() {
|
|||
<Sample label="modal, caller-owned content">
|
||||
<DialogSample />
|
||||
</Sample>
|
||||
<Sample label="plain — content supplies its own card + close">
|
||||
<PlainDialogSample />
|
||||
</Sample>
|
||||
</Section>
|
||||
|
||||
<Section title="ConfirmDialog">
|
||||
|
|
|
|||
|
|
@ -72,7 +72,14 @@ type SubmitState =
|
|||
// exception report on this page.
|
||||
const NAME_PATTERN = "[a-z0-9\\-]{1,63}";
|
||||
|
||||
export function CreateAgentForm() {
|
||||
export function CreateAgentForm({
|
||||
onClose,
|
||||
}: {
|
||||
/** Rendered as the form panel's own close button — this form always
|
||||
* mounts inside a `plain` `Dialog` (see `AgentsPage`), which no longer
|
||||
* floats its own. */
|
||||
onClose?: () => void;
|
||||
}) {
|
||||
const [name, setName] = useState("");
|
||||
const [hive, setHive] = useState("");
|
||||
// `null` = still loading, `[]` = loaded but empty (a real, if unusual,
|
||||
|
|
@ -132,7 +139,7 @@ export function CreateAgentForm() {
|
|||
return (
|
||||
<div class="create-agent-layout">
|
||||
<div class="create-agent-form-col">
|
||||
<Panel title="create agent" icon="🤖">
|
||||
<Panel title="create agent" icon="🤖" onClose={onClose}>
|
||||
<p class="create-agent-intro">
|
||||
Create a new agent's swarm-level identity. This only queues the job
|
||||
— check <Link href="/jobs">jobs</Link> to watch it settle.
|
||||
|
|
|
|||
|
|
@ -63,9 +63,14 @@ const MODE_OPTIONS = [
|
|||
export function LinkMatrixAccountForm({
|
||||
hive,
|
||||
agent,
|
||||
onClose,
|
||||
}: {
|
||||
hive: string;
|
||||
agent: string;
|
||||
/** Rendered as the panel header's own close button — this form always
|
||||
* mounts inside a `plain` `Dialog` (see `AgentsPage`), which no longer
|
||||
* floats its own. */
|
||||
onClose?: () => void;
|
||||
}) {
|
||||
const [account, setAccount] = useState("");
|
||||
const [mode, setMode] = useState<Mode>("token");
|
||||
|
|
@ -127,7 +132,11 @@ export function LinkMatrixAccountForm({
|
|||
}
|
||||
|
||||
return (
|
||||
<Panel title={`link a matrix account — ${agent}`} icon="🔗">
|
||||
<Panel
|
||||
title={`link a matrix account — ${agent}`}
|
||||
icon="🔗"
|
||||
onClose={onClose}
|
||||
>
|
||||
<p>
|
||||
Writes the credential to the swarm secret store and notifies{" "}
|
||||
<strong>{hive}</strong> to deliver it. The agent's own matrix daemon
|
||||
|
|
|
|||
|
|
@ -592,13 +592,15 @@ export function AgentsPage() {
|
|||
open={createOpen}
|
||||
onClose={() => setCreateOpen(false)}
|
||||
label="create agent"
|
||||
plain
|
||||
>
|
||||
<CreateAgentForm />
|
||||
<CreateAgentForm onClose={() => setCreateOpen(false)} />
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={matrixTarget !== null}
|
||||
onClose={() => setMatrixTarget(null)}
|
||||
label="link a matrix account"
|
||||
plain
|
||||
>
|
||||
{/* `matrixTarget.hive` is non-null here — the trigger badge above
|
||||
is disabled without one, so this can only open with a real
|
||||
|
|
@ -607,6 +609,7 @@ export function AgentsPage() {
|
|||
<LinkMatrixAccountForm
|
||||
hive={matrixTarget.hive}
|
||||
agent={matrixTarget.name}
|
||||
onClose={() => setMatrixTarget(null)}
|
||||
/>
|
||||
) : null}
|
||||
</Dialog>
|
||||
|
|
|
|||
Loading…
Reference in a new issue