import { LitElement, html, css } from 'lit' import { customElement, property } from 'lit/decorators.js' import * as d3 from 'd3' @customElement('kpi-mini-trend-chart') export class KpiMiniTrendChart extends LitElement { @property({ type: Array }) data: number[] = [] @property({ type: Number }) width: number = 60 @property({ type: Number }) height: number = 30 @property({ type: String }) lineColor: string = '#2196f3' @property({ type: Number }) strokeWidth: number = 1.5 @property({ type: Boolean }) showPoints: boolean = true @property({ type: Number }) pointRadius: number = 1.5 static styles = css` :host { display: block; width: 100%; height: 100%; } .mini-chart { width: 100%; height: 100%; background: #f8f9fa; border-radius: 4px; display: flex; align-items: center; justify-content: center; } .trend-line { fill: none; stroke-linecap: round; stroke-linejoin: round; } .data-point { fill: white; stroke-width: 1; } ` render() { return html`
` } updated() { this.drawMiniTrend() } drawMiniTrend() { if (!this.data || this.data.length === 0) return const svg = d3.select(this.renderRoot.querySelector('#mini-trend')) svg.selectAll('*').remove() const margin = { top: 2, right: 2, bottom: 2, left: 2 } const width = this.width - margin.left - margin.right const height = this.height - margin.top - margin.bottom // 스케일 설정 const xScale = d3 .scaleLinear() .domain([0, this.data.length - 1]) .range([0, width]) const yScale = d3 .scaleLinear() .domain([0, d3.max(this.data) || 100]) .range([height, 0]) // SVG 설정 svg.attr('width', this.width).attr('height', this.height) const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`) // 라인 생성기 const line = d3 .line() .x((d, i) => xScale(i)) .y(d => yScale(d)) .curve(d3.curveMonotoneX) // 트렌드 라인 그리기 g.append('path') .datum(this.data) .attr('class', 'trend-line') .attr('d', line as any) .attr('stroke', this.lineColor) .attr('stroke-width', this.strokeWidth) // 데이터 포인트 그리기 (첫 번째와 마지막 포인트만) if (this.showPoints && this.data.length > 0) { // 첫 번째 포인트 g.append('circle') .attr('class', 'data-point') .attr('cx', xScale(0)) .attr('cy', yScale(this.data[0])) .attr('r', this.pointRadius) .attr('stroke', '#4caf50') .attr('fill', 'white') // 마지막 포인트 if (this.data.length > 1) { g.append('circle') .attr('class', 'data-point') .attr('cx', xScale(this.data.length - 1)) .attr('cy', yScale(this.data[this.data.length - 1])) .attr('r', this.pointRadius) .attr('stroke', '#2196f3') .attr('fill', 'white') } } } }