diff --git a/packages/client/src/components/digital-clock.vue b/packages/client/src/components/digital-clock.vue new file mode 100644 index 0000000000..9ed8d63d19 --- /dev/null +++ b/packages/client/src/components/digital-clock.vue @@ -0,0 +1,77 @@ +<template> +<span class="zjobosdg"> + <span v-text="hh"></span> + <span class="colon" :class="{ showColon }">:</span> + <span v-text="mm"></span> + <span v-if="showS" class="colon" :class="{ showColon }">:</span> + <span v-if="showS" v-text="ss"></span> + <span v-if="showMs" class="colon" :class="{ showColon }">:</span> + <span v-if="showMs" v-text="ms"></span> +</span> +</template> + +<script lang="ts" setup> +import { onUnmounted, ref, watch } from 'vue'; + +const props = withDefaults(defineProps<{ + showS?: boolean; + showMs?: boolean; + offset?: number; +}>(), { + showS: true, + showMs: false, + offset: 0 - new Date().getTimezoneOffset(), +}); + +let intervalId; +const hh = ref(''); +const mm = ref(''); +const ss = ref(''); +const ms = ref(''); +const showColon = ref(false); +let prevSec: number | null = null; + +watch(showColon, (v) => { + if (v) { + window.setTimeout(() => { + showColon.value = false; + }, 30); + } +}); + +const tick = () => { + const now = new Date(); + now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset)); + hh.value = now.getHours().toString().padStart(2, '0'); + mm.value = now.getMinutes().toString().padStart(2, '0'); + ss.value = now.getSeconds().toString().padStart(2, '0'); + ms.value = Math.floor(now.getMilliseconds() / 10).toString().padStart(2, '0'); + if (now.getSeconds() !== prevSec) showColon.value = true; + prevSec = now.getSeconds(); +}; + +tick(); + +watch(() => props.showMs, () => { + if (intervalId) window.clearInterval(intervalId); + intervalId = window.setInterval(tick, props.showMs ? 10 : 1000); +}, { immediate: true }); + +onUnmounted(() => { + window.clearInterval(intervalId); +}); +</script> + +<style lang="scss" scoped> +.zjobosdg { + > .colon { + opacity: 0; + transition: opacity 1s ease; + + &.showColon { + opacity: 1; + transition: opacity 0s; + } + } +} +</style> diff --git a/packages/client/src/widgets/clock.vue b/packages/client/src/widgets/clock.vue index c844bdd14e..7d78b05f84 100644 --- a/packages/client/src/widgets/clock.vue +++ b/packages/client/src/widgets/clock.vue @@ -1,7 +1,7 @@ <template> <MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-clock"> <div class="vubelbmv" :class="widgetProps.size"> - <div v-if="widgetProps.showLabel" class="label abbrev">{{ tzAbbrev }}</div> + <div v-if="widgetProps.label === 'tz' || widgetProps.label === 'timeAndTz'" class="_monospace label a abbrev">{{ tzAbbrev }}</div> <MkAnalogClock class="clock" :thickness="widgetProps.thickness" @@ -10,7 +10,8 @@ :fade-graduations="widgetProps.fadeGraduations" :twentyfour="widgetProps.twentyFour" /> - <div v-if="widgetProps.showLabel" class="label offset">{{ tzOffsetLabel }}</div> + <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> </div> </MkContainer> </template> @@ -21,6 +22,7 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/ui/container.vue'; import MkAnalogClock from '@/components/analog-clock.vue'; +import MkDigitalClock from '@/components/digital-clock.vue'; import { timezones } from '@/scripts/timezones'; import { i18n } from '@/i18n'; @@ -72,9 +74,18 @@ const widgetPropsDef = { type: 'boolean' as const, default: false, }, - showLabel: { - type: 'boolean' as const, - default: true, + 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, @@ -125,16 +136,25 @@ defineExpose<WidgetComponentExpose>({ position: relative; > .label { + position: absolute; opacity: 0.7; - &.abbrev { - position: absolute; + &.a { top: 14px; left: 14px; } - &.offset { - position: absolute; + &.b { + top: 14px; + right: 14px; + } + + &.c { + bottom: 14px; + left: 14px; + } + + &.d { bottom: 14px; right: 14px; } @@ -145,7 +165,7 @@ defineExpose<WidgetComponentExpose>({ } &.small { - padding: 8px; + padding: 12px; > .clock { height: 100px; @@ -153,7 +173,7 @@ defineExpose<WidgetComponentExpose>({ } &.medium { - padding: 8px; + padding: 14px; > .clock { height: 150px; diff --git a/packages/client/src/widgets/digital-clock.vue b/packages/client/src/widgets/digital-clock.vue index 0091674216..387fad9b62 100644 --- a/packages/client/src/widgets/digital-clock.vue +++ b/packages/client/src/widgets/digital-clock.vue @@ -2,13 +2,7 @@ <div class="mkw-digitalClock _monospace" :class="{ _panel: !widgetProps.transparent }" :style="{ fontSize: `${widgetProps.fontSize}em` }"> <div v-if="widgetProps.showLabel" class="label">{{ tzAbbrev }}</div> <div class="time"> - <span v-text="hh"></span> - <span class="colon" :class="{ showColon }">:</span> - <span v-text="mm"></span> - <span class="colon" :class="{ showColon }">:</span> - <span v-text="ss"></span> - <span v-if="widgetProps.showMs" class="colon" :class="{ showColon }">:</span> - <span v-if="widgetProps.showMs" v-text="ms"></span> + <MkDigitalClock :show-ms="widgetProps.showMs" :offset="tzOffset"/> </div> <div v-if="widgetProps.showLabel" class="label">{{ tzOffsetLabel }}</div> </div> @@ -19,6 +13,7 @@ import { onUnmounted, ref, watch } from 'vue'; import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; import { GetFormResultType } from '@/scripts/form'; import { timezones } from '@/scripts/timezones'; +import MkDigitalClock from '@/components/digital-clock.vue'; const name = 'digitalClock'; @@ -77,44 +72,6 @@ const tzOffset = $computed(() => widgetProps.timezone === null const tzOffsetLabel = $computed(() => (tzOffset >= 0 ? '+' : '-') + Math.floor(tzOffset / 60).toString().padStart(2, '0') + ':' + (tzOffset % 60).toString().padStart(2, '0')); -let intervalId; -const hh = ref(''); -const mm = ref(''); -const ss = ref(''); -const ms = ref(''); -const showColon = ref(false); -let prevSec: number | null = null; - -watch(showColon, (v) => { - if (v) { - window.setTimeout(() => { - showColon.value = false; - }, 30); - } -}); - -const tick = () => { - const now = new Date(); - now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + tzOffset)); - hh.value = now.getHours().toString().padStart(2, '0'); - mm.value = now.getMinutes().toString().padStart(2, '0'); - ss.value = now.getSeconds().toString().padStart(2, '0'); - ms.value = Math.floor(now.getMilliseconds() / 10).toString().padStart(2, '0'); - if (now.getSeconds() !== prevSec) showColon.value = true; - prevSec = now.getSeconds(); -}; - -tick(); - -watch(() => widgetProps.showMs, () => { - if (intervalId) window.clearInterval(intervalId); - intervalId = window.setInterval(tick, widgetProps.showMs ? 10 : 1000); -}, { immediate: true }); - -onUnmounted(() => { - window.clearInterval(intervalId); -}); - defineExpose<WidgetComponentExpose>({ name, configure, @@ -131,17 +88,5 @@ defineExpose<WidgetComponentExpose>({ font-size: 65%; opacity: 0.7; } - - > .time { - > .colon { - opacity: 0; - transition: opacity 1s ease; - - &.showColon { - opacity: 1; - transition: opacity 0s; - } - } - } } </style>