simple-sparkline-chart
Advanced tools
Comparing version 0.2.3 to 0.3.0
@@ -1,13 +0,2 @@ | ||
var t,e,i=(t=(t,e)=>{var i= | ||
/* | ||
* | ||
* Simple SparkLine Chart | ||
* @version 0.2.3 | ||
* @license MIT | ||
* @author https://github.com/dejurin | ||
* | ||
* https://github.com/dejurin/simple-sparkline | ||
* | ||
*/ | ||
class{constructor(t){document.querySelectorAll(t).forEach((t=>{this.createChart(t)}))}createChart(t){const e=t.dataset.values;let i=[],s=[],a=!1;if(!e)return void console.warn("Missing data-values attribute for element:",t);try{const r=JSON.parse(e);if(!(Array.isArray(r)&&r.length>0))return void console.warn("Empty or invalid array in data-values:",t);if("object"==typeof r[0]&&null!==r[0])a=!0,s=r.map((t=>({timestamp:t.timestamp,value:t.value}))),i=s.map((t=>t.value));else{if("number"!=typeof r[0])return void console.warn("Invalid data format in data-values:",t);i=r.filter(Number.isFinite)}}catch{i=e.split(",").map(parseFloat).filter(Number.isFinite)}if(0===i.length)return void console.warn("No valid data values for element:",t);const r=t.dataset.width?parseInt(t.dataset.width):200,n=t.dataset.height?parseInt(t.dataset.height):Math.round(.2*r),o="true"===t.dataset.filled,l=t.dataset.colorStroke||"#8956ff",d=t.dataset.colorFilled||l,u=t.dataset.strokeWidth?parseFloat(t.dataset.strokeWidth):2,c=void 0!==t.dataset.filledOpacity?parseFloat(t.dataset.filledOpacity):.2,p=t.dataset.ariaLabel||"Simple SparkLine Chart",h="false"!==t.dataset.tooltip,m=t.dataset.tooltipPosition||"above",b=t.dataset.locale||navigator.language||"en-US";this.makeChart(i,s,a,r,n,t,o,l,d,u,c,p,h,m,b)}makeChart(t,e,i,s,a,r,n,o,l,d,u,c,p,h,m){const b="http://www.w3.org/2000/svg",y=s,f=a,v=Math.max(...t),A=Math.min(...t),g=v-A||1,x=t=>f-d/2-(f-d)/g*(t-A),k=document.createElementNS(b,"svg");k.setAttribute("role","img"),k.setAttribute("width",y.toString()),k.setAttribute("height",f.toString()),k.setAttribute("aria-label",c),k.setAttribute("viewBox",`0 0 ${y} ${f}`),k.setAttribute("overflow","visible"),k.setAttribute("preserveAspectRatio","none");const w=t.length>1?y/(t.length-1):0,S=[];for(let e=0;e<t.length;e++){const i=(e*w).toFixed(2),s=x(t[e]).toFixed(2);S.push(`${i},${s}`)}if(n){const t=`${S.map(((t,e)=>0===e?"M"+t:"L"+t)).join(" ")} L${y.toFixed(2)},${f.toFixed(2)} L0,${f.toFixed(2)} Z`,e=document.createElementNS(b,"path");e.setAttribute("d",t),e.setAttribute("stroke","none"),e.setAttribute("fill",l),e.setAttribute("fill-opacity",u.toString()),e.classList.add("sparkline-fill"),k.appendChild(e)}const E=`M${S.join(" L")}`,C=document.createElementNS(b,"path");C.setAttribute("d",E),C.setAttribute("fill","none"),C.setAttribute("stroke",o),C.setAttribute("stroke-width",d.toString()),C.setAttribute("stroke-linecap","butt"),C.setAttribute("stroke-linejoin","round"),C.classList.add("sparkline-path"),k.appendChild(C);const F=document.createElement("div");F.style.position="relative",F.style.display="inline-block",F.style.width=y+"px",F.appendChild(k);const L=document.createElement("div");L.style.position="absolute",L.style.pointerEvents="none",L.style.background="#333",L.style.border="1px solid #222",L.style.color="#fff",L.style.fontSize="small",L.style.whiteSpace="nowrap",L.style.padding="2px 4px",L.style.borderRadius="4px",L.classList.add("sparkline-tooltip"),L.style.display="none","bottom"===h?L.style.top=f+"px":L.style.bottom=f+"px",p&&F.appendChild(L),r.innerHTML="",r.appendChild(F);const M=document.createElementNS(b,"line");M.setAttribute("class","sparkline-cursor-line"),M.setAttribute("x1","0"),M.setAttribute("y1","0"),M.setAttribute("x2","0"),M.setAttribute("y2",f.toString()),M.setAttribute("stroke",o),M.setAttribute("stroke-width","1"),M.setAttribute("stroke-dasharray","4"),M.style.display="none",k.appendChild(M);const $=document.createElementNS(b,"circle");$.setAttribute("class","sparkline-spot"),$.setAttribute("r",(1.5*d).toString()),$.setAttribute("fill",o),$.setAttribute("stroke","#fff"),$.setAttribute("stroke-width","1"),$.style.display="none",k.appendChild($);const N=document.createElementNS(b,"rect");N.setAttribute("width",y.toString()),N.setAttribute("height",f.toString()),N.setAttribute("fill","transparent"),N.style.cursor="pointer",k.appendChild(N);const j=new Intl.DateTimeFormat(m,{hour:"numeric",minute:"numeric",second:"numeric",day:"numeric",month:"short",year:"numeric"}),I=s=>{s.preventDefault();const a=k.getBoundingClientRect();let r;if(s instanceof MouseEvent)r=s.clientX;else{if(!(s.touches&&s.touches.length>0))return;r=s.touches[0].clientX}const n=r-a.left,o=Math.round(n/w),l=Math.max(0,Math.min(t.length-1,o)),d=l*w,u=x(t[l]);if($.setAttribute("cx",d.toFixed(2)),$.setAttribute("cy",u.toFixed(2)),$.style.display="block",M.setAttribute("x1",d.toFixed(2)),M.setAttribute("x2",d.toFixed(2)),M.style.display="block",i){const t=e[l],i=j.format(new Date(t.timestamp));L.innerHTML=`${t.value} (${i})`}else L.textContent=t[l].toString();L.style.display="block";const c=L.getBoundingClientRect();let p=d-c.width/2;p<0?p=0:p+c.width>y&&(p=y-c.width),L.style.left=p+"px"},R=()=>{$.style.display="none",M.style.display="none",L.style.display="none"};N.addEventListener("mousemove",I),N.addEventListener("touchmove",I),N.addEventListener("mouseleave",R),N.addEventListener("touchend",R),N.addEventListener("touchcancel",R)}};e.exports&&(e.exports=i)},()=>(e||t((e={exports:{}}).exports,e),e.exports));export default i(); | ||
var t,e,i=(t=(t,e)=>{var i=class{constructor(t){document.querySelectorAll(t).forEach((t=>{this.createChart(t)}))}createChart(t){const e=t.dataset.values;let i=[],s=[],a=!1;if(!e)return void console.warn("Missing data-values attribute for element:",t);try{const n=JSON.parse(e);if(!(Array.isArray(n)&&n.length>0))return void console.warn("Empty or invalid array in data-values:",t);if("object"==typeof n[0]&&null!==n[0])a=!0,s=n.map((t=>({timestamp:t.timestamp,value:t.value}))),i=s.map((t=>t.value));else{if("number"!=typeof n[0])return void console.warn("Invalid data format in data-values:",t);i=n.filter(Number.isFinite)}}catch{i=e.split(",").map(parseFloat).filter(Number.isFinite)}if(0===i.length)return void console.warn("No valid data values for element:",t);const n=t.dataset.width?parseInt(t.dataset.width):200,o=t.dataset.height?parseInt(t.dataset.height):Math.round(.2*n),r=void 0!==t.dataset.filled&&"false"!==t.dataset.filled,l=r?parseFloat(t.dataset.filled||"0.2"):0,d=t.dataset.colorStroke||"#8956ff",u=t.dataset.colorFilled||d,c=t.dataset.strokeWidth?parseFloat(t.dataset.strokeWidth):2,p=t.dataset.ariaLabel||"Simple SparkLine Chart",m=t.dataset.tooltip,h=void 0!==m,b="bottom"===m?"bottom":"top",y=t.dataset.locale||navigator.language||"en-US";this.makeChart(i,s,a,n,o,t,r,d,u,c,l,p,h,b,y)}makeChart(t,e,i,s,a,n,o,r,l,d,u,c,p,m,h){const b="http://www.w3.org/2000/svg",y=s,f=a,v=Math.max(...t),A=Math.min(...t),g=v-A||1,x=t=>f-d/2-(f-d)/g*(t-A),k=document.createElementNS(b,"svg");k.setAttribute("role","img"),k.setAttribute("width",y.toString()),k.setAttribute("height",f.toString()),k.setAttribute("aria-label",c),k.setAttribute("viewBox",`0 0 ${y} ${f}`),k.setAttribute("overflow","visible"),k.setAttribute("preserveAspectRatio","none");const w=t.length>1?y/(t.length-1):0,S=t.map(((t,e)=>`${(e*w).toFixed(2)},${x(t).toFixed(2)}`));if(o){const t=`${S.map(((t,e)=>0===e?"M"+t:"L"+t)).join(" ")} L${y.toFixed(2)},${f.toFixed(2)} L0,${f.toFixed(2)} Z`,e=document.createElementNS(b,"path");e.setAttribute("d",t),e.setAttribute("stroke","none"),e.setAttribute("fill",l),e.setAttribute("fill-opacity",u.toString()),e.classList.add("sparkline-fill"),k.appendChild(e)}const E=`M${S.join(" L")}`,C=document.createElementNS(b,"path");C.setAttribute("d",E),C.setAttribute("fill","none"),C.setAttribute("stroke",r),C.setAttribute("stroke-width",d.toString()),C.setAttribute("stroke-linecap","butt"),C.setAttribute("stroke-linejoin","round"),C.classList.add("sparkline-path"),k.appendChild(C);const F=document.createElement("div");if(F.style.position="relative",F.style.display="inline-block",F.style.width=`${y}px`,F.appendChild(k),p){const s=document.createElement("div");s.classList.add("sparkline-tooltip"),s.style.position="absolute",s.style.pointerEvents="none",s.style.background="#333",s.style.border="1px solid #222",s.style.color="#fff",s.style.fontSize="small",s.style.padding="2px 4px",s.style.borderRadius="4px",s.style.whiteSpace="nowrap",s.style.display="none",s.style["bottom"===m?"top":"bottom"]=`${f}px`,F.appendChild(s);const a=document.createElementNS(b,"line");a.setAttribute("x1","0"),a.setAttribute("y1","0"),a.setAttribute("x2","0"),a.setAttribute("y2",f.toString()),a.setAttribute("stroke",r),a.setAttribute("stroke-width","1"),a.setAttribute("stroke-dasharray","4"),a.style.display="none",k.appendChild(a);const n=document.createElementNS(b,"circle");n.setAttribute("r",(1.5*d).toString()),n.setAttribute("fill",r),n.setAttribute("stroke","#fff"),n.setAttribute("stroke-width","1"),n.style.display="none",k.appendChild(n);const o=document.createElementNS(b,"rect");o.setAttribute("width",y.toString()),o.setAttribute("height",f.toString()),o.setAttribute("fill","transparent"),o.style.cursor="pointer",k.appendChild(o);const l=new Intl.DateTimeFormat(h,{hour:"numeric",minute:"numeric",second:"numeric",day:"numeric",month:"short",year:"numeric"}),u=o=>{const r=k.getBoundingClientRect(),d=(o instanceof MouseEvent?o.clientX:o.touches[0].clientX)-r.left,u=Math.round(d/w),c=Math.max(0,Math.min(t.length-1,u)),p=c*w,m=x(t[c]);if(n.setAttribute("cx",p.toFixed(2)),n.setAttribute("cy",m.toFixed(2)),n.style.display="block",a.setAttribute("x1",p.toFixed(2)),a.setAttribute("x2",p.toFixed(2)),a.style.display="block",i){const t=e[c],i=l.format(new Date(t.timestamp));s.innerHTML=`${t.value} (${i})`}else s.textContent=t[c].toString();s.style.display="block";const h=s.getBoundingClientRect();s.style.left=`${Math.max(0,Math.min(y-h.width,p-h.width/2))}px`},c=()=>{n.style.display="none",a.style.display="none",s.style.display="none"};o.addEventListener("mousemove",u),o.addEventListener("touchmove",u),o.addEventListener("mouseleave",c),o.addEventListener("touchend",c),o.addEventListener("touchcancel",c)}n.innerHTML="",n.appendChild(F)}};e.exports&&(e.exports=i)},()=>(e||t((e={exports:{}}).exports,e),e.exports));export default i(); | ||
//# sourceMappingURL=index.js.map |
@@ -1,11 +0,2 @@ | ||
/* | ||
* | ||
* Simple SparkLine Chart | ||
* @version 0.2.3 | ||
* @license MIT | ||
* @author https://github.com/dejurin | ||
* | ||
* https://github.com/dejurin/simple-sparkline | ||
* | ||
*/ class $efcab5d8b7cb3e5f$var$SimpleSparkLineChart { | ||
class $efcab5d8b7cb3e5f$var$SimpleSparkLineChart { | ||
constructor(selector){ | ||
@@ -26,3 +17,2 @@ const elements = document.querySelectorAll(selector); | ||
if (typeof parsedData[0] === "object" && parsedData[0] !== null) { | ||
// Data is an array of objects with 'timestamp' and 'value' | ||
isObjectData = true; | ||
@@ -34,4 +24,3 @@ dataObjects = parsedData.map((item)=>({ | ||
dataValues = dataObjects.map((item)=>item.value); | ||
} else if (typeof parsedData[0] === "number") // Data is an array of numbers | ||
dataValues = parsedData.filter(Number.isFinite); | ||
} else if (typeof parsedData[0] === "number") dataValues = parsedData.filter(Number.isFinite); | ||
else { | ||
@@ -46,3 +35,2 @@ console.warn("Invalid data format in data-values:", element); | ||
} catch (e) { | ||
// If not JSON, try parsing as CSV (comma-separated values) | ||
dataValues = valuesAttr.split(",").map(parseFloat).filter(Number.isFinite); | ||
@@ -58,13 +46,13 @@ } | ||
} | ||
// Read optional attributes with default values | ||
const width = element.dataset.width ? parseInt(element.dataset.width) : 200; | ||
const height = element.dataset.height ? parseInt(element.dataset.height) : Math.round(width * 0.2); | ||
const filled = element.dataset.filled === "true"; | ||
const filled = element.dataset.filled !== undefined && element.dataset.filled !== "false"; | ||
const filledOpacity = filled ? parseFloat(element.dataset.filled || "0.2") : 0; | ||
const colorStroke = element.dataset.colorStroke || "#8956ff"; | ||
const colorFilled = element.dataset.colorFilled || colorStroke; | ||
const strokeWidth = element.dataset.strokeWidth ? parseFloat(element.dataset.strokeWidth) : 2; | ||
const filledOpacity = element.dataset.filledOpacity !== undefined ? parseFloat(element.dataset.filledOpacity) : 0.2; | ||
const ariaLabel = element.dataset.ariaLabel || "Simple SparkLine Chart"; | ||
const showTooltip = element.dataset.tooltip !== "false"; | ||
const tooltipPosition = element.dataset.tooltipPosition || "above"; | ||
const tooltipAttr = element.dataset.tooltip; | ||
const showTooltip = tooltipAttr !== undefined; | ||
const tooltipPosition = tooltipAttr === "bottom" ? "bottom" : "top"; | ||
const locale = element.dataset.locale || navigator.language || "en-US"; | ||
@@ -80,3 +68,2 @@ this.makeChart(dataValues, dataObjects, isObjectData, width, height, element, filled, colorStroke, colorFilled, strokeWidth, filledOpacity, ariaLabel, showTooltip, tooltipPosition, locale); | ||
const range = max - min || 1; | ||
// Function to calculate Y coordinate | ||
const c = (x)=>{ | ||
@@ -86,3 +73,2 @@ const s = (adjustedHeight - strokeWidth) / range; | ||
}; | ||
// Create SVG element | ||
const svg = document.createElementNS(svgNS, "svg"); | ||
@@ -97,10 +83,3 @@ svg.setAttribute("role", "img"); | ||
const offset = values.length > 1 ? adjustedWidth / (values.length - 1) : 0; | ||
// Generate points for the line | ||
const linePoints = []; | ||
for(let i = 0; i < values.length; i++){ | ||
const x = (i * offset).toFixed(2); | ||
const y = c(values[i]).toFixed(2); | ||
linePoints.push(`${x},${y}`); | ||
} | ||
// Create filled area if 'filled' is true | ||
const linePoints = values.map((val, i)=>`${(i * offset).toFixed(2)},${c(val).toFixed(2)}`); | ||
if (filled) { | ||
@@ -116,3 +95,2 @@ const fillPathD = `${linePoints.map((p, i)=>i === 0 ? "M" + p : "L" + p).join(" ")} L${adjustedWidth.toFixed(2)},${adjustedHeight.toFixed(2)} L0,${adjustedHeight.toFixed(2)} Z`; | ||
} | ||
// Create the line path | ||
const linePathD = `M${linePoints.join(" L")}`; | ||
@@ -128,110 +106,89 @@ const pathElm = document.createElementNS(svgNS, "path"); | ||
svg.appendChild(pathElm); | ||
// Create container for SVG and Tooltip | ||
const container = document.createElement("div"); | ||
container.style.position = "relative"; | ||
container.style.display = "inline-block"; | ||
container.style.width = adjustedWidth + "px"; | ||
// Append SVG to container | ||
container.style.width = `${adjustedWidth}px`; | ||
container.appendChild(svg); | ||
// Create Tooltip | ||
const tooltip = document.createElement("div"); | ||
tooltip.style.position = "absolute"; | ||
tooltip.style.pointerEvents = "none"; | ||
tooltip.style.background = "#333"; | ||
tooltip.style.border = "1px solid #222"; | ||
tooltip.style.color = "#fff"; | ||
tooltip.style.fontSize = "small"; | ||
tooltip.style.whiteSpace = "nowrap"; | ||
tooltip.style.padding = "2px 4px"; | ||
tooltip.style.borderRadius = "4px"; | ||
tooltip.classList.add("sparkline-tooltip"); | ||
tooltip.style.display = "none"; | ||
// Position the tooltip | ||
if (tooltipPosition === "bottom") tooltip.style.top = adjustedHeight + "px"; | ||
else tooltip.style.bottom = adjustedHeight + "px"; | ||
if (showTooltip) container.appendChild(tooltip); | ||
// Clear parent element and append container | ||
if (showTooltip) { | ||
const tooltip = document.createElement("div"); | ||
tooltip.classList.add("sparkline-tooltip"); | ||
tooltip.style.position = "absolute"; | ||
tooltip.style.pointerEvents = "none"; | ||
tooltip.style.background = "#333"; | ||
tooltip.style.border = "1px solid #222"; | ||
tooltip.style.color = "#fff"; | ||
tooltip.style.fontSize = "small"; | ||
tooltip.style.padding = "2px 4px"; | ||
tooltip.style.borderRadius = "4px"; | ||
tooltip.style.whiteSpace = "nowrap"; | ||
tooltip.style.display = "none"; | ||
tooltip.style[tooltipPosition === "bottom" ? "top" : "bottom"] = `${adjustedHeight}px`; | ||
container.appendChild(tooltip); | ||
const cursorLine = document.createElementNS(svgNS, "line"); | ||
cursorLine.setAttribute("x1", "0"); | ||
cursorLine.setAttribute("y1", "0"); | ||
cursorLine.setAttribute("x2", "0"); | ||
cursorLine.setAttribute("y2", adjustedHeight.toString()); | ||
cursorLine.setAttribute("stroke", colorStroke); | ||
cursorLine.setAttribute("stroke-width", "1"); | ||
cursorLine.setAttribute("stroke-dasharray", "4"); | ||
cursorLine.style.display = "none"; | ||
svg.appendChild(cursorLine); | ||
const spot = document.createElementNS(svgNS, "circle"); | ||
spot.setAttribute("r", (strokeWidth * 1.5).toString()); | ||
spot.setAttribute("fill", colorStroke); | ||
spot.setAttribute("stroke", "#fff"); | ||
spot.setAttribute("stroke-width", "1"); | ||
spot.style.display = "none"; | ||
svg.appendChild(spot); | ||
const interactionLayer = document.createElementNS(svgNS, "rect"); | ||
interactionLayer.setAttribute("width", adjustedWidth.toString()); | ||
interactionLayer.setAttribute("height", adjustedHeight.toString()); | ||
interactionLayer.setAttribute("fill", "transparent"); | ||
interactionLayer.style.cursor = "pointer"; | ||
svg.appendChild(interactionLayer); | ||
const dateFormatter = new Intl.DateTimeFormat(locale, { | ||
hour: "numeric", | ||
minute: "numeric", | ||
second: "numeric", | ||
day: "numeric", | ||
month: "short", | ||
year: "numeric" | ||
}); | ||
const handleMove = (event)=>{ | ||
const rect = svg.getBoundingClientRect(); | ||
const clientX = event instanceof MouseEvent ? event.clientX : event.touches[0].clientX; | ||
const x = clientX - rect.left; | ||
const index = Math.round(x / offset); | ||
const clampedIndex = Math.max(0, Math.min(values.length - 1, index)); | ||
const cx = clampedIndex * offset; | ||
const cy = c(values[clampedIndex]); | ||
spot.setAttribute("cx", cx.toFixed(2)); | ||
spot.setAttribute("cy", cy.toFixed(2)); | ||
spot.style.display = "block"; | ||
cursorLine.setAttribute("x1", cx.toFixed(2)); | ||
cursorLine.setAttribute("x2", cx.toFixed(2)); | ||
cursorLine.style.display = "block"; | ||
if (isObjectData) { | ||
const dataPoint = dataObjects[clampedIndex]; | ||
const date = dateFormatter.format(new Date(dataPoint.timestamp)); | ||
tooltip.innerHTML = `${dataPoint.value} (${date})`; | ||
} else tooltip.textContent = values[clampedIndex].toString(); | ||
tooltip.style.display = "block"; | ||
const tooltipRect = tooltip.getBoundingClientRect(); | ||
tooltip.style.left = `${Math.max(0, Math.min(adjustedWidth - tooltipRect.width, cx - tooltipRect.width / 2))}px`; | ||
}; | ||
const handleOut = ()=>{ | ||
spot.style.display = "none"; | ||
cursorLine.style.display = "none"; | ||
tooltip.style.display = "none"; | ||
}; | ||
interactionLayer.addEventListener("mousemove", handleMove); | ||
interactionLayer.addEventListener("touchmove", handleMove); | ||
interactionLayer.addEventListener("mouseleave", handleOut); | ||
interactionLayer.addEventListener("touchend", handleOut); | ||
interactionLayer.addEventListener("touchcancel", handleOut); | ||
} | ||
parent.innerHTML = ""; | ||
parent.appendChild(container); | ||
// Create cursor line | ||
const cursorLine = document.createElementNS(svgNS, "line"); | ||
cursorLine.setAttribute("class", "sparkline-cursor-line"); | ||
cursorLine.setAttribute("x1", "0"); | ||
cursorLine.setAttribute("y1", "0"); | ||
cursorLine.setAttribute("x2", "0"); | ||
cursorLine.setAttribute("y2", adjustedHeight.toString()); | ||
cursorLine.setAttribute("stroke", colorStroke); | ||
cursorLine.setAttribute("stroke-width", "1"); | ||
cursorLine.setAttribute("stroke-dasharray", "4"); | ||
cursorLine.style.display = "none"; | ||
svg.appendChild(cursorLine); | ||
// Create spot circle | ||
const spot = document.createElementNS(svgNS, "circle"); | ||
spot.setAttribute("class", "sparkline-spot"); | ||
spot.setAttribute("r", (strokeWidth * 1.5).toString()); | ||
spot.setAttribute("fill", colorStroke); | ||
spot.setAttribute("stroke", "#fff"); | ||
spot.setAttribute("stroke-width", "1"); | ||
spot.style.display = "none"; | ||
svg.appendChild(spot); | ||
// Create interaction layer | ||
const interactionLayer = document.createElementNS(svgNS, "rect"); | ||
interactionLayer.setAttribute("width", adjustedWidth.toString()); | ||
interactionLayer.setAttribute("height", adjustedHeight.toString()); | ||
interactionLayer.setAttribute("fill", "transparent"); | ||
interactionLayer.style.cursor = "pointer"; | ||
svg.appendChild(interactionLayer); | ||
// Create date formatter | ||
const dateFormatter = new Intl.DateTimeFormat(locale, { | ||
hour: "numeric", | ||
minute: "numeric", | ||
second: "numeric", | ||
day: "numeric", | ||
month: "short", | ||
year: "numeric" | ||
}); | ||
// Event handlers | ||
const handleMove = (event)=>{ | ||
event.preventDefault(); | ||
const rect = svg.getBoundingClientRect(); | ||
let clientX; | ||
if (event instanceof MouseEvent) clientX = event.clientX; | ||
else if (event.touches && event.touches.length > 0) clientX = event.touches[0].clientX; | ||
else return; | ||
const x = clientX - rect.left; | ||
const index = Math.round(x / offset); | ||
const clampedIndex = Math.max(0, Math.min(values.length - 1, index)); | ||
const cx = clampedIndex * offset; | ||
const cy = c(values[clampedIndex]); | ||
spot.setAttribute("cx", cx.toFixed(2)); | ||
spot.setAttribute("cy", cy.toFixed(2)); | ||
spot.style.display = "block"; | ||
cursorLine.setAttribute("x1", cx.toFixed(2)); | ||
cursorLine.setAttribute("x2", cx.toFixed(2)); | ||
cursorLine.style.display = "block"; | ||
// Update tooltip content | ||
if (isObjectData) { | ||
const dataPoint = dataObjects[clampedIndex]; | ||
const date = dateFormatter.format(new Date(dataPoint.timestamp)); | ||
tooltip.innerHTML = `${dataPoint.value} (${date})`; | ||
} else tooltip.textContent = values[clampedIndex].toString(); | ||
// Position tooltip horizontally | ||
// Ensure tooltip is displayed to get correct dimensions | ||
tooltip.style.display = "block"; | ||
const tooltipRect = tooltip.getBoundingClientRect(); | ||
let tooltipX = cx - tooltipRect.width / 2; | ||
if (tooltipX < 0) tooltipX = 0; | ||
else if (tooltipX + tooltipRect.width > adjustedWidth) tooltipX = adjustedWidth - tooltipRect.width; | ||
tooltip.style.left = tooltipX + "px"; | ||
}; | ||
const handleOut = ()=>{ | ||
spot.style.display = "none"; | ||
cursorLine.style.display = "none"; | ||
tooltip.style.display = "none"; | ||
}; | ||
interactionLayer.addEventListener("mousemove", handleMove); | ||
interactionLayer.addEventListener("touchmove", handleMove); | ||
interactionLayer.addEventListener("mouseleave", handleOut); | ||
interactionLayer.addEventListener("touchend", handleOut); | ||
interactionLayer.addEventListener("touchcancel", handleOut); | ||
} | ||
@@ -238,0 +195,0 @@ } |
@@ -1,13 +0,2 @@ | ||
!function(){var t= | ||
/* | ||
* | ||
* Simple SparkLine Chart | ||
* @version 0.2.3 | ||
* @license MIT | ||
* @author https://github.com/dejurin | ||
* | ||
* https://github.com/dejurin/simple-sparkline | ||
* | ||
*/ | ||
class{constructor(t){document.querySelectorAll(t).forEach((t=>{this.createChart(t)}))}createChart(t){const e=t.dataset.values;let i=[],s=[],a=!1;if(!e)return void console.warn("Missing data-values attribute for element:",t);try{const n=JSON.parse(e);if(!(Array.isArray(n)&&n.length>0))return void console.warn("Empty or invalid array in data-values:",t);if("object"==typeof n[0]&&null!==n[0])a=!0,s=n.map((t=>({timestamp:t.timestamp,value:t.value}))),i=s.map((t=>t.value));else{if("number"!=typeof n[0])return void console.warn("Invalid data format in data-values:",t);i=n.filter(Number.isFinite)}}catch{i=e.split(",").map(parseFloat).filter(Number.isFinite)}if(0===i.length)return void console.warn("No valid data values for element:",t);const n=t.dataset.width?parseInt(t.dataset.width):200,r=t.dataset.height?parseInt(t.dataset.height):Math.round(.2*n),o="true"===t.dataset.filled,l=t.dataset.colorStroke||"#8956ff",d=t.dataset.colorFilled||l,u=t.dataset.strokeWidth?parseFloat(t.dataset.strokeWidth):2,c=void 0!==t.dataset.filledOpacity?parseFloat(t.dataset.filledOpacity):.2,p=t.dataset.ariaLabel||"Simple SparkLine Chart",h="false"!==t.dataset.tooltip,m=t.dataset.tooltipPosition||"above",b=t.dataset.locale||navigator.language||"en-US";this.makeChart(i,s,a,n,r,t,o,l,d,u,c,p,h,m,b)}makeChart(t,e,i,s,a,n,r,o,l,d,u,c,p,h,m){const b="http://www.w3.org/2000/svg",y=s,f=a,A=Math.max(...t),v=Math.min(...t),g=A-v||1,w=t=>f-d/2-(f-d)/g*(t-v),k=document.createElementNS(b,"svg");k.setAttribute("role","img"),k.setAttribute("width",y.toString()),k.setAttribute("height",f.toString()),k.setAttribute("aria-label",c),k.setAttribute("viewBox",`0 0 ${y} ${f}`),k.setAttribute("overflow","visible"),k.setAttribute("preserveAspectRatio","none");const x=t.length>1?y/(t.length-1):0,S=[];for(let e=0;e<t.length;e++){const i=(e*x).toFixed(2),s=w(t[e]).toFixed(2);S.push(`${i},${s}`)}if(r){const t=`${S.map(((t,e)=>0===e?"M"+t:"L"+t)).join(" ")} L${y.toFixed(2)},${f.toFixed(2)} L0,${f.toFixed(2)} Z`,e=document.createElementNS(b,"path");e.setAttribute("d",t),e.setAttribute("stroke","none"),e.setAttribute("fill",l),e.setAttribute("fill-opacity",u.toString()),e.classList.add("sparkline-fill"),k.appendChild(e)}const C=`M${S.join(" L")}`,E=document.createElementNS(b,"path");E.setAttribute("d",C),E.setAttribute("fill","none"),E.setAttribute("stroke",o),E.setAttribute("stroke-width",d.toString()),E.setAttribute("stroke-linecap","butt"),E.setAttribute("stroke-linejoin","round"),E.classList.add("sparkline-path"),k.appendChild(E);const L=document.createElement("div");L.style.position="relative",L.style.display="inline-block",L.style.width=y+"px",L.appendChild(k);const F=document.createElement("div");F.style.position="absolute",F.style.pointerEvents="none",F.style.background="#333",F.style.border="1px solid #222",F.style.color="#fff",F.style.fontSize="small",F.style.whiteSpace="nowrap",F.style.padding="2px 4px",F.style.borderRadius="4px",F.classList.add("sparkline-tooltip"),F.style.display="none","bottom"===h?F.style.top=f+"px":F.style.bottom=f+"px",p&&L.appendChild(F),n.innerHTML="",n.appendChild(L);const M=document.createElementNS(b,"line");M.setAttribute("class","sparkline-cursor-line"),M.setAttribute("x1","0"),M.setAttribute("y1","0"),M.setAttribute("x2","0"),M.setAttribute("y2",f.toString()),M.setAttribute("stroke",o),M.setAttribute("stroke-width","1"),M.setAttribute("stroke-dasharray","4"),M.style.display="none",k.appendChild(M);const $=document.createElementNS(b,"circle");$.setAttribute("class","sparkline-spot"),$.setAttribute("r",(1.5*d).toString()),$.setAttribute("fill",o),$.setAttribute("stroke","#fff"),$.setAttribute("stroke-width","1"),$.style.display="none",k.appendChild($);const N=document.createElementNS(b,"rect");N.setAttribute("width",y.toString()),N.setAttribute("height",f.toString()),N.setAttribute("fill","transparent"),N.style.cursor="pointer",k.appendChild(N);const j=new Intl.DateTimeFormat(m,{hour:"numeric",minute:"numeric",second:"numeric",day:"numeric",month:"short",year:"numeric"}),I=s=>{s.preventDefault();const a=k.getBoundingClientRect();let n;if(s instanceof MouseEvent)n=s.clientX;else{if(!(s.touches&&s.touches.length>0))return;n=s.touches[0].clientX}const r=n-a.left,o=Math.round(r/x),l=Math.max(0,Math.min(t.length-1,o)),d=l*x,u=w(t[l]);if($.setAttribute("cx",d.toFixed(2)),$.setAttribute("cy",u.toFixed(2)),$.style.display="block",M.setAttribute("x1",d.toFixed(2)),M.setAttribute("x2",d.toFixed(2)),M.style.display="block",i){const t=e[l],i=j.format(new Date(t.timestamp));F.innerHTML=`${t.value} (${i})`}else F.textContent=t[l].toString();F.style.display="block";const c=F.getBoundingClientRect();let p=d-c.width/2;p<0?p=0:p+c.width>y&&(p=y-c.width),F.style.left=p+"px"},R=()=>{$.style.display="none",M.style.display="none",F.style.display="none"};N.addEventListener("mousemove",I),N.addEventListener("touchmove",I),N.addEventListener("mouseleave",R),N.addEventListener("touchend",R),N.addEventListener("touchcancel",R)}};"undefined"!=typeof window&&(window.SimpleSparkLineChart=t)}(); | ||
!function(){var t=class{constructor(t){document.querySelectorAll(t).forEach((t=>{this.createChart(t)}))}createChart(t){const e=t.dataset.values;let i=[],a=[],s=!1;if(!e)return void console.warn("Missing data-values attribute for element:",t);try{const n=JSON.parse(e);if(!(Array.isArray(n)&&n.length>0))return void console.warn("Empty or invalid array in data-values:",t);if("object"==typeof n[0]&&null!==n[0])s=!0,a=n.map((t=>({timestamp:t.timestamp,value:t.value}))),i=a.map((t=>t.value));else{if("number"!=typeof n[0])return void console.warn("Invalid data format in data-values:",t);i=n.filter(Number.isFinite)}}catch{i=e.split(",").map(parseFloat).filter(Number.isFinite)}if(0===i.length)return void console.warn("No valid data values for element:",t);const n=t.dataset.width?parseInt(t.dataset.width):200,o=t.dataset.height?parseInt(t.dataset.height):Math.round(.2*n),r=void 0!==t.dataset.filled&&"false"!==t.dataset.filled,l=r?parseFloat(t.dataset.filled||"0.2"):0,d=t.dataset.colorStroke||"#8956ff",u=t.dataset.colorFilled||d,c=t.dataset.strokeWidth?parseFloat(t.dataset.strokeWidth):2,p=t.dataset.ariaLabel||"Simple SparkLine Chart",m=t.dataset.tooltip,h=void 0!==m,b="bottom"===m?"bottom":"top",y=t.dataset.locale||navigator.language||"en-US";this.makeChart(i,a,s,n,o,t,r,d,u,c,l,p,h,b,y)}makeChart(t,e,i,a,s,n,o,r,l,d,u,c,p,m,h){const b="http://www.w3.org/2000/svg",y=a,f=s,v=Math.max(...t),A=Math.min(...t),g=v-A||1,w=t=>f-d/2-(f-d)/g*(t-A),x=document.createElementNS(b,"svg");x.setAttribute("role","img"),x.setAttribute("width",y.toString()),x.setAttribute("height",f.toString()),x.setAttribute("aria-label",c),x.setAttribute("viewBox",`0 0 ${y} ${f}`),x.setAttribute("overflow","visible"),x.setAttribute("preserveAspectRatio","none");const k=t.length>1?y/(t.length-1):0,S=t.map(((t,e)=>`${(e*k).toFixed(2)},${w(t).toFixed(2)}`));if(o){const t=`${S.map(((t,e)=>0===e?"M"+t:"L"+t)).join(" ")} L${y.toFixed(2)},${f.toFixed(2)} L0,${f.toFixed(2)} Z`,e=document.createElementNS(b,"path");e.setAttribute("d",t),e.setAttribute("stroke","none"),e.setAttribute("fill",l),e.setAttribute("fill-opacity",u.toString()),e.classList.add("sparkline-fill"),x.appendChild(e)}const C=`M${S.join(" L")}`,E=document.createElementNS(b,"path");E.setAttribute("d",C),E.setAttribute("fill","none"),E.setAttribute("stroke",r),E.setAttribute("stroke-width",d.toString()),E.setAttribute("stroke-linecap","butt"),E.setAttribute("stroke-linejoin","round"),E.classList.add("sparkline-path"),x.appendChild(E);const L=document.createElement("div");if(L.style.position="relative",L.style.display="inline-block",L.style.width=`${y}px`,L.appendChild(x),p){const a=document.createElement("div");a.classList.add("sparkline-tooltip"),a.style.position="absolute",a.style.pointerEvents="none",a.style.background="#333",a.style.border="1px solid #222",a.style.color="#fff",a.style.fontSize="small",a.style.padding="2px 4px",a.style.borderRadius="4px",a.style.whiteSpace="nowrap",a.style.display="none",a.style["bottom"===m?"top":"bottom"]=`${f}px`,L.appendChild(a);const s=document.createElementNS(b,"line");s.setAttribute("x1","0"),s.setAttribute("y1","0"),s.setAttribute("x2","0"),s.setAttribute("y2",f.toString()),s.setAttribute("stroke",r),s.setAttribute("stroke-width","1"),s.setAttribute("stroke-dasharray","4"),s.style.display="none",x.appendChild(s);const n=document.createElementNS(b,"circle");n.setAttribute("r",(1.5*d).toString()),n.setAttribute("fill",r),n.setAttribute("stroke","#fff"),n.setAttribute("stroke-width","1"),n.style.display="none",x.appendChild(n);const o=document.createElementNS(b,"rect");o.setAttribute("width",y.toString()),o.setAttribute("height",f.toString()),o.setAttribute("fill","transparent"),o.style.cursor="pointer",x.appendChild(o);const l=new Intl.DateTimeFormat(h,{hour:"numeric",minute:"numeric",second:"numeric",day:"numeric",month:"short",year:"numeric"}),u=o=>{const r=x.getBoundingClientRect(),d=(o instanceof MouseEvent?o.clientX:o.touches[0].clientX)-r.left,u=Math.round(d/k),c=Math.max(0,Math.min(t.length-1,u)),p=c*k,m=w(t[c]);if(n.setAttribute("cx",p.toFixed(2)),n.setAttribute("cy",m.toFixed(2)),n.style.display="block",s.setAttribute("x1",p.toFixed(2)),s.setAttribute("x2",p.toFixed(2)),s.style.display="block",i){const t=e[c],i=l.format(new Date(t.timestamp));a.innerHTML=`${t.value} (${i})`}else a.textContent=t[c].toString();a.style.display="block";const h=a.getBoundingClientRect();a.style.left=`${Math.max(0,Math.min(y-h.width,p-h.width/2))}px`},c=()=>{n.style.display="none",s.style.display="none",a.style.display="none"};o.addEventListener("mousemove",u),o.addEventListener("touchmove",u),o.addEventListener("mouseleave",c),o.addEventListener("touchend",c),o.addEventListener("touchcancel",c)}n.innerHTML="",n.appendChild(L)}};"undefined"!=typeof window&&(window.SimpleSparkLineChart=t)}(); | ||
//# sourceMappingURL=index.js.map |
@@ -689,12 +689,3 @@ // modules are defined as an array | ||
function (require, module, exports) { | ||
/* | ||
* | ||
* Simple SparkLine Chart | ||
* @version 0.2.3 | ||
* @license MIT | ||
* @author https://github.com/dejurin | ||
* | ||
* https://github.com/dejurin/simple-sparkline | ||
* | ||
*/ var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); | ||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); | ||
parcelHelpers.defineInteropFlag(exports); | ||
@@ -721,3 +712,2 @@ class SimpleSparkLineChart { | ||
) { | ||
// Data is an array of objects with 'timestamp' and 'value' | ||
isObjectData = true; | ||
@@ -730,3 +720,2 @@ dataObjects = parsedData.map((item) => ({ | ||
} else if (typeof parsedData[0] === "number") | ||
// Data is an array of numbers | ||
dataValues = parsedData.filter(Number.isFinite); | ||
@@ -748,3 +737,2 @@ else { | ||
} catch (e) { | ||
// If not JSON, try parsing as CSV (comma-separated values) | ||
dataValues = valuesAttr | ||
@@ -766,3 +754,2 @@ .split(",") | ||
} | ||
// Read optional attributes with default values | ||
const width = element.dataset.width | ||
@@ -774,3 +761,8 @@ ? parseInt(element.dataset.width) | ||
: Math.round(width * 0.2); | ||
const filled = element.dataset.filled === "true"; | ||
const filled = | ||
element.dataset.filled !== undefined && | ||
element.dataset.filled !== "false"; | ||
const filledOpacity = filled | ||
? parseFloat(element.dataset.filled || "0.2") | ||
: 0; | ||
const colorStroke = element.dataset.colorStroke || "#8956ff"; | ||
@@ -781,10 +773,7 @@ const colorFilled = element.dataset.colorFilled || colorStroke; | ||
: 2; | ||
const filledOpacity = | ||
element.dataset.filledOpacity !== undefined | ||
? parseFloat(element.dataset.filledOpacity) | ||
: 0.2; | ||
const ariaLabel = | ||
element.dataset.ariaLabel || "Simple SparkLine Chart"; | ||
const showTooltip = element.dataset.tooltip !== "false"; | ||
const tooltipPosition = element.dataset.tooltipPosition || "above"; | ||
const tooltipAttr = element.dataset.tooltip; | ||
const showTooltip = tooltipAttr !== undefined; | ||
const tooltipPosition = tooltipAttr === "bottom" ? "bottom" : "top"; | ||
const locale = | ||
@@ -833,3 +822,2 @@ element.dataset.locale || navigator.language || "en-US"; | ||
const range = max - min || 1; | ||
// Function to calculate Y coordinate | ||
const c = (x) => { | ||
@@ -839,3 +827,2 @@ const s = (adjustedHeight - strokeWidth) / range; | ||
}; | ||
// Create SVG element | ||
const svg = document.createElementNS(svgNS, "svg"); | ||
@@ -854,10 +841,5 @@ svg.setAttribute("role", "img"); | ||
values.length > 1 ? adjustedWidth / (values.length - 1) : 0; | ||
// Generate points for the line | ||
const linePoints = []; | ||
for (let i = 0; i < values.length; i++) { | ||
const x = (i * offset).toFixed(2); | ||
const y = c(values[i]).toFixed(2); | ||
linePoints.push(`${x},${y}`); | ||
} | ||
// Create filled area if 'filled' is true | ||
const linePoints = values.map( | ||
(val, i) => `${(i * offset).toFixed(2)},${c(val).toFixed(2)}`, | ||
); | ||
if (filled) { | ||
@@ -873,3 +855,2 @@ const fillPathD = `${linePoints.map((p, i) => (i === 0 ? "M" + p : "L" + p)).join(" ")} L${adjustedWidth.toFixed(2)},${adjustedHeight.toFixed(2)} L0,${adjustedHeight.toFixed(2)} Z`; | ||
} | ||
// Create the line path | ||
const linePathD = `M${linePoints.join(" L")}`; | ||
@@ -885,118 +866,101 @@ const pathElm = document.createElementNS(svgNS, "path"); | ||
svg.appendChild(pathElm); | ||
// Create container for SVG and Tooltip | ||
const container = document.createElement("div"); | ||
container.style.position = "relative"; | ||
container.style.display = "inline-block"; | ||
container.style.width = adjustedWidth + "px"; | ||
// Append SVG to container | ||
container.style.width = `${adjustedWidth}px`; | ||
container.appendChild(svg); | ||
// Create Tooltip | ||
const tooltip = document.createElement("div"); | ||
tooltip.style.position = "absolute"; | ||
tooltip.style.pointerEvents = "none"; | ||
tooltip.style.background = "#333"; | ||
tooltip.style.border = "1px solid #222"; | ||
tooltip.style.color = "#fff"; | ||
tooltip.style.fontSize = "small"; | ||
tooltip.style.whiteSpace = "nowrap"; | ||
tooltip.style.padding = "2px 4px"; | ||
tooltip.style.borderRadius = "4px"; | ||
tooltip.classList.add("sparkline-tooltip"); | ||
tooltip.style.display = "none"; | ||
// Position the tooltip | ||
if (tooltipPosition === "bottom") | ||
tooltip.style.top = adjustedHeight + "px"; | ||
else tooltip.style.bottom = adjustedHeight + "px"; | ||
if (showTooltip) container.appendChild(tooltip); | ||
// Clear parent element and append container | ||
if (showTooltip) { | ||
const tooltip = document.createElement("div"); | ||
tooltip.classList.add("sparkline-tooltip"); | ||
tooltip.style.position = "absolute"; | ||
tooltip.style.pointerEvents = "none"; | ||
tooltip.style.background = "#333"; | ||
tooltip.style.border = "1px solid #222"; | ||
tooltip.style.color = "#fff"; | ||
tooltip.style.fontSize = "small"; | ||
tooltip.style.padding = "2px 4px"; | ||
tooltip.style.borderRadius = "4px"; | ||
tooltip.style.whiteSpace = "nowrap"; | ||
tooltip.style.display = "none"; | ||
tooltip.style[tooltipPosition === "bottom" ? "top" : "bottom"] = | ||
`${adjustedHeight}px`; | ||
container.appendChild(tooltip); | ||
const cursorLine = document.createElementNS(svgNS, "line"); | ||
cursorLine.setAttribute("x1", "0"); | ||
cursorLine.setAttribute("y1", "0"); | ||
cursorLine.setAttribute("x2", "0"); | ||
cursorLine.setAttribute("y2", adjustedHeight.toString()); | ||
cursorLine.setAttribute("stroke", colorStroke); | ||
cursorLine.setAttribute("stroke-width", "1"); | ||
cursorLine.setAttribute("stroke-dasharray", "4"); | ||
cursorLine.style.display = "none"; | ||
svg.appendChild(cursorLine); | ||
const spot = document.createElementNS(svgNS, "circle"); | ||
spot.setAttribute("r", (strokeWidth * 1.5).toString()); | ||
spot.setAttribute("fill", colorStroke); | ||
spot.setAttribute("stroke", "#fff"); | ||
spot.setAttribute("stroke-width", "1"); | ||
spot.style.display = "none"; | ||
svg.appendChild(spot); | ||
const interactionLayer = document.createElementNS(svgNS, "rect"); | ||
interactionLayer.setAttribute("width", adjustedWidth.toString()); | ||
interactionLayer.setAttribute( | ||
"height", | ||
adjustedHeight.toString(), | ||
); | ||
interactionLayer.setAttribute("fill", "transparent"); | ||
interactionLayer.style.cursor = "pointer"; | ||
svg.appendChild(interactionLayer); | ||
const dateFormatter = new Intl.DateTimeFormat(locale, { | ||
hour: "numeric", | ||
minute: "numeric", | ||
second: "numeric", | ||
day: "numeric", | ||
month: "short", | ||
year: "numeric", | ||
}); | ||
const handleMove = (event) => { | ||
const rect = svg.getBoundingClientRect(); | ||
const clientX = | ||
event instanceof MouseEvent | ||
? event.clientX | ||
: event.touches[0].clientX; | ||
const x = clientX - rect.left; | ||
const index = Math.round(x / offset); | ||
const clampedIndex = Math.max( | ||
0, | ||
Math.min(values.length - 1, index), | ||
); | ||
const cx = clampedIndex * offset; | ||
const cy = c(values[clampedIndex]); | ||
spot.setAttribute("cx", cx.toFixed(2)); | ||
spot.setAttribute("cy", cy.toFixed(2)); | ||
spot.style.display = "block"; | ||
cursorLine.setAttribute("x1", cx.toFixed(2)); | ||
cursorLine.setAttribute("x2", cx.toFixed(2)); | ||
cursorLine.style.display = "block"; | ||
if (isObjectData) { | ||
const dataPoint = dataObjects[clampedIndex]; | ||
const date = dateFormatter.format( | ||
new Date(dataPoint.timestamp), | ||
); | ||
tooltip.innerHTML = `${dataPoint.value} (${date})`; | ||
} else tooltip.textContent = values[clampedIndex].toString(); | ||
tooltip.style.display = "block"; | ||
const tooltipRect = tooltip.getBoundingClientRect(); | ||
tooltip.style.left = `${Math.max(0, Math.min(adjustedWidth - tooltipRect.width, cx - tooltipRect.width / 2))}px`; | ||
}; | ||
const handleOut = () => { | ||
spot.style.display = "none"; | ||
cursorLine.style.display = "none"; | ||
tooltip.style.display = "none"; | ||
}; | ||
interactionLayer.addEventListener("mousemove", handleMove); | ||
interactionLayer.addEventListener("touchmove", handleMove); | ||
interactionLayer.addEventListener("mouseleave", handleOut); | ||
interactionLayer.addEventListener("touchend", handleOut); | ||
interactionLayer.addEventListener("touchcancel", handleOut); | ||
} | ||
parent.innerHTML = ""; | ||
parent.appendChild(container); | ||
// Create cursor line | ||
const cursorLine = document.createElementNS(svgNS, "line"); | ||
cursorLine.setAttribute("class", "sparkline-cursor-line"); | ||
cursorLine.setAttribute("x1", "0"); | ||
cursorLine.setAttribute("y1", "0"); | ||
cursorLine.setAttribute("x2", "0"); | ||
cursorLine.setAttribute("y2", adjustedHeight.toString()); | ||
cursorLine.setAttribute("stroke", colorStroke); | ||
cursorLine.setAttribute("stroke-width", "1"); | ||
cursorLine.setAttribute("stroke-dasharray", "4"); | ||
cursorLine.style.display = "none"; | ||
svg.appendChild(cursorLine); | ||
// Create spot circle | ||
const spot = document.createElementNS(svgNS, "circle"); | ||
spot.setAttribute("class", "sparkline-spot"); | ||
spot.setAttribute("r", (strokeWidth * 1.5).toString()); | ||
spot.setAttribute("fill", colorStroke); | ||
spot.setAttribute("stroke", "#fff"); | ||
spot.setAttribute("stroke-width", "1"); | ||
spot.style.display = "none"; | ||
svg.appendChild(spot); | ||
// Create interaction layer | ||
const interactionLayer = document.createElementNS(svgNS, "rect"); | ||
interactionLayer.setAttribute("width", adjustedWidth.toString()); | ||
interactionLayer.setAttribute("height", adjustedHeight.toString()); | ||
interactionLayer.setAttribute("fill", "transparent"); | ||
interactionLayer.style.cursor = "pointer"; | ||
svg.appendChild(interactionLayer); | ||
// Create date formatter | ||
const dateFormatter = new Intl.DateTimeFormat(locale, { | ||
hour: "numeric", | ||
minute: "numeric", | ||
second: "numeric", | ||
day: "numeric", | ||
month: "short", | ||
year: "numeric", | ||
}); | ||
// Event handlers | ||
const handleMove = (event) => { | ||
event.preventDefault(); | ||
const rect = svg.getBoundingClientRect(); | ||
let clientX; | ||
if (event instanceof MouseEvent) clientX = event.clientX; | ||
else if (event.touches && event.touches.length > 0) | ||
clientX = event.touches[0].clientX; | ||
else return; | ||
const x = clientX - rect.left; | ||
const index = Math.round(x / offset); | ||
const clampedIndex = Math.max( | ||
0, | ||
Math.min(values.length - 1, index), | ||
); | ||
const cx = clampedIndex * offset; | ||
const cy = c(values[clampedIndex]); | ||
spot.setAttribute("cx", cx.toFixed(2)); | ||
spot.setAttribute("cy", cy.toFixed(2)); | ||
spot.style.display = "block"; | ||
cursorLine.setAttribute("x1", cx.toFixed(2)); | ||
cursorLine.setAttribute("x2", cx.toFixed(2)); | ||
cursorLine.style.display = "block"; | ||
// Update tooltip content | ||
if (isObjectData) { | ||
const dataPoint = dataObjects[clampedIndex]; | ||
const date = dateFormatter.format( | ||
new Date(dataPoint.timestamp), | ||
); | ||
tooltip.innerHTML = `${dataPoint.value} (${date})`; | ||
} else tooltip.textContent = values[clampedIndex].toString(); | ||
// Position tooltip horizontally | ||
// Ensure tooltip is displayed to get correct dimensions | ||
tooltip.style.display = "block"; | ||
const tooltipRect = tooltip.getBoundingClientRect(); | ||
let tooltipX = cx - tooltipRect.width / 2; | ||
if (tooltipX < 0) tooltipX = 0; | ||
else if (tooltipX + tooltipRect.width > adjustedWidth) | ||
tooltipX = adjustedWidth - tooltipRect.width; | ||
tooltip.style.left = tooltipX + "px"; | ||
}; | ||
const handleOut = () => { | ||
spot.style.display = "none"; | ||
cursorLine.style.display = "none"; | ||
tooltip.style.display = "none"; | ||
}; | ||
interactionLayer.addEventListener("mousemove", handleMove); | ||
interactionLayer.addEventListener("touchmove", handleMove); | ||
interactionLayer.addEventListener("mouseleave", handleOut); | ||
interactionLayer.addEventListener("touchend", handleOut); | ||
interactionLayer.addEventListener("touchcancel", handleOut); | ||
} | ||
@@ -1003,0 +967,0 @@ } |
{ | ||
"name": "simple-sparkline-chart", | ||
"version": "0.2.3", | ||
"version": "0.3.0", | ||
"source": "src/index.ts", | ||
@@ -5,0 +5,0 @@ "main": "./dist/commonjs/index.js", |
@@ -77,12 +77,12 @@ 📈 Simple SparkLine Chart | ||
Attribute Type Default Description | ||
data-values string null A comma-separated list of values or JSON data. | ||
data-width number 200 The width of the chart. | ||
data-height number 40 The height of the chart. | ||
data-color-stroke string #8956ff The color of the line (stroke). | ||
data-filled boolean false Whether to fill the area under the line. | ||
data-filled-opacity number 0.2 Opacity of the fill. | ||
data-tooltip boolean true Whether to show tooltips on hover. | ||
data-tooltip-position string above or below Position of the tooltip relative to the chart. | ||
data-aria-label string null Accessible label for the chart. | ||
| Attribute | Type | Default | Description | | ||
| ------------------- | ------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------- | | ||
| `data-values` | string | `null` **(Required)** | A comma-separated list of values or JSON data to plot. | | ||
| `data-width` | number | `200` | The width of the chart in pixels. | | ||
| `data-height` | number | Proportional to width | The height of the chart in pixels. Automatically calculated based on width, maintaining a proportional aspect ratio. | | ||
| `data-color-stroke` | string | `#8956ff` | The color of the chart line (stroke). | | ||
| `data-filled` | number | (none) | Defines the opacity of the fill area under the line if set. If not provided, no fill is displayed. | | ||
| `data-tooltip` | string | `top` | Tooltip position: "top" or "bottom". Tooltip is enabled if this attribute is set. | | ||
| `data-aria-label` | string | `Simple SparkLine Chart` | Accessible label for the chart. | | ||
| `data-locale` | string | User's locale | The locale used for formatting dates in tooltips (if using timestamp data). | | ||
@@ -89,0 +89,0 @@ ## 🧑💻 API |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
217382
1222