|
|
class CustomChart extends HTMLElement { |
|
|
constructor() { |
|
|
super(); |
|
|
} |
|
|
|
|
|
connectedCallback() { |
|
|
this.attachShadow({ mode: 'open' }); |
|
|
|
|
|
const type = this.getAttribute('type') || 'line'; |
|
|
const data = (this.getAttribute('data') || '').split(',').map(Number); |
|
|
const labels = (this.getAttribute('labels') || '').split(','); |
|
|
|
|
|
const max = Math.max(...data); |
|
|
const min = Math.min(...data); |
|
|
const range = max - min || 1; |
|
|
|
|
|
const width = 600; |
|
|
const height = 200; |
|
|
const padding = 30; |
|
|
|
|
|
|
|
|
const points = data.map((value, index) => { |
|
|
const x = padding + (index / (data.length - 1)) * (width - 2 * padding); |
|
|
const y = height - padding - ((value - min) / range) * (height - 2 * padding); |
|
|
return `${x},${y}`; |
|
|
}).join(' '); |
|
|
|
|
|
|
|
|
let gridLines = ''; |
|
|
for (let i = 0; i <= 4; i++) { |
|
|
const y = padding + (i / 4) * (height - 2 * padding); |
|
|
gridLines += `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}" class="chart-grid"/>`; |
|
|
} |
|
|
|
|
|
|
|
|
let pointCircles = ''; |
|
|
data.forEach((value, index) => { |
|
|
const x = padding + (index / (data.length - 1)) * (width - 2 * padding); |
|
|
const y = height - padding - ((value - min) / range) * (height - 2 * padding); |
|
|
pointCircles += `<circle cx="${x}" cy="${y}" class="chart-point" data-value="${value}"/>`; |
|
|
}); |
|
|
|
|
|
|
|
|
let xLabels = ''; |
|
|
labels.forEach((label, index) => { |
|
|
const x = padding + (index / (labels.length - 1)) * (width - 2 * padding); |
|
|
xLabels += `<text x="${x}" y="${height - 8}" class="chart-label" text-anchor="middle">${label}</text>`; |
|
|
}); |
|
|
|
|
|
|
|
|
let yLabels = ''; |
|
|
for (let i = 0; i <= 4; i++) { |
|
|
const y = padding + (i / 4) * (height - 2 * padding); |
|
|
const value = Math.round(max - (i / 4) * range); |
|
|
yLabels += `<text x="${padding - 8}" y="${y + 4}" class="chart-label" text-anchor="end">${value}</text>`; |
|
|
} |
|
|
|
|
|
this.shadowRoot.innerHTML = ` |
|
|
<style> |
|
|
:host { |
|
|
display: block; |
|
|
width: 100%; |
|
|
} |
|
|
svg { |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
overflow: visible; |
|
|
} |
|
|
.chart-line { |
|
|
fill: none; |
|
|
stroke: #22c55e; |
|
|
stroke-width: 2; |
|
|
stroke-linecap: round; |
|
|
stroke-linejoin: round; |
|
|
filter: drop-shadow(0 0 4px rgba(34, 197, 94, 0.5)); |
|
|
} |
|
|
.chart-grid { |
|
|
stroke: #1f2937; |
|
|
stroke-width: 1; |
|
|
stroke-dasharray: 4 4; |
|
|
} |
|
|
.chart-label { |
|
|
fill: #6b7280; |
|
|
font-size: 10px; |
|
|
font-family: monospace; |
|
|
} |
|
|
.chart-point { |
|
|
fill: #0a0a0a; |
|
|
stroke: #22c55e; |
|
|
stroke-width: 2; |
|
|
r: 4; |
|
|
transition: all 0.2s; |
|
|
cursor: pointer; |
|
|
} |
|
|
.chart-point:hover { |
|
|
r: 6; |
|
|
fill: #22c55e; |
|
|
} |
|
|
.tooltip { |
|
|
position: absolute; |
|
|
background: #1f2937; |
|
|
color: #fff; |
|
|
padding: 4px 8px; |
|
|
border-radius: 4px; |
|
|
font-size: 12px; |
|
|
pointer-events: none; |
|
|
opacity: 0; |
|
|
transition: opacity 0.2s; |
|
|
} |
|
|
</style> |
|
|
<div style="height: 200px; position: relative;"> |
|
|
<svg viewBox="0 0 ${width} ${height}"> |
|
|
${gridLines} |
|
|
<polyline points="${points}" class="chart-line"/> |
|
|
${pointCircles} |
|
|
${xLabels} |
|
|
${yLabels} |
|
|
</svg> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
const points = this.shadowRoot.querySelectorAll('.chart-point'); |
|
|
points.forEach(point => { |
|
|
point.addEventListener('mouseenter', (e) => { |
|
|
const tooltip = document.createElement('div'); |
|
|
tooltip.className = 'tooltip'; |
|
|
tooltip.textContent = `Value: ${e.target.dataset.value}`; |
|
|
tooltip.style.left = `${e.target.cx.baseVal.value + 10}px`; |
|
|
tooltip.style.top = `${e.target.cy.baseVal.value - 30}px`; |
|
|
this.shadowRoot.appendChild(tooltip); |
|
|
setTimeout(() => tooltip.style.opacity = '1', 10); |
|
|
}); |
|
|
|
|
|
point.addEventListener('mouseleave', () => { |
|
|
const tooltip = this.shadowRoot.querySelector('.tooltip'); |
|
|
if (tooltip) tooltip.remove(); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
customElements.define('custom-chart', CustomChart); |