(() => { if (!window.Chart) return; Chart.defaults.color = '#93a4bb'; Chart.defaults.font.family = 'Inter, "Segoe UI", Arial, sans-serif'; Chart.defaults.animation.duration = 850; Chart.defaults.animation.easing = 'easeOutQuart'; const currency = value => new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(value || 0)); const compactCurrency = value => new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL', notation: 'compact', maximumFractionDigits: 1 }).format(Number(value || 0)); const number = (value, digits = 0) => new Intl.NumberFormat('pt-BR', { maximumFractionDigits: digits }).format(Number(value || 0)); const tooltip = { backgroundColor: 'rgba(6, 15, 31, .97)', titleColor: '#ffffff', bodyColor: '#cbd8e9', borderColor: 'rgba(80, 127, 190, .58)', borderWidth: 1, padding: 13, cornerRadius: 11, displayColors: true, boxPadding: 5, titleFont: {weight: '800'}, bodyFont: {weight: '650'} }; const currencyAxis = { beginAtZero: true, grid: {color: 'rgba(82, 105, 145, .14)', drawBorder: false}, border: {display: false}, ticks: { color: '#788aa4', callback: value => compactCurrency(value), maxTicksLimit: 6, font: {size: 10, weight: '650'} } }; const countAxis = { beginAtZero: true, grid: {color: 'rgba(82, 105, 145, .14)', drawBorder: false}, border: {display: false}, ticks: { color: '#788aa4', precision: 0, maxTicksLimit: 7, font: {size: 10, weight: '650'} } }; const categoryAxis = { grid: {display: false}, border: {display: false}, ticks: {color: '#aebbd0', autoSkip: false, font: {size: 10, weight: '750'}} }; const valueLabelsPlugin = { id: 'staloneValueLabels', afterDatasetsDraw(chart, args, options) { if (!options || !options.display) return; const {ctx, chartArea} = chart; const horizontal = chart.options.indexAxis === 'y'; ctx.save(); ctx.fillStyle = options.color || '#dbe7f7'; ctx.font = options.font || '700 11px Inter, "Segoe UI", Arial, sans-serif'; chart.data.datasets.forEach((dataset, datasetIndex) => { const meta = chart.getDatasetMeta(datasetIndex); if (meta.hidden) return; meta.data.forEach((element, index) => { const value = Number(dataset.data[index] || 0); if (!Number.isFinite(value) || value === 0) return; const text = options.formatter ? options.formatter(value, index) : String(value); const width = ctx.measureText(text).width; if (horizontal) { let x = element.x + 8; let align = 'left'; if (x + width > chartArea.right) { x = element.x - 8; align = 'right'; } ctx.textAlign = align; ctx.textBaseline = 'middle'; ctx.fillText(text, x, element.y); } else { ctx.textAlign = 'center'; ctx.textBaseline = value >= 0 ? 'bottom' : 'top'; ctx.fillText(text, element.x, value >= 0 ? element.y - 8 : element.y + 8); } }); }); ctx.restore(); } }; const centerTextPlugin = { id: 'staloneCenterText', afterDraw(chart, args, options) { if (!options || !options.display) return; const meta = chart.getDatasetMeta(0); if (!meta.data.length) return; const {ctx} = chart; const {x, y} = meta.data[0]; ctx.save(); ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#f6f9ff'; ctx.font = '800 23px Inter, "Segoe UI", Arial, sans-serif'; ctx.fillText(options.value || '0', x, y - 7); ctx.fillStyle = '#8798b0'; ctx.font = '700 10px Inter, "Segoe UI", Arial, sans-serif'; ctx.fillText(options.label || 'TOTAL', x, y + 15); ctx.restore(); } }; Chart.register(valueLabelsPlugin, centerTextPlugin); const palette = { blue: '#43afff', teal: '#43d3bd', purple: '#927bff', coral: '#ff687b', yellow: '#ffbd52', green: '#41cd90', muted: '#52647f' }; const rankedColors = (length, hue = 205, saturation = 78, lightness = 58, opacity = .84) => Array.from({length}, (_, index) => `hsla(${hue - index * 3}, ${saturation}%, ${lightness - Math.min(index * 1.3, 13)}%, ${opacity})` ); window.StaloneCharts = { currency, compactCurrency, number, tooltip, currencyAxis, countAxis, categoryAxis, palette, rankedColors }; })();