Adjust colors to be controlled from outside chart component (#1568)

* Adjust colors to be controlled from outside chart component

* Access colors from source of truth

* Change access method to omit projects from params

* Just omit projects from query
This commit is contained in:
Carter
2024-01-11 16:11:26 -08:00
committed by GitHub
parent 81948a5c29
commit 9add661a5b
3 changed files with 76 additions and 16 deletions

View File

@@ -88,7 +88,7 @@ function formatTooltipValue(value, props) {
} }
function generateListEntry(value, index, _, w, props) { function generateListEntry(value, index, _, w, props) {
const color = props.colors[index % props.colors.length] const color = w.globals.colors?.[index]
return `<div class="list-entry"> return `<div class="list-entry">
<span class="circle" style="background-color: ${color}"></span> <span class="circle" style="background-color: ${color}"></span>
@@ -102,7 +102,7 @@ function generateListEntry(value, index, _, w, props) {
} }
function generateTooltip({ series, seriesIndex, dataPointIndex, w }, props) { function generateTooltip({ series, seriesIndex, dataPointIndex, w }, props) {
const label = w.globals.lastXAxis.categories[dataPointIndex] const label = w.globals.lastXAxis.categories?.[dataPointIndex]
const formattedLabel = props.formatLabels(label) const formattedLabel = props.formatLabels(label)
@@ -158,7 +158,7 @@ function generateTooltip({ series, seriesIndex, dataPointIndex, w }, props) {
return tooltip return tooltip
} }
const chartOptions = ref({ const chartOptions = {
chart: { chart: {
id: props.name, id: props.name,
fontFamily: fontFamily:
@@ -254,7 +254,7 @@ const chartOptions = ref({
tooltip: { tooltip: {
custom: (d) => generateTooltip(d, props), custom: (d) => generateTooltip(d, props),
}, },
}) }
const fillOptions = { const fillOptions = {
colors: props.colors, colors: props.colors,
@@ -292,7 +292,6 @@ const resetChart = () => {
xaxis: { xaxis: {
categories: props.labels, categories: props.labels,
}, },
colors: props.colors,
}) })
chart.value.resetSeries() chart.value.resetSeries()
legendValues.value.forEach((legend) => { legendValues.value.forEach((legend) => {
@@ -300,8 +299,16 @@ const resetChart = () => {
}) })
} }
const updateColors = (colors) => {
chart.value.updateOptions({
colors,
})
chart.value.resetSeries()
}
defineExpose({ defineExpose({
resetChart, resetChart,
updateColors,
flipLegend, flipLegend,
}) })
</script> </script>

View File

@@ -73,6 +73,9 @@
</span> </span>
</h2> </h2>
<div class="chart-controls__buttons"> <div class="chart-controls__buttons">
<Button v-tooltip="'Toggle project colors'" icon-only @click="onToggleColors">
<EyeIcon />
</Button>
<Button v-tooltip="'Download this data as CSV'" icon-only @click="onDownloadSetAsCSV"> <Button v-tooltip="'Download this data as CSV'" icon-only @click="onDownloadSetAsCSV">
<DownloadIcon /> <DownloadIcon />
</Button> </Button>
@@ -144,7 +147,9 @@
> >
<div <div
:style="{ :style="{
'--color-brand': intToRgba(project.color, project.id, theme || 'dark'), '--color-brand': isUsingProjectColors
? intToRgba(project.color, project.id, theme.value ?? undefined)
: getDefaultColor(project.id),
}" }"
class="legend__item__color" class="legend__item__color"
></div> ></div>
@@ -281,6 +286,7 @@ import {
formatNumber, formatNumber,
DropdownSelect, DropdownSelect,
formatCategoryHeader, formatCategoryHeader,
EyeIcon,
} from 'omorphia' } from 'omorphia'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { defineProps, ref, computed } from 'vue' import { defineProps, ref, computed } from 'vue'
@@ -357,6 +363,24 @@ const projectIsOnDisplay = (id: string) => {
return selectedDisplayProjects.value.some((p) => p.id === id) return selectedDisplayProjects.value.some((p) => p.id === id)
} }
const setChartColors = (updatedVal: Ref<boolean>) => {
downloadsChart.value?.updateColors(
updatedVal.value
? analytics.formattedData.value.downloads.chart.colors
: analytics.formattedData.value.downloads.chart.defaultColors
)
viewsChart.value?.updateColors(
updatedVal.value
? analytics.formattedData.value.views.chart.colors
: analytics.formattedData.value.views.chart.defaultColors
)
revenueChart.value?.updateColors(
updatedVal.value
? analytics.formattedData.value.revenue.chart.colors
: analytics.formattedData.value.revenue.chart.defaultColors
)
}
const resetCharts = () => { const resetCharts = () => {
downloadsChart.value?.resetChart() downloadsChart.value?.resetChart()
viewsChart.value?.resetChart() viewsChart.value?.resetChart()
@@ -365,8 +389,15 @@ const resetCharts = () => {
tinyDownloadChart.value?.resetChart() tinyDownloadChart.value?.resetChart()
tinyViewChart.value?.resetChart() tinyViewChart.value?.resetChart()
tinyRevenueChart.value?.resetChart() tinyRevenueChart.value?.resetChart()
setChartColors(isUsingProjectColors)
} }
const isUsingProjectColors = ref(true)
watch(() => isUsingProjectColors, setChartColors, {
deep: true,
})
const analytics = useFetchAllAnalytics(resetCharts, selectedDisplayProjects) const analytics = useFetchAllAnalytics(resetCharts, selectedDisplayProjects)
const { startDate, endDate, timeRange, timeResolution } = analytics const { startDate, endDate, timeRange, timeResolution } = analytics
@@ -425,6 +456,9 @@ const downloadSelectedSetAsCSV = () => {
} }
const onDownloadSetAsCSV = useClientTry(async () => await downloadSelectedSetAsCSV()) const onDownloadSetAsCSV = useClientTry(async () => await downloadSelectedSetAsCSV())
const onToggleColors = () => {
isUsingProjectColors.value = !isUsingProjectColors.value
}
</script> </script>
<script lang="ts"> <script lang="ts">

View File

@@ -38,7 +38,7 @@ const hashProjectId = (projectId) => {
return projectId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % 30 return projectId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % 30
} }
const defaultColors = ['#ff496e', '#ffa347', '#1bd96a', '#4f9cff', '#c78aff'] export const defaultColors = ['#ff496e', '#ffa347', '#1bd96a', '#4f9cff', '#c78aff']
/** /**
* @param {string | number} value * @param {string | number} value
@@ -51,7 +51,7 @@ export const getDefaultColor = (value) => {
return defaultColors[value % defaultColors.length] return defaultColors[value % defaultColors.length]
} }
export const intToRgba = (color, projectId = 'Unknown', theme) => { export const intToRgba = (color, projectId = 'Unknown', theme = 'dark') => {
const hash = hashProjectId(projectId) const hash = hashProjectId(projectId)
if (!color || color === 0) { if (!color || color === 0) {
@@ -109,6 +109,7 @@ const emptyAnalytics = {
}, },
], ],
colors: [], colors: [],
defaultColors: [],
}, },
} }
@@ -204,6 +205,10 @@ export const processAnalytics = (category, projects, labelFn, sortFn, mapFn, cha
return intToRgba(project.color, project.id, theme.value) return intToRgba(project.color, project.id, theme.value)
}), }),
defaultColors: projectData.map((_, i) => {
const project = chartData[i]
return getDefaultColor(project.id)
}),
}, },
} }
} }
@@ -300,11 +305,10 @@ export const useFetchAllAnalytics = (onDataRefresh, projects) => {
const fetchData = async (query) => { const fetchData = async (query) => {
const normalQuery = new URLSearchParams(query) const normalQuery = new URLSearchParams(query)
const revenueQuery = new URLSearchParams(query)
const revQuery = new URLSearchParams(query) revenueQuery.delete('projects')
const qs = normalQuery.toString() const qs = normalQuery.toString()
const revQs = revQuery.toString() const revenueQs = revenueQuery.toString()
try { try {
loading.value = true loading.value = true
@@ -313,14 +317,29 @@ export const useFetchAllAnalytics = (onDataRefresh, projects) => {
const responses = await Promise.all([ const responses = await Promise.all([
useFetchAnalytics(`analytics/downloads?${qs}`), useFetchAnalytics(`analytics/downloads?${qs}`),
useFetchAnalytics(`analytics/views?${qs}`), useFetchAnalytics(`analytics/views?${qs}`),
useFetchAnalytics(`analytics/revenue?${revQs}`), useFetchAnalytics(`analytics/revenue?${revenueQs}`),
useFetchAnalytics(`analytics/countries/downloads?${qs}`), useFetchAnalytics(`analytics/countries/downloads?${qs}`),
useFetchAnalytics(`analytics/countries/views?${qs}`), useFetchAnalytics(`analytics/countries/views?${qs}`),
]) ])
downloadData.value = responses[0] || {} // collect project ids from projects.value into a set
viewData.value = responses[1] || {} const projectIds = new Set()
revenueData.value = responses[2] || {} projects.value.forEach((p) => projectIds.add(p.id))
const filterProjectIds = (data) => {
const filtered = {}
Object.entries(data).forEach(([id, values]) => {
if (projectIds.has(id)) {
filtered[id] = values
}
})
return filtered
}
downloadData.value = filterProjectIds(responses[0] || {})
viewData.value = filterProjectIds(responses[1] || {})
revenueData.value = filterProjectIds(responses[2] || {})
downloadsByCountry.value = responses[3] || {} downloadsByCountry.value = responses[3] || {}
viewsByCountry.value = responses[4] || {} viewsByCountry.value = responses[4] || {}
} catch (e) { } catch (e) {