@mitcsutt/kiln-ui 0.6.0 → 0.7.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.
Files changed (178) hide show
  1. package/dist/components/actions/CopyButton/CopyButton.d.ts +58 -0
  2. package/dist/components/actions/CopyButton/CopyButton.js +63 -0
  3. package/dist/components/actions/CopyButton/CopyButton.js.map +1 -0
  4. package/dist/components/actions/CopyButton/CopyButton.module.js +9 -0
  5. package/dist/components/actions/CopyButton/CopyButton.module.js.map +1 -0
  6. package/dist/components/actions/CopyButton/index.d.ts +4 -0
  7. package/dist/components/actions/CopyButton/useClipboard.d.ts +35 -0
  8. package/dist/components/actions/CopyButton/useClipboard.js +62 -0
  9. package/dist/components/actions/CopyButton/useClipboard.js.map +1 -0
  10. package/dist/components/actions/ToggleChip/ToggleChip.d.ts +6 -0
  11. package/dist/components/actions/ToggleChip/ToggleChip.js +26 -24
  12. package/dist/components/actions/ToggleChip/ToggleChip.js.map +1 -1
  13. package/dist/components/display/Badge/Badge.d.ts +6 -0
  14. package/dist/components/display/Badge/Badge.js +16 -15
  15. package/dist/components/display/Badge/Badge.js.map +1 -1
  16. package/dist/components/display/CodeBlock/CodeBlock.js +45 -58
  17. package/dist/components/display/CodeBlock/CodeBlock.js.map +1 -1
  18. package/dist/components/display/Swatch/Swatch.d.ts +52 -0
  19. package/dist/components/display/Swatch/Swatch.js +27 -0
  20. package/dist/components/display/Swatch/Swatch.js.map +1 -0
  21. package/dist/components/display/Swatch/Swatch.module.js +6 -0
  22. package/dist/components/display/Swatch/Swatch.module.js.map +1 -0
  23. package/dist/components/display/Swatch/index.d.ts +2 -0
  24. package/dist/components/display/Tag/Tag.d.ts +7 -0
  25. package/dist/components/display/Tag/Tag.js +31 -26
  26. package/dist/components/display/Tag/Tag.js.map +1 -1
  27. package/dist/components/feedback/Indicator/Indicator.d.ts +74 -0
  28. package/dist/components/feedback/Indicator/Indicator.js +50 -0
  29. package/dist/components/feedback/Indicator/Indicator.js.map +1 -0
  30. package/dist/components/feedback/Indicator/Indicator.module.js +12 -0
  31. package/dist/components/feedback/Indicator/Indicator.module.js.map +1 -0
  32. package/dist/components/feedback/Indicator/index.d.ts +2 -0
  33. package/dist/components/feedback/Toaster/Toaster.d.ts +71 -0
  34. package/dist/components/feedback/Toaster/Toaster.js +121 -0
  35. package/dist/components/feedback/Toaster/Toaster.js.map +1 -0
  36. package/dist/components/feedback/Toaster/Toaster.module.js +20 -0
  37. package/dist/components/feedback/Toaster/Toaster.module.js.map +1 -0
  38. package/dist/components/feedback/Toaster/index.d.ts +4 -0
  39. package/dist/components/feedback/Toaster/toast.d.ts +96 -0
  40. package/dist/components/feedback/Toaster/toast.js +131 -0
  41. package/dist/components/feedback/Toaster/toast.js.map +1 -0
  42. package/dist/components/inputs/Calendar/Calendar.d.ts +87 -0
  43. package/dist/components/inputs/Calendar/Calendar.js +217 -0
  44. package/dist/components/inputs/Calendar/Calendar.js.map +1 -0
  45. package/dist/components/inputs/Calendar/Calendar.module.js +18 -0
  46. package/dist/components/inputs/Calendar/Calendar.module.js.map +1 -0
  47. package/dist/components/inputs/Calendar/index.d.ts +2 -0
  48. package/dist/components/inputs/Combobox/Combobox.js +36 -44
  49. package/dist/components/inputs/Combobox/Combobox.js.map +1 -1
  50. package/dist/components/inputs/DateField/DateField.d.ts +17 -0
  51. package/dist/components/inputs/DateField/DateField.js +26 -0
  52. package/dist/components/inputs/DateField/DateField.js.map +1 -0
  53. package/dist/components/inputs/DateField/index.d.ts +2 -0
  54. package/dist/components/inputs/DateInput/DateInput.d.ts +75 -0
  55. package/dist/components/inputs/DateInput/DateInput.js +139 -0
  56. package/dist/components/inputs/DateInput/DateInput.js.map +1 -0
  57. package/dist/components/inputs/DateInput/index.d.ts +3 -0
  58. package/dist/components/inputs/DateRangeField/DateRangeField.d.ts +18 -3
  59. package/dist/components/inputs/DateRangeField/DateRangeField.js.map +1 -1
  60. package/dist/components/inputs/DateRangeField/DateRangeField.module.js +8 -3
  61. package/dist/components/inputs/DateRangeField/DateRangeField.module.js.map +1 -1
  62. package/dist/components/inputs/DateRangeField/DateRangePickerField.d.ts +48 -0
  63. package/dist/components/inputs/DateRangeField/DateRangePickerField.js +246 -0
  64. package/dist/components/inputs/DateRangeField/DateRangePickerField.js.map +1 -0
  65. package/dist/components/inputs/DateRangeField/index.d.ts +2 -0
  66. package/dist/components/inputs/DateTimeField/DateTimeField.d.ts +17 -0
  67. package/dist/components/inputs/DateTimeField/DateTimeField.js +26 -0
  68. package/dist/components/inputs/DateTimeField/DateTimeField.js.map +1 -0
  69. package/dist/components/inputs/DateTimeField/index.d.ts +2 -0
  70. package/dist/components/inputs/DateTimeInput/DateTimeInput.d.ts +83 -0
  71. package/dist/components/inputs/DateTimeInput/DateTimeInput.js +216 -0
  72. package/dist/components/inputs/DateTimeInput/DateTimeInput.js.map +1 -0
  73. package/dist/components/inputs/DateTimeInput/index.d.ts +2 -0
  74. package/dist/components/inputs/JsonField/JsonField.d.ts +23 -0
  75. package/dist/components/inputs/JsonField/JsonField.js +33 -0
  76. package/dist/components/inputs/JsonField/JsonField.js.map +1 -0
  77. package/dist/components/inputs/JsonField/index.d.ts +2 -0
  78. package/dist/components/inputs/JsonInput/JsonInput.d.ts +119 -0
  79. package/dist/components/inputs/JsonInput/JsonInput.js +282 -0
  80. package/dist/components/inputs/JsonInput/JsonInput.js.map +1 -0
  81. package/dist/components/inputs/JsonInput/JsonInput.module.js +16 -0
  82. package/dist/components/inputs/JsonInput/JsonInput.module.js.map +1 -0
  83. package/dist/components/inputs/JsonInput/index.d.ts +4 -0
  84. package/dist/components/inputs/JsonInput/json.d.ts +59 -0
  85. package/dist/components/inputs/JsonInput/json.js +231 -0
  86. package/dist/components/inputs/JsonInput/json.js.map +1 -0
  87. package/dist/components/inputs/TagsInput/TagsInput.js +5 -5
  88. package/dist/components/inputs/TimeField/TimeField.d.ts +17 -0
  89. package/dist/components/inputs/TimeField/TimeField.js +26 -0
  90. package/dist/components/inputs/TimeField/TimeField.js.map +1 -0
  91. package/dist/components/inputs/TimeField/index.d.ts +2 -0
  92. package/dist/components/inputs/TimeInput/TimeInput.d.ts +54 -0
  93. package/dist/components/inputs/TimeInput/TimeInput.js +107 -0
  94. package/dist/components/inputs/TimeInput/TimeInput.js.map +1 -0
  95. package/dist/components/inputs/TimeInput/index.d.ts +2 -0
  96. package/dist/components/inputs/internal/DateSegments.d.ts +80 -0
  97. package/dist/components/inputs/internal/DateSegments.js +345 -0
  98. package/dist/components/inputs/internal/DateSegments.js.map +1 -0
  99. package/dist/components/inputs/internal/dates.d.ts +101 -0
  100. package/dist/components/inputs/internal/dates.js +262 -0
  101. package/dist/components/inputs/internal/dates.js.map +1 -0
  102. package/dist/components/inputs/internal/filterOptions.d.ts +22 -1
  103. package/dist/components/inputs/internal/filterOptions.js +32 -1
  104. package/dist/components/inputs/internal/filterOptions.js.map +1 -1
  105. package/dist/components/inputs/internal/picker.d.ts +31 -0
  106. package/dist/components/inputs/internal/picker.js +29 -0
  107. package/dist/components/inputs/internal/picker.js.map +1 -0
  108. package/dist/components/inputs/internal/picker.module.js +15 -0
  109. package/dist/components/inputs/internal/picker.module.js.map +1 -0
  110. package/dist/components/inputs/internal/segments.module.js +10 -0
  111. package/dist/components/inputs/internal/segments.module.js.map +1 -0
  112. package/dist/components/layout/Collapse/Collapse.d.ts +39 -0
  113. package/dist/components/layout/Collapse/Collapse.js +24 -0
  114. package/dist/components/layout/Collapse/Collapse.js.map +1 -0
  115. package/dist/components/layout/Collapse/Collapse.module.js +6 -0
  116. package/dist/components/layout/Collapse/Collapse.module.js.map +1 -0
  117. package/dist/components/layout/Collapse/index.d.ts +2 -0
  118. package/dist/components/layout/Collapse/useHeightMotion.d.ts +16 -0
  119. package/dist/components/layout/Collapse/useHeightMotion.js +60 -0
  120. package/dist/components/layout/Collapse/useHeightMotion.js.map +1 -0
  121. package/dist/components/layout/ShowMore/ShowMore.d.ts +59 -0
  122. package/dist/components/layout/ShowMore/ShowMore.js +114 -0
  123. package/dist/components/layout/ShowMore/ShowMore.js.map +1 -0
  124. package/dist/components/layout/ShowMore/ShowMore.module.js +12 -0
  125. package/dist/components/layout/ShowMore/ShowMore.module.js.map +1 -0
  126. package/dist/components/layout/ShowMore/index.d.ts +2 -0
  127. package/dist/components/layout/Transition/Transition.d.ts +63 -0
  128. package/dist/components/layout/Transition/Transition.js +63 -0
  129. package/dist/components/layout/Transition/Transition.js.map +1 -0
  130. package/dist/components/layout/Transition/Transition.module.js +17 -0
  131. package/dist/components/layout/Transition/Transition.module.js.map +1 -0
  132. package/dist/components/layout/Transition/index.d.ts +2 -0
  133. package/dist/components/navigation/Collapsible/Collapsible.d.ts +49 -0
  134. package/dist/components/navigation/Collapsible/Collapsible.js +83 -0
  135. package/dist/components/navigation/Collapsible/Collapsible.js.map +1 -0
  136. package/dist/components/navigation/Collapsible/Collapsible.module.js +11 -0
  137. package/dist/components/navigation/Collapsible/Collapsible.module.js.map +1 -0
  138. package/dist/components/navigation/Collapsible/index.d.ts +2 -0
  139. package/dist/components/navigation/NavLinks/NavLinks.d.ts +32 -4
  140. package/dist/components/navigation/NavLinks/NavLinks.js +69 -39
  141. package/dist/components/navigation/NavLinks/NavLinks.js.map +1 -1
  142. package/dist/components/navigation/NavLinks/NavLinks.module.js +13 -3
  143. package/dist/components/navigation/NavLinks/NavLinks.module.js.map +1 -1
  144. package/dist/components/navigation/NavLinks/NavLinksGroup.d.ts +49 -0
  145. package/dist/components/navigation/NavLinks/NavLinksGroup.js +185 -0
  146. package/dist/components/navigation/NavLinks/NavLinksGroup.js.map +1 -0
  147. package/dist/components/navigation/NavLinks/context.d.ts +28 -0
  148. package/dist/components/navigation/NavLinks/context.js +13 -0
  149. package/dist/components/navigation/NavLinks/context.js.map +1 -0
  150. package/dist/components/navigation/NavLinks/index.d.ts +2 -0
  151. package/dist/components/navigation/NavLinks/treeKeys.d.ts +8 -0
  152. package/dist/components/navigation/NavLinks/treeKeys.js +57 -0
  153. package/dist/components/navigation/NavLinks/treeKeys.js.map +1 -0
  154. package/dist/components/overlays/CommandPalette/CommandPalette.d.ts +120 -0
  155. package/dist/components/overlays/CommandPalette/CommandPalette.js +386 -0
  156. package/dist/components/overlays/CommandPalette/CommandPalette.js.map +1 -0
  157. package/dist/components/overlays/CommandPalette/CommandPalette.module.js +31 -0
  158. package/dist/components/overlays/CommandPalette/CommandPalette.module.js.map +1 -0
  159. package/dist/components/overlays/CommandPalette/index.d.ts +2 -0
  160. package/dist/components/overlays/CommandPalette/shortcut.d.ts +28 -0
  161. package/dist/components/overlays/CommandPalette/shortcut.js +104 -0
  162. package/dist/components/overlays/CommandPalette/shortcut.js.map +1 -0
  163. package/dist/index.d.ts +40 -4
  164. package/dist/index.js +123 -100
  165. package/dist/styles.css +1 -1
  166. package/dist/themes/fiesta.css +1 -1
  167. package/dist/themes/flightdeck.css +1 -1
  168. package/dist/themes/ledger.css +1 -1
  169. package/dist/themes/monograph.css +1 -1
  170. package/dist/themes/riso.css +1 -1
  171. package/package.json +1 -1
  172. package/skills/custom-theme/SKILL.md +32 -0
  173. package/skills/custom-theme/references/colour.md +20 -0
  174. package/skills/custom-theme/references/motion.md +9 -0
  175. package/skills/custom-theme/references/tokens.md +35 -0
  176. package/skills/design-rules/SKILL.md +5 -2
  177. package/skills/setup-and-theming/SKILL.md +1 -1
  178. 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,6 @@
1
+ //#region src/components/display/Swatch/Swatch.module.css
2
+ var e = "kiln-Swatch__swatch", t = { swatch: e };
3
+ //#endregion
4
+ export { t as default, e as swatch };
5
+
6
+ //# sourceMappingURL=Swatch.module.js.map
@@ -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":""}
@@ -0,0 +1,2 @@
1
+ export { Swatch, isCategoryColor } from './Swatch.js';
2
+ export type { SwatchProps, SwatchColor, SwatchSize } from './Swatch.js';
@@ -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 "./Tag.module.js";
4
- import { Children as r, forwardRef as i, isValidElement as a } from "react";
5
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
6
- import { Slot as c } from "radix-ui";
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 l = i(function({ color: r, size: i = "md", onRemove: a, removeLabel: l, asChild: u = !1, className: d, children: f, ...p }, m) {
9
- let h = u ? c.Root : "span", g = !!a && !u, _ = l ?? (typeof f == "string" ? `Remove ${f}` : "Remove");
10
- return /* @__PURE__ */ s(h, {
11
- ref: m,
12
- className: e(n.tag, d),
13
- "data-color": r,
14
- "data-size": i,
15
- "data-removable": g || void 0,
16
- ...p,
17
- children: [/* @__PURE__ */ o(c.Slottable, { children: f }), g ? /* @__PURE__ */ o("button", {
18
- type: "button",
19
- className: n.remove,
20
- "aria-label": _,
21
- onClick: a,
22
- children: /* @__PURE__ */ o(t, {})
23
- }) : null]
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
- }), u = i(function({ className: t, children: i, ...s }, c) {
26
- return /* @__PURE__ */ o("ul", {
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(n.list, t),
30
- ...s,
31
- children: r.map(i, (e) => a(e) || typeof e == "string" || typeof e == "number" ? /* @__PURE__ */ o("li", { children: e }, a(e) ? e.key : void 0) : null)
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 { l as Tag, u as TagList };
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 { color, size = 'md', onRemove, removeLabel, asChild = false, className, children, ...rest },\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 <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":";;;;;;;AAkCA,IAAaE,IAAMC,EAAsC,SACvD,EAAE,UAAO,UAAO,MAAM,aAAU,gBAAa,aAAU,IAAO,cAAW,aAAU,GAAGC,KACtF,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,CAQE,kBAACG,EAAK,WAAN,EAAiB,YAAyB,CAAA,GACzCC,IACC,kBAAC,UAAD;GAAQ,MAAK;GAAS,WAAWR,EAAO;GAAQ,cAAYU;GAAO,SAASD;GAC1E,UAAA,kBAACQ,GAAD,CAAY,CAAA;EACN,CAAA,IACN,IACA;;AAEV,CAAC,GAKYC,IAAUf,EAA2C,SAChE,EAAE,cAAW,aAAU,GAAGC,KAC1B,GACA;CACA,OACE,kBAAC,MAAD;EAAS;EAAK,MAAK;EAAO,WAAWS,EAAGb,EAAO,MAAMc,CAAS;EAAG,GAAIV;EAClE,UAAAe,EAAS,IAAIP,IAAW,MACvBQ,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"}
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,74 @@
1
+ import { type HTMLAttributes, type ReactElement } from 'react';
2
+ import { type Space, type Tone } from '../../../utils/tokens.js';
3
+ import { type SwatchColor } from '../../display/Swatch/index.js';
4
+ export type IndicatorPosition = 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
5
+ export type IndicatorShape = 'rect' | 'round' | 'avatar';
6
+ export interface IndicatorProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children' | 'color'> {
7
+ /**
8
+ * The element the mark is pinned to: an `IconButton`, an `Avatar`, a link. It must accept
9
+ * `aria-describedby`, which carries `label`. Omit it to show the mark on its own, in the flow.
10
+ */
11
+ children?: ReactElement;
12
+ /**
13
+ * What the mark means, said in full: "3 unread messages", "Waiting for your answer". It's added
14
+ * to the child's accessible description, so the child is announced as "Inbox, 3 unread messages"
15
+ * rather than a bare number. Required, because a dot or a number alone is not information.
16
+ */
17
+ label: string;
18
+ /** A number to show. Without it the mark is a dot. */
19
+ count?: number;
20
+ /** The largest count shown in full. Above it the mark reads "99+". Default `99`. */
21
+ max?: number;
22
+ /** Show the mark when `count` is `0`. Hidden by default, since nothing is waiting. */
23
+ showZero?: boolean;
24
+ /** Hide the mark (and its description) without unmounting the child. */
25
+ invisible?: boolean;
26
+ /** Colour of the mark, from the shared tone set. Default `accent`. */
27
+ tone?: Tone;
28
+ /**
29
+ * Draw the mark as a colour swatch dot instead: identity, like a team's colour on an avatar,
30
+ * never status. A categorical slot (`1` to `8`) or a CSS colour from data. `count` and `tone`
31
+ * don't apply.
32
+ */
33
+ swatch?: SwatchColor;
34
+ /** A slow ring swells out of the mark, for something in progress. It stills under reduced motion. */
35
+ processing?: boolean;
36
+ /** Which corner, in the reading direction: `top-end` (default) is top right in English. */
37
+ position?: IndicatorPosition;
38
+ /**
39
+ * The child's outline, so the mark sits on its edge rather than in the empty corner of its box.
40
+ * `rect` (default) for square-cornered children, `round` for circles (a round `IconButton`),
41
+ * `avatar` to follow the theme's avatar radius.
42
+ */
43
+ shape?: IndicatorShape;
44
+ /** Move the mark in toward the child's centre by a space step, for a child with an empty edge. */
45
+ offset?: Space;
46
+ }
47
+ /**
48
+ * A count or a dot pinned to the corner of another element: unread messages on a button, a
49
+ * waiting dot on an avatar.
50
+ *
51
+ * @remarks
52
+ * `Indicator` wraps one element and places a small mark on its edge. With `count` it's a number,
53
+ * capped at `max` ("99+"), and hidden at zero unless you pass `showZero`. Without it, it's a dot.
54
+ * `invisible` hides the mark without moving or remounting the child.
55
+ *
56
+ * The mark is hidden from screen readers. What it means is `label`, which is added to the
57
+ * child's accessible description, so give the child its own name (an `IconButton`'s `label`, an
58
+ * `Avatar`'s `name`) and write `label` as the rest of the sentence.
59
+ *
60
+ * `position` is logical, so `top-end` is the top left in a right-to-left page. `shape` keeps the
61
+ * mark on the outline of a round child instead of floating in the corner of its box, and `offset`
62
+ * moves it in toward a child whose visible edge is inside its box, like a ghost `IconButton`.
63
+ *
64
+ * ## When it changes
65
+ *
66
+ * An `Indicator` doesn't announce changes: a count that speaks every time it ticks is noise.
67
+ * Where a change matters, such as a reply arriving, announce it where it happens. `processing`
68
+ * pulses a ring for something under way, and stills under reduced motion.
69
+ *
70
+ * @privateRemarks
71
+ * The tone colours match StatusDot and Badge's solid fill, the pulse is LiveIndicator's, and a
72
+ * `swatch` mark is the shared Swatch part, so the dots in the system stay one family.
73
+ */
74
+ export declare const Indicator: import("react").ForwardRefExoticComponent<IndicatorProps & import("react").RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,50 @@
1
+ import { cx as e } from "../../../utils/cx.js";
2
+ import { mergeStyles as t } from "../../../utils/responsive.js";
3
+ import { space as n } from "../../../utils/tokens.js";
4
+ import { joinIds as r } from "../../inputs/internal/refs.js";
5
+ import { Swatch as i } from "../../display/Swatch/Swatch.js";
6
+ import a from "./Indicator.module.js";
7
+ import { cloneElement as o, forwardRef as s, isValidElement as c, useId as l } from "react";
8
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
9
+ //#region src/components/feedback/Indicator/Indicator.tsx
10
+ var f = (e, t) => e > t ? `${String(t)}+` : String(e), p = s(function({ children: s, label: p, count: m, max: h = 99, showZero: g = !1, invisible: _ = !1, tone: v = "accent", swatch: y, processing: b = !1, position: x = "top-end", shape: S = "rect", offset: C, className: w, style: T, ...E }, D) {
11
+ let O = l(), k = y === void 0 && m !== void 0, A = !_ && !(k && m === 0 && !g), j = c(s), M = A ? y === void 0 ? /* @__PURE__ */ u("span", {
12
+ className: a.mark,
13
+ "data-kind": k ? "count" : "dot",
14
+ "data-tone": v,
15
+ "data-processing": b || void 0,
16
+ "aria-hidden": "true",
17
+ children: k ? f(m, h) : null
18
+ }) : /* @__PURE__ */ u("span", {
19
+ className: a.mark,
20
+ "data-kind": "swatch",
21
+ "data-processing": b || void 0,
22
+ "aria-hidden": "true",
23
+ children: /* @__PURE__ */ u(i, {
24
+ color: y,
25
+ className: a.fill
26
+ })
27
+ }) : null;
28
+ return /* @__PURE__ */ d("span", {
29
+ ref: D,
30
+ className: e(a.indicator, w),
31
+ "data-position": x,
32
+ "data-shape": S,
33
+ "data-pinned": j || void 0,
34
+ style: t(C ? { "--_offset": n(C) } : void 0, T),
35
+ ...E,
36
+ children: [
37
+ j ? o(s, { "aria-describedby": r(s.props["aria-describedby"], A ? O : void 0) }) : null,
38
+ M,
39
+ A ? /* @__PURE__ */ u("span", {
40
+ id: O,
41
+ className: a.label,
42
+ children: p
43
+ }) : null
44
+ ]
45
+ });
46
+ });
47
+ //#endregion
48
+ export { p as Indicator };
49
+
50
+ //# sourceMappingURL=Indicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Indicator.js","names":["styles","Indicator_module_default","formatCount","count","max","Indicator","forwardRef","rest","labelId","useId","hasCount","swatch","shown","invisible","showZero","pinned","isValidElement","children","mark","tone","processing","Swatch","cx","className","position","shape","mergeStyles","offset","space","style","cloneElement","joinIds","label"],"sources":["../../../../src/components/feedback/Indicator/Indicator.tsx"],"sourcesContent":["import {\n cloneElement,\n forwardRef,\n isValidElement,\n useId,\n type CSSProperties,\n type HTMLAttributes,\n type ReactElement,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space, type Tone } from '#utils/tokens'\nimport { joinIds } from '#components/inputs/internal/refs'\nimport { Swatch, type SwatchColor } from '#components/display/Swatch'\nimport styles from './Indicator.module.css'\n\nexport type IndicatorPosition = 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end'\nexport type IndicatorShape = 'rect' | 'round' | 'avatar'\n\nexport interface IndicatorProps extends Omit<\n HTMLAttributes<HTMLSpanElement>,\n 'children' | 'color'\n> {\n /**\n * The element the mark is pinned to: an `IconButton`, an `Avatar`, a link. It must accept\n * `aria-describedby`, which carries `label`. Omit it to show the mark on its own, in the flow.\n */\n children?: ReactElement\n /**\n * What the mark means, said in full: \"3 unread messages\", \"Waiting for your answer\". It's added\n * to the child's accessible description, so the child is announced as \"Inbox, 3 unread messages\"\n * rather than a bare number. Required, because a dot or a number alone is not information.\n */\n label: string\n /** A number to show. Without it the mark is a dot. */\n count?: number\n /** The largest count shown in full. Above it the mark reads \"99+\". Default `99`. */\n max?: number\n /** Show the mark when `count` is `0`. Hidden by default, since nothing is waiting. */\n showZero?: boolean\n /** Hide the mark (and its description) without unmounting the child. */\n invisible?: boolean\n /** Colour of the mark, from the shared tone set. Default `accent`. */\n tone?: Tone\n /**\n * Draw the mark as a colour swatch dot instead: identity, like a team's colour on an avatar,\n * never status. A categorical slot (`1` to `8`) or a CSS colour from data. `count` and `tone`\n * don't apply.\n */\n swatch?: SwatchColor\n /** A slow ring swells out of the mark, for something in progress. It stills under reduced motion. */\n processing?: boolean\n /** Which corner, in the reading direction: `top-end` (default) is top right in English. */\n position?: IndicatorPosition\n /**\n * The child's outline, so the mark sits on its edge rather than in the empty corner of its box.\n * `rect` (default) for square-cornered children, `round` for circles (a round `IconButton`),\n * `avatar` to follow the theme's avatar radius.\n */\n shape?: IndicatorShape\n /** Move the mark in toward the child's centre by a space step, for a child with an empty edge. */\n offset?: Space\n}\n\nconst formatCount = (count: number, max: number) =>\n count > max ? `${String(max)}+` : String(count)\n\n/**\n * A count or a dot pinned to the corner of another element: unread messages on a button, a\n * waiting dot on an avatar.\n *\n * @remarks\n * `Indicator` wraps one element and places a small mark on its edge. With `count` it's a number,\n * capped at `max` (\"99+\"), and hidden at zero unless you pass `showZero`. Without it, it's a dot.\n * `invisible` hides the mark without moving or remounting the child.\n *\n * The mark is hidden from screen readers. What it means is `label`, which is added to the\n * child's accessible description, so give the child its own name (an `IconButton`'s `label`, an\n * `Avatar`'s `name`) and write `label` as the rest of the sentence.\n *\n * `position` is logical, so `top-end` is the top left in a right-to-left page. `shape` keeps the\n * mark on the outline of a round child instead of floating in the corner of its box, and `offset`\n * moves it in toward a child whose visible edge is inside its box, like a ghost `IconButton`.\n *\n * ## When it changes\n *\n * An `Indicator` doesn't announce changes: a count that speaks every time it ticks is noise.\n * Where a change matters, such as a reply arriving, announce it where it happens. `processing`\n * pulses a ring for something under way, and stills under reduced motion.\n *\n * @privateRemarks\n * The tone colours match StatusDot and Badge's solid fill, the pulse is LiveIndicator's, and a\n * `swatch` mark is the shared Swatch part, so the dots in the system stay one family.\n */\nexport const Indicator = forwardRef<HTMLSpanElement, IndicatorProps>(function Indicator(\n {\n children,\n label,\n count,\n max = 99,\n showZero = false,\n invisible = false,\n tone = 'accent',\n swatch,\n processing = false,\n position = 'top-end',\n shape = 'rect',\n offset,\n className,\n style,\n ...rest\n },\n ref,\n) {\n const labelId = useId()\n const hasCount = swatch === undefined && count !== undefined\n const shown = !invisible && !(hasCount && count === 0 && !showZero)\n const pinned = isValidElement<{ 'aria-describedby'?: string }>(children)\n\n const mark = shown ? (\n swatch !== undefined ? (\n <span\n className={styles.mark}\n data-kind=\"swatch\"\n data-processing={processing || undefined}\n aria-hidden=\"true\"\n >\n <Swatch color={swatch} className={styles.fill} />\n </span>\n ) : (\n <span\n className={styles.mark}\n data-kind={hasCount ? 'count' : 'dot'}\n data-tone={tone}\n data-processing={processing || undefined}\n aria-hidden=\"true\"\n >\n {hasCount ? formatCount(count, max) : null}\n </span>\n )\n ) : null\n\n return (\n <span\n ref={ref}\n className={cx(styles.indicator, className)}\n data-position={position}\n data-shape={shape}\n data-pinned={pinned || undefined}\n style={mergeStyles(\n offset ? ({ '--_offset': space(offset) } as CSSProperties) : undefined,\n style,\n )}\n {...rest}\n >\n {pinned\n ? cloneElement(children, {\n 'aria-describedby': joinIds(\n children.props['aria-describedby'],\n shown ? labelId : undefined,\n ),\n })\n : null}\n {mark}\n {shown ? (\n <span id={labelId} className={styles.label}>\n {label}\n </span>\n ) : null}\n </span>\n )\n})\n"],"mappings":";;;;;;;;;AAgEA,IAAME,KAAe,GAAe,MAClCC,IAAQC,IAAM,GAAG,OAAOA,CAAG,EAAE,KAAK,OAAOD,CAAK,GA6BnCE,IAAYC,EAA4C,SACnE,EACE,aACA,UACA,UACA,SAAM,IACN,cAAW,IACX,eAAY,IACZ,UAAO,UACP,WACA,gBAAa,IACb,cAAW,WACX,WAAQ,QACR,WACA,cACA,UACA,GAAGC,KAEL,GACA;CACA,IAAMC,IAAUC,EAAM,GAChBC,IAAWC,MAAW,KAAA,KAAaR,MAAU,KAAA,GAC7CS,IAAQ,CAACC,KAAa,EAAEH,KAAYP,MAAU,KAAK,CAACW,IACpDC,IAASC,EAAgDC,CAAQ,GAEjEC,IAAON,IACXD,MAAW,KAAA,IAUT,kBAAC,QAAD;EACE,WAAWX,EAAO;EAClB,aAAWU,IAAW,UAAU;EAChC,aAAWS;EACX,mBAAiBC,KAAc,KAAA;EAC/B,eAAY;EAEX,UAAAV,IAAWR,EAAYC,GAAOC,CAAG,IAAI;CAClC,CAAA,IAjBN,kBAAC,QAAD;EACE,WAAWJ,EAAO;EAClB,aAAU;EACV,mBAAiBoB,KAAc,KAAA;EAC/B,eAAY;EAEZ,UAAA,kBAACC,GAAD;GAAQ,OAAOV;GAAQ,WAAWX,EAAO;EAAO,CAAA;CAC5C,CAAA,IAYN;CAEJ,OACE,kBAAC,QAAD;EACO;EACL,WAAWsB,EAAGtB,EAAO,WAAWuB,CAAS;EACzC,iBAAeC;EACf,cAAYC;EACZ,eAAaV,KAAU,KAAA;EACvB,OAAOW,EACLC,IAAU,EAAE,aAAaC,EAAMD,CAAM,EAAE,IAAsB,KAAA,GAC7DE,CACF;EACA,GAAItB;EAVN,UAAA;GAYGQ,IACGe,EAAab,GAAU,EACrB,oBAAoBc,EAClBd,EAAS,MAAM,qBACfL,IAAQJ,IAAU,KAAA,CACpB,EACF,CAAC,IACD;GACHU;GACAN,IACC,kBAAC,QAAD;IAAM,IAAIJ;IAAS,WAAWR,EAAO;IAClC,UAAAgC;GACG,CAAA,IACJ;EACA;;AAEV,CAAC"}
@@ -0,0 +1,12 @@
1
+ //#region src/components/feedback/Indicator/Indicator.module.css
2
+ var e = "kiln-Indicator__indicator", t = "kiln-Indicator__mark", n = "kiln-Indicator__fill", r = "kiln-Indicator__ring", i = "kiln-Indicator__label", a = {
3
+ indicator: e,
4
+ mark: t,
5
+ fill: n,
6
+ ring: r,
7
+ label: i
8
+ };
9
+ //#endregion
10
+ export { a as default, n as fill, e as indicator, i as label, t as mark, r as ring };
11
+
12
+ //# sourceMappingURL=Indicator.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Indicator.module.js","names":["indicator","mark","fill","ring","label","Indicator_module_default"],"sources":["../../../../src/components/feedback/Indicator/Indicator.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --indicator-ring the gap drawn round the mark so it reads over its child,\n * default var(--color-surface)\n * --indicator-dot-size default var(--space-2)\n * --indicator-count-size height of a count, default var(--space-4)\n * --indicator-radius default var(--radius-chip)\n * --indicator-pulse-scale how far the processing ring swells, default 2.4\n *\n * The mark sits centred on the child's corner: the inset follows the child's outline (`shape`)\n * plus `offset`, and negative margins of half the mark's height centre it on that point. A count\n * grows inward from there, so it never needs a transform and RTL needs nothing extra.\n */\n.indicator {\n --_offset: 0px;\n --_inset: 0px;\n\n position: relative;\n display: inline-flex;\n flex: none;\n vertical-align: middle;\n}\n\n/* On a circle, the point of the outline nearest the corner is 1 − 1/√2 of the radius in. */\n.indicator[data-shape='round'] {\n --_inset: 14.645%;\n}\n.indicator[data-shape='avatar'] {\n --_inset: calc(var(--avatar-radius, var(--radius-avatar)) * 0.2929);\n}\n\n.mark {\n --_c: var(--color-accent);\n --_c-ink: var(--color-accent-ink);\n --_h: var(--indicator-dot-size, var(--space-2));\n --_edge: calc(var(--_inset) + var(--_offset));\n\n position: relative;\n display: inline-grid;\n place-items: center;\n box-sizing: border-box;\n min-inline-size: var(--_h);\n block-size: var(--_h);\n border-radius: var(--indicator-radius, var(--radius-chip));\n background: var(--_c);\n color: var(--_c-ink);\n box-shadow: 0 0 0 2px var(--indicator-ring, var(--color-surface));\n font-family: var(--font-numeric);\n font-stretch: var(--numeric-stretch);\n font-size: var(--text-2xs);\n font-weight: var(--label-weight);\n font-variant-numeric: tabular-nums lining-nums;\n line-height: 1;\n white-space: nowrap;\n pointer-events: none;\n}\n.mark[data-kind='count'] {\n --_h: var(--indicator-count-size, var(--space-4));\n\n padding-inline: calc(var(--_h) / 4);\n}\n.mark[data-kind='swatch'] {\n inline-size: var(--_h);\n background: none;\n}\n.mark > .fill {\n position: relative;\n inline-size: 100%;\n block-size: 100%;\n border-radius: inherit;\n}\n\n.indicator[data-pinned] > .mark {\n position: absolute;\n z-index: 1;\n}\n.indicator[data-position^='top'] > .mark {\n inset-block-start: var(--_edge);\n margin-block-start: calc(var(--_h) / -2);\n}\n.indicator[data-position^='bottom'] > .mark {\n inset-block-end: var(--_edge);\n margin-block-end: calc(var(--_h) / -2);\n}\n.indicator[data-position$='-start'] > .mark {\n inset-inline-start: var(--_edge);\n margin-inline-start: calc(var(--_h) / -2);\n}\n.indicator[data-position$='-end'] > .mark {\n inset-inline-end: var(--_edge);\n margin-inline-end: calc(var(--_h) / -2);\n}\n\n.mark[data-tone='neutral'] {\n --_c: var(--color-surface-inverse);\n --_c-ink: var(--color-ink-inverse);\n}\n.mark[data-tone='positive'] {\n --_c: var(--tone-positive);\n --_c-ink: var(--tone-positive-ink);\n}\n.mark[data-tone='caution'] {\n --_c: var(--tone-caution);\n --_c-ink: var(--tone-caution-ink);\n}\n.mark[data-tone='critical'] {\n --_c: var(--tone-critical);\n --_c-ink: var(--tone-critical-ink);\n}\n.mark[data-tone='info'] {\n --_c: var(--tone-info);\n --_c-ink: var(--tone-info-ink);\n}\n\n/* The same slow ring as LiveIndicator. */\n.mark[data-processing]:not([data-kind='swatch'])::after,\n.mark[data-processing] > .fill::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: currentColor;\n color: var(--_c);\n animation: ring calc(var(--dur-3) * 5) var(--ease-out) infinite;\n}\n.mark[data-processing] > .fill::after {\n background: inherit;\n}\n\n@keyframes ring {\n 0% {\n scale: 1;\n opacity: 0.5;\n }\n 70%,\n 100% {\n scale: var(--indicator-pulse-scale, 2.4);\n opacity: 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .mark[data-processing]:not([data-kind='swatch'])::after,\n .mark[data-processing] > .fill::after {\n animation: none;\n opacity: 0;\n }\n}\n\n/* Read through the child's aria-describedby; kept in the tree for reading order. */\n.label {\n position: absolute;\n inline-size: 1px;\n block-size: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n@media (forced-colors: active) {\n .mark:not([data-kind='swatch']) {\n border: 1px solid CanvasText;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { Indicator } from './Indicator.js';
2
+ export type { IndicatorProps, IndicatorPosition, IndicatorShape } from './Indicator.js';
@@ -0,0 +1,71 @@
1
+ export type ToastPosition = 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end';
2
+ export interface ToasterProps {
3
+ /** Where the toasts stack. Default `bottom-end`. */
4
+ position?: ToastPosition;
5
+ /**
6
+ * How many toasts show at once. Older ones beyond it wait, with their timers stopped, and come
7
+ * back as the newer ones close. Default 3.
8
+ */
9
+ max?: number;
10
+ /**
11
+ * Names the region for screen readers. `{hotkey}` is replaced with the shortcut. Default
12
+ * "Notifications ({hotkey})".
13
+ */
14
+ label?: string;
15
+ /** The keys that move focus to the toasts. Default `['F8']`. */
16
+ hotkey?: string[];
17
+ /** Accessible name of each toast's close button. Default "Dismiss". */
18
+ closeLabel?: string;
19
+ /** Portal target. Defaults to `document.body`. */
20
+ container?: HTMLElement | null;
21
+ /** Class on the region the toasts stack in. */
22
+ className?: string;
23
+ }
24
+ /**
25
+ * The region that shows toasts. Mount it once near the root, then raise toasts with `toast()` from
26
+ * anywhere.
27
+ *
28
+ * @remarks
29
+ * A toast tells someone that something happened away from where they're looking: a save
30
+ * finished, a background job failed, a link landed on the clipboard. For a message about the
31
+ * content beside it, use an {@link Alert | Alert} instead; it stays in the layout.
32
+ *
33
+ * ```tsx
34
+ * <ThemeProvider>
35
+ * <App />
36
+ * <Toaster />
37
+ * </ThemeProvider>
38
+ * ```
39
+ *
40
+ * Each toast has the same frame as an `Alert`: a hairline, the tone in its glyph and a short tab
41
+ * on the top edge. It can carry one action, such as Undo, and a close button. It closes on its
42
+ * own after a few seconds; `critical` toasts and loading ones stay until they're dismissed. The
43
+ * timer pauses while the pointer or keyboard focus is on the toasts, and while the window is in
44
+ * the background.
45
+ *
46
+ * Toasts are announced as they appear, and again when one changes (a loading toast turning into
47
+ * "Saved"). `critical` and `caution` toasts interrupt; the rest wait their turn. F8 moves focus to
48
+ * the region, Escape closes the focused toast, and a swipe toward the nearest edge dismisses one.
49
+ * The toasts take the theme of the place the `Toaster` is mounted, and slide in from that edge;
50
+ * with reduced motion they appear and go without moving. If more than one `Toaster` is mounted,
51
+ * the first one shows the toasts.
52
+ *
53
+ * ## Updating and dismissing
54
+ *
55
+ * `toast()` returns an id. `toast.update(id, options)` changes that toast, `toast.dismiss(id)`
56
+ * closes it, and `toast.dismiss()` closes every toast. Raising a toast with an `id` that's still on
57
+ * screen updates it rather than stacking a copy.
58
+ *
59
+ * `toast.promise(promise, { loading, success, error })` shows a loading toast, then turns it into
60
+ * the success or error message when the promise settles.
61
+ *
62
+ * @privateRemarks
63
+ * Radix Toast underneath (swipe, hotkey, pause, live announcements). Radix announces a toast's
64
+ * text once, on mount, so a changed message bumps `version` and remounts the toast under a new
65
+ * key; `data-updated` stops that remount from replaying the entrance. The viewport is portalled
66
+ * and copies the theme of a hidden anchor where the Toaster sits (usePortalTheme).
67
+ *
68
+ * <Toaster position="bottom-end" />
69
+ * toast({ title: 'Invoice sent', tone: 'positive' })
70
+ */
71
+ export declare function Toaster({ position, max, label, hotkey, closeLabel, container, className, }: ToasterProps): import("react").JSX.Element | null;