@keboola/design 17.1.0 → 17.2.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/AGENTS.md +2 -0
- package/CHANGELOG.md +34 -1
- package/dist/{AIKit-DyysN1kS.js → AIKit-DgyoP_kW.js} +19 -11
- package/dist/AIKit-DgyoP_kW.js.map +1 -0
- package/dist/{Alert-BQB-og_I.js → Alert-CcAVKRSM.js} +2 -2
- package/dist/{Alert-BQB-og_I.js.map → Alert-CcAVKRSM.js.map} +1 -1
- package/dist/{AlertModal-BkmyEvsR.js → AlertModal-DG3GuHtd.js} +2 -2
- package/dist/{AlertModal-BkmyEvsR.js.map → AlertModal-DG3GuHtd.js.map} +1 -1
- package/dist/{AppShell-CDbecT-t.js → AppShell-CPr3vmUv.js} +2 -2
- package/dist/{AppShell-CDbecT-t.js.map → AppShell-CPr3vmUv.js.map} +1 -1
- package/dist/Badge-CFRhducL.js.map +1 -1
- package/dist/{BrandEditor-BHE6nsob.js → BrandEditor-8PxfTIcN.js} +6 -6
- package/dist/{BrandEditor-BHE6nsob.js.map → BrandEditor-8PxfTIcN.js.map} +1 -1
- package/dist/{BrandLogo-n4Qhi1tI.js → BrandLogo-DZLi6TZV.js} +2 -2
- package/dist/{BrandLogo-n4Qhi1tI.js.map → BrandLogo-DZLi6TZV.js.map} +1 -1
- package/dist/{Breadcrumb-BAwQTo0c.js → Breadcrumb-B_4zm7jJ.js} +3 -3
- package/dist/{Breadcrumb-BAwQTo0c.js.map → Breadcrumb-B_4zm7jJ.js.map} +1 -1
- package/dist/{Button-BAMX1iuI.js → Button-BgjVUBON.js} +2 -2
- package/dist/{Button-BAMX1iuI.js.map → Button-BgjVUBON.js.map} +1 -1
- package/dist/{Checkbox-CfjpwIcg.js → Checkbox-CsvhT9Bj.js} +2 -2
- package/dist/{Checkbox-CfjpwIcg.js.map → Checkbox-CsvhT9Bj.js.map} +1 -1
- package/dist/{Clipboard-BaU3cDi5.js → Clipboard-C6clKAwX.js} +2 -2
- package/dist/{Clipboard-BaU3cDi5.js.map → Clipboard-C6clKAwX.js.map} +1 -1
- package/dist/{ClipboardButton-BWeaQEM2.js → ClipboardButton-1ISIYJQj.js} +3 -3
- package/dist/{ClipboardButton-BWeaQEM2.js.map → ClipboardButton-1ISIYJQj.js.map} +1 -1
- package/dist/{CodeEditor-BdTxkPD4.js → CodeEditor-Dx9gyVHV.js} +4 -4
- package/dist/{CodeEditor-BdTxkPD4.js.map → CodeEditor-Dx9gyVHV.js.map} +1 -1
- package/dist/{ColorInput-BoVdBxJ7.js → ColorInput-CT3OTIOu.js} +3 -3
- package/dist/{ColorInput-BoVdBxJ7.js.map → ColorInput-CT3OTIOu.js.map} +1 -1
- package/dist/{ColorModeSwitch-BDkdmHYl.js → ColorModeSwitch-BsZ13XeH.js} +2 -2
- package/dist/{ColorModeSwitch-BDkdmHYl.js.map → ColorModeSwitch-BsZ13XeH.js.map} +1 -1
- package/dist/{Combobox-RZ0i3w8u.js → Combobox-D_modNUu.js} +6 -6
- package/dist/Combobox-D_modNUu.js.map +1 -0
- package/dist/{Command-D6XYzzJf.js → Command-ZOAxNfFf.js} +2 -2
- package/dist/{Command-D6XYzzJf.js.map → Command-ZOAxNfFf.js.map} +1 -1
- package/dist/{CopyField-9WBU8thS.js → CopyField-CXdg8t3Y.js} +2 -2
- package/dist/{CopyField-9WBU8thS.js.map → CopyField-CXdg8t3Y.js.map} +1 -1
- package/dist/{DateInput-CCYG0AtT.js → DateInput-DpoPkZRC.js} +3 -3
- package/dist/{DateInput-CCYG0AtT.js.map → DateInput-DpoPkZRC.js.map} +1 -1
- package/dist/{DateRangeInput-CpZK1IFp.js → DateRangeInput-DhmYH6V9.js} +3 -3
- package/dist/{DateRangeInput-CpZK1IFp.js.map → DateRangeInput-DhmYH6V9.js.map} +1 -1
- package/dist/{DateRangePicker-B2AUKX7u.js → DateRangePicker-CQXMvR3C.js} +2 -2
- package/dist/{DateRangePicker-B2AUKX7u.js.map → DateRangePicker-CQXMvR3C.js.map} +1 -1
- package/dist/{FieldItem-eM7IHBt5.js → FieldItem-D5EpKxjN.js} +3 -3
- package/dist/{FieldItem-eM7IHBt5.js.map → FieldItem-D5EpKxjN.js.map} +1 -1
- package/dist/{FileUpload-d-hNPfPV.js → FileUpload-CpdNficK.js} +2 -2
- package/dist/{FileUpload-d-hNPfPV.js.map → FileUpload-CpdNficK.js.map} +1 -1
- package/dist/{Form-B_9sNehB.js → Form-CXYgF4uw.js} +7 -7
- package/dist/{Form-B_9sNehB.js.map → Form-CXYgF4uw.js.map} +1 -1
- package/dist/{FormGroup-D7ahkK1q.js → FormGroup-B-_HsL1J.js} +3 -3
- package/dist/{FormGroup-D7ahkK1q.js.map → FormGroup-B-_HsL1J.js.map} +1 -1
- package/dist/{FullScreenModal-CiRKNsls.js → FullScreenModal-xMdgp0A-.js} +2 -2
- package/dist/{FullScreenModal-CiRKNsls.js.map → FullScreenModal-xMdgp0A-.js.map} +1 -1
- package/dist/{InlineEdit-BzOox46S.js → InlineEdit-DDPFk6Ak.js} +4 -4
- package/dist/{InlineEdit-BzOox46S.js.map → InlineEdit-DDPFk6Ak.js.map} +1 -1
- package/dist/{InputGroup-DnGP9-ZQ.js → InputGroup-oui6630r.js} +2 -2
- package/dist/{InputGroup-DnGP9-ZQ.js.map → InputGroup-oui6630r.js.map} +1 -1
- package/dist/{Kbd-6velz9J-.js → Kbd-D4F65jXO.js} +2 -2
- package/dist/{Kbd-6velz9J-.js.map → Kbd-D4F65jXO.js.map} +1 -1
- package/dist/{MermaidDiagram-l83gco3r.js → MermaidDiagram-Cg-c8jjI.js} +2 -2
- package/dist/{MermaidDiagram-l83gco3r.js.map → MermaidDiagram-Cg-c8jjI.js.map} +1 -1
- package/dist/{Modal-CLu6Eg5p.js → Modal-Cro4XHuT.js} +2 -2
- package/dist/{Modal-CLu6Eg5p.js.map → Modal-Cro4XHuT.js.map} +1 -1
- package/dist/{MultiActionButtonGroup-B3-wNw7w.js → MultiActionButtonGroup-DSxYdeRE.js} +3 -3
- package/dist/{MultiActionButtonGroup-B3-wNw7w.js.map → MultiActionButtonGroup-DSxYdeRE.js.map} +1 -1
- package/dist/{Navbar-BEc-Pqo5.js → Navbar-DB2YAs_2.js} +2 -2
- package/dist/{Navbar-BEc-Pqo5.js.map → Navbar-DB2YAs_2.js.map} +1 -1
- package/dist/{PageHeader-DosMyar2.js → PageHeader-CfFKjCzo.js} +2 -2
- package/dist/PageHeader-CfFKjCzo.js.map +1 -0
- package/dist/{PageToolbar-NmiausP7.js → PageToolbar-BTYKQCao.js} +2 -2
- package/dist/{PageToolbar-NmiausP7.js.map → PageToolbar-BTYKQCao.js.map} +1 -1
- package/dist/{Pagination-DidOPSPB.js → Pagination-C4pi5DHG.js} +2 -2
- package/dist/{Pagination-DidOPSPB.js.map → Pagination-C4pi5DHG.js.map} +1 -1
- package/dist/{Popconfirm-C4ryoeGP.js → Popconfirm-DOWgVS2t.js} +2 -2
- package/dist/{Popconfirm-C4ryoeGP.js.map → Popconfirm-DOWgVS2t.js.map} +1 -1
- package/dist/{RadioGroup-BIElT-nH.js → RadioGroup-BCpVnOjW.js} +2 -2
- package/dist/{RadioGroup-BIElT-nH.js.map → RadioGroup-BCpVnOjW.js.map} +1 -1
- package/dist/{Search-xDWaQ2z3.js → Search-hED5IJlr.js} +2 -2
- package/dist/{Search-xDWaQ2z3.js.map → Search-hED5IJlr.js.map} +1 -1
- package/dist/{SearchInput-BwlJ77mV.js → SearchInput-BTTnMdb1.js} +3 -3
- package/dist/{SearchInput-BwlJ77mV.js.map → SearchInput-BTTnMdb1.js.map} +1 -1
- package/dist/{SectionNav-DV3Hwj-5.js → SectionNav-O5hcU01h.js} +3 -3
- package/dist/{SectionNav-DV3Hwj-5.js.map → SectionNav-O5hcU01h.js.map} +1 -1
- package/dist/{Select-C3Q_Xk_Z.js → Select-DguW42V3.js} +2 -2
- package/dist/{Select-C3Q_Xk_Z.js.map → Select-DguW42V3.js.map} +1 -1
- package/dist/{Sheet-D7NnOyud.js → Sheet-DGqQqtof.js} +2 -2
- package/dist/{Sheet-D7NnOyud.js.map → Sheet-DGqQqtof.js.map} +1 -1
- package/dist/{Sidebar-DaUDM8jm.js → Sidebar-BD9iGHqL.js} +5 -5
- package/dist/{Sidebar-DaUDM8jm.js.map → Sidebar-BD9iGHqL.js.map} +1 -1
- package/dist/{Slider-Cdl8FOvI.js → Slider-D12JmXyd.js} +3 -3
- package/dist/{Slider-Cdl8FOvI.js.map → Slider-D12JmXyd.js.map} +1 -1
- package/dist/{Tabs-DUOUQKh7.js → Tabs-Z6ckOaSb.js} +2 -2
- package/dist/{Tabs-DUOUQKh7.js.map → Tabs-Z6ckOaSb.js.map} +1 -1
- package/dist/{TextInput-BAL-zOAN.js → TextInput-tWtbWBFQ.js} +3 -3
- package/dist/{TextInput-BAL-zOAN.js.map → TextInput-tWtbWBFQ.js.map} +1 -1
- package/dist/{ThemeProvider-MzVbTDBj.js → ThemeProvider-DHf_Tvwk.js} +2 -2
- package/dist/{ThemeProvider-MzVbTDBj.js.map → ThemeProvider-DHf_Tvwk.js.map} +1 -1
- package/dist/{ToggleAllTooltip-DQE5Lw6w.js → ToggleAllTooltip-EghNnsQC.js} +2 -2
- package/dist/{ToggleAllTooltip-DQE5Lw6w.js.map → ToggleAllTooltip-EghNnsQC.js.map} +1 -1
- package/dist/{ZoomableSvg-CIOqAYpd.js → ZoomableSvg-Cp2zJNIG.js} +4 -4
- package/dist/{ZoomableSvg-CIOqAYpd.js.map → ZoomableSvg-Cp2zJNIG.js.map} +1 -1
- package/dist/editors.js +3 -3
- package/dist/flint.js +3 -3
- package/dist/flint.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +47 -47
- package/dist/internal/blocks/AIKit.js +1 -1
- package/dist/internal/blocks/AppShell.js +1 -1
- package/dist/internal/blocks/AuthLayout.js +1 -1
- package/dist/internal/blocks/BrandEditor.js +1 -1
- package/dist/internal/blocks/BulkActionsBar.js +1 -1
- package/dist/internal/blocks/ChipEditor.js +2 -2
- package/dist/internal/blocks/ClipboardButton.js +1 -1
- package/dist/internal/blocks/ColorInput.js +1 -1
- package/dist/internal/blocks/ColorModeSwitch.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 +2 -2
- package/dist/internal/blocks/Form/connectors/ComboboxField.js +2 -2
- package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +1 -1
- package/dist/internal/blocks/Form.js +2 -2
- package/dist/internal/blocks/FormGroup.js +1 -1
- package/dist/internal/blocks/InlineEdit.js +1 -1
- package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
- package/dist/internal/blocks/MultiCombobox.js +3 -3
- package/dist/internal/blocks/NumberInput.js +1 -1
- package/dist/internal/blocks/PageHeader.js +1 -1
- package/dist/internal/blocks/PageToolbar.js +1 -1
- package/dist/internal/blocks/PasswordInput.js +2 -2
- package/dist/internal/blocks/SearchInput.js +1 -1
- package/dist/internal/blocks/SearchInputWithNavigation.js +3 -3
- package/dist/internal/blocks/SectionNav.js +1 -1
- package/dist/internal/blocks/SplitButton.js +1 -1
- package/dist/internal/blocks/TextInput.js +1 -1
- package/dist/internal/blocks/Textarea.js +1 -1
- package/dist/internal/blocks/TimeInput.js +2 -2
- package/dist/internal/components/Alert.js +1 -1
- package/dist/internal/components/AlertModal.js +1 -1
- package/dist/internal/components/Avatar.js +3 -2
- package/dist/internal/components/Avatar.js.map +1 -1
- package/dist/internal/components/BrandLogo.js +1 -1
- package/dist/internal/components/Breadcrumb.js +1 -1
- package/dist/internal/components/Button.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/DateRangePicker.js +1 -1
- package/dist/internal/components/FileUpload.js +1 -1
- package/dist/internal/components/FullScreenModal.js +1 -1
- package/dist/internal/components/InputGroup.js +1 -1
- package/dist/internal/components/Kbd.js +1 -1
- package/dist/internal/components/Modal.js +1 -1
- package/dist/internal/components/Navbar.js +1 -1
- package/dist/internal/components/Pagination.js +1 -1
- package/dist/internal/components/Popconfirm.js +1 -1
- package/dist/internal/components/RadioGroup.js +1 -1
- package/dist/internal/components/Search.js +1 -1
- package/dist/internal/components/Select.js +1 -1
- package/dist/internal/components/Sheet.js +1 -1
- package/dist/internal/components/Sidebar.js +1 -1
- package/dist/internal/components/Slider.js +1 -1
- package/dist/internal/components/Tabs.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 +6 -6
- package/dist/mermaid.js +1 -1
- package/dist/rjsf.js +74 -54
- package/dist/rjsf.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/{utils-BxEJOnFD.js → utils-CLNKe1ar.js} +10 -1
- package/dist/{utils-BxEJOnFD.js.map → utils-CLNKe1ar.js.map} +1 -1
- package/package.json +26 -26
- package/dist/AIKit-DyysN1kS.js.map +0 -1
- package/dist/Combobox-RZ0i3w8u.js.map +0 -1
- package/dist/PageHeader-DosMyar2.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Slider-
|
|
1
|
+
{"version":3,"file":"Slider-D12JmXyd.js","names":["RadixThumb","RadixTrack","RadixRange"],"sources":["../src/components/Slider/useInteracting.ts","../src/components/Slider/SliderThumb.tsx","../src/components/Slider/SliderTrack.tsx","../src/components/Slider/Slider.tsx"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\n\ntype InteractingRef = (element: HTMLElement | null) => void;\n\nexport const useInteracting = (): readonly [boolean, InteractingRef] => {\n const [isInteracting, setIsInteracting] = useState(false);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n const setRef = useCallback<InteractingRef>((element) => {\n cleanupRef.current?.();\n cleanupRef.current = null;\n if (!element) return;\n\n let hover = false;\n let focus = false;\n let press = false;\n const recompute = () => setIsInteracting(hover || focus || press);\n\n const handleEnter = () => {\n hover = true;\n recompute();\n };\n const handleLeave = () => {\n hover = false;\n recompute();\n };\n const handleFocus = () => {\n focus = true;\n recompute();\n };\n const handleBlur = () => {\n focus = false;\n recompute();\n };\n const handleDown = () => {\n press = true;\n recompute();\n };\n const handleRelease = () => {\n press = false;\n recompute();\n };\n\n element.addEventListener('pointerenter', handleEnter);\n element.addEventListener('pointerleave', handleLeave);\n element.addEventListener('focus', handleFocus);\n element.addEventListener('blur', handleBlur);\n element.addEventListener('pointerdown', handleDown);\n element.addEventListener('pointerup', handleRelease);\n element.addEventListener('pointercancel', handleRelease);\n\n cleanupRef.current = () => {\n element.removeEventListener('pointerenter', handleEnter);\n element.removeEventListener('pointerleave', handleLeave);\n element.removeEventListener('focus', handleFocus);\n element.removeEventListener('blur', handleBlur);\n element.removeEventListener('pointerdown', handleDown);\n element.removeEventListener('pointerup', handleRelease);\n element.removeEventListener('pointercancel', handleRelease);\n };\n }, []);\n\n return [isInteracting, setRef] as const;\n};\n","import type { ComponentProps, ReactNode } from 'react';\nimport { Thumb as RadixThumb } from '@radix-ui/react-slider';\n\nimport { cn, mergeRefs } from '../../utils';\nimport { buttonVariants } from '../Button';\nimport { Tooltip } from '../Tooltip';\n\nimport { useInteracting } from './useInteracting';\n\ntype Props = ComponentProps<typeof RadixThumb> & {\n tooltip?: ReactNode;\n tooltipSide?: ComponentProps<typeof Tooltip.Content>['placement'];\n};\n\nexport const SliderThumb = ({\n ref: forwardedRef,\n className,\n tooltip,\n tooltipSide = 'top',\n ...props\n}: Props) => {\n const [isInteracting, interactingRef] = useInteracting();\n\n const thumb = (\n <RadixThumb\n ref={mergeRefs(forwardedRef ?? null, interactingRef)}\n className={cn(\n buttonVariants({ variant: 'outline', size: 'icon-sm' }),\n\n // layout\n 'tw:p-0',\n 'tw:active:opacity-100',\n 'tw:block',\n 'tw:size-4',\n 'tw:rounded-full',\n\n // cursors\n 'tw:data-[disabled]:cursor-not-allowed',\n 'tw:[&:not([data-disabled])]:cursor-pointer',\n\n className,\n )}\n {...props}\n />\n );\n\n if (tooltip == null) return thumb;\n\n return (\n <Tooltip.Root open={isInteracting}>\n <Tooltip.Trigger asChild>{thumb}</Tooltip.Trigger>\n <Tooltip.Content variant=\"action\" placement={tooltipSide}>\n {tooltip}\n </Tooltip.Content>\n </Tooltip.Root>\n );\n};\n","import type { ComponentProps } from 'react';\nimport { Range as RadixRange, Track as RadixTrack } from '@radix-ui/react-slider';\n\nimport { cn } from '../../utils';\n\ntype Props = ComponentProps<typeof RadixTrack> & { rangeClassName?: string };\n\nexport const SliderTrack = ({ ref, className, ...props }: Props) => (\n <RadixTrack\n ref={ref}\n className={cn(\n // layout\n 'tw:relative',\n 'tw:grow',\n 'tw:w-full',\n 'tw:h-1.5',\n 'tw:overflow-hidden',\n\n // style\n 'tw:rounded-full',\n 'tw:bg-muted',\n\n // vertical orientation\n 'tw:data-[orientation=vertical]:h-full',\n 'tw:data-[orientation=vertical]:w-1.5',\n )}\n {...props}\n >\n <RadixRange\n className={cn(\n // layout\n 'tw:absolute',\n 'tw:h-full',\n\n // style\n 'tw:bg-primary',\n\n // vertical orientation\n 'tw:data-[orientation=vertical]:w-full',\n\n className,\n )}\n />\n </RadixTrack>\n);\n","import { useState } from 'react';\nimport type { ComponentProps, ReactNode } from 'react';\nimport { Root } from '@radix-ui/react-slider';\n\nimport { cn } from '../../utils';\nimport { TooltipProvider } from '../Tooltip';\n\nimport { SliderThumb } from './SliderThumb';\nimport { SliderTrack } from './SliderTrack';\n\nexport type ThumbTooltipFn = (value: number, index: number) => ReactNode;\n\ntype Props = ComponentProps<typeof Root> & {\n thumbTooltip?: ThumbTooltipFn;\n thumbTooltipSide?: ComponentProps<typeof SliderThumb>['tooltipSide'];\n rangeClassName?: string;\n};\n\nexport const Slider = ({\n ref,\n className,\n value,\n defaultValue,\n min = 0,\n max = 100,\n onValueChange,\n thumbTooltip,\n thumbTooltipSide,\n rangeClassName,\n ...rest\n}: Props) => {\n const isControlled = Array.isArray(value);\n\n const [internalValues, setInternalValues] = useState<number[]>(() => {\n if (isControlled) return value;\n if (Array.isArray(defaultValue)) return defaultValue;\n return [min];\n });\n\n const liveValues = isControlled ? value : internalValues;\n\n const handleValueChange = (next: number[]) => {\n if (!isControlled) setInternalValues(next);\n onValueChange?.(next);\n };\n\n const body = (\n <Root\n ref={ref}\n value={liveValues}\n min={min}\n max={max}\n onValueChange={handleValueChange}\n className={cn(\n 'tw:relative',\n 'tw:flex',\n 'tw:items-center',\n 'tw:w-full',\n\n 'tw:touch-none',\n 'tw:select-none',\n\n 'tw:data-[orientation=vertical]:h-full',\n 'tw:data-[orientation=vertical]:w-auto',\n 'tw:data-[orientation=vertical]:flex-col',\n\n 'tw:data-[disabled]:opacity-disabled',\n className,\n )}\n {...rest}\n >\n <SliderTrack className={rangeClassName} />\n {liveValues.map((thumbValue, index) => (\n <SliderThumb\n // oxlint-disable-next-line react/no-array-index-key\n key={index}\n tooltip={thumbTooltip?.(thumbValue, index)}\n tooltipSide={thumbTooltipSide}\n />\n ))}\n </Root>\n );\n\n return thumbTooltip ? <TooltipProvider>{body}</TooltipProvider> : body;\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "./classNames-DkX30VJU.js";
|
|
2
2
|
import { X } from "./icons-DDR9dFSr.js";
|
|
3
|
-
import { Button } from "./Button-
|
|
3
|
+
import { Button } from "./Button-BgjVUBON.js";
|
|
4
4
|
import { ScrollArea } from "./internal/components/ScrollArea.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import * as RadixTabs from "@radix-ui/react-tabs";
|
|
@@ -66,4 +66,4 @@ const TabsContent = (props) => /* @__PURE__ */ jsx(RadixTabs.Content, {
|
|
|
66
66
|
});
|
|
67
67
|
export { Tabs, TabsContent };
|
|
68
68
|
|
|
69
|
-
//# sourceMappingURL=Tabs-
|
|
69
|
+
//# sourceMappingURL=Tabs-Z6ckOaSb.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs-
|
|
1
|
+
{"version":3,"file":"Tabs-Z6ckOaSb.js","names":[],"sources":["../src/components/Tabs/styles.ts","../src/components/Tabs/TabsList.tsx","../src/components/Tabs/TabsTrigger.tsx","../src/components/Tabs/Tabs.tsx","../src/components/Tabs/TabsContent.tsx"],"sourcesContent":["// Directional resets (border-t-0/border-x-0), NOT border-0: under Tailwind v4 a\n// consumer app's `border-0` utility can land after design's compiled bottom-border in\n// the shared `utilities` layer and zero its width (same layer + specificity → source\n// order wins), dropping the line. With directional resets, only the bottom-border\n// utility (`border-b` on the wrapper, `border-b-[3px]` on the trigger) sets the bottom.\nexport const tabsListWrapperClass =\n 'tw:shrink-0 tw:border-t-0 tw:border-x-0 tw:border-b tw:border-solid tw:border-border';\n\nexport const tabsListClass = 'tw:flex tw:gap-8 tw:shrink-0 tw:w-max';\n\nexport const tabsTriggerGroupClass =\n 'tw:group/tab-trigger-group tw:flex tw:shrink-0 tw:items-center';\n\n// The focus ring is **inset**, not an outline. `TabsList` wraps the row in a horizontal\n// `ScrollArea`, whose viewport is `overflow-y: hidden`, and a trigger is exactly as tall as that\n// viewport — so there is 0px of slack for anything painted outside the border box, and a focus\n// outline (the browser's, or a host app's global rule) gets cropped on the top and bottom edges,\n// plus the left edge on the first trigger. Painting inside the box cannot be clipped by any\n// ancestor. Owning the affordance here is also what stops the consumer's global rule applying.\nexport const tabsTriggerClass =\n 'tw:focus-visible:outline-hidden tw:focus-visible:ring-2 tw:focus-visible:ring-inset tw:focus-visible:ring-ring tw:flex tw:justify-center tw:items-center tw:gap-1.5 tw:bg-transparent tw:shrink-0 tw:border-t-0 tw:border-x-0 tw:border-b-[3px] tw:border-solid tw:border-transparent tw:pt-2.5 tw:pb-5 tw:px-0 tw:m-0 tw:font-medium tw:leading-5 tw:text-muted-foreground tw:text-base tw:data-[state=active]:text-primary tw:data-[state=active]:border-primary tw:data-[disabled]:text-muted-foreground tw:data-[disabled]:opacity-disabled tw:data-[disabled]:cursor-not-allowed tw:group-hover/tab-trigger-group:enabled:text-primary tw:group-hover/tab-trigger-group:enabled:border-primary tw:group-hover/tab-trigger-group:enabled:cursor-pointer';\n\nexport const tabsTriggerCompactClass = 'tw:text-sm tw:pb-4';\n","import * as RadixTabs from '@radix-ui/react-tabs';\n\nimport { cn } from '../../utils';\nimport { ScrollArea } from '../ScrollArea';\n\nimport { tabsListClass, tabsListWrapperClass } from './styles';\n\ntype Props = Omit<RadixTabs.TabsListProps, 'asChild'>;\n\nexport const TabsList = ({ className, children, ...rest }: Props) => (\n <ScrollArea orientation=\"horizontal\" className={tabsListWrapperClass}>\n <RadixTabs.List {...rest} data-slot=\"tabs-list\" className={cn(tabsListClass, className)}>\n {children}\n </RadixTabs.List>\n </ScrollArea>\n);\n","import * as RadixTabs from '@radix-ui/react-tabs';\n\nimport { cn } from '../../utils';\n\nimport { tabsTriggerClass, tabsTriggerCompactClass } from './styles';\n\ntype Props = Omit<RadixTabs.TabsTriggerProps, 'asChild'> & {\n compact?: boolean;\n};\n\nexport const TabsTrigger = ({ className, compact = false, ...rest }: Props) => (\n <RadixTabs.Trigger\n data-slot=\"tabs-trigger\"\n {...rest}\n className={cn(tabsTriggerClass, compact && tabsTriggerCompactClass, className)}\n />\n);\n","import type { ReactNode } from 'react';\nimport * as RadixTabs from '@radix-ui/react-tabs';\n\nimport { X } from '../../icons';\nimport { cn } from '../../utils';\nimport { Button } from '../Button';\n\nimport { tabsTriggerGroupClass } from './styles';\nimport { TabsList } from './TabsList';\nimport { TabsTrigger } from './TabsTrigger';\n\nexport type Trigger<T extends string> = {\n value: T;\n title: ReactNode;\n onClose?: (value: T) => void;\n} & Omit<RadixTabs.TabsTriggerProps, 'title' | 'asChild'>;\n\ntype Props<T extends string> = {\n triggers: readonly Trigger<T>[];\n compact?: boolean;\n children: ReactNode;\n defaultValue?: string;\n onValueChange?: (value: T) => void;\n rootClassName?: string;\n listClassName?: string;\n triggerClassName?: string;\n triggerGroupClassName?: string;\n} & Omit<RadixTabs.TabsProps, 'onValueChange'>;\n\nexport const Tabs = <T extends string>({\n triggers,\n compact = false,\n children,\n defaultValue,\n onValueChange,\n rootClassName,\n listClassName,\n triggerClassName,\n triggerGroupClassName,\n ...rest\n}: Props<T>) => (\n <RadixTabs.Root\n {...rest}\n data-slot=\"tabs-root\"\n className={cn('tw:flex tw:flex-col tw:gap-6', rootClassName)}\n defaultValue={defaultValue ?? triggers[0]?.value}\n onValueChange={onValueChange as (value: string) => void}\n >\n <TabsList className={listClassName}>\n {triggers.map(({ value, title, onClose, className: callerClassName, ...triggerRest }) => (\n <div\n key={value}\n className={cn(tabsTriggerGroupClass, triggerGroupClassName)}\n data-slot=\"tabs-trigger-group\"\n >\n <TabsTrigger\n value={value}\n compact={compact}\n {...triggerRest}\n className={cn(triggerClassName, callerClassName)}\n >\n {title}\n </TabsTrigger>\n {onClose && (\n <Button\n type=\"button\"\n variant=\"inline\"\n size=\"icon-3xs\"\n disabled={triggerRest.disabled}\n onClick={() => onClose(value)}\n onMouseDown={(e) => e.preventDefault()}\n className=\"tw:!size-8 tw:opacity-0 tw:duration-200 tw:focus-visible:opacity-100 tw:group-focus-within/tab-trigger-group:opacity-100 tw:group-hover/tab-trigger-group:opacity-100\"\n aria-label={typeof title === 'string' ? `Close ${title}` : 'Close tab'}\n >\n <X className=\"tw:size-4\" />\n </Button>\n )}\n </div>\n ))}\n </TabsList>\n {children}\n </RadixTabs.Root>\n);\n","import * as RadixTabs from '@radix-ui/react-tabs';\n\nexport const TabsContent = (props: RadixTabs.TabsContentProps) => (\n <RadixTabs.Content data-slot=\"tabs-content\" tabIndex={-1} {...props} />\n);\n"],"mappings":";;;;;;;AAKA,MAAa,uBACX;AAEF,MAAa,gBAAgB;AAE7B,MAAa,wBACX;AAQF,MAAa,mBACX;;ACXF,MAAa,YAAY,EAAE,WAAW,UAAU,GAAG,WACjD,oBAAC,YAAD;CAAY,aAAY;CAAa,WAAW;CAC9C,UAAA,oBAAC,UAAU,MAAX;EAAgB,GAAI;EAAM,aAAU;EAAY,WAAW,GAAG,eAAe,SAAS;EACnF;CACa,CAAA;AACN,CAAA;;ACJd,MAAa,eAAe,EAAE,WAAW,UAAU,OAAO,GAAG,WAC3D,oBAAC,UAAU,SAAX;CACE,aAAU;CACV,GAAI;CACJ,WAAW,GAAG,kBAAkB,WAAA,sBAAoC,SAAS;AAC9E,CAAA;;ACcH,MAAa,QAA0B,EACrC,UACA,UAAU,OACV,UACA,cACA,eACA,eACA,eACA,kBACA,uBACA,GAAG,WAEH,qBAAC,UAAU,MAAX;CACE,GAAI;CACJ,aAAU;CACV,WAAW,GAAG,gCAAgC,aAAa;CAC3D,cAAc,gBAAgB,SAAS,EAAE,EAAE;CAC5B;CALjB,UAAA,CAOE,oBAAC,UAAD;EAAU,WAAW;EAClB,UAAA,SAAS,KAAK,EAAE,OAAO,OAAO,SAAS,WAAW,iBAAiB,GAAG,kBACrE,qBAAC,OAAD;GAEE,WAAW,GAAG,uBAAuB,qBAAqB;GAC1D,aAAU;GAHZ,UAAA,CAKE,oBAAC,aAAD;IACS;IACE;IACT,GAAI;IACJ,WAAW,GAAG,kBAAkB,eAAe;IAE9C,UAAA;GACU,CAAA,GACZ,WACC,oBAAC,QAAD;IACE,MAAK;IACL,SAAQ;IACR,MAAK;IACL,UAAU,YAAY;IACtB,eAAe,QAAQ,KAAK;IAC5B,cAAc,MAAM,EAAE,eAAe;IACrC,WAAU;IACV,cAAY,OAAO,UAAU,WAAW,SAAS,UAAU;IAE3D,UAAA,oBAAC,GAAD,EAAG,WAAU,YAAa,CAAA;GACpB,CAAA,CAEP;EA1BE,GAAA,KA0BF,CACN;CACO,CAAA,GACT,QACa;;;AC/ElB,MAAa,eAAe,UAC1B,oBAAC,UAAU,SAAX;CAAmB,aAAU;CAAe,UAAU;CAAI,GAAI;AAAQ,CAAA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from "./classNames-DkX30VJU.js";
|
|
2
|
-
import { createChangeInputEvent, createPrefixedString } from "./utils-
|
|
2
|
+
import { createChangeInputEvent, createPrefixedString } from "./utils-CLNKe1ar.js";
|
|
3
3
|
import { mergeRefs } from "./internal/utils/mergeRefs.js";
|
|
4
4
|
import { X } from "./icons-DDR9dFSr.js";
|
|
5
|
-
import { InputGroup } from "./InputGroup-
|
|
5
|
+
import { InputGroup } from "./InputGroup-oui6630r.js";
|
|
6
6
|
import { useControllableState } from "./useControllableState-BCvUOC6G.js";
|
|
7
7
|
import { useRef } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -99,4 +99,4 @@ const TextInput = ({ ref: forwardedRef, state = "default", className, prefix = n
|
|
|
99
99
|
};
|
|
100
100
|
export { TextInput };
|
|
101
101
|
|
|
102
|
-
//# sourceMappingURL=TextInput-
|
|
102
|
+
//# sourceMappingURL=TextInput-tWtbWBFQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextInput-
|
|
1
|
+
{"version":3,"file":"TextInput-tWtbWBFQ.js","names":[],"sources":["../src/blocks/TextInput/utils.ts","../src/blocks/TextInput/TextInput.tsx"],"sourcesContent":["import { createPrefixedString } from '../../utils';\n\nexport const genInputTestId = (prefix: string = '') => {\n const ps = createPrefixedString(prefix);\n return {\n toString: () => prefix,\n inputWrapper: ps`input-wrapper`,\n input: ps`input`,\n clearButton: ps`clear-button`,\n };\n};\n","import { useRef } from 'react';\nimport type {\n ChangeEventHandler,\n ComponentProps,\n ComponentRef,\n FocusEventHandler,\n MouseEventHandler,\n ReactNode,\n} from 'react';\n\nimport { InputGroup } from '../../components/InputGroup';\nimport { useControllableState } from '../../hooks/useControllableState';\nimport { X } from '../../icons';\nimport { cn, createChangeInputEvent, mergeRefs } from '../../utils';\n\nimport { genInputTestId } from './utils';\n\ntype InputGroupProps = Pick<ComponentProps<typeof InputGroup>, 'focusVariant' | 'state'>;\n\ntype OwnProps = {\n prefix?: ReactNode;\n /** `rightSuffixVariant=\"addon\"` truncates it; the ellipsis needs inline content (text or `<span>`). */\n rightSuffix?: ReactNode;\n rightSuffixVariant?: 'addon' | 'default';\n leftSuffix?: ReactNode;\n allowClear?: boolean;\n onClear?: () => void;\n testId?: string;\n value?: string | null;\n defaultValue?: string | null;\n /** Receives the string value; `onChange` gets the raw event. */\n onValueChange?: (value: string) => void;\n /** Receives the string value; `onBlur` gets the raw event. */\n onValueBlur?: (value: string) => void;\n type?: 'text' | 'email' | 'url' | 'password' | 'datetime-local' | 'date';\n};\n\n/** Props for `TextInput`; `null` / `undefined` values are coerced to `''`. */\nexport type Props = InputGroupProps &\n OwnProps &\n Omit<ComponentProps<'input'>, 'prefix' | 'value' | 'defaultValue' | 'type' | 'accept' | 'step'>;\n\nconst sanitizeTextInputValue = (value: string | null | undefined) => {\n if (typeof value === 'string' && value.trim() === '') return '';\n return value;\n};\n\n/**\n * Standard text input with a string-only API, built on `InputGroup`.\n *\n * **When to use:** plain text / email / password / date fields outside `useAppForm`.\n * **When NOT to use:** `FormGroup.TextInput` (labelled group), `field.TextField` (validated\n * form), `SearchInput` (search).\n */\nexport const TextInput = ({\n ref: forwardedRef,\n state = 'default',\n className,\n prefix = null,\n rightSuffix = null,\n rightSuffixVariant = 'default',\n leftSuffix = null,\n allowClear = false,\n onClear,\n disabled = false,\n testId: prefixTestId = '',\n autoFocus = false,\n id,\n name,\n readOnly,\n type = 'text',\n focusVariant = 'default',\n onChange,\n onBlur,\n onValueChange,\n onValueBlur,\n value: valueProp,\n defaultValue,\n ...inputProps\n}: Props) => {\n const inputRef = useRef<ComponentRef<'input'>>(null);\n\n const [value, setValue] = useControllableState<string>({\n // `undefined` stays uncontrolled; any other value (incl. `null`) is controlled.\n prop: valueProp === undefined ? undefined : (sanitizeTextInputValue(valueProp) ?? ''),\n defaultProp: sanitizeTextInputValue(defaultValue) ?? '',\n onChange: onValueChange,\n });\n\n const testId = genInputTestId(prefixTestId);\n const isClearButtonVisible = (!!value && !disabled && !readOnly) || !!onClear;\n\n const handleChange: ChangeEventHandler<HTMLInputElement> = (event) => {\n setValue(event.target.value);\n onChange?.(event);\n };\n\n const handleBlur: FocusEventHandler<HTMLInputElement> = (event) => {\n onValueBlur?.(event.target.value);\n onBlur?.(event);\n };\n\n const handleClear: MouseEventHandler<HTMLButtonElement> = () => {\n if (inputRef.current === null) return;\n inputRef.current.value = ''; // keep the DOM node in sync before firing the synthetic change\n inputRef.current.focus();\n onClear?.();\n handleChange(createChangeInputEvent(inputRef.current));\n };\n\n return (\n <InputGroup\n state={state}\n focusVariant={focusVariant}\n className={className}\n data-testid={testId.inputWrapper}\n >\n {prefix && <InputGroup.Addon align=\"inline-start\">{prefix}</InputGroup.Addon>}\n\n <InputGroup.Input\n {...inputProps}\n value={value}\n autoFocus={autoFocus}\n type={type}\n onChange={handleChange}\n onBlur={handleBlur}\n ref={mergeRefs(forwardedRef ?? null, inputRef)}\n disabled={disabled}\n id={id}\n name={name}\n readOnly={readOnly}\n data-testid={testId.input}\n />\n\n {(leftSuffix || allowClear || rightSuffix) && (\n <InputGroup.Addon\n align=\"inline-end\"\n className={cn({ 'tw:min-w-0 tw:shrink': rightSuffixVariant === 'addon' })}\n >\n {leftSuffix}\n {allowClear && isClearButtonVisible && (\n <InputGroup.Button\n aria-label=\"Clear\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={handleClear}\n data-testid={testId.clearButton}\n >\n <X className=\"tw:size-4\" />\n </InputGroup.Button>\n )}\n {rightSuffix && (\n <div\n className={cn({\n 'tw:truncate tw:min-w-0 tw:bg-muted tw:px-4 tw:-mr-4 tw:py-2.5':\n rightSuffixVariant === 'addon',\n })}\n >\n {rightSuffix}\n </div>\n )}\n </InputGroup.Addon>\n )}\n </InputGroup>\n );\n};\n"],"mappings":";;;;;;;;;AAEA,MAAa,kBAAkB,SAAiB,OAAO;CACrD,MAAM,KAAK,qBAAqB,MAAM;CACtC,OAAO;EACL,gBAAgB;EAChB,cAAc,EAAE;EAChB,OAAO,EAAE;EACT,aAAa,EAAE;CACjB;AACF;;ACgCA,MAAM,0BAA0B,UAAqC;CACnE,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAAI,OAAO;CAC7D,OAAO;AACT;;;;;;;;AASA,MAAa,aAAa,EACxB,KAAK,cACL,QAAQ,WACR,WACA,SAAS,MACT,cAAc,MACd,qBAAqB,WACrB,aAAa,MACb,aAAa,OACb,SACA,WAAW,OACX,QAAQ,eAAe,IACvB,YAAY,OACZ,IACA,MACA,UACA,OAAO,QACP,eAAe,WACf,UACA,QACA,eACA,aACA,OAAO,WACP,cACA,GAAG,iBACQ;CACX,MAAM,WAAW,OAA8B,IAAI;CAEnD,MAAM,CAAC,OAAO,YAAY,qBAA6B;EAErD,MAAM,cAAc,KAAA,IAAY,KAAA,IAAa,uBAAuB,SAAS,KAAK;EAClF,aAAa,uBAAuB,YAAY,KAAK;EACrD,UAAU;CACZ,CAAC;CAED,MAAM,SAAS,eAAe,YAAY;CAC1C,MAAM,uBAAwB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,YAAa,CAAC,CAAC;CAEtE,MAAM,gBAAsD,UAAU;EACpE,SAAS,MAAM,OAAO,KAAK;EAC3B,WAAW,KAAK;CAClB;CAEA,MAAM,cAAmD,UAAU;EACjE,cAAc,MAAM,OAAO,KAAK;EAChC,SAAS,KAAK;CAChB;CAEA,MAAM,oBAA0D;EAC9D,IAAI,SAAS,YAAY,MAAM;EAC/B,SAAS,QAAQ,QAAQ;EACzB,SAAS,QAAQ,MAAM;EACvB,UAAU;EACV,aAAa,uBAAuB,SAAS,OAAO,CAAC;CACvD;CAEA,OACE,qBAAC,YAAD;EACS;EACO;EACH;EACX,eAAa,OAAO;EAJtB,UAAA;GAMG,UAAU,oBAAC,WAAW,OAAZ;IAAkB,OAAM;IAAgB,UAAA;GAAyB,CAAA;GAE5E,oBAAC,WAAW,OAAZ;IACE,GAAI;IACG;IACI;IACL;IACN,UAAU;IACV,QAAQ;IACR,KAAK,UAAU,gBAAgB,MAAM,QAAQ;IACnC;IACN;IACE;IACI;IACV,eAAa,OAAO;GACrB,CAAA;IAEC,cAAc,cAAc,gBAC5B,qBAAC,WAAW,OAAZ;IACE,OAAM;IACN,WAAW,GAAG,EAAE,wBAAwB,uBAAuB,QAAQ,CAAC;IAF1E,UAAA;KAIG;KACA,cAAc,wBACb,oBAAC,WAAW,QAAZ;MACE,cAAW;MACX,cAAc,MAAM,EAAE,eAAe;MACrC,SAAS;MACT,eAAa,OAAO;MAEpB,UAAA,oBAAC,GAAD,EAAG,WAAU,YAAa,CAAA;KACT,CAAA;KAEpB,eACC,oBAAC,OAAD;MACE,WAAW,GAAG,EACZ,iEACE,uBAAuB,QAC3B,CAAC;MAEA,UAAA;KACE,CAAA;IAES;;EAEV;;AAEhB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TooltipProvider } from "./utils-
|
|
1
|
+
import { TooltipProvider } from "./utils-CLNKe1ar.js";
|
|
2
2
|
import { DEFAULT_BRANDS, DEFAULT_COLOR_SCHEME, ThemeScopeContainerContext, ThemeScopeSchemeContext, createCssVars, createTheme, defaultBrand, defaultTheme, useBrandState, useColorSchemeState } from "./themeScopeContext-BZGPdyG8.js";
|
|
3
3
|
import { createContext, use, useEffect, useInsertionEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { Slot } from "@radix-ui/react-slot";
|
|
@@ -288,4 +288,4 @@ const ThemeScope = ({ scheme, className, children }) => {
|
|
|
288
288
|
};
|
|
289
289
|
export { ThemeProvider, ThemeScope, agnes, brandSchema, exampleCustomer, harbor, kbcUi, keboola$1 as keboola, useTheme, useThemeScopeContainer };
|
|
290
290
|
|
|
291
|
-
//# sourceMappingURL=ThemeProvider-
|
|
291
|
+
//# sourceMappingURL=ThemeProvider-DHf_Tvwk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider-MzVbTDBj.js","names":[],"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/context.tsx","../src/components/ThemeProvider/useThemeTokens.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/components/ThemeProvider/ThemeScope.tsx"],"sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import { createContext, use } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nimport { DEFAULT_COLOR_SCHEME, defaultBrand, defaultTheme } from './defaults';\nimport type { Theme } from './types';\nimport type { useBrandState } from './useBrandState';\nimport type { useColorSchemeState } from './useColorSchemeState';\n\nexport type ThemeContextValue = ReturnType<typeof useColorSchemeState> &\n ReturnType<typeof useBrandState> & {\n themes: Theme[];\n currentTheme: Theme;\n /** Raw brand tokens, so a nested `ThemeScope` can re-derive them for another scheme. */\n brandTheme: Brand['theme'];\n };\n\nconst noop = () => void 0;\n\nconst defaultContext: ThemeContextValue = {\n scheme: DEFAULT_COLOR_SCHEME,\n resolvedScheme: DEFAULT_COLOR_SCHEME,\n setScheme: noop,\n brandId: defaultTheme.id,\n setBrand: noop,\n\n themes: [defaultTheme],\n currentTheme: defaultTheme,\n brandTheme: defaultBrand.theme,\n};\n\nexport const ThemeContext = createContext(defaultContext);\n\nexport const useTheme = () => use(ThemeContext);\n","import { useInsertionEffect } from 'react';\n\nimport type {\n BrandTheme,\n Colors,\n Gradient,\n Motion,\n Opacity,\n Radii,\n Shadows,\n Spacing,\n Typography,\n} from '@keboola/brand-registry';\n\nimport type { ResolvedColorScheme } from './types';\nimport { createCssVars } from './utils';\n\nexport type ThemeTokens = {\n colors?: Colors;\n typography?: Typography;\n radii?: Radii;\n shadows?: Shadows;\n spacing?: Spacing;\n motion?: Motion;\n gradient?: Gradient;\n opacity?: Opacity;\n};\n\nexport const brandThemeToTokens = (\n theme: BrandTheme,\n scheme: ResolvedColorScheme,\n): ThemeTokens => ({\n colors: scheme === 'dark' ? theme.colorsDark : theme.colors,\n typography: theme.typography,\n radii: theme.radii,\n // Scheme-independent — the same values apply in light and dark.\n shadows: theme.shadows,\n spacing: theme.spacing,\n motion: theme.motion,\n gradient: theme.gradient,\n opacity: theme.opacity,\n});\n\nexport const tokensToCssVars = ({\n colors,\n typography,\n radii,\n shadows,\n spacing,\n motion,\n gradient,\n opacity,\n}: ThemeTokens): Record<string, string> => {\n const vars = createCssVars();\n\n vars.set('--background', colors?.background);\n vars.set('--foreground', colors?.foreground);\n vars.set('--muted', colors?.muted);\n vars.set('--muted-foreground', colors?.mutedForeground);\n vars.set('--border', colors?.border);\n vars.set('--input', colors?.input);\n vars.set('--input-background', colors?.inputBackground);\n vars.set('--ring', colors?.ring);\n vars.set('--ring-offset', colors?.ringOffset);\n vars.set('--primary', colors?.primary);\n vars.set('--primary-foreground', colors?.primaryForeground);\n vars.set('--secondary', colors?.secondary);\n vars.set('--secondary-foreground', colors?.secondaryForeground);\n vars.set('--accent', colors?.accent);\n vars.set('--accent-foreground', colors?.accentForeground);\n vars.set('--card', colors?.card);\n vars.set('--card-foreground', colors?.cardForeground);\n vars.set('--popover', colors?.popover);\n vars.set('--popover-foreground', colors?.popoverForeground);\n vars.set('--destructive', colors?.destructive);\n vars.set('--destructive-foreground', colors?.destructiveForeground);\n vars.set('--success', colors?.success);\n vars.set('--success-foreground', colors?.successForeground);\n vars.set('--warning', colors?.warning);\n vars.set('--warning-foreground', colors?.warningForeground);\n vars.set('--info', colors?.info);\n vars.set('--info-foreground', colors?.infoForeground);\n\n // Full CSS colours, not channels — read as `var(--hover, …)`, never wrapped in `rgb()`.\n vars.set('--hover', colors?.hover);\n vars.set('--hover-foreground', colors?.hoverForeground);\n vars.set('--pressed', colors?.pressed);\n vars.set('--selected', colors?.selected);\n vars.set('--selected-foreground', colors?.selectedForeground);\n vars.set('--scrim', colors?.scrim);\n\n vars.set('--chart-1', colors?.chart?.chart1);\n vars.set('--chart-2', colors?.chart?.chart2);\n vars.set('--chart-3', colors?.chart?.chart3);\n vars.set('--chart-4', colors?.chart?.chart4);\n vars.set('--chart-5', colors?.chart?.chart5);\n\n vars.set('--sidebar', colors?.sidebar?.sidebar);\n vars.set('--sidebar-foreground', colors?.sidebar?.sidebarForeground);\n vars.set('--sidebar-primary', colors?.sidebar?.sidebarPrimary);\n vars.set('--sidebar-primary-foreground', colors?.sidebar?.sidebarPrimaryForeground);\n vars.set('--sidebar-accent', colors?.sidebar?.sidebarAccent);\n vars.set('--sidebar-accent-foreground', colors?.sidebar?.sidebarAccentForeground);\n vars.set('--sidebar-hover', colors?.sidebar?.sidebarHover);\n vars.set('--sidebar-hover-foreground', colors?.sidebar?.sidebarHoverForeground);\n vars.set('--sidebar-selected', colors?.sidebar?.sidebarSelected);\n vars.set('--sidebar-selected-foreground', colors?.sidebar?.sidebarSelectedForeground);\n vars.set('--sidebar-border', colors?.sidebar?.sidebarBorder);\n vars.set('--sidebar-ring', colors?.sidebar?.sidebarRing);\n\n vars.set('--font-sans', typography?.fontFamily?.trim() || undefined);\n vars.set('--font-display', typography?.fontDisplay?.trim() || undefined);\n vars.set('--font-mono', typography?.fontMono?.trim() || undefined);\n\n // Chrome font-weight slots — read by Tailwind's `fontWeight` scale.\n vars.set('--font-weight-normal', typography?.weight?.normal);\n vars.set('--font-weight-medium', typography?.weight?.medium);\n vars.set('--font-weight-semibold', typography?.weight?.semibold);\n vars.set('--font-weight-bold', typography?.weight?.bold);\n\n // Chrome tracking — behind Button/Badge `tracking-chrome`.\n vars.set('--letter-spacing-chrome', typography?.letterSpacing?.chrome);\n\n // Type scale — display/heading/body steps read by `text-display/heading/body`.\n vars.set('--type-display-size', typography?.typeScale?.display?.size);\n vars.set('--type-display-weight', typography?.typeScale?.display?.weight);\n vars.set('--type-display-leading', typography?.typeScale?.display?.leading);\n vars.set('--type-display-tracking', typography?.typeScale?.display?.tracking);\n vars.set('--type-heading-size', typography?.typeScale?.heading?.size);\n vars.set('--type-heading-weight', typography?.typeScale?.heading?.weight);\n vars.set('--type-heading-leading', typography?.typeScale?.heading?.leading);\n vars.set('--type-heading-tracking', typography?.typeScale?.heading?.tracking);\n vars.set('--type-body-size', typography?.typeScale?.body?.size);\n vars.set('--type-body-weight', typography?.typeScale?.body?.weight);\n vars.set('--type-body-leading', typography?.typeScale?.body?.leading);\n vars.set('--type-body-tracking', typography?.typeScale?.body?.tracking);\n\n vars.set('--radius-sm', radii?.sm);\n vars.set('--radius-md', radii?.md);\n vars.set('--radius-lg', radii?.lg);\n vars.set('--radius-card', radii?.card);\n vars.set('--radius-popover', radii?.popover);\n vars.set('--radius-modal', radii?.modal);\n vars.set('--radius-sheet', radii?.sheet);\n vars.set('--radius-toast', radii?.toast);\n\n vars.set('--shadow-sm', shadows?.sm);\n vars.set('--shadow-md', shadows?.md);\n vars.set('--shadow-lg', shadows?.lg);\n vars.set('--shadow-lift', shadows?.lift);\n\n vars.set('--space-gutter', spacing?.gutter);\n vars.set('--space-inset', spacing?.inset);\n\n vars.set('--motion-duration', motion?.duration);\n vars.set('--motion-easing', motion?.easing);\n\n vars.set('--gradient-brand', gradient?.brand);\n\n vars.set('--disabled-opacity', opacity?.disabled);\n\n return vars.expose();\n};\n\nconst setTokens = (node: HTMLElement, vars: Record<string, string>) => {\n for (const [name, value] of Object.entries(vars)) {\n node.style.setProperty(name, value);\n }\n};\n\nconst removeTokens = (node: HTMLElement, vars: Record<string, string>) => {\n for (const name of Object.keys(vars)) {\n node.style.removeProperty(name);\n }\n};\n\n/**\n * Writes a brand's token overrides as inline custom properties.\n *\n * - `target` defaults to `<html>`; an element scopes them to its subtree (`ThemeScope`).\n * - A brand injects resolved `--color-*` aliases that `[data-theme='dark']` does not re-point,\n * so a nested scheme must re-inject its own values or light colours leak into dark.\n */\nexport const useThemeTokens = (tokens: ThemeTokens, target?: HTMLElement | null) => {\n // Runs before any descendant layout effect measures token-dependent geometry.\n // Unset slots fall through to the stylesheet's defaults.\n useInsertionEffect(() => {\n if (typeof document === 'undefined') return;\n // `undefined` = app-wide; `null` = scope node not mounted yet.\n const node = target === undefined ? document.documentElement : target;\n if (!node) return;\n\n const vars = tokensToCssVars(tokens);\n\n setTokens(node, vars);\n\n return () => removeTokens(node, vars);\n }, [tokens, target]);\n};\n","import { type ReactNode, useMemo } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nimport { TooltipProvider } from '../Tooltip';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { ThemeContext, type ThemeContextValue } from './context';\nimport { DEFAULT_BRANDS, defaultBrand } from './defaults';\nimport { type UseBrandOptions, useBrandState } from './useBrandState';\nimport { type UseColorSchemeOptions, useColorSchemeState } from './useColorSchemeState';\nimport { brandThemeToTokens, useThemeTokens } from './useThemeTokens';\nimport { createTheme } from './utils';\n\ntype Props = UseColorSchemeOptions &\n UseBrandOptions & {\n /** Brands this provider knows about; the active brand id resolves against these. */\n brands?: readonly Brand[];\n children: ReactNode;\n };\n\nexport const ThemeProvider = ({\n defaultScheme,\n scheme,\n onSchemeChange,\n defaultBrandId,\n brandId,\n onBrandIdChange,\n brands = DEFAULT_BRANDS,\n children,\n}: Props) => {\n const colorSchemeState = useColorSchemeState({\n defaultScheme,\n scheme,\n onSchemeChange,\n });\n\n const brandState = useBrandState({\n defaultBrandId,\n brandId,\n onBrandIdChange,\n });\n\n // Resolve the active brand; an unknown id (incl. the default sentinel) falls\n // back to `defaultBrand`, whose empty theme injects nothing so the stylesheet\n // defaults apply.\n const brand = useMemo(\n () => brands.find((item) => item.id === brandState.brandId) ?? defaultBrand,\n [brands, brandState.brandId],\n );\n const themes = useMemo(() => brands.map(createTheme), [brands]);\n const currentTheme = useMemo(() => createTheme(brand), [brand]);\n\n // Only the brand's overrides for the resolved scheme are injected; every slot\n // it omits (including a missing dark palette) falls through to the stylesheet's\n // `:root` / `[data-theme='dark']` defaults — so dark mode works for any brand.\n const themeTokens = useMemo(\n () => brandThemeToTokens(brand.theme, colorSchemeState.resolvedScheme),\n [brand, colorSchemeState.resolvedScheme],\n );\n\n useThemeTokens(themeTokens);\n\n const { scheme: activeScheme, resolvedScheme, setScheme } = colorSchemeState;\n const { brandId: activeBrandId, setBrand } = brandState;\n const context = useMemo<ThemeContextValue>(\n () => ({\n scheme: activeScheme,\n resolvedScheme,\n setScheme,\n brandId: activeBrandId,\n setBrand,\n themes,\n currentTheme,\n brandTheme: brand.theme,\n }),\n [activeScheme, resolvedScheme, setScheme, activeBrandId, setBrand, themes, currentTheme, brand],\n );\n\n return (\n <ThemeContext value={context}>\n <AIGradientsSvg />\n <TooltipProvider>{children}</TooltipProvider>\n </ThemeContext>\n );\n};\n","import { type ReactNode, use, useEffect, useMemo, useRef, useState } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { useTheme } from './context';\nimport { ThemeScopeContainerContext, ThemeScopeSchemeContext } from './themeScopeContext';\nimport type { ColorScheme, ResolvedColorScheme } from './types';\nimport { brandThemeToTokens, tokensToCssVars, useThemeTokens } from './useThemeTokens';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\n// Overlays portal to <body>, outside the scope; pass this as their `container` to keep its tokens.\n/** The scope's themed portal container, or `null` outside a `ThemeScope`. */\nexport const useThemeScopeContainer = () => use(ThemeScopeContainerContext);\n\nconst useResolvedScheme = (scheme: ColorScheme): ResolvedColorScheme => {\n const [systemPrefersDark, setSystemPrefersDark] = useState(\n () => globalThis.matchMedia?.(DARK_QUERY).matches ?? false,\n );\n\n useEffect(() => {\n const query = globalThis.matchMedia?.(DARK_QUERY);\n if (!query) return;\n\n const onChange = (event: MediaQueryListEvent) => setSystemPrefersDark(event.matches);\n query.addEventListener('change', onChange);\n\n return () => query.removeEventListener('change', onChange);\n }, []);\n\n if (scheme === 'system') return systemPrefersDark ? 'dark' : 'light';\n\n return scheme;\n};\n\ntype Props = {\n /** Colour scheme for this subtree. `system` follows the OS preference. */\n scheme: ColorScheme;\n /** Merged onto the child element. */\n className?: string;\n /** A single element to theme. Its props are merged — no wrapper is rendered. */\n children: ReactNode;\n};\n\n/**\n * Runs one subtree in its own colour scheme.\n *\n * - Also re-injects the brand's tokens for its scheme: a bare `data-theme` only re-points raw\n * tokens, not the brand's inline vars inherited from `<html>`.\n * - Renders no wrapper: one would break direct-child selectors like `[data-fill=true] > &`.\n */\nexport const ThemeScope = ({ scheme, className, children }: Props) => {\n const { brandTheme } = useTheme();\n const resolvedScheme = useResolvedScheme(scheme);\n const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(null);\n\n const tokens = useMemo(\n () => brandThemeToTokens(brandTheme, resolvedScheme),\n [brandTheme, resolvedScheme],\n );\n const style = useMemo(() => tokensToCssVars(tokens), [tokens]);\n\n useThemeTokens(tokens, portalContainer);\n\n // Keeps the container from being rebuilt on every scheme flip.\n const schemeRef = useRef(resolvedScheme);\n schemeRef.current = resolvedScheme;\n\n useEffect(() => {\n const container = document.createElement('div');\n container.dataset.themeScopePortal = '';\n // Set before publishing, or a consumer would read and cache the page's palette.\n container.dataset.theme = schemeRef.current;\n document.body.append(container);\n setPortalContainer(container);\n\n return () => container.remove();\n }, []);\n\n // Keeps an existing container in step; the creation above covers the first commit.\n useEffect(() => {\n portalContainer?.setAttribute('data-theme', resolvedScheme);\n }, [portalContainer, resolvedScheme]);\n\n return (\n <ThemeScopeContainerContext value={portalContainer}>\n {/* Canvases (charts, editors) build palettes from this scope's scheme, not the page's. */}\n <ThemeScopeSchemeContext value={resolvedScheme}>\n <Slot data-theme={resolvedScheme} className={className} style={style}>\n {children}\n </Slot>\n </ThemeScopeSchemeContext>\n </ThemeScopeContainerContext>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ThemeProvider-DHf_Tvwk.js","names":[],"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/context.tsx","../src/components/ThemeProvider/useThemeTokens.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/components/ThemeProvider/ThemeScope.tsx"],"sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import { createContext, use } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nimport { DEFAULT_COLOR_SCHEME, defaultBrand, defaultTheme } from './defaults';\nimport type { Theme } from './types';\nimport type { useBrandState } from './useBrandState';\nimport type { useColorSchemeState } from './useColorSchemeState';\n\nexport type ThemeContextValue = ReturnType<typeof useColorSchemeState> &\n ReturnType<typeof useBrandState> & {\n themes: Theme[];\n currentTheme: Theme;\n /** Raw brand tokens, so a nested `ThemeScope` can re-derive them for another scheme. */\n brandTheme: Brand['theme'];\n };\n\nconst noop = () => void 0;\n\nconst defaultContext: ThemeContextValue = {\n scheme: DEFAULT_COLOR_SCHEME,\n resolvedScheme: DEFAULT_COLOR_SCHEME,\n setScheme: noop,\n brandId: defaultTheme.id,\n setBrand: noop,\n\n themes: [defaultTheme],\n currentTheme: defaultTheme,\n brandTheme: defaultBrand.theme,\n};\n\nexport const ThemeContext = createContext(defaultContext);\n\nexport const useTheme = () => use(ThemeContext);\n","import { useInsertionEffect } from 'react';\n\nimport type {\n BrandTheme,\n Colors,\n Gradient,\n Motion,\n Opacity,\n Radii,\n Shadows,\n Spacing,\n Typography,\n} from '@keboola/brand-registry';\n\nimport type { ResolvedColorScheme } from './types';\nimport { createCssVars } from './utils';\n\nexport type ThemeTokens = {\n colors?: Colors;\n typography?: Typography;\n radii?: Radii;\n shadows?: Shadows;\n spacing?: Spacing;\n motion?: Motion;\n gradient?: Gradient;\n opacity?: Opacity;\n};\n\nexport const brandThemeToTokens = (\n theme: BrandTheme,\n scheme: ResolvedColorScheme,\n): ThemeTokens => ({\n colors: scheme === 'dark' ? theme.colorsDark : theme.colors,\n typography: theme.typography,\n radii: theme.radii,\n // Scheme-independent — the same values apply in light and dark.\n shadows: theme.shadows,\n spacing: theme.spacing,\n motion: theme.motion,\n gradient: theme.gradient,\n opacity: theme.opacity,\n});\n\nexport const tokensToCssVars = ({\n colors,\n typography,\n radii,\n shadows,\n spacing,\n motion,\n gradient,\n opacity,\n}: ThemeTokens): Record<string, string> => {\n const vars = createCssVars();\n\n vars.set('--background', colors?.background);\n vars.set('--foreground', colors?.foreground);\n vars.set('--muted', colors?.muted);\n vars.set('--muted-foreground', colors?.mutedForeground);\n vars.set('--border', colors?.border);\n vars.set('--input', colors?.input);\n vars.set('--input-background', colors?.inputBackground);\n vars.set('--ring', colors?.ring);\n vars.set('--ring-offset', colors?.ringOffset);\n vars.set('--primary', colors?.primary);\n vars.set('--primary-foreground', colors?.primaryForeground);\n vars.set('--secondary', colors?.secondary);\n vars.set('--secondary-foreground', colors?.secondaryForeground);\n vars.set('--accent', colors?.accent);\n vars.set('--accent-foreground', colors?.accentForeground);\n vars.set('--card', colors?.card);\n vars.set('--card-foreground', colors?.cardForeground);\n vars.set('--popover', colors?.popover);\n vars.set('--popover-foreground', colors?.popoverForeground);\n vars.set('--destructive', colors?.destructive);\n vars.set('--destructive-foreground', colors?.destructiveForeground);\n vars.set('--success', colors?.success);\n vars.set('--success-foreground', colors?.successForeground);\n vars.set('--warning', colors?.warning);\n vars.set('--warning-foreground', colors?.warningForeground);\n vars.set('--info', colors?.info);\n vars.set('--info-foreground', colors?.infoForeground);\n\n // Full CSS colours, not channels — read as `var(--hover, …)`, never wrapped in `rgb()`.\n vars.set('--hover', colors?.hover);\n vars.set('--hover-foreground', colors?.hoverForeground);\n vars.set('--pressed', colors?.pressed);\n vars.set('--selected', colors?.selected);\n vars.set('--selected-foreground', colors?.selectedForeground);\n vars.set('--scrim', colors?.scrim);\n\n vars.set('--chart-1', colors?.chart?.chart1);\n vars.set('--chart-2', colors?.chart?.chart2);\n vars.set('--chart-3', colors?.chart?.chart3);\n vars.set('--chart-4', colors?.chart?.chart4);\n vars.set('--chart-5', colors?.chart?.chart5);\n\n vars.set('--sidebar', colors?.sidebar?.sidebar);\n vars.set('--sidebar-foreground', colors?.sidebar?.sidebarForeground);\n vars.set('--sidebar-primary', colors?.sidebar?.sidebarPrimary);\n vars.set('--sidebar-primary-foreground', colors?.sidebar?.sidebarPrimaryForeground);\n vars.set('--sidebar-accent', colors?.sidebar?.sidebarAccent);\n vars.set('--sidebar-accent-foreground', colors?.sidebar?.sidebarAccentForeground);\n vars.set('--sidebar-hover', colors?.sidebar?.sidebarHover);\n vars.set('--sidebar-hover-foreground', colors?.sidebar?.sidebarHoverForeground);\n vars.set('--sidebar-selected', colors?.sidebar?.sidebarSelected);\n vars.set('--sidebar-selected-foreground', colors?.sidebar?.sidebarSelectedForeground);\n vars.set('--sidebar-border', colors?.sidebar?.sidebarBorder);\n vars.set('--sidebar-ring', colors?.sidebar?.sidebarRing);\n\n vars.set('--font-sans', typography?.fontFamily?.trim() || undefined);\n vars.set('--font-display', typography?.fontDisplay?.trim() || undefined);\n vars.set('--font-mono', typography?.fontMono?.trim() || undefined);\n\n // Chrome font-weight slots — read by Tailwind's `fontWeight` scale.\n vars.set('--font-weight-normal', typography?.weight?.normal);\n vars.set('--font-weight-medium', typography?.weight?.medium);\n vars.set('--font-weight-semibold', typography?.weight?.semibold);\n vars.set('--font-weight-bold', typography?.weight?.bold);\n\n // Chrome tracking — behind Button/Badge `tracking-chrome`.\n vars.set('--letter-spacing-chrome', typography?.letterSpacing?.chrome);\n\n // Type scale — display/heading/body steps read by `text-display/heading/body`.\n vars.set('--type-display-size', typography?.typeScale?.display?.size);\n vars.set('--type-display-weight', typography?.typeScale?.display?.weight);\n vars.set('--type-display-leading', typography?.typeScale?.display?.leading);\n vars.set('--type-display-tracking', typography?.typeScale?.display?.tracking);\n vars.set('--type-heading-size', typography?.typeScale?.heading?.size);\n vars.set('--type-heading-weight', typography?.typeScale?.heading?.weight);\n vars.set('--type-heading-leading', typography?.typeScale?.heading?.leading);\n vars.set('--type-heading-tracking', typography?.typeScale?.heading?.tracking);\n vars.set('--type-body-size', typography?.typeScale?.body?.size);\n vars.set('--type-body-weight', typography?.typeScale?.body?.weight);\n vars.set('--type-body-leading', typography?.typeScale?.body?.leading);\n vars.set('--type-body-tracking', typography?.typeScale?.body?.tracking);\n\n vars.set('--radius-sm', radii?.sm);\n vars.set('--radius-md', radii?.md);\n vars.set('--radius-lg', radii?.lg);\n vars.set('--radius-card', radii?.card);\n vars.set('--radius-popover', radii?.popover);\n vars.set('--radius-modal', radii?.modal);\n vars.set('--radius-sheet', radii?.sheet);\n vars.set('--radius-toast', radii?.toast);\n\n vars.set('--shadow-sm', shadows?.sm);\n vars.set('--shadow-md', shadows?.md);\n vars.set('--shadow-lg', shadows?.lg);\n vars.set('--shadow-lift', shadows?.lift);\n\n vars.set('--space-gutter', spacing?.gutter);\n vars.set('--space-inset', spacing?.inset);\n\n vars.set('--motion-duration', motion?.duration);\n vars.set('--motion-easing', motion?.easing);\n\n vars.set('--gradient-brand', gradient?.brand);\n\n vars.set('--disabled-opacity', opacity?.disabled);\n\n return vars.expose();\n};\n\nconst setTokens = (node: HTMLElement, vars: Record<string, string>) => {\n for (const [name, value] of Object.entries(vars)) {\n node.style.setProperty(name, value);\n }\n};\n\nconst removeTokens = (node: HTMLElement, vars: Record<string, string>) => {\n for (const name of Object.keys(vars)) {\n node.style.removeProperty(name);\n }\n};\n\n/**\n * Writes a brand's token overrides as inline custom properties.\n *\n * - `target` defaults to `<html>`; an element scopes them to its subtree (`ThemeScope`).\n * - A brand injects resolved `--color-*` aliases that `[data-theme='dark']` does not re-point,\n * so a nested scheme must re-inject its own values or light colours leak into dark.\n */\nexport const useThemeTokens = (tokens: ThemeTokens, target?: HTMLElement | null) => {\n // Runs before any descendant layout effect measures token-dependent geometry.\n // Unset slots fall through to the stylesheet's defaults.\n useInsertionEffect(() => {\n if (typeof document === 'undefined') return;\n // `undefined` = app-wide; `null` = scope node not mounted yet.\n const node = target === undefined ? document.documentElement : target;\n if (!node) return;\n\n const vars = tokensToCssVars(tokens);\n\n setTokens(node, vars);\n\n return () => removeTokens(node, vars);\n }, [tokens, target]);\n};\n","import { type ReactNode, useMemo } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nimport { TooltipProvider } from '../Tooltip';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { ThemeContext, type ThemeContextValue } from './context';\nimport { DEFAULT_BRANDS, defaultBrand } from './defaults';\nimport { type UseBrandOptions, useBrandState } from './useBrandState';\nimport { type UseColorSchemeOptions, useColorSchemeState } from './useColorSchemeState';\nimport { brandThemeToTokens, useThemeTokens } from './useThemeTokens';\nimport { createTheme } from './utils';\n\ntype Props = UseColorSchemeOptions &\n UseBrandOptions & {\n /** Brands this provider knows about; the active brand id resolves against these. */\n brands?: readonly Brand[];\n children: ReactNode;\n };\n\nexport const ThemeProvider = ({\n defaultScheme,\n scheme,\n onSchemeChange,\n defaultBrandId,\n brandId,\n onBrandIdChange,\n brands = DEFAULT_BRANDS,\n children,\n}: Props) => {\n const colorSchemeState = useColorSchemeState({\n defaultScheme,\n scheme,\n onSchemeChange,\n });\n\n const brandState = useBrandState({\n defaultBrandId,\n brandId,\n onBrandIdChange,\n });\n\n // Resolve the active brand; an unknown id (incl. the default sentinel) falls\n // back to `defaultBrand`, whose empty theme injects nothing so the stylesheet\n // defaults apply.\n const brand = useMemo(\n () => brands.find((item) => item.id === brandState.brandId) ?? defaultBrand,\n [brands, brandState.brandId],\n );\n const themes = useMemo(() => brands.map(createTheme), [brands]);\n const currentTheme = useMemo(() => createTheme(brand), [brand]);\n\n // Only the brand's overrides for the resolved scheme are injected; every slot\n // it omits (including a missing dark palette) falls through to the stylesheet's\n // `:root` / `[data-theme='dark']` defaults — so dark mode works for any brand.\n const themeTokens = useMemo(\n () => brandThemeToTokens(brand.theme, colorSchemeState.resolvedScheme),\n [brand, colorSchemeState.resolvedScheme],\n );\n\n useThemeTokens(themeTokens);\n\n const { scheme: activeScheme, resolvedScheme, setScheme } = colorSchemeState;\n const { brandId: activeBrandId, setBrand } = brandState;\n const context = useMemo<ThemeContextValue>(\n () => ({\n scheme: activeScheme,\n resolvedScheme,\n setScheme,\n brandId: activeBrandId,\n setBrand,\n themes,\n currentTheme,\n brandTheme: brand.theme,\n }),\n [activeScheme, resolvedScheme, setScheme, activeBrandId, setBrand, themes, currentTheme, brand],\n );\n\n return (\n <ThemeContext value={context}>\n <AIGradientsSvg />\n <TooltipProvider>{children}</TooltipProvider>\n </ThemeContext>\n );\n};\n","import { type ReactNode, use, useEffect, useMemo, useRef, useState } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { useTheme } from './context';\nimport { ThemeScopeContainerContext, ThemeScopeSchemeContext } from './themeScopeContext';\nimport type { ColorScheme, ResolvedColorScheme } from './types';\nimport { brandThemeToTokens, tokensToCssVars, useThemeTokens } from './useThemeTokens';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\n// Overlays portal to <body>, outside the scope; pass this as their `container` to keep its tokens.\n/** The scope's themed portal container, or `null` outside a `ThemeScope`. */\nexport const useThemeScopeContainer = () => use(ThemeScopeContainerContext);\n\nconst useResolvedScheme = (scheme: ColorScheme): ResolvedColorScheme => {\n const [systemPrefersDark, setSystemPrefersDark] = useState(\n () => globalThis.matchMedia?.(DARK_QUERY).matches ?? false,\n );\n\n useEffect(() => {\n const query = globalThis.matchMedia?.(DARK_QUERY);\n if (!query) return;\n\n const onChange = (event: MediaQueryListEvent) => setSystemPrefersDark(event.matches);\n query.addEventListener('change', onChange);\n\n return () => query.removeEventListener('change', onChange);\n }, []);\n\n if (scheme === 'system') return systemPrefersDark ? 'dark' : 'light';\n\n return scheme;\n};\n\ntype Props = {\n /** Colour scheme for this subtree. `system` follows the OS preference. */\n scheme: ColorScheme;\n /** Merged onto the child element. */\n className?: string;\n /** A single element to theme. Its props are merged — no wrapper is rendered. */\n children: ReactNode;\n};\n\n/**\n * Runs one subtree in its own colour scheme.\n *\n * - Also re-injects the brand's tokens for its scheme: a bare `data-theme` only re-points raw\n * tokens, not the brand's inline vars inherited from `<html>`.\n * - Renders no wrapper: one would break direct-child selectors like `[data-fill=true] > &`.\n */\nexport const ThemeScope = ({ scheme, className, children }: Props) => {\n const { brandTheme } = useTheme();\n const resolvedScheme = useResolvedScheme(scheme);\n const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(null);\n\n const tokens = useMemo(\n () => brandThemeToTokens(brandTheme, resolvedScheme),\n [brandTheme, resolvedScheme],\n );\n const style = useMemo(() => tokensToCssVars(tokens), [tokens]);\n\n useThemeTokens(tokens, portalContainer);\n\n // Keeps the container from being rebuilt on every scheme flip.\n const schemeRef = useRef(resolvedScheme);\n schemeRef.current = resolvedScheme;\n\n useEffect(() => {\n const container = document.createElement('div');\n container.dataset.themeScopePortal = '';\n // Set before publishing, or a consumer would read and cache the page's palette.\n container.dataset.theme = schemeRef.current;\n document.body.append(container);\n setPortalContainer(container);\n\n return () => container.remove();\n }, []);\n\n // Keeps an existing container in step; the creation above covers the first commit.\n useEffect(() => {\n portalContainer?.setAttribute('data-theme', resolvedScheme);\n }, [portalContainer, resolvedScheme]);\n\n return (\n <ThemeScopeContainerContext value={portalContainer}>\n {/* Canvases (charts, editors) build palettes from this scope's scheme, not the page's. */}\n <ThemeScopeSchemeContext value={resolvedScheme}>\n <Slot data-theme={resolvedScheme} className={className} style={style}>\n {children}\n </Slot>\n </ThemeScopeSchemeContext>\n </ThemeScopeContainerContext>\n );\n};\n"],"mappings":";;;;;;;AAAA,MAAa,uBACX,oBAAC,OAAD;CAAK,OAAM;CAAI,QAAO;CAAI,OAAO;EAAE,UAAU;EAAY,YAAY;CAAS;CAC5E,UAAA,qBAAC,QAAD,EAAA,UAAA,CAEE,qBAAC,kBAAD;EAAgB,IAAG;EAAiB,IAAG;EAAK,IAAG;EAAK,IAAG;EAAO,IAAG;EAAjE,UAAA;GACE,oBAAC,QAAD;IAAM,QAAO;IAAK,WAAU;GAAW,CAAA;GACvC,oBAAC,QAAD;IAAM,QAAO;IAAM,WAAU;GAAW,CAAA;GACxC,oBAAC,QAAD;IAAM,QAAO;IAAO,WAAU;GAAW,CAAA;EAC3B;CAGhB,CAAA,GAAA,qBAAC,kBAAD;EAAgB,IAAG;EAAiB,IAAG;EAAK,IAAG;EAAK,IAAG;EAAO,IAAG;EAAjE,UAAA,CACE,oBAAC,QAAD;GAAM,QAAO;GAAK,WAAU;EAAW,CAAA,GACvC,oBAAC,QAAD;GAAM,QAAO;GAAO,WAAU;EAAW,CAAA,CAC3B;CACZ,CAAA,CAAA,EAAA,CAAA;AACH,CAAA;;ACCP,MAAM,aAAa,KAAK;AAExB,MAAM,iBAAoC;CACxC,QAAQ;CACR,gBAAgB;CAChB,WAAW;CACX,SAAS,aAAa;CACtB,UAAU;CAEV,QAAQ,CAAC,YAAY;CACrB,cAAc;CACd,YAAY,aAAa;AAC3B;AAEA,MAAa,eAAe,cAAc,cAAc;AAExD,MAAa,iBAAiB,IAAI,YAAY;;ACL9C,MAAa,sBACX,OACA,YACiB;CACjB,QAAQ,WAAW,SAAS,MAAM,aAAa,MAAM;CACrD,YAAY,MAAM;CAClB,OAAO,MAAM;CAEb,SAAS,MAAM;CACf,SAAS,MAAM;CACf,QAAQ,MAAM;CACd,UAAU,MAAM;CAChB,SAAS,MAAM;AACjB;AAEA,MAAa,mBAAmB,EAC9B,QACA,YACA,OACA,SACA,SACA,QACA,UACA,cACyC;CACzC,MAAM,OAAO,cAAc;CAE3B,KAAK,IAAI,gBAAgB,QAAQ,UAAU;CAC3C,KAAK,IAAI,gBAAgB,QAAQ,UAAU;CAC3C,KAAK,IAAI,WAAW,QAAQ,KAAK;CACjC,KAAK,IAAI,sBAAsB,QAAQ,eAAe;CACtD,KAAK,IAAI,YAAY,QAAQ,MAAM;CACnC,KAAK,IAAI,WAAW,QAAQ,KAAK;CACjC,KAAK,IAAI,sBAAsB,QAAQ,eAAe;CACtD,KAAK,IAAI,UAAU,QAAQ,IAAI;CAC/B,KAAK,IAAI,iBAAiB,QAAQ,UAAU;CAC5C,KAAK,IAAI,aAAa,QAAQ,OAAO;CACrC,KAAK,IAAI,wBAAwB,QAAQ,iBAAiB;CAC1D,KAAK,IAAI,eAAe,QAAQ,SAAS;CACzC,KAAK,IAAI,0BAA0B,QAAQ,mBAAmB;CAC9D,KAAK,IAAI,YAAY,QAAQ,MAAM;CACnC,KAAK,IAAI,uBAAuB,QAAQ,gBAAgB;CACxD,KAAK,IAAI,UAAU,QAAQ,IAAI;CAC/B,KAAK,IAAI,qBAAqB,QAAQ,cAAc;CACpD,KAAK,IAAI,aAAa,QAAQ,OAAO;CACrC,KAAK,IAAI,wBAAwB,QAAQ,iBAAiB;CAC1D,KAAK,IAAI,iBAAiB,QAAQ,WAAW;CAC7C,KAAK,IAAI,4BAA4B,QAAQ,qBAAqB;CAClE,KAAK,IAAI,aAAa,QAAQ,OAAO;CACrC,KAAK,IAAI,wBAAwB,QAAQ,iBAAiB;CAC1D,KAAK,IAAI,aAAa,QAAQ,OAAO;CACrC,KAAK,IAAI,wBAAwB,QAAQ,iBAAiB;CAC1D,KAAK,IAAI,UAAU,QAAQ,IAAI;CAC/B,KAAK,IAAI,qBAAqB,QAAQ,cAAc;CAGpD,KAAK,IAAI,WAAW,QAAQ,KAAK;CACjC,KAAK,IAAI,sBAAsB,QAAQ,eAAe;CACtD,KAAK,IAAI,aAAa,QAAQ,OAAO;CACrC,KAAK,IAAI,cAAc,QAAQ,QAAQ;CACvC,KAAK,IAAI,yBAAyB,QAAQ,kBAAkB;CAC5D,KAAK,IAAI,WAAW,QAAQ,KAAK;CAEjC,KAAK,IAAI,aAAa,QAAQ,OAAO,MAAM;CAC3C,KAAK,IAAI,aAAa,QAAQ,OAAO,MAAM;CAC3C,KAAK,IAAI,aAAa,QAAQ,OAAO,MAAM;CAC3C,KAAK,IAAI,aAAa,QAAQ,OAAO,MAAM;CAC3C,KAAK,IAAI,aAAa,QAAQ,OAAO,MAAM;CAE3C,KAAK,IAAI,aAAa,QAAQ,SAAS,OAAO;CAC9C,KAAK,IAAI,wBAAwB,QAAQ,SAAS,iBAAiB;CACnE,KAAK,IAAI,qBAAqB,QAAQ,SAAS,cAAc;CAC7D,KAAK,IAAI,gCAAgC,QAAQ,SAAS,wBAAwB;CAClF,KAAK,IAAI,oBAAoB,QAAQ,SAAS,aAAa;CAC3D,KAAK,IAAI,+BAA+B,QAAQ,SAAS,uBAAuB;CAChF,KAAK,IAAI,mBAAmB,QAAQ,SAAS,YAAY;CACzD,KAAK,IAAI,8BAA8B,QAAQ,SAAS,sBAAsB;CAC9E,KAAK,IAAI,sBAAsB,QAAQ,SAAS,eAAe;CAC/D,KAAK,IAAI,iCAAiC,QAAQ,SAAS,yBAAyB;CACpF,KAAK,IAAI,oBAAoB,QAAQ,SAAS,aAAa;CAC3D,KAAK,IAAI,kBAAkB,QAAQ,SAAS,WAAW;CAEvD,KAAK,IAAI,eAAe,YAAY,YAAY,KAAK,KAAK,KAAA,CAAS;CACnE,KAAK,IAAI,kBAAkB,YAAY,aAAa,KAAK,KAAK,KAAA,CAAS;CACvE,KAAK,IAAI,eAAe,YAAY,UAAU,KAAK,KAAK,KAAA,CAAS;CAGjE,KAAK,IAAI,wBAAwB,YAAY,QAAQ,MAAM;CAC3D,KAAK,IAAI,wBAAwB,YAAY,QAAQ,MAAM;CAC3D,KAAK,IAAI,0BAA0B,YAAY,QAAQ,QAAQ;CAC/D,KAAK,IAAI,sBAAsB,YAAY,QAAQ,IAAI;CAGvD,KAAK,IAAI,2BAA2B,YAAY,eAAe,MAAM;CAGrE,KAAK,IAAI,uBAAuB,YAAY,WAAW,SAAS,IAAI;CACpE,KAAK,IAAI,yBAAyB,YAAY,WAAW,SAAS,MAAM;CACxE,KAAK,IAAI,0BAA0B,YAAY,WAAW,SAAS,OAAO;CAC1E,KAAK,IAAI,2BAA2B,YAAY,WAAW,SAAS,QAAQ;CAC5E,KAAK,IAAI,uBAAuB,YAAY,WAAW,SAAS,IAAI;CACpE,KAAK,IAAI,yBAAyB,YAAY,WAAW,SAAS,MAAM;CACxE,KAAK,IAAI,0BAA0B,YAAY,WAAW,SAAS,OAAO;CAC1E,KAAK,IAAI,2BAA2B,YAAY,WAAW,SAAS,QAAQ;CAC5E,KAAK,IAAI,oBAAoB,YAAY,WAAW,MAAM,IAAI;CAC9D,KAAK,IAAI,sBAAsB,YAAY,WAAW,MAAM,MAAM;CAClE,KAAK,IAAI,uBAAuB,YAAY,WAAW,MAAM,OAAO;CACpE,KAAK,IAAI,wBAAwB,YAAY,WAAW,MAAM,QAAQ;CAEtE,KAAK,IAAI,eAAe,OAAO,EAAE;CACjC,KAAK,IAAI,eAAe,OAAO,EAAE;CACjC,KAAK,IAAI,eAAe,OAAO,EAAE;CACjC,KAAK,IAAI,iBAAiB,OAAO,IAAI;CACrC,KAAK,IAAI,oBAAoB,OAAO,OAAO;CAC3C,KAAK,IAAI,kBAAkB,OAAO,KAAK;CACvC,KAAK,IAAI,kBAAkB,OAAO,KAAK;CACvC,KAAK,IAAI,kBAAkB,OAAO,KAAK;CAEvC,KAAK,IAAI,eAAe,SAAS,EAAE;CACnC,KAAK,IAAI,eAAe,SAAS,EAAE;CACnC,KAAK,IAAI,eAAe,SAAS,EAAE;CACnC,KAAK,IAAI,iBAAiB,SAAS,IAAI;CAEvC,KAAK,IAAI,kBAAkB,SAAS,MAAM;CAC1C,KAAK,IAAI,iBAAiB,SAAS,KAAK;CAExC,KAAK,IAAI,qBAAqB,QAAQ,QAAQ;CAC9C,KAAK,IAAI,mBAAmB,QAAQ,MAAM;CAE1C,KAAK,IAAI,oBAAoB,UAAU,KAAK;CAE5C,KAAK,IAAI,sBAAsB,SAAS,QAAQ;CAEhD,OAAO,KAAK,OAAO;AACrB;AAEA,MAAM,aAAa,MAAmB,SAAiC;CACrE,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,IAAI,GAC7C,KAAK,MAAM,YAAY,MAAM,KAAK;AAEtC;AAEA,MAAM,gBAAgB,MAAmB,SAAiC;CACxE,KAAK,MAAM,QAAQ,OAAO,KAAK,IAAI,GACjC,KAAK,MAAM,eAAe,IAAI;AAElC;;;;;;;;AASA,MAAa,kBAAkB,QAAqB,WAAgC;CAGlF,yBAAyB;EACvB,IAAI,OAAO,aAAa,aAAa;EAErC,MAAM,OAAO,WAAW,KAAA,IAAY,SAAS,kBAAkB;EAC/D,IAAI,CAAC,MAAM;EAEX,MAAM,OAAO,gBAAgB,MAAM;EAEnC,UAAU,MAAM,IAAI;EAEpB,aAAa,aAAa,MAAM,IAAI;CACtC,GAAG,CAAC,QAAQ,MAAM,CAAC;AACrB;;ACjLA,MAAa,iBAAiB,EAC5B,eACA,QACA,gBACA,gBACA,SACA,iBACA,SAAS,gBACT,eACW;CACX,MAAM,mBAAmB,oBAAoB;EAC3C;EACA;EACA;CACF,CAAC;CAED,MAAM,aAAa,cAAc;EAC/B;EACA;EACA;CACF,CAAC;CAKD,MAAM,QAAQ,cACN,OAAO,MAAM,SAAS,KAAK,OAAO,WAAW,OAAO,KAAK,cAC/D,CAAC,QAAQ,WAAW,OAAO,CAC7B;CACA,MAAM,SAAS,cAAc,OAAO,IAAI,WAAW,GAAG,CAAC,MAAM,CAAC;CAC9D,MAAM,eAAe,cAAc,YAAY,KAAK,GAAG,CAAC,KAAK,CAAC;CAK9D,MAAM,cAAc,cACZ,mBAAmB,MAAM,OAAO,iBAAiB,cAAc,GACrE,CAAC,OAAO,iBAAiB,cAAc,CACzC;CAEA,eAAe,WAAW;CAE1B,MAAM,EAAE,QAAQ,cAAc,gBAAgB,cAAc;CAC5D,MAAM,EAAE,SAAS,eAAe,aAAa;CAC7C,MAAM,UAAU,eACP;EACL,QAAQ;EACR;EACA;EACA,SAAS;EACT;EACA;EACA;EACA,YAAY,MAAM;CACpB,IACA;EAAC;EAAc;EAAgB;EAAW;EAAe;EAAU;EAAQ;EAAc;CAAK,CAChG;CAEA,OACE,qBAAC,cAAD;EAAc,OAAO;EAArB,UAAA,CACE,oBAAC,gBAAD,CAAiB,CAAA,GACjB,oBAAC,iBAAD,EAAkB,SAA0B,CAAA,CAChC;;AAElB;;AC7EA,MAAM,aAAa;;AAInB,MAAa,+BAA+B,IAAI,0BAA0B;AAE1E,MAAM,qBAAqB,WAA6C;CACtE,MAAM,CAAC,mBAAmB,wBAAwB,eAC1C,WAAW,aAAa,UAAU,CAAC,CAAC,WAAW,KACvD;CAEA,gBAAgB;EACd,MAAM,QAAQ,WAAW,aAAa,UAAU;EAChD,IAAI,CAAC,OAAO;EAEZ,MAAM,YAAY,UAA+B,qBAAqB,MAAM,OAAO;EACnF,MAAM,iBAAiB,UAAU,QAAQ;EAEzC,aAAa,MAAM,oBAAoB,UAAU,QAAQ;CAC3D,GAAG,CAAC,CAAC;CAEL,IAAI,WAAW,UAAU,OAAO,oBAAoB,SAAS;CAE7D,OAAO;AACT;;;;;;;;AAkBA,MAAa,cAAc,EAAE,QAAQ,WAAW,eAAsB;CACpE,MAAM,EAAE,eAAe,SAAS;CAChC,MAAM,iBAAiB,kBAAkB,MAAM;CAC/C,MAAM,CAAC,iBAAiB,sBAAsB,SAAgC,IAAI;CAElF,MAAM,SAAS,cACP,mBAAmB,YAAY,cAAc,GACnD,CAAC,YAAY,cAAc,CAC7B;CACA,MAAM,QAAQ,cAAc,gBAAgB,MAAM,GAAG,CAAC,MAAM,CAAC;CAE7D,eAAe,QAAQ,eAAe;CAGtC,MAAM,YAAY,OAAO,cAAc;CACvC,UAAU,UAAU;CAEpB,gBAAgB;EACd,MAAM,YAAY,SAAS,cAAc,KAAK;EAC9C,UAAU,QAAQ,mBAAmB;EAErC,UAAU,QAAQ,QAAQ,UAAU;EACpC,SAAS,KAAK,OAAO,SAAS;EAC9B,mBAAmB,SAAS;EAE5B,aAAa,UAAU,OAAO;CAChC,GAAG,CAAC,CAAC;CAGL,gBAAgB;EACd,iBAAiB,aAAa,cAAc,cAAc;CAC5D,GAAG,CAAC,iBAAiB,cAAc,CAAC;CAEpC,OACE,oBAAC,4BAAD;EAA4B,OAAO;EAEjC,UAAA,oBAAC,yBAAD;GAAyB,OAAO;GAC9B,UAAA,oBAAC,MAAD;IAAM,cAAY;IAA2B;IAAkB;IAC5D;GACG,CAAA;EACiB,CAAA;CACC,CAAA;AAEhC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger, isMac } from "./utils-
|
|
1
|
+
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger, isMac } from "./utils-CLNKe1ar.js";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
// src/components/Tooltip/toggleAll.ts
|
|
4
4
|
/** True for the click that should fold or unfold every group: Cmd on a Mac, Ctrl elsewhere. */
|
|
@@ -32,4 +32,4 @@ const ToggleAllTooltip = ({ expanded, children }) => {
|
|
|
32
32
|
};
|
|
33
33
|
export { ToggleAllTooltip, isToggleAllClick };
|
|
34
34
|
|
|
35
|
-
//# sourceMappingURL=ToggleAllTooltip-
|
|
35
|
+
//# sourceMappingURL=ToggleAllTooltip-EghNnsQC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToggleAllTooltip-
|
|
1
|
+
{"version":3,"file":"ToggleAllTooltip-EghNnsQC.js","names":[],"sources":["../src/components/Tooltip/toggleAll.ts","../src/components/Tooltip/ToggleAllTooltip.tsx"],"sourcesContent":["import { isMac } from '../../utils';\n\n/** True for the click that should fold or unfold every group: Cmd on a Mac, Ctrl elsewhere. */\nexport const isToggleAllClick = (event: { metaKey: boolean; ctrlKey: boolean }) =>\n isMac ? event.metaKey : event.ctrlKey;\n","import type { ReactElement } from 'react';\n\nimport { isMac } from '../../utils';\n\nimport { TooltipProvider } from './Tooltip';\nimport { TooltipContent } from './TooltipContent';\nimport { TooltipRoot } from './TooltipRoot';\nimport { TooltipTrigger } from './TooltipTrigger';\n\n/**\n * A fold chevron's tooltip: what a click does, and what the modifier click does to every group.\n *\n * - Its own provider, so a host without one still renders it.\n */\nexport const ToggleAllTooltip = ({\n expanded,\n children,\n}: {\n expanded: boolean;\n children: ReactElement;\n}) => {\n const verb = expanded ? 'collapse' : 'expand';\n return (\n <TooltipProvider>\n <TooltipRoot>\n <TooltipTrigger asChild>{children}</TooltipTrigger>\n <TooltipContent placement=\"top\">\n <span className=\"tw:flex tw:flex-col\">\n <span>Click to {verb}</span>\n <span className=\"tw:opacity-70\">\n {isMac ? '⌘' : 'Ctrl'}+click to {verb} all\n </span>\n </span>\n </TooltipContent>\n </TooltipRoot>\n </TooltipProvider>\n );\n};\n"],"mappings":";;;;AAGA,MAAa,oBAAoB,UAC/B,QAAQ,MAAM,UAAU,MAAM;;;;;;;ACUhC,MAAa,oBAAoB,EAC/B,UACA,eAII;CACJ,MAAM,OAAO,WAAW,aAAa;CACrC,OACE,oBAAC,iBAAD,EAAA,UACE,qBAAC,aAAD,EAAA,UAAA,CACE,oBAAC,gBAAD;EAAgB,SAAA;EAAS;CAAyB,CAAA,GAClD,oBAAC,gBAAD;EAAgB,WAAU;EACxB,UAAA,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,qBAAC,QAAD,EAAA,UAAA,CAAM,aAAU,IAAW,EAAA,CAAA,GAC3B,qBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA;KACG,QAAQ,MAAM;KAAO;KAAW;KAAK;IAClC;GACF,CAAA,CAAA;;CACQ,CAAA,CACL,EAAA,CAAA,EACE,CAAA;AAErB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from "./classNames-DkX30VJU.js";
|
|
2
|
-
import { Tooltip } from "./utils-
|
|
2
|
+
import { Tooltip } from "./utils-CLNKe1ar.js";
|
|
3
3
|
import { Maximize2, Minimize2 } from "./icons-DDR9dFSr.js";
|
|
4
|
-
import { Button } from "./Button-
|
|
5
|
-
import { ClipboardButton } from "./ClipboardButton-
|
|
4
|
+
import { Button } from "./Button-BgjVUBON.js";
|
|
5
|
+
import { ClipboardButton } from "./ClipboardButton-1ISIYJQj.js";
|
|
6
6
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { createPortal } from "react-dom";
|
|
@@ -233,4 +233,4 @@ const ZoomableSvg = ({ html, copyText, className, allowForeignObject, allowUse,
|
|
|
233
233
|
};
|
|
234
234
|
export { ZoomableSvg, svgToPngBlob, toSvgFileContent };
|
|
235
235
|
|
|
236
|
-
//# sourceMappingURL=ZoomableSvg-
|
|
236
|
+
//# sourceMappingURL=ZoomableSvg-Cp2zJNIG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZoomableSvg-CIOqAYpd.js","names":[],"sources":["../src/components/ZoomableSvg/exportSvg.ts","../src/components/ZoomableSvg/ZoomableSvg.tsx"],"sourcesContent":["const PNG_SCALE = 2;\n\n// The root `<svg …>` tag, so the namespace check looks only at its attributes.\n// A plain `includes('xmlns=')` would also match a nested element or body text,\n// and would miss `xmlns = \"…\"` — adding a second declaration, which parsers\n// reject outright with \"Attribute xmlns redefined\".\nconst ROOT_SVG_TAG = /^<svg\\b[^>]*>/i;\nconst HAS_DEFAULT_NAMESPACE = /\\bxmlns\\s*=/i;\n\n// A standalone SVG file needs the namespace declaration; inline diagram output may\n// omit it because an inline <svg> inherits it from the host document.\n// Trim before matching: the anchored `^<svg` misses markup that arrives with a\n// leading newline or indentation, which would emit a standalone file with no\n// namespace — unopenable in an image viewer.\nconst withSvgNamespace = (svg: string) => {\n const trimmed = svg.trim();\n const rootTag = trimmed.match(ROOT_SVG_TAG)?.[0];\n\n if (!rootTag || HAS_DEFAULT_NAMESPACE.test(rootTag)) return trimmed;\n\n return trimmed.replace(/^<svg\\b/i, '<svg xmlns=\"http://www.w3.org/2000/svg\"');\n};\n\nexport const toSvgFileContent = (svg: string) =>\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${withSvgNamespace(svg).trim()}\\n`;\n\n// A single decimal number: `12`, `12.5`, `.5`. Deliberately stricter than\n// `[\\d.]+`, which also matches `1.2.3` — and `parseFloat` truncates that to\n// `1.2` rather than rejecting it, so a malformed value would pass as a plausible\n// one.\nconst NUMBER = String.raw`\\d+(?:\\.\\d+)?|\\.\\d+`;\n\n// `parseFloat` happily reads a relative length as a number — `\"100%\"` becomes\n// `100`, `\"2em\"` becomes `2` — and `makeResponsive` sets `width=\"100%\"` on every\n// diagram root, so an SVG with no usable viewBox would rasterize 100px wide with\n// no error. Accept only a unitless number or an explicit `px`.\nconst PIXEL_LENGTH = new RegExp(String.raw`^\\s*(?:${NUMBER})(?:px)?\\s*$`, 'i');\n\nconst parsePixelLength = (value: string | null): number | null => {\n if (!PIXEL_LENGTH.test(value ?? '')) return null;\n\n const parsed = Number.parseFloat(value ?? '');\n\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n};\n\nexport const parseSvgSize = (svg: string) => {\n const doc = new DOMParser().parseFromString(withSvgNamespace(svg), 'image/svg+xml');\n const element = doc.documentElement;\n\n const viewBox = element\n .getAttribute('viewBox')\n ?.split(/[\\s,]+/)\n .map(Number);\n // `> 0`, not truthiness: a negative extent is accepted by a truthy check, and\n // negative canvas dimensions silently fall back to the 300x150 default — an\n // export at the wrong size with no error. Falling through to width/height is\n // the better answer, since those are usually still valid.\n if (viewBox?.length === 4 && viewBox.every((value) => Number.isFinite(value))) {\n const [, , width, height] = viewBox;\n if (width !== undefined && height !== undefined && width > 0 && height > 0) {\n return { width, height };\n }\n }\n\n const width = parsePixelLength(element.getAttribute('width'));\n const height = parsePixelLength(element.getAttribute('height'));\n\n return width !== null && height !== null ? { width, height } : null;\n};\n\n// Resolve the themed page background so the exported PNG follows the active\n// brand and color scheme instead of a frozen literal. Falls back to `white`\n// (a CSS keyword, not a brand-locked hex) when the token is unset.\n//\n// The token holds Tailwind's bare channel triplet (\"249 250 251\") so utilities\n// can add an alpha. Canvas `fillStyle` rejects that form *silently* and keeps\n// its previous value (black), so wrap it back into rgb() before use.\nconst CHANNEL_TRIPLET = new RegExp(String.raw`^(?:${NUMBER})\\s+(?:${NUMBER})\\s+(?:${NUMBER})$`);\n\nexport const toCanvasColor = (token: string) => {\n const trimmed = token.trim();\n if (!trimmed) return 'white';\n\n // Only wrap a well-formed triplet. A malformed one (`1.2.3 4 5`) would produce\n // an invalid `rgb()` that `fillStyle` rejects silently, painting the PNG on\n // whatever colour was set before — passing it through unwrapped at least lets\n // a genuine CSS color value work.\n return CHANNEL_TRIPLET.test(trimmed) ? `rgb(${trimmed})` : trimmed;\n};\n\nconst resolveBackground = () =>\n toCanvasColor(getComputedStyle(document.documentElement).getPropertyValue('--color-background'));\n\n/**\n * Rasterize SVG markup to a PNG blob at 2x for legible text.\n *\n * The SVG is loaded as a data URL rather than a blob URL: an <img> loading SVG\n * is treated as a foreign resource, so a blob URL would taint the canvas and\n * make toBlob throw a SecurityError. A background is painted first because a\n * transparent PNG reads as unusable when pasted into a slide deck.\n */\nexport const svgToPngBlob = async (svg: string, background?: string): Promise<Blob> => {\n const size = parseSvgSize(svg);\n if (!size) throw new Error('Cannot determine diagram dimensions.');\n\n const encoded = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(toSvgFileContent(svg))}`;\n\n const image = new Image();\n image.decoding = 'sync';\n await new Promise<void>((resolve, reject) => {\n image.onload = () => resolve();\n image.onerror = () => reject(new Error('Failed to load the diagram for export.'));\n image.src = encoded;\n });\n\n const canvas = document.createElement('canvas');\n canvas.width = Math.round(size.width * PNG_SCALE);\n canvas.height = Math.round(size.height * PNG_SCALE);\n\n const context = canvas.getContext('2d');\n if (!context) throw new Error('Canvas is unavailable in this browser.');\n\n context.fillStyle = background ?? resolveBackground();\n context.fillRect(0, 0, canvas.width, canvas.height);\n context.drawImage(image, 0, 0, canvas.width, canvas.height);\n\n return new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) => (blob ? resolve(blob) : reject(new Error('Failed to encode the PNG.'))),\n 'image/png',\n );\n });\n};\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport DOMPurify from 'dompurify';\n\nimport { ClipboardButton } from '../../blocks/ClipboardButton';\nimport { Maximize2, Minimize2 } from '../../icons';\nimport { cn } from '../../utils';\nimport { Button } from '../Button';\nimport { Tooltip } from '../Tooltip';\n\nconst MIN_SCALE = 0.5;\nconst MAX_SCALE = 3;\nconst ZOOM_STEP = 0.1;\n\ntype Props = {\n /** Raw SVG markup; DOMPurify-sanitized (SVG profile), so user/LLM content is safe. */\n html: string;\n copyText?: string;\n className?: string;\n /** Keep `<foreignObject>` through sanitization; its HTML children are still stripped.\n * Enable only when each one is pre-flattened to bare text. */\n allowForeignObject?: boolean;\n /** Keep `<use>` through sanitization. Enable only when every `href` is already a `#` reference. */\n allowUse?: boolean;\n /** Extra toolbar controls (e.g. `ExportMenu`) between copy and fullscreen; receives the\n * sanitized SVG. Injected so this lazy chunk carries no export dependencies. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const ZoomableSvg = ({\n html,\n copyText,\n className,\n allowForeignObject,\n allowUse,\n renderActions,\n}: Props) => {\n const safeHtml = useMemo(\n () =>\n DOMPurify.sanitize(html, {\n USE_PROFILES: { svg: true, svgFilters: true },\n ADD_TAGS: [...(allowForeignObject ? ['foreignObject'] : []), ...(allowUse ? ['use'] : [])],\n }),\n [html, allowForeignObject, allowUse],\n );\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [scale, setScale] = useState(1);\n const [translate, setTranslate] = useState({ x: 0, y: 0 });\n const [isFullscreen, setIsFullscreen] = useState(false);\n\n useEffect(() => {\n if (!isFullscreen) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopPropagation();\n setIsFullscreen(false);\n }\n };\n document.addEventListener('keydown', onKeyDown, { capture: true });\n return () => document.removeEventListener('keydown', onKeyDown, { capture: true });\n }, [isFullscreen]);\n\n const dragRef = useRef<{\n startX: number;\n startY: number;\n startTx: number;\n startTy: number;\n } | null>(null);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const onWheel = (e: WheelEvent) => {\n if (!e.ctrlKey && !e.metaKey) return;\n e.preventDefault();\n const delta = e.deltaY > 0 ? -ZOOM_STEP : ZOOM_STEP;\n setScale((s) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, s + delta)));\n };\n\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n // Re-bind when toggling fullscreen: the portal/inline switch remounts the\n // container element, so the listener must attach to the new node.\n }, [isFullscreen]);\n\n const didDragRef = useRef(false);\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n if (e.button !== 0) return;\n e.preventDefault();\n didDragRef.current = false;\n dragRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n startTx: translate.x,\n startTy: translate.y,\n };\n },\n [translate],\n );\n\n const handleMouseMove = useCallback((e: React.MouseEvent) => {\n if (!dragRef.current) return;\n didDragRef.current = true;\n setTranslate({\n x: dragRef.current.startTx + e.clientX - dragRef.current.startX,\n y: dragRef.current.startTy + e.clientY - dragRef.current.startY,\n });\n }, []);\n\n const handleMouseUp = useCallback(() => {\n dragRef.current = null;\n }, []);\n\n const handleReset = useCallback(() => {\n setScale(1);\n setTranslate({ x: 0, y: 0 });\n }, []);\n\n const isZoomed = Math.abs(scale - 1) > 0.001 || translate.x !== 0 || translate.y !== 0;\n\n const handleClick = useCallback((e: React.MouseEvent) => {\n if (didDragRef.current) {\n e.stopPropagation();\n }\n }, []);\n\n const stopPropagation = useCallback((e: React.SyntheticEvent) => {\n e.stopPropagation();\n }, []);\n\n const diagramContent = (\n <div\n role=\"presentation\"\n className={cn('tw:group/zoom tw:relative', isFullscreen && 'tw:h-full')}\n onClick={handleClick}\n >\n <div\n role=\"presentation\"\n ref={containerRef}\n className={cn(\n 'tw:overflow-hidden tw:cursor-grab tw:active:cursor-grabbing tw:p-3',\n isFullscreen && 'tw:h-full',\n )}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n >\n <div\n className={cn(\n 'tw:flex tw:justify-center tw:origin-center',\n isFullscreen && 'tw:h-full tw:[&>svg]:h-full',\n )}\n style={{ transform: `translate(${translate.x}px, ${translate.y}px) scale(${scale})` }}\n dangerouslySetInnerHTML={{ __html: safeHtml }}\n />\n </div>\n <div\n role=\"presentation\"\n // `focus-within` too: the opacity-0 toolbar is still in the tab order.\n className=\"tw:absolute tw:right-1 tw:top-1 tw:flex tw:gap-1 tw:opacity-0 tw:transition-opacity tw:focus-within:opacity-100 tw:group-hover/zoom:opacity-100\"\n onClick={stopPropagation}\n >\n {isZoomed && (\n <Tooltip tooltip=\"Reset zoom\" placement=\"top\" triggerAsChild>\n <Button size=\"small\" variant=\"outline\" onClick={handleReset}>\n Reset\n </Button>\n </Tooltip>\n )}\n {copyText && (\n <ClipboardButton text={copyText} tooltip=\"Copy code\" size=\"icon-sm\" variant=\"outline\" />\n )}\n {renderActions?.(safeHtml)}\n <Tooltip\n tooltip={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}\n placement=\"top\"\n triggerAsChild\n >\n <Button\n size=\"icon-sm\"\n variant=\"outline\"\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}\n onClick={() => setIsFullscreen((f) => !f)}\n >\n {isFullscreen ? (\n <Minimize2 className=\"tw:size-4\" />\n ) : (\n <Maximize2 className=\"tw:size-4\" />\n )}\n </Button>\n </Tooltip>\n </div>\n </div>\n );\n\n if (isFullscreen) {\n return createPortal(\n <div\n role=\"presentation\"\n data-testid=\"zoomable-svg-fullscreen\"\n // `z-overlay`: a toolbar menu opened later paints over it.\n // Tooltip (z-1060) and SpotlightBackdrop (z-1056) sit above it by design.\n className=\"tw:fixed tw:inset-0 tw:z-overlay tw:bg-muted tw:p-4 tailwindcss-preflight\"\n onClick={stopPropagation}\n >\n {diagramContent}\n </div>,\n document.body,\n );\n }\n\n return (\n <div\n className={cn(\n 'tw:overflow-hidden tw:rounded-md tw:border tw:border-border tw:bg-muted',\n className,\n )}\n >\n {diagramContent}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ZoomableSvg-Cp2zJNIG.js","names":[],"sources":["../src/components/ZoomableSvg/exportSvg.ts","../src/components/ZoomableSvg/ZoomableSvg.tsx"],"sourcesContent":["const PNG_SCALE = 2;\n\n// The root `<svg …>` tag, so the namespace check looks only at its attributes.\n// A plain `includes('xmlns=')` would also match a nested element or body text,\n// and would miss `xmlns = \"…\"` — adding a second declaration, which parsers\n// reject outright with \"Attribute xmlns redefined\".\nconst ROOT_SVG_TAG = /^<svg\\b[^>]*>/i;\nconst HAS_DEFAULT_NAMESPACE = /\\bxmlns\\s*=/i;\n\n// A standalone SVG file needs the namespace declaration; inline diagram output may\n// omit it because an inline <svg> inherits it from the host document.\n// Trim before matching: the anchored `^<svg` misses markup that arrives with a\n// leading newline or indentation, which would emit a standalone file with no\n// namespace — unopenable in an image viewer.\nconst withSvgNamespace = (svg: string) => {\n const trimmed = svg.trim();\n const rootTag = trimmed.match(ROOT_SVG_TAG)?.[0];\n\n if (!rootTag || HAS_DEFAULT_NAMESPACE.test(rootTag)) return trimmed;\n\n return trimmed.replace(/^<svg\\b/i, '<svg xmlns=\"http://www.w3.org/2000/svg\"');\n};\n\nexport const toSvgFileContent = (svg: string) =>\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${withSvgNamespace(svg).trim()}\\n`;\n\n// A single decimal number: `12`, `12.5`, `.5`. Deliberately stricter than\n// `[\\d.]+`, which also matches `1.2.3` — and `parseFloat` truncates that to\n// `1.2` rather than rejecting it, so a malformed value would pass as a plausible\n// one.\nconst NUMBER = String.raw`\\d+(?:\\.\\d+)?|\\.\\d+`;\n\n// `parseFloat` happily reads a relative length as a number — `\"100%\"` becomes\n// `100`, `\"2em\"` becomes `2` — and `makeResponsive` sets `width=\"100%\"` on every\n// diagram root, so an SVG with no usable viewBox would rasterize 100px wide with\n// no error. Accept only a unitless number or an explicit `px`.\nconst PIXEL_LENGTH = new RegExp(String.raw`^\\s*(?:${NUMBER})(?:px)?\\s*$`, 'i');\n\nconst parsePixelLength = (value: string | null): number | null => {\n if (!PIXEL_LENGTH.test(value ?? '')) return null;\n\n const parsed = Number.parseFloat(value ?? '');\n\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n};\n\nexport const parseSvgSize = (svg: string) => {\n const doc = new DOMParser().parseFromString(withSvgNamespace(svg), 'image/svg+xml');\n const element = doc.documentElement;\n\n const viewBox = element\n .getAttribute('viewBox')\n ?.split(/[\\s,]+/)\n .map(Number);\n // `> 0`, not truthiness: a negative extent is accepted by a truthy check, and\n // negative canvas dimensions silently fall back to the 300x150 default — an\n // export at the wrong size with no error. Falling through to width/height is\n // the better answer, since those are usually still valid.\n if (viewBox?.length === 4 && viewBox.every((value) => Number.isFinite(value))) {\n const [, , width, height] = viewBox;\n if (width !== undefined && height !== undefined && width > 0 && height > 0) {\n return { width, height };\n }\n }\n\n const width = parsePixelLength(element.getAttribute('width'));\n const height = parsePixelLength(element.getAttribute('height'));\n\n return width !== null && height !== null ? { width, height } : null;\n};\n\n// Resolve the themed page background so the exported PNG follows the active\n// brand and color scheme instead of a frozen literal. Falls back to `white`\n// (a CSS keyword, not a brand-locked hex) when the token is unset.\n//\n// The token holds Tailwind's bare channel triplet (\"249 250 251\") so utilities\n// can add an alpha. Canvas `fillStyle` rejects that form *silently* and keeps\n// its previous value (black), so wrap it back into rgb() before use.\nconst CHANNEL_TRIPLET = new RegExp(String.raw`^(?:${NUMBER})\\s+(?:${NUMBER})\\s+(?:${NUMBER})$`);\n\nexport const toCanvasColor = (token: string) => {\n const trimmed = token.trim();\n if (!trimmed) return 'white';\n\n // Only wrap a well-formed triplet. A malformed one (`1.2.3 4 5`) would produce\n // an invalid `rgb()` that `fillStyle` rejects silently, painting the PNG on\n // whatever colour was set before — passing it through unwrapped at least lets\n // a genuine CSS color value work.\n return CHANNEL_TRIPLET.test(trimmed) ? `rgb(${trimmed})` : trimmed;\n};\n\nconst resolveBackground = () =>\n toCanvasColor(getComputedStyle(document.documentElement).getPropertyValue('--color-background'));\n\n/**\n * Rasterize SVG markup to a PNG blob at 2x for legible text.\n *\n * The SVG is loaded as a data URL rather than a blob URL: an <img> loading SVG\n * is treated as a foreign resource, so a blob URL would taint the canvas and\n * make toBlob throw a SecurityError. A background is painted first because a\n * transparent PNG reads as unusable when pasted into a slide deck.\n */\nexport const svgToPngBlob = async (svg: string, background?: string): Promise<Blob> => {\n const size = parseSvgSize(svg);\n if (!size) throw new Error('Cannot determine diagram dimensions.');\n\n const encoded = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(toSvgFileContent(svg))}`;\n\n const image = new Image();\n image.decoding = 'sync';\n await new Promise<void>((resolve, reject) => {\n image.onload = () => resolve();\n image.onerror = () => reject(new Error('Failed to load the diagram for export.'));\n image.src = encoded;\n });\n\n const canvas = document.createElement('canvas');\n canvas.width = Math.round(size.width * PNG_SCALE);\n canvas.height = Math.round(size.height * PNG_SCALE);\n\n const context = canvas.getContext('2d');\n if (!context) throw new Error('Canvas is unavailable in this browser.');\n\n context.fillStyle = background ?? resolveBackground();\n context.fillRect(0, 0, canvas.width, canvas.height);\n context.drawImage(image, 0, 0, canvas.width, canvas.height);\n\n return new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) => (blob ? resolve(blob) : reject(new Error('Failed to encode the PNG.'))),\n 'image/png',\n );\n });\n};\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport DOMPurify from 'dompurify';\n\nimport { ClipboardButton } from '../../blocks/ClipboardButton';\nimport { Maximize2, Minimize2 } from '../../icons';\nimport { cn } from '../../utils';\nimport { Button } from '../Button';\nimport { Tooltip } from '../Tooltip';\n\nconst MIN_SCALE = 0.5;\nconst MAX_SCALE = 3;\nconst ZOOM_STEP = 0.1;\n\ntype Props = {\n /** Raw SVG markup; DOMPurify-sanitized (SVG profile), so user/LLM content is safe. */\n html: string;\n copyText?: string;\n className?: string;\n /** Keep `<foreignObject>` through sanitization; its HTML children are still stripped.\n * Enable only when each one is pre-flattened to bare text. */\n allowForeignObject?: boolean;\n /** Keep `<use>` through sanitization. Enable only when every `href` is already a `#` reference. */\n allowUse?: boolean;\n /** Extra toolbar controls (e.g. `ExportMenu`) between copy and fullscreen; receives the\n * sanitized SVG. Injected so this lazy chunk carries no export dependencies. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const ZoomableSvg = ({\n html,\n copyText,\n className,\n allowForeignObject,\n allowUse,\n renderActions,\n}: Props) => {\n const safeHtml = useMemo(\n () =>\n DOMPurify.sanitize(html, {\n USE_PROFILES: { svg: true, svgFilters: true },\n ADD_TAGS: [...(allowForeignObject ? ['foreignObject'] : []), ...(allowUse ? ['use'] : [])],\n }),\n [html, allowForeignObject, allowUse],\n );\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [scale, setScale] = useState(1);\n const [translate, setTranslate] = useState({ x: 0, y: 0 });\n const [isFullscreen, setIsFullscreen] = useState(false);\n\n useEffect(() => {\n if (!isFullscreen) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopPropagation();\n setIsFullscreen(false);\n }\n };\n document.addEventListener('keydown', onKeyDown, { capture: true });\n return () => document.removeEventListener('keydown', onKeyDown, { capture: true });\n }, [isFullscreen]);\n\n const dragRef = useRef<{\n startX: number;\n startY: number;\n startTx: number;\n startTy: number;\n } | null>(null);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const onWheel = (e: WheelEvent) => {\n if (!e.ctrlKey && !e.metaKey) return;\n e.preventDefault();\n const delta = e.deltaY > 0 ? -ZOOM_STEP : ZOOM_STEP;\n setScale((s) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, s + delta)));\n };\n\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n // Re-bind when toggling fullscreen: the portal/inline switch remounts the\n // container element, so the listener must attach to the new node.\n }, [isFullscreen]);\n\n const didDragRef = useRef(false);\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n if (e.button !== 0) return;\n e.preventDefault();\n didDragRef.current = false;\n dragRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n startTx: translate.x,\n startTy: translate.y,\n };\n },\n [translate],\n );\n\n const handleMouseMove = useCallback((e: React.MouseEvent) => {\n if (!dragRef.current) return;\n didDragRef.current = true;\n setTranslate({\n x: dragRef.current.startTx + e.clientX - dragRef.current.startX,\n y: dragRef.current.startTy + e.clientY - dragRef.current.startY,\n });\n }, []);\n\n const handleMouseUp = useCallback(() => {\n dragRef.current = null;\n }, []);\n\n const handleReset = useCallback(() => {\n setScale(1);\n setTranslate({ x: 0, y: 0 });\n }, []);\n\n const isZoomed = Math.abs(scale - 1) > 0.001 || translate.x !== 0 || translate.y !== 0;\n\n const handleClick = useCallback((e: React.MouseEvent) => {\n if (didDragRef.current) {\n e.stopPropagation();\n }\n }, []);\n\n const stopPropagation = useCallback((e: React.SyntheticEvent) => {\n e.stopPropagation();\n }, []);\n\n const diagramContent = (\n <div\n role=\"presentation\"\n className={cn('tw:group/zoom tw:relative', isFullscreen && 'tw:h-full')}\n onClick={handleClick}\n >\n <div\n role=\"presentation\"\n ref={containerRef}\n className={cn(\n 'tw:overflow-hidden tw:cursor-grab tw:active:cursor-grabbing tw:p-3',\n isFullscreen && 'tw:h-full',\n )}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n >\n <div\n className={cn(\n 'tw:flex tw:justify-center tw:origin-center',\n isFullscreen && 'tw:h-full tw:[&>svg]:h-full',\n )}\n style={{ transform: `translate(${translate.x}px, ${translate.y}px) scale(${scale})` }}\n dangerouslySetInnerHTML={{ __html: safeHtml }}\n />\n </div>\n <div\n role=\"presentation\"\n // `focus-within` too: the opacity-0 toolbar is still in the tab order.\n className=\"tw:absolute tw:right-1 tw:top-1 tw:flex tw:gap-1 tw:opacity-0 tw:transition-opacity tw:focus-within:opacity-100 tw:group-hover/zoom:opacity-100\"\n onClick={stopPropagation}\n >\n {isZoomed && (\n <Tooltip tooltip=\"Reset zoom\" placement=\"top\" triggerAsChild>\n <Button size=\"small\" variant=\"outline\" onClick={handleReset}>\n Reset\n </Button>\n </Tooltip>\n )}\n {copyText && (\n <ClipboardButton text={copyText} tooltip=\"Copy code\" size=\"icon-sm\" variant=\"outline\" />\n )}\n {renderActions?.(safeHtml)}\n <Tooltip\n tooltip={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}\n placement=\"top\"\n triggerAsChild\n >\n <Button\n size=\"icon-sm\"\n variant=\"outline\"\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}\n onClick={() => setIsFullscreen((f) => !f)}\n >\n {isFullscreen ? (\n <Minimize2 className=\"tw:size-4\" />\n ) : (\n <Maximize2 className=\"tw:size-4\" />\n )}\n </Button>\n </Tooltip>\n </div>\n </div>\n );\n\n if (isFullscreen) {\n return createPortal(\n <div\n role=\"presentation\"\n data-testid=\"zoomable-svg-fullscreen\"\n // `z-overlay`: a toolbar menu opened later paints over it.\n // Tooltip (z-1060) and SpotlightBackdrop (z-1056) sit above it by design.\n className=\"tw:fixed tw:inset-0 tw:z-overlay tw:bg-muted tw:p-4 tailwindcss-preflight\"\n onClick={stopPropagation}\n >\n {diagramContent}\n </div>,\n document.body,\n );\n }\n\n return (\n <div\n className={cn(\n 'tw:overflow-hidden tw:rounded-md tw:border tw:border-border tw:bg-muted',\n className,\n )}\n >\n {diagramContent}\n </div>\n );\n};\n"],"mappings":"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"}
|
package/dist/editors.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { cn } from "./classNames-DkX30VJU.js";
|
|
2
2
|
import { ChevronsLeft, ChevronsRight, X } from "./icons-DDR9dFSr.js";
|
|
3
|
-
import { Button } from "./Button-
|
|
3
|
+
import { Button } from "./Button-BgjVUBON.js";
|
|
4
4
|
import { useStableCallback } from "./internal/hooks/useStableCallback.js";
|
|
5
|
-
import { FieldItem, useFieldContext } from "./FieldItem-
|
|
5
|
+
import { FieldItem, useFieldContext } from "./FieldItem-D5EpKxjN.js";
|
|
6
6
|
import { CHANGE_CLASS } from "./internal/components/MergeEditor/constants.js";
|
|
7
7
|
import { resolveToken } from "./themeTokens-B60cQtAY.js";
|
|
8
|
-
import { CodeEditor, LANGUAGE_MODES, buildBaseThemeStyles, buildBasicSetup, buildCanvasChrome, buildChromeStyles, buildCustomTheme, buildDiffWash, isLanguageMode, searchExtension, updateSearch, useThemedExtension, useTooltipsExtension, useVariableCompletion } from "./CodeEditor-
|
|
8
|
+
import { CodeEditor, LANGUAGE_MODES, buildBaseThemeStyles, buildBasicSetup, buildCanvasChrome, buildChromeStyles, buildCustomTheme, buildDiffWash, isLanguageMode, searchExtension, updateSearch, useThemedExtension, useTooltipsExtension, useVariableCompletion } from "./CodeEditor-Dx9gyVHV.js";
|
|
9
9
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
10
10
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { Decoration, EditorView, GutterMarker, ViewPlugin, gutter, gutterLineClass } from "@codemirror/view";
|
package/dist/flint.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DiagramSkeleton } from "./internal/blocks/DiagramShared/DiagramSkeleton.js";
|
|
2
2
|
import { DiagramSource, makeResponsive } from "./svgTransforms-BBxxO9T6.js";
|
|
3
|
-
import { ZoomableSvg } from "./ZoomableSvg-
|
|
3
|
+
import { ZoomableSvg } from "./ZoomableSvg-Cp2zJNIG.js";
|
|
4
4
|
import { useThemeAttributes } from "./themeTokens-B60cQtAY.js";
|
|
5
5
|
import { SERIES_TOKENS, createVegaColorResolver, tierTwoColour } from "./vegaTokens-DnCcy7JY.js";
|
|
6
6
|
import { vegaTheme } from "./vegaTheme-BcTNRmVS.js";
|
|
@@ -142,8 +142,8 @@ const findUnsafeSpecKey = (spec) => {
|
|
|
142
142
|
const renderFlintSvg = async (runtime, input, config) => {
|
|
143
143
|
const unsafe = findUnsafeSpecKey(input);
|
|
144
144
|
if (unsafe !== null) throw new Error(`Refusing to render a flint spec that ${unsafe}`);
|
|
145
|
-
const
|
|
146
|
-
const
|
|
145
|
+
const vlSpec = { ...runtime.assembleVegaLite(input) };
|
|
146
|
+
for (const key of Object.keys(vlSpec)) if (key.startsWith("_")) delete vlSpec[key];
|
|
147
147
|
const { spec } = runtime.vegaLite.compile(vlSpec, { config });
|
|
148
148
|
const view = new runtime.vega.View(runtime.vega.parse(spec), { renderer: "none" });
|
|
149
149
|
try {
|
package/dist/flint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flint.js","names":[],"sources":["../src/blocks/FlintChart/flintTheme.ts","../src/blocks/FlintChart/refuseUnsafeSpec.ts","../src/blocks/FlintChart/renderFlintSvg.ts","../src/blocks/FlintChart/FlintChart.tsx"],"sourcesContent":["import type { ChartAssemblyInput } from 'flint-chart';\n\nimport type { ResolvedColorScheme } from '../../components/ThemeProvider/types';\nimport { createVegaColorResolver, SERIES_TOKENS, tierTwoColour } from '../../vega/vegaTokens';\n\n/**\n * Put the design system's `--chart-1..5` palette on a Flint input.\n *\n * - Flint resolves series colours during assembly, so they ride `theme_spec`, not `vegaTheme`.\n * - An author-supplied `theme_spec` is passed through untouched.\n * - A `null` `theme_spec` is replaced like a missing one — flint throws on it.\n */\nexport const withFlintSeriesPalette = (\n input: ChartAssemblyInput,\n scheme: ResolvedColorScheme,\n from?: Element | null,\n): ChartAssemblyInput => {\n if (input.theme_spec !== undefined && input.theme_spec !== null) return input;\n\n const token = createVegaColorResolver(scheme, from);\n const categorical = SERIES_TOKENS.map((name) => token(name));\n\n return {\n ...input,\n theme_spec: {\n ink: {\n // `endpointsAgainstSurface` below needs to know what the chart sits on.\n surface: { canvas: token('card') },\n // Flint's defaults assume a light canvas, so text and structure come from tokens.\n text: {\n primary: token('foreground'),\n secondary: token('muted-foreground'),\n muted: token('muted-foreground'),\n // Ink on a filled (series-coloured) mark.\n inverse: token('card'),\n },\n structure: {\n axis: token('border'),\n grid: token('border'),\n frame: token('border'),\n rule: token('border'),\n },\n series: {\n categorical,\n // Past five series flint swaps `categorical` for its theme-blind `tableau10`;\n // this keeps 6-10 on our hues (`tierTwoColour`).\n categoricalExtended: [\n ...categorical,\n ...categorical.map((colour) => tierTwoColour(colour, scheme)),\n ],\n // The tail past even the extended set.\n overflow: token('muted-foreground'),\n // For a quantitative `color` field; flint's default starts at black, invisible on dark.\n sequential: {\n stops: [token('accent'), token('chart-1')],\n space: 'lab',\n endpointsAgainstSurface: true,\n },\n },\n },\n },\n };\n};\n","/**\n * The boundary in front of the flint renderer: nothing in a spec may fetch or run.\n *\n * - Checked on the input, before `assembleVegaLite`: compiled Vega carries its own `signals`/`expr`.\n * - Refused on a key's presence, not its value: a relative `data.url` is still a fetch.\n */\n\n/** Keys whose presence anywhere in a spec is refused: each fetches, evaluates or binds a signal. */\nconst REFUSED_KEYS = new Set(['url', 'expr', 'signal', 'signals', 'params']);\n\n/** Suffix of Vega-Lite's other expression properties; matched by shape so new ones are refused. */\nconst EXPRESSION_KEY_SUFFIX = /Expr$/;\n\n/**\n * An own property's value, or `undefined`.\n *\n * - Via a descriptor: inherited keys are ignored and a getter is never called.\n */\nconst ownValue = (node: unknown, key: string): unknown => {\n if (typeof node !== 'object' || node === null) return undefined;\n\n return Object.getOwnPropertyDescriptor(node, key)?.value;\n};\n\n/**\n * The chart's rows (`spec.data.values` only), which the walk does not descend into.\n *\n * - Rows are data vega never evaluates, so a column named `url` must not be refused.\n * - Flint takes one top-level dataset, so every other node is spec.\n * - Skipped by identity, not key name: a `values` key elsewhere must not hide a refused key.\n */\nconst inlineRows = (spec: unknown): unknown[] | null => {\n const values = ownValue(ownValue(spec, 'data'), 'values');\n\n return Array.isArray(values) ? values : null;\n};\n\n/**\n * The top-level maps flint keys by column name, so their keys are data, not spec.\n *\n * - Only the keys are exempt; each value is walked like any other node.\n * - Matched by identity, as in `inlineRows`: a nested object with the same name gets no exemption.\n */\nconst COLUMN_KEYED_MAPS = ['semantic_types', 'field_display_names'];\n\nconst columnKeyedMaps = (spec: unknown): unknown[] =>\n COLUMN_KEYED_MAPS.map((key) => ownValue(spec, key)).filter(\n (map) => typeof map === 'object' && map !== null,\n );\n\n/** Ceiling on the nodes the walk visits; exceeding it refuses, since the spec went unchecked. */\nconst MAX_SPEC_NODES = 10_000;\n\nconst TOO_LARGE = 'is too large to check';\n\n/**\n * Why this spec may not be rendered, or `null` if it may.\n *\n * - The reason goes to the console; the reader sees `DiagramSource`'s fallback.\n * - Iterative, so deep nesting cannot overflow the stack; nodes are charged on push.\n * - An accessor is refused, not skipped: its value cannot be read without running it.\n */\nexport const findUnsafeSpecKey = (spec: unknown): string | null => {\n const rows = inlineRows(spec);\n const columnKeyed = columnKeyedMaps(spec);\n const stack: unknown[] = [spec];\n let visited = 1;\n\n while (stack.length > 0) {\n const node = stack.pop();\n\n if (Array.isArray(node)) {\n visited += node.length;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(...node);\n continue;\n }\n\n if (typeof node !== 'object' || node === null) continue;\n\n const keysAreColumnNames = columnKeyed.includes(node);\n\n // Descriptors: skip inherited keys and never call a getter.\n for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(node))) {\n if (!keysAreColumnNames && (REFUSED_KEYS.has(key) || EXPRESSION_KEY_SUFFIX.test(key))) {\n return `carries \\`${key}\\``;\n }\n if (!('value' in descriptor)) return `carries an accessor at \\`${key}\\``;\n\n const { value } = descriptor;\n if (rows !== null && value === rows) continue;\n\n visited += 1;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(value);\n }\n }\n\n return null;\n};\n","import type { ChartAssemblyInput } from 'flint-chart';\nimport type { assembleVegaLite } from 'flint-chart/vegalite';\nimport type * as vega from 'vega';\nimport type * as vegaLite from 'vega-lite';\nimport type { Config, TopLevelSpec } from 'vega-lite';\n\nimport { findUnsafeSpecKey } from './refuseUnsafeSpec';\n\n// The lazily imported modules the pipeline needs; a parameter so tests can pass a fake runtime.\nexport type FlintRuntime = {\n assembleVegaLite: typeof assembleVegaLite;\n vega: Pick<typeof vega, 'View' | 'parse'>;\n vegaLite: Pick<typeof vegaLite, 'compile'>;\n};\n\n/**\n * Compile a Flint `ChartAssemblyInput` to a static SVG string.\n *\n * - The one place unsafe input is refused (`refuseUnsafeSpec.ts`), before anything compiles it.\n * - Flint's `_`-prefixed hint keys are not Vega-Lite schema and are stripped before compiling.\n * - `config` rides the compile options; flint's spec-side config sets no colours to conflict.\n * - Renders headless; `toSVG()` is the only output.\n */\nexport const renderFlintSvg = async (\n runtime: FlintRuntime,\n input: ChartAssemblyInput,\n config: Config,\n): Promise<string> => {\n const unsafe = findUnsafeSpecKey(input);\n if (unsafe !== null) {\n throw new Error(`Refusing to render a flint spec that ${unsafe}`);\n }\n\n const assembled: Record<string, unknown> = runtime.assembleVegaLite(input);\n // `assembleVegaLite` is typed loosely upstream; without the hint keys it is a Vega-Lite spec.\n const vlSpec = Object.fromEntries(\n Object.entries(assembled).filter(([key]) => !key.startsWith('_')),\n ) as unknown as TopLevelSpec;\n const { spec } = runtime.vegaLite.compile(vlSpec, { config });\n const view = new runtime.vega.View(runtime.vega.parse(spec), { renderer: 'none' });\n try {\n await view.runAsync();\n return await view.toSVG();\n } finally {\n view.finalize();\n }\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ChartAssemblyInput } from 'flint-chart';\n\nimport { useThemeAttributes } from '../../components/ThemeProvider/useThemeAttributes';\nimport { ZoomableSvg } from '../../components/ZoomableSvg';\nimport { vegaTheme } from '../../vega/vegaTheme';\nimport { DiagramSkeleton } from '../DiagramShared/DiagramSkeleton';\nimport { DiagramSource } from '../DiagramShared/DiagramSource';\nimport { createSvgCache, diagramCacheKey } from '../DiagramShared/svgCache';\nimport { makeResponsive } from '../DiagramShared/svgTransforms';\n\nimport { withFlintSeriesPalette } from './flintTheme';\nimport type { FlintRuntime } from './renderFlintSvg';\nimport { renderFlintSvg } from './renderFlintSvg';\n\nlet runtimePromise: Promise<FlintRuntime> | null = null;\n\n// Memoized chart-stack import, reset on failure so a chunk-load error can retry.\n// Mirrors `getMermaid()` in `MermaidDiagram`.\nconst getFlintRuntime = (): Promise<FlintRuntime> => {\n if (!runtimePromise) {\n runtimePromise = Promise.all([\n import('flint-chart/vegalite'),\n import('vega'),\n import('vega-lite'),\n ])\n .then(([flint, vega, vegaLite]) => ({\n assembleVegaLite: flint.assembleVegaLite,\n vega,\n vegaLite,\n }))\n .catch((err) => {\n runtimePromise = null;\n throw err;\n });\n }\n return runtimePromise;\n};\n\nconst RENDER_DEBOUNCE_MS = 300;\n\n// A streaming fence is unparseable just like a broken one, so unparseable source\n// keeps the skeleton until it has been unchanged this long, then shows as raw source.\nconst PARSE_GRACE_MS = 1500;\n\n// Parse, compile and layout are synchronous, so a larger source would freeze the chat.\n// Far above the prompt's ~50-row inline data cap.\nconst MAX_SOURCE_CHARS = 200_000;\n\n// Values are already `makeResponsive`d, so ZoomableSvg's sanitize memo stays stable.\nconst svgCache = createSvgCache();\n\n// The theme half of a cache key — tells a still-streaming spec from a theme switch.\nconst prevThemeKeyOf = (key: string): string => key.slice(0, key.indexOf('\\n'));\n\ntype Props = {\n /** Raw source of a ```flint fence: a flint-chart `ChartAssemblyInput` as JSON. */\n code: string;\n /** Extra toolbar controls (e.g. `ExportMenu`) for `ZoomableSvg`; receives the sanitized SVG. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const FlintChart = ({ code, renderActions }: Props) => {\n // - Re-renders on a scheme or brand change, same contract as MermaidDiagram.\n // - `scope` is the element tokens are read off (`null` = `<html>`); it is part of `themeKey`.\n const { scheme, cacheKey: themeKey, scope } = useThemeAttributes();\n const cacheKey = diagramCacheKey(code, themeKey);\n\n const [svg, setSvg] = useState<string | null>(() => svgCache.get(cacheKey) ?? null);\n const [renderFailed, setRenderFailed] = useState(false);\n\n // Reset stale state synchronously on a code or theme change (no one-frame flash):\n // 1. A cache hit is applied at once.\n // 2. Same theme, source only grew (still streaming): keep the last chart, no flicker.\n // 3. Anything else clears — colours are baked in, so a stale chart is wrong.\n const [prevKey, setPrevKey] = useState(cacheKey);\n const [prevCode, setPrevCode] = useState(code);\n if (cacheKey !== prevKey) {\n const cached = svgCache.get(cacheKey);\n const sameThemeStillStreaming =\n themeKey === prevThemeKeyOf(prevKey) && code.startsWith(prevCode);\n setPrevKey(cacheKey);\n setPrevCode(code);\n if (cached) setSvg(cached);\n else if (!sameThemeStillStreaming) setSvg(null);\n setRenderFailed(false);\n }\n\n useEffect(() => {\n if (svgCache.has(cacheKey)) return;\n\n let cancelled = false;\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const debounceTimer = setTimeout(() => {\n if (code.length > MAX_SOURCE_CHARS) {\n // Never becomes renderable, so skip the grace period.\n console.error(\n `[FlintChart] source exceeds ${MAX_SOURCE_CHARS} characters (${code.length}); not rendering`,\n );\n setSvg(null);\n setRenderFailed(true);\n return;\n }\n\n let input: ChartAssemblyInput;\n try {\n input = JSON.parse(code);\n } catch (err) {\n // Likely still streaming — fail only if unchanged when the grace period ends.\n graceTimer = setTimeout(() => {\n console.debug('[FlintChart] source never became parseable JSON:', err);\n setSvg(null);\n setRenderFailed(true);\n }, PARSE_GRACE_MS - RENDER_DEBOUNCE_MS);\n return;\n }\n\n getFlintRuntime()\n .then(async (runtime) => {\n if (cancelled) return;\n // `scheme` comes from the same read as the cache key, so palette and key agree.\n const svgOutput = makeResponsive(\n await renderFlintSvg(\n runtime,\n withFlintSeriesPalette(input, scheme, scope),\n vegaTheme(scheme, scope),\n ),\n );\n if (cancelled) return;\n svgCache.set(cacheKey, svgOutput);\n setSvg(svgOutput);\n setRenderFailed(false);\n })\n .catch((err) => {\n if (cancelled) return;\n console.error('[FlintChart] render failed:', err);\n setSvg(null);\n setRenderFailed(true);\n });\n }, RENDER_DEBOUNCE_MS);\n\n return () => {\n cancelled = true;\n clearTimeout(debounceTimer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n }, [cacheKey, code, scheme, scope]);\n\n if (renderFailed) {\n return <DiagramSource code={code} />;\n }\n\n if (svg) {\n return (\n <ZoomableSvg\n // Keyed by SVG, not `code`: a streaming chart is held, and a remount per chunk\n // would drop zoom/pan.\n key={svg}\n html={svg}\n copyText={`\\`\\`\\`flint\\n${code}\\n\\`\\`\\``}\n className=\"tw:my-3\"\n renderActions={renderActions}\n />\n );\n }\n\n return <DiagramSkeleton />;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flint.js","names":[],"sources":["../src/blocks/FlintChart/flintTheme.ts","../src/blocks/FlintChart/refuseUnsafeSpec.ts","../src/blocks/FlintChart/renderFlintSvg.ts","../src/blocks/FlintChart/FlintChart.tsx"],"sourcesContent":["import type { ChartAssemblyInput } from 'flint-chart';\n\nimport type { ResolvedColorScheme } from '../../components/ThemeProvider/types';\nimport { createVegaColorResolver, SERIES_TOKENS, tierTwoColour } from '../../vega/vegaTokens';\n\n/**\n * Put the design system's `--chart-1..5` palette on a Flint input.\n *\n * - Flint resolves series colours during assembly, so they ride `theme_spec`, not `vegaTheme`.\n * - An author-supplied `theme_spec` is passed through untouched.\n * - A `null` `theme_spec` is replaced like a missing one — flint throws on it.\n */\nexport const withFlintSeriesPalette = (\n input: ChartAssemblyInput,\n scheme: ResolvedColorScheme,\n from?: Element | null,\n): ChartAssemblyInput => {\n if (input.theme_spec !== undefined && input.theme_spec !== null) return input;\n\n const token = createVegaColorResolver(scheme, from);\n const categorical = SERIES_TOKENS.map((name) => token(name));\n\n return {\n ...input,\n theme_spec: {\n ink: {\n // `endpointsAgainstSurface` below needs to know what the chart sits on.\n surface: { canvas: token('card') },\n // Flint's defaults assume a light canvas, so text and structure come from tokens.\n text: {\n primary: token('foreground'),\n secondary: token('muted-foreground'),\n muted: token('muted-foreground'),\n // Ink on a filled (series-coloured) mark.\n inverse: token('card'),\n },\n structure: {\n axis: token('border'),\n grid: token('border'),\n frame: token('border'),\n rule: token('border'),\n },\n series: {\n categorical,\n // Past five series flint swaps `categorical` for its theme-blind `tableau10`;\n // this keeps 6-10 on our hues (`tierTwoColour`).\n categoricalExtended: [\n ...categorical,\n ...categorical.map((colour) => tierTwoColour(colour, scheme)),\n ],\n // The tail past even the extended set.\n overflow: token('muted-foreground'),\n // For a quantitative `color` field; flint's default starts at black, invisible on dark.\n sequential: {\n stops: [token('accent'), token('chart-1')],\n space: 'lab',\n endpointsAgainstSurface: true,\n },\n },\n },\n },\n };\n};\n","/**\n * The boundary in front of the flint renderer: nothing in a spec may fetch or run.\n *\n * - Checked on the input, before `assembleVegaLite`: compiled Vega carries its own `signals`/`expr`.\n * - Refused on a key's presence, not its value: a relative `data.url` is still a fetch.\n */\n\n/** Keys whose presence anywhere in a spec is refused: each fetches, evaluates or binds a signal. */\nconst REFUSED_KEYS = new Set(['url', 'expr', 'signal', 'signals', 'params']);\n\n/** Suffix of Vega-Lite's other expression properties; matched by shape so new ones are refused. */\nconst EXPRESSION_KEY_SUFFIX = /Expr$/;\n\n/**\n * An own property's value, or `undefined`.\n *\n * - Via a descriptor: inherited keys are ignored and a getter is never called.\n */\nconst ownValue = (node: unknown, key: string): unknown => {\n if (typeof node !== 'object' || node === null) return undefined;\n\n return Object.getOwnPropertyDescriptor(node, key)?.value;\n};\n\n/**\n * The chart's rows (`spec.data.values` only), which the walk does not descend into.\n *\n * - Rows are data vega never evaluates, so a column named `url` must not be refused.\n * - Flint takes one top-level dataset, so every other node is spec.\n * - Skipped by identity, not key name: a `values` key elsewhere must not hide a refused key.\n */\nconst inlineRows = (spec: unknown): unknown[] | null => {\n const values = ownValue(ownValue(spec, 'data'), 'values');\n\n return Array.isArray(values) ? values : null;\n};\n\n/**\n * The top-level maps flint keys by column name, so their keys are data, not spec.\n *\n * - Only the keys are exempt; each value is walked like any other node.\n * - Matched by identity, as in `inlineRows`: a nested object with the same name gets no exemption.\n */\nconst COLUMN_KEYED_MAPS = ['semantic_types', 'field_display_names'];\n\nconst columnKeyedMaps = (spec: unknown): unknown[] =>\n COLUMN_KEYED_MAPS.map((key) => ownValue(spec, key)).filter(\n (map) => typeof map === 'object' && map !== null,\n );\n\n/** Ceiling on the nodes the walk visits; exceeding it refuses, since the spec went unchecked. */\nconst MAX_SPEC_NODES = 10_000;\n\nconst TOO_LARGE = 'is too large to check';\n\n/**\n * Why this spec may not be rendered, or `null` if it may.\n *\n * - The reason goes to the console; the reader sees `DiagramSource`'s fallback.\n * - Iterative, so deep nesting cannot overflow the stack; nodes are charged on push.\n * - An accessor is refused, not skipped: its value cannot be read without running it.\n */\nexport const findUnsafeSpecKey = (spec: unknown): string | null => {\n const rows = inlineRows(spec);\n const columnKeyed = columnKeyedMaps(spec);\n const stack: unknown[] = [spec];\n let visited = 1;\n\n while (stack.length > 0) {\n const node = stack.pop();\n\n if (Array.isArray(node)) {\n visited += node.length;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(...node);\n continue;\n }\n\n if (typeof node !== 'object' || node === null) continue;\n\n const keysAreColumnNames = columnKeyed.includes(node);\n\n // Descriptors: skip inherited keys and never call a getter.\n for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(node))) {\n if (!keysAreColumnNames && (REFUSED_KEYS.has(key) || EXPRESSION_KEY_SUFFIX.test(key))) {\n return `carries \\`${key}\\``;\n }\n if (!('value' in descriptor)) return `carries an accessor at \\`${key}\\``;\n\n const { value } = descriptor;\n if (rows !== null && value === rows) continue;\n\n visited += 1;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(value);\n }\n }\n\n return null;\n};\n","import type { ChartAssemblyInput } from 'flint-chart';\nimport type { assembleVegaLite } from 'flint-chart/vegalite';\nimport type * as vega from 'vega';\nimport type * as vegaLite from 'vega-lite';\nimport type { Config, TopLevelSpec } from 'vega-lite';\n\nimport { findUnsafeSpecKey } from './refuseUnsafeSpec';\n\n// The lazily imported modules the pipeline needs; a parameter so tests can pass a fake runtime.\nexport type FlintRuntime = {\n assembleVegaLite: typeof assembleVegaLite;\n vega: Pick<typeof vega, 'View' | 'parse'>;\n vegaLite: Pick<typeof vegaLite, 'compile'>;\n};\n\n/**\n * Compile a Flint `ChartAssemblyInput` to a static SVG string.\n *\n * - The one place unsafe input is refused (`refuseUnsafeSpec.ts`), before anything compiles it.\n * - Flint's `_`-prefixed hint keys are not Vega-Lite schema and are stripped before compiling.\n * - `config` rides the compile options; flint's spec-side config sets no colours to conflict.\n * - Renders headless; `toSVG()` is the only output.\n */\nexport const renderFlintSvg = async (\n runtime: FlintRuntime,\n input: ChartAssemblyInput,\n config: Config,\n): Promise<string> => {\n const unsafe = findUnsafeSpecKey(input);\n if (unsafe !== null) {\n throw new Error(`Refusing to render a flint spec that ${unsafe}`);\n }\n\n // `assembleVegaLite` returns `any` upstream; without the hint keys it is a Vega-Lite spec.\n const vlSpec: TopLevelSpec & Record<string, unknown> = { ...runtime.assembleVegaLite(input) };\n for (const key of Object.keys(vlSpec)) {\n if (key.startsWith('_')) delete vlSpec[key];\n }\n const { spec } = runtime.vegaLite.compile(vlSpec, { config });\n const view = new runtime.vega.View(runtime.vega.parse(spec), { renderer: 'none' });\n try {\n await view.runAsync();\n return await view.toSVG();\n } finally {\n view.finalize();\n }\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ChartAssemblyInput } from 'flint-chart';\n\nimport { useThemeAttributes } from '../../components/ThemeProvider/useThemeAttributes';\nimport { ZoomableSvg } from '../../components/ZoomableSvg';\nimport { vegaTheme } from '../../vega/vegaTheme';\nimport { DiagramSkeleton } from '../DiagramShared/DiagramSkeleton';\nimport { DiagramSource } from '../DiagramShared/DiagramSource';\nimport { createSvgCache, diagramCacheKey } from '../DiagramShared/svgCache';\nimport { makeResponsive } from '../DiagramShared/svgTransforms';\n\nimport { withFlintSeriesPalette } from './flintTheme';\nimport type { FlintRuntime } from './renderFlintSvg';\nimport { renderFlintSvg } from './renderFlintSvg';\n\nlet runtimePromise: Promise<FlintRuntime> | null = null;\n\n// Memoized chart-stack import, reset on failure so a chunk-load error can retry.\n// Mirrors `getMermaid()` in `MermaidDiagram`.\nconst getFlintRuntime = (): Promise<FlintRuntime> => {\n if (!runtimePromise) {\n runtimePromise = Promise.all([\n import('flint-chart/vegalite'),\n import('vega'),\n import('vega-lite'),\n ])\n .then(([flint, vega, vegaLite]) => ({\n assembleVegaLite: flint.assembleVegaLite,\n vega,\n vegaLite,\n }))\n .catch((err) => {\n runtimePromise = null;\n throw err;\n });\n }\n return runtimePromise;\n};\n\nconst RENDER_DEBOUNCE_MS = 300;\n\n// A streaming fence is unparseable just like a broken one, so unparseable source\n// keeps the skeleton until it has been unchanged this long, then shows as raw source.\nconst PARSE_GRACE_MS = 1500;\n\n// Parse, compile and layout are synchronous, so a larger source would freeze the chat.\n// Far above the prompt's ~50-row inline data cap.\nconst MAX_SOURCE_CHARS = 200_000;\n\n// Values are already `makeResponsive`d, so ZoomableSvg's sanitize memo stays stable.\nconst svgCache = createSvgCache();\n\n// The theme half of a cache key — tells a still-streaming spec from a theme switch.\nconst prevThemeKeyOf = (key: string): string => key.slice(0, key.indexOf('\\n'));\n\ntype Props = {\n /** Raw source of a ```flint fence: a flint-chart `ChartAssemblyInput` as JSON. */\n code: string;\n /** Extra toolbar controls (e.g. `ExportMenu`) for `ZoomableSvg`; receives the sanitized SVG. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const FlintChart = ({ code, renderActions }: Props) => {\n // - Re-renders on a scheme or brand change, same contract as MermaidDiagram.\n // - `scope` is the element tokens are read off (`null` = `<html>`); it is part of `themeKey`.\n const { scheme, cacheKey: themeKey, scope } = useThemeAttributes();\n const cacheKey = diagramCacheKey(code, themeKey);\n\n const [svg, setSvg] = useState<string | null>(() => svgCache.get(cacheKey) ?? null);\n const [renderFailed, setRenderFailed] = useState(false);\n\n // Reset stale state synchronously on a code or theme change (no one-frame flash):\n // 1. A cache hit is applied at once.\n // 2. Same theme, source only grew (still streaming): keep the last chart, no flicker.\n // 3. Anything else clears — colours are baked in, so a stale chart is wrong.\n const [prevKey, setPrevKey] = useState(cacheKey);\n const [prevCode, setPrevCode] = useState(code);\n if (cacheKey !== prevKey) {\n const cached = svgCache.get(cacheKey);\n const sameThemeStillStreaming =\n themeKey === prevThemeKeyOf(prevKey) && code.startsWith(prevCode);\n setPrevKey(cacheKey);\n setPrevCode(code);\n if (cached) setSvg(cached);\n else if (!sameThemeStillStreaming) setSvg(null);\n setRenderFailed(false);\n }\n\n useEffect(() => {\n if (svgCache.has(cacheKey)) return;\n\n let cancelled = false;\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const debounceTimer = setTimeout(() => {\n if (code.length > MAX_SOURCE_CHARS) {\n // Never becomes renderable, so skip the grace period.\n console.error(\n `[FlintChart] source exceeds ${MAX_SOURCE_CHARS} characters (${code.length}); not rendering`,\n );\n setSvg(null);\n setRenderFailed(true);\n return;\n }\n\n let input: ChartAssemblyInput;\n try {\n input = JSON.parse(code);\n } catch (err) {\n // Likely still streaming — fail only if unchanged when the grace period ends.\n graceTimer = setTimeout(() => {\n console.debug('[FlintChart] source never became parseable JSON:', err);\n setSvg(null);\n setRenderFailed(true);\n }, PARSE_GRACE_MS - RENDER_DEBOUNCE_MS);\n return;\n }\n\n getFlintRuntime()\n .then(async (runtime) => {\n if (cancelled) return;\n // `scheme` comes from the same read as the cache key, so palette and key agree.\n const svgOutput = makeResponsive(\n await renderFlintSvg(\n runtime,\n withFlintSeriesPalette(input, scheme, scope),\n vegaTheme(scheme, scope),\n ),\n );\n if (cancelled) return;\n svgCache.set(cacheKey, svgOutput);\n setSvg(svgOutput);\n setRenderFailed(false);\n })\n .catch((err) => {\n if (cancelled) return;\n console.error('[FlintChart] render failed:', err);\n setSvg(null);\n setRenderFailed(true);\n });\n }, RENDER_DEBOUNCE_MS);\n\n return () => {\n cancelled = true;\n clearTimeout(debounceTimer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n }, [cacheKey, code, scheme, scope]);\n\n if (renderFailed) {\n return <DiagramSource code={code} />;\n }\n\n if (svg) {\n return (\n <ZoomableSvg\n // Keyed by SVG, not `code`: a streaming chart is held, and a remount per chunk\n // would drop zoom/pan.\n key={svg}\n html={svg}\n copyText={`\\`\\`\\`flint\\n${code}\\n\\`\\`\\``}\n className=\"tw:my-3\"\n renderActions={renderActions}\n />\n );\n }\n\n return <DiagramSkeleton />;\n};\n"],"mappings":"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"}
|