Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: add CheckboxField and use it for the issue report toggle and the account delete option

This commit is contained in:
iris 2026-10-21 23:49:57 +02:00 • committed by mara
commit acf82beba3
7 changed files with 105 additions and 30 deletions

View file

@ -13,6 +13,7 @@ import { useEffect, useState } from "preact/hooks";
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
import { Badge } from "@hive/shared/badge.js";
import { CheckboxField } from "../../ui/checkbox-field/CheckboxField.js";
import { ConfirmDialog } from "../../ui/confirm-dialog/ConfirmDialog.js";
import { accountKindLabel } from "./account-kind.js";
import "./LinkedAccounts.css";
@ -136,17 +137,12 @@ export function LinkedAccounts({
removed from the swarm secret store. The agent is not told.
</p>
{deleteTarget.kind === "matrix" ? (
<label>
<input
type="checkbox"
checked={revoke}
onChange={(e) =>
setRevoke((e.target as HTMLInputElement).checked)
}
/>{" "}
also log the token out at the homeserver; if that fails, the
account is kept
</label>
<CheckboxField
id="linked-accounts-revoke"
label="also log the token out at the homeserver; if that fails, the account is kept"
checked={revoke}
onChange={setRevoke}
/>
) : null}
{deleteError ? (
<ApiErrorPanel context="delete failed" problem={deleteError} />

View file

@ -27,6 +27,7 @@ import {
SelectField,
type SelectOption,
} from "../ui/select-field/SelectField.js";
import { CheckboxField } from "../ui/checkbox-field/CheckboxField.js";
import { Button, type ButtonVariant } from "../ui/button/Button.js";
import { Dialog } from "../ui/dialog/Dialog.js";
import { ConfirmDialog } from "../ui/confirm-dialog/ConfirmDialog.js";
@ -156,6 +157,18 @@ function SelectFieldSample() {
);
}
function CheckboxFieldSample() {
const [checked, setChecked] = useState(true);
return (
<CheckboxField
id="sample-checkbox-field"
label="hide blocked (open dependency)"
checked={checked}
onChange={setChecked}
/>
);
}
function RefreshIntervalPickerSample() {
const [value, setValue] = useState<RefreshIntervalMs>(30_000);
return (
@ -517,6 +530,21 @@ export function ComponentsPage() {
</Sample>
</Section>
<Section title="CheckboxField">
<Sample label="editable">
<CheckboxFieldSample />
</Sample>
<Sample label="disabled">
<CheckboxField
id="sample-checkbox-field-disabled"
label="also log the token out"
checked={false}
onChange={() => {}}
disabled
/>
</Sample>
</Section>
<Section title="Button">
<div class="components-chip-row">
{BUTTON_VARIANTS.map((variant) => (

View file

@ -15,12 +15,6 @@
gap: 1.25em;
margin-bottom: 1em;
}
.issue-report-toggle {
display: flex;
align-items: center;
gap: 0.4em;
cursor: pointer;
}
.issue-report-sort-btn {
background: none;
border: none;

View file

@ -33,6 +33,7 @@ import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
import { Badge } from "@hive/shared/badge.js";
import { useLocalSetting } from "@hive/shared/settings-storage.js";
import { Panel } from "../ui/panel/Panel.js";
import { CheckboxField } from "../ui/checkbox-field/CheckboxField.js";
import { SelectField } from "../ui/select-field/SelectField.js";
import { Table, type TableColumn } from "../ui/table/Table.js";
import { ALL_REPOS, reportUrl, repoOptions } from "./issue-report-repo.js";
@ -378,16 +379,12 @@ export function IssueReportPage() {
onChange={setRepoFilter}
options={repoOptions(repos, repoFilter)}
/>
<label class="issue-report-toggle">
<input
type="checkbox"
checked={hideBlocked}
onChange={(e) =>
setHideBlocked((e.target as HTMLInputElement).checked)
}
/>
hide blocked (open dependency)
</label>
<CheckboxField
id="issue-report-hide-blocked"
label="hide blocked (open dependency)"
checked={hideBlocked}
onChange={setHideBlocked}
/>
</div>
{reposError ? (
<ApiErrorPanel

View file

@ -0,0 +1,25 @@
/* <CheckboxField> — box and label on one row. `min-height` is the same
touch-target floor `.ui-form-control` sets (FormField.css has the
reasoning), so a checkbox beside a text or select field is as tall as it and
its label is centred on the same line. The box takes the accent colour from
the shared theme instead of the browser's blue. */
.ui-checkbox-field {
display: flex;
align-items: center;
gap: 0.5em;
min-height: 2.75em;
cursor: pointer;
color: var(--fg);
}
.ui-checkbox-field input {
accent-color: var(--purple);
width: 1.1em;
height: 1.1em;
margin: 0;
flex: none;
cursor: inherit;
}
.ui-checkbox-field:has(input:disabled) {
color: var(--muted);
cursor: not-allowed;
}

View file

@ -0,0 +1,33 @@
// <CheckboxField> — a labelled checkbox, the on/off sibling of `TextField` and
// `SelectField`. The label sits beside the box, not above it, and the whole
// row is one click target at the kit's touch-target height, so a checkbox next
// to a `SelectField` lines up with it and a bare browser checkbox no longer
// sets the row's size.
import "./CheckboxField.css";
export function CheckboxField({
id,
label,
checked,
onChange,
disabled,
}: {
id: string;
label: string;
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
}) {
return (
<label class="ui-checkbox-field" for={id}>
<input
id={id}
type="checkbox"
checked={checked}
disabled={disabled}
onChange={(e) => onChange((e.target as HTMLInputElement).checked)}
/>
<span>{label}</span>
</label>
);
}