fix: 絵文字管理画面の表示不具合の修正
(cherry picked from commit 5300ff763bb008699b35dc6e219b50ac1c4fd7af)
This commit is contained in:
parent
21bee2ffde
commit
4c7cd47fb6
|
@ -0,0 +1,229 @@
|
||||||
|
<template>
|
||||||
|
<MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft">
|
||||||
|
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
||||||
|
<template #default="{items}">
|
||||||
|
<div class="ldhfsamy">
|
||||||
|
<template v-for="emoji in items" :key="emoji.id">
|
||||||
|
<div class="emoji _panel">
|
||||||
|
<div class="img">
|
||||||
|
<div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div>
|
||||||
|
<div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<div class="name _monospace">{{ i18n.ts.name }}: {{ emoji.name }}</div>
|
||||||
|
<div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div>
|
||||||
|
<div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div>
|
||||||
|
<div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="edit-button">
|
||||||
|
<button class="edit _button" @click="editDraft(emoji)">
|
||||||
|
{{ i18n.ts.edit }}
|
||||||
|
</button>
|
||||||
|
<button class="draft _button" @click="undrafted(emoji)">
|
||||||
|
{{ i18n.ts.undrafted }}
|
||||||
|
</button>
|
||||||
|
<button class="delete _button" @click="deleteDraft(emoji)">
|
||||||
|
{{ i18n.ts.delete }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MkPagination>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
|
||||||
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
|
const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
|
const query = ref(null);
|
||||||
|
|
||||||
|
const paginationDraft = {
|
||||||
|
endpoint: 'admin/emoji/list' as const,
|
||||||
|
limit: 30,
|
||||||
|
params: computed(() => ({
|
||||||
|
query: (query.value && query.value !== '') ? query.value : null,
|
||||||
|
draft: true,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
const editDraft = (emoji) => {
|
||||||
|
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
||||||
|
emoji: emoji,
|
||||||
|
isRequest: false,
|
||||||
|
}, {
|
||||||
|
done: result => {
|
||||||
|
if (result.updated) {
|
||||||
|
emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({
|
||||||
|
...oldEmoji,
|
||||||
|
...result.updated,
|
||||||
|
}));
|
||||||
|
emojisDraftPaginationComponent.value.reload();
|
||||||
|
} else if (result.deleted) {
|
||||||
|
emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
||||||
|
emojisDraftPaginationComponent.value.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, 'closed');
|
||||||
|
};
|
||||||
|
|
||||||
|
async function undrafted(emoji) {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.t('undraftAreYouSure', { x: emoji.name }),
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
await os.api('admin/emoji/update', {
|
||||||
|
id: emoji.id,
|
||||||
|
name: emoji.name,
|
||||||
|
category: emoji.category,
|
||||||
|
aliases: emoji.aliases,
|
||||||
|
license: emoji.license,
|
||||||
|
draft: false,
|
||||||
|
isSensitive: emoji.isSensitive,
|
||||||
|
localOnly: emoji.localOnly,
|
||||||
|
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
||||||
|
emojisDraftPaginationComponent.value.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteDraft(emoji) {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.t('removeAreYouSure', { x: emoji.name }),
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
os.api('admin/emoji/delete', {
|
||||||
|
id: emoji.id,
|
||||||
|
}).then(() => {
|
||||||
|
emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
||||||
|
emojisDraftPaginationComponent.value.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.empty {
|
||||||
|
margin: var(--margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ldhfsamy {
|
||||||
|
> .emoji {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 40px 1fr;
|
||||||
|
grid-template-columns: 1fr 150px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border: solid 1px var(--panel);
|
||||||
|
width: 100%;
|
||||||
|
margin: 10px;
|
||||||
|
|
||||||
|
> .img {
|
||||||
|
display: grid;
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 1/ span 2;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
place-content: center;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
> .imgLight {
|
||||||
|
display: grid;
|
||||||
|
grid-column: 1;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
max-height: 30px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .imgDark {
|
||||||
|
display: grid;
|
||||||
|
grid-column: 2;
|
||||||
|
background-color: #000;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
max-height: 30px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .info {
|
||||||
|
display: grid;
|
||||||
|
grid-row: 2;
|
||||||
|
grid-template-rows: 30px 30px 30px;
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
grid-row: 1;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .category {
|
||||||
|
grid-row: 2;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .aliases {
|
||||||
|
grid-row: 3;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .license {
|
||||||
|
grid-row: 4;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .edit-button {
|
||||||
|
display: grid;
|
||||||
|
grid-row: 2;
|
||||||
|
grid-template-rows: 30px 30px 30px;
|
||||||
|
|
||||||
|
> .edit {
|
||||||
|
grid-row: 1;
|
||||||
|
background-color: var(--buttonBg);
|
||||||
|
margin: 2px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .draft {
|
||||||
|
grid-row: 2;
|
||||||
|
background-color: var(--buttonBg);
|
||||||
|
margin: 2px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .delete {
|
||||||
|
background-color: var(--buttonBg);
|
||||||
|
grid-row: 3;
|
||||||
|
margin: 2px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,225 @@
|
||||||
|
<template>
|
||||||
|
<MkInput v-model="query" :debounce="true" type="search">
|
||||||
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
|
<template #label>{{ i18n.ts.search }}</template>
|
||||||
|
</MkInput>
|
||||||
|
<MkSwitch v-model="selectMode" style="margin: 8px 0;">
|
||||||
|
<template #label>Select mode</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<div v-if="selectMode" class="_buttons">
|
||||||
|
<MkButton inline @click="selectAll">Select all</MkButton>
|
||||||
|
<MkButton inline @click="setCategoryBulk">Set category</MkButton>
|
||||||
|
<MkButton inline @click="setTagBulk">Set tag</MkButton>
|
||||||
|
<MkButton inline @click="addTagBulk">Add tag</MkButton>
|
||||||
|
<MkButton inline @click="removeTagBulk">Remove tag</MkButton>
|
||||||
|
<MkButton inline @click="setLisenceBulk">Set Lisence</MkButton>
|
||||||
|
<MkButton inline danger @click="delBulk">Delete</MkButton>
|
||||||
|
</div>
|
||||||
|
<MkPagination ref="emojisPaginationComponent" :pagination="pagination" :displayLimit="100">
|
||||||
|
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
||||||
|
<template #default="{items}">
|
||||||
|
<div class="ldhfsamy">
|
||||||
|
<div v-for="emoji in items" :key="emoji.id">
|
||||||
|
<button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
||||||
|
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||||
|
<div class="body">
|
||||||
|
<div class="name _monospace">{{ emoji.name + ' (draft)' }}</div>
|
||||||
|
<div class="info">{{ emoji.category }}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
||||||
|
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||||
|
<div class="body">
|
||||||
|
<div class="name _monospace">{{ emoji.name }}</div>
|
||||||
|
<div class="info">{{ emoji.category }}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MkPagination>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
|
const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
|
const query = ref(null);
|
||||||
|
const selectMode = ref(false);
|
||||||
|
const selectedEmojis = ref<string[]>([]);
|
||||||
|
|
||||||
|
const pagination = {
|
||||||
|
endpoint: 'admin/emoji/list' as const,
|
||||||
|
limit: 30,
|
||||||
|
params: computed(() => ({
|
||||||
|
query: (query.value && query.value !== '') ? query.value : null,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectAll = () => {
|
||||||
|
if (selectedEmojis.value.length > 0) {
|
||||||
|
selectedEmojis.value = [];
|
||||||
|
} else {
|
||||||
|
selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelect = (emoji) => {
|
||||||
|
if (selectedEmojis.value.includes(emoji.id)) {
|
||||||
|
selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id);
|
||||||
|
} else {
|
||||||
|
selectedEmojis.value.push(emoji.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const edit = (emoji) => {
|
||||||
|
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
||||||
|
emoji: emoji,
|
||||||
|
isRequest: false,
|
||||||
|
}, {
|
||||||
|
done: result => {
|
||||||
|
if (result.updated) {
|
||||||
|
emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({
|
||||||
|
...oldEmoji,
|
||||||
|
...result.updated,
|
||||||
|
}));
|
||||||
|
emojisPaginationComponent.value.reload();
|
||||||
|
} else if (result.deleted) {
|
||||||
|
emojisPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, 'closed');
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCategoryBulk = async () => {
|
||||||
|
const { canceled, result } = await os.inputText({
|
||||||
|
title: 'Category',
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog('admin/emoji/set-category-bulk', {
|
||||||
|
ids: selectedEmojis.value,
|
||||||
|
category: result,
|
||||||
|
});
|
||||||
|
emojisPaginationComponent.value.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const setLisenceBulk = async () => {
|
||||||
|
const { canceled, result } = await os.inputText({
|
||||||
|
title: 'License',
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog('admin/emoji/set-license-bulk', {
|
||||||
|
ids: selectedEmojis.value,
|
||||||
|
license: result,
|
||||||
|
});
|
||||||
|
emojisPaginationComponent.value.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const addTagBulk = async () => {
|
||||||
|
const { canceled, result } = await os.inputText({
|
||||||
|
title: 'Tag',
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog('admin/emoji/add-aliases-bulk', {
|
||||||
|
ids: selectedEmojis.value,
|
||||||
|
aliases: result.split(' '),
|
||||||
|
});
|
||||||
|
emojisPaginationComponent.value.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTagBulk = async () => {
|
||||||
|
const { canceled, result } = await os.inputText({
|
||||||
|
title: 'Tag',
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog('admin/emoji/remove-aliases-bulk', {
|
||||||
|
ids: selectedEmojis.value,
|
||||||
|
aliases: result.split(' '),
|
||||||
|
});
|
||||||
|
emojisPaginationComponent.value.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const setTagBulk = async () => {
|
||||||
|
const { canceled, result } = await os.inputText({
|
||||||
|
title: 'Tag',
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog('admin/emoji/set-aliases-bulk', {
|
||||||
|
ids: selectedEmojis.value,
|
||||||
|
aliases: result.split(' '),
|
||||||
|
});
|
||||||
|
emojisPaginationComponent.value.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
const delBulk = async () => {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts.deleteConfirm,
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog('admin/emoji/delete-bulk', {
|
||||||
|
ids: selectedEmojis.value,
|
||||||
|
});
|
||||||
|
emojisPaginationComponent.value.reload();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.ldhfsamy {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
||||||
|
grid-gap: var(--margin);
|
||||||
|
|
||||||
|
div > .emoji {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border: solid 1px var(--panel);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--inputBorderHover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .img {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .body {
|
||||||
|
padding: 0 0 0 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .info {
|
||||||
|
opacity: 0.5;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-draft {
|
||||||
|
--c: rgb(255 196 0 / 15%);;
|
||||||
|
background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%);
|
||||||
|
background-size: 16px 16px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,110 @@
|
||||||
|
<template>
|
||||||
|
<FormSplit>
|
||||||
|
<MkInput v-model="queryRemote" :debounce="true" type="search">
|
||||||
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
|
<template #label>{{ i18n.ts.search }}</template>
|
||||||
|
</MkInput>
|
||||||
|
<MkInput v-model="host" :debounce="true">
|
||||||
|
<template #label>{{ i18n.ts.host }}</template>
|
||||||
|
</MkInput>
|
||||||
|
</FormSplit>
|
||||||
|
<MkPagination :pagination="remotePagination" :displayLimit="100">
|
||||||
|
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
||||||
|
<template #default="{items}">
|
||||||
|
<div class="ldhfsamy">
|
||||||
|
<div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)">
|
||||||
|
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||||
|
<div class="body">
|
||||||
|
<div class="name _monospace">{{ emoji.name }}</div>
|
||||||
|
<div class="info">{{ emoji.host }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MkPagination>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
|
const queryRemote = ref(null);
|
||||||
|
const host = ref(null);
|
||||||
|
|
||||||
|
const remotePagination = {
|
||||||
|
endpoint: 'admin/emoji/list-remote' as const,
|
||||||
|
limit: 30,
|
||||||
|
params: computed(() => ({
|
||||||
|
query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null,
|
||||||
|
host: (host.value && host.value !== '') ? host.value : null,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
const im = (emoji) => {
|
||||||
|
os.apiWithDialog('admin/emoji/copy', {
|
||||||
|
emojiId: emoji.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const remoteMenu = (emoji, ev: MouseEvent) => {
|
||||||
|
os.popupMenu([{
|
||||||
|
type: 'label',
|
||||||
|
text: ':' + emoji.name + ':',
|
||||||
|
}, {
|
||||||
|
text: i18n.ts.import,
|
||||||
|
icon: 'ti ti-plus',
|
||||||
|
action: () => { im(emoji); },
|
||||||
|
}], ev.currentTarget ?? ev.target);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.empty {
|
||||||
|
margin: var(--margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ldhfsamy {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
margin: var(--margin) 0;
|
||||||
|
|
||||||
|
> .emoji {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .body {
|
||||||
|
padding: 0 0 0 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .info {
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 90%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -120,7 +120,7 @@ function toggleTag(tag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const edit = () => {
|
const edit = () => {
|
||||||
os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
||||||
isRequest: true,
|
isRequest: true,
|
||||||
}, {
|
}, {
|
||||||
done: result => {
|
done: result => {
|
||||||
|
|
|
@ -10,106 +10,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkSpacer :contentMax="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="ogwlenmc">
|
<div class="ogwlenmc">
|
||||||
<div v-if="tab === 'local'" class="local">
|
<div v-if="tab === 'local'" class="local">
|
||||||
<MkInput v-model="query" :debounce="true" type="search">
|
<MkCustomEmojiEditLocal/>
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
|
||||||
<template #label>{{ i18n.ts.search }}</template>
|
|
||||||
</MkInput>
|
|
||||||
<MkSwitch v-model="selectMode" style="margin: 8px 0;">
|
|
||||||
<template #label>Select mode</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<div v-if="selectMode" class="_buttons">
|
|
||||||
<MkButton inline @click="selectAll">Select all</MkButton>
|
|
||||||
<MkButton inline @click="setCategoryBulk">Set category</MkButton>
|
|
||||||
<MkButton inline @click="setTagBulk">Set tag</MkButton>
|
|
||||||
<MkButton inline @click="addTagBulk">Add tag</MkButton>
|
|
||||||
<MkButton inline @click="removeTagBulk">Remove tag</MkButton>
|
|
||||||
<MkButton inline @click="setLicenseBulk">Set License</MkButton>
|
|
||||||
<MkButton inline danger @click="delBulk">Delete</MkButton>
|
|
||||||
</div>
|
|
||||||
<MkPagination ref="emojisPaginationComponent" :pagination="pagination">
|
|
||||||
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
|
||||||
<template #default="{items}">
|
|
||||||
<div class="ldhfsamy">
|
|
||||||
<div v-for="emoji in items" :key="emoji.id">
|
|
||||||
<button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
|
||||||
<img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/>
|
|
||||||
<div class="body">
|
|
||||||
<div class="name _monospace">{{ emoji.name + ' (draft)' }}</div>
|
|
||||||
<div class="info">{{ emoji.category }}</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
|
|
||||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
|
||||||
<div class="body">
|
|
||||||
<div class="name _monospace">{{ emoji.name }}</div>
|
|
||||||
<div class="info">{{ emoji.category }}</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</MkPagination>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="tab === 'draft'" class="draft">
|
<div v-if="tab === 'draft'" class="draft">
|
||||||
<MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft">
|
<MkCustomEmojiEditDraft/>
|
||||||
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
|
||||||
<template #default="{items}">
|
|
||||||
<div class="ldhfsamy">
|
|
||||||
<template v-for="emoji in items" :key="emoji.id">
|
|
||||||
<div class="emoji _panel">
|
|
||||||
<div class="img">
|
|
||||||
<div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div>
|
|
||||||
<div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div>
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
|
||||||
<div class="name _monospace">{{ i18n.ts.name }}: {{ emoji.name }}</div>
|
|
||||||
<div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div>
|
|
||||||
<div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div>
|
|
||||||
<div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="edit-button">
|
|
||||||
<button class="edit _button" @click="editDraft(emoji)">
|
|
||||||
{{ i18n.ts.edit }}
|
|
||||||
</button>
|
|
||||||
<button class="draft _button" @click="undrafted(emoji)">
|
|
||||||
{{ i18n.ts.undrafted }}
|
|
||||||
</button>
|
|
||||||
<button class="delete _button" @click="deleteDraft(emoji)">
|
|
||||||
{{ i18n.ts.delete }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</MkPagination>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="tab === 'remote'" class="remote">
|
<div v-else-if="tab === 'remote'" class="remote">
|
||||||
<FormSplit>
|
<MkCustomEmojiEditRemote/>
|
||||||
<MkInput v-model="queryRemote" :debounce="true" type="search">
|
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
|
||||||
<template #label>{{ i18n.ts.search }}</template>
|
|
||||||
</MkInput>
|
|
||||||
<MkInput v-model="host" :debounce="true">
|
|
||||||
<template #label>{{ i18n.ts.host }}</template>
|
|
||||||
</MkInput>
|
|
||||||
</FormSplit>
|
|
||||||
<MkPagination :pagination="remotePagination">
|
|
||||||
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
|
|
||||||
<template #default="{items}">
|
|
||||||
<div class="ldhfsamy">
|
|
||||||
<div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)">
|
|
||||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
|
||||||
<div class="body">
|
|
||||||
<div class="name _monospace">{{ emoji.name }}</div>
|
|
||||||
<div class="info">{{ emoji.host }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</MkPagination>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
@ -118,171 +25,30 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
|
import { computed, defineAsyncComponent, ref } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkCustomEmojiEditDraft from '@/components/MkCustomEmojiEditDraft.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkCustomEmojiEditLocal from '@/components/MkCustomEmojiEditLocal.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkCustomEmojiEditRemote from '@/components/MkCustomEmojiEditRemote.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import { selectFile } from '@/scripts/select-file';
|
||||||
import FormSplit from '@/components/form/split.vue';
|
import * as os from '@/os';
|
||||||
import { selectFile } from '@/scripts/select-file.js';
|
import { i18n } from '@/i18n';
|
||||||
import * as os from '@/os.js';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import { i18n } from '@/i18n.js';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
|
||||||
|
|
||||||
const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
|
||||||
const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
|
||||||
|
|
||||||
const tab = ref('draft');
|
const tab = ref('draft');
|
||||||
const query = ref(null);
|
|
||||||
const queryRemote = ref(null);
|
|
||||||
const host = ref(null);
|
|
||||||
const selectMode = ref(false);
|
|
||||||
const selectedEmojis = ref<string[]>([]);
|
|
||||||
|
|
||||||
const pagination = {
|
|
||||||
endpoint: 'admin/emoji/list' as const,
|
|
||||||
limit: 30,
|
|
||||||
params: computed(() => ({
|
|
||||||
query: (query.value && query.value !== '') ? query.value : null,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
const paginationDraft = {
|
|
||||||
endpoint: 'admin/emoji/list' as const,
|
|
||||||
limit: 30,
|
|
||||||
params: computed(() => ({
|
|
||||||
query: (query.value && query.value !== '') ? query.value : null,
|
|
||||||
draft: true,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
const remotePagination = {
|
|
||||||
endpoint: 'admin/emoji/list-remote' as const,
|
|
||||||
limit: 30,
|
|
||||||
params: computed(() => ({
|
|
||||||
query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null,
|
|
||||||
host: (host.value && host.value !== '') ? host.value : null,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectAll = () => {
|
|
||||||
if (selectedEmojis.value.length > 0) {
|
|
||||||
selectedEmojis.value = [];
|
|
||||||
} else {
|
|
||||||
selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleSelect = (emoji) => {
|
|
||||||
if (selectedEmojis.value.includes(emoji.id)) {
|
|
||||||
selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id);
|
|
||||||
} else {
|
|
||||||
selectedEmojis.value.push(emoji.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const add = async (ev: MouseEvent) => {
|
const add = async (ev: MouseEvent) => {
|
||||||
os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), {
|
||||||
}, {
|
}, {
|
||||||
done: result => {
|
done: result => {
|
||||||
if (result.created) {
|
//TODO: emitにして追加を反映
|
||||||
emojisPaginationComponent.value.prepend(result.created);
|
// if (result.created) {
|
||||||
emojisPaginationComponent.value.reload();
|
// emojisPaginationComponent.value.prepend(result.created);
|
||||||
}
|
// emojisPaginationComponent.value.reload();
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
};
|
};
|
||||||
|
|
||||||
const edit = (emoji) => {
|
|
||||||
os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
|
|
||||||
emoji: emoji,
|
|
||||||
}, {
|
|
||||||
done: result => {
|
|
||||||
if (result.updated) {
|
|
||||||
emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({
|
|
||||||
...oldEmoji,
|
|
||||||
...result.updated,
|
|
||||||
}));
|
|
||||||
emojisPaginationComponent.value.reload();
|
|
||||||
} else if (result.deleted) {
|
|
||||||
emojisPaginationComponent.value.removeItem(emoji.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, 'closed');
|
|
||||||
};
|
|
||||||
|
|
||||||
const editDraft = (emoji) => {
|
|
||||||
os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
|
|
||||||
emoji: emoji,
|
|
||||||
isRequest: false,
|
|
||||||
}, {
|
|
||||||
done: result => {
|
|
||||||
if (result.updated) {
|
|
||||||
emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({
|
|
||||||
...oldEmoji,
|
|
||||||
...result.updated,
|
|
||||||
}));
|
|
||||||
emojisDraftPaginationComponent.value.reload();
|
|
||||||
} else if (result.deleted) {
|
|
||||||
emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, 'closed');
|
|
||||||
};
|
|
||||||
|
|
||||||
async function undrafted(emoji) {
|
|
||||||
const { canceled } = await os.confirm({
|
|
||||||
type: 'warning',
|
|
||||||
text: i18n.t('undraftAreYouSure', { x: emoji.name }),
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
|
|
||||||
await os.api('admin/emoji/update', {
|
|
||||||
id: emoji.id,
|
|
||||||
name: emoji.name,
|
|
||||||
category: emoji.category,
|
|
||||||
aliases: emoji.aliases,
|
|
||||||
license: emoji.license,
|
|
||||||
draft: false,
|
|
||||||
isSensitive: emoji.isSensitive,
|
|
||||||
localOnly: emoji.localOnly,
|
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteDraft(emoji) {
|
|
||||||
const { canceled } = await os.confirm({
|
|
||||||
type: 'warning',
|
|
||||||
text: i18n.t('removeAreYouSure', { x: emoji.name }),
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
|
|
||||||
os.api('admin/emoji/delete', {
|
|
||||||
id: emoji.id,
|
|
||||||
}).then(() => {
|
|
||||||
emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const im = (emoji) => {
|
|
||||||
os.apiWithDialog('admin/emoji/copy', {
|
|
||||||
emojiId: emoji.id,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const remoteMenu = (emoji, ev: MouseEvent) => {
|
|
||||||
os.popupMenu([{
|
|
||||||
type: 'label',
|
|
||||||
text: ':' + emoji.name + ':',
|
|
||||||
}, {
|
|
||||||
text: i18n.ts.import,
|
|
||||||
icon: 'ti ti-plus',
|
|
||||||
action: () => { im(emoji); },
|
|
||||||
}], ev.currentTarget ?? ev.target);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menu = (ev: MouseEvent) => {
|
const menu = (ev: MouseEvent) => {
|
||||||
os.popupMenu([{
|
os.popupMenu([{
|
||||||
icon: 'ti ti-download',
|
icon: 'ti ti-download',
|
||||||
|
@ -325,78 +91,6 @@ const menu = (ev: MouseEvent) => {
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCategoryBulk = async () => {
|
|
||||||
const { canceled, result } = await os.inputText({
|
|
||||||
title: 'Category',
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
await os.apiWithDialog('admin/emoji/set-category-bulk', {
|
|
||||||
ids: selectedEmojis.value,
|
|
||||||
category: result,
|
|
||||||
});
|
|
||||||
emojisPaginationComponent.value.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setLicenseBulk = async () => {
|
|
||||||
const { canceled, result } = await os.inputText({
|
|
||||||
title: 'License',
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
await os.apiWithDialog('admin/emoji/set-license-bulk', {
|
|
||||||
ids: selectedEmojis.value,
|
|
||||||
license: result,
|
|
||||||
});
|
|
||||||
emojisPaginationComponent.value.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const addTagBulk = async () => {
|
|
||||||
const { canceled, result } = await os.inputText({
|
|
||||||
title: 'Tag',
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
await os.apiWithDialog('admin/emoji/add-aliases-bulk', {
|
|
||||||
ids: selectedEmojis.value,
|
|
||||||
aliases: result.split(' '),
|
|
||||||
});
|
|
||||||
emojisPaginationComponent.value.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeTagBulk = async () => {
|
|
||||||
const { canceled, result } = await os.inputText({
|
|
||||||
title: 'Tag',
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
await os.apiWithDialog('admin/emoji/remove-aliases-bulk', {
|
|
||||||
ids: selectedEmojis.value,
|
|
||||||
aliases: result.split(' '),
|
|
||||||
});
|
|
||||||
emojisPaginationComponent.value.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setTagBulk = async () => {
|
|
||||||
const { canceled, result } = await os.inputText({
|
|
||||||
title: 'Tag',
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
await os.apiWithDialog('admin/emoji/set-aliases-bulk', {
|
|
||||||
ids: selectedEmojis.value,
|
|
||||||
aliases: result.split(' '),
|
|
||||||
});
|
|
||||||
emojisPaginationComponent.value.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const delBulk = async () => {
|
|
||||||
const { canceled } = await os.confirm({
|
|
||||||
type: 'warning',
|
|
||||||
text: i18n.ts.deleteConfirm,
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
await os.apiWithDialog('admin/emoji/delete-bulk', {
|
|
||||||
ids: selectedEmojis.value,
|
|
||||||
});
|
|
||||||
emojisPaginationComponent.value.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
const headerActions = $computed(() => [{
|
||||||
asFullButton: true,
|
asFullButton: true,
|
||||||
icon: 'ti ti-plus',
|
icon: 'ti ti-plus',
|
||||||
|
@ -425,226 +119,4 @@ definePageMetadata(computed(() => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.ogwlenmc {
|
|
||||||
> .local {
|
|
||||||
.empty {
|
|
||||||
margin: var(--margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ldhfsamy {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
|
||||||
grid-gap: 12px;
|
|
||||||
margin: var(--margin) 0;
|
|
||||||
|
|
||||||
div > .emoji {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 11px;
|
|
||||||
text-align: left;
|
|
||||||
border: solid 1px var(--panel);
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: var(--inputBorderHover);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
border-color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .img {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .body {
|
|
||||||
padding: 0 0 0 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
> .name {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .info {
|
|
||||||
opacity: 0.5;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .draft {
|
|
||||||
.empty {
|
|
||||||
margin: var(--margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ldhfsamy {
|
|
||||||
> .emoji {
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: 40px 1fr;
|
|
||||||
grid-template-columns: 1fr 150px;
|
|
||||||
align-items: center;
|
|
||||||
padding: 11px;
|
|
||||||
text-align: left;
|
|
||||||
border: solid 1px var(--panel);
|
|
||||||
width: 100%;
|
|
||||||
margin: 10px;
|
|
||||||
|
|
||||||
> .img {
|
|
||||||
display: grid;
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 1/ span 2;
|
|
||||||
grid-template-columns: 50% 50%;
|
|
||||||
place-content: center;
|
|
||||||
place-items: center;
|
|
||||||
|
|
||||||
> .imgLight {
|
|
||||||
display: grid;
|
|
||||||
grid-column: 1;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
> img {
|
|
||||||
max-height: 30px;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .imgDark {
|
|
||||||
display: grid;
|
|
||||||
grid-column: 2;
|
|
||||||
background-color: #000;
|
|
||||||
|
|
||||||
> img {
|
|
||||||
max-height: 30px;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .info {
|
|
||||||
display: grid;
|
|
||||||
grid-row: 2;
|
|
||||||
grid-template-rows: 30px 30px 30px;
|
|
||||||
|
|
||||||
> .name {
|
|
||||||
grid-row: 1;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .category {
|
|
||||||
grid-row: 2;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .aliases {
|
|
||||||
grid-row: 3;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .license {
|
|
||||||
grid-row: 4;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .edit-button {
|
|
||||||
display: grid;
|
|
||||||
grid-row: 2;
|
|
||||||
grid-template-rows: 30px 30px 30px;
|
|
||||||
|
|
||||||
> .edit {
|
|
||||||
grid-row: 1;
|
|
||||||
background-color: var(--buttonBg);
|
|
||||||
margin: 2px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .draft {
|
|
||||||
grid-row: 2;
|
|
||||||
background-color: var(--buttonBg);
|
|
||||||
margin: 2px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .delete {
|
|
||||||
background-color: var(--buttonBg);
|
|
||||||
grid-row: 3;
|
|
||||||
margin: 2px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .remote {
|
|
||||||
.empty {
|
|
||||||
margin: var(--margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ldhfsamy {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
|
||||||
grid-gap: 12px;
|
|
||||||
margin: var(--margin) 0;
|
|
||||||
|
|
||||||
> .emoji {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px;
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .img {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .body {
|
|
||||||
padding: 0 0 0 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
> .name {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .info {
|
|
||||||
opacity: 0.5;
|
|
||||||
font-size: 90%;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.emoji-draft {
|
|
||||||
--c: rgb(255 196 0 / 15%);;
|
|
||||||
background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%);
|
|
||||||
background-size: 16px 16px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue