swarm-ui: add CheckboxField and use it for the issue report toggle and the account delete option
This commit is contained in:
parent
a1a70e257c
commit
acf82beba3
7 changed files with 105 additions and 30 deletions
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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) => (
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue