graph.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*!
  2. * All material copyright ESRI, All Rights Reserved, unless otherwise specified.
  3. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details.
  4. * v1.0.0-beta.97
  5. */
  6. import { h, forceUpdate } from "@stencil/core";
  7. import { guid } from "../../utils/guid";
  8. import { area, range, translate } from "./util";
  9. import { createObserver } from "../../utils/observers";
  10. export class Graph {
  11. constructor() {
  12. //--------------------------------------------------------------------------
  13. //
  14. // Properties
  15. //
  16. //--------------------------------------------------------------------------
  17. /**
  18. * Array of tuples describing a single data point ([x, y])
  19. * These data points should be sorted by x-axis value
  20. */
  21. this.data = [];
  22. //--------------------------------------------------------------------------
  23. //
  24. // Private State/Props
  25. //
  26. //--------------------------------------------------------------------------
  27. this.graphId = `calcite-graph-${guid()}`;
  28. this.resizeObserver = createObserver("resize", () => forceUpdate(this));
  29. }
  30. //--------------------------------------------------------------------------
  31. //
  32. // Lifecycle
  33. //
  34. //--------------------------------------------------------------------------
  35. connectedCallback() {
  36. var _a;
  37. (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.observe(this.el);
  38. }
  39. disconnectedCallback() {
  40. var _a;
  41. (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
  42. }
  43. render() {
  44. const { data, colorStops, el, highlightMax, highlightMin, min, max } = this;
  45. const id = this.graphId;
  46. const { clientHeight: height, clientWidth: width } = el;
  47. // if we have no data, return empty svg
  48. if (!data || data.length === 0) {
  49. return (h("svg", { class: "svg", height: height, preserveAspectRatio: "none", viewBox: `0 0 ${width} ${height}`, width: width }));
  50. }
  51. const { min: rangeMin, max: rangeMax } = range(data);
  52. let currentMin = rangeMin;
  53. let currentMax = rangeMax;
  54. if (min < rangeMin[0] || min > rangeMin[0]) {
  55. currentMin = [min, 0];
  56. }
  57. if (max > rangeMax[0] || max < rangeMax[0]) {
  58. currentMax = [max, rangeMax[1]];
  59. }
  60. const t = translate({ min: currentMin, max: currentMax, width, height });
  61. const [hMinX] = t([highlightMin, currentMax[1]]);
  62. const [hMaxX] = t([highlightMax, currentMax[1]]);
  63. const areaPath = area({ data, min: rangeMin, max: rangeMax, t });
  64. const fill = colorStops ? `url(#linear-gradient-${id})` : undefined;
  65. return (h("svg", { class: "svg", height: height, preserveAspectRatio: "none", viewBox: `0 0 ${width} ${height}`, width: width }, colorStops ? (h("defs", null, h("linearGradient", { id: `linear-gradient-${id}`, x1: "0", x2: "1", y1: "0", y2: "0" }, colorStops.map(({ offset, color, opacity }) => (h("stop", { offset: `${offset * 100}%`, "stop-color": color, "stop-opacity": opacity })))))) : null, highlightMin !== undefined ? ([
  66. h("mask", { height: "100%", id: `${id}1`, width: "100%", x: "0%", y: "0%" }, h("path", { d: `
  67. M 0,0
  68. L ${hMinX - 1},0
  69. L ${hMinX - 1},${height}
  70. L 0,${height}
  71. Z
  72. `, fill: "white" })),
  73. h("mask", { height: "100%", id: `${id}2`, width: "100%", x: "0%", y: "0%" }, h("path", { d: `
  74. M ${hMinX + 1},0
  75. L ${hMaxX - 1},0
  76. L ${hMaxX - 1},${height}
  77. L ${hMinX + 1}, ${height}
  78. Z
  79. `, fill: "white" })),
  80. h("mask", { height: "100%", id: `${id}3`, width: "100%", x: "0%", y: "0%" }, h("path", { d: `
  81. M ${hMaxX + 1},0
  82. L ${width},0
  83. L ${width},${height}
  84. L ${hMaxX + 1}, ${height}
  85. Z
  86. `, fill: "white" })),
  87. h("path", { class: "graph-path", d: areaPath, fill: fill, mask: `url(#${id}1)` }),
  88. h("path", { class: "graph-path--highlight", d: areaPath, fill: fill, mask: `url(#${id}2)` }),
  89. h("path", { class: "graph-path", d: areaPath, fill: fill, mask: `url(#${id}3)` })
  90. ]) : (h("path", { class: "graph-path", d: areaPath, fill: fill }))));
  91. }
  92. static get is() { return "calcite-graph"; }
  93. static get encapsulation() { return "shadow"; }
  94. static get originalStyleUrls() {
  95. return {
  96. "$": ["graph.scss"]
  97. };
  98. }
  99. static get styleUrls() {
  100. return {
  101. "$": ["graph.css"]
  102. };
  103. }
  104. static get properties() {
  105. return {
  106. "data": {
  107. "type": "unknown",
  108. "mutable": false,
  109. "complexType": {
  110. "original": "DataSeries",
  111. "resolved": "Point[]",
  112. "references": {
  113. "DataSeries": {
  114. "location": "import",
  115. "path": "./interfaces"
  116. }
  117. }
  118. },
  119. "required": false,
  120. "optional": false,
  121. "docs": {
  122. "tags": [],
  123. "text": "Array of tuples describing a single data point ([x, y])\nThese data points should be sorted by x-axis value"
  124. },
  125. "defaultValue": "[]"
  126. },
  127. "colorStops": {
  128. "type": "unknown",
  129. "mutable": false,
  130. "complexType": {
  131. "original": "ColorStop[]",
  132. "resolved": "ColorStop[]",
  133. "references": {
  134. "ColorStop": {
  135. "location": "import",
  136. "path": "./interfaces"
  137. }
  138. }
  139. },
  140. "required": false,
  141. "optional": false,
  142. "docs": {
  143. "tags": [],
  144. "text": "Array of values describing a single color stop ([offset, color, opacity])\nThese color stops should be sorted by offset value"
  145. }
  146. },
  147. "highlightMin": {
  148. "type": "number",
  149. "mutable": false,
  150. "complexType": {
  151. "original": "number",
  152. "resolved": "number",
  153. "references": {}
  154. },
  155. "required": false,
  156. "optional": false,
  157. "docs": {
  158. "tags": [],
  159. "text": "Start of highlight color if highlighting range"
  160. },
  161. "attribute": "highlight-min",
  162. "reflect": false
  163. },
  164. "highlightMax": {
  165. "type": "number",
  166. "mutable": false,
  167. "complexType": {
  168. "original": "number",
  169. "resolved": "number",
  170. "references": {}
  171. },
  172. "required": false,
  173. "optional": false,
  174. "docs": {
  175. "tags": [],
  176. "text": "End of highlight color if highlighting range"
  177. },
  178. "attribute": "highlight-max",
  179. "reflect": false
  180. },
  181. "min": {
  182. "type": "number",
  183. "mutable": false,
  184. "complexType": {
  185. "original": "number",
  186. "resolved": "number",
  187. "references": {}
  188. },
  189. "required": true,
  190. "optional": false,
  191. "docs": {
  192. "tags": [],
  193. "text": "Lowest point of the range"
  194. },
  195. "attribute": "min",
  196. "reflect": true
  197. },
  198. "max": {
  199. "type": "number",
  200. "mutable": false,
  201. "complexType": {
  202. "original": "number",
  203. "resolved": "number",
  204. "references": {}
  205. },
  206. "required": true,
  207. "optional": false,
  208. "docs": {
  209. "tags": [],
  210. "text": "Highest point of the range"
  211. },
  212. "attribute": "max",
  213. "reflect": true
  214. }
  215. };
  216. }
  217. static get elementRef() { return "el"; }
  218. }