{
	"$schema": "https://shadcn-svelte.com/schema/registry-item.json",
	"name": "chart",
	"title": "Chart",
	"type": "registry:ui",
	"description": "Kura Chart component source.",
	"devDependencies": [
		"layerchart@2.0.0-next.48"
	],
	"registryDependencies": [
		"utils"
	],
	"files": [
		{
			"content": "<script lang=\"ts\">\n  import { cn, type WithElementRef } from '$UTILS$.js';\n  import type { HTMLAttributes } from 'svelte/elements';\n  import ChartStyle from './chart-style.svelte';\n  import { setChartContext, type ChartConfig } from './chart-utils.js';\n\n  const uid = $props.id();\n\n  let {\n    ref = $bindable(null),\n    id = uid,\n    class: className,\n    children,\n    config,\n    ...restProps\n  }: WithElementRef<HTMLAttributes<HTMLElement>> & {\n    config: ChartConfig;\n  } = $props();\n\n  const chartId = $derived(`chart-${id || uid.replace(/:/g, '')}`);\n\n  setChartContext({\n    get config() {\n      return config;\n    }\n  });\n</script>\n\n<div\n  bind:this={ref}\n  data-chart={chartId}\n  data-slot=\"chart\"\n  class={cn(\n    'flex aspect-video justify-center overflow-visible border border-zinc-800 bg-background p-3 font-mono text-xs text-zinc-400',\n    // Overrides\n    //\n    // Stroke around dots/marks when hovering\n    '[&_.lc-highlight-point]:stroke-transparent',\n    // override the default stroke color of lines\n    '[&_.lc-line]:stroke-zinc-700',\n\n    // by default, layerchart shows a line intersecting the point when hovering, this hides that\n    '[&_.lc-highlight-line]:stroke-0',\n\n    // by default, when you hover a point on a stacked series chart, it will drop the opacity\n    // of the other series, this overrides that\n    '[&_.lc-area-path]:opacity-100 [&_.lc-highlight-line]:opacity-100 [&_.lc-highlight-point]:opacity-100 [&_.lc-spline-path]:opacity-100 [&_.lc-text]:text-xs [&_.lc-text-svg]:overflow-visible',\n\n    // We don't want the little tick lines between the axis labels and the chart, so we remove\n    // the stroke. The alternative is to manually disable `tickMarks` on the x/y axis of every\n    // chart.\n    '[&_.lc-axis-tick]:stroke-0',\n\n    // We don't want to display the rule on the x/y axis, as there is already going to be\n    // a grid line there and rule ends up overlapping the marks because it is rendered after\n    // the marks\n    '[&_.lc-rule-x-line:not(.lc-grid-x-rule)]:stroke-0 [&_.lc-rule-y-line:not(.lc-grid-y-rule)]:stroke-0',\n    '[&_.lc-grid-x-radial-line]:stroke-zinc-800 [&_.lc-grid-x-radial-circle]:stroke-zinc-800',\n    '[&_.lc-grid-y-radial-line]:stroke-zinc-800 [&_.lc-grid-y-radial-circle]:stroke-zinc-800',\n\n    // Legend adjustments\n    '[&_.lc-legend-swatch-button]:items-center [&_.lc-legend-swatch-button]:gap-1.5',\n    '[&_.lc-legend-swatch-group]:items-center [&_.lc-legend-swatch-group]:gap-4',\n    '[&_.lc-legend-swatch]:size-2.5 [&_.lc-legend-swatch]:rounded-[2px]',\n\n    // Labels\n    '[&_.lc-labels-text:not([fill])]:fill-zinc-100 [&_text]:stroke-transparent',\n\n    // Tick labels on th x/y axes\n    '[&_.lc-axis-tick-label]:fill-zinc-500 [&_.lc-axis-tick-label]:font-mono [&_.lc-axis-tick-label]:font-medium',\n    '[&_.lc-tooltip-rects-g]:fill-transparent',\n    '[&_.lc-layout-svg-g]:fill-transparent',\n    '[&_.lc-root-container]:w-full',\n    className\n  )}\n  {...restProps}\n>\n  <ChartStyle id={chartId} {config} />\n  {@render children?.()}\n</div>\n",
			"type": "registry:file",
			"target": "chart/chart-container.svelte"
		},
		{
			"content": "<script lang=\"ts\">\n  import { THEMES, type ChartConfig } from './chart-utils.js';\n\n  let { id, config }: { id: string; config: ChartConfig } = $props();\n\n  const colorConfig = $derived(\n    config ? Object.entries(config).filter(([, config]) => config.theme || config.color) : null\n  );\n\n  const themeContents = $derived.by(() => {\n    if (!colorConfig || !colorConfig.length) return;\n\n    const themeContents = [];\n    for (const [_theme, prefix] of Object.entries(THEMES)) {\n      let content = `${prefix} [data-chart=${id}] {\\n`;\n      const color = colorConfig.map(([key, itemConfig]) => {\n        const theme = _theme as keyof typeof itemConfig.theme;\n        const color = itemConfig.theme?.[theme] || itemConfig.color;\n        return color ? `\\t--color-${key}: ${color};` : null;\n      });\n\n      content += color.join('\\n') + '\\n}';\n\n      themeContents.push(content);\n    }\n\n    return themeContents.join('\\n');\n  });\n</script>\n\n{#if themeContents}\n  {#key id}\n    <svelte:element this={\"style\"}>\n      {themeContents}\n    </svelte:element>\n  {/key}\n{/if}\n",
			"type": "registry:file",
			"target": "chart/chart-style.svelte"
		},
		{
			"content": "<script lang=\"ts\">\n  import { cn, type WithElementRef, type WithoutChildren } from '$UTILS$.js';\n  import type { HTMLAttributes } from 'svelte/elements';\n  import { getPayloadConfigFromPayload, useChart, type TooltipPayload } from './chart-utils.js';\n  import { getChartContext, Tooltip as TooltipPrimitive } from 'layerchart';\n  import type { Snippet } from 'svelte';\n\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  function defaultFormatter(value: any) {\n    return `${value}`;\n  }\n\n  let {\n    ref = $bindable(null),\n    class: className,\n    hideLabel = false,\n    indicator = 'dot',\n    hideIndicator = false,\n    labelKey,\n    label,\n    labelFormatter = defaultFormatter,\n    labelClassName,\n    formatter,\n    nameKey,\n    color,\n    ...restProps\n  }: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {\n    hideLabel?: boolean;\n    label?: string;\n    indicator?: 'line' | 'dot' | 'dashed';\n    nameKey?: string;\n    labelKey?: string;\n    hideIndicator?: boolean;\n    labelClassName?: string;\n    labelFormatter?: // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      ((value: any, payload: TooltipPayload[]) => string | number | Snippet) | null;\n    formatter?: Snippet<\n      [\n        {\n          value: unknown;\n          name: string;\n          item: TooltipPayload;\n          index: number;\n          payload: TooltipPayload[];\n        }\n      ]\n    >;\n  } = $props();\n\n  const chart = useChart();\n  const chartCtx = getChartContext();\n\n  // Filter to series with defined values (important for item-based charts like Pie/Arc\n  // where only the hovered item has a value)\n  const visibleSeries = $derived(\n    chartCtx.tooltip.series.filter((s: TooltipPayload) => s.value !== undefined)\n  );\n\n  const formattedLabel = $derived.by(() => {\n    if (hideLabel || !visibleSeries?.length) return null;\n\n    const [item] = visibleSeries;\n    const tooltipData = chartCtx.tooltip.data;\n\n    // Get the x-axis label value from the raw tooltip data (e.g. a Date or month string)\n    const dataLabel = tooltipData != null ? chartCtx.x(tooltipData) : undefined;\n\n    const key = labelKey ?? item?.label ?? item?.key ?? 'value';\n    const itemConfig = getPayloadConfigFromPayload(\n      chart.config,\n      item,\n      key,\n      tooltipData as Record<string, unknown> | null\n    );\n\n    let value: unknown;\n    if (!labelKey && typeof label === 'string') {\n      value = chart.config[label as keyof typeof chart.config]?.label ?? label;\n    } else if (labelKey) {\n      value = itemConfig?.label ?? dataLabel;\n    } else {\n      value = dataLabel;\n    }\n\n    if (value === undefined) return null;\n    if (!labelFormatter) return value;\n    return labelFormatter(value, visibleSeries);\n  });\n\n  const nestLabel = $derived(visibleSeries.length === 1 && indicator !== 'dot');\n</script>\n\n{#snippet TooltipLabel()}\n  {#if formattedLabel}\n    <div class={cn('font-medium', labelClassName)}>\n      {#if typeof formattedLabel === 'function'}\n        {@render formattedLabel()}\n      {:else}\n        {formattedLabel}\n      {/if}\n    </div>\n  {/if}\n{/snippet}\n\n<TooltipPrimitive.Root variant=\"none\">\n  <div\n    bind:this={ref}\n    class={cn(\n      'grid min-w-[9rem] items-start gap-1.5 border border-zinc-800 bg-zinc-950 px-2.5 py-1.5 font-mono text-xs text-zinc-100 shadow-none',\n      className\n    )}\n    {...restProps}\n  >\n    {#if !nestLabel}\n      {@render TooltipLabel()}\n    {/if}\n    <div class=\"grid gap-1.5\">\n      {#each visibleSeries as item, i (item.key + i)}\n        {@const key = `${nameKey || item.key || item.label || 'value'}`}\n        {@const itemConfig = getPayloadConfigFromPayload(\n          chart.config,\n          item,\n          key,\n          chartCtx.tooltip.data\n        )}\n        {@const indicatorColor = color || item.config?.color || item.color}\n        <div\n          class={cn(\n            'flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5 [&>svg]:text-zinc-500',\n            indicator === 'dot' && 'items-center'\n          )}\n        >\n          {#if formatter && item.value !== undefined && item.label}\n            {@render formatter({\n              value: item.value,\n              name: item.label,\n              item,\n              index: i,\n              payload: visibleSeries\n            })}\n          {:else}\n            {#if itemConfig?.icon}\n              <itemConfig.icon />\n            {:else if !hideIndicator}\n              <div\n                style=\"--color-bg: {indicatorColor}; --color-border: {indicatorColor};\"\n                class={cn('shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)', {\n                  'size-2.5': indicator === 'dot',\n                  'h-full w-1': indicator === 'line',\n                  'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',\n                  'my-0.5': nestLabel && indicator === 'dashed'\n                })}\n              ></div>\n            {/if}\n            <div\n              class={cn(\n                'flex flex-1 shrink-0 justify-between leading-none',\n                nestLabel ? 'items-end' : 'items-center'\n              )}\n            >\n              <div class=\"grid gap-1.5\">\n                {#if nestLabel}\n                  {@render TooltipLabel()}\n                {/if}\n                <span class=\"text-zinc-500\">\n                  {itemConfig?.label || item.label}\n                </span>\n              </div>\n              {#if item.value !== undefined}\n                <span class=\"font-mono font-semibold text-zinc-100 tabular-nums\">\n                  {item.value.toLocaleString()}\n                </span>\n              {/if}\n            </div>\n          {/if}\n        </div>\n      {/each}\n    </div>\n  </div>\n</TooltipPrimitive.Root>\n",
			"type": "registry:file",
			"target": "chart/chart-tooltip.svelte"
		},
		{
			"content": "import type { Tooltip } from 'layerchart';\nimport { getContext, setContext, type Component, type Snippet } from 'svelte';\n\nexport const THEMES = { light: '', dark: '.dark' } as const;\n\nexport type ChartConfig = {\n  [k in string]: {\n    label?: string;\n    icon?: Component;\n  } & (\n    | { color?: string; theme?: never }\n    | { color?: never; theme: Record<keyof typeof THEMES, string> }\n  );\n};\n\nexport type ExtractSnippetParams<T> = T extends Snippet<[infer P]> ? P : never;\n\nexport type TooltipPayload = Tooltip.TooltipSeries;\n\n// Helper to extract item config from a payload.\nexport function getPayloadConfigFromPayload(\n  config: ChartConfig,\n  payload: TooltipPayload,\n  key: string,\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  data?: Record<string, any> | null\n) {\n  if (typeof payload !== 'object' || payload === null) return undefined;\n\n  const payloadConfig =\n    'config' in payload && typeof payload.config === 'object' && payload.config !== null\n      ? payload.config\n      : undefined;\n\n  let configLabelKey: string = key;\n\n  if (payload.key === key) {\n    configLabelKey = payload.key;\n  } else if (payload.label === key) {\n    configLabelKey = payload.label;\n  } else if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {\n    configLabelKey = payload[key as keyof typeof payload] as string;\n  } else if (\n    payloadConfig !== undefined &&\n    key in payloadConfig &&\n    typeof payloadConfig[key as keyof typeof payloadConfig] === 'string'\n  ) {\n    configLabelKey = payloadConfig[key as keyof typeof payloadConfig] as string;\n  } else if (data != null && key in data && typeof data[key] === 'string') {\n    configLabelKey = data[key] as string;\n  }\n\n  return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];\n}\n\ntype ChartContextValue = {\n  config: ChartConfig;\n};\n\nconst chartContextKey = Symbol('chart-context');\n\nexport function setChartContext(value: ChartContextValue) {\n  return setContext(chartContextKey, value);\n}\n\nexport function useChart() {\n  return getContext<ChartContextValue>(chartContextKey);\n}\n",
			"type": "registry:file",
			"target": "chart/chart-utils.ts"
		},
		{
			"content": "import ChartContainer from './chart-container.svelte';\nimport ChartTooltip from './chart-tooltip.svelte';\n\nexport { getPayloadConfigFromPayload, type ChartConfig } from './chart-utils.js';\n\nexport { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip };\n",
			"type": "registry:file",
			"target": "chart/index.ts"
		}
	]
}