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