@dssp/dkpi 1.0.0-alpha.87 → 1.0.0-alpha.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/_index.html +4 -4
  2. package/assets/helps/index.md +1 -1
  3. package/assets/manifest.json +2 -2
  4. package/dist-client/bootstrap.js +1 -1
  5. package/dist-client/bootstrap.js.map +1 -1
  6. package/dist-client/components/kpi-boxplot-chart.d.ts +7 -0
  7. package/dist-client/components/kpi-boxplot-chart.js +20 -5
  8. package/dist-client/components/kpi-boxplot-chart.js.map +1 -1
  9. package/dist-client/components/kpi-lookup-chart.js +1 -17
  10. package/dist-client/components/kpi-lookup-chart.js.map +1 -1
  11. package/dist-client/components/kpi-mini-trend-chart.d.ts +1 -1
  12. package/dist-client/components/kpi-mini-trend-chart.js +45 -13
  13. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -1
  14. package/dist-client/components/kpi-radar-chart.js +2 -2
  15. package/dist-client/components/kpi-radar-chart.js.map +1 -1
  16. package/dist-client/components/kpi-single-boxplot-chart.d.ts +11 -0
  17. package/dist-client/components/kpi-single-boxplot-chart.js +45 -26
  18. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -1
  19. package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
  20. package/dist-client/components/kpi-step-lookup-chart.js +181 -0
  21. package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
  22. package/dist-client/components/kpi-trend-chart.js +41 -20
  23. package/dist-client/components/kpi-trend-chart.js.map +1 -1
  24. package/dist-client/google-map/common-google-map.js +5 -1
  25. package/dist-client/google-map/common-google-map.js.map +1 -1
  26. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +2 -1
  27. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +24 -9
  28. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -1
  29. package/dist-client/pages/kpi-admin/kpi-system-guide.js +311 -158
  30. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -1
  31. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +8 -0
  32. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +142 -56
  33. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
  34. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +0 -9
  35. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +6 -91
  36. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
  37. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +2 -0
  38. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +162 -69
  39. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
  40. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +85 -41
  41. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  42. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +15 -7
  43. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  44. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +16 -25
  45. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
  46. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +9 -13
  47. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -1
  48. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +9 -15
  49. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -1
  50. package/dist-client/pages/sv-project-complete.js +7 -11
  51. package/dist-client/pages/sv-project-complete.js.map +1 -1
  52. package/dist-client/pages/sv-project-completed-list.js +4 -4
  53. package/dist-client/pages/sv-project-completed-list.js.map +1 -1
  54. package/dist-client/pages/sv-project-detail.d.ts +1 -0
  55. package/dist-client/pages/sv-project-detail.js +73 -55
  56. package/dist-client/pages/sv-project-detail.js.map +1 -1
  57. package/dist-client/pages/sv-project-list.js +15 -12
  58. package/dist-client/pages/sv-project-list.js.map +1 -1
  59. package/dist-client/tsconfig.tsbuildinfo +1 -1
  60. package/dist-client/viewparts/menu-tools.js +17 -3
  61. package/dist-client/viewparts/menu-tools.js.map +1 -1
  62. package/dist-server/service/kpi-stat/kpi-stat-query.js +20 -19
  63. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
  64. package/dist-server/tsconfig.tsbuildinfo +1 -1
  65. package/openapi/unstable.yaml +2 -2
  66. package/package.json +3 -3
  67. package/schema.graphql +20 -0
  68. package/translations/ko.json +2 -2
  69. package/views/auth-page.html +4 -4
  70. package/views/public/home.html +5 -5
@@ -44,40 +44,72 @@ let SvKpiMiniTrendChart = class SvKpiMiniTrendChart extends LitElement {
44
44
  .range([0, width]);
45
45
  const yScale = d3
46
46
  .scaleLinear()
47
- .domain([0, d3.max(this.data) || 100])
47
+ .domain([0, d3.max(this.data, d => (d == null ? undefined : d)) || 100])
48
48
  .range([height, 0]);
49
49
  // SVG 설정
50
50
  svg.attr('width', this.width).attr('height', this.height);
51
51
  const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
52
- // 라인 생성기
52
+ // 라인 생성기(실선) — defined() 로 데이터 없는(null) 월에서 끊김
53
53
  const line = d3
54
54
  .line()
55
+ .defined(d => d != null)
55
56
  .x((d, i) => xScale(i))
56
57
  .y(d => yScale(d))
57
58
  .curve(d3.curveMonotoneX);
58
- // 트렌드 라인 그리기
59
+ // 트렌드 라인(실선) — 연속 구간만
59
60
  g.append('path')
60
61
  .datum(this.data)
61
62
  .attr('class', 'trend-line')
62
63
  .attr('d', line)
63
64
  .attr('stroke', this.lineColor)
64
- .attr('stroke-width', this.strokeWidth);
65
- // 데이터 포인트 그리기 ( 번째와 마지막 포인트만)
66
- if (this.showPoints && this.data.length > 0) {
67
- // 번째 포인트
65
+ .attr('stroke-width', this.strokeWidth)
66
+ .attr('fill', 'none');
67
+ // 데이터 있는 월(인덱스 보존)
68
+ const present = this.data.map((v, i) => ({ v, i })).filter(p => p.v != null);
69
+ if (present.length === 0)
70
+ return;
71
+ // 결손 구간 점선 다리 — 사이에 데이터 없는 월이 낀 두 점을 점선 연결
72
+ for (let k = 1; k < present.length; k++) {
73
+ const a = present[k - 1];
74
+ const b = present[k];
75
+ if (b.i - a.i > 1) {
76
+ g.append('line')
77
+ .attr('x1', xScale(a.i))
78
+ .attr('y1', yScale(a.v))
79
+ .attr('x2', xScale(b.i))
80
+ .attr('y2', yScale(b.v))
81
+ .attr('stroke', this.lineColor)
82
+ .attr('stroke-width', this.strokeWidth)
83
+ .attr('stroke-dasharray', '3,2');
84
+ }
85
+ }
86
+ // 뒷부분(끝) 결손 → 마지막 데이터값에서 끝까지 같은 값(수평) 점선 연장
87
+ const lastP = present[present.length - 1];
88
+ if (lastP.i < this.data.length - 1) {
89
+ g.append('line')
90
+ .attr('x1', xScale(lastP.i))
91
+ .attr('y1', yScale(lastP.v))
92
+ .attr('x2', xScale(this.data.length - 1))
93
+ .attr('y2', yScale(lastP.v))
94
+ .attr('stroke', this.lineColor)
95
+ .attr('stroke-width', this.strokeWidth)
96
+ .attr('stroke-dasharray', '3,2');
97
+ }
98
+ // 데이터 포인트 — 데이터 있는 첫/마지막 월만
99
+ if (this.showPoints) {
100
+ const firstP = present[0];
68
101
  g.append('circle')
69
102
  .attr('class', 'data-point')
70
- .attr('cx', xScale(0))
71
- .attr('cy', yScale(this.data[0]))
103
+ .attr('cx', xScale(firstP.i))
104
+ .attr('cy', yScale(firstP.v))
72
105
  .attr('r', this.pointRadius)
73
106
  .attr('stroke', '#4caf50')
74
107
  .attr('fill', 'white');
75
- // 마지막 포인트
76
- if (this.data.length > 1) {
108
+ if (present.length > 1) {
77
109
  g.append('circle')
78
110
  .attr('class', 'data-point')
79
- .attr('cx', xScale(this.data.length - 1))
80
- .attr('cy', yScale(this.data[this.data.length - 1]))
111
+ .attr('cx', xScale(lastP.i))
112
+ .attr('cy', yScale(lastP.v))
81
113
  .attr('r', this.pointRadius)
82
114
  .attr('stroke', '#2196f3')
83
115
  .attr('fill', 'white');
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-mini-trend-chart.js","sourceRoot":"","sources":["../../client/components/kpi-mini-trend-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('sv-kpi-mini-trend-chart')\nexport class SvKpiMiniTrendChart extends LitElement {\n @property({ type: Array }) data: number[] = []\n @property({ type: Number }) width: number = 60\n @property({ type: Number }) height: number = 30\n @property({ type: String }) lineColor: string = '#2196f3'\n @property({ type: Number }) strokeWidth: number = 1.5\n @property({ type: Boolean }) showPoints: boolean = true\n @property({ type: Number }) pointRadius: number = 1.5\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .mini-chart {\n width: 100%;\n height: 100%;\n background: #f8f9fa;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .trend-line {\n fill: none;\n stroke-linecap: round;\n stroke-linejoin: round;\n }\n\n .data-point {\n fill: white;\n stroke-width: 1;\n }\n `\n\n render() {\n return html`\n <div class=\"mini-chart\">\n <svg\n id=\"mini-trend\"\n width=${this.width}\n height=${this.height}\n viewBox=\"0 0 ${this.width} ${this.height}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>\n </div>\n `\n }\n\n updated() {\n this.drawMiniTrend()\n }\n\n drawMiniTrend() {\n if (!this.data || this.data.length === 0) return\n\n const svg = d3.select(this.renderRoot.querySelector('#mini-trend'))\n svg.selectAll('*').remove()\n\n const margin = { top: 2, right: 2, bottom: 2, left: 2 }\n const width = this.width - margin.left - margin.right\n const height = this.height - margin.top - margin.bottom\n\n // 스케일 설정\n const xScale = d3\n .scaleLinear()\n .domain([0, this.data.length - 1])\n .range([0, width])\n\n const yScale = d3\n .scaleLinear()\n .domain([0, d3.max(this.data) || 100])\n .range([height, 0])\n\n // SVG 설정\n svg.attr('width', this.width).attr('height', this.height)\n const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)\n\n // 라인 생성기\n const line = d3\n .line<number>()\n .x((d, i) => xScale(i))\n .y(d => yScale(d))\n .curve(d3.curveMonotoneX)\n\n // 트렌드 라인 그리기\n g.append('path')\n .datum(this.data)\n .attr('class', 'trend-line')\n .attr('d', line as any)\n .attr('stroke', this.lineColor)\n .attr('stroke-width', this.strokeWidth)\n\n // 데이터 포인트 그리기 (첫 번째와 마지막 포인트만)\n if (this.showPoints && this.data.length > 0) {\n // 첫 번째 포인트\n g.append('circle')\n .attr('class', 'data-point')\n .attr('cx', xScale(0))\n .attr('cy', yScale(this.data[0]))\n .attr('r', this.pointRadius)\n .attr('stroke', '#4caf50')\n .attr('fill', 'white')\n\n // 마지막 포인트\n if (this.data.length > 1) {\n g.append('circle')\n .attr('class', 'data-point')\n .attr('cx', xScale(this.data.length - 1))\n .attr('cy', yScale(this.data[this.data.length - 1]))\n .attr('r', this.pointRadius)\n .attr('stroke', '#2196f3')\n .attr('fill', 'white')\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-mini-trend-chart.js","sourceRoot":"","sources":["../../client/components/kpi-mini-trend-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('sv-kpi-mini-trend-chart')\nexport class SvKpiMiniTrendChart extends LitElement {\n @property({ type: Array }) data: (number | null)[] = []\n @property({ type: Number }) width: number = 60\n @property({ type: Number }) height: number = 30\n @property({ type: String }) lineColor: string = '#2196f3'\n @property({ type: Number }) strokeWidth: number = 1.5\n @property({ type: Boolean }) showPoints: boolean = true\n @property({ type: Number }) pointRadius: number = 1.5\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .mini-chart {\n width: 100%;\n height: 100%;\n background: #f8f9fa;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .trend-line {\n fill: none;\n stroke-linecap: round;\n stroke-linejoin: round;\n }\n\n .data-point {\n fill: white;\n stroke-width: 1;\n }\n `\n\n render() {\n return html`\n <div class=\"mini-chart\">\n <svg\n id=\"mini-trend\"\n width=${this.width}\n height=${this.height}\n viewBox=\"0 0 ${this.width} ${this.height}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>\n </div>\n `\n }\n\n updated() {\n this.drawMiniTrend()\n }\n\n drawMiniTrend() {\n if (!this.data || this.data.length === 0) return\n\n const svg = d3.select(this.renderRoot.querySelector('#mini-trend'))\n svg.selectAll('*').remove()\n\n const margin = { top: 2, right: 2, bottom: 2, left: 2 }\n const width = this.width - margin.left - margin.right\n const height = this.height - margin.top - margin.bottom\n\n // 스케일 설정\n const xScale = d3\n .scaleLinear()\n .domain([0, this.data.length - 1])\n .range([0, width])\n\n const yScale = d3\n .scaleLinear()\n .domain([0, d3.max(this.data, d => (d == null ? undefined : d)) || 100])\n .range([height, 0])\n\n // SVG 설정\n svg.attr('width', this.width).attr('height', this.height)\n const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)\n\n // 라인 생성기(실선) — defined() 로 데이터 없는(null) 월에서 끊김\n const line = d3\n .line<number | null>()\n .defined(d => d != null)\n .x((d, i) => xScale(i))\n .y(d => yScale(d as number))\n .curve(d3.curveMonotoneX)\n\n // 트렌드 라인(실선) — 연속 구간만\n g.append('path')\n .datum(this.data)\n .attr('class', 'trend-line')\n .attr('d', line as any)\n .attr('stroke', this.lineColor)\n .attr('stroke-width', this.strokeWidth)\n .attr('fill', 'none')\n\n // 데이터 있는 월(인덱스 보존)\n const present = this.data.map((v, i) => ({ v, i })).filter(p => p.v != null)\n if (present.length === 0) return\n\n // 결손 구간 점선 다리 — 사이에 데이터 없는 월이 낀 두 점을 점선 연결\n for (let k = 1; k < present.length; k++) {\n const a = present[k - 1]\n const b = present[k]\n if (b.i - a.i > 1) {\n g.append('line')\n .attr('x1', xScale(a.i))\n .attr('y1', yScale(a.v as number))\n .attr('x2', xScale(b.i))\n .attr('y2', yScale(b.v as number))\n .attr('stroke', this.lineColor)\n .attr('stroke-width', this.strokeWidth)\n .attr('stroke-dasharray', '3,2')\n }\n }\n\n // 뒷부분(끝) 결손 → 마지막 데이터값에서 끝까지 같은 값(수평) 점선 연장\n const lastP = present[present.length - 1]\n if (lastP.i < this.data.length - 1) {\n g.append('line')\n .attr('x1', xScale(lastP.i))\n .attr('y1', yScale(lastP.v as number))\n .attr('x2', xScale(this.data.length - 1))\n .attr('y2', yScale(lastP.v as number))\n .attr('stroke', this.lineColor)\n .attr('stroke-width', this.strokeWidth)\n .attr('stroke-dasharray', '3,2')\n }\n\n // 데이터 포인트 — 데이터 있는 첫/마지막 월만\n if (this.showPoints) {\n const firstP = present[0]\n g.append('circle')\n .attr('class', 'data-point')\n .attr('cx', xScale(firstP.i))\n .attr('cy', yScale(firstP.v as number))\n .attr('r', this.pointRadius)\n .attr('stroke', '#4caf50')\n .attr('fill', 'white')\n\n if (present.length > 1) {\n g.append('circle')\n .attr('class', 'data-point')\n .attr('cx', xScale(lastP.i))\n .attr('cy', yScale(lastP.v as number))\n .attr('r', this.pointRadius)\n .attr('stroke', '#2196f3')\n .attr('fill', 'white')\n }\n }\n }\n}\n"]}
@@ -60,7 +60,7 @@ let SvKpiRadarChart = class SvKpiRadarChart extends LitElement {
60
60
  return { category: cat, value: found ? found[this.valueKey] : 0 };
61
61
  })
62
62
  }));
63
- // 레전드를 위한 공간 확보 (org가 2개 이상일 때만)
63
+ // 레전드를 위한 공간 확보 (org가 2개 이상일 때만 — 단일 시리즈면 범례 불필요)
64
64
  const legendWidth = orgData.length > 1 ? 120 : 0;
65
65
  const chartWidth = w - legendWidth;
66
66
  const chartHeight = h;
@@ -180,7 +180,7 @@ let SvKpiRadarChart = class SvKpiRadarChart extends LitElement {
180
180
  .on('mouseleave', hideTip);
181
181
  });
182
182
  });
183
- // 레전드 추가 (org가 2개 이상일 때만)
183
+ // 레전드 추가 (org가 2개 이상일 때만 — 단일 시리즈에서는 자동 미표시)
184
184
  if (orgData.length > 1) {
185
185
  const legend = svg
186
186
  .append('g')
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-radar-chart.js","sourceRoot":"","sources":["../../client/components/kpi-radar-chart.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AAGjB,IAAM,eAAe,GAArB,MAAM,eAAgB,SAAQ,UAAU;IAAxC;;QACsB,SAAI,GAAU,EAAE,CAAA;QAChB,eAAU,GAAa,EAAE,CAAA;QACxB,aAAQ,GAAW,OAAO,CAAA;QAC1B,iBAAY,GAAW,EAAE,CAAA;QACzB,iBAAY,GAA2B;YACjE,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;SAClB,CAAA;QAeO,eAAU,GAAG,CAAC,CAAA;QACd,gBAAW,GAAG,CAAC,CAAA;IAqOzB,CAAC;IAlOC,MAAM;QACJ,OAAO,IAAI,CAAA;;cAED,IAAI,CAAC,UAAU;eACd,IAAI,CAAC,WAAW;qBACV,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW;;YAE5C,CAAA;IACV,CAAC;IAED,iBAAiB;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QACzB,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE;YACjD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAA;gBAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAA;gBAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAA;gBAC9B,IAAI,CAAC,aAAa,EAAE,CAAA;YACtB,CAAC;QACH,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,CAAC;IAED,oBAAoB;;QAClB,MAAA,IAAI,CAAC,cAAc,0CAAE,UAAU,EAAE,CAAA;QACjC,KAAK,CAAC,oBAAoB,EAAE,CAAA;IAC9B,CAAC;IAED,OAAO;QACL,IAAI,CAAC,SAAS,EAAE,CAAA;IAClB,CAAC;IAED,SAAS;QACP,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC9D,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAA;QAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,IAAI,GAAG,CAAA;QAChC,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,IAAI,GAAG,CAAA;QAEjC,qDAAqD;QACrD,MAAM,OAAO,GAAG,EAAE;aACf,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;aAC7B,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,GAAG;YACH,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;gBACrC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAA;gBAClD,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;YACnE,CAAC,CAAC;SACH,CAAC,CAAC,CAAA;QAEL,iCAAiC;QACjC,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QAChD,MAAM,UAAU,GAAG,CAAC,GAAG,WAAW,CAAA;QAClC,MAAM,WAAW,GAAG,CAAC,CAAA;QACrB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,CAAA;QACpD,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC,CAAA;QAEhE,sCAAsC;QACtC,MAAM,YAAY,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAA;QAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA,CAAC,cAAc;QAC3D,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAEnE,SAAS;QACT,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;QACtC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,UAAU,GAAG,CAAC,IAAI,WAAW,GAAG,CAAC,GAAG,CAAC,CAAA;QAE9F,QAAQ;QACR,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;YAC9B,MAAM,SAAS,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;YAEpC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YAEpF,oCAAoC;YACpC,mCAAmC;YACnC,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;YACxC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;iBACb,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;iBAC5C,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;iBAC5C,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC;iBAC7B,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC;iBACpC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;iBACrB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;iBACpB,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC;iBAC1B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;QAC/B,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YACjC,MAAM,KAAK,GAAG,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAA;YAC1C,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;iBACb,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;iBACb,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;iBACb,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;iBAC9C,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;iBAC9C,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YACzB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;iBACb,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;iBACpD,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;iBACpD,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC;iBAC7B,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC;iBACpC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;iBACrB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;iBACpB,IAAI,CAAC,GAAG,CAAC,CAAA;QACd,CAAC,CAAC,CAAA;QAEF,wCAAwC;QACxC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC1C,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM;gBAAE,OAAO,CAAC,CAAC,CAAA;YAC/B,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM;gBAAE,OAAO,CAAC,CAAA;YAC9B,OAAO,CAAC,CAAA;QACV,CAAC,CAAC,CAAA;QAEF,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;YACzB,qBAAqB;YACrB,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;YACjD,MAAM,IAAI,GAAG,EAAE;iBACZ,UAAU,EAAE;iBACZ,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;iBACnC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,CAAA;YAElC,cAAc;YACd,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,MAAM,CAAA;YACrD,MAAM,SAAS,GACb,EAAE,CAAC,GAAG,KAAK,MAAM;gBACf,CAAC,CAAC,qBAAqB,CAAC,WAAW;gBACnC,CAAC,CAAC,sBAAsB,CAAA,CAAC,YAAY;YACzC,MAAM,WAAW,GACf,EAAE,CAAC,GAAG,KAAK,MAAM;gBACf,CAAC,CAAC,SAAS,CAAC,UAAU;gBACtB,CAAC,CAAC,SAAS,CAAA,CAAC,WAAW;YAC3B,MAAM,WAAW,GAAG,EAAE,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;YAEnD,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;iBACb,KAAK,CAAC,YAAY,CAAC;iBACnB,IAAI,CAAC,GAAG,EAAE,IAAW,CAAC;iBACtB,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC;iBACvB,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC;iBAC3B,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC;iBACjC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAA,CAAC,aAAa;QACnD,CAAC,CAAC,CAAA;QAEF,KAAK;QACL,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;QAC3F,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;QACrF,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;QAErE,MAAM,OAAO,GAAG,CAAC,GAAe,EAAE,KAAe,EAAE,EAAE;YACnD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YACtC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAA;YACnC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBACxB,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC/E,CAAC,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAA;YAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG,EAAE,CAAA;YACnC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;YAC5C,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YAC3G,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;YACnE,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,KAAK,EAAE,CAAA;QAC/C,CAAC,CAAA;QACD,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;QAE1D,aAAa;QACb,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;YACzB,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACzB,MAAM,KAAK,GAAG,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAA;gBAC1C,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;gBACnC,MAAM,EAAE,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBACxC,MAAM,EAAE,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAExC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;qBACf,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;qBACd,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;qBACd,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;qBACZ,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;qBACvD,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC;qBACtB,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;qBACvB,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC;qBAC1B,EAAE,CAAC,YAAY,EAAE,CAAC,GAAe,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;oBAClD,GAAG,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,GAAG,EAAE;oBAC3B,MAAM,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;iBACnC,CAAC,CAAC;qBACF,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;YAC9B,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEF,0BAA0B;QAC1B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,GAAG;iBACf,MAAM,CAAC,GAAG,CAAC;iBACX,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC;iBACvB,IAAI,CAAC,WAAW,EAAE,aAAa,UAAU,GAAG,EAAE,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;YAE9E,MAAM,UAAU,GAAG;gBACjB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE;gBACzD,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE;aAC5D,CAAA;YAED,MAAM,WAAW,GAAG,MAAM;iBACvB,SAAS,CAAC,cAAc,CAAC;iBACzB,IAAI,CAAC,UAAU,CAAC;iBAChB,KAAK,EAAE;iBACP,MAAM,CAAC,GAAG,CAAC;iBACX,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;iBAC5B,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;YAEzD,mBAAmB;YACnB,WAAW;iBACR,MAAM,CAAC,MAAM,CAAC;iBACd,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;iBACZ,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;iBACZ,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;iBACjB,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;iBAClB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;iBAC1B,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC;iBACpB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;iBAC5B,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;YAE5B,UAAU;YACV,WAAW;iBACR,MAAM,CAAC,MAAM,CAAC;iBACd,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;iBACb,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;iBACZ,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC;iBACzB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;iBACpB,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC;iBACpC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QACvB,CAAC;IACH,CAAC;;AAlPM,sBAAM,GAAG,GAAG,CAAA;;;;;;;;;;;GAWlB,AAXY,CAWZ;AApB0B;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;6CAAiB;AAChB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;mDAA0B;AACxB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;iDAA2B;AAC1B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;qDAA0B;AACzB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;qDAG1B;AARU,eAAe;IAD3B,aAAa,CAAC,oBAAoB,CAAC;GACvB,eAAe,CA6P3B","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('sv-kpi-radar-chart')\nexport class SvKpiRadarChart extends LitElement {\n @property({ type: Array }) data: any[] = []\n @property({ type: Array }) categories: string[] = []\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) currentGroup: string = ''\n @property({ type: Object }) seriesColors: Record<string, string> = {\n 기준평균: '#E3F2FD',\n 프로젝트성과: '#2196F3'\n }\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`<svg\n id=\"radar\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>`\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawRadar()\n }\n\n drawRadar() {\n const svg = d3.select(this.renderRoot.querySelector('#radar'))\n svg.selectAll('*').remove()\n const w = this.chartWidth || 300\n const h = this.chartHeight || 300\n\n // 데이터 변환: { org, values: [ {category, value} ... ] }\n const orgData = d3\n .groups(this.data, d => d.org)\n .map(([org, values]) => ({\n org,\n values: this.categories.map((cat, i) => {\n const found = values.find(v => v.category === cat)\n return { category: cat, value: found ? found[this.valueKey] : 0 }\n })\n }))\n\n // 레전드를 위한 공간 확보 (org가 2개 이상일 때만)\n const legendWidth = orgData.length > 1 ? 120 : 0\n const chartWidth = w - legendWidth\n const chartHeight = h\n const r = Math.min(chartWidth, chartHeight) / 2 - 40\n const angleSlice = (2 * Math.PI) / (this.categories.length || 1)\n\n // 스케일 - 0-100 고정 범위 사용 (박스플롯과 동일한 범위)\n const dataMaxValue = d3.max(this.data, d => d[this.valueKey]) || 1\n const maxValue = Math.max(100, dataMaxValue) // 최소 100까지 표시\n const radius = d3.scaleLinear().domain([0, maxValue]).range([0, r])\n\n // SVG 기본\n svg.attr('width', w).attr('height', h)\n const g = svg.append('g').attr('transform', `translate(${chartWidth / 2},${chartHeight / 2})`)\n\n // 그리드/축\n for (let i = 1; i <= 5; i++) {\n const gridRadius = (r / 5) * i\n const gridValue = (maxValue / 5) * i\n\n g.append('circle').attr('r', gridRadius).attr('fill', 'none').attr('stroke', '#ccc')\n\n // 그리드 값 라벨 (30도 시계방향 = Y1에서 30도 회전)\n // 차트가 위쪽에서 시작하므로 -90도 + 30도 = -60도\n const labelAngle = (-60 * Math.PI) / 180\n g.append('text')\n .attr('x', gridRadius * Math.cos(labelAngle))\n .attr('y', gridRadius * Math.sin(labelAngle))\n .attr('text-anchor', 'middle')\n .attr('alignment-baseline', 'middle')\n .attr('font-size', 10)\n .attr('fill', '#999')\n .attr('background', '#fff')\n .text(gridValue.toFixed(0))\n }\n this.categories.forEach((cat, i) => {\n const angle = i * angleSlice - Math.PI / 2\n g.append('line')\n .attr('x1', 0)\n .attr('y1', 0)\n .attr('x2', radius(maxValue) * Math.cos(angle))\n .attr('y2', radius(maxValue) * Math.sin(angle))\n .attr('stroke', '#ccc')\n g.append('text')\n .attr('x', (radius(maxValue) + 10) * Math.cos(angle))\n .attr('y', (radius(maxValue) + 10) * Math.sin(angle))\n .attr('text-anchor', 'middle')\n .attr('alignment-baseline', 'middle')\n .attr('font-size', 12)\n .attr('fill', '#333')\n .text(cat)\n })\n\n // 그룹별 폴리곤 - 기준평균을 먼저 그리고 프로젝트성과를 위에 그리기\n const sortedOrgData = orgData.sort((a, b) => {\n if (a.org === '기준평균') return -1\n if (b.org === '기준평균') return 1\n return 0\n })\n\n sortedOrgData.forEach(gd => {\n // 마지막에 첫 점을 한 번 더 추가\n const closedValues = [...gd.values, gd.values[0]]\n const line = d3\n .lineRadial()\n .radius((d: any) => radius(d.value))\n .angle((d, i) => i * angleSlice)\n\n // 색상 및 스타일 결정\n const baseColor = this.seriesColors[gd.org] || '#ccc'\n const fillColor =\n gd.org === '기준평균'\n ? 'rgba(76,175,80,0.4)' // 진한 녹색 배경\n : 'rgba(33,150,243,0.4)' // 진한 파란색 배경\n const strokeColor =\n gd.org === '기준평균'\n ? '#4CAF50' // 진한 녹색 선\n : '#2196F3' // 진한 파란색 선\n const strokeWidth = gd.org === '프로젝트성과' ? 1.5 : 1.5\n\n g.append('path')\n .datum(closedValues)\n .attr('d', line as any)\n .attr('fill', fillColor)\n .attr('stroke', strokeColor)\n .attr('stroke-width', strokeWidth)\n .attr('stroke-dasharray', 'none') // 모두 직선으로 변경\n })\n\n // 툴팁\n const tooltip = svg.append('g').attr('class', 'radar-tooltip').attr('visibility', 'hidden')\n tooltip.append('rect').attr('rx', 4).attr('ry', 4).attr('fill', 'rgba(33,33,33,0.9)')\n tooltip.append('text').attr('fill', '#fff').attr('font-size', '10px')\n\n const showTip = (evt: MouseEvent, lines: string[]) => {\n const tipText = tooltip.select('text')\n tipText.selectAll('tspan').remove()\n lines.forEach((line, i) => {\n tipText.append('tspan').attr('x', 8).attr('dy', i === 0 ? 14 : 13).text(line)\n })\n const tipW = Math.max(...lines.map(l => l.length)) * 8 + 24\n const tipH = lines.length * 13 + 10\n const [mx, my] = d3.pointer(evt, svg.node())\n tooltip.select('rect').attr('x', mx + 10).attr('y', my - tipH - 5).attr('width', tipW).attr('height', tipH)\n tipText.attr('transform', `translate(${mx + 10},${my - tipH - 5})`)\n tooltip.attr('visibility', 'visible').raise()\n }\n const hideTip = () => tooltip.attr('visibility', 'hidden')\n\n // 데이터 포인트 표시\n sortedOrgData.forEach(gd => {\n gd.values.forEach((d, i) => {\n const angle = i * angleSlice - Math.PI / 2\n const pointRadius = radius(d.value)\n const px = pointRadius * Math.cos(angle)\n const py = pointRadius * Math.sin(angle)\n\n g.append('circle')\n .attr('cx', px)\n .attr('cy', py)\n .attr('r', 3)\n .attr('fill', gd.org === '기준평균' ? '#4CAF50' : '#2196F3')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `${d.category} — ${gd.org}`,\n `값: ${Number(d.value).toFixed(2)}`\n ]))\n .on('mouseleave', hideTip)\n })\n })\n\n // 레전드 추가 (org가 2개 이상일 때만)\n if (orgData.length > 1) {\n const legend = svg\n .append('g')\n .attr('class', 'legend')\n .attr('transform', `translate(${chartWidth + 20}, ${chartHeight / 2 - 30})`)\n\n const legendData = [\n { label: '기준평균값', color: '#4CAF50', strokeStyle: 'none' },\n { label: '프로젝트 성과', color: '#2196F3', strokeStyle: 'none' }\n ]\n\n const legendItems = legend\n .selectAll('.legend-item')\n .data(legendData)\n .enter()\n .append('g')\n .attr('class', 'legend-item')\n .attr('transform', (d, i) => `translate(0, ${i * 25})`)\n\n // 레전드 아이콘 (작은 사각형)\n legendItems\n .append('rect')\n .attr('x', 0)\n .attr('y', 0)\n .attr('width', 12)\n .attr('height', 12)\n .attr('fill', d => d.color)\n .attr('opacity', 0.6)\n .attr('stroke', d => d.color)\n .attr('stroke-width', 1.5)\n\n // 레전드 텍스트\n legendItems\n .append('text')\n .attr('x', 18)\n .attr('y', 9)\n .attr('font-size', '12px')\n .attr('fill', '#333')\n .attr('alignment-baseline', 'middle')\n .text(d => d.label)\n }\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-radar-chart.js","sourceRoot":"","sources":["../../client/components/kpi-radar-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('sv-kpi-radar-chart')\nexport class SvKpiRadarChart extends LitElement {\n @property({ type: Array }) data: any[] = []\n @property({ type: Array }) categories: string[] = []\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) currentGroup: string = ''\n @property({ type: Object }) seriesColors: Record<string, string> = {\n 기준평균: '#E3F2FD',\n 프로젝트성과: '#2196F3'\n }\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`<svg\n id=\"radar\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>`\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawRadar()\n }\n\n drawRadar() {\n const svg = d3.select(this.renderRoot.querySelector('#radar'))\n svg.selectAll('*').remove()\n const w = this.chartWidth || 300\n const h = this.chartHeight || 300\n\n // 데이터 변환: { org, values: [ {category, value} ... ] }\n const orgData = d3\n .groups(this.data, d => d.org)\n .map(([org, values]) => ({\n org,\n values: this.categories.map((cat, i) => {\n const found = values.find(v => v.category === cat)\n return { category: cat, value: found ? found[this.valueKey] : 0 }\n })\n }))\n\n // 레전드를 위한 공간 확보 (org가 2개 이상일 때만 — 단일 시리즈면 범례 불필요)\n const legendWidth = orgData.length > 1 ? 120 : 0\n const chartWidth = w - legendWidth\n const chartHeight = h\n const r = Math.min(chartWidth, chartHeight) / 2 - 40\n const angleSlice = (2 * Math.PI) / (this.categories.length || 1)\n\n // 스케일 - 0-100 고정 범위 사용 (박스플롯과 동일한 범위)\n const dataMaxValue = d3.max(this.data, d => d[this.valueKey]) || 1\n const maxValue = Math.max(100, dataMaxValue) // 최소 100까지 표시\n const radius = d3.scaleLinear().domain([0, maxValue]).range([0, r])\n\n // SVG 기본\n svg.attr('width', w).attr('height', h)\n const g = svg.append('g').attr('transform', `translate(${chartWidth / 2},${chartHeight / 2})`)\n\n // 그리드/축\n for (let i = 1; i <= 5; i++) {\n const gridRadius = (r / 5) * i\n const gridValue = (maxValue / 5) * i\n\n g.append('circle').attr('r', gridRadius).attr('fill', 'none').attr('stroke', '#ccc')\n\n // 그리드 값 라벨 (30도 시계방향 = Y1에서 30도 회전)\n // 차트가 위쪽에서 시작하므로 -90도 + 30도 = -60도\n const labelAngle = (-60 * Math.PI) / 180\n g.append('text')\n .attr('x', gridRadius * Math.cos(labelAngle))\n .attr('y', gridRadius * Math.sin(labelAngle))\n .attr('text-anchor', 'middle')\n .attr('alignment-baseline', 'middle')\n .attr('font-size', 10)\n .attr('fill', '#999')\n .attr('background', '#fff')\n .text(gridValue.toFixed(0))\n }\n this.categories.forEach((cat, i) => {\n const angle = i * angleSlice - Math.PI / 2\n g.append('line')\n .attr('x1', 0)\n .attr('y1', 0)\n .attr('x2', radius(maxValue) * Math.cos(angle))\n .attr('y2', radius(maxValue) * Math.sin(angle))\n .attr('stroke', '#ccc')\n g.append('text')\n .attr('x', (radius(maxValue) + 10) * Math.cos(angle))\n .attr('y', (radius(maxValue) + 10) * Math.sin(angle))\n .attr('text-anchor', 'middle')\n .attr('alignment-baseline', 'middle')\n .attr('font-size', 12)\n .attr('fill', '#333')\n .text(cat)\n })\n\n // 그룹별 폴리곤 - 기준평균을 먼저 그리고 프로젝트성과를 위에 그리기\n const sortedOrgData = orgData.sort((a, b) => {\n if (a.org === '기준평균') return -1\n if (b.org === '기준평균') return 1\n return 0\n })\n\n sortedOrgData.forEach(gd => {\n // 마지막에 첫 점을 한 번 더 추가\n const closedValues = [...gd.values, gd.values[0]]\n const line = d3\n .lineRadial()\n .radius((d: any) => radius(d.value))\n .angle((d, i) => i * angleSlice)\n\n // 색상 및 스타일 결정\n const baseColor = this.seriesColors[gd.org] || '#ccc'\n const fillColor =\n gd.org === '기준평균'\n ? 'rgba(76,175,80,0.4)' // 진한 녹색 배경\n : 'rgba(33,150,243,0.4)' // 진한 파란색 배경\n const strokeColor =\n gd.org === '기준평균'\n ? '#4CAF50' // 진한 녹색 선\n : '#2196F3' // 진한 파란색 선\n const strokeWidth = gd.org === '프로젝트성과' ? 1.5 : 1.5\n\n g.append('path')\n .datum(closedValues)\n .attr('d', line as any)\n .attr('fill', fillColor)\n .attr('stroke', strokeColor)\n .attr('stroke-width', strokeWidth)\n .attr('stroke-dasharray', 'none') // 모두 직선으로 변경\n })\n\n // 툴팁\n const tooltip = svg.append('g').attr('class', 'radar-tooltip').attr('visibility', 'hidden')\n tooltip.append('rect').attr('rx', 4).attr('ry', 4).attr('fill', 'rgba(33,33,33,0.9)')\n tooltip.append('text').attr('fill', '#fff').attr('font-size', '10px')\n\n const showTip = (evt: MouseEvent, lines: string[]) => {\n const tipText = tooltip.select('text')\n tipText.selectAll('tspan').remove()\n lines.forEach((line, i) => {\n tipText.append('tspan').attr('x', 8).attr('dy', i === 0 ? 14 : 13).text(line)\n })\n const tipW = Math.max(...lines.map(l => l.length)) * 8 + 24\n const tipH = lines.length * 13 + 10\n const [mx, my] = d3.pointer(evt, svg.node())\n tooltip.select('rect').attr('x', mx + 10).attr('y', my - tipH - 5).attr('width', tipW).attr('height', tipH)\n tipText.attr('transform', `translate(${mx + 10},${my - tipH - 5})`)\n tooltip.attr('visibility', 'visible').raise()\n }\n const hideTip = () => tooltip.attr('visibility', 'hidden')\n\n // 데이터 포인트 표시\n sortedOrgData.forEach(gd => {\n gd.values.forEach((d, i) => {\n const angle = i * angleSlice - Math.PI / 2\n const pointRadius = radius(d.value)\n const px = pointRadius * Math.cos(angle)\n const py = pointRadius * Math.sin(angle)\n\n g.append('circle')\n .attr('cx', px)\n .attr('cy', py)\n .attr('r', 3)\n .attr('fill', gd.org === '기준평균' ? '#4CAF50' : '#2196F3')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `${d.category} — ${gd.org}`,\n `값: ${Number(d.value).toFixed(2)}`\n ]))\n .on('mouseleave', hideTip)\n })\n })\n\n // 레전드 추가 (org가 2개 이상일 때만 — 단일 시리즈에서는 자동 미표시)\n if (orgData.length > 1) {\n const legend = svg\n .append('g')\n .attr('class', 'legend')\n .attr('transform', `translate(${chartWidth + 20}, ${chartHeight / 2 - 30})`)\n\n const legendData = [\n { label: '기준평균값', color: '#4CAF50', strokeStyle: 'none' },\n { label: '프로젝트 성과', color: '#2196F3', strokeStyle: 'none' }\n ]\n\n const legendItems = legend\n .selectAll('.legend-item')\n .data(legendData)\n .enter()\n .append('g')\n .attr('class', 'legend-item')\n .attr('transform', (d, i) => `translate(0, ${i * 25})`)\n\n // 레전드 아이콘 (작은 사각형)\n legendItems\n .append('rect')\n .attr('x', 0)\n .attr('y', 0)\n .attr('width', 12)\n .attr('height', 12)\n .attr('fill', d => d.color)\n .attr('opacity', 0.6)\n .attr('stroke', d => d.color)\n .attr('stroke-width', 1.5)\n\n // 레전드 텍스트\n legendItems\n .append('text')\n .attr('x', 18)\n .attr('y', 9)\n .attr('font-size', '12px')\n .attr('fill', '#333')\n .attr('alignment-baseline', 'middle')\n .text(d => d.label)\n }\n }\n}\n"]}
@@ -10,6 +10,17 @@ export declare class KpiSingleBoxplotChart extends LitElement {
10
10
  valueKey: string;
11
11
  color: string;
12
12
  vertical: boolean;
13
+ /**
14
+ * y축 스케일 모드:
15
+ * - fitToData=true(기본): 각 구성값의 min/max(수염 양끝)로 전체 높이를 채움(자동맞춤).
16
+ * 범위 밖 현재값은 clamp 되어 끝에 표시(도메인을 늘리지 않음). → 고객 선호 기본 동작.
17
+ * - fitToData=false: domainMin~domainMax(기본 0~100) 고정 스케일.
18
+ */
19
+ fitToData: boolean;
20
+ domainMin: number;
21
+ domainMax: number;
22
+ /** 현재 모드에 맞는 [min, max] 도메인 계산 */
23
+ private computeDomain;
13
24
  static styles: import("lit").CSSResult;
14
25
  /** 현재 host 크기 (ResizeObserver 가 채움). viewBox 를 이 값으로 동적 설정 → 여백 없이 풀 채움. */
15
26
  private vbW;
@@ -15,10 +15,30 @@ let KpiSingleBoxplotChart = class KpiSingleBoxplotChart extends LitElement {
15
15
  this.valueKey = 'value';
16
16
  this.color = '#2196f3';
17
17
  this.vertical = true; // true: 세로형, false: 가로형
18
+ /**
19
+ * y축 스케일 모드:
20
+ * - fitToData=true(기본): 각 구성값의 min/max(수염 양끝)로 전체 높이를 채움(자동맞춤).
21
+ * 범위 밖 현재값은 clamp 되어 끝에 표시(도메인을 늘리지 않음). → 고객 선호 기본 동작.
22
+ * - fitToData=false: domainMin~domainMax(기본 0~100) 고정 스케일.
23
+ */
24
+ this.fitToData = true;
25
+ this.domainMin = 0;
26
+ this.domainMax = 100;
18
27
  /** 현재 host 크기 (ResizeObserver 가 채움). viewBox 를 이 값으로 동적 설정 → 여백 없이 풀 채움. */
19
28
  this.vbW = 200;
20
29
  this.vbH = 200;
21
30
  }
31
+ /** 현재 모드에 맞는 [min, max] 도메인 계산 */
32
+ computeDomain() {
33
+ var _a, _b;
34
+ if (this.fitToData) {
35
+ const lo = Number((_a = this.data) === null || _a === void 0 ? void 0 : _a[this.minKey]);
36
+ const hi = Number((_b = this.data) === null || _b === void 0 ? void 0 : _b[this.maxKey]);
37
+ if (isFinite(lo) && isFinite(hi) && hi > lo)
38
+ return [lo, hi];
39
+ }
40
+ return [this.domainMin, this.domainMax];
41
+ }
22
42
  render() {
23
43
  // 초기 viewBox 는 200x200. ResizeObserver 가 컨테이너 크기로 갱신.
24
44
  return html `
@@ -113,19 +133,13 @@ let KpiSingleBoxplotChart = class KpiSingleBoxplotChart extends LitElement {
113
133
  }
114
134
  }
115
135
  drawVerticalBoxplot(g, plotW, plotH, showTip, hideTip) {
116
- // 박스플롯 도메인은 min/max 통계 범위로 유지
136
+ // 스케일 모드(computeDomain): 기본은 데이터 min/max 자동맞춤, 옵션으로 0~100 고정.
137
+ // clamp 로 도메인 밖 현재값은 끝에 고정(도메인 확장 안 함).
117
138
  const y = d3
118
139
  .scaleLinear()
119
- .domain([this.data[this.minKey], this.data[this.maxKey]])
120
- .range([plotH, 0]);
121
- // 현재값이 범위를 벗어나면 도메인 확장
122
- if (this.data[this.valueKey] != null) {
123
- const currentValue = this.data[this.valueKey];
124
- if (currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]) {
125
- const allValues = [this.data[this.minKey], this.data[this.maxKey], currentValue];
126
- y.domain([Math.min(...allValues), Math.max(...allValues)]);
127
- }
128
- }
140
+ .domain(this.computeDomain())
141
+ .range([plotH, 0])
142
+ .clamp(true);
129
143
  const boxWidth = Math.min(plotW * 0.6, 80);
130
144
  const centerX = plotW / 2;
131
145
  // 박스 (Q1 ~ Q3)
@@ -210,8 +224,8 @@ let KpiSingleBoxplotChart = class KpiSingleBoxplotChart extends LitElement {
210
224
  g.append('circle')
211
225
  .attr('cx', centerX)
212
226
  .attr('cy', y(currentValue))
213
- .attr('r', 3)
214
- .attr('fill', isOutlier ? '#ff4444' : '#ff9800')
227
+ .attr('r', 5)
228
+ .attr('fill', '#e53935')
215
229
  .attr('stroke', '#fff')
216
230
  .attr('stroke-width', 1)
217
231
  .style('cursor', 'pointer')
@@ -223,19 +237,12 @@ let KpiSingleBoxplotChart = class KpiSingleBoxplotChart extends LitElement {
223
237
  }
224
238
  }
225
239
  drawHorizontalBoxplot(g, plotW, plotH, showTip, hideTip) {
226
- // 박스플롯 도메인은 min/max 통계 범위로 유지
240
+ // 스케일 모드(computeDomain): 기본은 데이터 min/max 자동맞춤, 옵션으로 0~100 고정.
227
241
  const x = d3
228
242
  .scaleLinear()
229
- .domain([this.data[this.minKey], this.data[this.maxKey]])
230
- .range([0, plotW]);
231
- // 현재값이 범위를 벗어나면 도메인 확장
232
- if (this.data[this.valueKey] != null) {
233
- const currentValue = this.data[this.valueKey];
234
- if (currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]) {
235
- const allValues = [this.data[this.minKey], this.data[this.maxKey], currentValue];
236
- x.domain([Math.min(...allValues), Math.max(...allValues)]);
237
- }
238
- }
243
+ .domain(this.computeDomain())
244
+ .range([0, plotW])
245
+ .clamp(true);
239
246
  const boxHeight = Math.min(plotH * 0.6, 80);
240
247
  const centerY = plotH / 2;
241
248
  // 박스 (Q1 ~ Q3)
@@ -320,8 +327,8 @@ let KpiSingleBoxplotChart = class KpiSingleBoxplotChart extends LitElement {
320
327
  g.append('circle')
321
328
  .attr('cx', x(currentValue))
322
329
  .attr('cy', centerY)
323
- .attr('r', 3)
324
- .attr('fill', isOutlier ? '#ff4444' : '#ff9800')
330
+ .attr('r', 5)
331
+ .attr('fill', '#e53935')
325
332
  .attr('stroke', '#fff')
326
333
  .attr('stroke-width', 1)
327
334
  .style('cursor', 'pointer')
@@ -391,6 +398,18 @@ __decorate([
391
398
  property({ type: Boolean }),
392
399
  __metadata("design:type", Boolean)
393
400
  ], KpiSingleBoxplotChart.prototype, "vertical", void 0);
401
+ __decorate([
402
+ property({ type: Boolean }),
403
+ __metadata("design:type", Boolean)
404
+ ], KpiSingleBoxplotChart.prototype, "fitToData", void 0);
405
+ __decorate([
406
+ property({ type: Number }),
407
+ __metadata("design:type", Number)
408
+ ], KpiSingleBoxplotChart.prototype, "domainMin", void 0);
409
+ __decorate([
410
+ property({ type: Number }),
411
+ __metadata("design:type", Number)
412
+ ], KpiSingleBoxplotChart.prototype, "domainMax", void 0);
394
413
  KpiSingleBoxplotChart = __decorate([
395
414
  customElement('kpi-single-boxplot-chart')
396
415
  ], KpiSingleBoxplotChart);
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-single-boxplot-chart.js","sourceRoot":"","sources":["../../client/components/kpi-single-boxplot-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('kpi-single-boxplot-chart')\nexport class KpiSingleBoxplotChart extends LitElement {\n @property({ type: Object }) data: any = {}\n @property({ type: String }) minKey: string = 'min'\n @property({ type: String }) maxKey: string = 'max'\n @property({ type: String }) meanKey: string = 'mean'\n @property({ type: String }) medianKey: string = 'median'\n @property({ type: String }) q1Key: string = 'q1'\n @property({ type: String }) q3Key: string = 'q3'\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) color: string = '#2196f3'\n @property({ type: Boolean }) vertical: boolean = true // true: 세로형, false: 가로형\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n /* flex/grid item 의 intrinsic min-content 가 부모를 밀어올리는 것 방지 */\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n /* 절대 늘어나지 않도록 contain — layout/size 격리 */\n contain: strict;\n }\n svg {\n display: block;\n width: 100%;\n height: 100%;\n }\n `\n\n /** 현재 host 크기 (ResizeObserver 가 채움). viewBox 를 이 값으로 동적 설정 → 여백 없이 풀 채움. */\n private vbW = 200\n private vbH = 200\n\n private resizeObserver?: ResizeObserver\n\n render() {\n // 초기 viewBox 는 200x200. ResizeObserver 가 컨테이너 크기로 갱신.\n return html`\n <svg id=\"single-boxplot\" viewBox=\"0 0 200 200\" preserveAspectRatio=\"xMidYMid meet\"></svg>\n `\n }\n\n connectedCallback() {\n super.connectedCallback()\n // host 크기 변경 시 viewBox 를 컨테이너 비율로 맞춰 D3 만 다시 그리기 (Lit requestUpdate X).\n this.resizeObserver = new ResizeObserver(entries => {\n const entry = entries[0]\n if (!entry) return\n const w = Math.max(1, entry.contentRect.width)\n const h = Math.max(1, entry.contentRect.height)\n if (w === this.vbW && h === this.vbH) return\n this.vbW = w\n this.vbH = h\n this.drawSingleBoxplot()\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawSingleBoxplot()\n }\n\n private drawSingleBoxplot() {\n const svg = d3.select(this.renderRoot.querySelector('#single-boxplot'))\n if (svg.empty()) return\n svg.selectAll('*').remove()\n\n // 데이터 검증\n if (!this.data || Object.keys(this.data).length === 0) {\n return\n }\n\n // viewBox 를 host 실제 크기로 — preserveAspectRatio 와 무관하게 풀 채움.\n const w = this.vbW\n const h = this.vbH\n svg.attr('viewBox', `0 0 ${w} ${h}`)\n const margin = 20\n const plotW = w - margin * 2\n const plotH = h - margin * 2\n\n // 필수 필드 검증\n const requiredFields = [this.minKey, this.maxKey, this.q1Key, this.q3Key, this.medianKey, this.meanKey]\n const missingFields = requiredFields.filter(field => this.data[field] == null)\n if (missingFields.length > 0) {\n console.warn('Missing required fields:', missingFields)\n return\n }\n\n const values = [\n this.data[this.minKey],\n this.data[this.maxKey],\n this.data[this.q1Key],\n this.data[this.q3Key],\n this.data[this.medianKey],\n this.data[this.meanKey]\n ]\n\n if (this.data[this.valueKey] != null) {\n values.push(this.data[this.valueKey])\n }\n\n const g = svg.append('g').attr('transform', `translate(${margin}, ${margin})`)\n\n // 툴팁\n const tooltip = svg.append('g').attr('class', 'sb-tooltip').attr('visibility', 'hidden')\n tooltip.append('rect').attr('rx', 4).attr('ry', 4).attr('fill', 'rgba(33,33,33,0.9)')\n tooltip.append('text').attr('fill', '#fff').attr('font-size', '10px')\n\n const showTip = (evt: MouseEvent, lines: string[]) => {\n const tipText = tooltip.select('text')\n tipText.selectAll('tspan').remove()\n lines.forEach((line, i) => {\n tipText.append('tspan').attr('x', 8).attr('dy', i === 0 ? 14 : 13).text(line)\n })\n const tipW = Math.max(...lines.map(l => l.length)) * 8 + 24\n const tipH = lines.length * 13 + 10\n const [mx, my] = d3.pointer(evt, svg.node())\n const tx = Math.min(mx + 10, w - tipW - 5)\n const ty = Math.max(my - tipH - 5, 5)\n tooltip.select('rect').attr('x', tx).attr('y', ty).attr('width', tipW).attr('height', tipH)\n tipText.attr('transform', `translate(${tx},${ty})`)\n tooltip.attr('visibility', 'visible').raise()\n }\n const hideTip = () => tooltip.attr('visibility', 'hidden')\n\n if (this.vertical) {\n this.drawVerticalBoxplot(g, plotW, plotH, showTip, hideTip)\n } else {\n this.drawHorizontalBoxplot(g, plotW, plotH, showTip, hideTip)\n }\n }\n\n private drawVerticalBoxplot(g: any, plotW: number, plotH: number, showTip: Function, hideTip: Function) {\n // 박스플롯 도메인은 min/max 통계 범위로 유지\n const y = d3\n .scaleLinear()\n .domain([this.data[this.minKey], this.data[this.maxKey]])\n .range([plotH, 0])\n\n // 현재값이 범위를 벗어나면 도메인 확장\n if (this.data[this.valueKey] != null) {\n const currentValue = this.data[this.valueKey]\n if (currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]) {\n const allValues = [this.data[this.minKey], this.data[this.maxKey], currentValue]\n y.domain([Math.min(...allValues), Math.max(...allValues)])\n }\n }\n\n const boxWidth = Math.min(plotW * 0.6, 80)\n const centerX = plotW / 2\n\n // 박스 (Q1 ~ Q3)\n const boxHeight = y(this.data[this.q1Key]) - y(this.data[this.q3Key])\n g.append('rect')\n .attr('x', centerX - boxWidth / 2)\n .attr('y', y(this.data[this.q3Key]))\n .attr('width', boxWidth)\n .attr('height', boxHeight)\n .attr('fill', '#B3D4FC')\n .attr('opacity', 0.8)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `최대: ${Number(this.data[this.maxKey]).toFixed(2)}`,\n `Q3: ${Number(this.data[this.q3Key]).toFixed(2)}`,\n `중앙값: ${Number(this.data[this.medianKey]).toFixed(2)}`,\n `Q1: ${Number(this.data[this.q1Key]).toFixed(2)}`,\n `최소: ${Number(this.data[this.minKey]).toFixed(2)}`,\n `평균: ${Number(this.data[this.meanKey]).toFixed(2)}`\n ]))\n .on('mouseleave', () => hideTip())\n\n // 중앙선 (Median)\n g.append('line')\n .attr('x1', centerX - boxWidth / 2)\n .attr('x2', centerX + boxWidth / 2)\n .attr('y1', y(this.data[this.medianKey]))\n .attr('y2', y(this.data[this.medianKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1.5)\n\n // 위쪽 수염 (Q3 ~ Max)\n g.append('line')\n .attr('x1', centerX)\n .attr('x2', centerX)\n .attr('y1', y(this.data[this.q3Key]))\n .attr('y2', y(this.data[this.maxKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 아래쪽 수염 (Min ~ Q1)\n g.append('line')\n .attr('x1', centerX)\n .attr('x2', centerX)\n .attr('y1', y(this.data[this.minKey]))\n .attr('y2', y(this.data[this.q1Key]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Max 캡\n const capWidth = boxWidth * 0.6\n g.append('line')\n .attr('x1', centerX - capWidth / 2)\n .attr('x2', centerX + capWidth / 2)\n .attr('y1', y(this.data[this.maxKey]))\n .attr('y2', y(this.data[this.maxKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Min 캡\n g.append('line')\n .attr('x1', centerX - capWidth / 2)\n .attr('x2', centerX + capWidth / 2)\n .attr('y1', y(this.data[this.minKey]))\n .attr('y2', y(this.data[this.minKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 평균값 (초록색)\n if (this.data[this.meanKey] != null) {\n g.append('circle')\n .attr('cx', centerX)\n .attr('cy', y(this.data[this.meanKey]))\n .attr('r', 3)\n .attr('fill', '#4CAF50')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [`평균: ${Number(this.data[this.meanKey]).toFixed(2)}`]))\n .on('mouseleave', () => hideTip())\n }\n\n // 현재값 (오렌지색)\n if (this.data[this.valueKey] != null) {\n const currentValue = this.data[this.valueKey]\n const isOutlier = currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]\n\n g.append('circle')\n .attr('cx', centerX)\n .attr('cy', y(currentValue))\n .attr('r', 3)\n .attr('fill', isOutlier ? '#ff4444' : '#ff9800')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `현재 프로젝트: ${Number(currentValue).toFixed(2)}`,\n isOutlier ? '⚠ 이상치' : '정상 범위'\n ]))\n .on('mouseleave', () => hideTip())\n }\n }\n\n private drawHorizontalBoxplot(g: any, plotW: number, plotH: number, showTip: Function, hideTip: Function) {\n // 박스플롯 도메인은 min/max 통계 범위로 유지\n const x = d3\n .scaleLinear()\n .domain([this.data[this.minKey], this.data[this.maxKey]])\n .range([0, plotW])\n\n // 현재값이 범위를 벗어나면 도메인 확장\n if (this.data[this.valueKey] != null) {\n const currentValue = this.data[this.valueKey]\n if (currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]) {\n const allValues = [this.data[this.minKey], this.data[this.maxKey], currentValue]\n x.domain([Math.min(...allValues), Math.max(...allValues)])\n }\n }\n\n const boxHeight = Math.min(plotH * 0.6, 80)\n const centerY = plotH / 2\n\n // 박스 (Q1 ~ Q3)\n const boxWidth = x(this.data[this.q3Key]) - x(this.data[this.q1Key])\n g.append('rect')\n .attr('x', x(this.data[this.q1Key]))\n .attr('y', centerY - boxHeight / 2)\n .attr('width', boxWidth)\n .attr('height', boxHeight)\n .attr('fill', '#B3D4FC')\n .attr('opacity', 0.8)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `최대: ${Number(this.data[this.maxKey]).toFixed(2)}`,\n `Q3: ${Number(this.data[this.q3Key]).toFixed(2)}`,\n `중앙값: ${Number(this.data[this.medianKey]).toFixed(2)}`,\n `Q1: ${Number(this.data[this.q1Key]).toFixed(2)}`,\n `최소: ${Number(this.data[this.minKey]).toFixed(2)}`,\n `평균: ${Number(this.data[this.meanKey]).toFixed(2)}`\n ]))\n .on('mouseleave', () => hideTip())\n\n // 중앙선 (Median)\n g.append('line')\n .attr('x1', x(this.data[this.medianKey]))\n .attr('x2', x(this.data[this.medianKey]))\n .attr('y1', centerY - boxHeight / 2)\n .attr('y2', centerY + boxHeight / 2)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1.5)\n\n // 왼쪽 수염 (Min ~ Q1)\n g.append('line')\n .attr('x1', x(this.data[this.minKey]))\n .attr('x2', x(this.data[this.q1Key]))\n .attr('y1', centerY)\n .attr('y2', centerY)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 오른쪽 수염 (Q3 ~ Max)\n g.append('line')\n .attr('x1', x(this.data[this.q3Key]))\n .attr('x2', x(this.data[this.maxKey]))\n .attr('y1', centerY)\n .attr('y2', centerY)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Min 캡\n const capHeight = boxHeight * 0.6\n g.append('line')\n .attr('x1', x(this.data[this.minKey]))\n .attr('x2', x(this.data[this.minKey]))\n .attr('y1', centerY - capHeight / 2)\n .attr('y2', centerY + capHeight / 2)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Max 캡\n g.append('line')\n .attr('x1', x(this.data[this.maxKey]))\n .attr('x2', x(this.data[this.maxKey]))\n .attr('y1', centerY - capHeight / 2)\n .attr('y2', centerY + capHeight / 2)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 평균값 (초록색)\n if (this.data[this.meanKey] != null) {\n g.append('circle')\n .attr('cx', x(this.data[this.meanKey]))\n .attr('cy', centerY)\n .attr('r', 3)\n .attr('fill', '#4CAF50')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [`평균: ${Number(this.data[this.meanKey]).toFixed(2)}`]))\n .on('mouseleave', () => hideTip())\n }\n\n // 현재값 (오렌지색)\n if (this.data[this.valueKey] != null) {\n const currentValue = this.data[this.valueKey]\n const isOutlier = currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]\n\n g.append('circle')\n .attr('cx', x(currentValue))\n .attr('cy', centerY)\n .attr('r', 3)\n .attr('fill', isOutlier ? '#ff4444' : '#ff9800')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `현재 프로젝트: ${Number(currentValue).toFixed(2)}`,\n isOutlier ? '⚠ 이상치' : '정상 범위'\n ]))\n .on('mouseleave', () => hideTip())\n }\n }\n}"]}
1
+ {"version":3,"file":"kpi-single-boxplot-chart.js","sourceRoot":"","sources":["../../client/components/kpi-single-boxplot-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('kpi-single-boxplot-chart')\nexport class KpiSingleBoxplotChart extends LitElement {\n @property({ type: Object }) data: any = {}\n @property({ type: String }) minKey: string = 'min'\n @property({ type: String }) maxKey: string = 'max'\n @property({ type: String }) meanKey: string = 'mean'\n @property({ type: String }) medianKey: string = 'median'\n @property({ type: String }) q1Key: string = 'q1'\n @property({ type: String }) q3Key: string = 'q3'\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) color: string = '#2196f3'\n @property({ type: Boolean }) vertical: boolean = true // true: 세로형, false: 가로형\n /**\n * y축 스케일 모드:\n * - fitToData=true(기본): 각 구성값의 min/max(수염 양끝)로 전체 높이를 채움(자동맞춤).\n * 범위 밖 현재값은 clamp 되어 끝에 표시(도메인을 늘리지 않음). → 고객 선호 기본 동작.\n * - fitToData=false: domainMin~domainMax(기본 0~100) 고정 스케일.\n */\n @property({ type: Boolean }) fitToData: boolean = true\n @property({ type: Number }) domainMin: number = 0\n @property({ type: Number }) domainMax: number = 100\n\n /** 현재 모드에 맞는 [min, max] 도메인 계산 */\n private computeDomain(): [number, number] {\n if (this.fitToData) {\n const lo = Number(this.data?.[this.minKey])\n const hi = Number(this.data?.[this.maxKey])\n if (isFinite(lo) && isFinite(hi) && hi > lo) return [lo, hi]\n }\n return [this.domainMin, this.domainMax]\n }\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n /* flex/grid item 의 intrinsic min-content 가 부모를 밀어올리는 것 방지 */\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n /* 절대 늘어나지 않도록 contain — layout/size 격리 */\n contain: strict;\n }\n svg {\n display: block;\n width: 100%;\n height: 100%;\n }\n `\n\n /** 현재 host 크기 (ResizeObserver 가 채움). viewBox 를 이 값으로 동적 설정 → 여백 없이 풀 채움. */\n private vbW = 200\n private vbH = 200\n\n private resizeObserver?: ResizeObserver\n\n render() {\n // 초기 viewBox 는 200x200. ResizeObserver 가 컨테이너 크기로 갱신.\n return html`\n <svg id=\"single-boxplot\" viewBox=\"0 0 200 200\" preserveAspectRatio=\"xMidYMid meet\"></svg>\n `\n }\n\n connectedCallback() {\n super.connectedCallback()\n // host 크기 변경 시 viewBox 를 컨테이너 비율로 맞춰 D3 만 다시 그리기 (Lit requestUpdate X).\n this.resizeObserver = new ResizeObserver(entries => {\n const entry = entries[0]\n if (!entry) return\n const w = Math.max(1, entry.contentRect.width)\n const h = Math.max(1, entry.contentRect.height)\n if (w === this.vbW && h === this.vbH) return\n this.vbW = w\n this.vbH = h\n this.drawSingleBoxplot()\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawSingleBoxplot()\n }\n\n private drawSingleBoxplot() {\n const svg = d3.select(this.renderRoot.querySelector('#single-boxplot'))\n if (svg.empty()) return\n svg.selectAll('*').remove()\n\n // 데이터 검증\n if (!this.data || Object.keys(this.data).length === 0) {\n return\n }\n\n // viewBox 를 host 실제 크기로 — preserveAspectRatio 와 무관하게 풀 채움.\n const w = this.vbW\n const h = this.vbH\n svg.attr('viewBox', `0 0 ${w} ${h}`)\n const margin = 20\n const plotW = w - margin * 2\n const plotH = h - margin * 2\n\n // 필수 필드 검증\n const requiredFields = [this.minKey, this.maxKey, this.q1Key, this.q3Key, this.medianKey, this.meanKey]\n const missingFields = requiredFields.filter(field => this.data[field] == null)\n if (missingFields.length > 0) {\n console.warn('Missing required fields:', missingFields)\n return\n }\n\n const values = [\n this.data[this.minKey],\n this.data[this.maxKey],\n this.data[this.q1Key],\n this.data[this.q3Key],\n this.data[this.medianKey],\n this.data[this.meanKey]\n ]\n\n if (this.data[this.valueKey] != null) {\n values.push(this.data[this.valueKey])\n }\n\n const g = svg.append('g').attr('transform', `translate(${margin}, ${margin})`)\n\n // 툴팁\n const tooltip = svg.append('g').attr('class', 'sb-tooltip').attr('visibility', 'hidden')\n tooltip.append('rect').attr('rx', 4).attr('ry', 4).attr('fill', 'rgba(33,33,33,0.9)')\n tooltip.append('text').attr('fill', '#fff').attr('font-size', '10px')\n\n const showTip = (evt: MouseEvent, lines: string[]) => {\n const tipText = tooltip.select('text')\n tipText.selectAll('tspan').remove()\n lines.forEach((line, i) => {\n tipText.append('tspan').attr('x', 8).attr('dy', i === 0 ? 14 : 13).text(line)\n })\n const tipW = Math.max(...lines.map(l => l.length)) * 8 + 24\n const tipH = lines.length * 13 + 10\n const [mx, my] = d3.pointer(evt, svg.node())\n const tx = Math.min(mx + 10, w - tipW - 5)\n const ty = Math.max(my - tipH - 5, 5)\n tooltip.select('rect').attr('x', tx).attr('y', ty).attr('width', tipW).attr('height', tipH)\n tipText.attr('transform', `translate(${tx},${ty})`)\n tooltip.attr('visibility', 'visible').raise()\n }\n const hideTip = () => tooltip.attr('visibility', 'hidden')\n\n if (this.vertical) {\n this.drawVerticalBoxplot(g, plotW, plotH, showTip, hideTip)\n } else {\n this.drawHorizontalBoxplot(g, plotW, plotH, showTip, hideTip)\n }\n }\n\n private drawVerticalBoxplot(g: any, plotW: number, plotH: number, showTip: Function, hideTip: Function) {\n // 스케일 모드(computeDomain): 기본은 데이터 min/max 자동맞춤, 옵션으로 0~100 고정.\n // clamp 로 도메인 밖 현재값은 끝에 고정(도메인 확장 안 함).\n const y = d3\n .scaleLinear()\n .domain(this.computeDomain())\n .range([plotH, 0])\n .clamp(true)\n\n const boxWidth = Math.min(plotW * 0.6, 80)\n const centerX = plotW / 2\n\n // 박스 (Q1 ~ Q3)\n const boxHeight = y(this.data[this.q1Key]) - y(this.data[this.q3Key])\n g.append('rect')\n .attr('x', centerX - boxWidth / 2)\n .attr('y', y(this.data[this.q3Key]))\n .attr('width', boxWidth)\n .attr('height', boxHeight)\n .attr('fill', '#B3D4FC')\n .attr('opacity', 0.8)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `최대: ${Number(this.data[this.maxKey]).toFixed(2)}`,\n `Q3: ${Number(this.data[this.q3Key]).toFixed(2)}`,\n `중앙값: ${Number(this.data[this.medianKey]).toFixed(2)}`,\n `Q1: ${Number(this.data[this.q1Key]).toFixed(2)}`,\n `최소: ${Number(this.data[this.minKey]).toFixed(2)}`,\n `평균: ${Number(this.data[this.meanKey]).toFixed(2)}`\n ]))\n .on('mouseleave', () => hideTip())\n\n // 중앙선 (Median)\n g.append('line')\n .attr('x1', centerX - boxWidth / 2)\n .attr('x2', centerX + boxWidth / 2)\n .attr('y1', y(this.data[this.medianKey]))\n .attr('y2', y(this.data[this.medianKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1.5)\n\n // 위쪽 수염 (Q3 ~ Max)\n g.append('line')\n .attr('x1', centerX)\n .attr('x2', centerX)\n .attr('y1', y(this.data[this.q3Key]))\n .attr('y2', y(this.data[this.maxKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 아래쪽 수염 (Min ~ Q1)\n g.append('line')\n .attr('x1', centerX)\n .attr('x2', centerX)\n .attr('y1', y(this.data[this.minKey]))\n .attr('y2', y(this.data[this.q1Key]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Max 캡\n const capWidth = boxWidth * 0.6\n g.append('line')\n .attr('x1', centerX - capWidth / 2)\n .attr('x2', centerX + capWidth / 2)\n .attr('y1', y(this.data[this.maxKey]))\n .attr('y2', y(this.data[this.maxKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Min 캡\n g.append('line')\n .attr('x1', centerX - capWidth / 2)\n .attr('x2', centerX + capWidth / 2)\n .attr('y1', y(this.data[this.minKey]))\n .attr('y2', y(this.data[this.minKey]))\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 평균값 (초록색)\n if (this.data[this.meanKey] != null) {\n g.append('circle')\n .attr('cx', centerX)\n .attr('cy', y(this.data[this.meanKey]))\n .attr('r', 3)\n .attr('fill', '#4CAF50')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [`평균: ${Number(this.data[this.meanKey]).toFixed(2)}`]))\n .on('mouseleave', () => hideTip())\n }\n\n // 현재값 (오렌지색)\n if (this.data[this.valueKey] != null) {\n const currentValue = this.data[this.valueKey]\n const isOutlier = currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]\n\n g.append('circle')\n .attr('cx', centerX)\n .attr('cy', y(currentValue))\n .attr('r', 5)\n .attr('fill', '#e53935')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `현재 프로젝트: ${Number(currentValue).toFixed(2)}`,\n isOutlier ? '⚠ 이상치' : '정상 범위'\n ]))\n .on('mouseleave', () => hideTip())\n }\n }\n\n private drawHorizontalBoxplot(g: any, plotW: number, plotH: number, showTip: Function, hideTip: Function) {\n // 스케일 모드(computeDomain): 기본은 데이터 min/max 자동맞춤, 옵션으로 0~100 고정.\n const x = d3\n .scaleLinear()\n .domain(this.computeDomain())\n .range([0, plotW])\n .clamp(true)\n\n const boxHeight = Math.min(plotH * 0.6, 80)\n const centerY = plotH / 2\n\n // 박스 (Q1 ~ Q3)\n const boxWidth = x(this.data[this.q3Key]) - x(this.data[this.q1Key])\n g.append('rect')\n .attr('x', x(this.data[this.q1Key]))\n .attr('y', centerY - boxHeight / 2)\n .attr('width', boxWidth)\n .attr('height', boxHeight)\n .attr('fill', '#B3D4FC')\n .attr('opacity', 0.8)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `최대: ${Number(this.data[this.maxKey]).toFixed(2)}`,\n `Q3: ${Number(this.data[this.q3Key]).toFixed(2)}`,\n `중앙값: ${Number(this.data[this.medianKey]).toFixed(2)}`,\n `Q1: ${Number(this.data[this.q1Key]).toFixed(2)}`,\n `최소: ${Number(this.data[this.minKey]).toFixed(2)}`,\n `평균: ${Number(this.data[this.meanKey]).toFixed(2)}`\n ]))\n .on('mouseleave', () => hideTip())\n\n // 중앙선 (Median)\n g.append('line')\n .attr('x1', x(this.data[this.medianKey]))\n .attr('x2', x(this.data[this.medianKey]))\n .attr('y1', centerY - boxHeight / 2)\n .attr('y2', centerY + boxHeight / 2)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1.5)\n\n // 왼쪽 수염 (Min ~ Q1)\n g.append('line')\n .attr('x1', x(this.data[this.minKey]))\n .attr('x2', x(this.data[this.q1Key]))\n .attr('y1', centerY)\n .attr('y2', centerY)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 오른쪽 수염 (Q3 ~ Max)\n g.append('line')\n .attr('x1', x(this.data[this.q3Key]))\n .attr('x2', x(this.data[this.maxKey]))\n .attr('y1', centerY)\n .attr('y2', centerY)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Min 캡\n const capHeight = boxHeight * 0.6\n g.append('line')\n .attr('x1', x(this.data[this.minKey]))\n .attr('x2', x(this.data[this.minKey]))\n .attr('y1', centerY - capHeight / 2)\n .attr('y2', centerY + capHeight / 2)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // Max 캡\n g.append('line')\n .attr('x1', x(this.data[this.maxKey]))\n .attr('x2', x(this.data[this.maxKey]))\n .attr('y1', centerY - capHeight / 2)\n .attr('y2', centerY + capHeight / 2)\n .attr('stroke', '#7BB3F0')\n .attr('stroke-width', 1)\n\n // 평균값 (초록색)\n if (this.data[this.meanKey] != null) {\n g.append('circle')\n .attr('cx', x(this.data[this.meanKey]))\n .attr('cy', centerY)\n .attr('r', 3)\n .attr('fill', '#4CAF50')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [`평균: ${Number(this.data[this.meanKey]).toFixed(2)}`]))\n .on('mouseleave', () => hideTip())\n }\n\n // 현재값 (오렌지색)\n if (this.data[this.valueKey] != null) {\n const currentValue = this.data[this.valueKey]\n const isOutlier = currentValue < this.data[this.minKey] || currentValue > this.data[this.maxKey]\n\n g.append('circle')\n .attr('cx', x(currentValue))\n .attr('cy', centerY)\n .attr('r', 5)\n .attr('fill', '#e53935')\n .attr('stroke', '#fff')\n .attr('stroke-width', 1)\n .style('cursor', 'pointer')\n .on('mouseenter', (evt: MouseEvent) => showTip(evt, [\n `현재 프로젝트: ${Number(currentValue).toFixed(2)}`,\n isOutlier ? '⚠ 이상치' : '정상 범위'\n ]))\n .on('mouseleave', () => hideTip())\n }\n }\n}"]}
@@ -0,0 +1,33 @@
1
+ import { LitElement } from 'lit';
2
+ /**
3
+ * 계단형(step) 등급 분포 차트
4
+ *
5
+ * SL-PA 결과처럼 측정값을 구간표로 1~5 이산 점수에 매핑하는 KPI 전용.
6
+ * 각 등급 구간 [minValue, maxValue] 을 점수 높이만큼의 사각형(계단)으로 그리고,
7
+ * 현재 측정값이 속한 구간을 강조한다. (곡선 보간 대신 이산 계단으로 표현)
8
+ *
9
+ * ※ 다른 KPI 차트에 영향을 주지 않도록 독립 컴포넌트로 분리.
10
+ */
11
+ interface Grade {
12
+ name?: string;
13
+ minValue: number;
14
+ maxValue: number;
15
+ score?: number;
16
+ description?: string;
17
+ }
18
+ export declare class KpiStepLookupChart extends LitElement {
19
+ grades: Grade[];
20
+ value: number | null;
21
+ score: number | null;
22
+ unit: string;
23
+ static styles: import("lit").CSSResult;
24
+ private chartWidth;
25
+ private chartHeight;
26
+ private resizeObserver?;
27
+ render(): import("lit-html").TemplateResult<1>;
28
+ connectedCallback(): void;
29
+ disconnectedCallback(): void;
30
+ updated(): void;
31
+ private renderChart;
32
+ }
33
+ export {};