You've already forked AstralRinth
forked from didirus/AstralRinth
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:
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user