Menu

Change language

GitHub repo

Line Chart

line-chartui

Line chart wrapper around Apache ECharts. Smooth/stepped/dashed lines, multi-series, point markers. Theme-aware via registry tokens.

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/line-chart.json
Named registry:npx shadcn-vue@latest add @uipkge/line-chartInstalls to:app/components/ui/charts/line-chart/

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
dataRecord<string, any>[]—required
xFieldstring'x'optional
yFieldstring | string[]'y'optional
curve

Line interpolation. Default 'smooth'.

'smooth''linear''step''stepStart''stepEnd'
'smooth'optional
stacked

Stack series cumulatively. Default false.

booleanfalseoptional
markers

Show point markers. Default true.

booleantrueoptional
dashed

Dashed stroke on all series (forecast look). Default false.

booleanfalseoptional
heightnumber | string300optional
optionany—optional
classstring—optional
ariaLabel

Accessible name announced for the chart image. Defaults to "Chart".

string—optional

npm dependencies

Used by

Files installed (3)

  • app/components/ui/charts/line-chart/LineChart.vue4.5 kB
    <script setup lang="ts">
    import { computed } from 'vue'
    import { use } from 'echarts/core'
    import { CanvasRenderer } from 'echarts/renderers'
    import { LineChart as EChartsLineChart } from 'echarts/charts'
    import {
      GridComponent,
      TooltipComponent,
      LegendComponent,
      MarkPointComponent,
      MarkLineComponent,
      MarkAreaComponent,
    } from 'echarts/components'
    import VChart from 'vue-echarts'
    import { cn } from '@/lib/utils'
    import {
      chartColors,
      chartTextColor,
      chartAxisColor,
      chartSplitLineColor,
      chartTooltipBg,
      chartTooltipBorder,
      chartTooltipText,
      mergeOptionBlock,
    } from '../useChartTheme'
    
    use([
      CanvasRenderer,
      EChartsLineChart,
      GridComponent,
      TooltipComponent,
      LegendComponent,
      MarkPointComponent,
      MarkLineComponent,
      MarkAreaComponent,
    ])
    
    interface Props {
      data: Record<string, any>[]
      xField?: string
      yField?: string | string[]
      /** Line interpolation. Default 'smooth'. */
      curve?: 'smooth' | 'linear' | 'step' | 'stepStart' | 'stepEnd'
      /** Stack series cumulatively. Default false. */
      stacked?: boolean
      /** Show point markers. Default true. */
      markers?: boolean
      /** Dashed stroke on all series (forecast look). Default false. */
      dashed?: boolean
      height?: number | string
      option?: any
      class?: string
      /** Accessible name announced for the chart image. Defaults to "Chart". */
      ariaLabel?: string
    }
    
    const props = withDefaults(defineProps<Props>(), {
      xField: 'x',
      yField: 'y',
      curve: 'smooth',
      stacked: false,
      markers: true,
      dashed: false,
      height: 300,
    })
    
    const mergedOption = computed(() => {
      const fields = Array.isArray(props.yField) ? props.yField : [props.yField]
      const xData = props.data.map((d) => d[props.xField!])
    
      const series = fields.map((field, i) => ({
        name: field,
        type: 'line',
        smooth: props.curve === 'smooth',
        step:
          props.curve === 'step'
            ? 'middle'
            : props.curve === 'stepStart'
              ? 'start'
              : props.curve === 'stepEnd'
                ? 'end'
                : false,
        stack: props.stacked ? 'lines' : undefined,
        symbol: props.markers ? 'circle' : 'none',
        symbolSize: 6,
        lineStyle: { width: 2, type: props.dashed ? 'dashed' : 'solid' },
        itemStyle: { color: chartColors.value[i % chartColors.value.length] },
        data: props.data.map((d) => d[field]),
      }))
    
      // Per-index series merge + 2-level deep merge for axis blocks.
      const userOption: any = props.option ?? {}
      const {
        series: userSeries,
        xAxis: userXAxis,
        yAxis: userYAxis,
        grid: userGrid,
        tooltip: userTooltip,
        legend: userLegend,
        ...userRest
      } = userOption
      const mergedSeries = Array.isArray(userSeries) ? series.map((s, i) => ({ ...s, ...(userSeries[i] ?? {}) })) : series
    
      const baseLegend =
        fields.length > 1
          ? {
              bottom: 0,
              icon: 'circle',
              itemWidth: 8,
              itemHeight: 8,
              textStyle: { fontSize: 11, color: chartTextColor.value },
            }
          : undefined
    
      return {
        color: chartColors.value,
        grid: mergeOptionBlock(
          { left: 16, right: 16, top: 24, bottom: fields.length > 1 ? 32 : 24, containLabel: true },
          userGrid,
        ),
        tooltip: mergeOptionBlock(
          {
            trigger: 'axis',
            backgroundColor: chartTooltipBg.value,
            borderColor: chartTooltipBorder.value,
            textStyle: { color: chartTooltipText.value, fontSize: 12 },
          },
          userTooltip,
        ),
        legend: userLegend?.show === false ? undefined : mergeOptionBlock(baseLegend ?? {}, userLegend),
        xAxis: mergeOptionBlock(
          {
            type: 'category',
            data: xData,
            axisLine: { lineStyle: { color: chartAxisColor.value } },
            axisLabel: { color: chartTextColor.value, fontSize: 11 },
            axisTick: { show: false },
          },
          userXAxis,
        ),
        yAxis: mergeOptionBlock(
          {
            type: 'value',
            splitLine: { lineStyle: { color: chartSplitLineColor.value } },
            axisLabel: { color: chartTextColor.value, fontSize: 11 },
            axisLine: { show: false },
            axisTick: { show: false },
          },
          userYAxis,
        ),
        series: mergedSeries,
        ...userRest,
      }
    })
    </script>
    
    <template>
      <div
        role="img"
        tabindex="0"
        :aria-label="ariaLabel || 'Chart'"
        :style="{ height: /^\d+$/.test(String(height)) ? `${height}px` : String(height) }"
        :class="cn('focus-visible:ring-ring w-full focus-visible:ring-2 focus-visible:outline-none', props.class)"
      >
        <VChart :option="mergedOption" :autoresize="true" class="size-full" />
      </div>
    </template>
    
  • app/components/ui/charts/line-chart/index.ts0.1 kB
  • app/components/ui/charts/useChartTheme.ts8.2 kB

Raw manifest:https://uipkge.dev/r/vue/line-chart.json