improve heatmap
This commit is contained in:
		
							parent
							
								
									78a8e918a0
								
							
						
					
					
						commit
						3968597a7b
					
				
					 3 changed files with 61 additions and 18 deletions
				
			
		|  | @ -8,7 +8,7 @@ | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; | import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; | ||||||
| import { | import { | ||||||
| 	Chart, | 	Chart, | ||||||
| 	ArcElement, | 	ArcElement, | ||||||
|  | @ -54,6 +54,10 @@ Chart.register( | ||||||
| 	MatrixController, MatrixElement, | 	MatrixController, MatrixElement, | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  | 	src: string; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
| const rootEl = $ref<HTMLDivElement>(null); | const rootEl = $ref<HTMLDivElement>(null); | ||||||
| const chartEl = $ref<HTMLCanvasElement>(null); | const chartEl = $ref<HTMLCanvasElement>(null); | ||||||
| const now = new Date(); | const now = new Date(); | ||||||
|  | @ -96,7 +100,24 @@ async function renderChart() { | ||||||
| 		}); | 		}); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); | 	let values; | ||||||
|  | 
 | ||||||
|  | 	if (props.src === 'active-users') { | ||||||
|  | 		const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); | ||||||
|  | 		values = raw.readWrite; | ||||||
|  | 	} else if (props.src === 'notes') { | ||||||
|  | 		const raw = await os.api('charts/notes', { limit: chartLimit, span: 'day' }); | ||||||
|  | 		values = raw.local.inc; | ||||||
|  | 	} else if (props.src === 'ap-requests-inbox-received') { | ||||||
|  | 		const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); | ||||||
|  | 		values = raw.inboxReceived; | ||||||
|  | 	} else if (props.src === 'ap-requests-deliver-succeeded') { | ||||||
|  | 		const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); | ||||||
|  | 		values = raw.deliverSucceeded; | ||||||
|  | 	} else if (props.src === 'ap-requests-deliver-failed') { | ||||||
|  | 		const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); | ||||||
|  | 		values = raw.deliverFailed; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	fetching = false; | 	fetching = false; | ||||||
| 
 | 
 | ||||||
|  | @ -110,7 +131,9 @@ async function renderChart() { | ||||||
| 	const color = defaultStore.state.darkMode ? '#b4e900' : '#86b300'; | 	const color = defaultStore.state.darkMode ? '#b4e900' : '#86b300'; | ||||||
| 
 | 
 | ||||||
| 	// 視覚上の分かりやすさのため上から最も大きい3つの値の平均を最大値とする | 	// 視覚上の分かりやすさのため上から最も大きい3つの値の平均を最大値とする | ||||||
| 	const max = raw.readWrite.slice().sort((a, b) => b - a).slice(0, 3).reduce((a, b) => a + b, 0) / 3; | 	const max = values.slice().sort((a, b) => b - a).slice(0, 3).reduce((a, b) => a + b, 0) / 3; | ||||||
|  | 
 | ||||||
|  | 	const min = Math.max(0, Math.min(...values) - 1); | ||||||
| 
 | 
 | ||||||
| 	const marginEachCell = 4; | 	const marginEachCell = 4; | ||||||
| 
 | 
 | ||||||
|  | @ -119,14 +142,17 @@ async function renderChart() { | ||||||
| 		data: { | 		data: { | ||||||
| 			datasets: [{ | 			datasets: [{ | ||||||
| 				label: 'Read & Write', | 				label: 'Read & Write', | ||||||
| 				data: format(raw.readWrite), | 				data: format(values), | ||||||
| 				pointRadius: 0, | 				pointRadius: 0, | ||||||
| 				borderWidth: 0, | 				borderWidth: 0, | ||||||
| 				borderJoinStyle: 'round', | 				borderJoinStyle: 'round', | ||||||
| 				borderRadius: 3, | 				borderRadius: 3, | ||||||
| 				backgroundColor(c) { | 				backgroundColor(c) { | ||||||
| 					const value = c.dataset.data[c.dataIndex].v; | 					const value = c.dataset.data[c.dataIndex].v; | ||||||
| 					const a = value / max; | 					let a = (value - min) / max; | ||||||
|  | 					if (value !== 0) { // 0でない限りは完全に不可視にはしない | ||||||
|  | 						a = Math.max(a, 0.05); | ||||||
|  | 					} | ||||||
| 					return alpha(color, a); | 					return alpha(color, a); | ||||||
| 				}, | 				}, | ||||||
| 				fill: true, | 				fill: true, | ||||||
|  | @ -222,6 +248,11 @@ async function renderChart() { | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | watch(() => props.src, () => { | ||||||
|  | 	fetching = true; | ||||||
|  | 	renderChart(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| onMounted(async () => { | onMounted(async () => { | ||||||
| 	renderChart(); | 	renderChart(); | ||||||
| }); | }); | ||||||
|  | @ -38,8 +38,15 @@ | ||||||
| 
 | 
 | ||||||
| 	<MkFolder class="item"> | 	<MkFolder class="item"> | ||||||
| 		<template #header>Active users heatmap</template> | 		<template #header>Active users heatmap</template> | ||||||
|  | 		<MkSelect v-model="heatmapSrc" style="margin: 0 0 12px 0;"> | ||||||
|  | 			<option value="active-users">Active users</option> | ||||||
|  | 			<option value="notes">Notes</option> | ||||||
|  | 			<option value="ap-requests-inbox-received">AP Requests: inboxReceived</option> | ||||||
|  | 			<option value="ap-requests-deliver-succeeded">AP Requests: deliverSucceeded</option> | ||||||
|  | 			<option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option> | ||||||
|  | 		</MkSelect> | ||||||
| 		<div class="_panel" :class="$style.heatmap"> | 		<div class="_panel" :class="$style.heatmap"> | ||||||
| 			<MkActiveUsersHeatmap/> | 			<MkHeatmap :src="heatmapSrc"/> | ||||||
| 		</div> | 		</div> | ||||||
| 	</MkFolder> | 	</MkFolder> | ||||||
| 
 | 
 | ||||||
|  | @ -93,7 +100,7 @@ import MkChart from '@/components/MkChart.vue'; | ||||||
| import { useChartTooltip } from '@/scripts/use-chart-tooltip'; | import { useChartTooltip } from '@/scripts/use-chart-tooltip'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import MkActiveUsersHeatmap from '@/components/MkActiveUsersHeatmap.vue'; | import MkHeatmap from '@/components/MkHeatmap.vue'; | ||||||
| import MkFolder from '@/components/MkFolder.vue'; | import MkFolder from '@/components/MkFolder.vue'; | ||||||
| import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue'; | import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue'; | ||||||
| 
 | 
 | ||||||
|  | @ -115,15 +122,10 @@ Chart.register( | ||||||
| 	Filler, | 	Filler, | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| const props = withDefaults(defineProps<{ | const chartLimit = 90; | ||||||
| 	chartLimit?: number; | let chartSpan = $ref<'hour' | 'day'>('hour'); | ||||||
| 	detailed?: boolean; | let chartSrc = $ref('active-users'); | ||||||
| }>(), { | let heatmapSrc = $ref('active-users'); | ||||||
| 	chartLimit: 90, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const chartSpan = $ref<'hour' | 'day'>('hour'); |  | ||||||
| const chartSrc = $ref('active-users'); |  | ||||||
| let subDoughnutEl = $ref<HTMLCanvasElement>(); | let subDoughnutEl = $ref<HTMLCanvasElement>(); | ||||||
| let pubDoughnutEl = $ref<HTMLCanvasElement>(); | let pubDoughnutEl = $ref<HTMLCanvasElement>(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,11 +1,21 @@ | ||||||
| <template> | <template> | ||||||
| <div class="_panel" :class="$style.root"> | <div class="_panel" :class="$style.root"> | ||||||
| 	<MkActiveUsersHeatmap/> | 	<MkSelect v-model="src" style="margin: 0 0 12px 0;" small> | ||||||
|  | 		<option value="active-users">Active users</option> | ||||||
|  | 		<option value="notes">Notes</option> | ||||||
|  | 		<option value="ap-requests-inbox-received">AP Requests: inboxReceived</option> | ||||||
|  | 		<option value="ap-requests-deliver-succeeded">AP Requests: deliverSucceeded</option> | ||||||
|  | 		<option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option> | ||||||
|  | 	</MkSelect> | ||||||
|  | 	<MkHeatmap :src="src"/> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import MkActiveUsersHeatmap from '@/components/MkActiveUsersHeatmap.vue'; | import MkHeatmap from '@/components/MkHeatmap.vue'; | ||||||
|  | import MkSelect from '@/components/form/select.vue'; | ||||||
|  | 
 | ||||||
|  | let src = $ref('active-users'); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="scss" module> | <style lang="scss" module> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue