Skip to content
Snippets Groups Projects
WidgetClock.vue 4.6 KiB
Newer Older
<MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-clock">
	<div class="vubelbmv" :class="widgetProps.size">
		<div v-if="widgetProps.label === 'tz' || widgetProps.label === 'timeAndTz'" class="_monospace label a abbrev">{{ tzAbbrev }}</div>
		<MkAnalogClock
			class="clock"
			:thickness="widgetProps.thickness"
			:offset="tzOffset"
			:graduations="widgetProps.graduations"
			:fade-graduations="widgetProps.fadeGraduations"
			:twentyfour="widgetProps.twentyFour"
			:s-animation="widgetProps.sAnimation"
		<MkDigitalClock v-if="widgetProps.label === 'time' || widgetProps.label === 'timeAndTz'" class="_monospace label c time" :show-s="false" :offset="tzOffset"/>
		<div v-if="widgetProps.label === 'tz' || widgetProps.label === 'timeAndTz'" class="_monospace label d offset">{{ tzOffsetLabel }}</div>
syuilo's avatar
syuilo committed
	</div>
syuilo's avatar
syuilo committed
</MkContainer>
<script lang="ts" setup>
import { } from 'vue';
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
import { GetFormResultType } from '@/scripts/form';
import MkContainer from '@/components/MkContainer.vue';
import MkAnalogClock from '@/components/MkAnalogClock.vue';
import MkDigitalClock from '@/components/MkDigitalClock.vue';
import { timezones } from '@/scripts/timezones';
import { i18n } from '@/i18n';
syuilo's avatar
syuilo committed

const name = 'clock';

const widgetPropsDef = {
	transparent: {
		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.2,
			value: 0.1, label: 'thin',
			value: 0.2, label: 'medium',
			value: 0.3, label: 'thick',
		}],
	},
	graduations: {
		type: 'radio' as const,
		default: 'numbers',
		options: [{
			value: 'none', label: 'None',
		}, {
			value: 'dots', label: 'Dots',
		}, {
			value: 'numbers', label: 'Numbers',
		}],
	fadeGraduations: {
		type: 'boolean' as const,
		default: true,
	},
	sAnimation: {
		type: 'radio' as const,
		default: 'elastic',
		options: [{
			value: 'none', label: 'None',
		}, {
			value: 'elastic', label: 'Elastic',
		}, {
			value: 'easeOut', label: 'Ease out',
		}],
	},
	twentyFour: {
		type: 'boolean' as const,
		default: false,
	},
	label: {
		type: 'radio' as const,
		default: 'none',
		options: [{
			value: 'none', label: 'None',
		}, {
			value: 'time', label: 'Time',
		}, {
			value: 'tz', label: 'TZ',
		}, {
			value: 'timeAndTz', label: 'Time + TZ',
		}],
	},
	timezone: {
		type: 'enum' as const,
		default: null,
		enum: [...timezones.map((tz) => ({
			label: tz.name,
			value: tz.name.toLowerCase(),
		})), {
			label: '(auto)',
			value: null,
		}],
	},
};

type WidgetProps = GetFormResultType<typeof widgetPropsDef>;

// 現時点ではvueの制限によりimportしたtypeをジェネリックに渡せない
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();

const { widgetProps, configure } = useWidgetPropsManager(name,
	widgetPropsDef,
	props,
	emit,
);

const tzAbbrev = $computed(() => (widgetProps.timezone === null
	? timezones.find((tz) => tz.name.toLowerCase() === Intl.DateTimeFormat().resolvedOptions().timeZone.toLowerCase())?.abbrev
	: timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.abbrev) ?? '?');

const tzOffset = $computed(() => widgetProps.timezone === null
	? 0 - new Date().getTimezoneOffset()
	: timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.offset ?? 0);

const tzOffsetLabel = $computed(() => (tzOffset >= 0 ? '+' : '-') + Math.floor(tzOffset / 60).toString().padStart(2, '0') + ':' + (tzOffset % 60).toString().padStart(2, '0'));

defineExpose<WidgetComponentExpose>({
	name,
	configure,
	id: props.widget ? props.widget.id : null,
syuilo's avatar
syuilo committed
.vubelbmv {
	position: relative;

	> .label {
		position: absolute;
		opacity: 0.7;

			top: 14px;
			left: 14px;
		}

		&.b {
			top: 14px;
			right: 14px;
		}

		&.c {
			bottom: 14px;
			left: 14px;
		}

		&.d {
			bottom: 14px;
			right: 14px;
		}
	}
syuilo's avatar
syuilo committed

	> .clock {
		margin: auto;
		padding: 12px;

		> .clock {
			height: 100px;
		}
	}

	&.medium {
		padding: 14px;

		> .clock {
			height: 150px;
		}
	}

	&.large {
		padding: 16px;

		> .clock {
			height: 200px;
		}
	}