UNPKG

chrt-dotplot

Version:

Dot Plot component for Chrt

260 lines (238 loc) 8.04 kB
import { pointSize, pointColor, pointStroke, pointStrokeWidth, pointOpacity, strokeOpacity, range, rangeWidth, rangeColor, orient, horizontal, vertical, } from './lib'; import chrtObject, { utils, cssDisplay } from 'chrt-object'; const { isNull, createSVG: create } = utils; const DEFAULT_POINT_SIZE = 3; const DEFAULT_POINT_COLOR = '#000'; function chrtDotPlot() { chrtObject.call(this); this.type = 'series'; // this.size = DEFAULT_POINT_SIZE; // this.fill = DEFAULT_POINT_COLOR; // this.stroke = DEFAULT_POINT_COLOR; // this.strokeWidth = 0; // this._opacity = 1; this.attr('radius', DEFAULT_POINT_SIZE); this.attr('stroke', DEFAULT_POINT_COLOR); this.attr('fill', DEFAULT_POINT_COLOR); this.attr('strokeWidth', 0); this.attr('strokeOpacity', 1); this.attr('fillOpacity', 1); this.attr('range', false); this.attr('rangeWidth', DEFAULT_POINT_SIZE); this.attr('rangeColor', DEFAULT_POINT_COLOR); horizontal.call(this); this._classNames = ['chrt-dotplot']; this.getXScale = () => { if (isNull(this.fields.x)) { this.fields.x = this.parentNode.scales.x[this.scales.x].field; } return this.parentNode.scales.x[this.scales.x]; }; this.draw = () => { cssDisplay.call(this, this.attr('display')()); this.g.classList.remove(...this.g.classList) this.g.classList.add(...this._classNames); if (!isNull(this._data)) { if (isNull(this.fields.x)) { this.fields.x = this.parentNode.scales.x[this.scales.x].field; } if (isNull(this.fields.y)) { //console.log('this.scales', this.scales) //console.log('this.parentNode.scales', this.parentNode.scales) this.fields.y = this.parentNode.scales.y[this.scales.y].field; } if (this.attr('range')()) { // console.log('ORIENT', this.attr('orient')()) const rangeWidth = this.attr('rangeWidth')(); const rangeColor = this.attr('rangeColor')(); const rangeType = this.attr('orient')() === 'vertical' ? 'x' : 'y'; const ranges = this._data.reduce((acc, d) => { const otherField = rangeType === 'x' ? this.fields.y : this.fields.x; acc[d[this.fields[rangeType]]] = acc[d[this.fields[rangeType]]] || { [rangeType]: d[this.fields[rangeType]], data: [], field: rangeType, otherField }; acc[d[this.fields[rangeType]]].data.push(d); const otherData = acc[d[this.fields[rangeType]]].data.map( d => d[otherField] ); acc[d[this.fields[rangeType]]].max = Math.max(...otherData); acc[d[this.fields[rangeType]]].min = Math.min(...otherData); return acc; }, {}); // console.log('ranges', ranges); Object.values(ranges).forEach((r, rangeIndex) => { const dataID = escape(`range-${r[rangeType]}-${rangeIndex}`) let range = this.g.querySelector( `[data-id='${dataID}']` ); if (!range) { range = create('line'); range.setAttribute( 'data-id', dataID ); this.g.appendChild(range); } if ( !isNull(this.parentNode.scales.x[this.scales.x]) && !isNull(this.parentNode.scales.y[this.scales.y]) ) { // const x = this.parentNode.scales.x[this.scales.x](range.min); // const y = this.parentNode.scales.y[this.scales.y](d[this.fields.y]); const coords = { [r.field]: this.parentNode.scales[r.field][this.scales[r.field]]( r[r.field] ), [`${r.otherField}Min`]: this.parentNode.scales[r.otherField][ this.scales[r.otherField] ](r.min), [`${r.otherField}Max`]: this.parentNode.scales[r.otherField][ this.scales[r.otherField] ](r.max) }; // console.log(coords); if (rangeType === 'y') { range.setAttribute( 'x1', !isNaN(coords[`${r.otherField}Min`]) ? coords[`${r.otherField}Min`] : 0 ); range.setAttribute( 'x2', !isNaN(coords[`${r.otherField}Max`]) ? coords[`${r.otherField}Max`] : 0 ); range.setAttribute( 'y1', !isNaN(coords[r.field]) ? coords[r.field] : 0 ); range.setAttribute( 'y2', !isNaN(coords[r.field]) ? coords[r.field] : 0 ); } else { range.setAttribute( 'y1', !isNaN(coords[`${r.otherField}Min`]) ? coords[`${r.otherField}Min`] : 0 ); range.setAttribute( 'y2', !isNaN(coords[`${r.otherField}Max`]) ? coords[`${r.otherField}Max`] : 0 ); range.setAttribute( 'x1', !isNaN(coords[r.field]) ? coords[r.field] : 0 ); range.setAttribute( 'x2', !isNaN(coords[r.field]) ? coords[r.field] : 0 ); } range.setAttribute('stroke-width', rangeWidth); range.setAttribute('stroke', rangeColor); } }); } this._data.forEach((d, i, arr) => { const dataID = escape(`circle-${name}-${i}`) let circle = this.g.querySelector(`[data-id='${dataID}']`); if (!circle) { circle = create('circle'); circle.setAttribute('data-id', dataID); this.g.appendChild(circle); } if ( !isNull(this.parentNode.scales.x[this.scales.x]) && !isNull(this.parentNode.scales.y[this.scales.y]) ) { const x = this.parentNode.scales.x[this.scales.x](d[this.fields.x]); const y = this.parentNode.scales.y[this.scales.y](d[this.fields.y]); const cx = !isNaN(x) ? x : 0; const cy = !isNaN(y) ? y : 0; const r = this.attr('radius')(d, i, arr); d.anchorPoints = { x: cx, width: r, y: cy, height: r, relativePosition: [0,0], directions: { x: 1, y: 1, }, alignment: { horizontal: 'middle', vertical: 'top', } }; circle.setAttribute('cx', cx); circle.setAttribute('cy', cy); circle.setAttribute('r', r); circle.setAttribute('fill', this.attr('fill')(d, i, arr)); circle.setAttribute( 'fill-opacity', this.attr('fillOpacity')(d, i, arr) || 1 ); circle.setAttribute('stroke', this.attr('stroke')(d, i, arr)); circle.setAttribute( 'stroke-width', this.attr('strokeWidth')(d, i, arr) ); circle.setAttribute( 'stroke-opacity', this.attr('strokeOpacity')(d, i, arr) ); } }); // // // console.log('points', points); } this.objects.forEach(obj => obj.draw()); return this.parentNode; }; } chrtDotPlot.prototype = Object.create(chrtObject.prototype); chrtDotPlot.prototype.constructor = chrtDotPlot; chrtDotPlot.parent = chrtObject.prototype; chrtDotPlot.prototype = Object.assign(chrtDotPlot.prototype, { pointSize, size: pointSize, radius: pointSize, color: pointColor, stroke: pointStroke, width: pointStrokeWidth, strokeWidth: pointStrokeWidth, opacity: pointOpacity, strokeOpacity, range, rangeColor, rangeWidth, orient, horizontal, vertical, }); // export default chrtDotPlot; export default function() { return new chrtDotPlot(); }