enhance(client): improve clock widget
This commit is contained in:
		
							parent
							
								
									2cd70b80a2
								
							
						
					
					
						commit
						dea5e6207e
					
				
					 2 changed files with 136 additions and 28 deletions
				
			
		|  | @ -1,28 +1,58 @@ | |||
| <template> | ||||
| <svg class="mbcofsoe" viewBox="0 0 10 10" preserveAspectRatio="none"> | ||||
| 	<template v-if="props.graduations === 'dots'"> | ||||
| 		<circle | ||||
| 		v-for="(angle, i) in graduations" | ||||
| 		:key="i" | ||||
| 			v-for="(angle, i) in graduationsMinor" | ||||
| 			:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))" | ||||
| 			:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))" | ||||
| 		:r="i % 5 == 0 ? 0.125 : 0.05" | ||||
| 		:fill="i % 5 == 0 ? majorGraduationColor : minorGraduationColor" | ||||
| 			:r="i % 5 == 0 ? 0 : 0.05" | ||||
| 			:fill="minorGraduationColor" | ||||
| 		/> | ||||
| 
 | ||||
| 	<template v-if="props.numbers"> | ||||
| 		<circle | ||||
| 			v-for="(angle, i) in graduationsMajor" | ||||
| 			:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))" | ||||
| 			:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))" | ||||
| 			:r="0.125" | ||||
| 			:fill="majorGraduationColor" | ||||
| 		/> | ||||
| 	</template> | ||||
| 	<template v-else-if="props.graduations === 'dotsMajor'"> | ||||
| 		<circle | ||||
| 			v-for="(angle, i) in graduationsMajor" | ||||
| 			:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))" | ||||
| 			:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))" | ||||
| 			:r="0.125" | ||||
| 			:fill="majorGraduationColor" | ||||
| 		/> | ||||
| 	</template> | ||||
| 	<template v-else-if="props.graduations === 'numbers'"> | ||||
| 		<text | ||||
| 			v-for="(angle, i) in texts" | ||||
| 			:x="5 + (Math.sin(angle) * (5 - textsPadding))" | ||||
| 			:y="5 - (Math.cos(angle) * (5 - textsPadding))" | ||||
| 			text-anchor="middle" | ||||
| 			dominant-baseline="middle" | ||||
| 			font-family="Verdana" | ||||
| 			font-size="0.75" | ||||
| 			fill="currentColor" | ||||
| 		> | ||||
| 			{{ i === 0 ? (props.twentyfour ? '24' : '12') : i }} | ||||
| 		</text> | ||||
| 	</template> | ||||
| 	<template v-else-if="props.graduations === 'numbersCurrent'"> | ||||
| 		<text | ||||
| 			v-for="(angle, i) in texts" | ||||
| 			:x="5 + (Math.sin(angle) * (5 - textsPadding))" | ||||
| 			:y="5 - (Math.cos(angle) * (5 - textsPadding))" | ||||
| 			text-anchor="middle" | ||||
| 			dominant-baseline="middle" | ||||
| 			:font-size="(props.twentyfour ? h : h % 12) === i ? 1 : 0.7" | ||||
| 			:font-weight="(props.twentyfour ? h : h % 12) === i ? 'bold' : 'normal'" | ||||
| 			:fill="(props.twentyfour ? h : h % 12) === i ? currentHTextColor : 'currentColor'" | ||||
| 			:opacity="(props.twentyfour ? h : h % 12) === i ? 1 : 0.5" | ||||
| 		> | ||||
| 			{{ i === 0 ? (props.twentyfour ? '24' : '12') : i }} | ||||
| 		</text> | ||||
| 	</template> | ||||
| 
 | ||||
| 	<line | ||||
| 		:x1="5 - (Math.sin(sAngle) * (sHandLengthRatio * handsTailLength))" | ||||
|  | @ -59,6 +89,7 @@ | |||
| <script lang="ts" setup> | ||||
| import { ref, computed, onMounted, onBeforeUnmount } from 'vue'; | ||||
| import tinycolor from 'tinycolor2'; | ||||
| import { globalEvents } from '@/events.js'; | ||||
| 
 | ||||
| const graduationsPadding = 0.5; | ||||
| const textsPadding = 0.5; | ||||
|  | @ -67,28 +98,37 @@ const handsTailLength = 0.7; | |||
| const hHandLengthRatio = 0.75; | ||||
| const mHandLengthRatio = 1; | ||||
| const sHandLengthRatio = 1; | ||||
| const graduations = (() => { | ||||
| const graduationsMinor = (() => { | ||||
| 	const angles: number[] = []; | ||||
| 	for (let i = 0; i < 60; i++) { | ||||
| 		const angle = Math.PI * i / 30; | ||||
| 		angles.push(angle); | ||||
| 	} | ||||
| 
 | ||||
| 	return angles; | ||||
| })(); | ||||
| 
 | ||||
| const props = withDefaults(defineProps<{ | ||||
| 	numbers?: boolean; | ||||
| 	thickness?: number; | ||||
| 	offset?: number; | ||||
| 	twentyfour?: boolean; | ||||
| 	graduations?: 'none' | 'dots' | 'dotsMajor' | 'numbers' | 'numbersCurrent'; | ||||
| }>(), { | ||||
| 	numbers: false, | ||||
| 	thickness: 0.1, | ||||
| 	offset: 0 - new Date().getTimezoneOffset(), | ||||
| 	twentyfour: false, | ||||
| 	graduations: 'dots', | ||||
| }); | ||||
| 
 | ||||
| const graduationsMajor = computed(() => { | ||||
| 	const angles: number[] = []; | ||||
| 	const times = props.twentyfour ? 24 : 12; | ||||
| 	for (let i = 0; i < times; i++) { | ||||
| 		const angle = Math.PI * i / (times / 2); | ||||
| 		angles.push(angle); | ||||
| 	} | ||||
| 	return angles; | ||||
| }); | ||||
| const texts = computed(() => { | ||||
| 	const angles: number[] = []; | ||||
| 	const times = props.twentyfour ? 24 : 12; | ||||
|  | @ -103,13 +143,12 @@ const now = ref(new Date()); | |||
| now.value.setMinutes(now.value.getMinutes() + (new Date().getTimezoneOffset() + props.offset)); | ||||
| 
 | ||||
| const enabled = ref(true); | ||||
| const computedStyle = getComputedStyle(document.documentElement); | ||||
| const dark = computed(() => tinycolor(computedStyle.getPropertyValue('--bg')).isDark()); | ||||
| const majorGraduationColor = computed(() => dark.value ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)'); | ||||
| const minorGraduationColor = computed(() => dark.value ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'); | ||||
| const sHandColor = computed(() => dark.value ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)'); | ||||
| const mHandColor = computed(() => tinycolor(computedStyle.getPropertyValue('--fg')).toHexString()); | ||||
| const hHandColor = computed(() => tinycolor(computedStyle.getPropertyValue('--accent')).toHexString()); | ||||
| const majorGraduationColor = ref<string>(); | ||||
| const minorGraduationColor = ref<string>(); | ||||
| const sHandColor = ref<string>(); | ||||
| const mHandColor = ref<string>(); | ||||
| const hHandColor = ref<string>(); | ||||
| const currentHTextColor = ref<string>(); | ||||
| const s = computed(() => now.value.getSeconds()); | ||||
| const m = computed(() => now.value.getMinutes()); | ||||
| const h = computed(() => now.value.getHours()); | ||||
|  | @ -122,6 +161,20 @@ function tick() { | |||
| 	now.value.setMinutes(now.value.getMinutes() + (new Date().getTimezoneOffset() + props.offset)); | ||||
| } | ||||
| 
 | ||||
| function calcColors() { | ||||
| 	const computedStyle = getComputedStyle(document.documentElement); | ||||
| 	const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark(); | ||||
| 	const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString(); | ||||
| 	majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)'; | ||||
| 	minorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; | ||||
| 	sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)'; | ||||
| 	mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString(); | ||||
| 	hHandColor.value = accent; | ||||
| 	currentHTextColor.value = accent; | ||||
| } | ||||
| 
 | ||||
| calcColors(); | ||||
| 
 | ||||
| onMounted(() => { | ||||
| 	const update = () => { | ||||
| 		if (enabled.value) { | ||||
|  | @ -130,10 +183,14 @@ onMounted(() => { | |||
| 		} | ||||
| 	}; | ||||
| 	update(); | ||||
| 
 | ||||
| 	globalEvents.on('themeChanged', calcColors); | ||||
| }); | ||||
| 
 | ||||
| onBeforeUnmount(() => { | ||||
| 	enabled.value = false; | ||||
| 
 | ||||
| 	globalEvents.off('themeChanged', calcColors); | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,14 @@ | |||
| <template> | ||||
| <MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-clock"> | ||||
| 	<div class="vubelbmv"> | ||||
| 	<div class="vubelbmv" :class="widgetProps.size"> | ||||
| 		<div v-if="widgetProps.showLabel" class="label abbrev">{{ tzAbbrev }}</div> | ||||
| 		<MkAnalogClock class="clock" :thickness="widgetProps.thickness" :offset="tzOffset" :numbers="widgetProps.numbers" :twentyfour="widgetProps.twentyFour"/> | ||||
| 		<MkAnalogClock | ||||
| 			class="clock" | ||||
| 			:thickness="widgetProps.thickness" | ||||
| 			:offset="tzOffset" | ||||
| 			:graduations="widgetProps.graduations" | ||||
| 			:twentyfour="widgetProps.twentyFour" | ||||
| 		/> | ||||
| 		<div v-if="widgetProps.showLabel" class="label offset">{{ tzOffsetLabel }}</div> | ||||
| 	</div> | ||||
| </MkContainer> | ||||
|  | @ -15,6 +21,7 @@ import { GetFormResultType } from '@/scripts/form'; | |||
| import MkContainer from '@/components/ui/container.vue'; | ||||
| import MkAnalogClock from '@/components/analog-clock.vue'; | ||||
| import { timezones } from '@/scripts/timezones'; | ||||
| import { i18n } from '@/i18n'; | ||||
| 
 | ||||
| const name = 'clock'; | ||||
| 
 | ||||
|  | @ -23,6 +30,17 @@ const widgetPropsDef = { | |||
| 		type: 'boolean' as const, | ||||
| 		default: false, | ||||
| 	}, | ||||
| 	size: { | ||||
| 		type: 'radio' as const, | ||||
| 		default: 'medium', | ||||
| 		options: [{ | ||||
| 			value: 'small', label: i18n.ts.small, | ||||
| 		}, { | ||||
| 			value: 'medium', label: i18n.ts.medium, | ||||
| 		}, { | ||||
| 			value: 'large', label: i18n.ts.large, | ||||
| 		}], | ||||
| 	}, | ||||
| 	thickness: { | ||||
| 		type: 'radio' as const, | ||||
| 		default: 0.1, | ||||
|  | @ -34,9 +52,20 @@ const widgetPropsDef = { | |||
| 			value: 0.3, label: 'thick', | ||||
| 		}], | ||||
| 	}, | ||||
| 	numbers: { | ||||
| 		type: 'boolean' as const, | ||||
| 		default: false, | ||||
| 	graduations: { | ||||
| 		type: 'radio' as const, | ||||
| 		default: 'dots', | ||||
| 		options: [{ | ||||
| 			value: 'none', label: 'None', | ||||
| 		}, { | ||||
| 			value: 'dots', label: 'Dots', | ||||
| 		}, { | ||||
| 			value: 'dotsMajor', label: 'Dots (major)', | ||||
| 		}, { | ||||
| 			value: 'numbers', label: 'Numbers', | ||||
| 		}, { | ||||
| 			value: 'numbersCurrent', label: 'Numbers (current)', | ||||
| 		}], | ||||
| 	}, | ||||
| 	twentyFour: { | ||||
| 		type: 'boolean' as const, | ||||
|  | @ -92,7 +121,6 @@ defineExpose<WidgetComponentExpose>({ | |||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .vubelbmv { | ||||
| 	padding: 8px; | ||||
| 	position: relative; | ||||
| 
 | ||||
| 	> .label { | ||||
|  | @ -112,8 +140,31 @@ defineExpose<WidgetComponentExpose>({ | |||
| 	} | ||||
| 
 | ||||
| 	> .clock { | ||||
| 		height: 150px; | ||||
| 		margin: auto; | ||||
| 	} | ||||
| 
 | ||||
| 	&.small { | ||||
| 		padding: 8px; | ||||
| 
 | ||||
| 		> .clock { | ||||
| 			height: 100px; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	&.medium { | ||||
| 		padding: 8px; | ||||
| 
 | ||||
| 		> .clock { | ||||
| 			height: 150px; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	&.large { | ||||
| 		padding: 16px; | ||||
| 
 | ||||
| 		> .clock { | ||||
| 			height: 200px; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue