From e151b4071eeade8a12a989304ac081b7c6ecb953 Mon Sep 17 00:00:00 2001 From: "Chart.js" Date: Sun, 24 Apr 2022 16:51:02 +0000 Subject: [PATCH] Release v2.0.0 --- bower.json | 8 ++ dist/chartjs-plugin-deferred.esm.js | 188 +++++++++++++++++++++++++++ dist/chartjs-plugin-deferred.js | 194 ++++++++++++++++++++++++++++ dist/chartjs-plugin-deferred.min.js | 7 + 4 files changed, 397 insertions(+) create mode 100644 bower.json create mode 100644 dist/chartjs-plugin-deferred.esm.js create mode 100644 dist/chartjs-plugin-deferred.js create mode 100644 dist/chartjs-plugin-deferred.min.js diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..f074815 --- /dev/null +++ b/bower.json @@ -0,0 +1,8 @@ +{ + "name": "chartjs-plugin-deferred", + "description": "Chart.js plugin to defer initial chart updates", + "homepage": "https://chartjs-plugin-deferred.netlify.app", + "license": "MIT", + "version": "2.0.0", + "main": "dist/chartjs-plugin-deferred.js" +} diff --git a/dist/chartjs-plugin-deferred.esm.js b/dist/chartjs-plugin-deferred.esm.js new file mode 100644 index 0000000..9ea8678 --- /dev/null +++ b/dist/chartjs-plugin-deferred.esm.js @@ -0,0 +1,188 @@ +/*! + * chartjs-plugin-deferred v2.0.0 + * https://chartjs-plugin-deferred.netlify.app + * (c) 2016-2022 chartjs-plugin-deferred contributors + * Released under the MIT license + */ +import { requestAnimFrame, getStyle } from 'chart.js/helpers'; + +var STUB_KEY = '$chartjs_deferred'; +var MODEL_KEY = '$deferred'; + +/** + * Plugin based on discussion from Chart.js issue #2745. + * @see https://github.com/chartjs/Chart.js/issues/2745 + */ + +function defer(fn, delay) { + if (delay) { + window.setTimeout(fn, delay); + } else { + requestAnimFrame.call(window, fn); + } +} + +function computeOffset(value, base) { + var number = parseInt(value, 10); + if (isNaN(number)) { + return 0; + } else if (typeof value === 'string' && value.indexOf('%') !== -1) { + return number / 100 * base; + } + return number; +} + +function chartInViewport(chart) { + var options = chart[MODEL_KEY].options; + var canvas = chart.canvas; + + // https://stackoverflow.com/a/21696585 + if (!canvas || canvas.offsetParent === null) { + return false; + } + + var rect = canvas.getBoundingClientRect(); + var dy = computeOffset(options.yOffset || 0, rect.height); + var dx = computeOffset(options.xOffset || 0, rect.width); + + return rect.right - dx >= 0 + && rect.bottom - dy >= 0 + && rect.left + dx <= window.innerWidth + && rect.top + dy <= window.innerHeight; +} + +function onScroll(event) { + var node = event.target; + var stub = node[STUB_KEY]; + if (stub.ticking) { + return; + } + + stub.ticking = true; + defer(function() { + var charts = stub.charts.slice(); + var ilen = charts.length; + var chart, i; + + for (i = 0; i < ilen; ++i) { + chart = charts[i]; + if (chartInViewport(chart)) { + unwatch(chart); // eslint-disable-line + chart[MODEL_KEY].appeared = true; + chart.update(); + } + } + + stub.ticking = false; + }); +} + +function isScrollable(node) { + var type = node.nodeType; + if (type === Node.ELEMENT_NODE) { + var overflowX = getStyle(node, 'overflow-x'); + var overflowY = getStyle(node, 'overflow-y'); + return overflowX === 'auto' || overflowX === 'scroll' + || overflowY === 'auto' || overflowY === 'scroll'; + } + + return node.nodeType === Node.DOCUMENT_NODE; +} + +function watch(chart) { + var canvas = chart.canvas; + var parent = canvas.parentElement; + var stub, charts; + + while (parent) { + if (isScrollable(parent)) { + stub = parent[STUB_KEY] || (parent[STUB_KEY] = {}); + charts = stub.charts || (stub.charts = []); + if (charts.length === 0) { + parent.addEventListener('scroll', onScroll); + } + + charts.push(chart); + chart[MODEL_KEY].elements.push(parent); + } + + parent = parent.parentElement || parent.ownerDocument; + } +} + +function unwatch(chart) { + chart[MODEL_KEY].elements.forEach(function(element) { + var charts = element[STUB_KEY].charts; + charts.splice(charts.indexOf(chart), 1); + if (!charts.length) { + element.removeEventListener('scroll', onScroll); + delete element[STUB_KEY]; + } + }); + + chart[MODEL_KEY].elements = []; +} + +var plugin = { + id: 'deferred', + + defaults: { + xOffset: 0, + yOffset: 0, + delay: 0 + }, + + beforeInit: function(chart, _, options) { + chart[MODEL_KEY] = { + options: options, + appeared: false, + delayed: false, + loaded: false, + elements: [] + }; + + watch(chart); + }, + + beforeDatasetsUpdate: function(chart, _, options) { + var model = chart[MODEL_KEY]; + if (!model.loaded) { + if (!model.appeared && !chartInViewport(chart)) { + // cancel the datasets update + return false; + } + + model.appeared = true; + model.loaded = true; + unwatch(chart); + + if (options.delay > 0) { + model.delayed = true; + defer(function() { + // Ensure the chart instance is still alive. It may have been destroyed + // during a delay and calling `chart.update()` will fail. The most common + // reason for such scenario is user navigation. + // https://github.com/chartjs/chartjs-plugin-deferred/pull/14 + if (chart.ctx) { + model.delayed = false; + chart.update(); + } + }, options.delay); + + return false; + } + } + + if (model.delayed) { + // in case of delayed update, ensure to block external requests, such + // as interacting with the legend label, or direct calls to update() + return false; + } + }, + + destroy: function(chart) { + unwatch(chart); + } +}; + +export { plugin as default }; diff --git a/dist/chartjs-plugin-deferred.js b/dist/chartjs-plugin-deferred.js new file mode 100644 index 0000000..7ed9d96 --- /dev/null +++ b/dist/chartjs-plugin-deferred.js @@ -0,0 +1,194 @@ +/*! + * chartjs-plugin-deferred v2.0.0 + * https://chartjs-plugin-deferred.netlify.app + * (c) 2016-2022 chartjs-plugin-deferred contributors + * Released under the MIT license + */ +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js/helpers')) : +typeof define === 'function' && define.amd ? define(['chart.js/helpers'], factory) : +(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ChartDeferred = factory(global.Chart.helpers)); +})(this, (function (helpers) { 'use strict'; + +var STUB_KEY = '$chartjs_deferred'; +var MODEL_KEY = '$deferred'; + +/** + * Plugin based on discussion from Chart.js issue #2745. + * @see https://github.com/chartjs/Chart.js/issues/2745 + */ + +function defer(fn, delay) { + if (delay) { + window.setTimeout(fn, delay); + } else { + helpers.requestAnimFrame.call(window, fn); + } +} + +function computeOffset(value, base) { + var number = parseInt(value, 10); + if (isNaN(number)) { + return 0; + } else if (typeof value === 'string' && value.indexOf('%') !== -1) { + return number / 100 * base; + } + return number; +} + +function chartInViewport(chart) { + var options = chart[MODEL_KEY].options; + var canvas = chart.canvas; + + // https://stackoverflow.com/a/21696585 + if (!canvas || canvas.offsetParent === null) { + return false; + } + + var rect = canvas.getBoundingClientRect(); + var dy = computeOffset(options.yOffset || 0, rect.height); + var dx = computeOffset(options.xOffset || 0, rect.width); + + return rect.right - dx >= 0 + && rect.bottom - dy >= 0 + && rect.left + dx <= window.innerWidth + && rect.top + dy <= window.innerHeight; +} + +function onScroll(event) { + var node = event.target; + var stub = node[STUB_KEY]; + if (stub.ticking) { + return; + } + + stub.ticking = true; + defer(function() { + var charts = stub.charts.slice(); + var ilen = charts.length; + var chart, i; + + for (i = 0; i < ilen; ++i) { + chart = charts[i]; + if (chartInViewport(chart)) { + unwatch(chart); // eslint-disable-line + chart[MODEL_KEY].appeared = true; + chart.update(); + } + } + + stub.ticking = false; + }); +} + +function isScrollable(node) { + var type = node.nodeType; + if (type === Node.ELEMENT_NODE) { + var overflowX = helpers.getStyle(node, 'overflow-x'); + var overflowY = helpers.getStyle(node, 'overflow-y'); + return overflowX === 'auto' || overflowX === 'scroll' + || overflowY === 'auto' || overflowY === 'scroll'; + } + + return node.nodeType === Node.DOCUMENT_NODE; +} + +function watch(chart) { + var canvas = chart.canvas; + var parent = canvas.parentElement; + var stub, charts; + + while (parent) { + if (isScrollable(parent)) { + stub = parent[STUB_KEY] || (parent[STUB_KEY] = {}); + charts = stub.charts || (stub.charts = []); + if (charts.length === 0) { + parent.addEventListener('scroll', onScroll); + } + + charts.push(chart); + chart[MODEL_KEY].elements.push(parent); + } + + parent = parent.parentElement || parent.ownerDocument; + } +} + +function unwatch(chart) { + chart[MODEL_KEY].elements.forEach(function(element) { + var charts = element[STUB_KEY].charts; + charts.splice(charts.indexOf(chart), 1); + if (!charts.length) { + element.removeEventListener('scroll', onScroll); + delete element[STUB_KEY]; + } + }); + + chart[MODEL_KEY].elements = []; +} + +var plugin = { + id: 'deferred', + + defaults: { + xOffset: 0, + yOffset: 0, + delay: 0 + }, + + beforeInit: function(chart, _, options) { + chart[MODEL_KEY] = { + options: options, + appeared: false, + delayed: false, + loaded: false, + elements: [] + }; + + watch(chart); + }, + + beforeDatasetsUpdate: function(chart, _, options) { + var model = chart[MODEL_KEY]; + if (!model.loaded) { + if (!model.appeared && !chartInViewport(chart)) { + // cancel the datasets update + return false; + } + + model.appeared = true; + model.loaded = true; + unwatch(chart); + + if (options.delay > 0) { + model.delayed = true; + defer(function() { + // Ensure the chart instance is still alive. It may have been destroyed + // during a delay and calling `chart.update()` will fail. The most common + // reason for such scenario is user navigation. + // https://github.com/chartjs/chartjs-plugin-deferred/pull/14 + if (chart.ctx) { + model.delayed = false; + chart.update(); + } + }, options.delay); + + return false; + } + } + + if (model.delayed) { + // in case of delayed update, ensure to block external requests, such + // as interacting with the legend label, or direct calls to update() + return false; + } + }, + + destroy: function(chart) { + unwatch(chart); + } +}; + +return plugin; + +})); diff --git a/dist/chartjs-plugin-deferred.min.js b/dist/chartjs-plugin-deferred.min.js new file mode 100644 index 0000000..81bc250 --- /dev/null +++ b/dist/chartjs-plugin-deferred.min.js @@ -0,0 +1,7 @@ +/*! + * chartjs-plugin-deferred v2.0.0 + * https://chartjs-plugin-deferred.netlify.app + * (c) 2016-2022 chartjs-plugin-deferred contributors + * Released under the MIT license + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["chart.js/helpers"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).ChartDeferred=t(e.Chart.helpers)}(this,(function(e){"use strict";var t="$chartjs_deferred";function r(t,r){r?window.setTimeout(t,r):e.requestAnimFrame.call(window,t)}function n(e,t){var r=parseInt(e,10);return isNaN(r)?0:"string"==typeof e&&-1!==e.indexOf("%")?r/100*t:r}function d(e){var t=e.$deferred.options,r=e.canvas;if(!r||null===r.offsetParent)return!1;var d=r.getBoundingClientRect(),o=n(t.yOffset||0,d.height),i=n(t.xOffset||0,d.width);return d.right-i>=0&&d.bottom-o>=0&&d.left+i<=window.innerWidth&&d.top+o<=window.innerHeight}function o(e){var n=e.target[t];n.ticking||(n.ticking=!0,r((function(){var e,t,r=n.charts.slice(),o=r.length;for(t=0;t0)return o.delayed=!0,r((function(){e.ctx&&(o.delayed=!1,e.update())}),n.delay),!1}if(o.delayed)return!1},destroy:function(e){f(e)}}}));