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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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) {
const color = props.colors[index % props.colors.length]
const color = w.globals.colors?.[index]
return `<div class="list-entry">
<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) {
const label = w.globals.lastXAxis.categories[dataPointIndex]
const label = w.globals.lastXAxis.categories?.[dataPointIndex]
const formattedLabel = props.formatLabels(label)
@ -158,7 +158,7 @@ function generateTooltip({ series, seriesIndex, dataPointIndex, w }, props) {
return tooltip
}
const chartOptions = ref({
const chartOptions = {
chart: {
id: props.name,
fontFamily:
@ -254,7 +254,7 @@ const chartOptions = ref({
tooltip: {
custom: (d) => generateTooltip(d, props),
},
})
}
const fillOptions = {
colors: props.colors,
@ -292,7 +292,6 @@ const resetChart = () => {
xaxis: {
categories: props.labels,
},
colors: props.colors,
})
chart.value.resetSeries()
legendValues.value.forEach((legend) => {
@ -300,8 +299,16 @@ const resetChart = () => {
})
}
const updateColors = (colors) => {
chart.value.updateOptions({
colors,
})
chart.value.resetSeries()
}
defineExpose({
resetChart,
updateColors,
flipLegend,
})
</script>

View File

@ -73,6 +73,9 @@
</span>
</h2>
<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">
<DownloadIcon />
</Button>
@ -144,7 +147,9 @@
>
<div
: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"
></div>
@ -281,6 +286,7 @@ import {
formatNumber,
DropdownSelect,
formatCategoryHeader,
EyeIcon,
} from 'omorphia'
import dayjs from 'dayjs'
import { defineProps, ref, computed } from 'vue'
@ -357,6 +363,24 @@ const projectIsOnDisplay = (id: string) => {
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 = () => {
downloadsChart.value?.resetChart()
viewsChart.value?.resetChart()
@ -365,8 +389,15 @@ const resetCharts = () => {
tinyDownloadChart.value?.resetChart()
tinyViewChart.value?.resetChart()
tinyRevenueChart.value?.resetChart()
setChartColors(isUsingProjectColors)
}
const isUsingProjectColors = ref(true)
watch(() => isUsingProjectColors, setChartColors, {
deep: true,
})
const analytics = useFetchAllAnalytics(resetCharts, selectedDisplayProjects)
const { startDate, endDate, timeRange, timeResolution } = analytics
@ -425,6 +456,9 @@ const downloadSelectedSetAsCSV = () => {
}
const onDownloadSetAsCSV = useClientTry(async () => await downloadSelectedSetAsCSV())
const onToggleColors = () => {
isUsingProjectColors.value = !isUsingProjectColors.value
}
</script>
<script lang="ts">

View File

@ -38,7 +38,7 @@ const hashProjectId = (projectId) => {
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
@ -51,7 +51,7 @@ export const getDefaultColor = (value) => {
return defaultColors[value % defaultColors.length]
}
export const intToRgba = (color, projectId = 'Unknown', theme) => {
export const intToRgba = (color, projectId = 'Unknown', theme = 'dark') => {
const hash = hashProjectId(projectId)
if (!color || color === 0) {
@ -109,6 +109,7 @@ const emptyAnalytics = {
},
],
colors: [],
defaultColors: [],
},
}
@ -204,6 +205,10 @@ export const processAnalytics = (category, projects, labelFn, sortFn, mapFn, cha
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 normalQuery = new URLSearchParams(query)
const revQuery = new URLSearchParams(query)
const revenueQuery = new URLSearchParams(query)
revenueQuery.delete('projects')
const qs = normalQuery.toString()
const revQs = revQuery.toString()
const revenueQs = revenueQuery.toString()
try {
loading.value = true
@ -313,14 +317,29 @@ export const useFetchAllAnalytics = (onDataRefresh, projects) => {
const responses = await Promise.all([
useFetchAnalytics(`analytics/downloads?${qs}`),
useFetchAnalytics(`analytics/views?${qs}`),
useFetchAnalytics(`analytics/revenue?${revQs}`),
useFetchAnalytics(`analytics/revenue?${revenueQs}`),
useFetchAnalytics(`analytics/countries/downloads?${qs}`),
useFetchAnalytics(`analytics/countries/views?${qs}`),
])
downloadData.value = responses[0] || {}
viewData.value = responses[1] || {}
revenueData.value = responses[2] || {}
// collect project ids from projects.value into a set
const projectIds = new Set()
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] || {}
viewsByCountry.value = responses[4] || {}
} catch (e) {