enhance: プロフィール設定「追加情報」の並び替え・削除に対応 (#10766)
* (enhance) profile fields dnd * Update CHANGELOG.md * Fix typo * fix lint * fix styles * fix lint * (change) style * (fix) label * (fix) typo * (fix) LINT ISSUES * (change) style * remove unnecessary style declaration * (fix) breakpoint
This commit is contained in:
parent
ae21b75687
commit
3a105024c7
|
@ -49,6 +49,7 @@
|
||||||
- 新しい実績を追加
|
- 新しい実績を追加
|
||||||
- Fix: AiScript APIのMk:dialogで何も返していなかったのをNULLを返すように修正
|
- Fix: AiScript APIのMk:dialogで何も返していなかったのをNULLを返すように修正
|
||||||
- Fix: リアクションをホバーした時のユーザーリストで猫耳が切れてしまっていた問題を修正
|
- Fix: リアクションをホバーした時のユーザーリストで猫耳が切れてしまっていた問題を修正
|
||||||
|
- プロフィール設定「追加情報」の項目の削除と並び替えができるように
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- channel/searchのqueryが空の場合に全てのチャンネルを返すように変更
|
- channel/searchのqueryが空の場合に全てのチャンネルを返すように変更
|
||||||
|
|
|
@ -560,6 +560,7 @@ accountDeletedDescription: "このアカウントは削除されています。"
|
||||||
menu: "メニュー"
|
menu: "メニュー"
|
||||||
divider: "分割線"
|
divider: "分割線"
|
||||||
addItem: "項目を追加"
|
addItem: "項目を追加"
|
||||||
|
rearrange: "並び替え"
|
||||||
relays: "リレー"
|
relays: "リレー"
|
||||||
addRelay: "リレーの追加"
|
addRelay: "リレーの追加"
|
||||||
inboxUrl: "inboxのURL"
|
inboxUrl: "inboxのURL"
|
||||||
|
|
|
@ -37,19 +37,40 @@
|
||||||
<template #icon><i class="ti ti-list"></i></template>
|
<template #icon><i class="ti ti-list"></i></template>
|
||||||
<template #label>{{ i18n.ts._profile.metadataEdit }}</template>
|
<template #label>{{ i18n.ts._profile.metadataEdit }}</template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div :class="$style.metadataRoot">
|
||||||
<div>
|
<div :class="$style.metadataMargin">
|
||||||
<MkButton :disabled="fields.length >= 16" inline style="margin-right: 8px;" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
<MkButton :disabled="fields.length >= 16" inline style="margin-right: 8px;" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||||
|
<MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" inline danger style="margin-right: 8px;" @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
||||||
|
<MkButton v-else inline style="margin-right: 8px;" @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
|
||||||
<MkButton inline primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
<MkButton inline primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<FormSplit v-for="(record, i) in fields" :min-width="250">
|
|
||||||
<MkInput v-model="record.name" small>
|
<Sortable
|
||||||
<template #label>{{ i18n.ts._profile.metadataLabel }} #{{ i + 1 }}</template>
|
v-model="fields"
|
||||||
</MkInput>
|
class="_gaps_s"
|
||||||
<MkInput v-model="record.value" small>
|
item-key="id"
|
||||||
<template #label>{{ i18n.ts._profile.metadataContent }} #{{ i + 1 }}</template>
|
:animation="150"
|
||||||
</MkInput>
|
:handle="'.' + $style.dragItemHandle"
|
||||||
</FormSplit>
|
@start="e => e.item.classList.add('active')"
|
||||||
|
@end="e => e.item.classList.remove('active')"
|
||||||
|
>
|
||||||
|
<template #item="{element, index}">
|
||||||
|
<div :class="$style.fieldDragItem">
|
||||||
|
<button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1"><i class="ti ti-menu"></i></button>
|
||||||
|
<button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(index)"><i class="ti ti-x"></i></button>
|
||||||
|
<div :class="$style.dragItemForm">
|
||||||
|
<FormSplit :min-width="200">
|
||||||
|
<MkInput v-model="element.name" small>
|
||||||
|
<template #label>{{ i18n.ts._profile.metadataLabel }}</template>
|
||||||
|
</MkInput>
|
||||||
|
<MkInput v-model="element.value" small>
|
||||||
|
<template #label>{{ i18n.ts._profile.metadataContent }}</template>
|
||||||
|
</MkInput>
|
||||||
|
</FormSplit>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Sortable>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
|
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
|
||||||
|
@ -76,7 +97,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, reactive, watch } from 'vue';
|
import { computed, reactive, ref, watch, defineAsyncComponent, onMounted, onUnmounted } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
|
@ -94,6 +115,8 @@ import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import { claimAchievement } from '@/scripts/achievements';
|
import { claimAchievement } from '@/scripts/achievements';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
|
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||||
|
|
||||||
const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
|
const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
|
||||||
|
|
||||||
const profile = reactive({
|
const profile = reactive({
|
||||||
|
@ -113,22 +136,28 @@ watch(() => profile, () => {
|
||||||
deep: true,
|
deep: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fields = reactive($i.fields.map(field => ({ name: field.name, value: field.value })));
|
const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []);
|
||||||
|
const fieldEditMode = ref(false);
|
||||||
|
|
||||||
function addField() {
|
function addField() {
|
||||||
fields.push({
|
fields.value.push({
|
||||||
|
id: Math.random().toString(),
|
||||||
name: '',
|
name: '',
|
||||||
value: '',
|
value: '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fields.length < 4) {
|
while (fields.value.length < 4) {
|
||||||
addField();
|
addField();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteField(index: number) {
|
||||||
|
fields.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
function saveFields() {
|
function saveFields() {
|
||||||
os.apiWithDialog('i/update', {
|
os.apiWithDialog('i/update', {
|
||||||
fields: fields.filter(field => field.name !== '' && field.value !== ''),
|
fields: fields.value.filter(field => field.name !== '' && field.value !== '').map(field => ({ name: field.name, value: field.value })),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,3 +277,60 @@ definePageMetadata({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<style lang="scss" module>
|
||||||
|
.metadataRoot {
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadataMargin {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldDragItem {
|
||||||
|
display: flex;
|
||||||
|
padding-bottom: .75em;
|
||||||
|
align-items: flex-end;
|
||||||
|
border-bottom: solid 0.5px var(--divider);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (drag button) 32px + (drag button margin) 8px + (input width) 200px * 2 + (input gap) 12px = 452px */
|
||||||
|
@container (max-width: 452px) {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragItemHandle {
|
||||||
|
cursor: grab;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin: 0 8px 0 0;
|
||||||
|
opacity: 0.5;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragItemRemove {
|
||||||
|
@extend .dragItemHandle;
|
||||||
|
|
||||||
|
color: #ff2a2a;
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragItemForm {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue