Various fixes for lovely-chart
This commit is contained in:
parent
fd5bcf6851
commit
03f163e528
@ -57,7 +57,9 @@ export function buildChannelMonetizationStatistics(
|
||||
return {
|
||||
// Graphs
|
||||
topHoursGraph: stats.topHoursGraph ? buildGraph(stats.topHoursGraph) : undefined,
|
||||
revenueGraph: buildGraph(stats.revenueGraph, undefined, true, stats.usdRate),
|
||||
revenueGraph: buildGraph(stats.revenueGraph, undefined, {
|
||||
label: 'USD ≈', multiplier: stats.usdRate, prefix: '$',
|
||||
}),
|
||||
|
||||
// Statistics overview
|
||||
balances: buildChannelMonetizationBalances(stats.status),
|
||||
@ -156,7 +158,9 @@ export function buildStoryPublicForwards(
|
||||
}
|
||||
|
||||
export function buildGraph(
|
||||
result: GramJs.TypeStatsGraph, isPercentage?: boolean, isCurrency?: boolean, currencyRate?: number,
|
||||
result: GramJs.TypeStatsGraph, isPercentage?: boolean, secondaryYAxis?: {
|
||||
label: string; multiplier: number; prefix?: string; suffix?: string;
|
||||
},
|
||||
): TypeStatisticsGraph {
|
||||
if (result instanceof GramJs.StatsGraphError) {
|
||||
return {
|
||||
@ -187,15 +191,15 @@ export function buildGraph(
|
||||
hasSecondYAxis,
|
||||
isStacked: data.stacked && !hasSecondYAxis,
|
||||
isPercentage,
|
||||
isCurrency,
|
||||
currencyRate,
|
||||
secondaryYAxis,
|
||||
datasets: y.map((item: any) => {
|
||||
const key = item[0];
|
||||
const values = item.slice(1);
|
||||
|
||||
return {
|
||||
name: data.names[key],
|
||||
color: extractColor(data.colors[key]),
|
||||
values: item.slice(1),
|
||||
values: secondaryYAxis ? values.map((v: number) => v / 1e9) : values,
|
||||
};
|
||||
}),
|
||||
...calculateMinimapRange(data.subchart.defaultZoom, x.slice(1)),
|
||||
|
||||
@ -91,8 +91,12 @@ export interface StatisticsGraph {
|
||||
labels: Array<string | number>;
|
||||
isStacked: boolean;
|
||||
isPercentage?: boolean;
|
||||
isCurrency?: boolean;
|
||||
currencyRate?: number;
|
||||
secondaryYAxis?: {
|
||||
label: string;
|
||||
multiplier: number;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
};
|
||||
hideCaption: boolean;
|
||||
hasSecondYAxis: boolean;
|
||||
minimapRange: {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { GUTTER, AXES_FONT, X_AXIS_HEIGHT, X_AXIS_SHIFT_START, PLOT_TOP_PADDING } from './constants.js';
|
||||
import { formatCryptoValue, humanize } from './format.js';
|
||||
import { humanize } from './format.js';
|
||||
import { getCssColor } from './skin.js';
|
||||
import { applyXEdgeOpacity, applyYEdgeOpacity, xScaleLevelToStep, yScaleLevelToStep } from './formulas.js';
|
||||
import { toPixels } from './Projection.js';
|
||||
@ -47,8 +47,25 @@ export function createAxes(context, data, plotSize, colors) {
|
||||
|
||||
if (data.isPercentage) {
|
||||
_drawYAxisPercents(projection);
|
||||
} else if (data.isCurrency) {
|
||||
_drawYAxisCurrency(projection, data);
|
||||
} else if (data.secondaryYAxis) {
|
||||
_drawYAxisScaled(
|
||||
state,
|
||||
projection,
|
||||
Math.round(yAxisScaleTo || yAxisScale),
|
||||
yMinViewportTo !== undefined ? yMinViewportTo : yMinViewport,
|
||||
yMaxViewportTo !== undefined ? yMaxViewportTo : yMaxViewport,
|
||||
yAxisScaleFrom ? yAxisScaleProgress : 1,
|
||||
);
|
||||
|
||||
_drawSecondaryYAxis(
|
||||
state,
|
||||
projection,
|
||||
Math.round(yAxisScaleTo || yAxisScale),
|
||||
yMinViewportTo !== undefined ? yMinViewportTo : yMinViewport,
|
||||
yMaxViewportTo !== undefined ? yMaxViewportTo : yMaxViewport,
|
||||
yAxisScaleFrom ? yAxisScaleProgress : 1,
|
||||
data.secondaryYAxis,
|
||||
);
|
||||
} else {
|
||||
_drawYAxisScaled(
|
||||
state,
|
||||
@ -125,10 +142,14 @@ export function createAxes(context, data, plotSize, colors) {
|
||||
? getCssColor(colors, colorKey, textOpacity)
|
||||
: getCssColor(colors, 'y-axis-text', textOpacity);
|
||||
|
||||
const label = isSecondary
|
||||
? humanize(value)
|
||||
: `${data.valuePrefix || ''}${humanize(value)}${data.valueSuffix || ''}`;
|
||||
|
||||
if (!isSecondary) {
|
||||
context.fillText(humanize(value), GUTTER, yPx - GUTTER / 2);
|
||||
context.fillText(label, GUTTER, yPx - GUTTER / 2);
|
||||
} else {
|
||||
context.fillText(humanize(value), plotSize.width - GUTTER, yPx - GUTTER / 2);
|
||||
context.fillText(label, plotSize.width - GUTTER, yPx - GUTTER / 2);
|
||||
}
|
||||
|
||||
if (isSecondary) {
|
||||
@ -171,48 +192,24 @@ export function createAxes(context, data, plotSize, colors) {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
function _drawYAxisCurrency(projection, data) {
|
||||
const formatValue = data.datasets[0].values.map(value => formatCryptoValue(value));
|
||||
|
||||
const total = formatValue.reduce((sum, value) => sum + value, 0);
|
||||
const avg1 = total / formatValue.length;
|
||||
const avg2 = total / (formatValue.length / 2);
|
||||
const avg3 = total / (formatValue.length / 3);
|
||||
|
||||
const averageRate1 = avg1 * data.currencyRate;
|
||||
const averageRate2 = avg2 * data.currencyRate;
|
||||
const averageRate3 = avg3 * data.currencyRate;
|
||||
|
||||
const totalAvg = [0, avg1, avg2, avg3];
|
||||
const totalRate = [0, averageRate1, averageRate2, averageRate3];
|
||||
|
||||
const [, height] = projection.getSize();
|
||||
function _drawSecondaryYAxis(state, projection, scaleLevel, yMin, yMax, opacity = 1, secondaryYAxis) {
|
||||
const { multiplier, prefix = '', suffix = '' } = secondaryYAxis;
|
||||
const step = yScaleLevelToStep(scaleLevel);
|
||||
const firstVisibleValue = Math.ceil(yMin / step) * step;
|
||||
const lastVisibleValue = Math.floor(yMax / step) * step;
|
||||
|
||||
context.font = AXES_FONT;
|
||||
context.textAlign = 'left';
|
||||
context.textAlign = 'right';
|
||||
context.textBaseline = 'bottom';
|
||||
context.lineWidth = 1;
|
||||
|
||||
context.beginPath();
|
||||
for (let value = firstVisibleValue; value <= lastVisibleValue; value += step) {
|
||||
const [, yPx] = toPixels(projection, 0, value);
|
||||
const textOpacity = applyXEdgeOpacity(opacity, yPx);
|
||||
const secondaryValue = value * multiplier;
|
||||
|
||||
totalAvg.forEach((value, index) => {
|
||||
const yPx = height - height * (value / Math.max(...formatValue)) + PLOT_TOP_PADDING;
|
||||
|
||||
context.fillStyle = getCssColor(colors, 'y-axis-text', 1);
|
||||
|
||||
context.fillText(`${value.toFixed(2)} TON`, GUTTER, yPx - GUTTER / 4);
|
||||
|
||||
context.textAlign = 'right';
|
||||
context.fillText(`$${totalRate[index].toFixed(2)}`, plotSize.width - GUTTER, yPx - GUTTER / 4);
|
||||
|
||||
context.textAlign = 'left';
|
||||
|
||||
context.moveTo(GUTTER, yPx);
|
||||
context.strokeStyle = getCssColor(colors, 'grid-lines', 1);
|
||||
context.lineTo(plotSize.width - GUTTER, yPx);
|
||||
});
|
||||
|
||||
context.stroke();
|
||||
context.fillStyle = getCssColor(colors, 'y-axis-text', textOpacity);
|
||||
context.fillText(`${prefix}${humanize(secondaryValue)}${suffix}`, plotSize.width - GUTTER, yPx - GUTTER / 2);
|
||||
}
|
||||
}
|
||||
|
||||
return { drawXAxis, drawYAxis };
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { createElement } from './minifiers.js';
|
||||
import { captureEvents } from './captureEvents.js';
|
||||
import { isColorCloseToWhite } from './skin.js';
|
||||
|
||||
export function createTools(container, data, filterCallback) {
|
||||
let _element;
|
||||
@ -35,7 +36,8 @@ export function createTools(container, data, filterCallback) {
|
||||
const control = createElement('a');
|
||||
control.href = '#';
|
||||
control.dataset.key = key;
|
||||
control.className = `lovely-chart--button lovely-chart--color-${data.colors[key].slice(1)} lovely-chart--state-checked`;
|
||||
const darkContent = isColorCloseToWhite(data.colors[key]) ? ' lovely-chart--dark-content' : '';
|
||||
control.className = `lovely-chart--button lovely-chart--color-${data.colors[key].slice(1)} lovely-chart--state-checked${darkContent}`;
|
||||
control.innerHTML = `<span class="lovely-chart--button-check"></span><span class="lovely-chart--button-label">${name}</span>`;
|
||||
|
||||
control.addEventListener('click', (e) => {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { setupCanvas, clearCanvas } from './canvas.js';
|
||||
import { BALLOON_OFFSET, X_AXIS_HEIGHT } from './constants.js';
|
||||
import { BALLOON_OFFSET, X_AXIS_HEIGHT, MAX_TOOLTIP_ITEMS } from './constants.js';
|
||||
import { getPieRadius } from './formulas.js';
|
||||
import {formatCryptoValue, formatInteger, getLabelDate, getLabelTime, statsFormatDayHourFull} from './format.js';
|
||||
import { getCssColor } from './skin.js';
|
||||
import { formatInteger, getLabelDate, getLabelTime, statsFormatDayHourFull } from './format.js';
|
||||
import { getCssColor, isColorCloseToBackground } from './skin.js';
|
||||
import { throttle, throttleWithRaf } from './utils.js';
|
||||
import { addEventListener, createElement } from './minifiers.js';
|
||||
import { toPixels } from './Projection.js';
|
||||
@ -333,12 +333,14 @@ export function createTooltip(container, data, plotSize, colors, onZoom, onFocus
|
||||
}
|
||||
|
||||
function _insertNewDataSet(dataSetContainer, { name, key, value }, totalValue) {
|
||||
const className = `lovely-chart--tooltip-dataset-value lovely-chart--position-right lovely-chart--color-${data.colors[key].slice(1)}`;
|
||||
const colorHex = data.colors[key];
|
||||
const colorClass = isColorCloseToBackground(colors, colorHex) ? '' : ` lovely-chart--color-${colorHex.slice(1)}`;
|
||||
const className = `lovely-chart--tooltip-dataset-value lovely-chart--position-right${colorClass}`;
|
||||
const newDataSet = createElement();
|
||||
newDataSet.className = 'lovely-chart--tooltip-dataset';
|
||||
newDataSet.setAttribute('data-present', 'true');
|
||||
newDataSet.setAttribute('data-name', name);
|
||||
newDataSet.innerHTML = `<span class="lovely-chart--dataset-title">${name}</span><span class="${className}">${formatInteger(value)}</span>`;
|
||||
newDataSet.innerHTML = `<span class="lovely-chart--dataset-title">${name}</span><span class="${className}">${_formatValue(value)}</span>`;
|
||||
_renderPercentageValue(newDataSet, value, totalValue);
|
||||
|
||||
const totalText = dataSetContainer.querySelector(`[data-total="true"]`);
|
||||
@ -352,17 +354,19 @@ export function createTooltip(container, data, plotSize, colors, onZoom, onFocus
|
||||
function _updateDataSet(currentDataSet, { key, value } = {}, totalValue) {
|
||||
currentDataSet.setAttribute('data-present', 'true');
|
||||
|
||||
const valueElement = currentDataSet.querySelector(`.lovely-chart--tooltip-dataset-value.lovely-chart--color-${data.colors[key].slice(1)}:not(.lovely-chart--state-hidden)`);
|
||||
const valueElement = currentDataSet.querySelector(`.lovely-chart--tooltip-dataset-value`);
|
||||
|
||||
if (data.isCurrency) {
|
||||
valueElement.innerHTML = formatCryptoValue(value);
|
||||
} else {
|
||||
valueElement.innerHTML = formatInteger(value);
|
||||
if (valueElement) {
|
||||
valueElement.innerHTML = _formatValue(value);
|
||||
}
|
||||
|
||||
_renderPercentageValue(currentDataSet, value, totalValue);
|
||||
}
|
||||
|
||||
function _formatValue(value) {
|
||||
return `${data.valuePrefix || ''}${formatInteger(value)}${data.valueSuffix || ''}`;
|
||||
}
|
||||
|
||||
function _renderPercentageValue(dataSet, value, totalValue) {
|
||||
if (!data.isPercentage) {
|
||||
return;
|
||||
@ -402,7 +406,10 @@ export function createTooltip(container, data, plotSize, colors, onZoom, onFocus
|
||||
|
||||
const totalValue = statistics.reduce((a, x) => a + x.value, 0);
|
||||
const pointerVector = getPointerVector();
|
||||
const finalStatistics = data.isPie ? statistics.filter(({ value }, index) => _isPieSectorSelected(statistics, value, totalValue, index, pointerVector)) : statistics;
|
||||
const filteredStatistics = statistics.filter(({ value }) => value !== 0);
|
||||
const sortedStatistics = filteredStatistics.sort((a, b) => b.value - a.value);
|
||||
const limitedStatistics = sortedStatistics.slice(0, MAX_TOOLTIP_ITEMS);
|
||||
const finalStatistics = data.isPie ? limitedStatistics.filter(({ value }, index) => _isPieSectorSelected(statistics, value, totalValue, index, pointerVector)) : limitedStatistics;
|
||||
|
||||
finalStatistics.forEach((statItem) => {
|
||||
const currentDataSet = dataSetContainer.querySelector(`[data-name="${statItem.name}"]`);
|
||||
@ -411,17 +418,22 @@ export function createTooltip(container, data, plotSize, colors, onZoom, onFocus
|
||||
_insertNewDataSet(dataSetContainer, statItem, totalValue);
|
||||
} else {
|
||||
_updateDataSet(currentDataSet, statItem, totalValue);
|
||||
dataSetContainer.appendChild(currentDataSet);
|
||||
}
|
||||
});
|
||||
|
||||
if ((data.isBars || data.isSteps) && data.isStacked) {
|
||||
_renderTotal(dataSetContainer, formatInteger(totalValue));
|
||||
if ((data.isBars || data.isSteps || data.isAreas) && data.isStacked) {
|
||||
_renderTotal(dataSetContainer, _formatValue(totalValue));
|
||||
}
|
||||
|
||||
if (data.isCurrency) {
|
||||
_renderCurrencyRate(dataSetContainer, formatCryptoValue(totalValue));
|
||||
if (data.secondaryYAxis) {
|
||||
_renderSecondaryTotal(dataSetContainer, totalValue);
|
||||
}
|
||||
|
||||
// Re-append total rows to keep them at the bottom after sort reordering
|
||||
Array.from(dataSetContainer.querySelectorAll('[data-total="true"]'))
|
||||
.forEach((el) => dataSetContainer.appendChild(el));
|
||||
|
||||
Array.from(dataSetContainer.querySelectorAll('[data-present="false"]'))
|
||||
.forEach((dataSet) => {
|
||||
dataSet.remove();
|
||||
@ -438,10 +450,10 @@ export function createTooltip(container, data, plotSize, colors, onZoom, onFocus
|
||||
const className = `lovely-chart--tooltip-dataset-value lovely-chart--position-right`;
|
||||
if (!totalText) {
|
||||
const newTotalText = createElement();
|
||||
newTotalText.className = 'lovely-chart--tooltip-dataset';
|
||||
newTotalText.className = 'lovely-chart--tooltip-dataset lovely-chart--tooltip-dataset-total';
|
||||
newTotalText.setAttribute('data-present', 'true');
|
||||
newTotalText.setAttribute('data-total', 'true');
|
||||
newTotalText.innerHTML = `<span>All</span><span class="${className}">${totalValue}</span>`;
|
||||
newTotalText.innerHTML = `<span>Total</span><span class="${className}">${totalValue}</span>`;
|
||||
dataSetContainer.appendChild(newTotalText);
|
||||
} else {
|
||||
totalText.setAttribute('data-present', 'true');
|
||||
@ -451,24 +463,25 @@ export function createTooltip(container, data, plotSize, colors, onZoom, onFocus
|
||||
}
|
||||
}
|
||||
|
||||
function _renderCurrencyRate(dataSetContainer, totalValue) {
|
||||
function _renderSecondaryTotal(dataSetContainer, totalValue) {
|
||||
const { label, multiplier, prefix = '', suffix = '' } = data.secondaryYAxis;
|
||||
const totalText = dataSetContainer.querySelector(`[data-total="true"]`);
|
||||
const className = `lovely-chart--tooltip-dataset-value lovely-chart--position-right`;
|
||||
|
||||
const totalUsd = (parseFloat(totalValue) * data.currencyRate).toFixed(2);
|
||||
const secondaryValue = (totalValue * multiplier).toFixed(2);
|
||||
|
||||
if (!totalText) {
|
||||
const newTotalText = createElement();
|
||||
newTotalText.className = 'lovely-chart--tooltip-dataset';
|
||||
newTotalText.className = 'lovely-chart--tooltip-dataset lovely-chart--tooltip-dataset-total';
|
||||
newTotalText.setAttribute('data-present', 'true');
|
||||
newTotalText.setAttribute('data-total', 'true');
|
||||
newTotalText.innerHTML = `<span>USD ≈</span><span class="${className}">$${totalUsd}</span>`;
|
||||
newTotalText.innerHTML = `<span>${label}</span><span class="${className}">${prefix}${secondaryValue}${suffix}</span>`;
|
||||
dataSetContainer.appendChild(newTotalText);
|
||||
} else {
|
||||
totalText.setAttribute('data-present', 'true');
|
||||
|
||||
const valueElement = totalText.querySelector(`.lovely-chart--tooltip-dataset-value:not(.lovely-chart--state-hidden)`);
|
||||
valueElement.innerHTML = `$${totalUsd}`;
|
||||
valueElement.innerHTML = `${prefix}${secondaryValue}${suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ export const PLOT_BARS_WIDTH_SHIFT = 0.5;
|
||||
export const PIE_MINIMUM_VISIBLE_PERCENT = 0.02;
|
||||
|
||||
export const BALLOON_OFFSET = 20;
|
||||
export const MAX_TOOLTIP_ITEMS = 12;
|
||||
|
||||
export const AXES_FONT = '300 10px Helvetica, Arial, sans-serif';
|
||||
export const AXES_MAX_COLUMN_WIDTH = 45;
|
||||
|
||||
@ -1,8 +1,17 @@
|
||||
import { getMaxMin } from './utils.js';
|
||||
import { statsFormatDay, statsFormatDayHour, statsFormatText, statsFormatMin } from './format.js';
|
||||
|
||||
const LABEL_TYPE_TO_FORMATTER = {
|
||||
'day': "statsFormat('day')",
|
||||
'hour': "statsFormat('hour')",
|
||||
'5min': "statsFormat('5min')",
|
||||
'dayHour': 'statsFormatDayHour',
|
||||
'text': undefined,
|
||||
};
|
||||
|
||||
export function analyzeData(data) {
|
||||
const { title, labelFormatter, tooltipFormatter, isStacked, isPercentage, isCurrency, currencyRate, hasSecondYAxis, onZoom, minimapRange, hideCaption, zoomOutLabel } = data;
|
||||
const { title, labelFormatter: labelFormatterRaw, labelType, tooltipFormatter, isStacked, isPercentage, secondaryYAxis, hasSecondYAxis, onZoom, minimapRange, hideCaption, zoomOutLabel, valuePrefix, valueSuffix } = data;
|
||||
const labelFormatter = labelFormatterRaw || (labelType && LABEL_TYPE_TO_FORMATTER[labelType]);
|
||||
const { datasets, labels } = prepareDatasets(data);
|
||||
|
||||
const colors = {};
|
||||
@ -20,13 +29,8 @@ export function analyzeData(data) {
|
||||
}
|
||||
});
|
||||
|
||||
let effectiveLabelFormatter = labelFormatter;
|
||||
if (isCurrency) {
|
||||
effectiveLabelFormatter = 'statsFormat(\'day\')';
|
||||
}
|
||||
|
||||
let xLabels;
|
||||
switch (effectiveLabelFormatter) {
|
||||
switch (labelFormatter) {
|
||||
case 'statsFormatDayHour':
|
||||
xLabels = statsFormatDayHour(labels);
|
||||
break;
|
||||
@ -50,9 +54,10 @@ export function analyzeData(data) {
|
||||
datasets,
|
||||
isStacked,
|
||||
isPercentage,
|
||||
isCurrency,
|
||||
currencyRate,
|
||||
secondaryYAxis,
|
||||
hasSecondYAxis,
|
||||
valuePrefix,
|
||||
valueSuffix,
|
||||
onZoom,
|
||||
isLines: data.type === 'line',
|
||||
isBars: data.type === 'bar',
|
||||
|
||||
@ -27,18 +27,14 @@ export function drawDatasets(
|
||||
let datasetProjection = hasOwnYAxis ? secondaryProjection : projection;
|
||||
|
||||
if (datasetType === 'area') {
|
||||
const { yMin, yMax } = projection.getParams();
|
||||
const yHeight = yMax - yMin;
|
||||
const bottomLine = [
|
||||
{ labelIndex: range.from, stackValue: 0 },
|
||||
{ labelIndex: range.to, stackValue: 0 },
|
||||
];
|
||||
const topLine = [
|
||||
{ labelIndex: range.to, stackValue: yHeight },
|
||||
{ labelIndex: range.from, stackValue: yHeight },
|
||||
];
|
||||
const lowerBoundary = points[i - 1] || bottomLine;
|
||||
const upperBoundary = points[i].slice().reverse();
|
||||
|
||||
datasetPoints = mergeArrays([points[i - 1] || bottomLine, topLine]);
|
||||
datasetPoints = mergeArrays([lowerBoundary, upperBoundary]);
|
||||
}
|
||||
|
||||
if (datasetType === 'pie') {
|
||||
|
||||
@ -59,11 +59,20 @@ function keepThreeDigits(value, decimals) {
|
||||
}
|
||||
|
||||
export function formatInteger(n) {
|
||||
return String(n).replace(/\d(?=(\d{3})+$)/g, '$& ');
|
||||
if (!Number.isInteger(n)) {
|
||||
const abs = Math.abs(n);
|
||||
const decimals = (abs > 0 && abs < 1)
|
||||
? Math.max(2, -Math.floor(Math.log10(abs)) + 1)
|
||||
: 2;
|
||||
const [intPart, decPart] = n.toFixed(decimals).split('.');
|
||||
const trimmed = decPart.replace(/0+$/, '');
|
||||
return trimmed ? addThousandSeparators(intPart) + '.' + trimmed : addThousandSeparators(intPart);
|
||||
}
|
||||
return addThousandSeparators(String(n));
|
||||
}
|
||||
|
||||
export function formatCryptoValue(n) {
|
||||
return Number(n / 10 ** 9);
|
||||
function addThousandSeparators(s) {
|
||||
return s.replace(/\d(?=(\d{3})+$)/g, '$& ');
|
||||
}
|
||||
|
||||
export function getFullLabelDate(label, { isShort = false } = {}) {
|
||||
|
||||
@ -59,7 +59,9 @@ export function createColors(datasetColors) {
|
||||
|
||||
addCssRule(styleSheet, `.lovely-chart--tooltip-dataset-value${baseClass}-${datasetColors[key].slice(1)}`, `color: ${datasetColors[key]}`);
|
||||
addCssRule(styleSheet, `.lovely-chart--button${baseClass}-${datasetColors[key].slice(1)}`, `border-color: ${datasetColors[key]}; color: ${datasetColors[key]}`);
|
||||
addCssRule(styleSheet, `.lovely-chart--button.lovely-chart--state-checked${baseClass}-${datasetColors[key].slice(1)}`, `background-color: ${datasetColors[key]}`);
|
||||
|
||||
const checkedBtnSelector = `.lovely-chart--button.lovely-chart--state-checked${baseClass}-${datasetColors[key].slice(1)}`;
|
||||
addCssRule(styleSheet, checkedBtnSelector, `background-color: ${datasetColors[key]}`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -85,6 +87,20 @@ function buildCssColor([r, g, b, a = 1], opacity = 1) {
|
||||
return `rgba(${r}, ${g}, ${b}, ${a * opacity})`;
|
||||
}
|
||||
|
||||
export function isColorCloseToBackground(colors, hex) {
|
||||
const bg = colors[skin]['tooltip-background'];
|
||||
const fg = hexToChannels(hex);
|
||||
return colorDistance(bg, fg) < 70;
|
||||
}
|
||||
|
||||
export function isColorCloseToWhite(hex) {
|
||||
return colorDistance(hexToChannels(hex), [255, 255, 255]) < 70;
|
||||
}
|
||||
|
||||
function colorDistance([r1, g1, b1], [r2, g2, b2]) {
|
||||
return Math.sqrt((r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2);
|
||||
}
|
||||
|
||||
function addCssRule(sheet, selector, rule) {
|
||||
sheet.insertRule(`${selector} { ${rule} }`, sheet.cssRules.length);
|
||||
}
|
||||
|
||||
@ -15,6 +15,11 @@
|
||||
text-decoration: none;
|
||||
|
||||
background-color: transparent;
|
||||
transition: opacity 150ms ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
&.lovely-chart--state-checked {
|
||||
background-color: var(--text-color);
|
||||
@ -28,6 +33,14 @@
|
||||
transform: translateX(6px);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&.lovely-chart--dark-content .lovely-chart--button-label {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
&.lovely-chart--dark-content .lovely-chart--button-check::after {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -256 1792 1792' version='1.1'%0A%3E%3Cg transform='matrix(1,0,0,-1,7.5932203,1217.0847)' id='g3003'%3E%3Cpath d='m 1671,970 q 0,-40 -28,-68 L 919,178 783,42 Q 755,14 715,14 675,14 647,42 L 511,178 149,540 q -28,28 -28,68 0,40 28,68 l 136,136 q 28,28 68,28 40,0 68,-28 l 294,-295 656,657 q 28,28 68,28 40,0 68,-28 l 136,-136 q 28,-28 28,-68 z' style='fill:%23222222'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
|
||||
&.lovely-chart--state-shake {
|
||||
@ -64,6 +77,7 @@
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -256 1792 1792' version='1.1'%0A%3E%3Cg transform='matrix(1,0,0,-1,7.5932203,1217.0847)' id='g3003'%3E%3Cpath d='m 1671,970 q 0,-40 -28,-68 L 919,178 783,42 Q 755,14 715,14 675,14 647,42 L 511,178 149,540 q -28,28 -28,68 0,40 28,68 l 136,136 q 28,28 68,28 40,0 68,-28 l 294,-295 656,657 q 28,28 68,28 40,0 68,-28 l 136,-136 q 28,-28 28,-68 z' style='fill:white'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.lovely-chart--button-label {
|
||||
|
||||
@ -1,7 +1,27 @@
|
||||
.lovely-chart--container {
|
||||
--background-color: #ffffff;
|
||||
--text-color: #222222;
|
||||
--minimap-mask: #{rgba(#E2EEF9, 0.6)};
|
||||
--minimap-slider: #C0D1E1;
|
||||
--grid-lines: #{rgba(#182D3B, 0.1)};
|
||||
--zoom-out-text: #108BE3;
|
||||
--tooltip-background: #ffffff;
|
||||
--tooltip-arrow: #D2D5D7;
|
||||
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
html.theme-dark & {
|
||||
--background-color: #242F3E;
|
||||
--text-color: #ffffff;
|
||||
--minimap-mask: #{rgba(#304259, 0.6)};
|
||||
--minimap-slider: #56626D;
|
||||
--grid-lines: #{rgba(#FFFFFF, 0.1)};
|
||||
--zoom-out-text: #48AAF0;
|
||||
--tooltip-background: #1c2533;
|
||||
--tooltip-arrow: #D2D5D7;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
@ -16,13 +16,7 @@
|
||||
|
||||
&-title {
|
||||
float: left;
|
||||
margin: 0 1rem;
|
||||
font-size: 16px;
|
||||
text-transform: lowercase;
|
||||
|
||||
&:first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
&-caption {
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
.lovely-chart--tooltip-title {
|
||||
position: relative;
|
||||
padding-bottom: 5px;
|
||||
padding-bottom: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
|
||||
@ -89,7 +89,12 @@
|
||||
|
||||
.lovely-chart--tooltip-dataset-value {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
margin-left: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.lovely-chart--tooltip-dataset-total {
|
||||
margin-top: 6px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
.lovely-chart--container {
|
||||
--background-color: #ffffff;
|
||||
--text-color: #222222;
|
||||
--minimap-mask: #{rgba(#E2EEF9, 0.6)};
|
||||
--minimap-slider: #C0D1E1;
|
||||
--grid-lines: #{rgba(#182D3B, 0.1)};
|
||||
--zoom-out-text: #108BE3;
|
||||
--tooltip-background: #ffffff;
|
||||
--tooltip-arrow: #D2D5D7;
|
||||
}
|
||||
|
||||
html.theme-dark .lovely-chart--container {
|
||||
--background-color: #242F3E;
|
||||
--text-color: #ffffff;
|
||||
--minimap-mask: #{rgba(#304259, 0.6)};
|
||||
--minimap-slider: #56626D;
|
||||
--grid-lines: #{rgba(#FFFFFF, 0.1)};
|
||||
--zoom-out-text: #48AAF0;
|
||||
--tooltip-background: #1c2533;
|
||||
--tooltip-arrow: #D2D5D7;
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
@use 'variables';
|
||||
@use 'animations';
|
||||
@use 'common';
|
||||
@use 'header';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user