UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

161 lines (160 loc) • 6.79 kB
/*! All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://github.com/Esri/calcite-design-system/blob/dev/LICENSE.md for details. v3.2.1 */ import { c as customElement } from "../../chunks/runtime.js"; import { html, svg } from "lit-html"; import { LitElement, safeClassMap, nothing } from "@arcgis/lumina"; import { g as guid } from "../../chunks/guid.js"; import { c as createObserver } from "../../chunks/observers.js"; import { css } from "@lit/reactive-element/css-tag.js"; function slope(p0, p1, p2) { const dx = p1[0] - p0[0]; const dx1 = p2[0] - p1[0]; const dy = p1[1] - p0[1]; const dy1 = p2[1] - p1[1]; const m = dy / (dx || dx1 < 0 && 0); const m1 = dy1 / (dx1 || dx < 0 && 0); const p = (m * dx1 + m1 * dx) / (dx + dx1); return (Math.sign(m) + Math.sign(m1)) * Math.min(Math.abs(m), Math.abs(m1), 0.5 * Math.abs(p)) || 0; } function slopeSingle(p0, p1, m) { const dx = p1[0] - p0[0]; const dy = p1[1] - p0[1]; return dx ? (3 * dy / dx - m) / 2 : m; } function bezier(p0, p1, m0, m1, t) { const [x0, y0] = p0; const [x1, y1] = p1; const dx = (x1 - x0) / 3; const h1 = t([x0 + dx, y0 + dx * m0]).join(","); const h2 = t([x1 - dx, y1 - dx * m1]).join(","); const p = t([x1, y1]).join(","); return `C ${h1} ${h2} ${p}`; } function translate({ width, height, min, max }) { const rangeX = max[0] - min[0]; const rangeY = max[1] - min[1]; return (point) => { const x = (point[0] - min[0]) / rangeX * width; const y = height - (point[1] - min[1]) / rangeY * height; return [x, y]; }; } function range(data) { const [startX, startY] = data[0]; const min = [startX, startY]; const max = [startX, startY]; return data.reduce( ({ min: min2, max: max2 }, [x, y]) => ({ min: [Math.min(min2[0], x), Math.min(min2[1], y)], max: [Math.max(max2[0], x), Math.max(max2[1], y)] }), { min, max } ); } function area({ data, min, max, t }) { if (data.length === 0) { return ""; } const [startX, startY] = t(data[0]); const [minX, minY] = t(min); const [maxX] = t(max); let m; let p0; let p1; const commands = data.reduce((acc, point, i) => { p0 = data[i - 2]; p1 = data[i - 1]; if (i > 1) { const m1 = slope(p0, p1, point); const m0 = m === void 0 ? slopeSingle(p0, p1, m1) : m; const command = bezier(p0, p1, m0, m1, t); m = m1; return `${acc} ${command}`; } return acc; }, `M ${minX},${minY} L ${minX},${startY} L ${startX},${startY}`); const last = data[data.length - 1]; const end = bezier(p1, last, m, slopeSingle(p1, last, m), t); return `${commands} ${end} L ${maxX},${minY} Z`; } const styles = css`:host{display:block;block-size:100%}.svg{fill:currentColor;stroke:transparent;margin:0;display:block;block-size:100%;inline-size:100%;padding:0}.svg .graph-path--highlight{fill:var(--calcite-graph-highlight-fill-color, var(--calcite-color-brand));opacity:.5}:host([hidden]){display:none}[hidden]{display:none}`; const CSS = { svg: "svg", graphPath: "graph-path", graphPathHighlight: "graph-path--highlight" }; class Graph extends LitElement { constructor() { super(...arguments); this.graphId = `calcite-graph-${guid()}`; this.resizeObserver = createObserver("resize", () => this.requestUpdate()); this.data = []; } static { this.properties = { colorStops: [0, {}, { attribute: false }], data: [0, {}, { attribute: false }], highlightMax: [9, {}, { type: Number }], highlightMin: [9, {}, { type: Number }], max: [11, {}, { reflect: true, type: Number }], min: [11, {}, { reflect: true, type: Number }] }; } static { this.styles = styles; } connectedCallback() { super.connectedCallback(); this.resizeObserver?.observe(this.el); } disconnectedCallback() { super.disconnectedCallback(); this.resizeObserver?.disconnect(); } render() { const { data, colorStops, el, highlightMax, highlightMin, min, max } = this; const id = this.graphId; const { clientHeight: height, clientWidth: width } = el; if (!data || data.length === 0) { return html`<svg aria-hidden=true class=${safeClassMap(CSS.svg)} height=${height ?? nothing} preserveAspectRatio=none viewBox=${`0 0 ${width} ${height}`} width=${width ?? nothing}></svg>`; } const { min: rangeMin, max: rangeMax } = range(data); let currentMin = rangeMin; let currentMax = rangeMax; if (min < rangeMin[0] || min > rangeMin[0]) { currentMin = [min, 0]; } if (max > rangeMax[0] || max < rangeMax[0]) { currentMax = [max, rangeMax[1]]; } const t = translate({ min: currentMin, max: currentMax, width, height }); const [hMinX] = t([highlightMin, currentMax[1]]); const [hMaxX] = t([highlightMax, currentMax[1]]); const areaPath = area({ data, min: rangeMin, max: rangeMax, t }); const fill = colorStops ? `url(#linear-gradient-${id})` : void 0; return html`<svg aria-hidden=true class=${safeClassMap(CSS.svg)} height=${height ?? nothing} preserveAspectRatio=none viewBox=${`0 0 ${width} ${height}`} width=${width ?? nothing}>${colorStops ? svg`<defs><linearGradient .id=${`linear-gradient-${id}`} x1=0 x2=1 y1=0 y2=0>${colorStops.map(({ offset, color, opacity }) => svg`<stop offset=${`${offset * 100}%`} stop-color=${color ?? nothing} stop-opacity=${opacity ?? nothing} />`)}</linearGradient></defs>` : null}${highlightMin !== void 0 ? [ svg`<mask height=100% .id=${`${id}1`} width=100% x=0% y=0%><path d=${` M 0,0 L ${hMinX - 1},0 L ${hMinX - 1},${height} L 0,${height} Z `} fill=white /></mask>`, svg`<mask height=100% .id=${`${id}2`} width=100% x=0% y=0%><path d=${` M ${hMinX + 1},0 L ${hMaxX - 1},0 L ${hMaxX - 1},${height} L ${hMinX + 1}, ${height} Z `} fill=white /></mask>`, svg`<mask height=100% .id=${`${id}3`} width=100% x=0% y=0%><path d=${` M ${hMaxX + 1},0 L ${width},0 L ${width},${height} L ${hMaxX + 1}, ${height} Z `} fill=white /></mask>`, svg`<path class=${safeClassMap(CSS.graphPath)} d=${areaPath ?? nothing} fill=${fill ?? nothing} mask=${`url(#${id}1)`} />`, svg`<path class=${safeClassMap(CSS.graphPathHighlight)} d=${areaPath ?? nothing} fill=${fill ?? nothing} mask=${`url(#${id}2)`} />`, svg`<path class=${safeClassMap(CSS.graphPath)} d=${areaPath ?? nothing} fill=${fill ?? nothing} mask=${`url(#${id}3)`} />` ] : svg`<path class=${safeClassMap(CSS.graphPath)} d=${areaPath ?? nothing} fill=${fill ?? nothing} />`}</svg>`; } } customElement("calcite-graph", Graph); export { Graph };