diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-01-10 06:08:40 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2023-01-10 06:08:40 +0900 |
| commit | 618405c4d39753c1a9135fd0759aff2ecb3a94b3 (patch) | |
| tree | b0551494d3ffd37ee3e7f7ad75bc81f54d3fb249 /packages/frontend/src/widgets/WidgetSlideshow.vue | |
| parent | refactor(client): use css modules (diff) | |
| download | misskey-618405c4d39753c1a9135fd0759aff2ecb3a94b3.tar.gz misskey-618405c4d39753c1a9135fd0759aff2ecb3a94b3.tar.bz2 misskey-618405c4d39753c1a9135fd0759aff2ecb3a94b3.zip | |
refactor(client): rename widget filename
Diffstat (limited to 'packages/frontend/src/widgets/WidgetSlideshow.vue')
| -rw-r--r-- | packages/frontend/src/widgets/WidgetSlideshow.vue | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue new file mode 100644 index 0000000000..2c937d85c5 --- /dev/null +++ b/packages/frontend/src/widgets/WidgetSlideshow.vue @@ -0,0 +1,159 @@ +<template> +<div class="kvausudm _panel mkw-slideshow" :style="{ height: widgetProps.height + 'px' }"> + <div @click="choose"> + <p v-if="widgetProps.folderId == null"> + {{ i18n.ts.folder }} + </p> + <p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ $t('no-image') }}</p> + <div ref="slideA" class="slide a"></div> + <div ref="slideB" class="slide b"></div> + </div> +</div> +</template> + +<script lang="ts" setup> +import { nextTick, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { GetFormResultType } from '@/scripts/form'; +import * as os from '@/os'; +import { useInterval } from '@/scripts/use-interval'; +import { i18n } from '@/i18n'; + +const name = 'slideshow'; + +const widgetPropsDef = { + height: { + type: 'number' as const, + default: 300, + }, + folderId: { + type: 'string' as const, + default: null, + hidden: true, + }, +}; + +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, save } = useWidgetPropsManager(name, + widgetPropsDef, + props, + emit, +); + +const images = ref([]); +const fetching = ref(true); +const slideA = shallowRef<HTMLElement>(); +const slideB = shallowRef<HTMLElement>(); + +const change = () => { + if (images.value.length === 0) return; + + const index = Math.floor(Math.random() * images.value.length); + const img = `url(${ images.value[index].url })`; + + slideB.value.style.backgroundImage = img; + + slideB.value.classList.add('anime'); + window.setTimeout(() => { + // 既にこのウィジェットがunmountされていたら要素がない + if (slideA.value == null) return; + + slideA.value.style.backgroundImage = img; + + slideB.value.classList.remove('anime'); + }, 1000); +}; + +const fetch = () => { + fetching.value = true; + + os.api('drive/files', { + folderId: widgetProps.folderId, + type: 'image/*', + limit: 100, + }).then(res => { + images.value = res; + fetching.value = false; + slideA.value.style.backgroundImage = ''; + slideB.value.style.backgroundImage = ''; + change(); + }); +}; + +const choose = () => { + os.selectDriveFolder(false).then(folder => { + if (folder == null) { + return; + } + widgetProps.folderId = folder.id; + save(); + fetch(); + }); +}; + +useInterval(change, 10000, { + immediate: false, + afterMounted: true, +}); + +onMounted(() => { + if (widgetProps.folderId != null) { + fetch(); + } +}); + +defineExpose<WidgetComponentExpose>({ + name, + configure, + id: props.widget ? props.widget.id : null, +}); +</script> + +<style lang="scss" scoped> +.kvausudm { + position: relative; + + > div { + width: 100%; + height: 100%; + cursor: pointer; + + > p { + display: block; + margin: 1em; + text-align: center; + color: #888; + } + + > * { + pointer-events: none; + } + + > .slide { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-size: cover; + background-position: center; + + &.b { + opacity: 0; + } + + &.anime { + transition: opacity 1s; + opacity: 1; + } + } + } +} +</style> |