Скилл переносится из ~/.claude/skills/aura-design в репозиторий обвязки: SKILL.md, справочники reference/ и набор kit/ (токены, base.css, компоненты React, оболочка AppShell, тест токенов, test-setup, примеры входа). install.sh ставит его в ~/.claude/skills/aura-design, --check его видит. tests/test-aura-design.sh проверяет структуру и инварианты набора. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
34 lines
1.5 KiB
TypeScript
34 lines
1.5 KiB
TypeScript
import { Fragment, type ReactNode } from "react";
|
||
|
||
type Option<T extends string> = { value: T; label: string; href?: string };
|
||
// ссылка по умолчанию — <a>; в приложении с роутером передай link={(p) => <Link to={p.href} …/>}
|
||
export type LinkRender = (p: { href: string; className: string; "aria-current"?: "page"; children: ReactNode }) => ReactNode;
|
||
const plainLink: LinkRender = ({ href, ...rest }) => <a href={href} {...rest} />;
|
||
|
||
// переключатель на сером треке: кнопки (role=radio) или ссылки разделов (aria-current)
|
||
export function Segmented<T extends string>({ label, value, options, onChange, link = plainLink }: {
|
||
label: string; value: T | null; options: Option<T>[]; onChange?(v: T): void; link?: LinkRender;
|
||
}) {
|
||
if (options.some((o) => o.href)) {
|
||
return (
|
||
<nav className="ui-seg" aria-label={label}>
|
||
{options.map((o) => (
|
||
<Fragment key={o.value}>
|
||
{link({ href: o.href ?? "#", className: "ui-seg__item", "aria-current": o.value === value ? "page" : undefined, children: o.label })}
|
||
</Fragment>
|
||
))}
|
||
</nav>
|
||
);
|
||
}
|
||
return (
|
||
<div className="ui-seg" role="radiogroup" aria-label={label}>
|
||
{options.map((o) => (
|
||
<button key={o.value} type="button" role="radio" aria-checked={o.value === value} className="ui-seg__item"
|
||
onClick={() => onChange?.(o.value)}>
|
||
{o.label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|