SelectControl: 32px trigger scale + option-group divider in the theme picker

This commit is contained in:
2026-06-12 21:46:53 +03:00
parent ca70b3e491
commit d1ba921141
8 changed files with 136 additions and 30 deletions
+24 -14
View File
@@ -22,7 +22,7 @@
* caller intercept the click entirely (the armed-channel fast path).
*/
import { type ReactNode } from 'react';
import { Fragment, type ReactNode } from 'react';
import { createPortal } from 'react-dom';
import { usePopover } from '../hooks/usePopover';
import styles from './SelectControl.module.css';
@@ -35,6 +35,12 @@ export interface SelectControlOption<V extends string> {
label: string;
/** Optional secondary line (e.g. "replaces Ship Mode" on an occupied channel). */
detail?: string;
/**
* Draw a group separator above this option (purely visual, `role="presentation"`;
* keyboard order is untouched) — e.g. the chart-theme picker's boundary between
* the user's themes and the preset roster.
*/
dividerBefore?: boolean;
}
export interface SelectControlProps<V extends string> {
@@ -150,19 +156,23 @@ export function SelectControl<V extends string>({
{options.map((o) => {
const selected = value !== undefined && o.value === value;
return (
<button
key={o.value}
type="button"
className={`${styles.option} ${selected ? styles.selected : ''}`}
aria-current={selected || undefined}
onClick={() => choose(o.value)}
>
<span className={styles.optionLabel}>
{o.label}
{o.detail !== undefined && <span className={styles.detail}>{o.detail}</span>}
</span>
{selected && <span aria-hidden="true"></span>}
</button>
<Fragment key={o.value}>
{o.dividerBefore && <div className={styles.divider} role="presentation" />}
<button
type="button"
className={`${styles.option} ${selected ? styles.selected : ''}`}
aria-current={selected || undefined}
onClick={() => choose(o.value)}
>
<span className={styles.optionLabel}>
{o.label}
{o.detail !== undefined && (
<span className={styles.detail}>{o.detail}</span>
)}
</span>
{selected && <span aria-hidden="true"></span>}
</button>
</Fragment>
);
})}
</div>