UIPackage
Menu

Framework

Change language

Boilerplate repo

Area Chart

area-chartui

Filled-area line chart wrapper around Apache ECharts. Multi-series, stacked, gradient, and stepped variants. Theme-aware via registry tokens (--chart-1..5, --muted-foreground, --border).

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/area-chart.json
Named registry:npx shadcn-vue@latest add @uipkge/area-chartInstalls to:app/components/ui/charts/area-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 false.

booleanfalseoptional
dashed

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

booleanfalseoptional
heightnumber | string300optional
optionanyoptional
classstringoptional
ariaLabel

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

stringoptional

npm dependencies

Used by

Files installed (3)

  • app/components/ui/charts/area-chart/AreaChart.vue4.9 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 } 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])
    
    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 false. */
      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: false,
      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 ? 'areas' : undefined,
        symbol: props.markers ? 'circle' : 'none',
        symbolSize: 6,
        areaStyle: { opacity: props.stacked ? 0.5 : 0.15 },
        lineStyle: { width: 2, type: props.dashed ? 'dashed' : 'solid' },
        itemStyle: { color: chartColors.value[i % chartColors.value.length] },
        data: props.data.map((d) => d[field]),
      }))
    
      // Deep-merge `series[i]` from `option` so consumers can pass partial
      // overrides (e.g. `series: [{ stack: 'r' }, ...]`) without clobbering the
      // computed `type`/`data`. Top-level option blocks (xAxis, yAxis, grid,
      // tooltip, legend) go through `mergeOptionBlock` so overrides like
      // `xAxis: { axisLabel: { fontSize: 9 } }` only replace the axisLabel
      // inner fields they touch, not the whole axisLabel (and never the data).
      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
    
      // Single-series charts hide the legend explicitly so ECharts' default
      // doesn't dump the y-field name (e.g. "y") onto the chart canvas.
      const baseLegend: any =
        fields.length > 1
          ? {
              bottom: 0,
              icon: 'circle',
              itemWidth: 8,
              itemHeight: 8,
              textStyle: { fontSize: 11, color: chartTextColor.value },
            }
          : { show: false }
    
      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/area-chart/index.ts0.1 kB
  • app/components/ui/charts/useChartTheme.ts8.2 kB

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