diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-09-26 15:36:25 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-09-26 15:36:25 +0900 |
| commit | 0b7634b12636398c1e9429fbcca6beef4a144224 (patch) | |
| tree | 43b07b2e6f1de2f56e5d41e2ce0a653a7a7f31eb /packages/frontend/src/pages/settings | |
| parent | feat: scheduled post (#16577) (diff) | |
| download | misskey-0b7634b12636398c1e9429fbcca6beef4a144224.tar.gz misskey-0b7634b12636398c1e9429fbcca6beef4a144224.tar.bz2 misskey-0b7634b12636398c1e9429fbcca6beef4a144224.zip | |
enhance(frontend): テーマをドラッグ&ドロップできるように
Diffstat (limited to 'packages/frontend/src/pages/settings')
| -rw-r--r-- | packages/frontend/src/pages/settings/theme.vue | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index beae1224e4..0129aebe94 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -5,7 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/theme" :label="i18n.ts.theme" :keywords="['theme']" icon="ti ti-palette"> - <div class="_gaps_m"> + <div + class="_gaps_m" + @dragover.prevent.stop="onDragover" + @drop.prevent.stop="onDrop" + > <div v-adaptive-border class="rfqxtzch _panel"> <div class="toggle"> <div class="toggleWrapper"> @@ -58,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="instanceLightTheme.id" /> - <label :for="`themeRadio_${instanceLightTheme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(instanceLightTheme, $event)"> + <label :for="`themeRadio_${instanceLightTheme.id}`" :class="$style.themeItemRoot" class="_button" draggable="true" @dragstart="onThemeDragstart($event, instanceLightTheme)" @contextmenu.prevent.stop="onThemeContextmenu(instanceLightTheme, $event)"> <MkThemePreview :theme="instanceLightTheme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ instanceLightTheme.name }}</div> </label> @@ -78,7 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" draggable="true" @dragstart="onThemeDragstart($event, theme)" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -98,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" draggable="true" @dragstart="onThemeDragstart($event, theme)" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -129,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="instanceDarkTheme.id" /> - <label :for="`themeRadio_${instanceDarkTheme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(instanceDarkTheme, $event)"> + <label :for="`themeRadio_${instanceDarkTheme.id}`" :class="$style.themeItemRoot" class="_button" draggable="true" @dragstart="onThemeDragstart($event, instanceDarkTheme)" @contextmenu.prevent.stop="onThemeContextmenu(instanceDarkTheme, $event)"> <MkThemePreview :theme="instanceDarkTheme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ instanceDarkTheme.name }}</div> </label> @@ -149,7 +153,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" draggable="true" @dragstart="onThemeDragstart($event, theme)" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -169,7 +173,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.themeRadio" :value="theme.id" /> - <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> + <label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" draggable="true" @dragstart="onThemeDragstart($event, theme)" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)"> <MkThemePreview :theme="theme" :class="$style.themeItemPreview"/> <div :class="$style.themeItemCaption">{{ theme.name }}</div> </label> @@ -214,7 +218,7 @@ import FormLink from '@/components/form/link.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkThemePreview from '@/components/MkThemePreview.vue'; import MkInfo from '@/components/MkInfo.vue'; -import { getBuiltinThemesRef, getThemesRef, removeTheme } from '@/theme.js'; +import { getBuiltinThemesRef, getThemesRef, installTheme, parseThemeCode, removeTheme } from '@/theme.js'; import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js'; import { store } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -223,6 +227,7 @@ import { uniqueBy } from '@/utility/array.js'; import { definePage } from '@/page.js'; import { prefer } from '@/preferences.js'; import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; +import { checkDragDataType, getDragData, getPlainDragData, setDragData, setPlainDragData } from '@/drag-and-drop.js'; const installedThemes = getThemesRef(); const builtinThemes = getBuiltinThemesRef(); @@ -321,6 +326,38 @@ function onThemeContextmenu(theme: Theme, ev: MouseEvent) { }], ev); } +function onThemeDragstart(ev: DragEvent, theme: Theme) { + if (!ev.dataTransfer) return; + + ev.dataTransfer.effectAllowed = 'copy'; + setPlainDragData(ev, JSON5.stringify(theme, null, '\t')); +} + +function onDragover(ev: DragEvent) { + if (!ev.dataTransfer) return; + + if (ev.dataTransfer.types[0] === 'text/plain') { + ev.dataTransfer.dropEffect = 'copy'; + } else { + ev.dataTransfer.dropEffect = 'none'; + } + + return false; +} + +async function onDrop(ev: DragEvent) { + if (!ev.dataTransfer) return; + + const code = getPlainDragData(ev); + if (code != null) { + try { + await installTheme(code); + } catch (err) { + // nop + } + } +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); |