forked from FoundKeyGang/FoundKey
refactor(client): extract interval logic to a composable function
Also, I fixed the `onUnmounted` call in `onMounted` and so on.
This commit is contained in:
parent
17f857584c
commit
707754fece
17 changed files with 203 additions and 174 deletions
|
@ -3,7 +3,8 @@
|
||||||
<div class="label" @click="focus"><slot name="label"></slot></div>
|
<div class="label" @click="focus"><slot name="label"></slot></div>
|
||||||
<div class="input" :class="{ inline, disabled, focused }">
|
<div class="input" :class="{ inline, disabled, focused }">
|
||||||
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
|
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
|
||||||
<input ref="inputEl"
|
<input
|
||||||
|
ref="inputEl"
|
||||||
v-model="v"
|
v-model="v"
|
||||||
v-adaptive-border
|
v-adaptive-border
|
||||||
:type="type"
|
:type="type"
|
||||||
|
@ -34,8 +35,9 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
|
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
|
||||||
import MkButton from '@/components/ui/button.vue';
|
|
||||||
import { debounce } from 'throttle-debounce';
|
import { debounce } from 'throttle-debounce';
|
||||||
|
import MkButton from '@/components/ui/button.vue';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -44,45 +46,45 @@ export default defineComponent({
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
required: {
|
required: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
readonly: {
|
readonly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
pattern: {
|
pattern: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
autofocus: {
|
autofocus: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
autocomplete: {
|
autocomplete: {
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
spellcheck: {
|
spellcheck: {
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
step: {
|
step: {
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
datalist: {
|
datalist: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -91,17 +93,17 @@ export default defineComponent({
|
||||||
inline: {
|
inline: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
debounce: {
|
debounce: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
manualSave: {
|
manualSave: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -134,7 +136,7 @@ export default defineComponent({
|
||||||
|
|
||||||
const updated = () => {
|
const updated = () => {
|
||||||
changed.value = false;
|
changed.value = false;
|
||||||
if (type?.value === 'number') {
|
if (type.value === 'number') {
|
||||||
context.emit('update:modelValue', parseFloat(v.value));
|
context.emit('update:modelValue', parseFloat(v.value));
|
||||||
} else {
|
} else {
|
||||||
context.emit('update:modelValue', v.value);
|
context.emit('update:modelValue', v.value);
|
||||||
|
@ -159,30 +161,29 @@ export default defineComponent({
|
||||||
invalid.value = inputEl.value.validity.badInput;
|
invalid.value = inputEl.value.validity.badInput;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// このコンポーネントが作成された時、非表示状態である場合がある
|
||||||
|
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
|
||||||
|
useInterval(() => {
|
||||||
|
if (prefixEl.value) {
|
||||||
|
if (prefixEl.value.offsetWidth) {
|
||||||
|
inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (suffixEl.value) {
|
||||||
|
if (suffixEl.value.offsetWidth) {
|
||||||
|
inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100, {
|
||||||
|
immediate: true,
|
||||||
|
afterMounted: true,
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (autofocus.value) {
|
if (autofocus.value) {
|
||||||
focus();
|
focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// このコンポーネントが作成された時、非表示状態である場合がある
|
|
||||||
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
|
|
||||||
const clock = window.setInterval(() => {
|
|
||||||
if (prefixEl.value) {
|
|
||||||
if (prefixEl.value.offsetWidth) {
|
|
||||||
inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (suffixEl.value) {
|
|
||||||
if (suffixEl.value.offsetWidth) {
|
|
||||||
inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(clock);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -24,31 +24,31 @@ export default defineComponent({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
min: {
|
min: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
max: {
|
max: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: 100
|
default: 100,
|
||||||
},
|
},
|
||||||
step: {
|
step: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: 1
|
default: 1,
|
||||||
},
|
},
|
||||||
autofocus: {
|
autofocus: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
textConverter: {
|
textConverter: {
|
||||||
type: Function,
|
type: Function,
|
||||||
|
@ -90,14 +90,18 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
watch([steppedValue, containerEl], calcThumbPosition);
|
watch([steppedValue, containerEl], calcThumbPosition);
|
||||||
|
|
||||||
|
let ro: ResizeObserver | undefined;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const ro = new ResizeObserver((entries, observer) => {
|
ro = new ResizeObserver((entries, observer) => {
|
||||||
calcThumbPosition();
|
calcThumbPosition();
|
||||||
});
|
});
|
||||||
ro.observe(containerEl.value);
|
ro.observe(containerEl.value);
|
||||||
onUnmounted(() => {
|
});
|
||||||
ro.disconnect();
|
|
||||||
});
|
onUnmounted(() => {
|
||||||
|
if (ro) ro.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
const steps = computed(() => {
|
const steps = computed(() => {
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
<div class="label" @click="focus"><slot name="label"></slot></div>
|
<div class="label" @click="focus"><slot name="label"></slot></div>
|
||||||
<div ref="container" class="input" :class="{ inline, disabled, focused }" @click.prevent="onClick">
|
<div ref="container" class="input" :class="{ inline, disabled, focused }" @click.prevent="onClick">
|
||||||
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
|
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
|
||||||
<select ref="inputEl"
|
<select
|
||||||
|
ref="inputEl"
|
||||||
v-model="v"
|
v-model="v"
|
||||||
v-adaptive-border
|
v-adaptive-border
|
||||||
class="select"
|
class="select"
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode } from 'vue';
|
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode } from 'vue';
|
||||||
import MkButton from '@/components/ui/button.vue';
|
import MkButton from '@/components/ui/button.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -37,38 +39,38 @@ export default defineComponent({
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
required: {
|
required: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
readonly: {
|
readonly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
autofocus: {
|
autofocus: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
inline: {
|
inline: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
manualSave: {
|
manualSave: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -109,30 +111,29 @@ export default defineComponent({
|
||||||
invalid.value = inputEl.value.validity.badInput;
|
invalid.value = inputEl.value.validity.badInput;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// このコンポーネントが作成された時、非表示状態である場合がある
|
||||||
|
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
|
||||||
|
useInterval(() => {
|
||||||
|
if (prefixEl.value) {
|
||||||
|
if (prefixEl.value.offsetWidth) {
|
||||||
|
inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (suffixEl.value) {
|
||||||
|
if (suffixEl.value.offsetWidth) {
|
||||||
|
inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100, {
|
||||||
|
immediate: true,
|
||||||
|
afterMounted: true,
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (autofocus.value) {
|
if (autofocus.value) {
|
||||||
focus();
|
focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// このコンポーネントが作成された時、非表示状態である場合がある
|
|
||||||
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
|
|
||||||
const clock = window.setInterval(() => {
|
|
||||||
if (prefixEl.value) {
|
|
||||||
if (prefixEl.value.offsetWidth) {
|
|
||||||
inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (suffixEl.value) {
|
|
||||||
if (suffixEl.value.offsetWidth) {
|
|
||||||
inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(clock);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,14 @@ let now = $ref(new Date());
|
||||||
const relative = $computed(() => {
|
const relative = $computed(() => {
|
||||||
const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/;
|
const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/;
|
||||||
return (
|
return (
|
||||||
ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) :
|
ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) :
|
||||||
ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) :
|
ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) :
|
||||||
ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) :
|
ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) :
|
||||||
ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) :
|
ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) :
|
||||||
ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) :
|
ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) :
|
||||||
ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) :
|
ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) :
|
||||||
ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
|
ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
|
||||||
ago >= -1 ? i18n.ts._ago.justNow :
|
ago >= -1 ? i18n.ts._ago.justNow :
|
||||||
i18n.ts._ago.future);
|
i18n.ts._ago.future);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ if (props.mode === 'relative' || props.mode === 'detail') {
|
||||||
tickId = window.requestAnimationFrame(tick);
|
tickId = window.requestAnimationFrame(tick);
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.clearTimeout(tickId);
|
window.cancelAnimationFrame(tickId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
import { onUnmounted, watch } from 'vue';
|
import { onUnmounted, watch } from 'vue';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
src: number[];
|
src: number[];
|
||||||
|
@ -65,9 +66,8 @@ function draw(): void {
|
||||||
watch(() => props.src, draw, { immediate: true });
|
watch(() => props.src, draw, { immediate: true });
|
||||||
|
|
||||||
// Vueが何故かWatchを発動させない場合があるので
|
// Vueが何故かWatchを発動させない場合があるので
|
||||||
clock = window.setInterval(draw, 1000);
|
useInterval(draw, 1000, {
|
||||||
|
immediate: false,
|
||||||
onUnmounted(() => {
|
afterMounted: true,
|
||||||
window.clearInterval(clock);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -112,9 +112,12 @@ export default defineComponent({
|
||||||
const elRef = ref<HTMLElement>(null);
|
const elRef = ref<HTMLElement>(null);
|
||||||
const reactionRef = ref(null);
|
const reactionRef = ref(null);
|
||||||
|
|
||||||
|
let readObserver: IntersectionObserver | undefined;
|
||||||
|
let connection;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!props.notification.isRead) {
|
if (!props.notification.isRead) {
|
||||||
const readObserver = new IntersectionObserver((entries, observer) => {
|
readObserver = new IntersectionObserver((entries, observer) => {
|
||||||
if (!entries.some(entry => entry.isIntersecting)) return;
|
if (!entries.some(entry => entry.isIntersecting)) return;
|
||||||
stream.send('readNotification', {
|
stream.send('readNotification', {
|
||||||
id: props.notification.id,
|
id: props.notification.id,
|
||||||
|
@ -124,20 +127,20 @@ export default defineComponent({
|
||||||
|
|
||||||
readObserver.observe(elRef.value);
|
readObserver.observe(elRef.value);
|
||||||
|
|
||||||
const connection = stream.useChannel('main');
|
connection = stream.useChannel('main');
|
||||||
connection.on('readAllNotifications', () => readObserver.disconnect());
|
connection.on('readAllNotifications', () => readObserver.disconnect());
|
||||||
|
|
||||||
watch(props.notification.isRead, () => {
|
watch(props.notification.isRead, () => {
|
||||||
readObserver.disconnect();
|
readObserver.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
readObserver.disconnect();
|
|
||||||
connection.dispose();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (readObserver) readObserver.disconnect();
|
||||||
|
if (connection) connection.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
const followRequestDone = ref(false);
|
const followRequestDone = ref(false);
|
||||||
const groupInviteDone = ref(false);
|
const groupInviteDone = ref(false);
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,10 @@ const onNotification = (notification) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let connection;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const connection = stream.useChannel('main');
|
connection = stream.useChannel('main');
|
||||||
connection.on('notification', onNotification);
|
connection.on('notification', onNotification);
|
||||||
connection.on('readAllNotifications', () => {
|
connection.on('readAllNotifications', () => {
|
||||||
if (pagingComponent.value) {
|
if (pagingComponent.value) {
|
||||||
|
@ -87,10 +89,10 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
connection.dispose();
|
if (connection) connection.dispose();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -27,18 +27,19 @@ import { sum } from '@/scripts/array';
|
||||||
import { pleaseLogin } from '@/scripts/please-login';
|
import { pleaseLogin } from '@/scripts/please-login';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
note: {
|
note: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
readOnly: {
|
readOnly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
@ -54,7 +55,7 @@ export default defineComponent({
|
||||||
s: Math.floor(remaining.value % 60),
|
s: Math.floor(remaining.value % 60),
|
||||||
m: Math.floor(remaining.value / 60) % 60,
|
m: Math.floor(remaining.value / 60) % 60,
|
||||||
h: Math.floor(remaining.value / 3600) % 24,
|
h: Math.floor(remaining.value / 3600) % 24,
|
||||||
d: Math.floor(remaining.value / 86400)
|
d: Math.floor(remaining.value / 86400),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const showResult = ref(props.readOnly || isVoted.value);
|
const showResult = ref(props.readOnly || isVoted.value);
|
||||||
|
@ -68,10 +69,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tick();
|
useInterval(tick, 3000, {
|
||||||
const intevalId = window.setInterval(tick, 3000);
|
immediate: true,
|
||||||
onUnmounted(() => {
|
afterMounted: false,
|
||||||
window.clearInterval(intevalId);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
-->
|
-->
|
||||||
<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg">
|
<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path style="transform-origin: center; transform-box: fill-box;"
|
<path
|
||||||
|
style="transform-origin: center; transform-box: fill-box;"
|
||||||
:transform="`translate(${particle.x} ${particle.y})`"
|
:transform="`translate(${particle.x} ${particle.y})`"
|
||||||
:fill="particle.color"
|
:fill="particle.color"
|
||||||
d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z"
|
d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z"
|
||||||
|
@ -73,14 +74,15 @@ export default defineComponent({
|
||||||
const width = ref(0);
|
const width = ref(0);
|
||||||
const height = ref(0);
|
const height = ref(0);
|
||||||
const colors = ['#FF1493', '#00FFFF', '#FFE202', '#FFE202', '#FFE202'];
|
const colors = ['#FF1493', '#00FFFF', '#FFE202', '#FFE202', '#FFE202'];
|
||||||
|
let stop = false;
|
||||||
|
let ro: ResizeObserver | undefined;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const ro = new ResizeObserver((entries, observer) => {
|
ro = new ResizeObserver((entries, observer) => {
|
||||||
width.value = el.value?.offsetWidth + 64;
|
width.value = el.value?.offsetWidth + 64;
|
||||||
height.value = el.value?.offsetHeight + 64;
|
height.value = el.value?.offsetHeight + 64;
|
||||||
});
|
});
|
||||||
ro.observe(el.value);
|
ro.observe(el.value);
|
||||||
let stop = false;
|
|
||||||
const add = () => {
|
const add = () => {
|
||||||
if (stop) return;
|
if (stop) return;
|
||||||
const x = (Math.random() * (width.value - 64));
|
const x = (Math.random() * (width.value - 64));
|
||||||
|
@ -104,10 +106,11 @@ export default defineComponent({
|
||||||
}, 500 + (Math.random() * 500));
|
}, 500 + (Math.random() * 500));
|
||||||
};
|
};
|
||||||
add();
|
add();
|
||||||
onUnmounted(() => {
|
});
|
||||||
ro.disconnect();
|
|
||||||
stop = true;
|
onUnmounted(() => {
|
||||||
});
|
if (ro) ro.disconnect();
|
||||||
|
stop = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
22
packages/client/src/scripts/use-interval.ts
Normal file
22
packages/client/src/scripts/use-interval.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
export function useInterval(fn: () => void, interval: number, options: {
|
||||||
|
immediate: boolean;
|
||||||
|
afterMounted: boolean;
|
||||||
|
}): void {
|
||||||
|
let intervalId: number | null = null;
|
||||||
|
|
||||||
|
if (options.afterMounted) {
|
||||||
|
onMounted(() => {
|
||||||
|
if (options.immediate) fn();
|
||||||
|
intervalId = window.setInterval(fn, interval);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (options.immediate) fn();
|
||||||
|
intervalId = window.setInterval(fn, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (intervalId) window.clearInterval(intervalId);
|
||||||
|
});
|
||||||
|
}
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
import { GetFormResultType } from '@/scripts/form';
|
|
||||||
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
||||||
|
import { GetFormResultType } from '@/scripts/form';
|
||||||
|
|
||||||
const name = 'ai';
|
const name = 'ai';
|
||||||
|
|
||||||
|
@ -38,22 +38,23 @@ const touched = () => {
|
||||||
//if (this.live2d) this.live2d.changeExpression('gurugurume');
|
//if (this.live2d) this.live2d.changeExpression('gurugurume');
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
const onMousemove = (ev: MouseEvent) => {
|
||||||
const onMousemove = (ev: MouseEvent) => {
|
const iframeRect = live2d.value.getBoundingClientRect();
|
||||||
const iframeRect = live2d.value.getBoundingClientRect();
|
live2d.value.contentWindow.postMessage({
|
||||||
live2d.value.contentWindow.postMessage({
|
type: 'moveCursor',
|
||||||
type: 'moveCursor',
|
body: {
|
||||||
body: {
|
x: ev.clientX - iframeRect.left,
|
||||||
x: ev.clientX - iframeRect.left,
|
y: ev.clientY - iframeRect.top,
|
||||||
y: ev.clientY - iframeRect.top,
|
},
|
||||||
}
|
}, '*');
|
||||||
}, '*');
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
window.addEventListener('mousemove', onMousemove, { passive: true });
|
window.addEventListener('mousemove', onMousemove, { passive: true });
|
||||||
onUnmounted(() => {
|
});
|
||||||
window.removeEventListener('mousemove', onMousemove);
|
|
||||||
});
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('mousemove', onMousemove);
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
|
|
|
@ -34,9 +34,10 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted, ref } from 'vue';
|
import { onUnmounted, ref } from 'vue';
|
||||||
import { GetFormResultType } from '@/scripts/form';
|
|
||||||
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
||||||
|
import { GetFormResultType } from '@/scripts/form';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
const name = 'calendar';
|
const name = 'calendar';
|
||||||
|
|
||||||
|
@ -85,28 +86,26 @@ const tick = () => {
|
||||||
i18n.ts._weekday.wednesday,
|
i18n.ts._weekday.wednesday,
|
||||||
i18n.ts._weekday.thursday,
|
i18n.ts._weekday.thursday,
|
||||||
i18n.ts._weekday.friday,
|
i18n.ts._weekday.friday,
|
||||||
i18n.ts._weekday.saturday
|
i18n.ts._weekday.saturday,
|
||||||
][now.getDay()];
|
][now.getDay()];
|
||||||
|
|
||||||
const dayNumer = now.getTime() - new Date(ny, nm, nd).getTime();
|
const dayNumer = now.getTime() - new Date(ny, nm, nd).getTime();
|
||||||
const dayDenom = 1000/*ms*/ * 60/*s*/ * 60/*m*/ * 24/*h*/;
|
const dayDenom = 1000/*ms*/ * 60/*s*/ * 60/*m*/ * 24/*h*/;
|
||||||
const monthNumer = now.getTime() - new Date(ny, nm, 1).getTime();
|
const monthNumer = now.getTime() - new Date(ny, nm, 1).getTime();
|
||||||
const monthDenom = new Date(ny, nm + 1, 1).getTime() - new Date(ny, nm, 1).getTime();
|
const monthDenom = new Date(ny, nm + 1, 1).getTime() - new Date(ny, nm, 1).getTime();
|
||||||
const yearNumer = now.getTime() - new Date(ny, 0, 1).getTime();
|
const yearNumer = now.getTime() - new Date(ny, 0, 1).getTime();
|
||||||
const yearDenom = new Date(ny + 1, 0, 1).getTime() - new Date(ny, 0, 1).getTime();
|
const yearDenom = new Date(ny + 1, 0, 1).getTime() - new Date(ny, 0, 1).getTime();
|
||||||
|
|
||||||
dayP.value = dayNumer / dayDenom * 100;
|
dayP.value = dayNumer / dayDenom * 100;
|
||||||
monthP.value = monthNumer / monthDenom * 100;
|
monthP.value = monthNumer / monthDenom * 100;
|
||||||
yearP.value = yearNumer / yearDenom * 100;
|
yearP.value = yearNumer / yearDenom * 100;
|
||||||
|
|
||||||
isHoliday.value = now.getDay() === 0 || now.getDay() === 6;
|
isHoliday.value = now.getDay() === 0 || now.getDay() === 6;
|
||||||
};
|
};
|
||||||
|
|
||||||
tick();
|
useInterval(tick, 1000, {
|
||||||
|
immediate: true,
|
||||||
const intervalId = window.setInterval(tick, 1000);
|
afterMounted: false,
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(intervalId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { GetFormResultType } from '@/scripts/form';
|
||||||
import MkContainer from '@/components/ui/container.vue';
|
import MkContainer from '@/components/ui/container.vue';
|
||||||
import MkMiniChart from '@/components/mini-chart.vue';
|
import MkMiniChart from '@/components/mini-chart.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
const name = 'federation';
|
const name = 'federation';
|
||||||
|
|
||||||
|
@ -64,12 +65,9 @@ const fetch = async () => {
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
useInterval(fetch, 1000 * 60, {
|
||||||
fetch();
|
immediate: true,
|
||||||
const intervalId = window.setInterval(fetch, 1000 * 60);
|
afterMounted: true,
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(intervalId);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { GetFormResultType } from '@/scripts/form';
|
|
||||||
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
||||||
|
import { GetFormResultType } from '@/scripts/form';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
const name = 'onlineUsers';
|
const name = 'onlineUsers';
|
||||||
|
|
||||||
|
@ -43,12 +44,9 @@ const tick = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
useInterval(tick, 1000 * 15, {
|
||||||
tick();
|
immediate: true,
|
||||||
const intervalId = window.setInterval(tick, 1000 * 15);
|
afterMounted: true,
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(intervalId);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
|
|
|
@ -14,10 +14,11 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
import { GetFormResultType } from '@/scripts/form';
|
|
||||||
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
||||||
|
import { GetFormResultType } from '@/scripts/form';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import MkContainer from '@/components/ui/container.vue';
|
import MkContainer from '@/components/ui/container.vue';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
const name = 'rss';
|
const name = 'rss';
|
||||||
|
|
||||||
|
@ -60,12 +61,9 @@ const tick = () => {
|
||||||
|
|
||||||
watch(() => widgetProps.url, tick);
|
watch(() => widgetProps.url, tick);
|
||||||
|
|
||||||
onMounted(() => {
|
useInterval(tick, 60000, {
|
||||||
tick();
|
immediate: true,
|
||||||
const intervalId = window.setInterval(tick, 60000);
|
afterMounted: true,
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(intervalId);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
|
|
|
@ -13,9 +13,10 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
|
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
import { GetFormResultType } from '@/scripts/form';
|
|
||||||
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
||||||
|
import { GetFormResultType } from '@/scripts/form';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
const name = 'slideshow';
|
const name = 'slideshow';
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ const fetch = () => {
|
||||||
os.api('drive/files', {
|
os.api('drive/files', {
|
||||||
folderId: widgetProps.folderId,
|
folderId: widgetProps.folderId,
|
||||||
type: 'image/*',
|
type: 'image/*',
|
||||||
limit: 100
|
limit: 100,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
images.value = res;
|
images.value = res;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
|
@ -96,15 +97,15 @@ const choose = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useInterval(change, 10000, {
|
||||||
|
immediate: false,
|
||||||
|
afterMounted: true,
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (widgetProps.folderId != null) {
|
if (widgetProps.folderId != null) {
|
||||||
fetch();
|
fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
const intervalId = window.setInterval(change, 10000);
|
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(intervalId);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
|
|
|
@ -19,11 +19,12 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { GetFormResultType } from '@/scripts/form';
|
|
||||||
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
|
||||||
|
import { GetFormResultType } from '@/scripts/form';
|
||||||
import MkContainer from '@/components/ui/container.vue';
|
import MkContainer from '@/components/ui/container.vue';
|
||||||
import MkMiniChart from '@/components/mini-chart.vue';
|
import MkMiniChart from '@/components/mini-chart.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
|
||||||
const name = 'hashtags';
|
const name = 'hashtags';
|
||||||
|
|
||||||
|
@ -58,12 +59,9 @@ const fetch = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
useInterval(fetch, 1000 * 60, {
|
||||||
fetch();
|
immediate: true,
|
||||||
const intervalId = window.setInterval(fetch, 1000 * 60);
|
afterMounted: true,
|
||||||
onUnmounted(() => {
|
|
||||||
window.clearInterval(intervalId);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
|
|
Loading…
Reference in a new issue