nevera
Version:
Minimal 3kb charts as web components
77 lines (67 loc) • 2.05 kB
text/typescript
/**
* @param domain - input domain [min, max]
* @param range - output range [min, max]
* @returns scale function
*/
export function scaleLinear(domain: [number, number], range: [number, number]) {
const [domainMin, domainMax] = domain;
const [rangeMin, rangeMax] = range;
const scale = (value: number): number => {
if (domainMax === domainMin) {
throw new Error("Domain min and max cannot be the same");
}
const ratio = (value - domainMin) / (domainMax - domainMin);
return rangeMin + ratio * (rangeMax - rangeMin);
};
return scale;
}
/**
* @param domain - name of categories as array
* @param range - output range [min, max]
* @param padding - padding between column as fraction, 0 to 1
* @returns band function
*/
export function scaleBand(
domain: string[],
range: [number, number],
padding: number = 0
) {
const [rangeMin, rangeMax] = range;
const step =
(rangeMax - rangeMin) / (domain.length + padding * (domain.length - 1));
const bandWidth = step * (1 - padding);
const positions = domain.map(
(_, index) => rangeMin + index * (step + padding * step)
);
const scale = (category: string): number | undefined => {
const index = domain.indexOf(category);
if (index === -1) return;
return positions[index];
};
// TODO: not in love with this
scale.bandWidth = bandWidth;
return scale;
}
export const attachEvents = (
svg: SVGSVGElement,
elName: string,
data: { key: string }[],
self: any
) => {
const elements = svg.querySelectorAll(elName);
const emitEvent = (type: string, value: string) => (e: Event) => {
e.stopPropagation();
self.dispatchEvent(
new CustomEvent(type, {
bubbles: true,
composed: true,
detail: { value },
})
);
};
elements?.forEach((el, i) => {
el.addEventListener("mouseover", emitEvent("mouseover", data[i].key));
el.addEventListener("mouseout", emitEvent("mouseout", data[i].key));
el.addEventListener("click", emitEvent("click", data[i].key));
});
};