@keboola/design 11.6.0 → 11.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/{AIKit-BExvt9pt.js → AIKit-CQxVwyAk.js} +7 -7
- package/dist/{AIKit-BExvt9pt.js.map → AIKit-CQxVwyAk.js.map} +1 -1
- package/dist/{Alert-Ba5b2YPq.js → Alert-C4Y9IwMI.js} +2 -2
- package/dist/{Alert-Ba5b2YPq.js.map → Alert-C4Y9IwMI.js.map} +1 -1
- package/dist/{Badge-D0pXdekt.js → Badge-o5jURtpB.js} +2 -2
- package/dist/{Badge-D0pXdekt.js.map → Badge-o5jURtpB.js.map} +1 -1
- package/dist/{BrandEditor-CSANsxUz.js → BrandEditor-CS9P4r2p.js} +5 -5
- package/dist/{BrandEditor-CSANsxUz.js.map → BrandEditor-CS9P4r2p.js.map} +1 -1
- package/dist/{Breadcrumb-g_xn_-Nf.js → Breadcrumb-D1id7YXd.js} +2 -2
- package/dist/{Breadcrumb-g_xn_-Nf.js.map → Breadcrumb-D1id7YXd.js.map} +1 -1
- package/dist/{Checkbox-CUIn2jap.js → Checkbox-Bqr6zXcW.js} +2 -2
- package/dist/{Checkbox-CUIn2jap.js.map → Checkbox-Bqr6zXcW.js.map} +1 -1
- package/dist/{Clipboard-BTW6C-b_.js → Clipboard-C2YBRfD8.js} +2 -2
- package/dist/{Clipboard-BTW6C-b_.js.map → Clipboard-C2YBRfD8.js.map} +1 -1
- package/dist/{ClipboardButton-CwZJdRaz.js → ClipboardButton-CrljClL1.js} +2 -2
- package/dist/{ClipboardButton-CwZJdRaz.js.map → ClipboardButton-CrljClL1.js.map} +1 -1
- package/dist/{CodeEditor-C9zy7PxK.js → CodeEditor-C9AS8Ch0.js} +4 -4
- package/dist/{CodeEditor-C9zy7PxK.js.map → CodeEditor-C9AS8Ch0.js.map} +1 -1
- package/dist/{ColorInput-BZV-u_94.js → ColorInput-Dg87lKcR.js} +2 -2
- package/dist/{ColorInput-BZV-u_94.js.map → ColorInput-Dg87lKcR.js.map} +1 -1
- package/dist/{Combobox-BfIfaVMA.js → Combobox-BfanUAqY.js} +3 -3
- package/dist/{Combobox-BfIfaVMA.js.map → Combobox-BfanUAqY.js.map} +1 -1
- package/dist/{Command-Bfa1kpbt.js → Command-CqxoI_De.js} +2 -2
- package/dist/{Command-Bfa1kpbt.js.map → Command-CqxoI_De.js.map} +1 -1
- package/dist/{CopyField-DpT0Ngz1.js → CopyField-Rx9HPTbJ.js} +2 -2
- package/dist/{CopyField-DpT0Ngz1.js.map → CopyField-Rx9HPTbJ.js.map} +1 -1
- package/dist/{DateInput-DiEd5uCL.js → DateInput-DpifECUQ.js} +2 -2
- package/dist/{DateInput-DiEd5uCL.js.map → DateInput-DpifECUQ.js.map} +1 -1
- package/dist/{DateRangeInput-DPfVYchl.js → DateRangeInput-9MIw7P6L.js} +2 -2
- package/dist/{DateRangeInput-DPfVYchl.js.map → DateRangeInput-9MIw7P6L.js.map} +1 -1
- package/dist/{FieldItem-B7XMmZSz.js → FieldItem-DtPTzNef.js} +3 -3
- package/dist/{FieldItem-B7XMmZSz.js.map → FieldItem-DtPTzNef.js.map} +1 -1
- package/dist/{FileUpload-ABThVZyg.js → FileUpload-DYQCTGjU.js} +3 -3
- package/dist/{FileUpload-ABThVZyg.js.map → FileUpload-DYQCTGjU.js.map} +1 -1
- package/dist/{Form-CsttTHn4.js → Form-DAaatlBk.js} +5 -5
- package/dist/{Form-CsttTHn4.js.map → Form-DAaatlBk.js.map} +1 -1
- package/dist/{FormGroup-DHvTFdno.js → FormGroup-Bl8JmnzJ.js} +3 -3
- package/dist/{FormGroup-DHvTFdno.js.map → FormGroup-Bl8JmnzJ.js.map} +1 -1
- package/dist/{Kbd-Cvvt91Q8.js → Kbd-D1Azcc4g.js} +2 -2
- package/dist/{Kbd-Cvvt91Q8.js.map → Kbd-D1Azcc4g.js.map} +1 -1
- package/dist/{MermaidDiagram-DSI7xMu2.js → MermaidDiagram-CujFxVzk.js} +2 -2
- package/dist/{MermaidDiagram-DSI7xMu2.js.map → MermaidDiagram-CujFxVzk.js.map} +1 -1
- package/dist/{MultiActionButtonGroup-DokHbPgM.js → MultiActionButtonGroup-CNNoBQbv.js} +2 -2
- package/dist/{MultiActionButtonGroup-DokHbPgM.js.map → MultiActionButtonGroup-CNNoBQbv.js.map} +1 -1
- package/dist/{PageToolbar-DExqEYGl.js → PageToolbar-BKVJiDAL.js} +2 -2
- package/dist/{PageToolbar-DExqEYGl.js.map → PageToolbar-BKVJiDAL.js.map} +1 -1
- package/dist/{RadioGroup-DXPbSj8P.js → RadioGroup-CUBsuP7r.js} +2 -2
- package/dist/{RadioGroup-DXPbSj8P.js.map → RadioGroup-CUBsuP7r.js.map} +1 -1
- package/dist/{Search-5uQAiNIP.js → Search-DVpJuzr9.js} +2 -2
- package/dist/{Search-5uQAiNIP.js.map → Search-DVpJuzr9.js.map} +1 -1
- package/dist/{SearchInput-D4UgAXJK.js → SearchInput-Q85p8Ews.js} +2 -2
- package/dist/{SearchInput-D4UgAXJK.js.map → SearchInput-Q85p8Ews.js.map} +1 -1
- package/dist/{SectionNav-OkKCjSA-.js → SectionNav-mpmSM3OC.js} +3 -3
- package/dist/{SectionNav-OkKCjSA-.js.map → SectionNav-mpmSM3OC.js.map} +1 -1
- package/dist/{Sidebar-D3cY1kha.js → Sidebar-CTWsHZhF.js} +2 -2
- package/dist/{Sidebar-D3cY1kha.js.map → Sidebar-CTWsHZhF.js.map} +1 -1
- package/dist/{Slider-CRf-bB4y.js → Slider-Bv0B6lI0.js} +2 -2
- package/dist/{Slider-CRf-bB4y.js.map → Slider-Bv0B6lI0.js.map} +1 -1
- package/dist/{TextInput-DH5SkbsE.js → TextInput-DiEMxVF6.js} +2 -2
- package/dist/{TextInput-DH5SkbsE.js.map → TextInput-DiEMxVF6.js.map} +1 -1
- package/dist/{ThemeProvider-2RMdVGxO.js → ThemeProvider-BI6JWBdJ.js} +2 -2
- package/dist/{ThemeProvider-2RMdVGxO.js.map → ThemeProvider-BI6JWBdJ.js.map} +1 -1
- package/dist/{ZoomableSvg-pooPb1Cu.js → ZoomableSvg-U_VH3QRO.js} +3 -3
- package/dist/{ZoomableSvg-pooPb1Cu.js.map → ZoomableSvg-U_VH3QRO.js.map} +1 -1
- package/dist/editors.js +2 -2
- package/dist/flint.js +1 -1
- package/dist/index.js +30 -30
- package/dist/internal/blocks/AIKit.js +1 -1
- package/dist/internal/blocks/BrandEditor.js +1 -1
- package/dist/internal/blocks/ClipboardButton.js +1 -1
- package/dist/internal/blocks/ColorInput.js +1 -1
- package/dist/internal/blocks/Command.js +1 -1
- package/dist/internal/blocks/CopyField.js +1 -1
- package/dist/internal/blocks/DateInput.js +1 -1
- package/dist/internal/blocks/DateRangeInput.js +1 -1
- package/dist/internal/blocks/ExportMenu.js +1 -1
- package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +1 -1
- package/dist/internal/blocks/Form.js +1 -1
- package/dist/internal/blocks/FormGroup.js +1 -1
- package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
- package/dist/internal/blocks/MultiCombobox.js +1 -1
- package/dist/internal/blocks/PageToolbar.js +1 -1
- package/dist/internal/blocks/PasswordInput.js +1 -1
- package/dist/internal/blocks/SearchInput.js +1 -1
- package/dist/internal/blocks/SearchInputWithNavigation.js +2 -2
- package/dist/internal/blocks/SectionNav.js +1 -1
- package/dist/internal/blocks/TextInput.js +1 -1
- package/dist/internal/blocks/TimeInput.js +1 -1
- package/dist/internal/components/Alert.js +1 -1
- package/dist/internal/components/Avatar.js +1 -1
- package/dist/internal/components/Badge.js +1 -1
- package/dist/internal/components/Breadcrumb.js +1 -1
- package/dist/internal/components/Checkbox.js +1 -1
- package/dist/internal/components/Clipboard.js +1 -1
- package/dist/internal/components/CollapsiblePanel.js +1 -1
- package/dist/internal/components/Combobox.js +1 -1
- package/dist/internal/components/FileUpload.js +1 -1
- package/dist/internal/components/Kbd.js +1 -1
- package/dist/internal/components/RadioGroup.js +1 -1
- package/dist/internal/components/Search.js +1 -1
- package/dist/internal/components/Sidebar.js +1 -1
- package/dist/internal/components/Slider.js +1 -1
- package/dist/internal/components/ThemeProvider.js +1 -1
- package/dist/internal/components/Tooltip.js +1 -1
- package/dist/internal/components/Truncated.js +1 -1
- package/dist/internal/components/ZoomableSvg.js +1 -1
- package/dist/internal/utils.js +1 -1
- package/dist/markdown-editor.js +4 -4
- package/dist/mermaid.js +1 -1
- package/dist/rjsf.js +9 -9
- package/dist/styles.css +1 -1
- package/dist/testing.js +1 -1
- package/dist/{utils-DSvrhU8E.js → utils-DnkrFYqY.js} +3 -5
- package/dist/utils-DnkrFYqY.js.map +1 -0
- package/package.json +13 -13
- package/dist/utils-DSvrhU8E.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cn } from "./classNames-BzrkDnOD.js";
|
|
2
|
-
import { Tooltip } from "./utils-
|
|
2
|
+
import { Tooltip } from "./utils-DnkrFYqY.js";
|
|
3
3
|
import { useArgoDesign } from "./context-D7X470dy.js";
|
|
4
4
|
import { AlertTriangle, ChevronDown, CircleCheck, Info } from "./icons-Cek151jC.js";
|
|
5
5
|
import { Collapsible } from "./Collapsible-BQXWEOBC.js";
|
|
@@ -130,4 +130,4 @@ const Alert = ({ title, variant = "info", children, hideIcon = false, icon, clas
|
|
|
130
130
|
//#endregion
|
|
131
131
|
export { Alert };
|
|
132
132
|
|
|
133
|
-
//# sourceMappingURL=Alert-
|
|
133
|
+
//# sourceMappingURL=Alert-C4Y9IwMI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert-Ba5b2YPq.js","names":[],"sources":["../src/components/Alert/AlertAction.tsx","../src/components/Alert/AlertCollapsibleIcon.tsx","../src/components/Alert/AlertDescription.tsx","../src/components/Alert/AlertIcon.tsx","../src/components/Alert/AlertRoot.tsx","../src/components/Alert/AlertTitle.tsx","../src/components/Alert/Alert.tsx"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'div'> & {\n asChild?: boolean;\n};\n\nexport const AlertAction = ({ ref, className, asChild = false, ...props }: Props) => {\n const Comp = asChild ? Slot : 'div';\n return (\n <Comp\n {...props}\n ref={ref}\n className={cn(\n 'tw:col-start-3',\n 'tw:row-start-1',\n 'tw:self-start',\n 'tw:flex',\n 'tw:gap-2',\n className,\n )}\n />\n );\n};\n","import { ChevronDown } from '../../icons';\nimport { cn } from '../../utils';\nimport { Tooltip } from '../Tooltip';\n\ntype Props = {\n open: boolean;\n};\n\nexport const AlertCollapsibleIcon = ({ open }: Props) => (\n <Tooltip placement=\"top\" tooltip={open ? 'Collapse alert' : 'Expand alert'} triggerAsChild>\n <ChevronDown\n className={cn(\n 'tw:mt-px',\n 'tw:size-5',\n 'tw:text-neutral-400',\n 'tw:group-data-[state=open]/alert:rotate-180',\n\n // Legacy uses the fixed categorical hover scales; argo uses the themeable status tokens.\n 'tw:group-data-[variant=info]/alert:group-hover:text-blue-600',\n 'tw:group-data-[variant=info]/alert:group-data-[argo=true]/alert:group-hover:text-info',\n 'tw:group-data-[variant=warning]/alert:group-hover:text-warning-600',\n 'tw:group-data-[variant=warning]/alert:group-data-[argo=true]/alert:group-hover:text-warning',\n 'tw:group-data-[variant=error]/alert:group-hover:text-error-600',\n 'tw:group-data-[variant=error]/alert:group-data-[argo=true]/alert:group-hover:text-destructive',\n 'tw:group-data-[variant=success]/alert:group-hover:text-green-600',\n 'tw:group-data-[variant=success]/alert:group-data-[argo=true]/alert:group-hover:text-success',\n )}\n />\n </Tooltip>\n);\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'div'>;\n\nexport const AlertDescription = ({ ref, children, className, ...rest }: Props) => (\n <div\n {...rest}\n ref={ref}\n className={cn(\n 'tw:col-start-2',\n 'tw:group-has-[[data-slot=alert-title]]/alert:col-end-[-1]',\n\n // spacer before description when a title exists (pseudo-element so it animates with Collapsible)\n 'tw:group-has-[[data-slot=alert-title]]/alert:before:block',\n 'tw:group-has-[[data-slot=alert-title]]/alert:before:h-4',\n \"tw:group-has-[[data-slot=alert-title]]/alert:before:content-['']\",\n\n 'tw:break-anywhere',\n\n // Default size for nested Lucide icons; callers can override on the icon.\n \"tw:[&_svg.lucide:not([class*='tw:size-'])]:size-4\",\n 'tw:[&_svg]:shrink-0',\n\n className,\n )}\n >\n {children}\n </div>\n);\n","import type { ComponentProps, ReactNode } from 'react';\n\nimport { AlertTriangle, CircleCheck, Info } from '../../icons';\nimport { cn } from '../../utils';\n\nimport type { Variant } from './AlertRoot';\n\nconst VARIANT_ICONS: Record<Variant, ReactNode> = {\n info: <Info />,\n warning: <AlertTriangle />,\n error: <AlertTriangle />,\n success: <CircleCheck />,\n};\n\ntype Props = {\n variant: Variant;\n icon?: ReactNode;\n} & Omit<ComponentProps<'span'>, 'children'>;\n\nexport const AlertIcon = ({ variant, icon, className, ...rest }: Props) => (\n <span\n {...rest}\n data-slot=\"alert-icon\"\n className={cn(\n 'tw:inline-flex',\n 'tw:items-center',\n 'tw:justify-center',\n 'tw:self-start',\n 'tw:p-0.5',\n 'tw:[&_svg:not([class*=\"tw:size-\"])]:size-4',\n // Legacy uses the fixed categorical icon scales; argo uses the themeable status tokens.\n 'tw:group-data-[variant=info]/alert:text-blue-500',\n 'tw:group-data-[variant=info]/alert:group-data-[argo=true]/alert:text-info',\n 'tw:group-data-[variant=warning]/alert:text-warning-500',\n 'tw:group-data-[variant=warning]/alert:group-data-[argo=true]/alert:text-warning',\n 'tw:group-data-[variant=error]/alert:text-error-500',\n 'tw:group-data-[variant=error]/alert:group-data-[argo=true]/alert:text-destructive',\n 'tw:group-data-[variant=success]/alert:text-green-500',\n 'tw:group-data-[variant=success]/alert:group-data-[argo=true]/alert:text-success',\n className,\n )}\n >\n {icon ?? VARIANT_ICONS[variant]}\n </span>\n);\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\nimport { useArgoDesign } from '../ThemeProvider/context';\n\nexport type Variant = 'info' | 'warning' | 'error' | 'success';\n\ntype Props = {\n variant?: Variant;\n} & ComponentProps<'div'>;\n\nexport const AlertRoot = ({ ref, children, className, variant, ...rest }: Props) => {\n const renderArgo = useArgoDesign();\n return (\n <div\n {...rest}\n ref={ref}\n data-variant={variant}\n data-argo={renderArgo}\n className={cn(\n 'tw:group/alert',\n\n // layout\n 'tw:grid',\n 'tw:grid-cols-[0_1fr_auto]',\n 'tw:has-[[data-slot=alert-icon]]:grid-cols-[auto_1fr_auto]',\n 'tw:has-[[data-slot=alert-icon]]:gap-x-2',\n\n // fonts\n 'tw:text-sm',\n // Legacy keeps the fixed neutral text; argo uses the semantic foreground\n // so the alert flips with the brand's light/dark palette (the categorical\n // variant tint stays — only the text token becomes brand-aware).\n 'tw:text-neutral-800',\n 'tw:data-[argo=true]:text-foreground',\n 'tw:text-left',\n\n 'tw:p-4',\n\n // border — legacy keeps a fixed radius; argo follows the brand radius token.\n 'tw:rounded',\n 'tw:data-[argo=true]:rounded-[var(--radius-lg,0.25rem)]',\n 'tw:border',\n 'tw:border-solid',\n\n // Variant tint + border. Legacy keeps the fixed categorical scales (solid\n // pastel tint); argo uses the brand-overridable status tokens (bg at 10%,\n // border at 25%) so status re-skins with the brand. The argo tokens carry\n // AA-legible defaults, so the variant icon (AlertIcon, full-strength\n // `tw:text-<role>`) clears ≥4.5:1 on the tint in both light and dark.\n 'tw:data-[variant=info]:bg-blue-100',\n 'tw:data-[variant=info]:border-blue-200',\n 'tw:data-[variant=info]:data-[argo=true]:bg-info/10',\n 'tw:data-[variant=info]:data-[argo=true]:border-info/25',\n\n 'tw:data-[variant=warning]:bg-warning-100',\n 'tw:data-[variant=warning]:border-warning-300',\n 'tw:data-[variant=warning]:data-[argo=true]:bg-warning/10',\n 'tw:data-[variant=warning]:data-[argo=true]:border-warning/25',\n\n 'tw:data-[variant=error]:bg-error-100',\n 'tw:data-[variant=error]:border-error-200',\n 'tw:data-[variant=error]:data-[argo=true]:bg-destructive/10',\n 'tw:data-[variant=error]:data-[argo=true]:border-destructive/25',\n\n 'tw:data-[variant=success]:bg-green-100',\n 'tw:data-[variant=success]:border-green-200',\n 'tw:data-[variant=success]:data-[argo=true]:bg-success/10',\n 'tw:data-[variant=success]:data-[argo=true]:border-success/25',\n className,\n )}\n >\n {children}\n </div>\n );\n};\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'div'>;\n\nexport const AlertTitle = ({ ref, className, ...props }: Props) => (\n <div\n {...props}\n ref={ref}\n data-slot=\"alert-title\"\n className={cn(\n 'tw:group/alert-title',\n 'tw:flex',\n 'tw:col-start-2',\n 'tw:font-medium',\n 'tw:items-center',\n className,\n )}\n />\n);\n","import { useState } from 'react';\nimport type { ComponentProps, ReactNode } from 'react';\n\nimport { cn } from '../../utils';\nimport { Collapsible } from '../Collapsible';\n\nimport { AlertAction } from './AlertAction';\nimport { AlertCollapsibleIcon } from './AlertCollapsibleIcon';\nimport { AlertDescription } from './AlertDescription';\nimport { AlertIcon } from './AlertIcon';\nimport { AlertRoot } from './AlertRoot';\nimport { AlertTitle } from './AlertTitle';\n\ntype CommonProps = {\n hideIcon?: boolean;\n icon?: ReactNode;\n} & Pick<ComponentProps<typeof Collapsible>, 'open' | 'onOpenChange' | 'defaultOpen'> &\n Omit<ComponentProps<typeof AlertRoot>, 'children'>;\n\ntype CollapsibleProps = {\n mode: 'collapsible';\n title: ReactNode;\n children: ReactNode;\n actions?: never;\n};\n\ntype DefaultProps = {\n mode?: 'default';\n title?: ReactNode;\n children?: ReactNode;\n actions?: ReactNode;\n};\n\n/**\n * Props for `Alert`. Discriminated by `mode` — `\"default\"` renders `title` / `children` / `actions`,\n * `\"collapsible\"` requires `title` + `children` (no `actions`) and renders a collapsible disclosure.\n * `variant` (`info` | `success` | `warning` | `error`) is a categorical colour contract.\n */\ntype Props = CommonProps & (CollapsibleProps | DefaultProps);\n\n/**\n * Persistent inline banner for contextual messages tied to the page layout.\n *\n * **When to use:** a message that lives in the page (info banner, warning about a misconfiguration,\n * post-save validation summary). Use `variant` to encode meaning; pass `actions` for inline buttons.\n * `mode=\"collapsible\"` collapses long error details behind a toggle.\n * **When NOT to use:** for transient feedback (save succeeded, job started) use `toast()`; for\n * field-level validation errors use `FormGroup state=\"error\"`; for a confirmation popover use\n * `Popconfirm`.\n * **Composition:** typically rendered at the top of `PageLayout.Content` or above a form section.\n *\n * @see RECIPES.md#alert-with-action — for action-bearing alert composition\n */\nexport const Alert = ({\n title,\n variant = 'info',\n children,\n hideIcon = false,\n icon,\n className,\n actions,\n mode = 'default',\n\n // collapsible props\n defaultOpen = true,\n open: openProp,\n onOpenChange: onOpenChangeProp,\n\n ...rest\n}: Props) => {\n const isControlled = openProp !== undefined;\n\n const [innerOpen, setInnerOpen] = useState(defaultOpen);\n const open = isControlled ? openProp : innerOpen;\n const handleOpenChange = (nextOpen: boolean) => {\n if (!isControlled) {\n setInnerOpen(nextOpen);\n }\n onOpenChangeProp?.(nextOpen);\n };\n\n const isCollapsible = mode === 'collapsible' && !!title && !!children && !actions;\n\n if (isCollapsible)\n return (\n <Collapsible open={open} onOpenChange={handleOpenChange} asChild>\n <AlertRoot {...rest} variant={variant} className={className}>\n <Collapsible.Trigger\n className={cn(\n 'tailwindcss-preflight',\n 'tw:group',\n 'tw:col-span-full',\n 'tw:grid',\n 'tw:grid-cols-subgrid',\n 'tw:rounded-sm',\n 'tw:focus-visible:outline',\n 'tw:focus-visible:outline-2',\n 'tw:focus-visible:outline-ring',\n )}\n >\n {!hideIcon && <AlertIcon variant={variant} icon={icon} />}\n <AlertTitle>{title}</AlertTitle>\n <AlertCollapsibleIcon open={open} />\n </Collapsible.Trigger>\n\n <Collapsible.Content asChild>\n <AlertDescription>{children}</AlertDescription>\n </Collapsible.Content>\n </AlertRoot>\n </Collapsible>\n );\n\n return (\n <AlertRoot {...rest} variant={variant} className={className}>\n {!hideIcon && <AlertIcon variant={variant} icon={icon} />}\n {title && <AlertTitle>{title}</AlertTitle>}\n {children && <AlertDescription>{children}</AlertDescription>}\n {actions && <AlertAction>{actions}</AlertAction>}\n </AlertRoot>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Alert-C4Y9IwMI.js","names":[],"sources":["../src/components/Alert/AlertAction.tsx","../src/components/Alert/AlertCollapsibleIcon.tsx","../src/components/Alert/AlertDescription.tsx","../src/components/Alert/AlertIcon.tsx","../src/components/Alert/AlertRoot.tsx","../src/components/Alert/AlertTitle.tsx","../src/components/Alert/Alert.tsx"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'div'> & {\n asChild?: boolean;\n};\n\nexport const AlertAction = ({ ref, className, asChild = false, ...props }: Props) => {\n const Comp = asChild ? Slot : 'div';\n return (\n <Comp\n {...props}\n ref={ref}\n className={cn(\n 'tw:col-start-3',\n 'tw:row-start-1',\n 'tw:self-start',\n 'tw:flex',\n 'tw:gap-2',\n className,\n )}\n />\n );\n};\n","import { ChevronDown } from '../../icons';\nimport { cn } from '../../utils';\nimport { Tooltip } from '../Tooltip';\n\ntype Props = {\n open: boolean;\n};\n\nexport const AlertCollapsibleIcon = ({ open }: Props) => (\n <Tooltip placement=\"top\" tooltip={open ? 'Collapse alert' : 'Expand alert'} triggerAsChild>\n <ChevronDown\n className={cn(\n 'tw:mt-px',\n 'tw:size-5',\n 'tw:text-neutral-400',\n 'tw:group-data-[state=open]/alert:rotate-180',\n\n // Legacy uses the fixed categorical hover scales; argo uses the themeable status tokens.\n 'tw:group-data-[variant=info]/alert:group-hover:text-blue-600',\n 'tw:group-data-[variant=info]/alert:group-data-[argo=true]/alert:group-hover:text-info',\n 'tw:group-data-[variant=warning]/alert:group-hover:text-warning-600',\n 'tw:group-data-[variant=warning]/alert:group-data-[argo=true]/alert:group-hover:text-warning',\n 'tw:group-data-[variant=error]/alert:group-hover:text-error-600',\n 'tw:group-data-[variant=error]/alert:group-data-[argo=true]/alert:group-hover:text-destructive',\n 'tw:group-data-[variant=success]/alert:group-hover:text-green-600',\n 'tw:group-data-[variant=success]/alert:group-data-[argo=true]/alert:group-hover:text-success',\n )}\n />\n </Tooltip>\n);\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'div'>;\n\nexport const AlertDescription = ({ ref, children, className, ...rest }: Props) => (\n <div\n {...rest}\n ref={ref}\n className={cn(\n 'tw:col-start-2',\n 'tw:group-has-[[data-slot=alert-title]]/alert:col-end-[-1]',\n\n // spacer before description when a title exists (pseudo-element so it animates with Collapsible)\n 'tw:group-has-[[data-slot=alert-title]]/alert:before:block',\n 'tw:group-has-[[data-slot=alert-title]]/alert:before:h-4',\n \"tw:group-has-[[data-slot=alert-title]]/alert:before:content-['']\",\n\n 'tw:break-anywhere',\n\n // Default size for nested Lucide icons; callers can override on the icon.\n \"tw:[&_svg.lucide:not([class*='tw:size-'])]:size-4\",\n 'tw:[&_svg]:shrink-0',\n\n className,\n )}\n >\n {children}\n </div>\n);\n","import type { ComponentProps, ReactNode } from 'react';\n\nimport { AlertTriangle, CircleCheck, Info } from '../../icons';\nimport { cn } from '../../utils';\n\nimport type { Variant } from './AlertRoot';\n\nconst VARIANT_ICONS: Record<Variant, ReactNode> = {\n info: <Info />,\n warning: <AlertTriangle />,\n error: <AlertTriangle />,\n success: <CircleCheck />,\n};\n\ntype Props = {\n variant: Variant;\n icon?: ReactNode;\n} & Omit<ComponentProps<'span'>, 'children'>;\n\nexport const AlertIcon = ({ variant, icon, className, ...rest }: Props) => (\n <span\n {...rest}\n data-slot=\"alert-icon\"\n className={cn(\n 'tw:inline-flex',\n 'tw:items-center',\n 'tw:justify-center',\n 'tw:self-start',\n 'tw:p-0.5',\n 'tw:[&_svg:not([class*=\"tw:size-\"])]:size-4',\n // Legacy uses the fixed categorical icon scales; argo uses the themeable status tokens.\n 'tw:group-data-[variant=info]/alert:text-blue-500',\n 'tw:group-data-[variant=info]/alert:group-data-[argo=true]/alert:text-info',\n 'tw:group-data-[variant=warning]/alert:text-warning-500',\n 'tw:group-data-[variant=warning]/alert:group-data-[argo=true]/alert:text-warning',\n 'tw:group-data-[variant=error]/alert:text-error-500',\n 'tw:group-data-[variant=error]/alert:group-data-[argo=true]/alert:text-destructive',\n 'tw:group-data-[variant=success]/alert:text-green-500',\n 'tw:group-data-[variant=success]/alert:group-data-[argo=true]/alert:text-success',\n className,\n )}\n >\n {icon ?? VARIANT_ICONS[variant]}\n </span>\n);\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\nimport { useArgoDesign } from '../ThemeProvider/context';\n\nexport type Variant = 'info' | 'warning' | 'error' | 'success';\n\ntype Props = {\n variant?: Variant;\n} & ComponentProps<'div'>;\n\nexport const AlertRoot = ({ ref, children, className, variant, ...rest }: Props) => {\n const renderArgo = useArgoDesign();\n return (\n <div\n {...rest}\n ref={ref}\n data-variant={variant}\n data-argo={renderArgo}\n className={cn(\n 'tw:group/alert',\n\n // layout\n 'tw:grid',\n 'tw:grid-cols-[0_1fr_auto]',\n 'tw:has-[[data-slot=alert-icon]]:grid-cols-[auto_1fr_auto]',\n 'tw:has-[[data-slot=alert-icon]]:gap-x-2',\n\n // fonts\n 'tw:text-sm',\n // Legacy keeps the fixed neutral text; argo uses the semantic foreground\n // so the alert flips with the brand's light/dark palette (the categorical\n // variant tint stays — only the text token becomes brand-aware).\n 'tw:text-neutral-800',\n 'tw:data-[argo=true]:text-foreground',\n 'tw:text-left',\n\n 'tw:p-4',\n\n // border — legacy keeps a fixed radius; argo follows the brand radius token.\n 'tw:rounded',\n 'tw:data-[argo=true]:rounded-[var(--radius-lg,0.25rem)]',\n 'tw:border',\n 'tw:border-solid',\n\n // Variant tint + border. Legacy keeps the fixed categorical scales (solid\n // pastel tint); argo uses the brand-overridable status tokens (bg at 10%,\n // border at 25%) so status re-skins with the brand. The argo tokens carry\n // AA-legible defaults, so the variant icon (AlertIcon, full-strength\n // `tw:text-<role>`) clears ≥4.5:1 on the tint in both light and dark.\n 'tw:data-[variant=info]:bg-blue-100',\n 'tw:data-[variant=info]:border-blue-200',\n 'tw:data-[variant=info]:data-[argo=true]:bg-info/10',\n 'tw:data-[variant=info]:data-[argo=true]:border-info/25',\n\n 'tw:data-[variant=warning]:bg-warning-100',\n 'tw:data-[variant=warning]:border-warning-300',\n 'tw:data-[variant=warning]:data-[argo=true]:bg-warning/10',\n 'tw:data-[variant=warning]:data-[argo=true]:border-warning/25',\n\n 'tw:data-[variant=error]:bg-error-100',\n 'tw:data-[variant=error]:border-error-200',\n 'tw:data-[variant=error]:data-[argo=true]:bg-destructive/10',\n 'tw:data-[variant=error]:data-[argo=true]:border-destructive/25',\n\n 'tw:data-[variant=success]:bg-green-100',\n 'tw:data-[variant=success]:border-green-200',\n 'tw:data-[variant=success]:data-[argo=true]:bg-success/10',\n 'tw:data-[variant=success]:data-[argo=true]:border-success/25',\n className,\n )}\n >\n {children}\n </div>\n );\n};\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'div'>;\n\nexport const AlertTitle = ({ ref, className, ...props }: Props) => (\n <div\n {...props}\n ref={ref}\n data-slot=\"alert-title\"\n className={cn(\n 'tw:group/alert-title',\n 'tw:flex',\n 'tw:col-start-2',\n 'tw:font-medium',\n 'tw:items-center',\n className,\n )}\n />\n);\n","import { useState } from 'react';\nimport type { ComponentProps, ReactNode } from 'react';\n\nimport { cn } from '../../utils';\nimport { Collapsible } from '../Collapsible';\n\nimport { AlertAction } from './AlertAction';\nimport { AlertCollapsibleIcon } from './AlertCollapsibleIcon';\nimport { AlertDescription } from './AlertDescription';\nimport { AlertIcon } from './AlertIcon';\nimport { AlertRoot } from './AlertRoot';\nimport { AlertTitle } from './AlertTitle';\n\ntype CommonProps = {\n hideIcon?: boolean;\n icon?: ReactNode;\n} & Pick<ComponentProps<typeof Collapsible>, 'open' | 'onOpenChange' | 'defaultOpen'> &\n Omit<ComponentProps<typeof AlertRoot>, 'children'>;\n\ntype CollapsibleProps = {\n mode: 'collapsible';\n title: ReactNode;\n children: ReactNode;\n actions?: never;\n};\n\ntype DefaultProps = {\n mode?: 'default';\n title?: ReactNode;\n children?: ReactNode;\n actions?: ReactNode;\n};\n\n/**\n * Props for `Alert`. Discriminated by `mode` — `\"default\"` renders `title` / `children` / `actions`,\n * `\"collapsible\"` requires `title` + `children` (no `actions`) and renders a collapsible disclosure.\n * `variant` (`info` | `success` | `warning` | `error`) is a categorical colour contract.\n */\ntype Props = CommonProps & (CollapsibleProps | DefaultProps);\n\n/**\n * Persistent inline banner for contextual messages tied to the page layout.\n *\n * **When to use:** a message that lives in the page (info banner, warning about a misconfiguration,\n * post-save validation summary). Use `variant` to encode meaning; pass `actions` for inline buttons.\n * `mode=\"collapsible\"` collapses long error details behind a toggle.\n * **When NOT to use:** for transient feedback (save succeeded, job started) use `toast()`; for\n * field-level validation errors use `FormGroup state=\"error\"`; for a confirmation popover use\n * `Popconfirm`.\n * **Composition:** typically rendered at the top of `PageLayout.Content` or above a form section.\n *\n * @see RECIPES.md#alert-with-action — for action-bearing alert composition\n */\nexport const Alert = ({\n title,\n variant = 'info',\n children,\n hideIcon = false,\n icon,\n className,\n actions,\n mode = 'default',\n\n // collapsible props\n defaultOpen = true,\n open: openProp,\n onOpenChange: onOpenChangeProp,\n\n ...rest\n}: Props) => {\n const isControlled = openProp !== undefined;\n\n const [innerOpen, setInnerOpen] = useState(defaultOpen);\n const open = isControlled ? openProp : innerOpen;\n const handleOpenChange = (nextOpen: boolean) => {\n if (!isControlled) {\n setInnerOpen(nextOpen);\n }\n onOpenChangeProp?.(nextOpen);\n };\n\n const isCollapsible = mode === 'collapsible' && !!title && !!children && !actions;\n\n if (isCollapsible)\n return (\n <Collapsible open={open} onOpenChange={handleOpenChange} asChild>\n <AlertRoot {...rest} variant={variant} className={className}>\n <Collapsible.Trigger\n className={cn(\n 'tailwindcss-preflight',\n 'tw:group',\n 'tw:col-span-full',\n 'tw:grid',\n 'tw:grid-cols-subgrid',\n 'tw:rounded-sm',\n 'tw:focus-visible:outline',\n 'tw:focus-visible:outline-2',\n 'tw:focus-visible:outline-ring',\n )}\n >\n {!hideIcon && <AlertIcon variant={variant} icon={icon} />}\n <AlertTitle>{title}</AlertTitle>\n <AlertCollapsibleIcon open={open} />\n </Collapsible.Trigger>\n\n <Collapsible.Content asChild>\n <AlertDescription>{children}</AlertDescription>\n </Collapsible.Content>\n </AlertRoot>\n </Collapsible>\n );\n\n return (\n <AlertRoot {...rest} variant={variant} className={className}>\n {!hideIcon && <AlertIcon variant={variant} icon={icon} />}\n {title && <AlertTitle>{title}</AlertTitle>}\n {children && <AlertDescription>{children}</AlertDescription>}\n {actions && <AlertAction>{actions}</AlertAction>}\n </AlertRoot>\n );\n};\n"],"mappings":"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"}
|
|
@@ -106,7 +106,7 @@ const badgeRootVariants = cva([
|
|
|
106
106
|
},
|
|
107
107
|
variants: {
|
|
108
108
|
variant: {
|
|
109
|
-
default: ["tw:rounded
|
|
109
|
+
default: ["tw:rounded", "tw:[[data-brand^=keboola-legacy]_&]:uppercase"],
|
|
110
110
|
tag: ["tw:rounded tw:normal-case"],
|
|
111
111
|
pill: ["tw:min-w-5 tw:rounded-full tw:normal-case tw:tracking-normal"]
|
|
112
112
|
},
|
|
@@ -192,4 +192,4 @@ const Badge = Object.assign(BadgeInner, {
|
|
|
192
192
|
//#endregion
|
|
193
193
|
export { Badge };
|
|
194
194
|
|
|
195
|
-
//# sourceMappingURL=Badge-
|
|
195
|
+
//# sourceMappingURL=Badge-o5jURtpB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge-D0pXdekt.js","names":[],"sources":["../src/components/Badge/BadgeAddon.tsx","../src/components/Badge/BadgeButton.tsx","../src/components/Badge/BadgeLabel.tsx","../src/components/Badge/styles.ts","../src/components/Badge/BadgeRoot.tsx","../src/components/Badge/Badge.tsx"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '../../utils';\n\nconst addonVariants = cva(\n [\n 'tw:flex',\n 'tw:items-center',\n 'tw:shrink-0',\n 'tw:bg-inherit',\n 'tw:self-stretch',\n 'tw:px-1',\n 'tw:has-[button]:px-0',\n 'tw:[&_svg:not([class*=\"tw:size-\"])]:size-3',\n ],\n {\n defaultVariants: {\n align: 'inline-end',\n variant: 'inline',\n },\n variants: {\n align: {\n 'inline-start': ['tw:order-first'],\n 'inline-end': ['tw:order-last'],\n },\n variant: {\n inline: [],\n floating: [\n 'tw:hidden',\n 'tw:group-hover/badge:flex',\n\n 'tw:absolute',\n 'tw:top-0',\n 'tw:bottom-0',\n 'tw:focus-within:flex',\n ],\n },\n },\n compoundVariants: [\n { variant: 'floating', align: 'inline-end', className: ['tw:right-0'] },\n { variant: 'floating', align: 'inline-start', className: ['tw:left-0'] },\n ],\n },\n);\n\ntype Props = ComponentProps<'div'> & VariantProps<typeof addonVariants>;\n\nexport const BadgeAddon = ({\n ref,\n align = 'inline-end',\n variant = 'inline',\n className,\n ...rest\n}: Props) => (\n <div\n {...rest}\n ref={ref}\n data-align={align}\n data-variant={variant}\n className={cn(addonVariants({ align, variant, className }))}\n />\n);\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'button'>;\n\nexport const BadgeButton = ({ ref, className, type = 'button', children, ...rest }: Props) => (\n <button\n {...rest}\n ref={ref}\n type={type}\n className={cn(\n 'tailwindcss-preflight',\n\n 'tw:inline-flex',\n 'tw:items-center',\n 'tw:justify-center',\n 'tw:cursor-pointer',\n\n 'tw:bg-inherit',\n 'tw:text-inherit',\n\n 'tw:hover:brightness-95',\n 'tw:px-1',\n\n // The focus ring lives here rather than on `BadgeRoot`, whose own `focus-visible` block\n // sits on a `div` that never takes focus. Inset, because a badge root clips its overflow —\n // an outset ring on a nested button would be cut off.\n 'tw:focus-visible:outline-hidden',\n 'tw:focus-visible:ring-2',\n 'tw:focus-visible:ring-inset',\n 'tw:focus-visible:ring-ring',\n // Default sizing for Lucide-icon children (which ship at 24×24); keeps\n // the visual baseline aligned with the previous FA-icon API and other\n // icon-wrapper components (ItemIcon, SidebarMenuIcon).\n 'tw:[&_svg:not([class*=\"tw:size-\"])]:size-3',\n className,\n )}\n >\n {children}\n </button>\n);\n","import type { ComponentProps } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'span'> & {\n asChild?: boolean;\n};\n\nexport const BadgeLabel = ({ ref, className, asChild = false, ...rest }: Props) => {\n const Comp = asChild ? Slot : 'span';\n return (\n <Comp\n {...rest}\n ref={ref}\n data-slot=\"badge-label\"\n className={cn(\n 'tw:max-w-56',\n 'tw:truncate',\n 'tw:px-2',\n 'tw:py-0.5',\n\n // Drop side padding only when an *inline* addon is present —\n // floating addons overlay the label without taking layout space,\n // so the label still needs full padding behind them.\n 'tw:group-has-[[data-align=inline-start][data-variant=inline]:not(:empty)]/badge:pl-0',\n 'tw:group-has-[[data-align=inline-end][data-variant=inline]:not(:empty)]/badge:pr-0',\n className,\n )}\n />\n );\n};\n","import { cva } from 'class-variance-authority';\n\n/**\n * Badge's `color` variants: a fill class, a foreground class, and a matching\n * `decoration-*` so an underlined badge underlines in its own fill colour.\n *\n * - Tinted (`-inverse`) variants each carry a `[data-theme=dark]` override, so the\n * chip inverts with the page instead of staying a light slab on a dark surface.\n * - Solid fills are fixed across schemes.\n * - Exported for `scripts/contrast-audit.ts` and the contrast story tests, which\n * measure each pairing rather than restating this map.\n */\nexport const badgeColors = {\n gray: 'tw:bg-neutral-400 tw:text-white tw:decoration-neutral-400',\n 'gray-darker': 'tw:bg-neutral-500 tw:text-white tw:decoration-neutral-500',\n 'gray-inverse':\n 'tw:bg-neutral-100 tw:text-neutral-500 tw:decoration-neutral-100 tw:[[data-theme=dark]_&]:bg-neutral-700 tw:[[data-theme=dark]_&]:text-neutral-300 tw:[[data-theme=dark]_&]:decoration-neutral-700',\n green: 'tw:bg-green-500 tw:text-white tw:decoration-green-500',\n 'green-darker': 'tw:bg-green-600 tw:text-white tw:decoration-green-600',\n 'green-inverse':\n 'tw:bg-green-100 tw:text-green-900 tw:decoration-green-100 tw:[[data-theme=dark]_&]:bg-green-900 tw:[[data-theme=dark]_&]:text-green-200 tw:[[data-theme=dark]_&]:decoration-green-900',\n blue: 'tw:bg-blue-500 tw:text-white tw:decoration-blue-500',\n 'blue-inverse':\n 'tw:bg-blue-100 tw:text-blue-800 tw:decoration-blue-100 tw:[[data-theme=dark]_&]:bg-blue-900 tw:[[data-theme=dark]_&]:text-blue-200 tw:[[data-theme=dark]_&]:decoration-blue-900',\n red: 'tw:bg-error-500 tw:text-white tw:decoration-error-500',\n orange: 'tw:bg-warning-500 tw:text-white tw:decoration-warning-500',\n 'orange-inverse':\n 'tw:bg-warning-100 tw:text-warning-800 tw:decoration-warning-100 tw:[[data-theme=dark]_&]:bg-warning-900 tw:[[data-theme=dark]_&]:text-warning-200 tw:[[data-theme=dark]_&]:decoration-warning-900',\n amber: 'tw:bg-warning-300 tw:text-warning-800 tw:decoration-warning-300',\n // One rung deeper than `orange-inverse` on both ends of the ramp, which is what\n // keeps caution and warning telling apart — the two share the `warning` hue.\n 'amber-inverse':\n 'tw:bg-warning-200 tw:text-warning-800 tw:decoration-warning-200 tw:[[data-theme=dark]_&]:bg-warning-800 tw:[[data-theme=dark]_&]:text-warning-200 tw:[[data-theme=dark]_&]:decoration-warning-800',\n teal: 'tw:bg-teal-500 tw:text-white tw:decoration-teal-500',\n 'teal-inverse':\n 'tw:bg-teal-100 tw:text-teal-800 tw:decoration-teal-100 tw:[[data-theme=dark]_&]:bg-teal-900 tw:[[data-theme=dark]_&]:text-teal-200 tw:[[data-theme=dark]_&]:decoration-teal-900',\n purple: 'tw:bg-purple-500 tw:text-white tw:decoration-purple-500',\n 'purple-inverse':\n 'tw:bg-purple-100 tw:text-purple-800 tw:decoration-purple-100 tw:[[data-theme=dark]_&]:bg-purple-900 tw:[[data-theme=dark]_&]:text-purple-200 tw:[[data-theme=dark]_&]:decoration-purple-900',\n cyan: 'tw:bg-cyan-500 tw:text-white tw:decoration-cyan-500',\n 'cyan-inverse':\n 'tw:bg-cyan-100 tw:text-cyan-800 tw:decoration-cyan-100 tw:[[data-theme=dark]_&]:bg-cyan-900 tw:[[data-theme=dark]_&]:text-cyan-200 tw:[[data-theme=dark]_&]:decoration-cyan-900',\n 'sidebar-primary': 'tw:bg-sidebar-primary tw:text-white tw:decoration-sidebar-primary',\n white: 'tw:bg-white/90 tw:text-neutral-600 tw:decoration-white/90',\n} as const;\n\n/** A `Badge` `color` value. */\nexport type BadgeColor = keyof typeof badgeColors;\n\n/**\n * Whether a variant paints text on its own tint rather than on a solid fill.\n *\n * - Tinted variants are the ones held to WCAG-AA per `AGENTS.md`; solid `-500`\n * fills are pinned across brands, so their contrast is recorded, not asserted.\n */\nexport const isTintedBadgeColor = (color: string): boolean => color.endsWith('-inverse');\n\nexport const badgeRootVariants = cva(\n [\n 'tailwindcss-preflight',\n 'tw:group/badge',\n\n // display\n 'tw:relative',\n 'tw:inline-flex',\n 'tw:justify-center',\n 'tw:items-center',\n 'tw:min-h-5',\n 'tw:h-fit',\n 'tw:shrink-0',\n 'tw:overflow-hidden',\n\n // text — chrome weight/tracking are brand-overridable tokens\n // (`--font-weight-medium` / `--letter-spacing-chrome`); casing lives on the\n // `default` variant below via `tw:uppercase`.\n 'tw:whitespace-pre-wrap',\n 'tw:break-normal',\n 'tw:text-xs',\n 'tw:font-medium',\n 'tw:tracking-chrome',\n 'tw:text-center',\n 'tw:underline',\n\n // content\n 'tw:border-none',\n 'tw:[&>svg]:self-center',\n 'tw:[button&]:cursor-pointer',\n\n // focus\n 'tw:focus-visible:outline-hidden',\n 'tw:focus-visible:ring-2',\n 'tw:focus-visible:ring-inset',\n 'tw:focus-visible:ring-ring',\n ],\n {\n defaultVariants: {\n color: 'gray',\n variant: 'default',\n },\n variants: {\n variant: {\n // Legacy chrome uppercases the default badge; the Keboola V2 brand\n // (`data-brand=\"keboola\"`) renders it in standard case instead.\n default: [\n 'tw:rounded tw:uppercase',\n 'tw:[[data-brand=keboola]_&]:normal-case tw:[[data-brand=keboola]_&]:tracking-normal',\n ],\n tag: ['tw:rounded tw:normal-case'],\n pill: ['tw:min-w-5 tw:rounded-full tw:normal-case tw:tracking-normal'],\n },\n color: badgeColors,\n },\n },\n);\n","import type { ComponentProps } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '../../utils';\n\nimport { badgeRootVariants } from './styles';\n\ntype Props = ComponentProps<'div'> & VariantProps<typeof badgeRootVariants>;\n\nexport const BadgeRoot = ({ ref, variant, color, className, children, ...rest }: Props) => (\n <div\n {...rest}\n ref={ref}\n data-slot=\"badge\"\n className={cn(badgeRootVariants({ variant, color, className }))}\n >\n {children}\n </div>\n);\n","import type { ComponentProps, ReactNode } from 'react';\n\nimport { BadgeAddon } from './BadgeAddon';\nimport { BadgeButton } from './BadgeButton';\nimport { BadgeLabel } from './BadgeLabel';\nimport { BadgeRoot } from './BadgeRoot';\n\ntype AddonVariant = ComponentProps<typeof BadgeAddon>['variant'];\ntype BadgeRootProps = ComponentProps<typeof BadgeRoot>;\ntype BadgeLabelProps = ComponentProps<typeof BadgeLabel>;\n\n/**\n * Props for `Badge`. `color` is a categorical contract (`green` = success, `red` = error, `orange` = warning,\n * `amber` = caution, `purple/cyan/teal/blue/gray` for decorative tags) — colours stay fixed across brand themes.\n * Pass `prefix` / `suffix` for icon-style addons.\n */\ntype Props = Pick<BadgeRootProps, 'variant' | 'color'> &\n Omit<BadgeLabelProps, 'prefix'> & {\n prefix?: ReactNode;\n prefixVariant?: AddonVariant;\n suffix?: ReactNode;\n suffixVariant?: AddonVariant;\n labelClassName?: string;\n };\n\n/**\n * Compact status / category label rendered as an inline pill.\n *\n * **When to use:** encoding status (active, deprecated, beta), counts, or decorative tags next to a title\n * or in a table cell. The `color` prop is a semantic contract — `green` means success, `red` means error\n * etc., and colours are intentionally stable across customer brand themes.\n * **When NOT to use:** for an inline action use `Button` / `ButtonInline`; for a persistent contextual\n * banner use `Alert`; for a counter-only chip prefer `Badge` with just a numeric label.\n * **Composition:** drop into `PageHeader.Detail` `badges` slot, `Table.Cell`, or alongside titles in cards.\n * Use `Badge.Button` for an interactive (clickable) badge variant.\n */\nconst BadgeInner = ({\n ref,\n // root props\n variant,\n color,\n // custom props\n prefix,\n prefixVariant = 'inline',\n suffix,\n suffixVariant = 'inline',\n className,\n labelClassName,\n ...labelProps\n}: Props) => (\n <BadgeRoot className={className} variant={variant} color={color}>\n {prefix && (\n <BadgeAddon align=\"inline-start\" variant={prefixVariant}>\n {prefix}\n </BadgeAddon>\n )}\n <BadgeLabel {...labelProps} ref={ref} className={labelClassName} />\n {suffix && (\n <BadgeAddon align=\"inline-end\" variant={suffixVariant}>\n {suffix}\n </BadgeAddon>\n )}\n </BadgeRoot>\n);\n\nexport const Badge = Object.assign(BadgeInner, {\n Root: BadgeRoot,\n Button: BadgeButton,\n Label: BadgeLabel,\n Addon: BadgeAddon,\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Badge-o5jURtpB.js","names":[],"sources":["../src/components/Badge/BadgeAddon.tsx","../src/components/Badge/BadgeButton.tsx","../src/components/Badge/BadgeLabel.tsx","../src/components/Badge/styles.ts","../src/components/Badge/BadgeRoot.tsx","../src/components/Badge/Badge.tsx"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '../../utils';\n\nconst addonVariants = cva(\n [\n 'tw:flex',\n 'tw:items-center',\n 'tw:shrink-0',\n 'tw:bg-inherit',\n 'tw:self-stretch',\n 'tw:px-1',\n 'tw:has-[button]:px-0',\n 'tw:[&_svg:not([class*=\"tw:size-\"])]:size-3',\n ],\n {\n defaultVariants: {\n align: 'inline-end',\n variant: 'inline',\n },\n variants: {\n align: {\n 'inline-start': ['tw:order-first'],\n 'inline-end': ['tw:order-last'],\n },\n variant: {\n inline: [],\n floating: [\n 'tw:hidden',\n 'tw:group-hover/badge:flex',\n\n 'tw:absolute',\n 'tw:top-0',\n 'tw:bottom-0',\n 'tw:focus-within:flex',\n ],\n },\n },\n compoundVariants: [\n { variant: 'floating', align: 'inline-end', className: ['tw:right-0'] },\n { variant: 'floating', align: 'inline-start', className: ['tw:left-0'] },\n ],\n },\n);\n\ntype Props = ComponentProps<'div'> & VariantProps<typeof addonVariants>;\n\nexport const BadgeAddon = ({\n ref,\n align = 'inline-end',\n variant = 'inline',\n className,\n ...rest\n}: Props) => (\n <div\n {...rest}\n ref={ref}\n data-align={align}\n data-variant={variant}\n className={cn(addonVariants({ align, variant, className }))}\n />\n);\n","import type { ComponentProps } from 'react';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'button'>;\n\nexport const BadgeButton = ({ ref, className, type = 'button', children, ...rest }: Props) => (\n <button\n {...rest}\n ref={ref}\n type={type}\n className={cn(\n 'tailwindcss-preflight',\n\n 'tw:inline-flex',\n 'tw:items-center',\n 'tw:justify-center',\n 'tw:cursor-pointer',\n\n 'tw:bg-inherit',\n 'tw:text-inherit',\n\n 'tw:hover:brightness-95',\n 'tw:px-1',\n\n // The focus ring lives here rather than on `BadgeRoot`, whose own `focus-visible` block\n // sits on a `div` that never takes focus. Inset, because a badge root clips its overflow —\n // an outset ring on a nested button would be cut off.\n 'tw:focus-visible:outline-hidden',\n 'tw:focus-visible:ring-2',\n 'tw:focus-visible:ring-inset',\n 'tw:focus-visible:ring-ring',\n // Default sizing for Lucide-icon children (which ship at 24×24); keeps\n // the visual baseline aligned with the previous FA-icon API and other\n // icon-wrapper components (ItemIcon, SidebarMenuIcon).\n 'tw:[&_svg:not([class*=\"tw:size-\"])]:size-3',\n className,\n )}\n >\n {children}\n </button>\n);\n","import type { ComponentProps } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<'span'> & {\n asChild?: boolean;\n};\n\nexport const BadgeLabel = ({ ref, className, asChild = false, ...rest }: Props) => {\n const Comp = asChild ? Slot : 'span';\n return (\n <Comp\n {...rest}\n ref={ref}\n data-slot=\"badge-label\"\n className={cn(\n 'tw:max-w-56',\n 'tw:truncate',\n 'tw:px-2',\n 'tw:py-0.5',\n\n // Drop side padding only when an *inline* addon is present —\n // floating addons overlay the label without taking layout space,\n // so the label still needs full padding behind them.\n 'tw:group-has-[[data-align=inline-start][data-variant=inline]:not(:empty)]/badge:pl-0',\n 'tw:group-has-[[data-align=inline-end][data-variant=inline]:not(:empty)]/badge:pr-0',\n className,\n )}\n />\n );\n};\n","import { cva } from 'class-variance-authority';\n\n/**\n * Badge's `color` variants: a fill class, a foreground class, and a matching\n * `decoration-*` so an underlined badge underlines in its own fill colour.\n *\n * - Tinted (`-inverse`) variants each carry a `[data-theme=dark]` override, so the\n * chip inverts with the page instead of staying a light slab on a dark surface.\n * - Solid fills are fixed across schemes.\n * - Exported for `scripts/contrast-audit.ts` and the contrast story tests, which\n * measure each pairing rather than restating this map.\n */\nexport const badgeColors = {\n gray: 'tw:bg-neutral-400 tw:text-white tw:decoration-neutral-400',\n 'gray-darker': 'tw:bg-neutral-500 tw:text-white tw:decoration-neutral-500',\n 'gray-inverse':\n 'tw:bg-neutral-100 tw:text-neutral-500 tw:decoration-neutral-100 tw:[[data-theme=dark]_&]:bg-neutral-700 tw:[[data-theme=dark]_&]:text-neutral-300 tw:[[data-theme=dark]_&]:decoration-neutral-700',\n green: 'tw:bg-green-500 tw:text-white tw:decoration-green-500',\n 'green-darker': 'tw:bg-green-600 tw:text-white tw:decoration-green-600',\n 'green-inverse':\n 'tw:bg-green-100 tw:text-green-900 tw:decoration-green-100 tw:[[data-theme=dark]_&]:bg-green-900 tw:[[data-theme=dark]_&]:text-green-200 tw:[[data-theme=dark]_&]:decoration-green-900',\n blue: 'tw:bg-blue-500 tw:text-white tw:decoration-blue-500',\n 'blue-inverse':\n 'tw:bg-blue-100 tw:text-blue-800 tw:decoration-blue-100 tw:[[data-theme=dark]_&]:bg-blue-900 tw:[[data-theme=dark]_&]:text-blue-200 tw:[[data-theme=dark]_&]:decoration-blue-900',\n red: 'tw:bg-error-500 tw:text-white tw:decoration-error-500',\n orange: 'tw:bg-warning-500 tw:text-white tw:decoration-warning-500',\n 'orange-inverse':\n 'tw:bg-warning-100 tw:text-warning-800 tw:decoration-warning-100 tw:[[data-theme=dark]_&]:bg-warning-900 tw:[[data-theme=dark]_&]:text-warning-200 tw:[[data-theme=dark]_&]:decoration-warning-900',\n amber: 'tw:bg-warning-300 tw:text-warning-800 tw:decoration-warning-300',\n // One rung deeper than `orange-inverse` on both ends of the ramp, which is what\n // keeps caution and warning telling apart — the two share the `warning` hue.\n 'amber-inverse':\n 'tw:bg-warning-200 tw:text-warning-800 tw:decoration-warning-200 tw:[[data-theme=dark]_&]:bg-warning-800 tw:[[data-theme=dark]_&]:text-warning-200 tw:[[data-theme=dark]_&]:decoration-warning-800',\n teal: 'tw:bg-teal-500 tw:text-white tw:decoration-teal-500',\n 'teal-inverse':\n 'tw:bg-teal-100 tw:text-teal-800 tw:decoration-teal-100 tw:[[data-theme=dark]_&]:bg-teal-900 tw:[[data-theme=dark]_&]:text-teal-200 tw:[[data-theme=dark]_&]:decoration-teal-900',\n purple: 'tw:bg-purple-500 tw:text-white tw:decoration-purple-500',\n 'purple-inverse':\n 'tw:bg-purple-100 tw:text-purple-800 tw:decoration-purple-100 tw:[[data-theme=dark]_&]:bg-purple-900 tw:[[data-theme=dark]_&]:text-purple-200 tw:[[data-theme=dark]_&]:decoration-purple-900',\n cyan: 'tw:bg-cyan-500 tw:text-white tw:decoration-cyan-500',\n 'cyan-inverse':\n 'tw:bg-cyan-100 tw:text-cyan-800 tw:decoration-cyan-100 tw:[[data-theme=dark]_&]:bg-cyan-900 tw:[[data-theme=dark]_&]:text-cyan-200 tw:[[data-theme=dark]_&]:decoration-cyan-900',\n 'sidebar-primary': 'tw:bg-sidebar-primary tw:text-white tw:decoration-sidebar-primary',\n white: 'tw:bg-white/90 tw:text-neutral-600 tw:decoration-white/90',\n} as const;\n\n/** A `Badge` `color` value. */\nexport type BadgeColor = keyof typeof badgeColors;\n\n/**\n * Whether a variant paints text on its own tint rather than on a solid fill.\n *\n * - Tinted variants are the ones held to WCAG-AA per `AGENTS.md`; solid `-500`\n * fills are pinned across brands, so their contrast is recorded, not asserted.\n */\nexport const isTintedBadgeColor = (color: string): boolean => color.endsWith('-inverse');\n\nexport const badgeRootVariants = cva(\n [\n 'tailwindcss-preflight',\n 'tw:group/badge',\n\n // display\n 'tw:relative',\n 'tw:inline-flex',\n 'tw:justify-center',\n 'tw:items-center',\n 'tw:min-h-5',\n 'tw:h-fit',\n 'tw:shrink-0',\n 'tw:overflow-hidden',\n\n // text — chrome weight/tracking are brand-overridable tokens\n // (`--font-weight-medium` / `--letter-spacing-chrome`); casing lives on the\n // `default` variant below via `tw:uppercase`.\n 'tw:whitespace-pre-wrap',\n 'tw:break-normal',\n 'tw:text-xs',\n 'tw:font-medium',\n 'tw:tracking-chrome',\n 'tw:text-center',\n 'tw:underline',\n\n // content\n 'tw:border-none',\n 'tw:[&>svg]:self-center',\n 'tw:[button&]:cursor-pointer',\n\n // focus\n 'tw:focus-visible:outline-hidden',\n 'tw:focus-visible:ring-2',\n 'tw:focus-visible:ring-inset',\n 'tw:focus-visible:ring-ring',\n ],\n {\n defaultVariants: {\n color: 'gray',\n variant: 'default',\n },\n variants: {\n variant: {\n // Casing is the legacy brands' to ask for: `keboola-legacy-*` uppercases the default\n // badge, every other brand reads standard case. Same rule, same shape, as `Button` and\n // `Tooltip`.\n default: ['tw:rounded', 'tw:[[data-brand^=keboola-legacy]_&]:uppercase'],\n tag: ['tw:rounded tw:normal-case'],\n pill: ['tw:min-w-5 tw:rounded-full tw:normal-case tw:tracking-normal'],\n },\n color: badgeColors,\n },\n },\n);\n","import type { ComponentProps } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '../../utils';\n\nimport { badgeRootVariants } from './styles';\n\ntype Props = ComponentProps<'div'> & VariantProps<typeof badgeRootVariants>;\n\nexport const BadgeRoot = ({ ref, variant, color, className, children, ...rest }: Props) => (\n <div\n {...rest}\n ref={ref}\n data-slot=\"badge\"\n className={cn(badgeRootVariants({ variant, color, className }))}\n >\n {children}\n </div>\n);\n","import type { ComponentProps, ReactNode } from 'react';\n\nimport { BadgeAddon } from './BadgeAddon';\nimport { BadgeButton } from './BadgeButton';\nimport { BadgeLabel } from './BadgeLabel';\nimport { BadgeRoot } from './BadgeRoot';\n\ntype AddonVariant = ComponentProps<typeof BadgeAddon>['variant'];\ntype BadgeRootProps = ComponentProps<typeof BadgeRoot>;\ntype BadgeLabelProps = ComponentProps<typeof BadgeLabel>;\n\n/**\n * Props for `Badge`. `color` is a categorical contract (`green` = success, `red` = error, `orange` = warning,\n * `amber` = caution, `purple/cyan/teal/blue/gray` for decorative tags) — colours stay fixed across brand themes.\n * Pass `prefix` / `suffix` for icon-style addons.\n */\ntype Props = Pick<BadgeRootProps, 'variant' | 'color'> &\n Omit<BadgeLabelProps, 'prefix'> & {\n prefix?: ReactNode;\n prefixVariant?: AddonVariant;\n suffix?: ReactNode;\n suffixVariant?: AddonVariant;\n labelClassName?: string;\n };\n\n/**\n * Compact status / category label rendered as an inline pill.\n *\n * **When to use:** encoding status (active, deprecated, beta), counts, or decorative tags next to a title\n * or in a table cell. The `color` prop is a semantic contract — `green` means success, `red` means error\n * etc., and colours are intentionally stable across customer brand themes.\n * **When NOT to use:** for an inline action use `Button` / `ButtonInline`; for a persistent contextual\n * banner use `Alert`; for a counter-only chip prefer `Badge` with just a numeric label.\n * **Composition:** drop into `PageHeader.Detail` `badges` slot, `Table.Cell`, or alongside titles in cards.\n * Use `Badge.Button` for an interactive (clickable) badge variant.\n */\nconst BadgeInner = ({\n ref,\n // root props\n variant,\n color,\n // custom props\n prefix,\n prefixVariant = 'inline',\n suffix,\n suffixVariant = 'inline',\n className,\n labelClassName,\n ...labelProps\n}: Props) => (\n <BadgeRoot className={className} variant={variant} color={color}>\n {prefix && (\n <BadgeAddon align=\"inline-start\" variant={prefixVariant}>\n {prefix}\n </BadgeAddon>\n )}\n <BadgeLabel {...labelProps} ref={ref} className={labelClassName} />\n {suffix && (\n <BadgeAddon align=\"inline-end\" variant={suffixVariant}>\n {suffix}\n </BadgeAddon>\n )}\n </BadgeRoot>\n);\n\nexport const Badge = Object.assign(BadgeInner, {\n Root: BadgeRoot,\n Button: BadgeButton,\n Label: BadgeLabel,\n Addon: BadgeAddon,\n});\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Button } from "./Button-DsRrU5F1.js";
|
|
2
2
|
import { ToggleGroup } from "./ToggleGroup-Cy_10Mvu.js";
|
|
3
3
|
import { Label } from "./internal/components/Label.js";
|
|
4
|
-
import { TextInput } from "./TextInput-
|
|
5
|
-
import { Slider } from "./Slider-
|
|
4
|
+
import { TextInput } from "./TextInput-DiEMxVF6.js";
|
|
5
|
+
import { Slider } from "./Slider-Bv0B6lI0.js";
|
|
6
6
|
import { Select } from "./Select-DV3Hak_l.js";
|
|
7
|
-
import { Badge } from "./Badge-
|
|
8
|
-
import { ColorInput } from "./ColorInput-
|
|
7
|
+
import { Badge } from "./Badge-o5jURtpB.js";
|
|
8
|
+
import { ColorInput } from "./ColorInput-Dg87lKcR.js";
|
|
9
9
|
import { Textarea } from "./internal/blocks/Textarea.js";
|
|
10
10
|
import { Collapsible } from "./Collapsible-BQXWEOBC.js";
|
|
11
11
|
import { useId, useMemo } from "react";
|
|
@@ -900,4 +900,4 @@ const BrandEditor = ({ value, onChange, colorScheme = "light", onColorSchemeChan
|
|
|
900
900
|
//#endregion
|
|
901
901
|
export { BrandEditor, DEFAULT_FALLBACK_FONT, DEFAULT_FONT_OPTIONS, channelsToHex, contrastRatio, hexToChannels, wcagLevel };
|
|
902
902
|
|
|
903
|
-
//# sourceMappingURL=BrandEditor-
|
|
903
|
+
//# sourceMappingURL=BrandEditor-CS9P4r2p.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrandEditor-CSANsxUz.js","names":[],"sources":["../src/blocks/BrandEditor/color.ts","../src/blocks/BrandEditor/ColorField.tsx","../src/blocks/BrandEditor/contrast.ts","../src/blocks/BrandEditor/ControlSection.tsx","../src/blocks/BrandEditor/LogoField.tsx","../src/blocks/BrandEditor/TextField.tsx","../src/blocks/BrandEditor/BrandEditor.tsx"],"sourcesContent":["// Brand colours are stored as space-separated \"R G B\" channel strings (see\n// `rgbChannelsSchema` in @keboola/brand-registry); `ColorInput` speaks hex.\n// These helpers convert at that boundary.\n\nexport const hexToChannels = (hex: string): string => {\n let h = hex.trim().replace(/^#/, '');\n // Expand 3/4-digit shorthand and drop alpha (4- and 8-digit `#rgba`/`#rrggbbaa`) — brand\n // channels carry no alpha. Anything else is malformed → fall back to black.\n if (h.length === 3 || h.length === 4) {\n h = h\n .slice(0, 3)\n .split('')\n .map((c) => c + c)\n .join('');\n } else if (h.length === 8) {\n h = h.slice(0, 6);\n }\n if (!/^[0-9a-fA-F]{6}$/.test(h)) return '0 0 0';\n const n = parseInt(h, 16);\n return `${(n >> 16) & 255} ${(n >> 8) & 255} ${n & 255}`;\n};\n\nexport const channelsToHex = (channels: string): string => {\n const [r = 0, g = 0, b = 0] = channels\n .trim()\n .split(/\\s+/)\n .map((n) => Number(n) || 0);\n const to2 = (v: number) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, '0');\n return `#${to2(r)}${to2(g)}${to2(b)}`;\n};\n","import { Label } from '../../components/Label';\nimport { ColorInput } from '../ColorInput';\n\nimport { channelsToHex, hexToChannels } from './color';\n\ntype Props = {\n id: string;\n label: string;\n /** Current value as an \"R G B\" channel string. */\n channels: string;\n /** Called with a new \"R G B\" channel string when a valid colour is picked. */\n onChange: (channels: string) => void;\n};\n\n// ColorInput fires `onValueChange` on every keystroke (it only coerces on blur), so commit a\n// channel value only for a COMPLETE 3-, 4-, 6-, or 8-digit hex — otherwise a partial \"#1\"/\"#\"\n// would write unintended channels. Alpha (4-/8-digit) is truncated to RGB, since brand tokens\n// carry no alpha.\nconst COMPLETE_HEX = /^([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\n\n/** A labelled hex colour picker bound to a brand token (stored as RGB channels). */\nexport const ColorField = ({ id, label, channels, onChange }: Props) => {\n const hex = channelsToHex(channels);\n\n return (\n <div className=\"tw:flex tw:items-center tw:justify-between tw:gap-2\">\n <Label htmlFor={id} className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {label}\n </Label>\n <ColorInput\n id={id}\n sizing=\"medium\"\n value={hex}\n className=\"tw:w-36\"\n onValueChange={(next) => {\n const clean = next.trim().replace(/^#/, '');\n // Commit only complete hex (hexToChannels handles the 3/6/8-digit shapes + alpha drop).\n if (COMPLETE_HEX.test(clean)) onChange(hexToChannels(`#${clean}`));\n }}\n />\n </div>\n );\n};\n","// WCAG contrast helpers over `\"R G B\"` channel strings (the brand token format).\n\n// Coerce non-numeric/blank channels to 0 and clamp to the valid 0–255 range, so a malformed\n// token (e.g. \"999 999 999\") can neither NaN-out nor push luminance past WCAG's expected bounds.\nconst clampChannel = (n: number): number =>\n Number.isFinite(n) ? Math.min(255, Math.max(0, n)) : 0;\n\nconst parseChannels = (channels: string): [number, number, number] => {\n const [r = 0, g = 0, b = 0] = channels\n .trim()\n .split(/\\s+/)\n .map((n) => clampChannel(Number(n)));\n return [r, g, b];\n};\n\nconst relativeLuminance = (channels: string): number => {\n // Destructure with defaults (not an `as` assertion) so `r`/`g`/`b` are `number` under\n // `noUncheckedIndexedAccess` without a production-code type cast.\n const [r = 0, g = 0, b = 0] = parseChannels(channels).map((c) => {\n const s = c / 255;\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n });\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n};\n\n/** WCAG contrast ratio (1–21) between two `\"R G B\"` colours. */\nexport const contrastRatio = (fg: string, bg: string): number => {\n const l1 = relativeLuminance(fg);\n const l2 = relativeLuminance(bg);\n const [lighter, darker] = l1 >= l2 ? [l1, l2] : [l2, l1];\n return (lighter + 0.05) / (darker + 0.05);\n};\n\n/** WCAG level for normal-size text at a given ratio. */\nexport const wcagLevel = (ratio: number): 'AAA' | 'AA' | 'fail' =>\n ratio >= 7 ? 'AAA' : ratio >= 4.5 ? 'AA' : 'fail';\n","import type { ReactNode } from 'react';\n\ntype Props = {\n title: string;\n children: ReactNode;\n};\n\n/** A titled block inside `BrandEditor`. */\nexport const ControlSection = ({ title, children }: Props) => (\n <section className=\"tw:flex tw:flex-col tw:gap-3 tw:border-b tw:border-border tw:p-4\">\n <h3 className=\"tw:m-0 tw:text-xs tw:font-semibold tw:uppercase tw:tracking-wide tw:text-muted-foreground\">\n {title}\n </h3>\n {children}\n </section>\n);\n","import { Label } from '../../components/Label';\nimport { Textarea } from '../Textarea';\n\ntype Props = {\n id: string;\n label: string;\n /** Current logo source: SVG markup, a `data:` URI, or an `http(s)` URL. */\n value: string;\n /** Called with the new source (empty string clears the slot). */\n onChange: (value: string) => void;\n};\n\n/** A labelled multi-line field for a brand logo source. */\nexport const LogoField = ({ id, label, value, onChange }: Props) => (\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label htmlFor={id} className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {label}\n </Label>\n <Textarea\n id={id}\n value={value}\n onValueChange={onChange}\n maxRows={4}\n placeholder=\"<svg…>, a data: URI, or an https:// URL\"\n className=\"tw:font-mono tw:text-xs\"\n />\n </div>\n);\n","import { Label } from '../../components/Label';\nimport { TextInput } from '../TextInput';\n\ntype Props = {\n id: string;\n label: string;\n /** Current CSS value (empty clears the token, falling back to the stylesheet default). */\n value: string;\n /** Called with the new CSS value on change. */\n onChange: (value: string) => void;\n /** The inherited default, shown as placeholder when the field is empty. */\n placeholder?: string;\n};\n\n/** A labelled single-line field for a free-form CSS-value token (shadow, motion, gradient). */\nexport const TextField = ({ id, label, value, onChange, placeholder }: Props) => (\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label htmlFor={id} className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {label}\n </Label>\n <TextInput\n id={id}\n sizing=\"small\"\n value={value}\n onValueChange={onChange}\n placeholder={placeholder}\n className=\"tw:font-mono tw:text-xs\"\n />\n </div>\n);\n","import { useId, useMemo } from 'react';\nimport DOMPurify from 'dompurify';\n\nimport type { Brand } from '@keboola/brand-registry';\nimport { keboola } from '@keboola/brand-registry';\n\nimport { Badge } from '../../components/Badge';\nimport { Button } from '../../components/Button';\nimport { Collapsible } from '../../components/Collapsible';\nimport { Label } from '../../components/Label';\nimport { Select } from '../../components/Select';\nimport { Slider } from '../../components/Slider';\nimport { ToggleGroup } from '../../components/ToggleGroup';\n\nimport { ColorField } from './ColorField';\nimport { contrastRatio, wcagLevel } from './contrast';\nimport { ControlSection } from './ControlSection';\nimport { LogoField } from './LogoField';\nimport { TextField } from './TextField';\n\nexport type ColorSlot = keyof Omit<NonNullable<Brand['theme']['colors']>, 'sidebar' | 'chart'>;\nexport type SidebarSlot = keyof NonNullable<NonNullable<Brand['theme']['colors']>['sidebar']>;\nexport type ChartSlot = keyof NonNullable<NonNullable<Brand['theme']['colors']>['chart']>;\nexport type RadiusSize = keyof NonNullable<Brand['theme']['radii']>;\n\ntype Typography = NonNullable<Brand['theme']['typography']>;\ntype FontFace = 'fontFamily' | 'fontDisplay' | 'fontMono';\ntype FontWeightSlot = keyof NonNullable<Typography['weight']>;\ntype ShadowSlot = keyof NonNullable<Brand['theme']['shadows']>;\ntype SpacingSlot = keyof NonNullable<Brand['theme']['spacing']>;\ntype MotionSlot = keyof NonNullable<Brand['theme']['motion']>;\n\nexport const DEFAULT_FALLBACK_FONT = \"'Inter', system-ui, sans-serif\";\n\nexport const DEFAULT_FONT_OPTIONS: { value: string; label: string }[] = [\n { value: DEFAULT_FALLBACK_FONT, label: 'Inter (default)' },\n { value: \"'Space Grotesk', system-ui, sans-serif\", label: 'Space Grotesk' },\n { value: \"'Poppins', system-ui, sans-serif\", label: 'Poppins' },\n { value: \"'Manrope', system-ui, sans-serif\", label: 'Manrope' },\n { value: \"'Quicksand', system-ui, sans-serif\", label: 'Quicksand' },\n { value: \"'Baloo 2', system-ui, sans-serif\", label: 'Baloo 2' },\n { value: \"'Fraunces', Georgia, serif\", label: 'Fraunces (serif)' },\n { value: \"Georgia, 'Times New Roman', serif\", label: 'Georgia (serif)' },\n { value: \"'JetBrains Mono', ui-monospace, monospace\", label: 'JetBrains Mono' },\n { value: 'system-ui, sans-serif', label: 'System UI' },\n];\n\n// Label maps double as a compile-time exhaustiveness guard: typed as\n// `Record<ColorSlot, string>` / `Record<SidebarSlot, string>`, a new token added\n// to the upstream `Brand` schema fails to compile here until it gets a control,\n// so it can't silently drift out of the editor. Insertion order is display order.\nconst COLOR_LABELS: Record<ColorSlot, string> = {\n primary: 'Primary',\n primaryForeground: 'Primary foreground',\n background: 'Background',\n foreground: 'Foreground',\n card: 'Card',\n cardForeground: 'Card foreground',\n popover: 'Popover',\n popoverForeground: 'Popover foreground',\n secondary: 'Secondary',\n secondaryForeground: 'Secondary foreground',\n muted: 'Muted',\n mutedForeground: 'Muted foreground',\n accent: 'Accent',\n accentForeground: 'Accent foreground',\n border: 'Border',\n input: 'Input border',\n inputBackground: 'Input background',\n ring: 'Ring',\n ringOffset: 'Ring offset',\n destructive: 'Destructive',\n destructiveForeground: 'Destructive foreground',\n success: 'Success',\n successForeground: 'Success foreground',\n warning: 'Warning',\n warningForeground: 'Warning foreground',\n info: 'Info',\n infoForeground: 'Info foreground',\n};\n\nconst SIDEBAR_LABELS: Record<SidebarSlot, string> = {\n sidebar: 'Sidebar',\n sidebarForeground: 'Foreground',\n sidebarPrimary: 'Primary',\n sidebarPrimaryForeground: 'Primary foreground',\n sidebarAccent: 'Accent',\n sidebarAccentForeground: 'Accent foreground',\n sidebarBorder: 'Border',\n sidebarRing: 'Ring',\n};\n\nconst CHART_LABELS: Record<ChartSlot, string> = {\n chart1: 'Chart 1',\n chart2: 'Chart 2',\n chart3: 'Chart 3',\n chart4: 'Chart 4',\n chart5: 'Chart 5',\n};\n\nconst WEIGHT_LABELS: Record<FontWeightSlot, string> = {\n normal: 'Normal',\n medium: 'Medium',\n semibold: 'Semibold',\n bold: 'Bold',\n};\n\nconst SHADOW_LABELS: Record<ShadowSlot, string> = {\n sm: 'Small',\n md: 'Medium',\n lg: 'Large',\n lift: 'Lift (raised)',\n};\n\nconst SPACING_LABELS: Record<SpacingSlot, string> = {\n gutter: 'Gutter (between sections)',\n inset: 'Inset (in-container)',\n};\n\nconst MOTION_LABELS: Record<MotionSlot, string> = {\n duration: 'Duration',\n easing: 'Easing',\n};\n\nconst COLOR_SLOTS = Object.keys(COLOR_LABELS) as ColorSlot[];\nconst SIDEBAR_SLOTS = Object.keys(SIDEBAR_LABELS) as SidebarSlot[];\nconst CHART_SLOTS = Object.keys(CHART_LABELS) as ChartSlot[];\nconst WEIGHT_SLOTS = Object.keys(WEIGHT_LABELS) as FontWeightSlot[];\nconst SHADOW_SLOTS = Object.keys(SHADOW_LABELS) as ShadowSlot[];\nconst SPACING_SLOTS = Object.keys(SPACING_LABELS) as SpacingSlot[];\nconst MOTION_SLOTS = Object.keys(MOTION_LABELS) as MotionSlot[];\nconst RADIUS_SIZES: RadiusSize[] = ['sm', 'md', 'lg'];\n\n// Foreground/background pairs whose contrast decides everyday legibility.\nconst A11Y_PAIRS: { label: string; fg: ColorSlot; bg: ColorSlot }[] = [\n { label: 'Text on background', fg: 'foreground', bg: 'background' },\n { label: 'Text on card', fg: 'cardForeground', bg: 'card' },\n { label: 'Primary button', fg: 'primaryForeground', bg: 'primary' },\n { label: 'Muted text', fg: 'mutedForeground', bg: 'muted' },\n { label: 'Accent', fg: 'accentForeground', bg: 'accent' },\n];\n\n// Fallback sidebar palette — the `keboola` brand doesn't set `colors.sidebar` /\n// `colorsDark.sidebar`, so a slot needs a value to seed from when a user starts\n// editing sidebar tokens on a brand that leaves them unset.\nconst DEFAULT_SIDEBAR: Record<'light' | 'dark', Record<SidebarSlot, string>> = {\n light: {\n sidebar: '255 255 255',\n sidebarForeground: '30 41 59',\n sidebarPrimary: '31 143 255',\n sidebarPrimaryForeground: '255 255 255',\n sidebarAccent: '219 234 254',\n sidebarAccentForeground: '30 41 59',\n sidebarBorder: '229 231 235',\n sidebarRing: '31 143 255',\n },\n dark: {\n sidebar: '14 14 18',\n sidebarForeground: '229 230 236',\n sidebarPrimary: '31 143 255',\n sidebarPrimaryForeground: '255 255 255',\n sidebarAccent: '38 38 45',\n sidebarAccentForeground: '229 230 236',\n sidebarBorder: '48 49 57',\n sidebarRing: '31 143 255',\n },\n};\n\nconst DEFAULT_RADII: NonNullable<Brand['theme']['radii']> = {\n sm: '0.25rem',\n md: '0.375rem',\n lg: '0.5rem',\n};\n\n// The `@keboola/design/styles` categorical chart palette — seeds the chart\n// pickers when a brand leaves `colors.chart` / `colorsDark.chart` unset.\nconst DEFAULT_CHART: Record<'light' | 'dark', Record<ChartSlot, string>> = {\n light: {\n chart1: '31 143 255',\n chart2: '0 194 135',\n chart3: '146 92 255',\n chart4: '245 147 0',\n chart5: '237 94 223',\n },\n dark: {\n chart1: '92 173 255',\n chart2: '0 230 161',\n chart3: '173 133 255',\n chart4: '255 190 92',\n chart5: '242 140 232',\n },\n};\n\n// Chrome font-weight defaults (the Tailwind scale `@keboola/design` falls back to\n// when `typography.weight` is unset) — seed values for the weight sliders.\nconst DEFAULT_FONT_WEIGHTS: Record<FontWeightSlot, string> = {\n normal: '400',\n medium: '500',\n semibold: '600',\n bold: '700',\n};\n\n// Chrome tracking default (behind the hardcoded `tw:tracking-[1px]`).\nconst DEFAULT_LETTER_SPACING = '1px';\n\n// The monospace stack the mono-face Select falls back to when `fontMono` is unset\n// (matches the `DEFAULT_FONT_OPTIONS` JetBrains Mono entry).\nconst MONO_FALLBACK = \"'JetBrains Mono', ui-monospace, monospace\";\n\n// `@keboola/design/styles` defaults for the advanced token groups, shown as\n// placeholders so an unset field reveals the value it inherits.\nconst DEFAULT_SHADOWS: Record<ShadowSlot, string> = {\n sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',\n md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',\n lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',\n lift: '0 10px 30px -12px rgb(0 0 0 / 0.28), 0 2px 6px rgb(0 0 0 / 0.06)',\n};\n\nconst DEFAULT_SPACING: Record<SpacingSlot, string> = {\n gutter: '1.5rem',\n inset: '1.5rem',\n};\n\nconst DEFAULT_MOTION: Record<MotionSlot, string> = {\n duration: '200ms',\n easing: 'cubic-bezier(0.2, 0.7, 0.2, 1)',\n};\n\nconst DEFAULT_GRADIENT =\n 'linear-gradient(135deg, rgb(var(--primary)), rgb(var(--accent, var(--primary))))';\n\n// Leading numeric value of a token string (font-weight \"500\").\nconst numOf = (value: string, fallback: number): number => {\n const n = Number.parseFloat(value);\n return Number.isFinite(n) ? n : fallback;\n};\n\n// Letter-spacing may be px or em. Drive the slider in px (treating `em` as ×16,\n// like the radius slider treats `rem`) and write back in the value's original\n// unit, so an `em`-authored brand (e.g. `0.02em`) stays `em` instead of being\n// silently rewritten to `px`.\nconst letterSpacingToPx = (value: string): number => {\n const n = Number.parseFloat(value);\n if (!Number.isFinite(n)) return 0;\n return value.trim().endsWith('em') ? n * 16 : n;\n};\n\nconst pxToLetterSpacing = (px: number, currentValue: string): string =>\n currentValue.trim().endsWith('em') ? `${Number((px / 16).toFixed(3))}em` : `${px}px`;\n\n// The Brand schema allows any CSS length for radii (`rem`, `px`, unitless). Convert to px for\n// the slider treating `rem` as ×16 and everything else as already-px, and write back preserving\n// the slot's original unit so `8px` stays `8px`, `0.5rem` stays `0.5rem`, and a unitless `8` stays `8`.\nconst lengthToPx = (value: string): number => {\n const n = Number.parseFloat(value);\n if (!Number.isFinite(n)) return 0;\n return Math.round(value.trim().endsWith('rem') ? n * 16 : n);\n};\n\nconst pxToLength = (px: number, currentValue: string): string => {\n const trimmed = currentValue.trim();\n if (trimmed.endsWith('rem')) return `${px / 16}rem`;\n // Preserve a unitless token (the schema allows it) rather than forcing `px` onto it.\n if (/^-?\\d*\\.?\\d+$/.test(trimmed)) return `${px}`;\n return `${px}px`;\n};\n\nexport type BrandEditorProps = {\n value: Brand;\n onChange: (next: Brand) => void;\n /** Palette being edited. Controlled. Default 'light'. */\n colorScheme?: 'light' | 'dark';\n /** If provided, the editor shows a light/dark toggle that calls this. If omitted, no toggle. */\n onColorSchemeChange?: (scheme: 'light' | 'dark') => void;\n /** Font choices for the typography Select. Defaults to DEFAULT_FONT_OPTIONS. */\n fontOptions?: { value: string; label: string }[];\n};\n\n// Match `BrandLogo`'s sniffing: any string starting with `<` is inline markup (covers\n// `<?xml …?><svg>` / `<!doctype …>` prefixes); only `data:` / `http(s)://` are image URLs.\nconst isSvgMarkup = (source: string) => source.trimStart().startsWith('<');\nconst isImageSource = (source: string) => /^(data:|https?:\\/\\/)/.test(source.trim());\n\n/** Renders a logo source (SVG markup, `data:` URI, or `http(s)` URL) from the raw string. */\nconst LogoPreviewImage = ({ source, className }: { source: string; className?: string }) => {\n const safeSvg = useMemo(\n () =>\n isSvgMarkup(source)\n ? DOMPurify.sanitize(source, { USE_PROFILES: { svg: true, svgFilters: true } })\n : null,\n [source],\n );\n\n if (safeSvg !== null) {\n // Decorative preview of what the caller is editing — the textarea holds the real value, so\n // hide the injected graphic from assistive tech (parity with the `alt=\"\"` image branch below).\n return <span aria-hidden className={className} dangerouslySetInnerHTML={{ __html: safeSvg }} />;\n }\n if (isImageSource(source)) {\n return <img src={source} alt=\"\" className={className} />;\n }\n return null;\n};\n\n/**\n * Controlled brand-editing block — colors (light/dark), sidebar colors, WCAG contrast readout,\n * corner radius, typography, and logo. Reads/writes a `Brand` (`@keboola/brand-registry`) via\n * `value` / `onChange`; owns no state of its own, so the caller decides where the brand and the\n * active color scheme live (Kai build-mode preview, theme-generator, …).\n */\nexport const BrandEditor = ({\n value,\n onChange,\n colorScheme = 'light',\n onColorSchemeChange,\n fontOptions = DEFAULT_FONT_OPTIONS,\n}: BrandEditorProps) => {\n // Prefix every control id so multiple BrandEditors on one page don't collide (broken label↔control).\n const baseId = useId();\n const { theme } = value;\n const coreColors = colorScheme === 'light' ? theme.colors : theme.colorsDark;\n const sidebarColors = colorScheme === 'light' ? theme.colors?.sidebar : theme.colorsDark?.sidebar;\n const chartColors = colorScheme === 'light' ? theme.colors?.chart : theme.colorsDark?.chart;\n // Fall back to the keboola default of the SAME scheme — never light values while editing dark.\n const defaultColors = colorScheme === 'light' ? keboola.theme.colors : keboola.theme.colorsDark;\n const radii = theme.radii ?? DEFAULT_RADII;\n const typography = theme.typography;\n\n const setColor = (slot: ColorSlot, channels: string) => {\n if (colorScheme === 'light') {\n onChange({ ...value, theme: { ...theme, colors: { ...theme.colors, [slot]: channels } } });\n return;\n }\n // Keep colorsDark sparse (mirrors the light path) so an unset dark palette inherits the\n // design-system defaults instead of baking in a full keboola palette on the first edit.\n onChange({\n ...value,\n theme: { ...theme, colorsDark: { ...theme.colorsDark, [slot]: channels } },\n });\n };\n\n const setSidebarColor = (slot: SidebarSlot, channels: string) => {\n if (colorScheme === 'light') {\n const base = theme.colors?.sidebar ?? DEFAULT_SIDEBAR.light;\n onChange({\n ...value,\n theme: { ...theme, colors: { ...theme.colors, sidebar: { ...base, [slot]: channels } } },\n });\n return;\n }\n // Spread the existing (possibly-empty) colorsDark so setting a sidebar token doesn't seed the\n // whole core dark palette; only the sidebar sub-object is seeded, since it has no default to inherit.\n const sidebarBase = theme.colorsDark?.sidebar ?? DEFAULT_SIDEBAR.dark;\n onChange({\n ...value,\n theme: {\n ...theme,\n colorsDark: { ...theme.colorsDark, sidebar: { ...sidebarBase, [slot]: channels } },\n },\n });\n };\n\n const setChart = (slot: ChartSlot, channels: string) => {\n const base = chartColors ?? DEFAULT_CHART[colorScheme];\n const chart = { ...base, [slot]: channels };\n if (colorScheme === 'light') {\n onChange({ ...value, theme: { ...theme, colors: { ...theme.colors, chart } } });\n return;\n }\n onChange({ ...value, theme: { ...theme, colorsDark: { ...theme.colorsDark, chart } } });\n };\n\n const setRadius = (size: RadiusSize, radius: string) =>\n onChange({\n ...value,\n theme: { ...theme, radii: { ...(theme.radii ?? DEFAULT_RADII), [size]: radius } },\n });\n\n // Merge a typography patch, keeping sibling faces / weight / letterSpacing.\n const patchTypography = (patch: Partial<Typography>) =>\n onChange({ ...value, theme: { ...theme, typography: { ...theme.typography, ...patch } } });\n\n const setFontFace = (face: FontFace, font: string) =>\n patchTypography({ [face]: font.trim() || undefined });\n\n const setFontWeight = (slot: FontWeightSlot, weight: string) =>\n patchTypography({ weight: { ...theme.typography?.weight, [slot]: weight || undefined } });\n\n const setLetterSpacing = (chrome: string) =>\n patchTypography({ letterSpacing: { chrome: chrome.trim() || undefined } });\n\n const setShadow = (slot: ShadowSlot, shadow: string) =>\n onChange({\n ...value,\n theme: { ...theme, shadows: { ...theme.shadows, [slot]: shadow.trim() || undefined } },\n });\n\n const setSpacing = (slot: SpacingSlot, length: string) =>\n onChange({\n ...value,\n theme: { ...theme, spacing: { ...theme.spacing, [slot]: length.trim() || undefined } },\n });\n\n const setMotion = (slot: MotionSlot, token: string) =>\n onChange({\n ...value,\n theme: { ...theme, motion: { ...theme.motion, [slot]: token.trim() || undefined } },\n });\n\n const setGradient = (gradient: string) =>\n onChange({\n ...value,\n theme: { ...theme, gradient: { brand: gradient.trim() || undefined } },\n });\n\n const setLogo = (slot: keyof NonNullable<Brand['logo']>, source: string) => {\n const trimmed = source.trim();\n const nextLogo: NonNullable<Brand['logo']> = { ...value.logo };\n if (trimmed) nextLogo[slot] = trimmed;\n else delete nextLogo[slot];\n const { logo: _drop, ...rest } = value;\n onChange(Object.keys(nextLogo).length > 0 ? { ...value, logo: nextLogo } : rest);\n };\n\n return (\n <div className=\"tw:flex tw:flex-col\">\n <ControlSection title=\"Colors\">\n {onColorSchemeChange ? (\n <ToggleGroup\n type=\"single\"\n value={colorScheme}\n onValueChange={(next) => {\n if (next === 'light' || next === 'dark') onColorSchemeChange(next);\n }}\n aria-label=\"Color scheme\"\n className=\"tw:w-full\"\n >\n <ToggleGroup.Item value=\"light\" className=\"tw:flex-1\">\n Light\n </ToggleGroup.Item>\n <ToggleGroup.Item value=\"dark\" className=\"tw:flex-1\">\n Dark\n </ToggleGroup.Item>\n </ToggleGroup>\n ) : null}\n <div className=\"tw:flex tw:flex-col tw:gap-2\">\n {COLOR_SLOTS.map((slot) => (\n <ColorField\n key={slot}\n id={`${baseId}color-${colorScheme}-${slot}`}\n label={COLOR_LABELS[slot]}\n channels={coreColors?.[slot] ?? defaultColors?.[slot] ?? '0 0 0'}\n onChange={(channels) => setColor(slot, channels)}\n />\n ))}\n </div>\n </ControlSection>\n\n <ControlSection title=\"Accessibility\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n WCAG contrast for the {colorScheme} palette (AA needs 4.5:1).\n </p>\n <div className=\"tw:flex tw:flex-col tw:gap-1.5\">\n {A11Y_PAIRS.map((pair) => {\n const fg = coreColors?.[pair.fg] ?? defaultColors?.[pair.fg] ?? '0 0 0';\n const bg = coreColors?.[pair.bg] ?? defaultColors?.[pair.bg] ?? '255 255 255';\n const ratio = contrastRatio(fg, bg);\n const level = wcagLevel(ratio);\n return (\n <div key={pair.label} className=\"tw:flex tw:items-center tw:justify-between tw:gap-2\">\n <span className=\"tw:text-xs tw:text-foreground\">{pair.label}</span>\n <span className=\"tw:flex tw:items-center tw:gap-1.5\">\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {ratio.toFixed(1)}:1\n </span>\n <Badge color={level === 'fail' ? 'red' : 'green'}>\n {level === 'fail' ? 'Fail' : level}\n </Badge>\n </span>\n </div>\n );\n })}\n </div>\n </ControlSection>\n\n <ControlSection title=\"Sidebar colors\">\n <Collapsible className=\"tw:flex tw:flex-col tw:gap-2\">\n <Collapsible.Trigger asChild>\n <Button variant=\"outline\" size=\"small\" className=\"tw:w-full\">\n Toggle sidebar tokens ({colorScheme})\n </Button>\n </Collapsible.Trigger>\n <Collapsible.Content className=\"tw:flex tw:flex-col tw:gap-2\">\n {SIDEBAR_SLOTS.map((slot) => (\n <ColorField\n key={slot}\n id={`${baseId}sidebar-${colorScheme}-${slot}`}\n label={SIDEBAR_LABELS[slot]}\n channels={sidebarColors?.[slot] ?? DEFAULT_SIDEBAR[colorScheme][slot]}\n onChange={(channels) => setSidebarColor(slot, channels)}\n />\n ))}\n </Collapsible.Content>\n </Collapsible>\n </ControlSection>\n\n <ControlSection title=\"Chart palette\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Categorical series colours for the {colorScheme} palette (`--chart-1…5`).\n </p>\n <div className=\"tw:flex tw:flex-col tw:gap-2\">\n {CHART_SLOTS.map((slot) => (\n <ColorField\n key={slot}\n id={`${baseId}chart-${colorScheme}-${slot}`}\n label={CHART_LABELS[slot]}\n channels={chartColors?.[slot] ?? DEFAULT_CHART[colorScheme][slot]}\n onChange={(channels) => setChart(slot, channels)}\n />\n ))}\n </div>\n </ControlSection>\n\n <ControlSection title=\"Radius\">\n {RADIUS_SIZES.map((size) => {\n const current = radii[size];\n const px = lengthToPx(current);\n return (\n <div key={size} className=\"tw:flex tw:flex-col tw:gap-1\">\n <div className=\"tw:flex tw:items-center tw:justify-between\">\n <Label className=\"tw:text-xs tw:font-normal tw:uppercase tw:text-foreground\">\n {size}\n </Label>\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {radii[size]}\n </span>\n </div>\n <Slider\n min={0}\n max={24}\n step={1}\n value={[px]}\n onValueChange={(values) => setRadius(size, pxToLength(values[0] ?? 0, current))}\n />\n </div>\n );\n })}\n </ControlSection>\n\n <ControlSection title=\"Typography\">\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label\n htmlFor={`${baseId}font-family`}\n className=\"tw:text-xs tw:font-normal tw:text-foreground\"\n >\n Font family\n </Label>\n <Select\n id={`${baseId}font-family`}\n value={typography?.fontFamily ?? DEFAULT_FALLBACK_FONT}\n onValueChange={(font) => setFontFace('fontFamily', font)}\n items={fontOptions}\n />\n </div>\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label\n htmlFor={`${baseId}font-display`}\n className=\"tw:text-xs tw:font-normal tw:text-foreground\"\n >\n Display font (headings)\n </Label>\n <Select\n id={`${baseId}font-display`}\n value={typography?.fontDisplay ?? typography?.fontFamily ?? DEFAULT_FALLBACK_FONT}\n onValueChange={(font) => setFontFace('fontDisplay', font)}\n items={fontOptions}\n />\n </div>\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label\n htmlFor={`${baseId}font-mono`}\n className=\"tw:text-xs tw:font-normal tw:text-foreground\"\n >\n Monospace font\n </Label>\n <Select\n id={`${baseId}font-mono`}\n value={typography?.fontMono ?? MONO_FALLBACK}\n onValueChange={(font) => setFontFace('fontMono', font)}\n items={fontOptions}\n />\n </div>\n\n <span className=\"tw:text-xs tw:text-muted-foreground\">Chrome font weights</span>\n {WEIGHT_SLOTS.map((slot) => {\n const current = typography?.weight?.[slot] ?? DEFAULT_FONT_WEIGHTS[slot];\n return (\n <div key={slot} className=\"tw:flex tw:flex-col tw:gap-1\">\n <div className=\"tw:flex tw:items-center tw:justify-between\">\n <Label className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {WEIGHT_LABELS[slot]}\n </Label>\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {current}\n </span>\n </div>\n <Slider\n min={100}\n max={900}\n step={25}\n value={[numOf(current, 400)]}\n onValueChange={(values) => setFontWeight(slot, String(values[0] ?? 400))}\n />\n </div>\n );\n })}\n\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <div className=\"tw:flex tw:items-center tw:justify-between\">\n <Label className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n Chrome letter spacing\n </Label>\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {typography?.letterSpacing?.chrome ?? DEFAULT_LETTER_SPACING}\n </span>\n </div>\n <Slider\n min={-2}\n max={4}\n step={0.25}\n value={[letterSpacingToPx(typography?.letterSpacing?.chrome ?? DEFAULT_LETTER_SPACING)]}\n onValueChange={(values) =>\n setLetterSpacing(\n pxToLetterSpacing(\n values[0] ?? 0,\n typography?.letterSpacing?.chrome ?? DEFAULT_LETTER_SPACING,\n ),\n )\n }\n />\n </div>\n </ControlSection>\n\n <ControlSection title=\"Elevation\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Box-shadow scale (`--shadow-*`), shared across light & dark. Empty inherits the default.\n </p>\n {SHADOW_SLOTS.map((slot) => (\n <TextField\n key={slot}\n id={`${baseId}shadow-${slot}`}\n label={SHADOW_LABELS[slot]}\n value={theme.shadows?.[slot] ?? ''}\n placeholder={DEFAULT_SHADOWS[slot]}\n onChange={(next) => setShadow(slot, next)}\n />\n ))}\n </ControlSection>\n\n <ControlSection title=\"Density\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Semantic spacing (`--space-*`), as CSS lengths.\n </p>\n {SPACING_SLOTS.map((slot) => (\n <TextField\n key={slot}\n id={`${baseId}spacing-${slot}`}\n label={SPACING_LABELS[slot]}\n value={theme.spacing?.[slot] ?? ''}\n placeholder={DEFAULT_SPACING[slot]}\n onChange={(next) => setSpacing(slot, next)}\n />\n ))}\n </ControlSection>\n\n <ControlSection title=\"Motion\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Base transition `duration` + `easing` (`--motion-*`).\n </p>\n {MOTION_SLOTS.map((slot) => (\n <TextField\n key={slot}\n id={`${baseId}motion-${slot}`}\n label={MOTION_LABELS[slot]}\n value={theme.motion?.[slot] ?? ''}\n placeholder={DEFAULT_MOTION[slot]}\n onChange={(next) => setMotion(slot, next)}\n />\n ))}\n </ControlSection>\n\n <ControlSection title=\"Gradient\">\n <TextField\n id={`${baseId}gradient-brand`}\n label=\"Brand gradient\"\n value={theme.gradient?.brand ?? ''}\n placeholder={DEFAULT_GRADIENT}\n onChange={setGradient}\n />\n </ControlSection>\n\n <ControlSection title=\"Logo\">\n <LogoField\n id={`${baseId}logo-mark`}\n label=\"Mark (icon)\"\n value={value.logo?.mark ?? ''}\n onChange={(source) => setLogo('mark', source)}\n />\n <LogoField\n id={`${baseId}logo-wordmark`}\n label=\"Wordmark (full)\"\n value={value.logo?.wordmark ?? ''}\n onChange={(source) => setLogo('wordmark', source)}\n />\n <Collapsible className=\"tw:flex tw:flex-col tw:gap-2\">\n <Collapsible.Trigger asChild>\n <Button variant=\"outline\" size=\"small\" className=\"tw:w-full\">\n Toggle dark-mode logos\n </Button>\n </Collapsible.Trigger>\n <Collapsible.Content className=\"tw:flex tw:flex-col tw:gap-2\">\n <LogoField\n id={`${baseId}logo-mark-dark`}\n label=\"Mark (dark)\"\n value={value.logo?.markDark ?? ''}\n onChange={(source) => setLogo('markDark', source)}\n />\n <LogoField\n id={`${baseId}logo-wordmark-dark`}\n label=\"Wordmark (dark)\"\n value={value.logo?.wordmarkDark ?? ''}\n onChange={(source) => setLogo('wordmarkDark', source)}\n />\n </Collapsible.Content>\n </Collapsible>\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <span className=\"tw:text-xs tw:text-muted-foreground\">Preview</span>\n <div className=\"tw:flex tw:min-h-12 tw:items-center tw:gap-4 tw:rounded-md tw:border tw:border-border tw:bg-background tw:p-3\">\n {value.logo?.mark || value.logo?.wordmark ? (\n <>\n {value.logo.mark ? (\n <LogoPreviewImage source={value.logo.mark} className=\"tw:h-8\" />\n ) : null}\n {value.logo.wordmark ? (\n <LogoPreviewImage source={value.logo.wordmark} className=\"tw:h-8\" />\n ) : null}\n </>\n ) : (\n <span className=\"tw:text-xs tw:text-muted-foreground\">No logo set</span>\n )}\n </div>\n </div>\n </ControlSection>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BrandEditor-CS9P4r2p.js","names":[],"sources":["../src/blocks/BrandEditor/color.ts","../src/blocks/BrandEditor/ColorField.tsx","../src/blocks/BrandEditor/contrast.ts","../src/blocks/BrandEditor/ControlSection.tsx","../src/blocks/BrandEditor/LogoField.tsx","../src/blocks/BrandEditor/TextField.tsx","../src/blocks/BrandEditor/BrandEditor.tsx"],"sourcesContent":["// Brand colours are stored as space-separated \"R G B\" channel strings (see\n// `rgbChannelsSchema` in @keboola/brand-registry); `ColorInput` speaks hex.\n// These helpers convert at that boundary.\n\nexport const hexToChannels = (hex: string): string => {\n let h = hex.trim().replace(/^#/, '');\n // Expand 3/4-digit shorthand and drop alpha (4- and 8-digit `#rgba`/`#rrggbbaa`) — brand\n // channels carry no alpha. Anything else is malformed → fall back to black.\n if (h.length === 3 || h.length === 4) {\n h = h\n .slice(0, 3)\n .split('')\n .map((c) => c + c)\n .join('');\n } else if (h.length === 8) {\n h = h.slice(0, 6);\n }\n if (!/^[0-9a-fA-F]{6}$/.test(h)) return '0 0 0';\n const n = parseInt(h, 16);\n return `${(n >> 16) & 255} ${(n >> 8) & 255} ${n & 255}`;\n};\n\nexport const channelsToHex = (channels: string): string => {\n const [r = 0, g = 0, b = 0] = channels\n .trim()\n .split(/\\s+/)\n .map((n) => Number(n) || 0);\n const to2 = (v: number) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, '0');\n return `#${to2(r)}${to2(g)}${to2(b)}`;\n};\n","import { Label } from '../../components/Label';\nimport { ColorInput } from '../ColorInput';\n\nimport { channelsToHex, hexToChannels } from './color';\n\ntype Props = {\n id: string;\n label: string;\n /** Current value as an \"R G B\" channel string. */\n channels: string;\n /** Called with a new \"R G B\" channel string when a valid colour is picked. */\n onChange: (channels: string) => void;\n};\n\n// ColorInput fires `onValueChange` on every keystroke (it only coerces on blur), so commit a\n// channel value only for a COMPLETE 3-, 4-, 6-, or 8-digit hex — otherwise a partial \"#1\"/\"#\"\n// would write unintended channels. Alpha (4-/8-digit) is truncated to RGB, since brand tokens\n// carry no alpha.\nconst COMPLETE_HEX = /^([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\n\n/** A labelled hex colour picker bound to a brand token (stored as RGB channels). */\nexport const ColorField = ({ id, label, channels, onChange }: Props) => {\n const hex = channelsToHex(channels);\n\n return (\n <div className=\"tw:flex tw:items-center tw:justify-between tw:gap-2\">\n <Label htmlFor={id} className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {label}\n </Label>\n <ColorInput\n id={id}\n sizing=\"medium\"\n value={hex}\n className=\"tw:w-36\"\n onValueChange={(next) => {\n const clean = next.trim().replace(/^#/, '');\n // Commit only complete hex (hexToChannels handles the 3/6/8-digit shapes + alpha drop).\n if (COMPLETE_HEX.test(clean)) onChange(hexToChannels(`#${clean}`));\n }}\n />\n </div>\n );\n};\n","// WCAG contrast helpers over `\"R G B\"` channel strings (the brand token format).\n\n// Coerce non-numeric/blank channels to 0 and clamp to the valid 0–255 range, so a malformed\n// token (e.g. \"999 999 999\") can neither NaN-out nor push luminance past WCAG's expected bounds.\nconst clampChannel = (n: number): number =>\n Number.isFinite(n) ? Math.min(255, Math.max(0, n)) : 0;\n\nconst parseChannels = (channels: string): [number, number, number] => {\n const [r = 0, g = 0, b = 0] = channels\n .trim()\n .split(/\\s+/)\n .map((n) => clampChannel(Number(n)));\n return [r, g, b];\n};\n\nconst relativeLuminance = (channels: string): number => {\n // Destructure with defaults (not an `as` assertion) so `r`/`g`/`b` are `number` under\n // `noUncheckedIndexedAccess` without a production-code type cast.\n const [r = 0, g = 0, b = 0] = parseChannels(channels).map((c) => {\n const s = c / 255;\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n });\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n};\n\n/** WCAG contrast ratio (1–21) between two `\"R G B\"` colours. */\nexport const contrastRatio = (fg: string, bg: string): number => {\n const l1 = relativeLuminance(fg);\n const l2 = relativeLuminance(bg);\n const [lighter, darker] = l1 >= l2 ? [l1, l2] : [l2, l1];\n return (lighter + 0.05) / (darker + 0.05);\n};\n\n/** WCAG level for normal-size text at a given ratio. */\nexport const wcagLevel = (ratio: number): 'AAA' | 'AA' | 'fail' =>\n ratio >= 7 ? 'AAA' : ratio >= 4.5 ? 'AA' : 'fail';\n","import type { ReactNode } from 'react';\n\ntype Props = {\n title: string;\n children: ReactNode;\n};\n\n/** A titled block inside `BrandEditor`. */\nexport const ControlSection = ({ title, children }: Props) => (\n <section className=\"tw:flex tw:flex-col tw:gap-3 tw:border-b tw:border-border tw:p-4\">\n <h3 className=\"tw:m-0 tw:text-xs tw:font-semibold tw:uppercase tw:tracking-wide tw:text-muted-foreground\">\n {title}\n </h3>\n {children}\n </section>\n);\n","import { Label } from '../../components/Label';\nimport { Textarea } from '../Textarea';\n\ntype Props = {\n id: string;\n label: string;\n /** Current logo source: SVG markup, a `data:` URI, or an `http(s)` URL. */\n value: string;\n /** Called with the new source (empty string clears the slot). */\n onChange: (value: string) => void;\n};\n\n/** A labelled multi-line field for a brand logo source. */\nexport const LogoField = ({ id, label, value, onChange }: Props) => (\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label htmlFor={id} className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {label}\n </Label>\n <Textarea\n id={id}\n value={value}\n onValueChange={onChange}\n maxRows={4}\n placeholder=\"<svg…>, a data: URI, or an https:// URL\"\n className=\"tw:font-mono tw:text-xs\"\n />\n </div>\n);\n","import { Label } from '../../components/Label';\nimport { TextInput } from '../TextInput';\n\ntype Props = {\n id: string;\n label: string;\n /** Current CSS value (empty clears the token, falling back to the stylesheet default). */\n value: string;\n /** Called with the new CSS value on change. */\n onChange: (value: string) => void;\n /** The inherited default, shown as placeholder when the field is empty. */\n placeholder?: string;\n};\n\n/** A labelled single-line field for a free-form CSS-value token (shadow, motion, gradient). */\nexport const TextField = ({ id, label, value, onChange, placeholder }: Props) => (\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label htmlFor={id} className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {label}\n </Label>\n <TextInput\n id={id}\n sizing=\"small\"\n value={value}\n onValueChange={onChange}\n placeholder={placeholder}\n className=\"tw:font-mono tw:text-xs\"\n />\n </div>\n);\n","import { useId, useMemo } from 'react';\nimport DOMPurify from 'dompurify';\n\nimport type { Brand } from '@keboola/brand-registry';\nimport { keboola } from '@keboola/brand-registry';\n\nimport { Badge } from '../../components/Badge';\nimport { Button } from '../../components/Button';\nimport { Collapsible } from '../../components/Collapsible';\nimport { Label } from '../../components/Label';\nimport { Select } from '../../components/Select';\nimport { Slider } from '../../components/Slider';\nimport { ToggleGroup } from '../../components/ToggleGroup';\n\nimport { ColorField } from './ColorField';\nimport { contrastRatio, wcagLevel } from './contrast';\nimport { ControlSection } from './ControlSection';\nimport { LogoField } from './LogoField';\nimport { TextField } from './TextField';\n\nexport type ColorSlot = keyof Omit<NonNullable<Brand['theme']['colors']>, 'sidebar' | 'chart'>;\nexport type SidebarSlot = keyof NonNullable<NonNullable<Brand['theme']['colors']>['sidebar']>;\nexport type ChartSlot = keyof NonNullable<NonNullable<Brand['theme']['colors']>['chart']>;\nexport type RadiusSize = keyof NonNullable<Brand['theme']['radii']>;\n\ntype Typography = NonNullable<Brand['theme']['typography']>;\ntype FontFace = 'fontFamily' | 'fontDisplay' | 'fontMono';\ntype FontWeightSlot = keyof NonNullable<Typography['weight']>;\ntype ShadowSlot = keyof NonNullable<Brand['theme']['shadows']>;\ntype SpacingSlot = keyof NonNullable<Brand['theme']['spacing']>;\ntype MotionSlot = keyof NonNullable<Brand['theme']['motion']>;\n\nexport const DEFAULT_FALLBACK_FONT = \"'Inter', system-ui, sans-serif\";\n\nexport const DEFAULT_FONT_OPTIONS: { value: string; label: string }[] = [\n { value: DEFAULT_FALLBACK_FONT, label: 'Inter (default)' },\n { value: \"'Space Grotesk', system-ui, sans-serif\", label: 'Space Grotesk' },\n { value: \"'Poppins', system-ui, sans-serif\", label: 'Poppins' },\n { value: \"'Manrope', system-ui, sans-serif\", label: 'Manrope' },\n { value: \"'Quicksand', system-ui, sans-serif\", label: 'Quicksand' },\n { value: \"'Baloo 2', system-ui, sans-serif\", label: 'Baloo 2' },\n { value: \"'Fraunces', Georgia, serif\", label: 'Fraunces (serif)' },\n { value: \"Georgia, 'Times New Roman', serif\", label: 'Georgia (serif)' },\n { value: \"'JetBrains Mono', ui-monospace, monospace\", label: 'JetBrains Mono' },\n { value: 'system-ui, sans-serif', label: 'System UI' },\n];\n\n// Label maps double as a compile-time exhaustiveness guard: typed as\n// `Record<ColorSlot, string>` / `Record<SidebarSlot, string>`, a new token added\n// to the upstream `Brand` schema fails to compile here until it gets a control,\n// so it can't silently drift out of the editor. Insertion order is display order.\nconst COLOR_LABELS: Record<ColorSlot, string> = {\n primary: 'Primary',\n primaryForeground: 'Primary foreground',\n background: 'Background',\n foreground: 'Foreground',\n card: 'Card',\n cardForeground: 'Card foreground',\n popover: 'Popover',\n popoverForeground: 'Popover foreground',\n secondary: 'Secondary',\n secondaryForeground: 'Secondary foreground',\n muted: 'Muted',\n mutedForeground: 'Muted foreground',\n accent: 'Accent',\n accentForeground: 'Accent foreground',\n border: 'Border',\n input: 'Input border',\n inputBackground: 'Input background',\n ring: 'Ring',\n ringOffset: 'Ring offset',\n destructive: 'Destructive',\n destructiveForeground: 'Destructive foreground',\n success: 'Success',\n successForeground: 'Success foreground',\n warning: 'Warning',\n warningForeground: 'Warning foreground',\n info: 'Info',\n infoForeground: 'Info foreground',\n};\n\nconst SIDEBAR_LABELS: Record<SidebarSlot, string> = {\n sidebar: 'Sidebar',\n sidebarForeground: 'Foreground',\n sidebarPrimary: 'Primary',\n sidebarPrimaryForeground: 'Primary foreground',\n sidebarAccent: 'Accent',\n sidebarAccentForeground: 'Accent foreground',\n sidebarBorder: 'Border',\n sidebarRing: 'Ring',\n};\n\nconst CHART_LABELS: Record<ChartSlot, string> = {\n chart1: 'Chart 1',\n chart2: 'Chart 2',\n chart3: 'Chart 3',\n chart4: 'Chart 4',\n chart5: 'Chart 5',\n};\n\nconst WEIGHT_LABELS: Record<FontWeightSlot, string> = {\n normal: 'Normal',\n medium: 'Medium',\n semibold: 'Semibold',\n bold: 'Bold',\n};\n\nconst SHADOW_LABELS: Record<ShadowSlot, string> = {\n sm: 'Small',\n md: 'Medium',\n lg: 'Large',\n lift: 'Lift (raised)',\n};\n\nconst SPACING_LABELS: Record<SpacingSlot, string> = {\n gutter: 'Gutter (between sections)',\n inset: 'Inset (in-container)',\n};\n\nconst MOTION_LABELS: Record<MotionSlot, string> = {\n duration: 'Duration',\n easing: 'Easing',\n};\n\nconst COLOR_SLOTS = Object.keys(COLOR_LABELS) as ColorSlot[];\nconst SIDEBAR_SLOTS = Object.keys(SIDEBAR_LABELS) as SidebarSlot[];\nconst CHART_SLOTS = Object.keys(CHART_LABELS) as ChartSlot[];\nconst WEIGHT_SLOTS = Object.keys(WEIGHT_LABELS) as FontWeightSlot[];\nconst SHADOW_SLOTS = Object.keys(SHADOW_LABELS) as ShadowSlot[];\nconst SPACING_SLOTS = Object.keys(SPACING_LABELS) as SpacingSlot[];\nconst MOTION_SLOTS = Object.keys(MOTION_LABELS) as MotionSlot[];\nconst RADIUS_SIZES: RadiusSize[] = ['sm', 'md', 'lg'];\n\n// Foreground/background pairs whose contrast decides everyday legibility.\nconst A11Y_PAIRS: { label: string; fg: ColorSlot; bg: ColorSlot }[] = [\n { label: 'Text on background', fg: 'foreground', bg: 'background' },\n { label: 'Text on card', fg: 'cardForeground', bg: 'card' },\n { label: 'Primary button', fg: 'primaryForeground', bg: 'primary' },\n { label: 'Muted text', fg: 'mutedForeground', bg: 'muted' },\n { label: 'Accent', fg: 'accentForeground', bg: 'accent' },\n];\n\n// Fallback sidebar palette — the `keboola` brand doesn't set `colors.sidebar` /\n// `colorsDark.sidebar`, so a slot needs a value to seed from when a user starts\n// editing sidebar tokens on a brand that leaves them unset.\nconst DEFAULT_SIDEBAR: Record<'light' | 'dark', Record<SidebarSlot, string>> = {\n light: {\n sidebar: '255 255 255',\n sidebarForeground: '30 41 59',\n sidebarPrimary: '31 143 255',\n sidebarPrimaryForeground: '255 255 255',\n sidebarAccent: '219 234 254',\n sidebarAccentForeground: '30 41 59',\n sidebarBorder: '229 231 235',\n sidebarRing: '31 143 255',\n },\n dark: {\n sidebar: '14 14 18',\n sidebarForeground: '229 230 236',\n sidebarPrimary: '31 143 255',\n sidebarPrimaryForeground: '255 255 255',\n sidebarAccent: '38 38 45',\n sidebarAccentForeground: '229 230 236',\n sidebarBorder: '48 49 57',\n sidebarRing: '31 143 255',\n },\n};\n\nconst DEFAULT_RADII: NonNullable<Brand['theme']['radii']> = {\n sm: '0.25rem',\n md: '0.375rem',\n lg: '0.5rem',\n};\n\n// The `@keboola/design/styles` categorical chart palette — seeds the chart\n// pickers when a brand leaves `colors.chart` / `colorsDark.chart` unset.\nconst DEFAULT_CHART: Record<'light' | 'dark', Record<ChartSlot, string>> = {\n light: {\n chart1: '31 143 255',\n chart2: '0 194 135',\n chart3: '146 92 255',\n chart4: '245 147 0',\n chart5: '237 94 223',\n },\n dark: {\n chart1: '92 173 255',\n chart2: '0 230 161',\n chart3: '173 133 255',\n chart4: '255 190 92',\n chart5: '242 140 232',\n },\n};\n\n// Chrome font-weight defaults (the Tailwind scale `@keboola/design` falls back to\n// when `typography.weight` is unset) — seed values for the weight sliders.\nconst DEFAULT_FONT_WEIGHTS: Record<FontWeightSlot, string> = {\n normal: '400',\n medium: '500',\n semibold: '600',\n bold: '700',\n};\n\n// Chrome tracking default (behind the hardcoded `tw:tracking-[1px]`).\nconst DEFAULT_LETTER_SPACING = '1px';\n\n// The monospace stack the mono-face Select falls back to when `fontMono` is unset\n// (matches the `DEFAULT_FONT_OPTIONS` JetBrains Mono entry).\nconst MONO_FALLBACK = \"'JetBrains Mono', ui-monospace, monospace\";\n\n// `@keboola/design/styles` defaults for the advanced token groups, shown as\n// placeholders so an unset field reveals the value it inherits.\nconst DEFAULT_SHADOWS: Record<ShadowSlot, string> = {\n sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',\n md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',\n lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',\n lift: '0 10px 30px -12px rgb(0 0 0 / 0.28), 0 2px 6px rgb(0 0 0 / 0.06)',\n};\n\nconst DEFAULT_SPACING: Record<SpacingSlot, string> = {\n gutter: '1.5rem',\n inset: '1.5rem',\n};\n\nconst DEFAULT_MOTION: Record<MotionSlot, string> = {\n duration: '200ms',\n easing: 'cubic-bezier(0.2, 0.7, 0.2, 1)',\n};\n\nconst DEFAULT_GRADIENT =\n 'linear-gradient(135deg, rgb(var(--primary)), rgb(var(--accent, var(--primary))))';\n\n// Leading numeric value of a token string (font-weight \"500\").\nconst numOf = (value: string, fallback: number): number => {\n const n = Number.parseFloat(value);\n return Number.isFinite(n) ? n : fallback;\n};\n\n// Letter-spacing may be px or em. Drive the slider in px (treating `em` as ×16,\n// like the radius slider treats `rem`) and write back in the value's original\n// unit, so an `em`-authored brand (e.g. `0.02em`) stays `em` instead of being\n// silently rewritten to `px`.\nconst letterSpacingToPx = (value: string): number => {\n const n = Number.parseFloat(value);\n if (!Number.isFinite(n)) return 0;\n return value.trim().endsWith('em') ? n * 16 : n;\n};\n\nconst pxToLetterSpacing = (px: number, currentValue: string): string =>\n currentValue.trim().endsWith('em') ? `${Number((px / 16).toFixed(3))}em` : `${px}px`;\n\n// The Brand schema allows any CSS length for radii (`rem`, `px`, unitless). Convert to px for\n// the slider treating `rem` as ×16 and everything else as already-px, and write back preserving\n// the slot's original unit so `8px` stays `8px`, `0.5rem` stays `0.5rem`, and a unitless `8` stays `8`.\nconst lengthToPx = (value: string): number => {\n const n = Number.parseFloat(value);\n if (!Number.isFinite(n)) return 0;\n return Math.round(value.trim().endsWith('rem') ? n * 16 : n);\n};\n\nconst pxToLength = (px: number, currentValue: string): string => {\n const trimmed = currentValue.trim();\n if (trimmed.endsWith('rem')) return `${px / 16}rem`;\n // Preserve a unitless token (the schema allows it) rather than forcing `px` onto it.\n if (/^-?\\d*\\.?\\d+$/.test(trimmed)) return `${px}`;\n return `${px}px`;\n};\n\nexport type BrandEditorProps = {\n value: Brand;\n onChange: (next: Brand) => void;\n /** Palette being edited. Controlled. Default 'light'. */\n colorScheme?: 'light' | 'dark';\n /** If provided, the editor shows a light/dark toggle that calls this. If omitted, no toggle. */\n onColorSchemeChange?: (scheme: 'light' | 'dark') => void;\n /** Font choices for the typography Select. Defaults to DEFAULT_FONT_OPTIONS. */\n fontOptions?: { value: string; label: string }[];\n};\n\n// Match `BrandLogo`'s sniffing: any string starting with `<` is inline markup (covers\n// `<?xml …?><svg>` / `<!doctype …>` prefixes); only `data:` / `http(s)://` are image URLs.\nconst isSvgMarkup = (source: string) => source.trimStart().startsWith('<');\nconst isImageSource = (source: string) => /^(data:|https?:\\/\\/)/.test(source.trim());\n\n/** Renders a logo source (SVG markup, `data:` URI, or `http(s)` URL) from the raw string. */\nconst LogoPreviewImage = ({ source, className }: { source: string; className?: string }) => {\n const safeSvg = useMemo(\n () =>\n isSvgMarkup(source)\n ? DOMPurify.sanitize(source, { USE_PROFILES: { svg: true, svgFilters: true } })\n : null,\n [source],\n );\n\n if (safeSvg !== null) {\n // Decorative preview of what the caller is editing — the textarea holds the real value, so\n // hide the injected graphic from assistive tech (parity with the `alt=\"\"` image branch below).\n return <span aria-hidden className={className} dangerouslySetInnerHTML={{ __html: safeSvg }} />;\n }\n if (isImageSource(source)) {\n return <img src={source} alt=\"\" className={className} />;\n }\n return null;\n};\n\n/**\n * Controlled brand-editing block — colors (light/dark), sidebar colors, WCAG contrast readout,\n * corner radius, typography, and logo. Reads/writes a `Brand` (`@keboola/brand-registry`) via\n * `value` / `onChange`; owns no state of its own, so the caller decides where the brand and the\n * active color scheme live (Kai build-mode preview, theme-generator, …).\n */\nexport const BrandEditor = ({\n value,\n onChange,\n colorScheme = 'light',\n onColorSchemeChange,\n fontOptions = DEFAULT_FONT_OPTIONS,\n}: BrandEditorProps) => {\n // Prefix every control id so multiple BrandEditors on one page don't collide (broken label↔control).\n const baseId = useId();\n const { theme } = value;\n const coreColors = colorScheme === 'light' ? theme.colors : theme.colorsDark;\n const sidebarColors = colorScheme === 'light' ? theme.colors?.sidebar : theme.colorsDark?.sidebar;\n const chartColors = colorScheme === 'light' ? theme.colors?.chart : theme.colorsDark?.chart;\n // Fall back to the keboola default of the SAME scheme — never light values while editing dark.\n const defaultColors = colorScheme === 'light' ? keboola.theme.colors : keboola.theme.colorsDark;\n const radii = theme.radii ?? DEFAULT_RADII;\n const typography = theme.typography;\n\n const setColor = (slot: ColorSlot, channels: string) => {\n if (colorScheme === 'light') {\n onChange({ ...value, theme: { ...theme, colors: { ...theme.colors, [slot]: channels } } });\n return;\n }\n // Keep colorsDark sparse (mirrors the light path) so an unset dark palette inherits the\n // design-system defaults instead of baking in a full keboola palette on the first edit.\n onChange({\n ...value,\n theme: { ...theme, colorsDark: { ...theme.colorsDark, [slot]: channels } },\n });\n };\n\n const setSidebarColor = (slot: SidebarSlot, channels: string) => {\n if (colorScheme === 'light') {\n const base = theme.colors?.sidebar ?? DEFAULT_SIDEBAR.light;\n onChange({\n ...value,\n theme: { ...theme, colors: { ...theme.colors, sidebar: { ...base, [slot]: channels } } },\n });\n return;\n }\n // Spread the existing (possibly-empty) colorsDark so setting a sidebar token doesn't seed the\n // whole core dark palette; only the sidebar sub-object is seeded, since it has no default to inherit.\n const sidebarBase = theme.colorsDark?.sidebar ?? DEFAULT_SIDEBAR.dark;\n onChange({\n ...value,\n theme: {\n ...theme,\n colorsDark: { ...theme.colorsDark, sidebar: { ...sidebarBase, [slot]: channels } },\n },\n });\n };\n\n const setChart = (slot: ChartSlot, channels: string) => {\n const base = chartColors ?? DEFAULT_CHART[colorScheme];\n const chart = { ...base, [slot]: channels };\n if (colorScheme === 'light') {\n onChange({ ...value, theme: { ...theme, colors: { ...theme.colors, chart } } });\n return;\n }\n onChange({ ...value, theme: { ...theme, colorsDark: { ...theme.colorsDark, chart } } });\n };\n\n const setRadius = (size: RadiusSize, radius: string) =>\n onChange({\n ...value,\n theme: { ...theme, radii: { ...(theme.radii ?? DEFAULT_RADII), [size]: radius } },\n });\n\n // Merge a typography patch, keeping sibling faces / weight / letterSpacing.\n const patchTypography = (patch: Partial<Typography>) =>\n onChange({ ...value, theme: { ...theme, typography: { ...theme.typography, ...patch } } });\n\n const setFontFace = (face: FontFace, font: string) =>\n patchTypography({ [face]: font.trim() || undefined });\n\n const setFontWeight = (slot: FontWeightSlot, weight: string) =>\n patchTypography({ weight: { ...theme.typography?.weight, [slot]: weight || undefined } });\n\n const setLetterSpacing = (chrome: string) =>\n patchTypography({ letterSpacing: { chrome: chrome.trim() || undefined } });\n\n const setShadow = (slot: ShadowSlot, shadow: string) =>\n onChange({\n ...value,\n theme: { ...theme, shadows: { ...theme.shadows, [slot]: shadow.trim() || undefined } },\n });\n\n const setSpacing = (slot: SpacingSlot, length: string) =>\n onChange({\n ...value,\n theme: { ...theme, spacing: { ...theme.spacing, [slot]: length.trim() || undefined } },\n });\n\n const setMotion = (slot: MotionSlot, token: string) =>\n onChange({\n ...value,\n theme: { ...theme, motion: { ...theme.motion, [slot]: token.trim() || undefined } },\n });\n\n const setGradient = (gradient: string) =>\n onChange({\n ...value,\n theme: { ...theme, gradient: { brand: gradient.trim() || undefined } },\n });\n\n const setLogo = (slot: keyof NonNullable<Brand['logo']>, source: string) => {\n const trimmed = source.trim();\n const nextLogo: NonNullable<Brand['logo']> = { ...value.logo };\n if (trimmed) nextLogo[slot] = trimmed;\n else delete nextLogo[slot];\n const { logo: _drop, ...rest } = value;\n onChange(Object.keys(nextLogo).length > 0 ? { ...value, logo: nextLogo } : rest);\n };\n\n return (\n <div className=\"tw:flex tw:flex-col\">\n <ControlSection title=\"Colors\">\n {onColorSchemeChange ? (\n <ToggleGroup\n type=\"single\"\n value={colorScheme}\n onValueChange={(next) => {\n if (next === 'light' || next === 'dark') onColorSchemeChange(next);\n }}\n aria-label=\"Color scheme\"\n className=\"tw:w-full\"\n >\n <ToggleGroup.Item value=\"light\" className=\"tw:flex-1\">\n Light\n </ToggleGroup.Item>\n <ToggleGroup.Item value=\"dark\" className=\"tw:flex-1\">\n Dark\n </ToggleGroup.Item>\n </ToggleGroup>\n ) : null}\n <div className=\"tw:flex tw:flex-col tw:gap-2\">\n {COLOR_SLOTS.map((slot) => (\n <ColorField\n key={slot}\n id={`${baseId}color-${colorScheme}-${slot}`}\n label={COLOR_LABELS[slot]}\n channels={coreColors?.[slot] ?? defaultColors?.[slot] ?? '0 0 0'}\n onChange={(channels) => setColor(slot, channels)}\n />\n ))}\n </div>\n </ControlSection>\n\n <ControlSection title=\"Accessibility\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n WCAG contrast for the {colorScheme} palette (AA needs 4.5:1).\n </p>\n <div className=\"tw:flex tw:flex-col tw:gap-1.5\">\n {A11Y_PAIRS.map((pair) => {\n const fg = coreColors?.[pair.fg] ?? defaultColors?.[pair.fg] ?? '0 0 0';\n const bg = coreColors?.[pair.bg] ?? defaultColors?.[pair.bg] ?? '255 255 255';\n const ratio = contrastRatio(fg, bg);\n const level = wcagLevel(ratio);\n return (\n <div key={pair.label} className=\"tw:flex tw:items-center tw:justify-between tw:gap-2\">\n <span className=\"tw:text-xs tw:text-foreground\">{pair.label}</span>\n <span className=\"tw:flex tw:items-center tw:gap-1.5\">\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {ratio.toFixed(1)}:1\n </span>\n <Badge color={level === 'fail' ? 'red' : 'green'}>\n {level === 'fail' ? 'Fail' : level}\n </Badge>\n </span>\n </div>\n );\n })}\n </div>\n </ControlSection>\n\n <ControlSection title=\"Sidebar colors\">\n <Collapsible className=\"tw:flex tw:flex-col tw:gap-2\">\n <Collapsible.Trigger asChild>\n <Button variant=\"outline\" size=\"small\" className=\"tw:w-full\">\n Toggle sidebar tokens ({colorScheme})\n </Button>\n </Collapsible.Trigger>\n <Collapsible.Content className=\"tw:flex tw:flex-col tw:gap-2\">\n {SIDEBAR_SLOTS.map((slot) => (\n <ColorField\n key={slot}\n id={`${baseId}sidebar-${colorScheme}-${slot}`}\n label={SIDEBAR_LABELS[slot]}\n channels={sidebarColors?.[slot] ?? DEFAULT_SIDEBAR[colorScheme][slot]}\n onChange={(channels) => setSidebarColor(slot, channels)}\n />\n ))}\n </Collapsible.Content>\n </Collapsible>\n </ControlSection>\n\n <ControlSection title=\"Chart palette\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Categorical series colours for the {colorScheme} palette (`--chart-1…5`).\n </p>\n <div className=\"tw:flex tw:flex-col tw:gap-2\">\n {CHART_SLOTS.map((slot) => (\n <ColorField\n key={slot}\n id={`${baseId}chart-${colorScheme}-${slot}`}\n label={CHART_LABELS[slot]}\n channels={chartColors?.[slot] ?? DEFAULT_CHART[colorScheme][slot]}\n onChange={(channels) => setChart(slot, channels)}\n />\n ))}\n </div>\n </ControlSection>\n\n <ControlSection title=\"Radius\">\n {RADIUS_SIZES.map((size) => {\n const current = radii[size];\n const px = lengthToPx(current);\n return (\n <div key={size} className=\"tw:flex tw:flex-col tw:gap-1\">\n <div className=\"tw:flex tw:items-center tw:justify-between\">\n <Label className=\"tw:text-xs tw:font-normal tw:uppercase tw:text-foreground\">\n {size}\n </Label>\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {radii[size]}\n </span>\n </div>\n <Slider\n min={0}\n max={24}\n step={1}\n value={[px]}\n onValueChange={(values) => setRadius(size, pxToLength(values[0] ?? 0, current))}\n />\n </div>\n );\n })}\n </ControlSection>\n\n <ControlSection title=\"Typography\">\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label\n htmlFor={`${baseId}font-family`}\n className=\"tw:text-xs tw:font-normal tw:text-foreground\"\n >\n Font family\n </Label>\n <Select\n id={`${baseId}font-family`}\n value={typography?.fontFamily ?? DEFAULT_FALLBACK_FONT}\n onValueChange={(font) => setFontFace('fontFamily', font)}\n items={fontOptions}\n />\n </div>\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label\n htmlFor={`${baseId}font-display`}\n className=\"tw:text-xs tw:font-normal tw:text-foreground\"\n >\n Display font (headings)\n </Label>\n <Select\n id={`${baseId}font-display`}\n value={typography?.fontDisplay ?? typography?.fontFamily ?? DEFAULT_FALLBACK_FONT}\n onValueChange={(font) => setFontFace('fontDisplay', font)}\n items={fontOptions}\n />\n </div>\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <Label\n htmlFor={`${baseId}font-mono`}\n className=\"tw:text-xs tw:font-normal tw:text-foreground\"\n >\n Monospace font\n </Label>\n <Select\n id={`${baseId}font-mono`}\n value={typography?.fontMono ?? MONO_FALLBACK}\n onValueChange={(font) => setFontFace('fontMono', font)}\n items={fontOptions}\n />\n </div>\n\n <span className=\"tw:text-xs tw:text-muted-foreground\">Chrome font weights</span>\n {WEIGHT_SLOTS.map((slot) => {\n const current = typography?.weight?.[slot] ?? DEFAULT_FONT_WEIGHTS[slot];\n return (\n <div key={slot} className=\"tw:flex tw:flex-col tw:gap-1\">\n <div className=\"tw:flex tw:items-center tw:justify-between\">\n <Label className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n {WEIGHT_LABELS[slot]}\n </Label>\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {current}\n </span>\n </div>\n <Slider\n min={100}\n max={900}\n step={25}\n value={[numOf(current, 400)]}\n onValueChange={(values) => setFontWeight(slot, String(values[0] ?? 400))}\n />\n </div>\n );\n })}\n\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <div className=\"tw:flex tw:items-center tw:justify-between\">\n <Label className=\"tw:text-xs tw:font-normal tw:text-foreground\">\n Chrome letter spacing\n </Label>\n <span className=\"tw:text-xs tw:tabular-nums tw:text-muted-foreground\">\n {typography?.letterSpacing?.chrome ?? DEFAULT_LETTER_SPACING}\n </span>\n </div>\n <Slider\n min={-2}\n max={4}\n step={0.25}\n value={[letterSpacingToPx(typography?.letterSpacing?.chrome ?? DEFAULT_LETTER_SPACING)]}\n onValueChange={(values) =>\n setLetterSpacing(\n pxToLetterSpacing(\n values[0] ?? 0,\n typography?.letterSpacing?.chrome ?? DEFAULT_LETTER_SPACING,\n ),\n )\n }\n />\n </div>\n </ControlSection>\n\n <ControlSection title=\"Elevation\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Box-shadow scale (`--shadow-*`), shared across light & dark. Empty inherits the default.\n </p>\n {SHADOW_SLOTS.map((slot) => (\n <TextField\n key={slot}\n id={`${baseId}shadow-${slot}`}\n label={SHADOW_LABELS[slot]}\n value={theme.shadows?.[slot] ?? ''}\n placeholder={DEFAULT_SHADOWS[slot]}\n onChange={(next) => setShadow(slot, next)}\n />\n ))}\n </ControlSection>\n\n <ControlSection title=\"Density\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Semantic spacing (`--space-*`), as CSS lengths.\n </p>\n {SPACING_SLOTS.map((slot) => (\n <TextField\n key={slot}\n id={`${baseId}spacing-${slot}`}\n label={SPACING_LABELS[slot]}\n value={theme.spacing?.[slot] ?? ''}\n placeholder={DEFAULT_SPACING[slot]}\n onChange={(next) => setSpacing(slot, next)}\n />\n ))}\n </ControlSection>\n\n <ControlSection title=\"Motion\">\n <p className=\"tw:m-0 tw:text-xs tw:text-muted-foreground\">\n Base transition `duration` + `easing` (`--motion-*`).\n </p>\n {MOTION_SLOTS.map((slot) => (\n <TextField\n key={slot}\n id={`${baseId}motion-${slot}`}\n label={MOTION_LABELS[slot]}\n value={theme.motion?.[slot] ?? ''}\n placeholder={DEFAULT_MOTION[slot]}\n onChange={(next) => setMotion(slot, next)}\n />\n ))}\n </ControlSection>\n\n <ControlSection title=\"Gradient\">\n <TextField\n id={`${baseId}gradient-brand`}\n label=\"Brand gradient\"\n value={theme.gradient?.brand ?? ''}\n placeholder={DEFAULT_GRADIENT}\n onChange={setGradient}\n />\n </ControlSection>\n\n <ControlSection title=\"Logo\">\n <LogoField\n id={`${baseId}logo-mark`}\n label=\"Mark (icon)\"\n value={value.logo?.mark ?? ''}\n onChange={(source) => setLogo('mark', source)}\n />\n <LogoField\n id={`${baseId}logo-wordmark`}\n label=\"Wordmark (full)\"\n value={value.logo?.wordmark ?? ''}\n onChange={(source) => setLogo('wordmark', source)}\n />\n <Collapsible className=\"tw:flex tw:flex-col tw:gap-2\">\n <Collapsible.Trigger asChild>\n <Button variant=\"outline\" size=\"small\" className=\"tw:w-full\">\n Toggle dark-mode logos\n </Button>\n </Collapsible.Trigger>\n <Collapsible.Content className=\"tw:flex tw:flex-col tw:gap-2\">\n <LogoField\n id={`${baseId}logo-mark-dark`}\n label=\"Mark (dark)\"\n value={value.logo?.markDark ?? ''}\n onChange={(source) => setLogo('markDark', source)}\n />\n <LogoField\n id={`${baseId}logo-wordmark-dark`}\n label=\"Wordmark (dark)\"\n value={value.logo?.wordmarkDark ?? ''}\n onChange={(source) => setLogo('wordmarkDark', source)}\n />\n </Collapsible.Content>\n </Collapsible>\n <div className=\"tw:flex tw:flex-col tw:gap-1\">\n <span className=\"tw:text-xs tw:text-muted-foreground\">Preview</span>\n <div className=\"tw:flex tw:min-h-12 tw:items-center tw:gap-4 tw:rounded-md tw:border tw:border-border tw:bg-background tw:p-3\">\n {value.logo?.mark || value.logo?.wordmark ? (\n <>\n {value.logo.mark ? (\n <LogoPreviewImage source={value.logo.mark} className=\"tw:h-8\" />\n ) : null}\n {value.logo.wordmark ? (\n <LogoPreviewImage source={value.logo.wordmark} className=\"tw:h-8\" />\n ) : null}\n </>\n ) : (\n <span className=\"tw:text-xs tw:text-muted-foreground\">No logo set</span>\n )}\n </div>\n </div>\n </ControlSection>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "./classNames-BzrkDnOD.js";
|
|
2
2
|
import { ChevronDown, ChevronRight, Ellipsis } from "./icons-Cek151jC.js";
|
|
3
3
|
import { Popover } from "./Popover-BEbQrC_P.js";
|
|
4
|
-
import { SearchInput } from "./SearchInput-
|
|
4
|
+
import { SearchInput } from "./SearchInput-Q85p8Ews.js";
|
|
5
5
|
import { Slot } from "@radix-ui/react-slot";
|
|
6
6
|
import { Children, createContext, isValidElement, use, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -327,4 +327,4 @@ Breadcrumb.Dropdown = BreadcrumbDropdown;
|
|
|
327
327
|
//#endregion
|
|
328
328
|
export { Breadcrumb };
|
|
329
329
|
|
|
330
|
-
//# sourceMappingURL=Breadcrumb-
|
|
330
|
+
//# sourceMappingURL=Breadcrumb-D1id7YXd.js.map
|