{
  "name": "chart",
  "type": "registry:ui",
  "dependencies": [
    "@unovis/vue",
    "@unovis/ts"
  ],
  "registryDependencies": [
    "button",
    "card"
  ],
  "files": [
    {
      "path": "ui/chart/ChartCrosshair.vue",
      "content": "<script setup lang=\"ts\">\nimport type { BulletLegendItemInterface } from \"@unovis/ts\"\nimport type { Component } from \"vue\"\nimport { omit } from \"@unovis/ts\"\nimport { VisCrosshair, VisTooltip } from \"@unovis/vue\"\nimport { createApp } from \"vue\"\nimport { ChartTooltip } from \".\"\n\nconst props = withDefaults(defineProps<{\n  colors: string[]\n  index: string\n  items: BulletLegendItemInterface[]\n  customTooltip?: Component\n}>(), {\n  colors: () => [],\n})\n\n// Use weakmap to store reference to each datapoint for Tooltip\nconst wm = new WeakMap()\nfunction template(d: any) {\n  if (wm.has(d)) {\n    return wm.get(d)\n  }\n  else {\n    const componentDiv = document.createElement(\"div\")\n    const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {\n      const legendReference = props.items.find(i => i.name === key)\n      return { ...legendReference, value }\n    })\n    const TooltipComponent = props.customTooltip ?? ChartTooltip\n    createApp(TooltipComponent, { title: d[props.index].toString(), data: omittedData }).mount(componentDiv)\n    wm.set(d, componentDiv.innerHTML)\n    return componentDiv.innerHTML\n  }\n}\n\nfunction color(d: unknown, i: number) {\n  return props.colors[i] ?? \"transparent\"\n}\n</script>\n\n<template>\n  <VisTooltip :horizontal-shift=\"20\" :vertical-shift=\"20\" />\n  <VisCrosshair :template=\"template\" :color=\"color\" />\n</template>\n",
      "type": "registry:ui",
      "target": ""
    },
    {
      "path": "ui/chart/ChartLegend.vue",
      "content": "<script setup lang=\"ts\">\nimport type { BulletLegendItemInterface } from \"@unovis/ts\"\nimport { BulletLegend } from \"@unovis/ts\"\nimport { VisBulletLegend } from \"@unovis/vue\"\nimport { nextTick, onMounted, ref } from \"vue\"\nimport { buttonVariants } from \"@/registry/new-york/ui/button\"\n\nconst props = withDefaults(defineProps<{ items: BulletLegendItemInterface[] }>(), {\n  items: () => [],\n})\n\nconst emits = defineEmits<{\n  \"legendItemClick\": [d: BulletLegendItemInterface, i: number]\n  \"update:items\": [payload: BulletLegendItemInterface[]]\n}>()\n\nconst elRef = ref<HTMLElement>()\n\nfunction keepStyling() {\n  const selector = `.${BulletLegend.selectors.item}`\n  nextTick(() => {\n    const elements = elRef.value?.querySelectorAll(selector)\n    const classes = buttonVariants({ variant: \"ghost\", size: \"xs\" }).split(\" \")\n\n    elements?.forEach(el => el.classList.add(...classes, \"!inline-flex\", \"!mr-2\"))\n  })\n}\n\nonMounted(() => {\n  keepStyling()\n})\n\nfunction onLegendItemClick(d: BulletLegendItemInterface, i: number) {\n  emits(\"legendItemClick\", d, i)\n  const isBulletActive = !props.items[i].inactive\n  const isFilterApplied = props.items.some(i => i.inactive)\n  if (isFilterApplied && isBulletActive) {\n    // reset filter\n    emits(\"update:items\", props.items.map(item => ({ ...item, inactive: false })))\n  }\n  else {\n    // apply selection, set other item as inactive\n    emits(\"update:items\", props.items.map(item => item.name === d.name ? ({ ...d, inactive: false }) : { ...item, inactive: true }))\n  }\n  keepStyling()\n}\n</script>\n\n<template>\n  <div\n    ref=\"elRef\" class=\"w-max\" :style=\"{\n      '--vis-legend-bullet-size': '16px',\n    }\"\n  >\n    <VisBulletLegend\n      :items=\"items\"\n      :on-legend-item-click=\"onLegendItemClick\"\n    />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": ""
    },
    {
      "path": "ui/chart/ChartSingleTooltip.vue",
      "content": "<script setup lang=\"ts\">\nimport type { BulletLegendItemInterface } from \"@unovis/ts\"\nimport type { Component } from \"vue\"\nimport { omit } from \"@unovis/ts\"\nimport { VisTooltip } from \"@unovis/vue\"\nimport { createApp } from \"vue\"\nimport { ChartTooltip } from \".\"\n\nconst props = defineProps<{\n  selector: string\n  index: string\n  items?: BulletLegendItemInterface[]\n  valueFormatter?: (tick: number, i?: number, ticks?: number[]) => string\n  customTooltip?: Component\n}>()\n\n// Use weakmap to store reference to each datapoint for Tooltip\nconst wm = new WeakMap()\nfunction template(d: any, i: number, elements: (HTMLElement | SVGElement)[]) {\n  const valueFormatter = props.valueFormatter ?? ((tick: number) => `${tick}`)\n  if (props.index in d) {\n    if (wm.has(d)) {\n      return wm.get(d)\n    }\n    else {\n      const componentDiv = document.createElement(\"div\")\n      const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {\n        const legendReference = props.items?.find(i => i.name === key)\n        return { ...legendReference, value: valueFormatter(value) }\n      })\n      const TooltipComponent = props.customTooltip ?? ChartTooltip\n      createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)\n      wm.set(d, componentDiv.innerHTML)\n      return componentDiv.innerHTML\n    }\n  }\n\n  else {\n    const data = d.data\n\n    if (wm.has(data)) {\n      return wm.get(data)\n    }\n    else {\n      const style = getComputedStyle(elements[i])\n      const omittedData = [{ name: data.name, value: valueFormatter(data[props.index]), color: style.fill }]\n      const componentDiv = document.createElement(\"div\")\n      const TooltipComponent = props.customTooltip ?? ChartTooltip\n      createApp(TooltipComponent, { title: d[props.index], data: omittedData }).mount(componentDiv)\n      wm.set(d, componentDiv.innerHTML)\n      return componentDiv.innerHTML\n    }\n  }\n}\n</script>\n\n<template>\n  <VisTooltip\n    :horizontal-shift=\"20\" :vertical-shift=\"20\" :triggers=\"{\n      [selector]: template,\n    }\"\n  />\n</template>\n",
      "type": "registry:ui",
      "target": ""
    },
    {
      "path": "ui/chart/ChartTooltip.vue",
      "content": "<script setup lang=\"ts\">\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/registry/new-york/ui/card\"\n\ndefineProps<{\n  title?: string\n  data: {\n    name: string\n    color: string\n    value: any\n  }[]\n}>()\n</script>\n\n<template>\n  <Card class=\"text-sm\">\n    <CardHeader v-if=\"title\" class=\"p-3 border-b\">\n      <CardTitle>\n        {{ title }}\n      </CardTitle>\n    </CardHeader>\n    <CardContent class=\"p-3 min-w-[180px] flex flex-col gap-1\">\n      <div v-for=\"(item, key) in data\" :key=\"key\" class=\"flex justify-between\">\n        <div class=\"flex items-center\">\n          <span class=\"w-2.5 h-2.5 mr-2\">\n            <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 30 30\">\n              <path\n                d=\" M 15 15 m -14, 0 a 14,14 0 1,1 28,0 a 14,14 0 1,1 -28,0\"\n                :stroke=\"item.color\"\n                :fill=\"item.color\"\n                stroke-width=\"1\"\n              />\n            </svg>\n          </span>\n          <span>{{ item.name }}</span>\n        </div>\n        <span class=\"font-semibold ml-4\">{{ item.value }}</span>\n      </div>\n    </CardContent>\n  </Card>\n</template>\n",
      "type": "registry:ui",
      "target": ""
    },
    {
      "path": "ui/chart/index.ts",
      "content": "export { default as ChartCrosshair } from \"./ChartCrosshair.vue\"\nexport { default as ChartLegend } from \"./ChartLegend.vue\"\nexport { default as ChartSingleTooltip } from \"./ChartSingleTooltip.vue\"\nexport { default as ChartTooltip } from \"./ChartTooltip.vue\"\n\nexport function defaultColors(count: number = 3) {\n  const quotient = Math.floor(count / 2)\n  const remainder = count % 2\n\n  const primaryCount = quotient + remainder\n  const secondaryCount = quotient\n  return [\n    ...Array.from(new Array(primaryCount).keys()).map(i => `hsl(var(--vis-primary-color) / ${1 - (1 / primaryCount) * i})`),\n    ...Array.from(new Array(secondaryCount).keys()).map(i => `hsl(var(--vis-secondary-color) / ${1 - (1 / secondaryCount) * i})`),\n  ]\n}\n\nexport * from \"./interface\"\n",
      "type": "registry:ui",
      "target": ""
    },
    {
      "path": "ui/chart/interface.ts",
      "content": "import type { Spacing } from \"@unovis/ts\"\n\ntype KeyOf<T extends Record<string, any>> = Extract<keyof T, string>\n\nexport interface BaseChartProps<T extends Record<string, any>> {\n  /**\n   * The source data, in which each entry is a dictionary.\n   */\n  data: T[]\n  /**\n   * Select the categories from your data. Used to populate the legend and tooltip.\n   */\n  categories: KeyOf<T>[]\n  /**\n   * Sets the key to map the data to the axis.\n   */\n  index: KeyOf<T>\n  /**\n   * Change the default colors.\n   */\n  colors?: string[]\n  /**\n   * Margin of each the container\n   */\n  margin?: Spacing\n  /**\n   * Change the opacity of the non-selected field\n   * @default 0.2\n   */\n  filterOpacity?: number\n  /**\n   * Function to format X label\n   */\n  xFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string\n  /**\n   * Function to format Y label\n   */\n  yFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string\n  /**\n   * Controls the visibility of the X axis.\n   * @default true\n   */\n  showXAxis?: boolean\n  /**\n   * Controls the visibility of the Y axis.\n   * @default true\n   */\n  showYAxis?: boolean\n  /**\n   * Controls the visibility of tooltip.\n   * @default true\n   */\n  showTooltip?: boolean\n  /**\n   * Controls the visibility of legend.\n   * @default true\n   */\n  showLegend?: boolean\n  /**\n   * Controls the visibility of gridline.\n   * @default true\n   */\n  showGridLine?: boolean\n}\n",
      "type": "registry:ui",
      "target": ""
    }
  ]
}
