From 7fff0e5806c54a89a4bddb56be5e3f72f13e6fd5 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Mon, 10 Mar 2025 17:09:10 +0530 Subject: [PATCH] force light color in exports --- lib/components/SChartBar.vue | 12 ++-- lib/components/SChartPie.vue | 10 +-- lib/support/Chart.ts | 64 +++++++++++-------- .../components/SChart.01_Playground.story.vue | 4 +- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/lib/components/SChartBar.vue b/lib/components/SChartBar.vue index d945de5d..eccaab3a 100644 --- a/lib/components/SChartBar.vue +++ b/lib/components/SChartBar.vue @@ -2,7 +2,7 @@ import { useElementSize } from '@vueuse/core' import * as d3 from 'd3' import { ref, watch } from 'vue' -import { type ChartColor, type KV, scheme } from '../support/Chart' +import { type ChartColor, type KV, c, scheme } from '../support/Chart' export type { ChartColor, KV } @@ -99,7 +99,7 @@ function renderChart({ .attr('transform', `translate(0,${height})`) .call(vertical ? d3.axisBottom(x) : d3.axisBottom(y).ticks(props.ticks)) .selectAll('text') - .attr('fill', 'var(--c-text-2)') + .attr('fill', c.text2) .style('font-size', '14px') .style('text-anchor', 'middle') @@ -113,7 +113,7 @@ function renderChart({ .append('g') .call(vertical ? d3.axisLeft(y).ticks(props.ticks) : d3.axisLeft(x)) .selectAll('text') - .attr('fill', 'var(--c-text-2)') + .attr('fill', c.text2) .style('font-size', '14px') // Remove Y axis line @@ -127,7 +127,7 @@ function renderChart({ .data(y.ticks(props.ticks)) .enter() .append('line') - .attr('stroke', 'var(--c-divider)') + .attr('stroke', c.divider) .attr('stroke-dasharray', '2,2') if (vertical) { @@ -150,7 +150,7 @@ function renderChart({ .append('text') .attr('x', width / 2) .attr('y', height + xLabelOffset) - .attr('fill', 'var(--c-text-2)') + .attr('fill', c.text2) .style('font-size', '14px') .style('text-anchor', 'middle') .html(props.xLabel) @@ -161,7 +161,7 @@ function renderChart({ .attr('x', -height / 2) .attr('y', -yLabelOffset) .attr('transform', 'rotate(-90)') - .attr('fill', 'var(--c-text-2)') + .attr('fill', c.text2) .style('font-size', '14px') .style('text-anchor', 'middle') .html(props.yLabel) diff --git a/lib/components/SChartPie.vue b/lib/components/SChartPie.vue index 6a360a12..6322c9aa 100644 --- a/lib/components/SChartPie.vue +++ b/lib/components/SChartPie.vue @@ -2,7 +2,7 @@ import { useElementSize } from '@vueuse/core' import * as d3 from 'd3' import { ref, watch } from 'vue' -import { type ChartColor, type KV, scheme } from '../support/Chart' +import { type ChartColor, type KV, c, scheme } from '../support/Chart' export type { ChartColor, KV } @@ -132,7 +132,7 @@ function renderChart({ .attr('x', 30) .attr('y', 14 / 2) .attr('dy', '0.35em') - .attr('fill', 'var(--c-text-2)') + .attr('fill', c.text2) .html((d) => props.legendFormatKey(d.data)) // Show value next to the legend @@ -141,7 +141,7 @@ function renderChart({ .attr('x', 30 + props.legendPadding) .attr('y', 14 / 2) .attr('dy', '0.35em') - .attr('fill', 'var(--c-text-1)') + .attr('fill', c.text1) .attr('text-anchor', 'end') .html((d) => props.legendFormatValue(d.data)) @@ -237,7 +237,7 @@ function renderChart({ labels .append('polyline') - .attr('stroke', 'var(--c-divider)') + .attr('stroke', c.divider) .attr('fill', 'none') .attr('points', (d) => { const posA = arc.centroid(d) @@ -255,9 +255,9 @@ function renderChart({ return `translate(${pos})` }) .attr('dy', '0.35em') + .attr('fill', c.text2) .attr('text-anchor', (d) => leftOrRight(d) === 1 ? 'start' : 'end') .style('font-size', '14px') - .style('fill', 'var(--c-text-2)') .html((d) => props.labelFormat(d.data)) if (animate) { diff --git a/lib/support/Chart.ts b/lib/support/Chart.ts index abaeeb99..71722224 100644 --- a/lib/support/Chart.ts +++ b/lib/support/Chart.ts @@ -12,6 +12,18 @@ import html2canvas from 'html2canvas' const isLightDarkSupported = typeof CSS !== 'undefined' && CSS.supports('color', 'light-dark(#000, #fff)') +export const c = { + text1: isLightDarkSupported ? 'light-dark(#1c2024, #edeef0)' : 'var(--c-text-1)', + text2: isLightDarkSupported ? 'light-dark(#0007149e, #eff5ffb0)' : 'var(--c-text-2)', + divider: isLightDarkSupported ? 'light-dark(#e0e0e1, #2e3035)' : 'var(--c-divider)' +} + +const c_light = { + text1: '#1c2024', + text2: '#0007149e', + divider: '#e0e0e1' +} + // radixLight.9, radixDark.10 export const chartColors = { // orange: isLightDarkSupported ? 'light-dark(#f76b15, #ff801f)' : '#f76b15', @@ -79,37 +91,37 @@ export async function exportAsPng(_el: any, fileName = 'chart.png', delay = 0): const SCard = el.closest('.SCard') if (!(SCard instanceof HTMLElement)) { return } - // TODO: force render in light mode const canvas = await html2canvas(SCard, { scale: 2, - backgroundColor: getBackgroundColor(el), logging: false, - ignoreElements: (el) => el.classList.contains('SControlActionBar') + ignoreElements: (el) => el.classList.contains('SControlActionBar'), + onclone(document, element) { + document.documentElement.classList.remove('dark') + for (const el of element.querySelectorAll('*')) { + el.style.backgroundColor = 'transparent' + for (const [key, value] of Object.entries(c)) { + for (const attr of ['fill', 'stroke'] as const) { + if (el.getAttribute(attr) === value) { + el.style[attr] = c_light[key as keyof typeof c_light] + } + } + } + } + } }) + // // for debugging + // const blob = await new Promise((resolve) => { + // canvas.toBlob((b) => { + // if (b) { + // resolve(b) + // } else { + // resolve(new Blob()) + // } + // }, 'image/png') + // }) + // window.open(URL.createObjectURL(blob), '_blank') + const dataUrl = canvas.toDataURL('image/png') FileSaver.saveAs(dataUrl, fileName) } - -function getBackgroundColor(el: HTMLElement | null): string { - const defaultColor = 'rgba(0, 0, 0, 0)' - let color = 'rgba(0, 0, 0, 0)' - - while (el) { - const bgColor = window.getComputedStyle(el).backgroundColor - if (bgColor !== defaultColor) { - color = bgColor - break - } - - el = el.parentElement - } - - if (color === defaultColor) { - return document.documentElement.classList.contains('dark') - ? 'rgba(0, 0, 0, 1)' - : 'rgba(255, 255, 255, 1)' - } - - return color -} diff --git a/stories/components/SChart.01_Playground.story.vue b/stories/components/SChart.01_Playground.story.vue index ebbe3048..7e8aa40b 100644 --- a/stories/components/SChart.01_Playground.story.vue +++ b/stories/components/SChart.01_Playground.story.vue @@ -10,7 +10,7 @@ import SControlActionBarButton from 'sefirot/components/SControlActionBarButton. import SControlLeft from 'sefirot/components/SControlLeft.vue' import SControlRight from 'sefirot/components/SControlRight.vue' import SControlText from 'sefirot/components/SControlText.vue' -import { type KV, exportAsPng } from 'sefirot/support/Chart' +import { type KV, c, exportAsPng } from 'sefirot/support/Chart' import { ref, useTemplateRef } from 'vue' const title = 'Components / SChart / 01. Playground' @@ -34,7 +34,7 @@ function tooltipFormat(d: KV) { } function labelFormat(d: KV) { - return `${d.key} – ${d.value}` + return `${d.key} – ${d.value}` } function state() {