UNPKG

wizechart

Version:

A jQuery + Bootstrap simple progress bar chart plugin

145 lines (128 loc) 4.5 kB
import $ from "jquery"; (function ($) { $.fn.WizeChart = function (options) { const defaultOptions = { data: null, ajax: null, label: "{label}", value: (val) => val, popover: { title: "{label}", content: (val) => `${val} doses`, placement: "top", }, color: "primary", colors: null, rulerPoints: 5, }; const settings = $.extend(true, {}, defaultOptions, options); const formatText = (tpl, val) => typeof tpl === "function" ? tpl(val) : tpl.replace("{label}", val); const ceilToNiceMax = (val) => { if (val <= 10) return 10; const digits = Math.floor(Math.log10(val)); const base = Math.pow(10, digits); return Math.ceil(val / base) * base; }; const buildChart = ($el, labels, values) => { const maxVal = ceilToNiceMax(Math.max(...values)); let html = ""; labels.forEach((label, idx) => { const val = values[idx]; const percent = Math.round((val / maxVal) * 100); const colorClass = (settings.colors && settings.colors[idx]) || settings.color; const isBootstrap = [ "primary", "secondary", "success", "danger", "warning", "info", "light", "dark", ].includes(colorClass); const barStyle = isBootstrap ? `progress-bar bg-${colorClass}` : "progress-bar"; const barColor = isBootstrap ? "" : `background-color: ${colorClass};`; html += ` <div class="mb-2"> <div class="d-flex justify-content-between"> <span class="fw-semibold text-dark">${formatText( settings.label, label )}</span> <small>${formatText(settings.value, val)}</small> </div> <div class="progress" role="progressbar" aria-valuenow="${percent}" aria-valuemin="0" aria-valuemax="100" data-bs-toggle="popover" data-bs-placement="${settings.popover.placement}" title="${formatText(settings.popover.title, label)}" data-bs-content="${formatText(settings.popover.content, val)}" > <div class="${barStyle}" style="width: ${percent}%; ${barColor}" ></div> </div> </div> `; }); // Ruler logic html += '<div class="d-flex justify-content-between px-1 text-muted small mt-2">'; if (Array.isArray(settings.rulerPoints)) { settings.rulerPoints.forEach((val) => { html += `<span>${val.toLocaleString()}</span>`; }); } else { const step = maxVal / (settings.rulerPoints - 1); for (let i = 0; i < settings.rulerPoints; i++) { html += `<span>${Math.round(i * step).toLocaleString()}</span>`; } } html += "</div>"; $el.html(html); // Popover setup (one active at a time) const popoverTriggerList = [].slice.call( $el.find('[data-bs-toggle="popover"]') ); let activePopover = null; popoverTriggerList.forEach((el) => { const popover = new bootstrap.Popover(el); el.addEventListener("click", () => { if (activePopover && activePopover !== popover) activePopover.hide(); activePopover = popover; }); }); document.addEventListener("click", (e) => { if (!e.target.closest('[data-bs-toggle="popover"]') && activePopover) { activePopover.hide(); activePopover = null; } }); }; return this.each(function () { const $el = $(this); if (settings.data) { buildChart($el, settings.data.labels, settings.data.values); } else if (settings.ajax) { const ajaxData = typeof settings.ajax.data === "function" ? settings.ajax.data() : settings.ajax.data; $.get(settings.ajax.url, ajaxData, (resp) => { if (!resp.success) return; const labels = resp.data.labels; const values = resp.data.values; buildChart($el, labels, values); }); } }); }; })(jQuery);