import { Box } from '@luke-ui/react/box';
import { Button } from '@luke-ui/react/button';
import { Cluster } from '@luke-ui/react/cluster';
import { Icon } from '@luke-ui/react/icon';
import { Stack } from '@luke-ui/react/stack';
import { Text } from '@luke-ui/react/text';
import { TextInputField } from '@luke-ui/react/text-input-field';
import { vars } from '@luke-ui/react/theme';
import { cx } from '@luke-ui/react/utils';
import type { CSSProperties, JSX, ReactNode } from 'react';
import { useState } from 'react';
import { Button as RacButton } from 'react-aria-components/Button';
import { Disclosure, DisclosurePanel } from 'react-aria-components/Disclosure';
import { Heading } from 'react-aria-components/Heading';
import type { ThemeToken, ThemeTokenFamily } from '../generated/token-reference.generated.js';
import { themeTokens } from '../generated/token-reference.generated.js';
import type { TokenPurposeGroup } from '../lib/token-purpose-groups.js';
import { tokenPurposeGroups } from '../lib/token-purpose-groups.js';
import { DocsLink } from './docs-link.js';
const TOTAL_TOKEN_COUNT = themeTokens.length;
const VARIABLE_BY_PATH = new Map(themeTokens.map((token) => [token.path, token.variable]));
const MOTION_KEYFRAMES = `@keyframes luke-docs-token-motion { from { transform: translateX(-0.75rem); } to { transform: translateX(0.75rem); } } @media (prefers-reduced-motion: reduce) { [data-token-motion] { animation: none !important; } }`;
const FALLBACK_MOTION_DURATION = '0.9s';
const FALLBACK_MOTION_EASING = 'ease-in-out';
const stageStyle = {
borderColor: vars.color.border.decorative,
borderRadius: vars.radius.detail,
borderStyle: 'solid',
borderWidth: 1,
} as const satisfies CSSProperties;
/** Purpose-grouped index of every public token, sampled in the active theme. */
export function TokenExplorer(): JSX.Element {
const [filter, setFilter] = useState('');
const query = filter.trim().toLowerCase();
const groups = matchGroups(query);
const matchCount = groups.reduce((total, group) => total + group.tokens.length, 0);
const countText =
query === '' ? `${TOTAL_TOKEN_COUNT} tokens` : `${matchCount} of ${TOTAL_TOKEN_COUNT}`;
return (
}
size="small"
value={filter}
/>
{countText}
{groups.length === 0 ? (
setFilter('')} query={filter.trim()} />
) : (
groups.map((group) => )
)}
);
}
function matchGroups(query: string): ReadonlyArray {
if (query === '') return tokenPurposeGroups;
return tokenPurposeGroups.flatMap((group) => {
if (group.title.toLowerCase().includes(query)) return [group];
const tokens = group.tokens.filter((token) => {
return (
token.path.toLowerCase().includes(query) || token.variable.toLowerCase().includes(query)
);
});
return tokens.length === 0 ? [] : [{ ...group, tokens }];
});
}
function PurposeDetails({ group }: { group: TokenPurposeGroup }) {
return (
{group.title}
{group.tokens.length}
{group.description}
{group.related ? (
{group.related.label}
) : null}
);
}
function TokenTable({
showSamples,
tokens,
}: {
showSamples: boolean;
tokens: ReadonlyArray;
}) {
return (
{showSamples ? (
|
Sample
|
) : null}
vars path
|
CSS variable
|
{tokens.map((token) => (
{showSamples ? (
|
|
) : null}
{token.path}
|
{token.variable}
|
))}
);
}
function TokenSample({ token }: { token: ThemeToken }) {
const Sample = FAMILY_SAMPLES[token.family];
return ;
}
function SampleFrame({
children,
className,
justifyContent = 'center',
paddingInline,
style,
}: {
children?: ReactNode;
className?: string;
justifyContent?: 'center' | 'flex-start';
paddingInline?: 'sp8';
style?: CSSProperties;
}): JSX.Element {
return (
{children}
);
}
function ColorSample({ variable }: ThemeToken) {
return (
);
}
function DepthSample({ variable }: ThemeToken) {
return (
);
}
function FinishSample({ variable }: ThemeToken) {
return (
);
}
function RadiusSample({ variable }: ThemeToken) {
return (
);
}
function SpaceSample({ variable }: ThemeToken) {
return (
);
}
function SizeSample({ variable }: ThemeToken) {
return (
);
}
function InteractionSample({ variable }: ThemeToken) {
return (
);
}
function MotionSample({ path, variable }: ThemeToken) {
const axis = path.split('.')[1];
return (
);
}
function TextSample({ children = 'Aa', style }: { children?: ReactNode; style: CSSProperties }) {
return (
{children}
);
}
function TrimSample({
property,
variable,
}: {
property: 'marginBlockEnd' | 'marginBlockStart';
variable: string;
}) {
return (
);
}
function FontSample({ path, variable }: ThemeToken) {
const [, section, property] = path.split('.');
if (section === 'family') return ;
if (section === 'weight') return ;
const stepFontSizeVariable = VARIABLE_BY_PATH.get(`font.${section}.fontSize`);
const fontSize = stepFontSizeVariable ? `var(${stepFontSizeVariable})` : undefined;
if (property === 'fontSize') return ;
if (property === 'lineHeight') {
return (
Aa
Aa
);
}
if (property === 'letterSpacing') {
return ;
}
if (property === 'baselineTrim') {
return ;
}
if (property === 'capHeightTrim') {
return ;
}
return ;
}
const FAMILY_SAMPLES: Record ReactNode> = {
actionControlFinish: FinishSample,
color: ColorSample,
controlSize: SizeSample,
depth: DepthSample,
font: FontSample,
iconSize: SizeSample,
interaction: InteractionSample,
motion: MotionSample,
radius: RadiusSample,
space: SpaceSample,
};
function EmptyState({ onClear, query }: { onClear: () => void; query: string }) {
return (
No token matches "{query}"
);
}