enhance(frontend): テーマをドラッグ&ドロップできるように
This commit is contained in:
parent
d1446d195a
commit
0b7634b126
|
@ -14,6 +14,7 @@
|
|||
- Enhance: チャットの日本語名称がダイレクトメッセージに戻るとともに、ベータ版機能ではなくなりました
|
||||
- Enhance: 画像編集にマスクエフェクト(塗りつぶし、ぼかし)を追加
|
||||
- Enhance: ウォーターマークにアカウントのQRコードを追加できるように
|
||||
- Enhance: テーマをドラッグ&ドロップできるように
|
||||
- Enhance: 絵文字ピッカーのサイズをより大きくできるように
|
||||
- Enhance: 時刻計算のための基準値を一か所で管理するようにし、パフォーマンスを向上
|
||||
- Fix: iOSで、デバイスがダークモードだと初回読み込み時にエラーになる問題を修正
|
||||
|
|
|
@ -23,6 +23,15 @@ export function setDragData<T extends keyof DragDataMap>(
|
|||
event.dataTransfer.setData(`misskey/${type}`.toLowerCase(), JSON.stringify(data));
|
||||
}
|
||||
|
||||
export function setPlainDragData(
|
||||
event: DragEvent,
|
||||
data: string,
|
||||
) {
|
||||
if (event.dataTransfer == null) return;
|
||||
|
||||
event.dataTransfer.setData('text/plain', data);
|
||||
}
|
||||
|
||||
export function getDragData<T extends keyof DragDataMap>(
|
||||
event: DragEvent,
|
||||
type: T,
|
||||
|
@ -35,6 +44,17 @@ export function getDragData<T extends keyof DragDataMap>(
|
|||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
export function getPlainDragData(
|
||||
event: DragEvent,
|
||||
): string | null {
|
||||
if (event.dataTransfer == null) return null;
|
||||
|
||||
const data = event.dataTransfer.getData('text/plain');
|
||||
if (data == null || data === '') return null;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export function checkDragDataType(
|
||||
event: DragEvent,
|
||||
types: (keyof DragDataMap)[],
|
||||
|
|
|
@ -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(() => []);
|
||||
|
|
Loading…
Reference in New Issue