enhance(frontend): 同じ種類のデコレーションを複数付けられるように
This commit is contained in:
parent
37820ad572
commit
839b7483ac
5 changed files with 240 additions and 96 deletions
|
@ -59,7 +59,7 @@ const props = withDefaults(defineProps<{
|
||||||
link?: boolean;
|
link?: boolean;
|
||||||
preview?: boolean;
|
preview?: boolean;
|
||||||
indicator?: boolean;
|
indicator?: boolean;
|
||||||
decorations?: Misskey.entities.UserDetailed['avatarDecorations'][number][];
|
decorations?: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>[];
|
||||||
forceShowDecoration?: boolean;
|
forceShowDecoration?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
target: null,
|
target: null,
|
||||||
|
@ -89,12 +89,12 @@ function onClick(ev: MouseEvent): void {
|
||||||
emit('click', ev);
|
emit('click', ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDecorationAngle(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
|
function getDecorationAngle(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
|
||||||
const angle = decoration.angle ?? 0;
|
const angle = decoration.angle ?? 0;
|
||||||
return angle === 0 ? undefined : `${angle * 360}deg`;
|
return angle === 0 ? undefined : `${angle * 360}deg`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDecorationScale(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
|
function getDecorationScale(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
|
||||||
const scaleX = decoration.flipH ? -1 : 1;
|
const scaleX = decoration.flipH ? -1 : 1;
|
||||||
return scaleX === 1 ? undefined : `${scaleX} 1`;
|
return scaleX === 1 ? undefined : `${scaleX} 1`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="[$style.root, { [$style.active]: active }]"
|
||||||
|
@click="emit('click')"
|
||||||
|
>
|
||||||
|
<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
|
||||||
|
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH }]" forceShowDecoration/>
|
||||||
|
<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { } from 'vue';
|
||||||
|
import { $i } from '@/account.js';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
active?: boolean;
|
||||||
|
decoration: {
|
||||||
|
id: string;
|
||||||
|
url: string;
|
||||||
|
name: string;
|
||||||
|
roleIdsThatCanBeUsedThisDecoration: string[];
|
||||||
|
};
|
||||||
|
angle?: number;
|
||||||
|
flipH?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'click'): void;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 16px 16px 28px 16px;
|
||||||
|
border: solid 2px var(--divider);
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 90%;
|
||||||
|
overflow: clip;
|
||||||
|
contain: content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: var(--accentedBg);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 12px;
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkSpacer :marginMin="20" :marginMax="28">
|
<MkSpacer :marginMin="20" :marginMax="28">
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
<div :class="$style.name">{{ decoration.name }}</div>
|
<div :class="$style.name">{{ decoration.name }}</div>
|
||||||
<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="[...$i.avatarDecorations, { url: decoration.url, angle, flipH }]" forceShowDecoration/>
|
<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="decorationsForPreview" forceShowDecoration/>
|
||||||
</div>
|
</div>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`">
|
<MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`">
|
||||||
|
@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
|
||||||
<div :class="$style.footer" class="_buttonsCenter">
|
<div :class="$style.footer" class="_buttonsCenter">
|
||||||
<MkButton v-if="using" primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
|
<MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
|
||||||
<MkButton v-if="using" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
|
<MkButton v-if="usingIndex != null" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
|
||||||
<MkButton v-else primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
|
<MkButton v-else primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,48 +51,69 @@ import MkRange from '@/components/MkRange.vue';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
usingIndex: number | null;
|
||||||
decoration: {
|
decoration: {
|
||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
};
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
|
(ev: 'attach', payload: {
|
||||||
|
angle: number;
|
||||||
|
flipH: boolean;
|
||||||
|
}): void;
|
||||||
|
(ev: 'update', payload: {
|
||||||
|
angle: number;
|
||||||
|
flipH: boolean;
|
||||||
|
}): void;
|
||||||
|
(ev: 'detach'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
const using = computed(() => $i.avatarDecorations.some(x => x.id === props.decoration.id));
|
const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0);
|
||||||
const angle = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).angle ?? 0 : 0);
|
const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false);
|
||||||
const flipH = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).flipH ?? false : false);
|
|
||||||
|
const decorationsForPreview = computed(() => {
|
||||||
|
const decoration = {
|
||||||
|
id: props.decoration.id,
|
||||||
|
url: props.decoration.url,
|
||||||
|
angle: angle.value,
|
||||||
|
flipH: flipH.value,
|
||||||
|
};
|
||||||
|
const decorations = [...$i.avatarDecorations];
|
||||||
|
if (props.usingIndex != null) {
|
||||||
|
decorations[props.usingIndex] = decoration;
|
||||||
|
} else {
|
||||||
|
decorations.push(decoration);
|
||||||
|
}
|
||||||
|
return decorations;
|
||||||
|
});
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
dialog.value.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function attach() {
|
async function update() {
|
||||||
const decoration = {
|
emit('update', {
|
||||||
id: props.decoration.id,
|
|
||||||
angle: angle.value,
|
angle: angle.value,
|
||||||
flipH: flipH.value,
|
flipH: flipH.value,
|
||||||
};
|
|
||||||
const update = [...$i.avatarDecorations, decoration];
|
|
||||||
await os.apiWithDialog('i/update', {
|
|
||||||
avatarDecorations: update,
|
|
||||||
});
|
});
|
||||||
$i.avatarDecorations = update;
|
dialog.value.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function attach() {
|
||||||
|
emit('attach', {
|
||||||
|
angle: angle.value,
|
||||||
|
flipH: flipH.value,
|
||||||
|
});
|
||||||
dialog.value.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detach() {
|
async function detach() {
|
||||||
const update = $i.avatarDecorations.filter(x => x.id !== props.decoration.id);
|
emit('detach');
|
||||||
await os.apiWithDialog('i/update', {
|
|
||||||
avatarDecorations: update,
|
|
||||||
});
|
|
||||||
$i.avatarDecorations = update;
|
|
||||||
|
|
||||||
dialog.value.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -0,0 +1,125 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="!loading" class="_gaps">
|
||||||
|
<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
|
||||||
|
|
||||||
|
<div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
|
||||||
|
<div>{{ i18n.ts.inUse }}</div>
|
||||||
|
|
||||||
|
<div :class="$style.decorations">
|
||||||
|
<XDecoration
|
||||||
|
v-for="(avatarDecoration, i) in $i.avatarDecorations"
|
||||||
|
:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
|
||||||
|
:angle="avatarDecoration.angle"
|
||||||
|
:flipH="avatarDecoration.flipH"
|
||||||
|
:active="true"
|
||||||
|
@click="openDecoration(avatarDecoration, i)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :class="$style.decorations">
|
||||||
|
<XDecoration
|
||||||
|
v-for="avatarDecoration in avatarDecorations"
|
||||||
|
:key="avatarDecoration.id"
|
||||||
|
:decoration="avatarDecoration"
|
||||||
|
@click="openDecoration(avatarDecoration)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<MkLoading/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, defineAsyncComponent } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import XDecoration from './profile.avatar-decoration.decoration.vue';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { $i } from '@/account.js';
|
||||||
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
|
||||||
|
const loading = ref(true);
|
||||||
|
const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]);
|
||||||
|
|
||||||
|
os.api('get-avatar-decorations').then(_avatarDecorations => {
|
||||||
|
avatarDecorations.value = _avatarDecorations;
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
function openDecoration(avatarDecoration, index?: number) {
|
||||||
|
os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration.dialog.vue')), {
|
||||||
|
decoration: avatarDecoration,
|
||||||
|
usingIndex: index,
|
||||||
|
}, {
|
||||||
|
'attach': async (payload) => {
|
||||||
|
const decoration = {
|
||||||
|
id: avatarDecoration.id,
|
||||||
|
angle: payload.angle,
|
||||||
|
flipH: payload.flipH,
|
||||||
|
};
|
||||||
|
const update = [...$i.avatarDecorations, decoration];
|
||||||
|
await os.apiWithDialog('i/update', {
|
||||||
|
avatarDecorations: update,
|
||||||
|
});
|
||||||
|
$i.avatarDecorations = update;
|
||||||
|
},
|
||||||
|
'update': async (payload) => {
|
||||||
|
const decoration = {
|
||||||
|
id: avatarDecoration.id,
|
||||||
|
angle: payload.angle,
|
||||||
|
flipH: payload.flipH,
|
||||||
|
};
|
||||||
|
const update = [...$i.avatarDecorations];
|
||||||
|
update[index] = decoration;
|
||||||
|
await os.apiWithDialog('i/update', {
|
||||||
|
avatarDecorations: update,
|
||||||
|
});
|
||||||
|
$i.avatarDecorations = update;
|
||||||
|
},
|
||||||
|
'detach': async () => {
|
||||||
|
const update = [...$i.avatarDecorations];
|
||||||
|
update.splice(index, 1);
|
||||||
|
await os.apiWithDialog('i/update', {
|
||||||
|
avatarDecorations: update,
|
||||||
|
});
|
||||||
|
$i.avatarDecorations = update;
|
||||||
|
},
|
||||||
|
}, 'closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function detachAllDecorations() {
|
||||||
|
os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts.areYouSure,
|
||||||
|
}).then(async ({ canceled }) => {
|
||||||
|
if (canceled) return;
|
||||||
|
await os.apiWithDialog('i/update', {
|
||||||
|
avatarDecorations: [],
|
||||||
|
});
|
||||||
|
$i.avatarDecorations = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.current {
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.decorations {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -87,24 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #icon><i class="ti ti-sparkles"></i></template>
|
<template #icon><i class="ti ti-sparkles"></i></template>
|
||||||
<template #label>{{ i18n.ts.avatarDecorations }}</template>
|
<template #label>{{ i18n.ts.avatarDecorations }}</template>
|
||||||
|
|
||||||
<div class="_gaps">
|
<XAvatarDecoration/>
|
||||||
<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
|
|
||||||
|
|
||||||
<MkButton v-if="$i.avatarDecorations.length > 0" danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
|
|
||||||
|
|
||||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 12px;">
|
|
||||||
<div
|
|
||||||
v-for="avatarDecoration in avatarDecorations"
|
|
||||||
:key="avatarDecoration.id"
|
|
||||||
:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
|
|
||||||
@click="openDecoration(avatarDecoration)"
|
|
||||||
>
|
|
||||||
<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
|
|
||||||
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: avatarDecoration.url }]" forceShowDecoration/>
|
|
||||||
<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
|
@ -128,7 +111,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, reactive, ref, watch, defineAsyncComponent, onMounted, onUnmounted } from 'vue';
|
import { computed, reactive, ref, watch, defineAsyncComponent } from 'vue';
|
||||||
|
import XAvatarDecoration from './profile.avatar-decoration.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';
|
||||||
|
@ -150,7 +134,6 @@ import MkInfo from '@/components/MkInfo.vue';
|
||||||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||||
|
|
||||||
const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
|
const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
|
||||||
const avatarDecorations = ref<any[]>([]);
|
|
||||||
|
|
||||||
const profile = reactive({
|
const profile = reactive({
|
||||||
name: $i.name,
|
name: $i.name,
|
||||||
|
@ -171,10 +154,6 @@ watch(() => profile, () => {
|
||||||
const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), 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);
|
const fieldEditMode = ref(false);
|
||||||
|
|
||||||
os.api('get-avatar-decorations').then(_avatarDecorations => {
|
|
||||||
avatarDecorations.value = _avatarDecorations;
|
|
||||||
});
|
|
||||||
|
|
||||||
function addField() {
|
function addField() {
|
||||||
fields.value.push({
|
fields.value.push({
|
||||||
id: Math.random().toString(),
|
id: Math.random().toString(),
|
||||||
|
@ -273,25 +252,6 @@ function changeBanner(ev) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDecoration(avatarDecoration) {
|
|
||||||
os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration-dialog.vue')), {
|
|
||||||
decoration: avatarDecoration,
|
|
||||||
}, {}, 'closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
function detachAllDecorations() {
|
|
||||||
os.confirm({
|
|
||||||
type: 'warning',
|
|
||||||
text: i18n.ts.areYouSure,
|
|
||||||
}).then(async ({ canceled }) => {
|
|
||||||
if (canceled) return;
|
|
||||||
await os.apiWithDialog('i/update', {
|
|
||||||
avatarDecorations: [],
|
|
||||||
});
|
|
||||||
$i.avatarDecorations = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const headerActions = computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
@ -386,33 +346,4 @@ definePageMetadata({
|
||||||
.dragItemForm {
|
.dragItemForm {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatarDecoration {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 16px 16px 28px 16px;
|
|
||||||
border: solid 2px var(--divider);
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 90%;
|
|
||||||
overflow: clip;
|
|
||||||
contain: content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatarDecorationActive {
|
|
||||||
background-color: var(--accentedBg);
|
|
||||||
border-color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatarDecorationName {
|
|
||||||
position: relative;
|
|
||||||
z-index: 10;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatarDecorationLock {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 12px;
|
|
||||||
right: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue