@upbound/monarch-blocks 0.7.0 → 0.7.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/code-block.tsx","../src/lib/utils.ts","../src/lib/highlight.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport { Icon } from '@upbound/monarch-core';\n\nimport { cn } from '@/lib/utils';\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '@upbound/monarch-core';\nimport { highlightCode, type CodeBlockLanguage } from './lib/highlight';\n\nexport type { CodeBlockLanguage } from './lib/highlight';\n\ntype CodeBlockCodeBase = {\n /** Raw code string — used for clipboard copy, download, and as the rendered text. */\n content: string;\n /** Numbers each line in the gutter. */\n lineNumbers?: boolean;\n};\n\nexport type CodeBlockCode =\n | (CodeBlockCodeBase & {\n /** Syntax-highlighted with Shiki inside CodeBlockBody — no highlighting step needed on your end. */\n type: 'parsed';\n language: CodeBlockLanguage;\n })\n | (CodeBlockCodeBase & {\n /** Rendered as plain, unhighlighted text. */\n type: 'raw';\n });\n\nfunction CodeBlock({\n className,\n variant = 'default',\n size = 'default',\n ...props\n}: React.ComponentProps<'div'> & {\n variant?: 'default' | 'terminal';\n size?: 'default' | 'sm';\n}) {\n return (\n <Card\n data-slot=\"code-block\"\n data-variant={variant}\n data-size={size}\n role=\"region\"\n className={cn(\n 'group/code-block gap-0 overflow-hidden py-0',\n size === 'default' && 'text-[13px]',\n size === 'sm' && 'text-body-sm',\n variant === 'terminal' && 'border-terminal-border bg-terminal-bg text-terminal-fg ring-0',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockHeader({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardHeader\n className={cn(\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted flex flex-row items-center gap-2 border-b bg-[color-mix(in_oklch,var(--muted)_50%,var(--card))] py-4',\n className,\n )}\n {...props}\n />\n );\n}\n\nconst LINE_NUMBER_GUTTER = cn(\n '[&_code]:[counter-reset:line]',\n '[&_.line]:before:[counter-increment:line]',\n '[&_.line]:before:content-[counter(line)]',\n '[&_.line]:before:inline-block',\n '[&_.line]:before:w-6',\n '[&_.line]:before:mr-3',\n '[&_.line]:before:text-right',\n '[&_.line]:before:text-muted-foreground',\n '[&_.line]:before:text-[0.75rem]',\n '[&_.line]:before:select-none',\n);\n\nfunction CodeBlockBody({\n className,\n code,\n maxHeight,\n copyButton,\n downloadButton,\n ...props\n}: Omit<React.ComponentProps<'div'>, 'children'> & {\n code: CodeBlockCode;\n maxHeight?: string;\n copyButton?: React.ReactNode;\n downloadButton?: React.ReactNode;\n}) {\n const { content, lineNumbers } = code;\n const [html, setHtml] = useState<string | null>(null);\n const language = code.type === 'parsed' ? code.language : null;\n\n useEffect(() => {\n if (!language) {\n setHtml(null);\n return;\n }\n\n let cancelled = false;\n highlightCode(content, language).then(result => {\n if (!cancelled) setHtml(result);\n });\n\n return () => {\n cancelled = true;\n };\n }, [content, language]);\n\n // Layout note: the copy/download buttons must stay anchored to the\n // top-right of the visible code area when the content scrolls\n // horizontally. To make that work the relative containing block\n // (CardContent) must NOT be the scrollable element — otherwise\n // `right`-positioning is computed against the scrolled content edge and\n // the buttons move with the scroll. So the overflow lives on an inner\n // wrapper and the absolute button group is its sibling under CardContent.\n return (\n <CardContent data-line-numbers={lineNumbers || undefined} className={cn('relative p-0!', className)} {...props}>\n {(copyButton || downloadButton) && (\n <div className=\"absolute top-2 right-2 z-10 flex items-center gap-1 opacity-0 transition-opacity group-hover/code-block:opacity-100\">\n {copyButton}\n {downloadButton}\n </div>\n )}\n <div\n className={cn('overflow-x-auto', maxHeight && 'overflow-y-auto')}\n style={maxHeight ? { maxHeight } : undefined}\n >\n <div\n className={cn(\n '[&_code]:font-mono [&_pre]:m-0 [&_pre]:p-4 group-data-[size=sm]/code-block:[&_pre]:p-3',\n html && '[&_pre]:bg-transparent!',\n lineNumbers && LINE_NUMBER_GUTTER,\n )}\n >\n {html ? (\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre>\n <code className={lineNumbers ? undefined : 'whitespace-pre'}>\n {lineNumbers\n ? content.split('\\n').map((line, i) => (\n <span key={i} className=\"line\">\n {line}\n {'\\n'}\n </span>\n ))\n : content}\n </code>\n </pre>\n )}\n </div>\n </div>\n </CardContent>\n );\n}\n\nfunction CodeBlockFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardFooter\n className={cn(\n 'text-body-sm group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-footer-bg group-data-[variant=terminal]/code-block:text-terminal-muted',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockCopyButton({\n code,\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n}) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-copy-button\"\n onClick={handleCopy}\n className={cn(\n 'border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors',\n 'text-muted-foreground hover:bg-accent hover:text-foreground',\n 'focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none',\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg',\n className,\n )}\n aria-label={copied ? 'Copied' : 'Copy code'}\n {...props}\n >\n {copied ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"copy\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{copied ? 'Copied' : 'Copy'}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CodeBlockDownloadButton({\n code,\n fileName,\n mimeType = 'text/plain',\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n fileName: string;\n mimeType?: string;\n}) {\n const [downloaded, setDownloaded] = useState(false);\n\n const handleDownload = () => {\n const blob = new Blob([code], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n setDownloaded(true);\n setTimeout(() => setDownloaded(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-download-button\"\n onClick={handleDownload}\n className={cn(\n 'border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors',\n 'text-muted-foreground hover:bg-accent hover:text-foreground',\n 'focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none',\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg',\n className,\n )}\n aria-label={downloaded ? 'Downloaded' : 'Download code'}\n {...props}\n >\n {downloaded ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"download\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{downloaded ? 'Downloaded' : 'Download'}</TooltipContent>\n </Tooltip>\n );\n}\n\nexport { CodeBlock, CodeBlockHeader, CodeBlockBody, CodeBlockFooter, CodeBlockCopyButton, CodeBlockDownloadButton };\n","import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [\n {\n text: [\n 'display-hero',\n 'display-kpi-sm',\n 'display-kpi',\n 'display-kpi-lg',\n 'display-feature',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'body-lg',\n 'body',\n 'body-sm',\n 'caption',\n 'eyebrow',\n ],\n },\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport function sortBy<T>(items: T[], getKey: (item: T) => string): T[] {\n return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));\n}\n","import { createHighlighter, type Highlighter } from 'shiki';\n\nexport const CODE_BLOCK_LANGUAGES = ['tsx', 'typescript', 'bash', 'shell', 'json', 'css', 'yaml'] as const;\n\nexport type CodeBlockLanguage = (typeof CODE_BLOCK_LANGUAGES)[number];\n\nconst HIGHLIGHTER_KEY = '__monarch_shiki_highlighter__';\n\ntype HighlighterGlobal = typeof globalThis & {\n [HIGHLIGHTER_KEY]?: Promise<Highlighter>;\n};\n\n/**\n * Cache the in-flight Promise, not the resolved instance. `createHighlighter`\n * is async — storing only after `await` lets every concurrent CodeBlockBody\n * start a new highlighter (Shiki warns at 10/20/30 instances). `globalThis`\n * keeps the same promise across HMR module re-evals in the same realm.\n */\nfunction getHighlighter(): Promise<Highlighter> {\n const g = globalThis as HighlighterGlobal;\n if (!g[HIGHLIGHTER_KEY]) {\n g[HIGHLIGHTER_KEY] = createHighlighter({\n themes: ['github-light-high-contrast', 'github-dark-dimmed'],\n langs: [...CODE_BLOCK_LANGUAGES],\n }).catch(error => {\n delete g[HIGHLIGHTER_KEY];\n throw error;\n });\n }\n return g[HIGHLIGHTER_KEY];\n}\n\n/**\n * Highlight a code string with Shiki dual themes. Returns raw HTML safe to\n * render via dangerouslySetInnerHTML.\n *\n * Light theme renders as inline `color:` / `background-color:`.\n * Dark theme renders as CSS variables `--shiki-dark` / `--shiki-dark-bg`.\n */\nexport async function highlightCode(code: string, lang: CodeBlockLanguage): Promise<string> {\n const hl = await getHighlighter();\n return hl.codeToHtml(code, {\n lang,\n themes: {\n light: 'github-light-high-contrast',\n dark: 'github-dark-dimmed',\n },\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/code-block.tsx","../src/lib/utils.ts","../src/lib/highlight.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport { Icon } from '@upbound/monarch-core';\n\nimport { cn } from '@/lib/utils';\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '@upbound/monarch-core';\nimport { highlightCode, type CodeBlockLanguage } from './lib/highlight';\n\nexport type { CodeBlockLanguage } from './lib/highlight';\n\ntype CodeBlockCodeBase = {\n /** Raw code string — used for clipboard copy, download, and as the rendered text. */\n content: string;\n /** Numbers each line in the gutter. */\n lineNumbers?: boolean;\n};\n\nexport type CodeBlockCode =\n | (CodeBlockCodeBase & {\n /** Syntax-highlighted with Shiki inside CodeBlockBody — no highlighting step needed on your end. */\n type: 'parsed';\n language: CodeBlockLanguage;\n })\n | (CodeBlockCodeBase & {\n /** Rendered as plain, unhighlighted text. */\n type: 'raw';\n });\n\nfunction CodeBlock({\n className,\n variant = 'default',\n size = 'default',\n ...props\n}: React.ComponentProps<'div'> & {\n variant?: 'default' | 'terminal';\n size?: 'default' | 'sm';\n}) {\n return (\n <Card\n data-slot=\"code-block\"\n data-variant={variant}\n data-size={size}\n role=\"region\"\n className={cn(\n 'group/code-block gap-0 overflow-hidden py-0',\n size === 'default' && 'text-[13px]',\n size === 'sm' && 'text-body-sm',\n variant === 'terminal' && 'border-terminal-border bg-terminal-bg text-terminal-fg ring-0',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockHeader({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardHeader\n className={cn(\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted flex flex-row items-center gap-2 border-b bg-[color-mix(in_oklch,var(--muted)_50%,var(--card))] py-4',\n className,\n )}\n {...props}\n />\n );\n}\n\nconst LINE_NUMBER_GUTTER = cn(\n '[&_code]:[counter-reset:line]',\n '[&_.line]:before:[counter-increment:line]',\n '[&_.line]:before:content-[counter(line)]',\n '[&_.line]:before:inline-block',\n '[&_.line]:before:w-6',\n '[&_.line]:before:mr-3',\n '[&_.line]:before:text-right',\n '[&_.line]:before:text-muted-foreground',\n '[&_.line]:before:text-[0.75rem]',\n '[&_.line]:before:select-none',\n);\n\nfunction CodeBlockBody({\n className,\n code,\n maxHeight,\n copyButton,\n downloadButton,\n ...props\n}: Omit<React.ComponentProps<'div'>, 'children'> & {\n code: CodeBlockCode;\n maxHeight?: string;\n copyButton?: React.ReactNode;\n downloadButton?: React.ReactNode;\n}) {\n const { content, lineNumbers } = code;\n const [html, setHtml] = useState<string | null>(null);\n const language = code.type === 'parsed' ? code.language : null;\n\n useEffect(() => {\n if (!language) {\n setHtml(null);\n return;\n }\n\n let cancelled = false;\n highlightCode(content, language).then(result => {\n if (!cancelled) setHtml(result);\n });\n\n return () => {\n cancelled = true;\n };\n }, [content, language]);\n\n // Layout note: the copy/download buttons must stay anchored to the\n // top-right of the visible code area when the content scrolls\n // horizontally. To make that work the relative containing block\n // (CardContent) must NOT be the scrollable element — otherwise\n // `right`-positioning is computed against the scrolled content edge and\n // the buttons move with the scroll. So the overflow lives on an inner\n // wrapper and the absolute button group is its sibling under CardContent.\n return (\n <CardContent data-line-numbers={lineNumbers || undefined} className={cn('relative p-0!', className)} {...props}>\n {(copyButton || downloadButton) && (\n <div className=\"absolute top-2 right-2 z-10 flex items-center gap-1 opacity-0 transition-opacity group-hover/code-block:opacity-100\">\n {copyButton}\n {downloadButton}\n </div>\n )}\n <div\n className={cn('overflow-x-auto', maxHeight && 'overflow-y-auto')}\n style={maxHeight ? { maxHeight } : undefined}\n >\n <div\n className={cn(\n '[&_code]:font-mono [&_pre]:m-0 [&_pre]:p-4 group-data-[size=sm]/code-block:[&_pre]:p-3',\n html && '[&_pre]:bg-transparent!',\n lineNumbers && LINE_NUMBER_GUTTER,\n )}\n >\n {html ? (\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre>\n <code className={lineNumbers ? undefined : 'whitespace-pre'}>\n {lineNumbers\n ? content.split('\\n').map((line, i) => (\n <span key={i} className=\"line\">\n {line}\n {'\\n'}\n </span>\n ))\n : content}\n </code>\n </pre>\n )}\n </div>\n </div>\n </CardContent>\n );\n}\n\nfunction CodeBlockFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardFooter\n className={cn(\n 'text-body-sm group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-footer-bg group-data-[variant=terminal]/code-block:text-terminal-muted',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockCopyButton({\n code,\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n}) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-copy-button\"\n onClick={handleCopy}\n className={cn(\n 'border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors',\n 'text-muted-foreground hover:bg-accent hover:text-foreground',\n 'focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none',\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg',\n className,\n )}\n aria-label={copied ? 'Copied' : 'Copy code'}\n {...props}\n >\n {copied ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"copy\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{copied ? 'Copied' : 'Copy'}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CodeBlockDownloadButton({\n code,\n fileName,\n mimeType = 'text/plain',\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n fileName: string;\n mimeType?: string;\n}) {\n const [downloaded, setDownloaded] = useState(false);\n\n const handleDownload = () => {\n const blob = new Blob([code], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n setDownloaded(true);\n setTimeout(() => setDownloaded(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-download-button\"\n onClick={handleDownload}\n className={cn(\n 'border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors',\n 'text-muted-foreground hover:bg-accent hover:text-foreground',\n 'focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none',\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg',\n className,\n )}\n aria-label={downloaded ? 'Downloaded' : 'Download code'}\n {...props}\n >\n {downloaded ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"download\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{downloaded ? 'Downloaded' : 'Download'}</TooltipContent>\n </Tooltip>\n );\n}\n\nexport { CodeBlock, CodeBlockHeader, CodeBlockBody, CodeBlockFooter, CodeBlockCopyButton, CodeBlockDownloadButton };\n","export { cn } from '@upbound/monarch-core/utils';\n\nexport function sortBy<T>(items: T[], getKey: (item: T) => string): T[] {\n return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));\n}\n","import { createHighlighter, type Highlighter } from 'shiki';\n\nexport const CODE_BLOCK_LANGUAGES = ['tsx', 'typescript', 'bash', 'shell', 'json', 'css', 'yaml'] as const;\n\nexport type CodeBlockLanguage = (typeof CODE_BLOCK_LANGUAGES)[number];\n\nconst HIGHLIGHTER_KEY = '__monarch_shiki_highlighter__';\n\ntype HighlighterGlobal = typeof globalThis & {\n [HIGHLIGHTER_KEY]?: Promise<Highlighter>;\n};\n\n/**\n * Cache the in-flight Promise, not the resolved instance. `createHighlighter`\n * is async — storing only after `await` lets every concurrent CodeBlockBody\n * start a new highlighter (Shiki warns at 10/20/30 instances). `globalThis`\n * keeps the same promise across HMR module re-evals in the same realm.\n */\nfunction getHighlighter(): Promise<Highlighter> {\n const g = globalThis as HighlighterGlobal;\n if (!g[HIGHLIGHTER_KEY]) {\n g[HIGHLIGHTER_KEY] = createHighlighter({\n themes: ['github-light-high-contrast', 'github-dark-dimmed'],\n langs: [...CODE_BLOCK_LANGUAGES],\n }).catch(error => {\n delete g[HIGHLIGHTER_KEY];\n throw error;\n });\n }\n return g[HIGHLIGHTER_KEY];\n}\n\n/**\n * Highlight a code string with Shiki dual themes. Returns raw HTML safe to\n * render via dangerouslySetInnerHTML.\n *\n * Light theme renders as inline `color:` / `background-color:`.\n * Dark theme renders as CSS variables `--shiki-dark` / `--shiki-dark-bg`.\n */\nexport async function highlightCode(code: string, lang: CodeBlockLanguage): Promise<string> {\n const hl = await getHighlighter();\n return hl.codeToHtml(code, {\n lang,\n themes: {\n light: 'github-light-high-contrast',\n dark: 'github-dark-dimmed',\n },\n });\n}\n"],"mappings":"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","names":[]}
@@ -256,7 +256,12 @@ interface DataTableProps<TData extends RowData> {
256
256
  className?: string;
257
257
  /** Table layout mode. 'auto' uses native HTML table layout, 'grid' uses CSS Grid with div elements. @default 'auto' */
258
258
  layout?: TableLayout;
259
- /** When true, table height fits content instead of using viewport-based height constraints. @default false */
259
+ /**
260
+ * When true, table height fits content instead of using viewport-based height constraints.
261
+ * The row scroll area then grows with the rows, so `stickyHeader` has no effect — a sticky
262
+ * header needs a scroll area that is shorter than its content to stick against.
263
+ * @default false
264
+ */
260
265
  fitContent?: boolean;
261
266
  serverSidePagination?: boolean;
262
267
  onRowRender?(row: TData): DataTableRowProps;
@@ -312,6 +317,10 @@ interface DataTableProps<TData extends RowData> {
312
317
  /**
313
318
  * Enable sticky column headers within the table scroll area.
314
319
  * Pass `true` for defaults, or an object to customise offset / row height.
320
+ *
321
+ * This caps the scroll area at a viewport-relative height so the rows scroll under the
322
+ * header. `fitContent` removes that cap, so the two props do not work together: with
323
+ * `fitContent` the table grows to its content and the header does not stick.
315
324
  */
316
325
  stickyHeader?: StickyHeaderConfig;
317
326
  /** When set, renders the CEL filter builder in the filter row instead of column filters. */
@@ -129,37 +129,7 @@ import {
129
129
  import { Icon } from "@upbound/monarch-core";
130
130
 
131
131
  // src/lib/utils.ts
132
- import { clsx } from "clsx";
133
- import { extendTailwindMerge } from "tailwind-merge";
134
- var twMerge = extendTailwindMerge({
135
- extend: {
136
- classGroups: {
137
- "font-size": [
138
- {
139
- text: [
140
- "display-hero",
141
- "display-kpi-sm",
142
- "display-kpi",
143
- "display-kpi-lg",
144
- "display-feature",
145
- "h1",
146
- "h2",
147
- "h3",
148
- "h4",
149
- "body-lg",
150
- "body",
151
- "body-sm",
152
- "caption",
153
- "eyebrow"
154
- ]
155
- }
156
- ]
157
- }
158
- }
159
- });
160
- function cn(...inputs) {
161
- return twMerge(clsx(inputs));
162
- }
132
+ import { cn } from "@upbound/monarch-core/utils";
163
133
  function sortBy(items, getKey) {
164
134
  return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));
165
135
  }
@@ -167,10 +137,9 @@ function sortBy(items, getKey) {
167
137
  // src/lib/data-table/utils.ts
168
138
  var TO_EACH_SIDE = 2;
169
139
  function normalizeColumnOrdering(columnOrdering, allColumnIds) {
170
- const validIds = new Set(allColumnIds);
171
140
  const seenIds = /* @__PURE__ */ new Set();
172
141
  const trackedColumnIds = columnOrdering.filter((columnId) => {
173
- if (!validIds.has(columnId) || seenIds.has(columnId)) return false;
142
+ if (seenIds.has(columnId)) return false;
174
143
  seenIds.add(columnId);
175
144
  return true;
176
145
  });
@@ -1825,6 +1794,7 @@ function CelAddFilterButton({
1825
1794
  items: groups,
1826
1795
  itemToStringValue: (item) => item.title,
1827
1796
  itemToStringLabel: (item) => item.title,
1797
+ value: null,
1828
1798
  onValueChange: (column) => {
1829
1799
  if (column) onSelect(column.id);
1830
1800
  },
@@ -3874,6 +3844,7 @@ function FilterBar(_a) {
3874
3844
  items: availableFilters,
3875
3845
  itemToStringValue: (item) => item.label,
3876
3846
  itemToStringLabel: (item) => item.label,
3847
+ value: null,
3877
3848
  onValueChange: (filter) => {
3878
3849
  if (filter) handleAddFilter(filter.key);
3879
3850
  },
@@ -4434,6 +4405,7 @@ function SectionHeaderDescription(_a) {
4434
4405
  // src/data-table/data-table.tsx
4435
4406
  import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
4436
4407
  var DEFAULT_PAGE_SIZE = 20;
4408
+ var STICKY_HEADER_MAX_HEIGHT_CLASS = "max-h-[calc(100svh-250px)]";
4437
4409
  function DataTable({
4438
4410
  id,
4439
4411
  config,
@@ -4771,9 +4743,10 @@ function DataTable({
4771
4743
  "div",
4772
4744
  {
4773
4745
  ref: tableBodyScrollRef,
4746
+ "data-testid": "table-scroll-container",
4774
4747
  className: cn(
4775
4748
  "min-h-0 flex-1 overflow-auto rounded-lg border **:data-[slot=table-container]:overflow-visible",
4776
- !!stickyHeader && "max-h-[calc(100svh-250px)]"
4749
+ !!stickyHeader && !fitContent && STICKY_HEADER_MAX_HEIGHT_CLASS
4777
4750
  ),
4778
4751
  children: /* @__PURE__ */ jsxs10(
4779
4752
  DataTableRoot,