Control scale: Button/IconButton primitives, two-height tokens, app-wide migration

This commit is contained in:
2026-06-12 23:13:13 +03:00
parent af9cc8a716
commit 7d4906ec38
41 changed files with 660 additions and 904 deletions
+16 -43
View File
@@ -37,6 +37,7 @@ import {
} from '../stores/DatasetStore';
import { useSnippetStore } from '../stores/SnippetStore';
import { SegmentedControl, type SegmentedOption } from './SegmentedControl';
import { Button } from './Button';
import { Icon } from './Icon';
import styles from './DatasetsModal.module.css';
@@ -73,9 +74,9 @@ export function DatasetsModal() {
return (
<div className={styles.manager}>
<div className={styles.listPane}>
<button type="button" className={styles.newButton} onClick={handleNew}>
<Button variant="primary" size="lg" className={styles.newButton} onClick={handleNew}>
<Icon name="add" /> New Dataset
</button>
</Button>
<ul className={styles.list}>
{ordered.length === 0 && (
<li className={styles.empty}>
@@ -239,9 +240,7 @@ function DatasetDetail({
<div className={styles.detailHead}>
<h3 className={styles.detailName}>{dataset.name}</h3>
<div className={styles.detailActions}>
<button type="button" className={styles.action} onClick={() => void handleCopy()}>
{copied ? 'Copied' : 'Copy Reference'}
</button>
<Button onClick={() => void handleCopy()}>{copied ? 'Copied' : 'Copy Reference'}</Button>
{/* The clipboard write is invisible, so the success is confirmed inline
("Copied") rather than by a toast (docs/architecture/10 → Toast copy).
This polite live region announces it to assistive tech, which the
@@ -250,35 +249,18 @@ function DatasetDetail({
{copied ? 'Reference copied to clipboard' : ''}
</span>
{dataset.source === 'url' && (
<button
type="button"
className={styles.action}
onClick={() => void handleRefresh()}
disabled={refreshing}
>
<Button onClick={() => void handleRefresh()} disabled={refreshing}>
{refreshing ? 'Refreshing…' : 'Refresh'}
</button>
</Button>
)}
<button type="button" className={styles.action} onClick={handleEdit}>
Edit
</button>
<Button onClick={handleEdit}>Edit</Button>
{/* Build Chart (spec §05 → §06) — opens the Chart Builder on this dataset.
Replaces the Datasets modal (one modal at a time, §01C); detail view has
no transient form state, so no discard prompt. */}
<button
type="button"
className={styles.action}
onClick={() => openModal('chartBuilder', String(dataset.id))}
>
Build Chart
</button>
<button
type="button"
className={`${styles.action} ${styles.danger}`}
onClick={() => void handleDelete()}
>
<Button onClick={() => openModal('chartBuilder', String(dataset.id))}>Build Chart</Button>
<Button variant="danger-outline" onClick={() => void handleDelete()}>
Delete
</button>
</Button>
</div>
</div>
@@ -574,27 +556,18 @@ function DatasetFormView({ editing }: { editing: boolean }) {
{/* A failed fetch is recoverable by pasting the data inline (the user's choice
when CORS or offline blocks the URL) — offered as a direct one-click path. */}
{fetchError && (
<button
type="button"
className={`${styles.action} ${styles.fallback}`}
onClick={handlePasteInline}
>
<Button className={styles.fallback} onClick={handlePasteInline}>
Paste data inline instead
</button>
</Button>
)}
<div className={styles.formActions}>
<button type="button" className={styles.action} onClick={handleCancel} disabled={fetching}>
<Button onClick={handleCancel} disabled={fetching}>
Cancel
</button>
<button
type="button"
className={`${styles.action} ${styles.primary}`}
disabled={!canSave || fetching}
onClick={() => void handleSave()}
>
</Button>
<Button variant="primary" disabled={!canSave || fetching} onClick={() => void handleSave()}>
{fetching ? 'Fetching…' : editing ? 'Save changes' : 'Create dataset'}
</button>
</Button>
</div>
</div>
);