diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | packages/frontend/.storybook/generate.tsx | 1 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts | 40 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkImgPreviewDialog.vue | 58 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkPostFormAttaches.vue | 20 |
5 files changed, 116 insertions, 4 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4be21b1444..1f07185516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ * β版として公開のため、旧画面も引き続き利用可能です ### Client +- Feat: 投稿フォームで画像をプレビュー可能に - Enhance: PC画面でチャンネルが複数列で表示されるように (Cherry-picked from https://github.com/Otaku-Social/maniakey/pull/13) - Enhance: 照会に失敗した場合、その理由を表示するように diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index 8830523810..3cd08191f5 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -414,6 +414,7 @@ function toStories(component: string): Promise<string> { glob('src/components/MkSignupServerRules.vue'), glob('src/components/MkUserSetupDialog.vue'), glob('src/components/MkUserSetupDialog.*.vue'), + glob('src/components/MkImgPreviewDialog.vue'), glob('src/components/MkInstanceCardMini.vue'), glob('src/components/MkInviteCode.vue'), glob('src/components/MkTagItem.vue'), diff --git a/packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts b/packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts new file mode 100644 index 0000000000..339e6d10f3 --- /dev/null +++ b/packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { StoryObj } from '@storybook/vue3'; +import { file } from '../../.storybook/fakes.js'; +import MkImgPreviewDialog from './MkImgPreviewDialog.vue'; +export const Default = { + render(args) { + return { + components: { + MkImgPreviewDialog, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '<MkImgPreviewDialog v-bind="props" />', + }; + }, + args: { + file: file(), + }, + parameters: { + chromatic: { + // NOTE: ロードが終わるまで待つ + delay: 3000, + }, + layout: 'centered', + }, +} satisfies StoryObj<typeof MkImgPreviewDialog>; diff --git a/packages/frontend/src/components/MkImgPreviewDialog.vue b/packages/frontend/src/components/MkImgPreviewDialog.vue new file mode 100644 index 0000000000..3e6e4e0ec9 --- /dev/null +++ b/packages/frontend/src/components/MkImgPreviewDialog.vue @@ -0,0 +1,58 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkModalWindow + ref="modal" + :width="1800" + :height="900" + @close="close" + @esc="close" + @click="close" +> + <template #header>{{ file.name }}</template> + <div :class="$style.container"> + <img :src="file.url" :alt="file.comment ?? file.name" :class="$style.img"/> + </div> +</MkModalWindow> +</template> +<script lang="ts" setup> +import { defineProps, ref } from 'vue'; +import MkModalWindow from './MkModalWindow.vue'; +import type * as Misskey from 'misskey-js'; + +defineProps<{ + file: Misskey.entities.DriveFile; +}>(); + +const modal = ref<typeof MkModalWindow | null>(null); + +function close() { + modal.value?.close(); +} + +</script> +<style lang="scss" module> + .container { + box-sizing: border-box; + width: 100%; + height: 100%; + min-height: 0; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + + background-color: var(--MI_THEME-bg); + background-size: auto auto; + background-image: repeating-linear-gradient(135deg, transparent, transparent 6px, var(--MI_THEME-panel) 6px, var(--MI_THEME-panel) 12px); + } + + .img { + width: 100%; + max-height: 100%; + object-fit: contain; + } +</style> diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index 1336b61356..33fe82b759 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -22,20 +22,24 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> </Sortable> - <p :class="[$style.remain, { - [$style.exceeded]: props.modelValue.length > 16, - }]">{{ 16 - props.modelValue.length }}/16</p> + <p + :class="[$style.remain, { + [$style.exceeded]: props.modelValue.length > 16, + }]" + > + {{ 16 - props.modelValue.length }}/16 + </p> </div> </template> <script lang="ts" setup> import { defineAsyncComponent, inject } from 'vue'; import * as Misskey from 'misskey-js'; +import type { MenuItem } from '@/types/menu'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import type { MenuItem } from '@/types/menu.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -168,6 +172,14 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar text: i18n.ts.cropImage, icon: 'ti ti-crop', action: () : void => { crop(file); }, + }, { + text: i18n.ts.preview, + icon: 'ti ti-photo-search', + action: () => { + os.popup(defineAsyncComponent(() => import('@/components/MkImgPreviewDialog.vue')), { + file: file, + }); + }, }); } |