@mitcsutt/kiln-ui 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/actions/CopyButton/CopyButton.d.ts +58 -0
- package/dist/components/actions/CopyButton/CopyButton.js +63 -0
- package/dist/components/actions/CopyButton/CopyButton.js.map +1 -0
- package/dist/components/actions/CopyButton/CopyButton.module.js +9 -0
- package/dist/components/actions/CopyButton/CopyButton.module.js.map +1 -0
- package/dist/components/actions/CopyButton/index.d.ts +4 -0
- package/dist/components/actions/CopyButton/useClipboard.d.ts +35 -0
- package/dist/components/actions/CopyButton/useClipboard.js +62 -0
- package/dist/components/actions/CopyButton/useClipboard.js.map +1 -0
- package/dist/components/actions/ToggleChip/ToggleChip.d.ts +6 -0
- package/dist/components/actions/ToggleChip/ToggleChip.js +26 -24
- package/dist/components/actions/ToggleChip/ToggleChip.js.map +1 -1
- package/dist/components/display/Badge/Badge.d.ts +6 -0
- package/dist/components/display/Badge/Badge.js +16 -15
- package/dist/components/display/Badge/Badge.js.map +1 -1
- package/dist/components/display/CodeBlock/CodeBlock.js +45 -58
- package/dist/components/display/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/display/Swatch/Swatch.d.ts +52 -0
- package/dist/components/display/Swatch/Swatch.js +27 -0
- package/dist/components/display/Swatch/Swatch.js.map +1 -0
- package/dist/components/display/Swatch/Swatch.module.js +6 -0
- package/dist/components/display/Swatch/Swatch.module.js.map +1 -0
- package/dist/components/display/Swatch/index.d.ts +2 -0
- package/dist/components/display/Tag/Tag.d.ts +7 -0
- package/dist/components/display/Tag/Tag.js +31 -26
- package/dist/components/display/Tag/Tag.js.map +1 -1
- package/dist/components/display/TreeTable/TreeTable.d.ts +69 -0
- package/dist/components/display/TreeTable/TreeTable.js +152 -0
- package/dist/components/display/TreeTable/TreeTable.js.map +1 -0
- package/dist/components/display/TreeTable/TreeTable.module.js +12 -0
- package/dist/components/display/TreeTable/TreeTable.module.js.map +1 -0
- package/dist/components/display/TreeTable/index.d.ts +2 -0
- package/dist/components/feedback/Indicator/Indicator.d.ts +74 -0
- package/dist/components/feedback/Indicator/Indicator.js +50 -0
- package/dist/components/feedback/Indicator/Indicator.js.map +1 -0
- package/dist/components/feedback/Indicator/Indicator.module.js +12 -0
- package/dist/components/feedback/Indicator/Indicator.module.js.map +1 -0
- package/dist/components/feedback/Indicator/index.d.ts +2 -0
- package/dist/components/feedback/Toaster/Toaster.d.ts +71 -0
- package/dist/components/feedback/Toaster/Toaster.js +121 -0
- package/dist/components/feedback/Toaster/Toaster.js.map +1 -0
- package/dist/components/feedback/Toaster/Toaster.module.js +20 -0
- package/dist/components/feedback/Toaster/Toaster.module.js.map +1 -0
- package/dist/components/feedback/Toaster/index.d.ts +4 -0
- package/dist/components/feedback/Toaster/toast.d.ts +96 -0
- package/dist/components/feedback/Toaster/toast.js +131 -0
- package/dist/components/feedback/Toaster/toast.js.map +1 -0
- package/dist/components/inputs/Calendar/Calendar.d.ts +87 -0
- package/dist/components/inputs/Calendar/Calendar.js +217 -0
- package/dist/components/inputs/Calendar/Calendar.js.map +1 -0
- package/dist/components/inputs/Calendar/Calendar.module.js +18 -0
- package/dist/components/inputs/Calendar/Calendar.module.js.map +1 -0
- package/dist/components/inputs/Calendar/index.d.ts +2 -0
- package/dist/components/inputs/Combobox/Combobox.js +36 -44
- package/dist/components/inputs/Combobox/Combobox.js.map +1 -1
- package/dist/components/inputs/DateField/DateField.d.ts +17 -0
- package/dist/components/inputs/DateField/DateField.js +26 -0
- package/dist/components/inputs/DateField/DateField.js.map +1 -0
- package/dist/components/inputs/DateField/index.d.ts +2 -0
- package/dist/components/inputs/DateInput/DateInput.d.ts +75 -0
- package/dist/components/inputs/DateInput/DateInput.js +139 -0
- package/dist/components/inputs/DateInput/DateInput.js.map +1 -0
- package/dist/components/inputs/DateInput/index.d.ts +3 -0
- package/dist/components/inputs/DateRangeField/DateRangeField.d.ts +18 -3
- package/dist/components/inputs/DateRangeField/DateRangeField.js.map +1 -1
- package/dist/components/inputs/DateRangeField/DateRangeField.module.js +8 -3
- package/dist/components/inputs/DateRangeField/DateRangeField.module.js.map +1 -1
- package/dist/components/inputs/DateRangeField/DateRangePickerField.d.ts +48 -0
- package/dist/components/inputs/DateRangeField/DateRangePickerField.js +246 -0
- package/dist/components/inputs/DateRangeField/DateRangePickerField.js.map +1 -0
- package/dist/components/inputs/DateRangeField/index.d.ts +2 -0
- package/dist/components/inputs/DateTimeField/DateTimeField.d.ts +17 -0
- package/dist/components/inputs/DateTimeField/DateTimeField.js +26 -0
- package/dist/components/inputs/DateTimeField/DateTimeField.js.map +1 -0
- package/dist/components/inputs/DateTimeField/index.d.ts +2 -0
- package/dist/components/inputs/DateTimeInput/DateTimeInput.d.ts +83 -0
- package/dist/components/inputs/DateTimeInput/DateTimeInput.js +216 -0
- package/dist/components/inputs/DateTimeInput/DateTimeInput.js.map +1 -0
- package/dist/components/inputs/DateTimeInput/index.d.ts +2 -0
- package/dist/components/inputs/JsonField/JsonField.d.ts +23 -0
- package/dist/components/inputs/JsonField/JsonField.js +33 -0
- package/dist/components/inputs/JsonField/JsonField.js.map +1 -0
- package/dist/components/inputs/JsonField/index.d.ts +2 -0
- package/dist/components/inputs/JsonInput/JsonInput.d.ts +119 -0
- package/dist/components/inputs/JsonInput/JsonInput.js +282 -0
- package/dist/components/inputs/JsonInput/JsonInput.js.map +1 -0
- package/dist/components/inputs/JsonInput/JsonInput.module.js +16 -0
- package/dist/components/inputs/JsonInput/JsonInput.module.js.map +1 -0
- package/dist/components/inputs/JsonInput/index.d.ts +4 -0
- package/dist/components/inputs/JsonInput/json.d.ts +59 -0
- package/dist/components/inputs/JsonInput/json.js +231 -0
- package/dist/components/inputs/JsonInput/json.js.map +1 -0
- package/dist/components/inputs/TagsInput/TagsInput.js +5 -5
- package/dist/components/inputs/TimeField/TimeField.d.ts +17 -0
- package/dist/components/inputs/TimeField/TimeField.js +26 -0
- package/dist/components/inputs/TimeField/TimeField.js.map +1 -0
- package/dist/components/inputs/TimeField/index.d.ts +2 -0
- package/dist/components/inputs/TimeInput/TimeInput.d.ts +54 -0
- package/dist/components/inputs/TimeInput/TimeInput.js +107 -0
- package/dist/components/inputs/TimeInput/TimeInput.js.map +1 -0
- package/dist/components/inputs/TimeInput/index.d.ts +2 -0
- package/dist/components/inputs/internal/DateSegments.d.ts +80 -0
- package/dist/components/inputs/internal/DateSegments.js +345 -0
- package/dist/components/inputs/internal/DateSegments.js.map +1 -0
- package/dist/components/inputs/internal/dates.d.ts +101 -0
- package/dist/components/inputs/internal/dates.js +262 -0
- package/dist/components/inputs/internal/dates.js.map +1 -0
- package/dist/components/inputs/internal/filterOptions.d.ts +22 -1
- package/dist/components/inputs/internal/filterOptions.js +32 -1
- package/dist/components/inputs/internal/filterOptions.js.map +1 -1
- package/dist/components/inputs/internal/picker.d.ts +31 -0
- package/dist/components/inputs/internal/picker.js +29 -0
- package/dist/components/inputs/internal/picker.js.map +1 -0
- package/dist/components/inputs/internal/picker.module.js +15 -0
- package/dist/components/inputs/internal/picker.module.js.map +1 -0
- package/dist/components/inputs/internal/segments.module.js +10 -0
- package/dist/components/inputs/internal/segments.module.js.map +1 -0
- package/dist/components/layout/Collapse/Collapse.d.ts +39 -0
- package/dist/components/layout/Collapse/Collapse.js +24 -0
- package/dist/components/layout/Collapse/Collapse.js.map +1 -0
- package/dist/components/layout/Collapse/Collapse.module.js +6 -0
- package/dist/components/layout/Collapse/Collapse.module.js.map +1 -0
- package/dist/components/layout/Collapse/index.d.ts +2 -0
- package/dist/components/layout/Collapse/useHeightMotion.d.ts +16 -0
- package/dist/components/layout/Collapse/useHeightMotion.js +60 -0
- package/dist/components/layout/Collapse/useHeightMotion.js.map +1 -0
- package/dist/components/layout/ShowMore/ShowMore.d.ts +59 -0
- package/dist/components/layout/ShowMore/ShowMore.js +114 -0
- package/dist/components/layout/ShowMore/ShowMore.js.map +1 -0
- package/dist/components/layout/ShowMore/ShowMore.module.js +12 -0
- package/dist/components/layout/ShowMore/ShowMore.module.js.map +1 -0
- package/dist/components/layout/ShowMore/index.d.ts +2 -0
- package/dist/components/layout/Transition/Transition.d.ts +63 -0
- package/dist/components/layout/Transition/Transition.js +63 -0
- package/dist/components/layout/Transition/Transition.js.map +1 -0
- package/dist/components/layout/Transition/Transition.module.js +17 -0
- package/dist/components/layout/Transition/Transition.module.js.map +1 -0
- package/dist/components/layout/Transition/index.d.ts +2 -0
- package/dist/components/navigation/Collapsible/Collapsible.d.ts +49 -0
- package/dist/components/navigation/Collapsible/Collapsible.js +83 -0
- package/dist/components/navigation/Collapsible/Collapsible.js.map +1 -0
- package/dist/components/navigation/Collapsible/Collapsible.module.js +11 -0
- package/dist/components/navigation/Collapsible/Collapsible.module.js.map +1 -0
- package/dist/components/navigation/Collapsible/index.d.ts +2 -0
- package/dist/components/navigation/NavLinks/NavLinks.d.ts +32 -4
- package/dist/components/navigation/NavLinks/NavLinks.js +69 -39
- package/dist/components/navigation/NavLinks/NavLinks.js.map +1 -1
- package/dist/components/navigation/NavLinks/NavLinks.module.js +13 -3
- package/dist/components/navigation/NavLinks/NavLinks.module.js.map +1 -1
- package/dist/components/navigation/NavLinks/NavLinksGroup.d.ts +49 -0
- package/dist/components/navigation/NavLinks/NavLinksGroup.js +185 -0
- package/dist/components/navigation/NavLinks/NavLinksGroup.js.map +1 -0
- package/dist/components/navigation/NavLinks/context.d.ts +28 -0
- package/dist/components/navigation/NavLinks/context.js +13 -0
- package/dist/components/navigation/NavLinks/context.js.map +1 -0
- package/dist/components/navigation/NavLinks/index.d.ts +2 -0
- package/dist/components/navigation/NavLinks/treeKeys.d.ts +8 -0
- package/dist/components/navigation/NavLinks/treeKeys.js +57 -0
- package/dist/components/navigation/NavLinks/treeKeys.js.map +1 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.d.ts +120 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.js +386 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.js.map +1 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.module.js +31 -0
- package/dist/components/overlays/CommandPalette/CommandPalette.module.js.map +1 -0
- package/dist/components/overlays/CommandPalette/index.d.ts +2 -0
- package/dist/components/overlays/CommandPalette/shortcut.d.ts +28 -0
- package/dist/components/overlays/CommandPalette/shortcut.js +104 -0
- package/dist/components/overlays/CommandPalette/shortcut.js.map +1 -0
- package/dist/index.d.ts +42 -4
- package/dist/index.js +124 -100
- package/dist/styles.css +1 -1
- package/dist/themes/fiesta.css +1 -1
- package/dist/themes/flightdeck.css +1 -1
- package/dist/themes/ledger.css +1 -1
- package/dist/themes/monograph.css +1 -1
- package/dist/themes/riso.css +1 -1
- package/package.json +2 -1
- package/skills/custom-theme/SKILL.md +32 -0
- package/skills/custom-theme/references/colour.md +20 -0
- package/skills/custom-theme/references/motion.md +9 -0
- package/skills/custom-theme/references/tokens.md +35 -0
- package/skills/design-rules/SKILL.md +5 -2
- package/skills/setup-and-theming/SKILL.md +1 -1
- package/skills/setup-and-theming/references/paper.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.js","names":["styles","CodeBlock_module_default","CodeBlock","forwardRef","rest","copy","setCopy","useState","timer","useRef","captionId","useId","source","trimTrailingNewlines","code","lines","lineTokens","tokens","line","token","i","highlighted","highlightLines","hasHeader","title","language","copyable","onCopy","cx","className","wrap","showLineNumbers","Button","jsx","CheckIcon","CopyIcon","copyLabel","j"],"sources":["../../../../src/components/display/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { CheckIcon, CopyIcon } from '#icons'\nimport { Button } from '#components/actions/Button'\nimport { cx } from '#utils/cx'\nimport { trimTrailingNewlines } from '#utils/newlines'\nimport styles from './CodeBlock.module.css'\n\n/**\n * What a piece of highlighted source is. Each kind is coloured from the theme's own tokens, so\n * highlighted code follows the theme and the colour mode.\n */\nexport type CodeTokenType =\n | 'keyword'\n | 'string'\n | 'comment'\n | 'constant'\n | 'function'\n | 'type'\n | 'tag'\n | 'attribute'\n | 'punctuation'\n\n/** A run of source text and its kind. A token with no `type` is plain text. */\nexport interface CodeToken {\n content: string\n type?: CodeTokenType\n}\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n /** The source, verbatim. Trailing newlines are trimmed. */\n code: string\n /** Shown as a label (\"TypeScript\", \"bash\"). It doesn't turn on highlighting: `tokens` does. */\n language?: string\n /**\n * Syntax highlighting for `code`: one array of tokens per line, as `highlight` from\n * `@mitcsutt/kiln-ui/highlight` returns them. Without it, or when the tokens don't spell out\n * `code` line for line (say, tokens from a previous `code`), the code is plain.\n */\n tokens?: CodeToken[][]\n /** Filename or caption shown in the header: `src/queries/keys.ts`. */\n title?: ReactNode\n /** Number the lines in a gutter (not copied with a selection). */\n showLineNumbers?: boolean\n /** 1-based line numbers to mark with the theme's highlight colour. */\n highlightLines?: number[]\n /** Show a copy button. Default `true`. */\n copyable?: boolean\n /** Wrap long lines instead of scrolling sideways. */\n wrap?: boolean\n /** Accessible name of the copy button. Default \"Copy code\". */\n copyLabel?: string\n}\n\ntype CopyState = 'idle' | 'copied' | 'failed'\n\n/**\n * A block of source with an optional filename bar, line numbers, highlighted lines and a copy\n * button.\n *\n * @remarks\n * `CodeBlock` shows code: a snippet in an article, a command, a config example. It's a plain\n * surface with an optional title bar, never fake window chrome. It's plain unless you pass\n * `tokens`: `highlight` from `@mitcsutt/kiln-ui/highlight` turns JavaScript, JSX, TypeScript and\n * TSX into tokens, coloured from the theme. Every code sample in these docs is a `CodeBlock`.\n *\n * ## Highlighting\n *\n * `highlight(code, language)` resolves to the tokens for `code`, or to `undefined` for a language\n * other than `js`, `jsx`, `ts` or `tsx` (or `javascript` and `typescript`), which `CodeBlock`\n * shows as plain code. It runs [Shiki](https://shiki.style), an optional peer dependency, so\n * install `shiki` to use it. The main `@mitcsutt/kiln-ui` entry never loads it.\n *\n * Keywords and tags take the accent, strings, constants, functions and types take the theme's\n * tones, and comments and punctuation are muted. A custom theme needs no code palette of its own.\n *\n * The tokens are plain data, so highlight wherever the code is ready. In a server component or\n * at build time, `await` it and pass the tokens to the client component that renders the\n * `CodeBlock`, and the browser downloads no highlighter. In the browser, call it in an effect, as\n * in the example above: Shiki and the grammar load on the first call.\n *\n * ```tsx\n * import { highlight } from '@mitcsutt/kiln-ui/highlight'\n *\n * export async function Snippet({ source }: { source: string }) {\n * const tokens = await highlight(source, 'tsx')\n * return <SnippetBlock code={source} tokens={tokens} />\n * }\n * ```\n *\n * @privateRemarks\n * A block of source: a snippet in a blog post, a command in a README, a config example.\n * A plain surface with an optional filename bar — no fake window chrome.\n *\n * <CodeBlock title=\"queries/keys.ts\" language=\"TypeScript\" code={source} highlightLines={[3, 4]} />\n */\nexport const CodeBlock = forwardRef<HTMLElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language,\n tokens,\n title,\n showLineNumbers = false,\n highlightLines,\n copyable = true,\n wrap = false,\n copyLabel = 'Copy code',\n className,\n ...rest\n },\n ref,\n) {\n const [copy, setCopy] = useState<CopyState>('idle')\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const captionId = useId()\n const source = trimTrailingNewlines(code)\n const lines = source.split('\\n')\n const lineTokens =\n tokens?.length === lines.length &&\n tokens.every((line, i) => line.map((token) => token.content).join('') === lines[i])\n ? tokens\n : undefined\n const highlighted = new Set(highlightLines)\n const hasHeader = Boolean(title) || Boolean(language) || copyable\n\n useEffect(\n () => () => {\n clearTimeout(timer.current)\n },\n [],\n )\n\n const onCopy = async () => {\n clearTimeout(timer.current)\n try {\n await navigator.clipboard.writeText(source)\n setCopy('copied')\n } catch {\n setCopy('failed')\n }\n timer.current = setTimeout(() => {\n setCopy('idle')\n }, 2000)\n }\n\n return (\n <figure\n ref={ref}\n className={cx(styles.codeBlock, className)}\n data-kiln-component=\"\"\n data-wrap={wrap || undefined}\n data-line-numbers={showLineNumbers || undefined}\n aria-labelledby={title ? captionId : undefined}\n {...rest}\n >\n {hasHeader ? (\n <figcaption className={styles.header}>\n {title ? (\n <span id={captionId} className={styles.title}>\n {title}\n </span>\n ) : null}\n {language ? <span className={styles.language}>{language}</span> : null}\n {copyable ? (\n <Button\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n className={styles.copy}\n leadingIcon={copy === 'copied' ? <CheckIcon /> : <CopyIcon />}\n aria-label={copyLabel}\n onClick={() => {\n void onCopy()\n }}\n data-state={copy}\n >\n {copy === 'copied' ? 'Copied' : copy === 'failed' ? 'Copy failed' : 'Copy'}\n </Button>\n ) : null}\n </figcaption>\n ) : null}\n {/* A scrollable region must be reachable by keyboard. */}\n <pre className={styles.pre} tabIndex={0}>\n <code className={styles.code}>\n {lines.map((line, i) => (\n <span\n key={i}\n className={styles.line}\n data-line={showLineNumbers ? i + 1 : undefined}\n data-highlighted={highlighted.has(i + 1) || undefined}\n >\n {lineTokens?.[i]\n ? lineTokens[i].map((token, j) =>\n token.type ? (\n <span key={j} className={styles.token} data-token={token.type}>\n {token.content}\n </span>\n ) : (\n token.content\n ),\n )\n : line}\n {i < lines.length - 1 ? '\\n' : null}\n </span>\n ))}\n </code>\n </pre>\n {copyable ? (\n <span className={styles.visuallyHidden} role=\"status\" aria-live=\"polite\">\n {copy === 'copied' ? 'Copied to clipboard' : copy === 'failed' ? 'Copy failed' : ''}\n </span>\n ) : null}\n </figure>\n )\n})\n"],"mappings":";;;;;;;;AAuGA,IAAaE,IAAYC,EAAwC,SAC/D,EACE,SACA,aACA,WACA,UACA,qBAAkB,IAClB,mBACA,cAAW,IACX,UAAO,IACP,eAAY,aACZ,cACA,GAAGC,KAEL,GACA;CACA,IAAM,CAACC,GAAMC,KAAWC,EAAoB,MAAM,GAC5CC,IAAQC,EAAkD,KAAA,CAAS,GACnEC,IAAYC,EAAM,GAClBC,IAASC,EAAqBC,CAAI,GAClCC,IAAQH,EAAO,MAAM,IAAI,GACzBI,IACJC,GAAQ,WAAWF,EAAM,UACzBE,EAAO,OAAO,GAAM,MAAMC,EAAK,KAAK,MAAUC,EAAM,OAAO,CAAC,CAAC,KAAK,EAAE,MAAMJ,EAAMK,EAAE,IAC9EH,IACA,KAAA,GACAI,IAAc,IAAI,IAAIC,CAAc,GACpCC,IAAY,EAAQC,KAAU,EAAQC,KAAaC;CAEzD,cACc;EACV,aAAalB,EAAM,OAAO;CAC5B,GACA,CAAC,CACH;CAEA,IAAMmB,IAAS,YAAY;EACzB,aAAanB,EAAM,OAAO;EAC1B,IAAI;GAEF,AADA,MAAM,UAAU,UAAU,UAAUI,CAAM,GAC1CN,EAAQ,QAAQ;EAClB,QAAQ;GACN,EAAQ,QAAQ;EAClB;EACA,EAAM,UAAU,iBAAiB;GAC/B,EAAQ,MAAM;EAChB,GAAG,GAAI;CACT;CAEA,OACE,kBAAC,UAAD;EACO;EACL,WAAWsB,EAAG5B,EAAO,WAAW6B,CAAS;EACzC,uBAAoB;EACpB,aAAWC,KAAQ,KAAA;EACnB,qBAAmBC,KAAmB,KAAA;EACtC,mBAAiBP,IAAQd,IAAY,KAAA;EACrC,GAAIN;EAPN,UAAA;GASGmB,IACC,kBAAC,cAAD;IAAY,WAAWvB,EAAO;IAA9B,UAAA;KACGwB,IACC,kBAAC,QAAD;MAAM,IAAId;MAAW,WAAWV,EAAO;MACpC,UAAAwB;KACG,CAAA,IACJ;KACHC,IAAW,kBAAC,QAAD;MAAM,WAAWzB,EAAO;MAAW,UAAAyB;KAAe,CAAA,IAAI;KACjEC,IACC,kBAACM,GAAD;MACE,SAAQ;MACR,MAAK;MACL,MAAK;MACL,WAAWhC,EAAO;MAClB,aAAiCiC,EAApB5B,MAAS,WAAY6B,IAAgBC,GAAjB,CAAY,CAAe;MAC5D,cAAYC;MACZ,eAAe;OACb,EAAY;MACd;MACA,cAAY/B;MAEX,UAAAA,MAAS,WAAW,WAAWA,MAAS,WAAW,gBAAgB;KAC9D,CAAA,IACN;IACM;GACV,CAAA,IAAA;GAEJ,kBAAC,OAAD;IAAK,WAAWL,EAAO;IAAK,UAAU;IACpC,UAAA,kBAAC,QAAD;KAAM,WAAWA,EAAO;KACrB,UAAAe,EAAM,KAAK,GAAM,MAChB,kBAAC,QAAD;MAEE,WAAWf,EAAO;MAClB,aAAW+B,IAAkBX,IAAI,IAAI,KAAA;MACrC,oBAAkBC,EAAY,IAAID,IAAI,CAAC,KAAK,KAAA;MAJ9C,UAAA,CAMGJ,IAAaI,KACVJ,EAAWI,EAAE,CAAC,KAAK,GAAO,MACxBD,EAAM,OACJ,kBAAC,QAAD;OAAc,WAAWnB,EAAO;OAAO,cAAYmB,EAAM;OACtD,UAAAA,EAAM;MACH,GAFKkB,CAEL,IAENlB,EAAM,OAEV,IACAD,GACHE,IAAIL,EAAM,SAAS,IAAI,OAAO,IAC3B;KAjBC,GAAAK,CAiBD,CACP;IACG,CAAA;GACH,CAAA;GACJM,IACC,kBAAC,QAAD;IAAM,WAAW1B,EAAO;IAAgB,MAAK;IAAS,aAAU;IAC7D,UAAAK,MAAS,WAAW,wBAAwBA,MAAS,WAAW,gBAAgB;GAC7E,CAAA,IACJ;EACE;;AAEZ,CAAC"}
|
|
1
|
+
{"version":3,"file":"CodeBlock.js","names":["styles","CodeBlock_module_default","CodeBlock","forwardRef","rest","clipboard","useClipboard","copy","captionId","useId","source","trimTrailingNewlines","code","lines","lineTokens","tokens","line","token","i","highlighted","highlightLines","hasHeader","title","language","copyable","cx","className","wrap","showLineNumbers","Button","jsx","CheckIcon","CopyIcon","copyLabel","j"],"sources":["../../../../src/components/display/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import { forwardRef, useId, type HTMLAttributes, type ReactNode } from 'react'\nimport { CheckIcon, CopyIcon } from '#icons'\nimport { Button } from '#components/actions/Button'\nimport { useClipboard } from '#components/actions/CopyButton'\nimport { cx } from '#utils/cx'\nimport { trimTrailingNewlines } from '#utils/newlines'\nimport styles from './CodeBlock.module.css'\n\n/**\n * What a piece of highlighted source is. Each kind is coloured from the theme's own tokens, so\n * highlighted code follows the theme and the colour mode.\n */\nexport type CodeTokenType =\n | 'keyword'\n | 'string'\n | 'comment'\n | 'constant'\n | 'function'\n | 'type'\n | 'tag'\n | 'attribute'\n | 'punctuation'\n\n/** A run of source text and its kind. A token with no `type` is plain text. */\nexport interface CodeToken {\n content: string\n type?: CodeTokenType\n}\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {\n /** The source, verbatim. Trailing newlines are trimmed. */\n code: string\n /** Shown as a label (\"TypeScript\", \"bash\"). It doesn't turn on highlighting: `tokens` does. */\n language?: string\n /**\n * Syntax highlighting for `code`: one array of tokens per line, as `highlight` from\n * `@mitcsutt/kiln-ui/highlight` returns them. Without it, or when the tokens don't spell out\n * `code` line for line (say, tokens from a previous `code`), the code is plain.\n */\n tokens?: CodeToken[][]\n /** Filename or caption shown in the header: `src/queries/keys.ts`. */\n title?: ReactNode\n /** Number the lines in a gutter (not copied with a selection). */\n showLineNumbers?: boolean\n /** 1-based line numbers to mark with the theme's highlight colour. */\n highlightLines?: number[]\n /** Show a copy button. Default `true`. */\n copyable?: boolean\n /** Wrap long lines instead of scrolling sideways. */\n wrap?: boolean\n /** Accessible name of the copy button. Default \"Copy code\". */\n copyLabel?: string\n}\n\n/**\n * A block of source with an optional filename bar, line numbers, highlighted lines and a copy\n * button.\n *\n * @remarks\n * `CodeBlock` shows code: a snippet in an article, a command, a config example. It's a plain\n * surface with an optional title bar, never fake window chrome. It's plain unless you pass\n * `tokens`: `highlight` from `@mitcsutt/kiln-ui/highlight` turns JavaScript, JSX, TypeScript and\n * TSX into tokens, coloured from the theme. Every code sample in these docs is a `CodeBlock`.\n *\n * ## Highlighting\n *\n * `highlight(code, language)` resolves to the tokens for `code`, or to `undefined` for a language\n * other than `js`, `jsx`, `ts` or `tsx` (or `javascript` and `typescript`), which `CodeBlock`\n * shows as plain code. It runs [Shiki](https://shiki.style), an optional peer dependency, so\n * install `shiki` to use it. The main `@mitcsutt/kiln-ui` entry never loads it.\n *\n * Keywords and tags take the accent, strings, constants, functions and types take the theme's\n * tones, and comments and punctuation are muted. A custom theme needs no code palette of its own.\n *\n * The tokens are plain data, so highlight wherever the code is ready. In a server component or\n * at build time, `await` it and pass the tokens to the client component that renders the\n * `CodeBlock`, and the browser downloads no highlighter. In the browser, call it in an effect, as\n * in the example above: Shiki and the grammar load on the first call.\n *\n * ```tsx\n * import { highlight } from '@mitcsutt/kiln-ui/highlight'\n *\n * export async function Snippet({ source }: { source: string }) {\n * const tokens = await highlight(source, 'tsx')\n * return <SnippetBlock code={source} tokens={tokens} />\n * }\n * ```\n *\n * @privateRemarks\n * A block of source: a snippet in a blog post, a command in a README, a config example.\n * A plain surface with an optional filename bar — no fake window chrome.\n *\n * <CodeBlock title=\"queries/keys.ts\" language=\"TypeScript\" code={source} highlightLines={[3, 4]} />\n */\nexport const CodeBlock = forwardRef<HTMLElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language,\n tokens,\n title,\n showLineNumbers = false,\n highlightLines,\n copyable = true,\n wrap = false,\n copyLabel = 'Copy code',\n className,\n ...rest\n },\n ref,\n) {\n const clipboard = useClipboard({ timeout: 2000 })\n const copy = clipboard.copied ? 'copied' : clipboard.error ? 'failed' : 'idle'\n const captionId = useId()\n const source = trimTrailingNewlines(code)\n const lines = source.split('\\n')\n const lineTokens =\n tokens?.length === lines.length &&\n tokens.every((line, i) => line.map((token) => token.content).join('') === lines[i])\n ? tokens\n : undefined\n const highlighted = new Set(highlightLines)\n const hasHeader = Boolean(title) || Boolean(language) || copyable\n\n return (\n <figure\n ref={ref}\n className={cx(styles.codeBlock, className)}\n data-kiln-component=\"\"\n data-wrap={wrap || undefined}\n data-line-numbers={showLineNumbers || undefined}\n aria-labelledby={title ? captionId : undefined}\n {...rest}\n >\n {hasHeader ? (\n <figcaption className={styles.header}>\n {title ? (\n <span id={captionId} className={styles.title}>\n {title}\n </span>\n ) : null}\n {language ? <span className={styles.language}>{language}</span> : null}\n {copyable ? (\n <Button\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n className={styles.copy}\n leadingIcon={copy === 'copied' ? <CheckIcon /> : <CopyIcon />}\n aria-label={copyLabel}\n onClick={() => {\n void clipboard.copy(source)\n }}\n data-state={copy}\n >\n {copy === 'copied' ? 'Copied' : copy === 'failed' ? 'Copy failed' : 'Copy'}\n </Button>\n ) : null}\n </figcaption>\n ) : null}\n {/* A scrollable region must be reachable by keyboard. */}\n <pre className={styles.pre} tabIndex={0}>\n <code className={styles.code}>\n {lines.map((line, i) => (\n <span\n key={i}\n className={styles.line}\n data-line={showLineNumbers ? i + 1 : undefined}\n data-highlighted={highlighted.has(i + 1) || undefined}\n >\n {lineTokens?.[i]\n ? lineTokens[i].map((token, j) =>\n token.type ? (\n <span key={j} className={styles.token} data-token={token.type}>\n {token.content}\n </span>\n ) : (\n token.content\n ),\n )\n : line}\n {i < lines.length - 1 ? '\\n' : null}\n </span>\n ))}\n </code>\n </pre>\n {copyable ? (\n <span className={styles.visuallyHidden} role=\"status\" aria-live=\"polite\">\n {copy === 'copied' ? 'Copied to clipboard' : copy === 'failed' ? 'Copy failed' : ''}\n </span>\n ) : null}\n </figure>\n )\n})\n"],"mappings":";;;;;;;;;AA8FA,IAAaE,IAAYC,EAAwC,SAC/D,EACE,SACA,aACA,WACA,UACA,qBAAkB,IAClB,mBACA,cAAW,IACX,UAAO,IACP,eAAY,aACZ,cACA,GAAGC,KAEL,GACA;CACA,IAAMC,IAAYC,EAAa,EAAE,SAAS,IAAK,CAAC,GAC1CC,IAAOF,EAAU,SAAS,WAAWA,EAAU,QAAQ,WAAW,QAClEG,IAAYC,EAAM,GAClBC,IAASC,EAAqBC,CAAI,GAClCC,IAAQH,EAAO,MAAM,IAAI,GACzBI,IACJC,GAAQ,WAAWF,EAAM,UACzBE,EAAO,OAAO,GAAM,MAAMC,EAAK,KAAK,MAAUC,EAAM,OAAO,CAAC,CAAC,KAAK,EAAE,MAAMJ,EAAMK,EAAE,IAC9EH,IACA,KAAA,GACAI,IAAc,IAAI,IAAIC,CAAc,GACpCC,IAAY,EAAQC,KAAU,EAAQC,KAAaC;CAEzD,OACE,kBAAC,UAAD;EACO;EACL,WAAWC,EAAGzB,EAAO,WAAW0B,CAAS;EACzC,uBAAoB;EACpB,aAAWC,KAAQ,KAAA;EACnB,qBAAmBC,KAAmB,KAAA;EACtC,mBAAiBN,IAAQd,IAAY,KAAA;EACrC,GAAIJ;EAPN,UAAA;GASGiB,IACC,kBAAC,cAAD;IAAY,WAAWrB,EAAO;IAA9B,UAAA;KACGsB,IACC,kBAAC,QAAD;MAAM,IAAId;MAAW,WAAWR,EAAO;MACpC,UAAAsB;KACG,CAAA,IACJ;KACHC,IAAW,kBAAC,QAAD;MAAM,WAAWvB,EAAO;MAAW,UAAAuB;KAAe,CAAA,IAAI;KACjEC,IACC,kBAACK,GAAD;MACE,SAAQ;MACR,MAAK;MACL,MAAK;MACL,WAAW7B,EAAO;MAClB,aAAiC8B,EAApBvB,MAAS,WAAYwB,IAAgBC,GAAjB,CAAY,CAAe;MAC5D,cAAYC;MACZ,eAAe;OACb,EAAe,KAAKvB,CAAM;MAC5B;MACA,cAAYH;MAEX,UAAAA,MAAS,WAAW,WAAWA,MAAS,WAAW,gBAAgB;KAC9D,CAAA,IACN;IACM;GACV,CAAA,IAAA;GAEJ,kBAAC,OAAD;IAAK,WAAWP,EAAO;IAAK,UAAU;IACpC,UAAA,kBAAC,QAAD;KAAM,WAAWA,EAAO;KACrB,UAAAa,EAAM,KAAK,GAAM,MAChB,kBAAC,QAAD;MAEE,WAAWb,EAAO;MAClB,aAAW4B,IAAkBV,IAAI,IAAI,KAAA;MACrC,oBAAkBC,EAAY,IAAID,IAAI,CAAC,KAAK,KAAA;MAJ9C,UAAA,CAMGJ,IAAaI,KACVJ,EAAWI,EAAE,CAAC,KAAK,GAAO,MACxBD,EAAM,OACJ,kBAAC,QAAD;OAAc,WAAWjB,EAAO;OAAO,cAAYiB,EAAM;OACtD,UAAAA,EAAM;MACH,GAFKiB,CAEL,IAENjB,EAAM,OAEV,IACAD,GACHE,IAAIL,EAAM,SAAS,IAAI,OAAO,IAC3B;KAjBC,GAAAK,CAiBD,CACP;IACG,CAAA;GACH,CAAA;GACJM,IACC,kBAAC,QAAD;IAAM,WAAWxB,EAAO;IAAgB,MAAK;IAAS,aAAU;IAC7D,UAAAO,MAAS,WAAW,wBAAwBA,MAAS,WAAW,gBAAgB;GAC7E,CAAA,IACJ;EACE;;AAEZ,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
|
+
import type { CategoryColor } from '../../../utils/tokens.js';
|
|
3
|
+
/**
|
|
4
|
+
* A colour a swatch can show: a categorical slot (`1` to `8`, themed in light and dark), or any
|
|
5
|
+
* CSS colour string for a colour that comes from data, such as one a user picked.
|
|
6
|
+
*/
|
|
7
|
+
export type SwatchColor = CategoryColor | (string & {});
|
|
8
|
+
export type SwatchSize = 'sm' | 'md' | 'lg';
|
|
9
|
+
export interface SwatchProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color' | 'children'> {
|
|
10
|
+
/**
|
|
11
|
+
* The colour. Prefer a categorical slot (`1` to `8`): it follows the theme and the colour mode.
|
|
12
|
+
* A CSS colour string (`#7c3aed`, `oklch(…)`) is for colours that are data, and renders as given.
|
|
13
|
+
*/
|
|
14
|
+
color: SwatchColor;
|
|
15
|
+
/**
|
|
16
|
+
* What the colour stands for ("Platform team"), when nothing next to the swatch says it. It
|
|
17
|
+
* names the swatch for screen readers. Without it the swatch is decorative and hidden from them.
|
|
18
|
+
*/
|
|
19
|
+
label?: string;
|
|
20
|
+
/** Relative to the surrounding text: `sm`, `md` (default) or `lg`. */
|
|
21
|
+
size?: SwatchSize;
|
|
22
|
+
}
|
|
23
|
+
/** Whether `color` is a categorical slot rather than a CSS colour string. */
|
|
24
|
+
export declare function isCategoryColor(color: SwatchColor): color is CategoryColor;
|
|
25
|
+
/**
|
|
26
|
+
* A small mark in a colour that says who or what something is: a team, a label, a chart series.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* Most of the time you don't need `Swatch` itself: {@link Badge | Badge}, {@link Tag | Tag},
|
|
30
|
+
* {@link ToggleChip | ToggleChip} and {@link Indicator | Indicator} take a `swatch` prop that draws
|
|
31
|
+
* this mark for you, sized to their text. Use `Swatch` on its own where there's no host, such as a
|
|
32
|
+
* table cell or a legend you lay out yourself.
|
|
33
|
+
*
|
|
34
|
+
* A swatch takes the theme's chip radius, so it's round where chips are pills and square where
|
|
35
|
+
* they're square, and a hairline keeps a pale colour visible on any surface. It's sized in `em`,
|
|
36
|
+
* so it follows the text beside it.
|
|
37
|
+
*
|
|
38
|
+
* ## Categorical colour marks who, never how
|
|
39
|
+
*
|
|
40
|
+
* A swatch is identity: the same team, label or series in the same colour everywhere it
|
|
41
|
+
* appears. It's never status. For a state ("Overdue", "Live"), use a tone on a `Badge` or a
|
|
42
|
+
* `StatusDot`. Prefer the categorical slots `1` to `8`, which every theme sets for light and dark.
|
|
43
|
+
* Pass a CSS colour only when the colour is data, and give it a `label` or put its name beside
|
|
44
|
+
* it, because colour alone is not information.
|
|
45
|
+
*
|
|
46
|
+
* @privateRemarks
|
|
47
|
+
* The shared part behind every host's `swatch` prop (Badge, Tag, ToggleChip, Indicator), and the
|
|
48
|
+
* legend mark the charts package reuses, so a series looks the same in a chart, its legend and a
|
|
49
|
+
* filter chip. Category slots are `data-color`; a string colour is written inline as `--_swatch`,
|
|
50
|
+
* the same way ColorInput renders a raw colour.
|
|
51
|
+
*/
|
|
52
|
+
export declare const Swatch: import("react").ForwardRefExoticComponent<SwatchProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cx as e } from "../../../utils/cx.js";
|
|
2
|
+
import { mergeStyles as t } from "../../../utils/responsive.js";
|
|
3
|
+
import n from "./Swatch.module.js";
|
|
4
|
+
import { forwardRef as r } from "react";
|
|
5
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/display/Swatch/Swatch.tsx
|
|
7
|
+
function a(e) {
|
|
8
|
+
return typeof e == "number";
|
|
9
|
+
}
|
|
10
|
+
var o = r(function({ color: r, label: o, size: s = "md", className: c, style: l, ...u }, d) {
|
|
11
|
+
let f = a(r);
|
|
12
|
+
return /* @__PURE__ */ i("span", {
|
|
13
|
+
ref: d,
|
|
14
|
+
className: e(n.swatch, c),
|
|
15
|
+
"data-color": f ? r : void 0,
|
|
16
|
+
"data-size": s,
|
|
17
|
+
style: t(f ? void 0 : { "--_swatch": r }, l),
|
|
18
|
+
role: o ? "img" : void 0,
|
|
19
|
+
"aria-label": o,
|
|
20
|
+
"aria-hidden": !o || void 0,
|
|
21
|
+
...u
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
export { o as Swatch, a as isCategoryColor };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=Swatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Swatch.js","names":["styles","Swatch_module_default","isCategoryColor","color","Swatch","forwardRef","rest","category","cx","className","size","mergeStyles","style","label"],"sources":["../../../../src/components/display/Swatch/Swatch.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { CategoryColor } from '#utils/tokens'\nimport styles from './Swatch.module.css'\n\n/**\n * A colour a swatch can show: a categorical slot (`1` to `8`, themed in light and dark), or any\n * CSS colour string for a colour that comes from data, such as one a user picked.\n */\nexport type SwatchColor = CategoryColor | (string & {})\n\nexport type SwatchSize = 'sm' | 'md' | 'lg'\n\nexport interface SwatchProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color' | 'children'> {\n /**\n * The colour. Prefer a categorical slot (`1` to `8`): it follows the theme and the colour mode.\n * A CSS colour string (`#7c3aed`, `oklch(…)`) is for colours that are data, and renders as given.\n */\n color: SwatchColor\n /**\n * What the colour stands for (\"Platform team\"), when nothing next to the swatch says it. It\n * names the swatch for screen readers. Without it the swatch is decorative and hidden from them.\n */\n label?: string\n /** Relative to the surrounding text: `sm`, `md` (default) or `lg`. */\n size?: SwatchSize\n}\n\n/** Whether `color` is a categorical slot rather than a CSS colour string. */\nexport function isCategoryColor(color: SwatchColor): color is CategoryColor {\n return typeof color === 'number'\n}\n\n/**\n * A small mark in a colour that says who or what something is: a team, a label, a chart series.\n *\n * @remarks\n * Most of the time you don't need `Swatch` itself: {@link Badge | Badge}, {@link Tag | Tag},\n * {@link ToggleChip | ToggleChip} and {@link Indicator | Indicator} take a `swatch` prop that draws\n * this mark for you, sized to their text. Use `Swatch` on its own where there's no host, such as a\n * table cell or a legend you lay out yourself.\n *\n * A swatch takes the theme's chip radius, so it's round where chips are pills and square where\n * they're square, and a hairline keeps a pale colour visible on any surface. It's sized in `em`,\n * so it follows the text beside it.\n *\n * ## Categorical colour marks who, never how\n *\n * A swatch is identity: the same team, label or series in the same colour everywhere it\n * appears. It's never status. For a state (\"Overdue\", \"Live\"), use a tone on a `Badge` or a\n * `StatusDot`. Prefer the categorical slots `1` to `8`, which every theme sets for light and dark.\n * Pass a CSS colour only when the colour is data, and give it a `label` or put its name beside\n * it, because colour alone is not information.\n *\n * @privateRemarks\n * The shared part behind every host's `swatch` prop (Badge, Tag, ToggleChip, Indicator), and the\n * legend mark the charts package reuses, so a series looks the same in a chart, its legend and a\n * filter chip. Category slots are `data-color`; a string colour is written inline as `--_swatch`,\n * the same way ColorInput renders a raw colour.\n */\nexport const Swatch = forwardRef<HTMLSpanElement, SwatchProps>(function Swatch(\n { color, label, size = 'md', className, style, ...rest },\n ref,\n) {\n const category = isCategoryColor(color)\n return (\n <span\n ref={ref}\n className={cx(styles.swatch, className)}\n data-color={category ? color : undefined}\n data-size={size}\n style={mergeStyles(category ? undefined : ({ '--_swatch': color } as CSSProperties), style)}\n role={label ? 'img' : undefined}\n aria-label={label}\n aria-hidden={label ? undefined : true}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;AA8BA,SAAgBE,EAAgB,GAA4C;CAC1E,OAAO,OAAOC,KAAU;AAC1B;AA6BA,IAAaC,IAASC,EAAyC,SAC7D,EAAE,UAAO,UAAO,UAAO,MAAM,cAAW,UAAO,GAAGC,KAClD,GACA;CACA,IAAMC,IAAWL,EAAgBC,CAAK;CACtC,OACE,kBAAC,QAAD;EACO;EACL,WAAWK,EAAGR,EAAO,QAAQS,CAAS;EACtC,cAAYF,IAAWJ,IAAQ,KAAA;EAC/B,aAAWO;EACX,OAAOC,EAAYJ,IAAW,KAAA,IAAa,EAAE,aAAaJ,EAAM,GAAqBS,CAAK;EAC1F,MAAMC,IAAQ,QAAQ,KAAA;EACtB,cAAYA;EACZ,eAAa,MAAQ,KAAA;EACrB,GAAIP;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Swatch.module.js","names":["swatch","Swatch_module_default"],"sources":["../../../../src/components/display/Swatch/Swatch.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --swatch-radius default var(--radius-chip)\n * --swatch-ring the hairline that keeps a pale colour visible,\n * default color-mix(in oklch, var(--color-ink) 22%, transparent)\n *\n * The fill is a categorical slot (data-color) or a colour from data, written inline as\n * --_swatch. Sized in em, so it follows its host's text.\n */\n.swatch {\n --_swatch: var(--color-ink-subtle);\n --_size: 0.75em;\n\n display: inline-block;\n flex: none;\n inline-size: var(--_size);\n block-size: var(--_size);\n border-radius: var(--swatch-radius, var(--radius-chip));\n background-color: var(--_swatch);\n box-shadow: inset 0 0 0 1px\n var(--swatch-ring, color-mix(in oklch, var(--color-ink) 22%, transparent));\n vertical-align: middle;\n}\n\n.swatch[data-size='sm'] {\n --_size: 0.625em;\n}\n.swatch[data-size='lg'] {\n --_size: 1em;\n}\n\n.swatch[data-color='1'] {\n --_swatch: var(--color-cat-1);\n}\n.swatch[data-color='2'] {\n --_swatch: var(--color-cat-2);\n}\n.swatch[data-color='3'] {\n --_swatch: var(--color-cat-3);\n}\n.swatch[data-color='4'] {\n --_swatch: var(--color-cat-4);\n}\n.swatch[data-color='5'] {\n --_swatch: var(--color-cat-5);\n}\n.swatch[data-color='6'] {\n --_swatch: var(--color-cat-6);\n}\n.swatch[data-color='7'] {\n --_swatch: var(--color-cat-7);\n}\n.swatch[data-color='8'] {\n --_swatch: var(--color-cat-8);\n}\n\n/* Windows high contrast drops backgrounds: keep the mark as an outline. */\n@media (forced-colors: active) {\n .swatch {\n border: 1px solid CanvasText;\n }\n}\n"],"mappings":""}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { type HTMLAttributes, type MouseEvent } from 'react';
|
|
2
|
+
import { type SwatchColor } from '../Swatch/index.js';
|
|
2
3
|
/** Categorical colour slot → `--color-cat-N`. */
|
|
3
4
|
export type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
4
5
|
export type TagSize = 'sm' | 'md';
|
|
5
6
|
export interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {
|
|
6
7
|
/** A categorical colour (category, person, topic). Omit for a neutral tag. */
|
|
7
8
|
color?: TagColor;
|
|
9
|
+
/**
|
|
10
|
+
* A leading colour swatch on a quiet tag, for a colour that belongs to the thing it labels: a
|
|
11
|
+
* label a user coloured, a series in a chart. A categorical slot (`1` to `8`) or a CSS colour.
|
|
12
|
+
* Use `color` instead to fill the whole tag with a categorical colour.
|
|
13
|
+
*/
|
|
14
|
+
swatch?: SwatchColor;
|
|
8
15
|
/** `md` (default), or `sm` for dense lists of many tags. */
|
|
9
16
|
size?: TagSize;
|
|
10
17
|
/** Renders a remove button after the label. Not rendered when `asChild`. */
|
|
@@ -1,37 +1,42 @@
|
|
|
1
1
|
import { cx as e } from "../../../utils/cx.js";
|
|
2
2
|
import { CloseIcon as t } from "../../../icons/icons.js";
|
|
3
|
-
import n from "
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { Swatch as n } from "../Swatch/Swatch.js";
|
|
4
|
+
import r from "./Tag.module.js";
|
|
5
|
+
import { Children as i, forwardRef as a, isValidElement as o } from "react";
|
|
6
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
7
|
+
import { Slot as l } from "radix-ui";
|
|
7
8
|
//#region src/components/display/Tag/Tag.tsx
|
|
8
|
-
var
|
|
9
|
-
let
|
|
10
|
-
return /* @__PURE__ */
|
|
11
|
-
ref:
|
|
12
|
-
className: e(
|
|
13
|
-
"data-color":
|
|
14
|
-
"data-size":
|
|
15
|
-
"data-removable":
|
|
16
|
-
...
|
|
17
|
-
children: [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
var u = a(function({ color: i, swatch: a, size: o = "md", onRemove: u, removeLabel: d, asChild: f = !1, className: p, children: m, ...h }, g) {
|
|
10
|
+
let _ = f ? l.Root : "span", v = !!u && !f, y = d ?? (typeof m == "string" ? `Remove ${m}` : "Remove");
|
|
11
|
+
return /* @__PURE__ */ c(_, {
|
|
12
|
+
ref: g,
|
|
13
|
+
className: e(r.tag, p),
|
|
14
|
+
"data-color": i,
|
|
15
|
+
"data-size": o,
|
|
16
|
+
"data-removable": v || void 0,
|
|
17
|
+
...h,
|
|
18
|
+
children: [
|
|
19
|
+
a === void 0 ? null : /* @__PURE__ */ s(n, { color: a }),
|
|
20
|
+
/* @__PURE__ */ s(l.Slottable, { children: m }),
|
|
21
|
+
v ? /* @__PURE__ */ s("button", {
|
|
22
|
+
type: "button",
|
|
23
|
+
className: r.remove,
|
|
24
|
+
"aria-label": y,
|
|
25
|
+
onClick: u,
|
|
26
|
+
children: /* @__PURE__ */ s(t, {})
|
|
27
|
+
}) : null
|
|
28
|
+
]
|
|
24
29
|
});
|
|
25
|
-
}),
|
|
26
|
-
return /* @__PURE__ */
|
|
30
|
+
}), d = a(function({ className: t, children: n, ...a }, c) {
|
|
31
|
+
return /* @__PURE__ */ s("ul", {
|
|
27
32
|
ref: c,
|
|
28
33
|
role: "list",
|
|
29
|
-
className: e(
|
|
30
|
-
...
|
|
31
|
-
children:
|
|
34
|
+
className: e(r.list, t),
|
|
35
|
+
...a,
|
|
36
|
+
children: i.map(n, (e) => o(e) || typeof e == "string" || typeof e == "number" ? /* @__PURE__ */ s("li", { children: e }, o(e) ? e.key : void 0) : null)
|
|
32
37
|
});
|
|
33
38
|
});
|
|
34
39
|
//#endregion
|
|
35
|
-
export {
|
|
40
|
+
export { u as Tag, d as TagList };
|
|
36
41
|
|
|
37
42
|
//# sourceMappingURL=Tag.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.js","names":["styles","Tag_module_default","Tag","forwardRef","rest","Comp","asChild","Slot","canRemove","onRemove","label","removeLabel","children","cx","className","color","size","CloseIcon","TagList","Children","isValidElement","child"],"sources":["../../../../src/components/display/Tag/Tag.tsx"],"sourcesContent":["import { Children, forwardRef, isValidElement, type HTMLAttributes, type MouseEvent } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { CloseIcon } from '#icons'\nimport styles from './Tag.module.css'\n\n/** Categorical colour slot → `--color-cat-N`. */\nexport type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\nexport type TagSize = 'sm' | 'md'\n\nexport interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {\n /** A categorical colour (category, person, topic). Omit for a neutral tag. */\n color?: TagColor\n /** `md` (default), or `sm` for dense lists of many tags. */\n size?: TagSize\n /** Renders a remove button after the label. Not rendered when `asChild`. */\n onRemove?: (event: MouseEvent<HTMLButtonElement>) => void\n /** Accessible name for the remove button. Default `Remove <label>` for text labels. */\n removeLabel?: string\n /** Render the single child (e.g. a filter link) as the tag. Incompatible with `onRemove`. */\n asChild?: boolean\n}\n\n/**\n * Quiet metadata, like a category, a facility or a group, in a wrapping list that can be edited.\n *\n * @remarks\n * A `Tag` labels a thing with metadata, and a `TagList` lays several out as a real list that\n * wraps. Tags are quieter than {@link Badge | badges}, which are for status.\n *\n * @privateRemarks\n * Quiet metadata: a category, a stack item, a group. Quieter than `Badge`, which is\n * for status. Group several in a `TagList`.\n */\nexport const Tag = forwardRef<HTMLSpanElement, TagProps>(function Tag(\n {
|
|
1
|
+
{"version":3,"file":"Tag.js","names":["styles","Tag_module_default","Tag","forwardRef","rest","Comp","asChild","Slot","canRemove","onRemove","label","removeLabel","children","cx","className","color","size","swatch","Swatch","CloseIcon","TagList","Children","isValidElement","child"],"sources":["../../../../src/components/display/Tag/Tag.tsx"],"sourcesContent":["import { Children, forwardRef, isValidElement, type HTMLAttributes, type MouseEvent } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { CloseIcon } from '#icons'\nimport { Swatch, type SwatchColor } from '#components/display/Swatch'\nimport styles from './Tag.module.css'\n\n/** Categorical colour slot → `--color-cat-N`. */\nexport type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\nexport type TagSize = 'sm' | 'md'\n\nexport interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {\n /** A categorical colour (category, person, topic). Omit for a neutral tag. */\n color?: TagColor\n /**\n * A leading colour swatch on a quiet tag, for a colour that belongs to the thing it labels: a\n * label a user coloured, a series in a chart. A categorical slot (`1` to `8`) or a CSS colour.\n * Use `color` instead to fill the whole tag with a categorical colour.\n */\n swatch?: SwatchColor\n /** `md` (default), or `sm` for dense lists of many tags. */\n size?: TagSize\n /** Renders a remove button after the label. Not rendered when `asChild`. */\n onRemove?: (event: MouseEvent<HTMLButtonElement>) => void\n /** Accessible name for the remove button. Default `Remove <label>` for text labels. */\n removeLabel?: string\n /** Render the single child (e.g. a filter link) as the tag. Incompatible with `onRemove`. */\n asChild?: boolean\n}\n\n/**\n * Quiet metadata, like a category, a facility or a group, in a wrapping list that can be edited.\n *\n * @remarks\n * A `Tag` labels a thing with metadata, and a `TagList` lays several out as a real list that\n * wraps. Tags are quieter than {@link Badge | badges}, which are for status.\n *\n * @privateRemarks\n * Quiet metadata: a category, a stack item, a group. Quieter than `Badge`, which is\n * for status. Group several in a `TagList`.\n */\nexport const Tag = forwardRef<HTMLSpanElement, TagProps>(function Tag(\n {\n color,\n swatch,\n size = 'md',\n onRemove,\n removeLabel,\n asChild = false,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const Comp = asChild ? Slot.Root : 'span'\n const canRemove = Boolean(onRemove) && !asChild\n const label = removeLabel ?? (typeof children === 'string' ? `Remove ${children}` : 'Remove')\n return (\n <Comp\n ref={ref}\n className={cx(styles.tag, className)}\n data-color={color}\n data-size={size}\n data-removable={canRemove || undefined}\n {...rest}\n >\n {swatch !== undefined ? <Swatch color={swatch} /> : null}\n <Slot.Slottable>{children}</Slot.Slottable>\n {canRemove ? (\n <button type=\"button\" className={styles.remove} aria-label={label} onClick={onRemove}>\n <CloseIcon />\n </button>\n ) : null}\n </Comp>\n )\n})\n\nexport type TagListProps = HTMLAttributes<HTMLUListElement>\n\n/** A wrapping list of tags. Each child is wrapped in an `<li>`. */\nexport const TagList = forwardRef<HTMLUListElement, TagListProps>(function TagList(\n { className, children, ...rest },\n ref,\n) {\n return (\n <ul ref={ref} role=\"list\" className={cx(styles.list, className)} {...rest}>\n {Children.map(children, (child) =>\n isValidElement(child) || typeof child === 'string' || typeof child === 'number' ? (\n <li key={isValidElement(child) ? child.key : undefined}>{child}</li>\n ) : null,\n )}\n </ul>\n )\n})\n"],"mappings":";;;;;;;;AAyCA,IAAaE,IAAMC,EAAsC,SACvD,EACE,UACA,WACA,UAAO,MACP,aACA,gBACA,aAAU,IACV,cACA,aACA,GAAGC,KAEL,GACA;CACA,IAAMC,IAAOC,IAAUC,EAAK,OAAO,QAC7BC,IAAY,EAAQC,KAAa,CAACH,GAClCI,IAAQC,MAAgB,OAAOC,KAAa,WAAW,UAAUA,MAAa;CACpF,OACE,kBAACP,GAAD;EACO;EACL,WAAWQ,EAAGb,EAAO,KAAKc,CAAS;EACnC,cAAYC;EACZ,aAAWC;EACX,kBAAgBR,KAAa,KAAA;EAC7B,GAAIJ;EANN,UAAA;GAQGa,MAAW,KAAA,IAAwC,OAA5B,kBAACC,GAAD,EAAQ,OAAOD,EAAS,CAAA;GAChD,kBAACV,EAAK,WAAN,EAAiB,YAAyB,CAAA;GACzCC,IACC,kBAAC,UAAD;IAAQ,MAAK;IAAS,WAAWR,EAAO;IAAQ,cAAYU;IAAO,SAASD;IAC1E,UAAA,kBAACU,GAAD,CAAY,CAAA;GACN,CAAA,IACN;EACA;;AAEV,CAAC,GAKYC,IAAUjB,EAA2C,SAChE,EAAE,cAAW,aAAU,GAAGC,KAC1B,GACA;CACA,OACE,kBAAC,MAAD;EAAS;EAAK,MAAK;EAAO,WAAWS,EAAGb,EAAO,MAAMc,CAAS;EAAG,GAAIV;EAClE,UAAAiB,EAAS,IAAIT,IAAW,MACvBU,EAAeC,CAAK,KAAK,OAAOA,KAAU,YAAY,OAAOA,KAAU,WACrE,kBAAC,MAAD,EAAA,UAAyDA,EAAU,GAA1DD,EAAeC,CAAK,IAAIA,EAAM,MAAM,KAAA,CAAsB,IACjE,IACN;CACE,CAAA;AAER,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode, type RefAttributes } from 'react';
|
|
2
|
+
import { type TableAlign, type TableColumnWidth, type TableProps } from '../Table/index.js';
|
|
3
|
+
import type { VisibilityProps } from '../../../utils/visibility.js';
|
|
4
|
+
/** One column of a {@link TreeTable}. The first column holds the tree: indent, toggle and the row's name. */
|
|
5
|
+
export interface TreeTableColumn extends VisibilityProps {
|
|
6
|
+
/** Stable key, passed back to `renderCell`. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** The header cell's content. */
|
|
9
|
+
header: ReactNode;
|
|
10
|
+
align?: TableAlign;
|
|
11
|
+
/** Numeric column: right-aligned with tabular figures. */
|
|
12
|
+
numeric?: boolean;
|
|
13
|
+
/** Column sizing: `fill` takes the spare width, `min` shrinks to fit. */
|
|
14
|
+
width?: TableColumnWidth;
|
|
15
|
+
}
|
|
16
|
+
export interface TreeTableProps<T> extends Omit<TableProps, 'children' | 'label' | 'role' | 'onChange' | 'defaultValue'> {
|
|
17
|
+
/** Name for the grid, read by assistive technology. */
|
|
18
|
+
label: string;
|
|
19
|
+
/** The columns, in order. The first holds the tree. */
|
|
20
|
+
columns: readonly TreeTableColumn[];
|
|
21
|
+
/** The top-level rows. */
|
|
22
|
+
rows: readonly T[];
|
|
23
|
+
/** A stable, unique id for a row. It is what `expanded` holds and what keeps focus on a row across renders. */
|
|
24
|
+
getRowId: (row: T) => string;
|
|
25
|
+
/** The row's children, or `undefined` when it has none. */
|
|
26
|
+
getChildren: (row: T) => readonly T[] | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the row can be opened. Defaults to `getChildren(row)` having entries. Return `true` for a
|
|
29
|
+
* row whose children load when it opens.
|
|
30
|
+
*/
|
|
31
|
+
hasChildren?: (row: T) => boolean;
|
|
32
|
+
/** The ids of the open rows (controlled). */
|
|
33
|
+
expanded?: readonly string[];
|
|
34
|
+
/** The ids of the rows that start open (uncontrolled). */
|
|
35
|
+
defaultExpanded?: readonly string[];
|
|
36
|
+
/** Called with the ids of the open rows when one opens or closes. */
|
|
37
|
+
onExpandedChange?: (expanded: string[]) => void;
|
|
38
|
+
/** The content of one cell. The first column's content sits beside the toggle. */
|
|
39
|
+
renderCell: (row: T, column: TreeTableColumn) => ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A table of rows in a hierarchy, with a toggle on each parent and keyboard navigation like a file
|
|
43
|
+
* tree.
|
|
44
|
+
*
|
|
45
|
+
* @remarks
|
|
46
|
+
* `TreeTable` is a WAI-ARIA `treegrid` set in `Table`'s styles. Hand it the top-level `rows`, how to
|
|
47
|
+
* find a row's `children`, and `renderCell`; it handles the indent, the toggles, and the
|
|
48
|
+
* `aria-level`, `aria-expanded`, `aria-setsize` and `aria-posinset` on every row.
|
|
49
|
+
*
|
|
50
|
+
* ## Keyboard
|
|
51
|
+
*
|
|
52
|
+
* The grid is one tab stop, and the arrow keys move between rows: Up and Down step by row, Right
|
|
53
|
+
* opens a closed row and then steps into its first child, Left closes an open row and then steps
|
|
54
|
+
* out to its parent, Home and End jump to the first and last visible row, and Enter follows the
|
|
55
|
+
* first link in the row. Links and buttons in a row's cells are reached by Enter, not Tab, so put
|
|
56
|
+
* the row's main link in its first column.
|
|
57
|
+
*
|
|
58
|
+
* ## Open rows
|
|
59
|
+
*
|
|
60
|
+
* `expanded` holds the ids of the open rows, as `getRowId` returns them. Pass it with
|
|
61
|
+
* `onExpandedChange` to control them, for example to keep them in the URL, or `defaultExpanded` to
|
|
62
|
+
* choose which start open. For children that load when a row opens, return `true` from
|
|
63
|
+
* `hasChildren` and give the row a placeholder child while they load.
|
|
64
|
+
*
|
|
65
|
+
* @privateRemarks
|
|
66
|
+
* A separate export, not `Table.Tree`, so a plain `import { Table }` never carries the key handling
|
|
67
|
+
* (docs/adr/0044). Ref and native props go to the `<table>`.
|
|
68
|
+
*/
|
|
69
|
+
export declare const TreeTable: <T>(props: TreeTableProps<T> & RefAttributes<HTMLTableElement>) => ReactElement;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { cx as e } from "../../../utils/cx.js";
|
|
2
|
+
import { ChevronRightIcon as t } from "../../../icons/icons.js";
|
|
3
|
+
import { useLatest as n } from "../../inputs/internal/useLatest.js";
|
|
4
|
+
import { Table as r } from "../Table/Table.js";
|
|
5
|
+
import i from "./TreeTable.module.js";
|
|
6
|
+
import { forwardRef as a, useCallback as o, useLayoutEffect as s, useMemo as c, useRef as l, useState as u } from "react";
|
|
7
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/display/TreeTable/TreeTable.tsx
|
|
9
|
+
function p({ label: a, columns: p, rows: m, getRowId: h, getChildren: g, hasChildren: _, expanded: v, defaultExpanded: y, onExpandedChange: b, renderCell: x, className: S, ...C }, w) {
|
|
10
|
+
let [T, E] = u(y ?? []), D = v !== void 0, O = D ? v : T, k = n(b), [A, j] = u(), M = l(/* @__PURE__ */ new Map()), N = l(null), P = c(() => {
|
|
11
|
+
let e = new Set(O), t = [], n = (r, i, a) => {
|
|
12
|
+
r.forEach((o, s) => {
|
|
13
|
+
let c = h(o), l = g(o), u = _ ? _(o) : (l?.length ?? 0) > 0, d = u && e.has(c);
|
|
14
|
+
t.push({
|
|
15
|
+
row: o,
|
|
16
|
+
id: c,
|
|
17
|
+
level: i + 1,
|
|
18
|
+
setSize: r.length,
|
|
19
|
+
posInSet: s + 1,
|
|
20
|
+
parentId: a,
|
|
21
|
+
expandable: u,
|
|
22
|
+
open: d
|
|
23
|
+
}), d && l && n(l, i + 1, c);
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
return n(m, 0, void 0), t;
|
|
27
|
+
}, [
|
|
28
|
+
m,
|
|
29
|
+
O,
|
|
30
|
+
h,
|
|
31
|
+
g,
|
|
32
|
+
_
|
|
33
|
+
]), F = P.some((e) => e.id === A) ? A : P[0]?.id, I = o((e, t) => {
|
|
34
|
+
let n = O.filter((t) => t !== e);
|
|
35
|
+
t && n.push(e), D || E(n), k.current?.(n);
|
|
36
|
+
}, [
|
|
37
|
+
D,
|
|
38
|
+
O,
|
|
39
|
+
k
|
|
40
|
+
]);
|
|
41
|
+
s(() => {
|
|
42
|
+
N.current?.querySelectorAll("a[href], button").forEach((e) => {
|
|
43
|
+
e.setAttribute("tabindex", "-1");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
let L = (e) => {
|
|
47
|
+
e !== void 0 && (j(e), M.current.get(e)?.focus());
|
|
48
|
+
}, R = (e) => {
|
|
49
|
+
let t = e.target.closest("tr");
|
|
50
|
+
if (e.target !== t || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
51
|
+
let n = P.findIndex((e) => M.current.get(e.id) === t), r = P[n];
|
|
52
|
+
if (!r) return;
|
|
53
|
+
let i = (t) => {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
let r = {
|
|
56
|
+
next: P[n + 1],
|
|
57
|
+
prev: P[n - 1],
|
|
58
|
+
first: P[0],
|
|
59
|
+
last: P[P.length - 1]
|
|
60
|
+
}[t];
|
|
61
|
+
L(r?.id);
|
|
62
|
+
};
|
|
63
|
+
switch (e.key) {
|
|
64
|
+
case "ArrowDown":
|
|
65
|
+
i("next");
|
|
66
|
+
return;
|
|
67
|
+
case "ArrowUp":
|
|
68
|
+
i("prev");
|
|
69
|
+
return;
|
|
70
|
+
case "Home":
|
|
71
|
+
i("first");
|
|
72
|
+
return;
|
|
73
|
+
case "End":
|
|
74
|
+
i("last");
|
|
75
|
+
return;
|
|
76
|
+
case "ArrowRight":
|
|
77
|
+
if (!r.expandable) return;
|
|
78
|
+
e.preventDefault(), r.open ? P[n + 1]?.parentId === r.id && L(P[n + 1]?.id) : I(r.id, !0);
|
|
79
|
+
return;
|
|
80
|
+
case "ArrowLeft":
|
|
81
|
+
r.open ? (e.preventDefault(), I(r.id, !1)) : r.parentId !== void 0 && (e.preventDefault(), L(r.parentId));
|
|
82
|
+
return;
|
|
83
|
+
case "Enter": {
|
|
84
|
+
let n = t.querySelector("a[href]");
|
|
85
|
+
if (!n) return;
|
|
86
|
+
e.preventDefault(), n.click();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}, z = (e) => {
|
|
91
|
+
let t = e.target.closest("tr"), n = P.find((e) => M.current.get(e.id) === t);
|
|
92
|
+
n && j(n.id);
|
|
93
|
+
};
|
|
94
|
+
return /* @__PURE__ */ f(r, {
|
|
95
|
+
ref: w,
|
|
96
|
+
role: "treegrid",
|
|
97
|
+
"aria-label": a,
|
|
98
|
+
className: e(i.treeTable, S),
|
|
99
|
+
...C,
|
|
100
|
+
children: [/* @__PURE__ */ d(r.Head, { children: /* @__PURE__ */ d(r.Row, { children: p.map((e) => /* @__PURE__ */ d(r.HeaderCell, {
|
|
101
|
+
align: e.align,
|
|
102
|
+
numeric: e.numeric,
|
|
103
|
+
width: e.width,
|
|
104
|
+
hideBelow: e.hideBelow,
|
|
105
|
+
hideAbove: e.hideAbove,
|
|
106
|
+
children: e.header
|
|
107
|
+
}, e.id)) }) }), /* @__PURE__ */ d(r.Body, {
|
|
108
|
+
ref: N,
|
|
109
|
+
onKeyDown: R,
|
|
110
|
+
onFocus: z,
|
|
111
|
+
children: P.map((e) => /* @__PURE__ */ d(r.Row, {
|
|
112
|
+
ref: (t) => {
|
|
113
|
+
t ? M.current.set(e.id, t) : M.current.delete(e.id);
|
|
114
|
+
},
|
|
115
|
+
className: i.row,
|
|
116
|
+
interactive: !0,
|
|
117
|
+
tabIndex: e.id === F ? 0 : -1,
|
|
118
|
+
"aria-level": e.level,
|
|
119
|
+
"aria-setsize": e.setSize,
|
|
120
|
+
"aria-posinset": e.posInSet,
|
|
121
|
+
"aria-expanded": e.expandable ? e.open : void 0,
|
|
122
|
+
children: p.map((n, a) => /* @__PURE__ */ d(r.Cell, {
|
|
123
|
+
rowHeader: a === 0,
|
|
124
|
+
align: n.align,
|
|
125
|
+
numeric: n.numeric,
|
|
126
|
+
hideBelow: n.hideBelow,
|
|
127
|
+
hideAbove: n.hideAbove,
|
|
128
|
+
children: a === 0 ? /* @__PURE__ */ f("span", {
|
|
129
|
+
className: i.treeCellContent,
|
|
130
|
+
style: { "--_level": e.level - 1 },
|
|
131
|
+
children: [/* @__PURE__ */ d("button", {
|
|
132
|
+
type: "button",
|
|
133
|
+
className: i.toggle,
|
|
134
|
+
tabIndex: -1,
|
|
135
|
+
"aria-hidden": "true",
|
|
136
|
+
"data-leaf": e.expandable ? void 0 : "",
|
|
137
|
+
onClick: () => {
|
|
138
|
+
L(e.id), I(e.id, !e.open);
|
|
139
|
+
},
|
|
140
|
+
children: /* @__PURE__ */ d(t, { className: i.chevron })
|
|
141
|
+
}), x(e.row, n)]
|
|
142
|
+
}) : x(e.row, n)
|
|
143
|
+
}, n.id))
|
|
144
|
+
}, e.id))
|
|
145
|
+
})]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
var m = a(p);
|
|
149
|
+
//#endregion
|
|
150
|
+
export { m as TreeTable };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=TreeTable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeTable.js","names":["styles","TreeTable_module_default","TreeTableInner","expandedProp","rest","inner","setInner","useState","defaultExpanded","controlled","expandedIds","onChange","useLatest","onExpandedChange","activeId","setActiveId","elements","useRef","body","nodes","useMemo","open","out","walk","id","getRowId","row","children","getChildren","expandable","hasChildren","isOpen","level","siblings","index","rows","tabId","node","setExpanded","useCallback","next","existing","moveTo","onKeyDown","rowElement","event","go","target","to","link","onFocus","found","Table","label","cx","className","columns","column","element","columnIndex","ChevronRightIcon","renderCell","TreeTable","forwardRef"],"sources":["../../../../src/components/display/TreeTable/TreeTable.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefAttributes,\n} from 'react'\nimport {\n Table,\n type TableAlign,\n type TableColumnWidth,\n type TableProps,\n} from '#components/display/Table'\nimport { ChevronRightIcon } from '#icons'\nimport { cx } from '#utils/cx'\nimport type { VisibilityProps } from '#utils/visibility'\nimport { useLatest } from '#components/inputs/internal/useLatest'\nimport styles from './TreeTable.module.css'\n\n/** One column of a {@link TreeTable}. The first column holds the tree: indent, toggle and the row's name. */\nexport interface TreeTableColumn extends VisibilityProps {\n /** Stable key, passed back to `renderCell`. */\n id: string\n /** The header cell's content. */\n header: ReactNode\n align?: TableAlign\n /** Numeric column: right-aligned with tabular figures. */\n numeric?: boolean\n /** Column sizing: `fill` takes the spare width, `min` shrinks to fit. */\n width?: TableColumnWidth\n}\n\nexport interface TreeTableProps<T> extends Omit<\n TableProps,\n 'children' | 'label' | 'role' | 'onChange' | 'defaultValue'\n> {\n /** Name for the grid, read by assistive technology. */\n label: string\n /** The columns, in order. The first holds the tree. */\n columns: readonly TreeTableColumn[]\n /** The top-level rows. */\n rows: readonly T[]\n /** A stable, unique id for a row. It is what `expanded` holds and what keeps focus on a row across renders. */\n getRowId: (row: T) => string\n /** The row's children, or `undefined` when it has none. */\n getChildren: (row: T) => readonly T[] | undefined\n /**\n * Whether the row can be opened. Defaults to `getChildren(row)` having entries. Return `true` for a\n * row whose children load when it opens.\n */\n hasChildren?: (row: T) => boolean\n /** The ids of the open rows (controlled). */\n expanded?: readonly string[]\n /** The ids of the rows that start open (uncontrolled). */\n defaultExpanded?: readonly string[]\n /** Called with the ids of the open rows when one opens or closes. */\n onExpandedChange?: (expanded: string[]) => void\n /** The content of one cell. The first column's content sits beside the toggle. */\n renderCell: (row: T, column: TreeTableColumn) => ReactNode\n}\n\ninterface Node<T> {\n row: T\n id: string\n level: number\n setSize: number\n posInSet: number\n parentId: string | undefined\n expandable: boolean\n open: boolean\n}\n\ntype Dispatch = 'next' | 'prev' | 'first' | 'last'\n\nfunction TreeTableInner<T>(\n {\n label,\n columns,\n rows,\n getRowId,\n getChildren,\n hasChildren,\n expanded: expandedProp,\n defaultExpanded,\n onExpandedChange,\n renderCell,\n className,\n ...rest\n }: TreeTableProps<T>,\n ref: Ref<HTMLTableElement>,\n) {\n const [inner, setInner] = useState<readonly string[]>(defaultExpanded ?? [])\n const controlled = expandedProp !== undefined\n const expandedIds = controlled ? expandedProp : inner\n const onChange = useLatest(onExpandedChange)\n const [activeId, setActiveId] = useState<string>()\n const elements = useRef(new Map<string, HTMLTableRowElement>())\n const body = useRef<HTMLTableSectionElement>(null)\n\n const nodes = useMemo(() => {\n const open = new Set(expandedIds)\n const out: Node<T>[] = []\n const walk = (siblings: readonly T[], level: number, parentId: string | undefined) => {\n siblings.forEach((row, index) => {\n const id = getRowId(row)\n const children = getChildren(row)\n const expandable = hasChildren ? hasChildren(row) : (children?.length ?? 0) > 0\n const isOpen = expandable && open.has(id)\n out.push({\n row,\n id,\n level: level + 1,\n setSize: siblings.length,\n posInSet: index + 1,\n parentId,\n expandable,\n open: isOpen,\n })\n if (isOpen && children) walk(children, level + 1, id)\n })\n }\n walk(rows, 0, undefined)\n return out\n }, [rows, expandedIds, getRowId, getChildren, hasChildren])\n\n const tabId = nodes.some((node) => node.id === activeId) ? activeId : nodes[0]?.id\n\n const setExpanded = useCallback(\n (id: string, open: boolean) => {\n const next = expandedIds.filter((existing) => existing !== id)\n if (open) next.push(id)\n if (!controlled) setInner(next)\n onChange.current?.(next)\n },\n [controlled, expandedIds, onChange],\n )\n\n // Links and buttons inside a row are reached with Enter on the row, so they stay out of the tab\n // order: the grid is one tab stop, however many rows are open.\n useLayoutEffect(() => {\n body.current?.querySelectorAll<HTMLElement>('a[href], button').forEach((element) => {\n element.setAttribute('tabindex', '-1')\n })\n })\n\n const moveTo = (id: string | undefined) => {\n if (id === undefined) return\n setActiveId(id)\n elements.current.get(id)?.focus()\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLTableSectionElement>) => {\n const rowElement = (event.target as HTMLElement).closest('tr')\n if (event.target !== rowElement || event.altKey || event.ctrlKey || event.metaKey) return\n const index = nodes.findIndex((node) => elements.current.get(node.id) === rowElement)\n const node = nodes[index]\n if (!node) return\n const go = (to: Dispatch) => {\n event.preventDefault()\n const target = {\n next: nodes[index + 1],\n prev: nodes[index - 1],\n first: nodes[0],\n last: nodes[nodes.length - 1],\n }[to]\n moveTo(target?.id)\n }\n switch (event.key) {\n case 'ArrowDown':\n go('next')\n return\n case 'ArrowUp':\n go('prev')\n return\n case 'Home':\n go('first')\n return\n case 'End':\n go('last')\n return\n case 'ArrowRight':\n if (!node.expandable) return\n event.preventDefault()\n if (!node.open) setExpanded(node.id, true)\n else if (nodes[index + 1]?.parentId === node.id) moveTo(nodes[index + 1]?.id)\n return\n case 'ArrowLeft':\n if (node.open) {\n event.preventDefault()\n setExpanded(node.id, false)\n } else if (node.parentId !== undefined) {\n event.preventDefault()\n moveTo(node.parentId)\n }\n return\n case 'Enter': {\n const link = rowElement.querySelector<HTMLElement>('a[href]')\n if (!link) return\n event.preventDefault()\n link.click()\n return\n }\n }\n }\n\n // Clicking inside a row (or focusing a link in it) makes that row the one tab stop.\n const onFocus = (event: FocusEvent<HTMLTableSectionElement>) => {\n const rowElement = (event.target as HTMLElement).closest('tr')\n const found = nodes.find((node) => elements.current.get(node.id) === rowElement)\n if (found) setActiveId(found.id)\n }\n\n return (\n <Table\n ref={ref}\n role=\"treegrid\"\n aria-label={label}\n className={cx(styles.treeTable, className)}\n {...rest}\n >\n <Table.Head>\n <Table.Row>\n {columns.map((column) => (\n <Table.HeaderCell\n key={column.id}\n align={column.align}\n numeric={column.numeric}\n width={column.width}\n hideBelow={column.hideBelow}\n hideAbove={column.hideAbove}\n >\n {column.header}\n </Table.HeaderCell>\n ))}\n </Table.Row>\n </Table.Head>\n <Table.Body ref={body} onKeyDown={onKeyDown} onFocus={onFocus}>\n {nodes.map((node) => (\n <Table.Row\n key={node.id}\n ref={(element) => {\n if (element) elements.current.set(node.id, element)\n else elements.current.delete(node.id)\n }}\n className={styles.row}\n interactive\n tabIndex={node.id === tabId ? 0 : -1}\n aria-level={node.level}\n aria-setsize={node.setSize}\n aria-posinset={node.posInSet}\n aria-expanded={node.expandable ? node.open : undefined}\n >\n {columns.map((column, columnIndex) => {\n return (\n <Table.Cell\n key={column.id}\n rowHeader={columnIndex === 0}\n align={column.align}\n numeric={column.numeric}\n hideBelow={column.hideBelow}\n hideAbove={column.hideAbove}\n >\n {columnIndex === 0 ? (\n <span\n className={styles.treeCellContent}\n style={{ '--_level': node.level - 1 } as CSSProperties}\n >\n <button\n type=\"button\"\n className={styles.toggle}\n tabIndex={-1}\n aria-hidden=\"true\"\n data-leaf={node.expandable ? undefined : ''}\n onClick={() => {\n moveTo(node.id)\n setExpanded(node.id, !node.open)\n }}\n >\n <ChevronRightIcon className={styles.chevron} />\n </button>\n {renderCell(node.row, column)}\n </span>\n ) : (\n renderCell(node.row, column)\n )}\n </Table.Cell>\n )\n })}\n </Table.Row>\n ))}\n </Table.Body>\n </Table>\n )\n}\n\n/**\n * A table of rows in a hierarchy, with a toggle on each parent and keyboard navigation like a file\n * tree.\n *\n * @remarks\n * `TreeTable` is a WAI-ARIA `treegrid` set in `Table`'s styles. Hand it the top-level `rows`, how to\n * find a row's `children`, and `renderCell`; it handles the indent, the toggles, and the\n * `aria-level`, `aria-expanded`, `aria-setsize` and `aria-posinset` on every row.\n *\n * ## Keyboard\n *\n * The grid is one tab stop, and the arrow keys move between rows: Up and Down step by row, Right\n * opens a closed row and then steps into its first child, Left closes an open row and then steps\n * out to its parent, Home and End jump to the first and last visible row, and Enter follows the\n * first link in the row. Links and buttons in a row's cells are reached by Enter, not Tab, so put\n * the row's main link in its first column.\n *\n * ## Open rows\n *\n * `expanded` holds the ids of the open rows, as `getRowId` returns them. Pass it with\n * `onExpandedChange` to control them, for example to keep them in the URL, or `defaultExpanded` to\n * choose which start open. For children that load when a row opens, return `true` from\n * `hasChildren` and give the row a placeholder child while they load.\n *\n * @privateRemarks\n * A separate export, not `Table.Tree`, so a plain `import { Table }` never carries the key handling\n * (docs/adr/0044). Ref and native props go to the `<table>`.\n */\nexport const TreeTable = forwardRef(TreeTableInner) as <T>(\n props: TreeTableProps<T> & RefAttributes<HTMLTableElement>,\n) => ReactElement\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/components/display/TreeTable/TreeTable.module.css
|
|
2
|
+
var e = "kiln-TreeTable__treeTable", t = "kiln-TreeTable__row", n = "kiln-TreeTable__treeCellContent", r = "kiln-TreeTable__toggle", i = "kiln-TreeTable__chevron", a = {
|
|
3
|
+
treeTable: e,
|
|
4
|
+
row: t,
|
|
5
|
+
treeCellContent: n,
|
|
6
|
+
toggle: r,
|
|
7
|
+
chevron: i
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { i as chevron, a as default, t as row, r as toggle, n as treeCellContent, e as treeTable };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=TreeTable.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeTable.module.js","names":["treeTable","row","treeCellContent","toggle","chevron","TreeTable_module_default"],"sources":["../../../../src/components/display/TreeTable/TreeTable.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --tree-table-indent default var(--space-5) — how far each level steps in\n *\n * The table itself is styled by Table; this file adds the indent, the toggle and the row focus.\n */\n.treeTable {\n --_indent: var(--tree-table-indent, var(--space-5));\n}\n\n.row:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: -2px;\n}\n\n.treeCellContent {\n --_level: 0;\n margin-inline-start: calc(var(--_level) * var(--_indent));\n display: inline-flex;\n align-items: baseline;\n gap: var(--space-1);\n min-inline-size: 0;\n}\n\n.toggle {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n inline-size: 1.25rem;\n block-size: 1.25rem;\n padding: 0;\n border: 0;\n background: none;\n color: var(--color-ink-muted);\n cursor: pointer;\n align-self: center;\n}\n\n.toggle[data-leaf] {\n visibility: hidden;\n}\n\n.chevron {\n inline-size: 1rem;\n block-size: 1rem;\n transition: transform var(--dur-3) var(--ease-out);\n}\n\n.row[aria-expanded='true'] .chevron {\n transform: rotate(90deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .chevron {\n transition: none;\n }\n}\n"],"mappings":""}
|