UNPKG

chartjs-chart-matrix

Version:

Chart.js module for creating matrix charts

260 lines (252 loc) 7.64 kB
/*! * chartjs-chart-matrix v0.0.0-development * https://chartjs-chart-matrix.pages.dev/ * (c) 2025 Jukka Kurkela * Released under the MIT license */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('chart.js'), require('chart.js/helpers')) : typeof define === 'function' && define.amd ? define(['exports', 'chart.js', 'chart.js/helpers'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["chartjs-chart-matrix"] = {}, global.Chart, global.Chart.helpers)); })(this, (function (exports, chart_js, helpers) { 'use strict'; var version = "0.0.0-development"; class MatrixController extends chart_js.DatasetController { initialize() { this.enableOptionSharing = true; super.initialize(); } update(mode) { const meta = this._cachedMeta; this.updateElements(meta.data, 0, meta.data.length, mode); } updateElements(rects, start, count, mode) { const reset = mode === 'reset'; const { xScale, yScale } = this._cachedMeta; const firstOpts = this.resolveDataElementOptions(start, mode); const sharedOptions = this.getSharedOptions(firstOpts); for(let i = start; i < start + count; i++){ const parsed = !reset && this.getParsed(i); const x = reset ? xScale.getBasePixel() : xScale.getPixelForValue(parsed.x); const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(parsed.y); const options = this.resolveDataElementOptions(i, mode); const { width, height, anchorX, anchorY } = options; const properties = { x: resolveX(anchorX, x, width), y: resolveY(anchorY, y, height), width, height, options }; this.updateElement(rects[i], i, properties, mode); } this.updateSharedOptions(sharedOptions, mode, firstOpts); } draw() { const ctx = this.chart.ctx; const data = this.getMeta().data || []; let i, ilen; for(i = 0, ilen = data.length; i < ilen; ++i){ data[i].draw(ctx); } } } MatrixController.id = 'matrix'; MatrixController.version = version; MatrixController.defaults = { dataElementType: 'matrix', animations: { numbers: { type: 'number', properties: [ 'x', 'y', 'width', 'height' ] } } }; MatrixController.overrides = { interaction: { mode: 'nearest', intersect: true }, scales: { x: { type: 'linear', offset: true }, y: { type: 'linear', reverse: true } } }; function resolveX(anchorX, x, width) { if (anchorX === 'left' || anchorX === 'start') { return x; } if (anchorX === 'right' || anchorX === 'end') { return x - width; } return x - width / 2; } function resolveY(anchorY, y, height) { if (anchorY === 'top' || anchorY === 'start') { return y; } if (anchorY === 'bottom' || anchorY === 'end') { return y - height; } return y - height / 2; } function getBounds(element, useFinalPosition) { const { x, y, width, height } = element.getProps([ 'x', 'y', 'width', 'height' ], useFinalPosition); return { left: x, top: y, right: x + width, bottom: y + height }; } function limit(value, min, max) { return Math.max(Math.min(value, max), min); } function parseBorderWidth(options, maxW, maxH) { const value = options.borderWidth; let t, r, b, l; if (helpers.isObject(value)) { t = +value.top || 0; r = +value.right || 0; b = +value.bottom || 0; l = +value.left || 0; } else { t = r = b = l = +value || 0; } return { t: limit(t, 0, maxH), r: limit(r, 0, maxW), b: limit(b, 0, maxH), l: limit(l, 0, maxW) }; } function boundingRects(element) { const bounds = getBounds(element, false); const width = bounds.right - bounds.left; const height = bounds.bottom - bounds.top; const border = parseBorderWidth(element.options, width / 2, height / 2); return { outer: { x: bounds.left, y: bounds.top, w: width, h: height }, inner: { x: bounds.left + border.l, y: bounds.top + border.t, w: width - border.l - border.r, h: height - border.t - border.b } }; } function inRange(element, x, y, useFinalPosition) { const skipX = x === null; const skipY = y === null; const bounds = !element || skipX && skipY ? false : getBounds(element, useFinalPosition); return bounds && (skipX || x >= bounds.left && x <= bounds.right) && (skipY || y >= bounds.top && y <= bounds.bottom); } class MatrixElement extends chart_js.Element { draw(ctx) { const options = this.options; const { inner, outer } = boundingRects(this); const radius = helpers.toTRBLCorners(options.borderRadius); ctx.save(); if (outer.w !== inner.w || outer.h !== inner.h) { ctx.beginPath(); helpers.addRoundedRectPath(ctx, { x: outer.x, y: outer.y, w: outer.w, h: outer.h, radius }); helpers.addRoundedRectPath(ctx, { x: inner.x, y: inner.y, w: inner.w, h: inner.h, radius }); ctx.fillStyle = options.backgroundColor; ctx.fill(); ctx.fillStyle = options.borderColor; ctx.fill('evenodd'); } else { ctx.beginPath(); helpers.addRoundedRectPath(ctx, { x: inner.x, y: inner.y, w: inner.w, h: inner.h, radius }); ctx.fillStyle = options.backgroundColor; ctx.fill(); } ctx.restore(); } inRange(mouseX, mouseY, useFinalPosition) { return inRange(this, mouseX, mouseY, useFinalPosition); } inXRange(mouseX, useFinalPosition) { return inRange(this, mouseX, null, useFinalPosition); } inYRange(mouseY, useFinalPosition) { return inRange(this, null, mouseY, useFinalPosition); } getCenterPoint(useFinalPosition) { const { x, y, width, height } = this.getProps([ 'x', 'y', 'width', 'height' ], useFinalPosition); return { x: x + width / 2, y: y + height / 2 }; } tooltipPosition() { return this.getCenterPoint(); } getRange(axis) { return axis === 'x' ? this.width / 2 : this.height / 2; } constructor(cfg){ super(); if (cfg) { Object.assign(this, cfg); } } } MatrixElement.id = 'matrix'; MatrixElement.defaults = { backgroundColor: undefined, borderColor: undefined, borderWidth: undefined, borderRadius: 0, anchorX: 'center', anchorY: 'center', width: 20, height: 20 }; chart_js.Chart.register(MatrixController, MatrixElement); exports.MatrixController = MatrixController; exports.MatrixElement = MatrixElement; }));