mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-14 22:18:19 +00:00
Prepare 1.0.0-beta.3
This commit is contained in:
@@ -129,6 +129,7 @@
|
||||
const dark = mode === 'dark';
|
||||
document.documentElement.classList.toggle('dark', dark);
|
||||
try { localStorage.setItem('themeMode', dark ? 'dark' : 'light'); } catch (_) {}
|
||||
document.dispatchEvent(new CustomEvent('basecoat:themechange', { detail: { mode: dark ? 'dark' : 'light' } }));
|
||||
};
|
||||
|
||||
const getTheme = () => document.documentElement.classList.contains('dark') ? 'dark' : 'light';
|
||||
|
||||
+112
-9
@@ -1,6 +1,8 @@
|
||||
(() => {
|
||||
const instances = new WeakMap();
|
||||
const activeCanvases = new Set();
|
||||
const defaultColors = ['var(--chart-1)', 'var(--chart-2)', 'var(--chart-3)', 'var(--chart-4)', 'var(--chart-5)'];
|
||||
let refreshFrame = null;
|
||||
|
||||
const isChartJsData = (data) => {
|
||||
return data && typeof data === 'object' && Array.isArray(data.datasets);
|
||||
@@ -54,6 +56,32 @@
|
||||
|
||||
const colorForIndex = (index) => defaultColors[index % defaultColors.length];
|
||||
|
||||
const cloneChartData = (chartData) => {
|
||||
if (!isChartJsData(chartData)) return chartData;
|
||||
|
||||
return {
|
||||
...chartData,
|
||||
labels: Array.isArray(chartData.labels) ? [...chartData.labels] : chartData.labels,
|
||||
datasets: chartData.datasets.map((dataset) => ({
|
||||
...dataset,
|
||||
data: Array.isArray(dataset.data) ? [...dataset.data] : dataset.data,
|
||||
backgroundColor: Array.isArray(dataset.backgroundColor) ? [...dataset.backgroundColor] : dataset.backgroundColor,
|
||||
borderColor: Array.isArray(dataset.borderColor) ? [...dataset.borderColor] : dataset.borderColor,
|
||||
hoverBackgroundColor: Array.isArray(dataset.hoverBackgroundColor) ? [...dataset.hoverBackgroundColor] : dataset.hoverBackgroundColor,
|
||||
hoverBorderColor: Array.isArray(dataset.hoverBorderColor) ? [...dataset.hoverBorderColor] : dataset.hoverBorderColor,
|
||||
})),
|
||||
};
|
||||
};
|
||||
|
||||
const clonePlainObject = (value) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return value;
|
||||
|
||||
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
||||
key,
|
||||
item && typeof item === 'object' && !Array.isArray(item) ? clonePlainObject(item) : item,
|
||||
]));
|
||||
};
|
||||
|
||||
const resolveColor = (color, element) => {
|
||||
if (Array.isArray(color)) return color.map(item => resolveColor(item, element));
|
||||
if (typeof color !== 'string') return color;
|
||||
@@ -148,15 +176,25 @@
|
||||
|
||||
const usesCartesianScales = (type) => !['pie', 'doughnut', 'polarArea', 'radar'].includes(type);
|
||||
|
||||
const moveCanvasClassesToContainer = (canvas, container) => {
|
||||
const classes = Array.from(canvas.classList).filter((className) => className !== 'chart');
|
||||
if (classes.length === 0) return;
|
||||
|
||||
container.classList.add(...classes);
|
||||
canvas.classList.remove(...classes);
|
||||
};
|
||||
|
||||
const ensureContainer = (canvas) => {
|
||||
if (canvas.parentElement?.classList.contains('chart')) {
|
||||
canvas.parentElement.dataset.basecoatChartContainer = 'true';
|
||||
moveCanvasClassesToContainer(canvas, canvas.parentElement);
|
||||
return canvas.parentElement;
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'chart';
|
||||
container.dataset.basecoatChartContainer = 'true';
|
||||
moveCanvasClassesToContainer(canvas, container);
|
||||
canvas.insertAdjacentElement('beforebegin', container);
|
||||
container.append(canvas);
|
||||
return container;
|
||||
@@ -170,7 +208,7 @@
|
||||
};
|
||||
|
||||
const toChartData = ({ type, labelKey, data, series }) => {
|
||||
if (isChartJsData(data)) return data;
|
||||
if (isChartJsData(data)) return cloneChartData(data);
|
||||
|
||||
const rows = Array.isArray(data) ? data : [];
|
||||
const entries = seriesEntries(series);
|
||||
@@ -223,6 +261,23 @@
|
||||
return typeof value === 'number' ? value.toLocaleString() : String(value);
|
||||
};
|
||||
|
||||
const colorForTooltipItem = (item) => {
|
||||
const elementColor = item.element?.options?.backgroundColor || item.element?.options?.borderColor;
|
||||
if (typeof elementColor === 'string') return elementColor;
|
||||
|
||||
const backgroundColor = Array.isArray(item.dataset.backgroundColor)
|
||||
? item.dataset.backgroundColor[item.dataIndex]
|
||||
: item.dataset.backgroundColor;
|
||||
if (typeof backgroundColor === 'string') return backgroundColor;
|
||||
|
||||
const borderColor = Array.isArray(item.dataset.borderColor)
|
||||
? item.dataset.borderColor[item.dataIndex]
|
||||
: item.dataset.borderColor;
|
||||
if (typeof borderColor === 'string') return borderColor;
|
||||
|
||||
return colorForIndex(item.datasetIndex);
|
||||
};
|
||||
|
||||
const ensureTooltip = (canvas) => {
|
||||
let tooltip = canvas._basecoatChartTooltip;
|
||||
if (tooltip) return tooltip;
|
||||
@@ -258,7 +313,7 @@
|
||||
const items = document.createElement('div');
|
||||
items.className = 'chart-tooltip-items';
|
||||
tooltip.dataPoints.forEach((item) => {
|
||||
const color = item.dataset.borderColor || item.dataset.backgroundColor || colorForIndex(item.datasetIndex);
|
||||
const color = colorForTooltipItem(item);
|
||||
const value = item.formattedValue || formatValue(item.raw);
|
||||
|
||||
const row = document.createElement('div');
|
||||
@@ -284,8 +339,8 @@
|
||||
const rect = chart.canvas.getBoundingClientRect();
|
||||
element.replaceChildren(...children);
|
||||
element.hidden = false;
|
||||
element.style.left = `${rect.left + window.scrollX + tooltip.caretX}px`;
|
||||
element.style.top = `${rect.top + window.scrollY + tooltip.caretY}px`;
|
||||
element.style.left = `${rect.left + tooltip.caretX}px`;
|
||||
element.style.top = `${rect.top + tooltip.caretY}px`;
|
||||
};
|
||||
|
||||
const createLegendPlugin = (canvas, enabled) => ({
|
||||
@@ -308,7 +363,12 @@
|
||||
|
||||
const indicator = document.createElement('span');
|
||||
indicator.className = 'chart-legend-indicator';
|
||||
indicator.style.setProperty('--chart-indicator-color', item.fillStyle || item.strokeStyle || colorForIndex(item.datasetIndex || item.index || 0));
|
||||
const indicatorColor = typeof item.fillStyle === 'string'
|
||||
? item.fillStyle
|
||||
: typeof item.strokeStyle === 'string'
|
||||
? item.strokeStyle
|
||||
: colorForIndex(item.datasetIndex || item.index || 0);
|
||||
indicator.style.setProperty('--chart-indicator-color', indicatorColor);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.textContent = item.text;
|
||||
@@ -345,7 +405,7 @@
|
||||
const tooltip = readBoolean(canvas, config, 'tooltip', 'chartTooltip', true);
|
||||
const data = config.data || readJsonAttribute(canvas, 'data-chart-data') || [];
|
||||
const series = config.series || readJsonAttribute(canvas, 'data-chart-series') || {};
|
||||
const chartData = config.chartData || toChartData({ type, labelKey, data, series });
|
||||
const chartData = config.chartData ? cloneChartData(config.chartData) : toChartData({ type, labelKey, data, series });
|
||||
const userOptions = config.options || {};
|
||||
const legendPlugin = createLegendPlugin(canvas, legend);
|
||||
const container = ensureContainer(canvas);
|
||||
@@ -359,19 +419,21 @@
|
||||
border: { display: false, color: borderColor },
|
||||
grid: { display: false, color: gridColor },
|
||||
ticks: { color: mutedColor, padding: 8 },
|
||||
...(userOptions.scales?.x || {}),
|
||||
...clonePlainObject(userOptions.scales?.x || {}),
|
||||
},
|
||||
y: {
|
||||
border: { display: false, color: borderColor },
|
||||
grid: { color: gridColor },
|
||||
ticks: { display: false, color: mutedColor },
|
||||
...(userOptions.scales?.y || {}),
|
||||
...clonePlainObject(userOptions.scales?.y || {}),
|
||||
},
|
||||
}
|
||||
: {};
|
||||
const scales = {
|
||||
...defaultScales,
|
||||
...(Object.fromEntries(Object.entries(userOptions.scales || {}).filter(([key]) => key !== 'x' && key !== 'y'))),
|
||||
...(Object.fromEntries(Object.entries(userOptions.scales || {})
|
||||
.filter(([key]) => key !== 'x' && key !== 'y')
|
||||
.map(([key, value]) => [key, clonePlainObject(value)]))),
|
||||
};
|
||||
|
||||
canvas.classList.remove('chart');
|
||||
@@ -407,14 +469,18 @@
|
||||
});
|
||||
|
||||
instances.set(canvas, chart);
|
||||
activeCanvases.add(canvas);
|
||||
canvas.dataset.chartInitialized = 'true';
|
||||
canvas.chart = chart;
|
||||
canvas._basecoatChartConfig = config;
|
||||
canvas._destroy = () => {
|
||||
chart.destroy();
|
||||
removeGeneratedElements(canvas);
|
||||
instances.delete(canvas);
|
||||
activeCanvases.delete(canvas);
|
||||
canvas.removeAttribute('data-chart-initialized');
|
||||
delete canvas._basecoatChartContainer;
|
||||
delete canvas._basecoatChartConfig;
|
||||
delete canvas.chart;
|
||||
delete canvas._destroy;
|
||||
};
|
||||
@@ -428,7 +494,44 @@
|
||||
return charts.length === 1 ? charts[0] : charts;
|
||||
};
|
||||
|
||||
const refreshCharts = () => {
|
||||
refreshFrame = null;
|
||||
|
||||
Array.from(activeCanvases).forEach((canvas) => {
|
||||
if (!canvas.isConnected) {
|
||||
canvas._destroy?.();
|
||||
activeCanvases.delete(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
initChart(canvas, canvas._basecoatChartConfig || {});
|
||||
});
|
||||
};
|
||||
|
||||
const scheduleRefresh = () => {
|
||||
if (refreshFrame !== null) return;
|
||||
refreshFrame = requestAnimationFrame(refreshCharts);
|
||||
};
|
||||
|
||||
const observeTokenChanges = () => {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
if (mutations.some((mutation) => mutation.attributeName === 'class' || mutation.attributeName === 'data-style-variant')) {
|
||||
scheduleRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class', 'data-style-variant'],
|
||||
});
|
||||
|
||||
document.addEventListener('basecoat:themechange', scheduleRefresh);
|
||||
document.addEventListener('basecoat:stylechange', scheduleRefresh);
|
||||
};
|
||||
|
||||
if (window.basecoat) {
|
||||
window.basecoat.chart = chart;
|
||||
}
|
||||
|
||||
observeTokenChanges();
|
||||
})();
|
||||
|
||||
+1
-1
@@ -151,7 +151,7 @@
|
||||
? `<button type="button" class="btn" data-toast-action onclick="${action.onclick}">${action.label}</button>`
|
||||
: '';
|
||||
const cancelHtml = cancel
|
||||
? `<button type="button" class="btn-outline h-6 text-xs px-2.5 rounded-sm" data-toast-cancel onclick="${cancel?.onclick || ''}">${cancel.label}</button>`
|
||||
? `<button type="button" class="btn h-6 text-xs px-2.5 rounded-sm" data-variant="outline" data-toast-cancel onclick="${cancel?.onclick || ''}">${cancel.label}</button>`
|
||||
: '';
|
||||
|
||||
const footerHtml = actionHtml || cancelHtml ? `<footer>${actionHtml}${cancelHtml}</footer>` : '';
|
||||
|
||||
Reference in New Issue
Block a user