refactor css

This commit is contained in:
pa
2026-03-08 20:22:24 +09:00
parent eeb5288027
commit b9c874bed0
7 changed files with 241 additions and 70 deletions

View File

@@ -65,17 +65,26 @@
>
<Spinner v-if="setAvatarTagsDialog.loading" class="inline-block ml-2" />
<br />
<div class="x-friend-list" style="margin-top: 10px; min-height: 60px; max-height: 280px">
<div
class="flex flex-wrap items-start max-h-[300px] overflow-hidden auto"
style="margin-top: 10px; min-height: 60px">
<div
v-for="avatar in setAvatarTagsDialog.ownAvatars"
:key="avatar.id"
:class="['item-width', 'x-friend-item', 'x-friend-item-border']"
:class="[
'item-width',
'box-border flex items-center p-1.5 text-[13px] cursor-pointer border border-border rounded'
]"
@click="showAvatarDialog(avatar.id)">
<div class="avatar">
<img v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" loading="lazy" />
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
v-if="avatar.thumbnailImageUrl"
class="size-full rounded-full object-cover"
:src="avatar.thumbnailImageUrl"
loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="avatar.name"></span>
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]" v-text="avatar.name"></span>
<span
v-if="avatar.releaseStatus === 'public'"
class="block truncate text-xs"

View File

@@ -37,8 +37,7 @@
<div
v-for="image in galleryTable"
:key="image.id"
class="x-friend-item"
style="display: inline-block; margin-top: 10px; width: unset; cursor: default">
class="box-border inline-block mt-2.5 cursor-default">
<template v-if="image.versions && image.versions.length > 0">
<div
v-if="image.versions[image.versions.length - 1].file.url"

View File

@@ -46,15 +46,22 @@
:search-placeholder="t('dialog.invite.select_placeholder')"
:clearable="true">
<template #item="{ item, selected }">
<div class="x-friend-item flex w-full items-center">
<div class="flex w-full items-center p-1.5 text-[13px]">
<template v-if="item.user">
<div :class="['avatar', userStatusClass(item.user)]">
<img :src="userImage(item.user)" loading="lazy" />
<div
class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(item.user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(item.user)"
loading="lazy" />
</div>
<div class="detail">
<span class="name" :style="{ color: item.user.$userColour }">{{
item.user.displayName
}}</span>
<div class="flex-1 overflow-hidden">
<span
class="block truncate font-medium leading-[18px]"
:style="{ color: item.user.$userColour }"
>{{ item.user.displayName }}</span
>
</div>
</template>
<template v-else>
@@ -178,6 +185,10 @@
return groups;
});
/**
*
* @param value
*/
function setInviteUserIds(value) {
const next = Array.isArray(value) ? value.map((v) => String(v ?? '')).filter(Boolean) : [];
const ids = Array.isArray(props.inviteDialog.userIds) ? props.inviteDialog.userIds : [];
@@ -193,6 +204,10 @@
return map;
});
/**
*
* @param userId
*/
function resolveUserDisplayName(userId) {
if (currentUser.value?.id && currentUser.value.id === userId) {
return currentUser.value.displayName;
@@ -201,10 +216,18 @@
return friend?.ref?.displayName ?? friend?.name ?? String(userId);
}
/**
*
*/
function closeInviteDialog() {
emit('closeInviteDialog');
}
/**
*
* @param params
* @param userId
*/
function showSendInviteDialog(params, userId) {
sendInviteDialog.value = {
params,
@@ -216,6 +239,9 @@
sendInviteDialogVisible.value = true;
}
/**
*
*/
function addSelfToInvite() {
const D = props.inviteDialog;
if (!D.userIds.includes(currentUser.value.id)) {
@@ -223,6 +249,9 @@
}
}
/**
*
*/
function addFriendsInInstanceToInvite() {
const D = props.inviteDialog;
for (const friend of D.friendsInInstance) {
@@ -232,6 +261,9 @@
}
}
/**
*
*/
function addFavoriteFriendsToInvite() {
const D = props.inviteDialog;
for (const friend of vipFriends.value) {
@@ -241,6 +273,9 @@
}
}
/**
*
*/
function sendInvite() {
modalStore
.confirm({

View File

@@ -20,12 +20,15 @@
:close-on-select="true"
:deselect-on-reselect="true">
<template #item="{ item, selected }">
<div class="x-friend-item flex w-full items-center">
<div class="avatar">
<img :src="item.iconUrl" loading="lazy" />
<div class="flex w-full items-center p-1.5 text-[13px]">
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
class="size-full rounded-full object-cover"
:src="item.iconUrl"
loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="item.label"></span>
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]" v-text="item.label"></span>
</div>
<CheckIcon :class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
</div>
@@ -43,14 +46,19 @@
:search-placeholder="t('dialog.invite_to_group.choose_friends_placeholder')"
:clearable="true">
<template #item="{ item, selected }">
<div class="x-friend-item flex w-full items-center">
<div class="flex w-full items-center p-1.5 text-[13px]">
<template v-if="item.user">
<div class="avatar" :class="userStatusClass(item.user)">
<img :src="userImage(item.user)" loading="lazy" />
<div
class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(item.user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(item.user)"
loading="lazy" />
</div>
<div class="detail">
<div class="flex-1 overflow-hidden">
<span
class="name"
class="block truncate font-medium leading-[18px]"
:style="{ color: item.user.$userColour }"
v-text="item.user.displayName"></span>
</div>
@@ -141,6 +149,10 @@
return map;
});
/**
*
* @param userId
*/
function resolveUserDisplayName(userId) {
const D = inviteGroupDialog.value;
if (D?.userObject?.id && D.userObject.id === userId) {
@@ -224,6 +236,9 @@
}
);
/**
*
*/
function initDialog() {
const D = inviteGroupDialog.value;
if (D.groupId) {
@@ -247,6 +262,9 @@
});
}
}
/**
*
*/
function isAllowedToInviteToGroup() {
const D = inviteGroupDialog.value;
const groupId = D.groupId;
@@ -270,6 +288,9 @@
inviteGroupDialog.value.loading = false;
});
}
/**
*
*/
function sendGroupInvite() {
modalStore
.confirm({

View File

@@ -6,14 +6,17 @@
</DialogHeader>
<div v-if="moderateGroupDialog.visible">
<div class="x-friend-item" style="cursor: default">
<div class="avatar">
<img :src="userImage(moderateGroupDialog.userObject)" loading="lazy" />
<div class="box-border flex items-center p-1.5 text-[13px] cursor-default">
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
class="size-full rounded-full object-cover"
:src="userImage(moderateGroupDialog.userObject)"
loading="lazy" />
</div>
<div class="detail">
<div class="flex-1 overflow-hidden">
<span
v-if="moderateGroupDialog.userObject.id"
class="name"
class="block truncate font-medium leading-[18px]"
:style="{ color: moderateGroupDialog.userObject.$userColour }"
v-text="moderateGroupDialog.userObject.displayName"></span>
<span v-else v-text="moderateGroupDialog.userId"></span>
@@ -86,6 +89,10 @@
}
]);
/**
*
* @param value
*/
function setGroupId(value) {
moderateGroupDialog.value.groupId = String(value ?? '');
}
@@ -101,6 +108,9 @@
}
);
/**
*
*/
function initDialog() {
const D = moderateGroupDialog.value;
if (D.groupId) {

View File

@@ -170,12 +170,17 @@
:deselect-on-reselect="true"
@change="buildInstance">
<template #item="{ item, selected }">
<div class="x-friend-item flex w-full items-center">
<div class="avatar">
<img :src="item.iconUrl" loading="lazy" />
<div class="flex w-full items-center p-1.5 text-[13px]">
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
class="size-full rounded-full object-cover"
:src="item.iconUrl"
loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="item.label"></span>
<div class="flex-1 overflow-hidden">
<span
class="block truncate font-medium leading-[18px]"
v-text="item.label"></span>
</div>
<CheckIcon
:class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
@@ -378,14 +383,19 @@
:deselect-on-reselect="true"
@change="buildLegacyInstance">
<template #item="{ item, selected }">
<div class="x-friend-item flex w-full items-center">
<div class="flex w-full items-center p-1.5 text-[13px]">
<template v-if="item.user">
<div class="avatar" :class="userStatusClass(item.user)">
<img :src="userImage(item.user)" loading="lazy" />
<div
class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(item.user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(item.user)"
loading="lazy" />
</div>
<div class="detail">
<div class="flex-1 overflow-hidden">
<span
class="name"
class="block truncate font-medium leading-[18px]"
:style="{ color: item.user.$userColour }"
v-text="item.user.displayName"></span>
</div>
@@ -414,12 +424,17 @@
:deselect-on-reselect="true"
@change="buildLegacyInstance">
<template #item="{ item, selected }">
<div class="x-friend-item flex w-full items-center">
<div class="avatar">
<img :src="item.iconUrl" loading="lazy" />
<div class="flex w-full items-center p-1.5 text-[13px]">
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
class="size-full rounded-full object-cover"
:src="item.iconUrl"
loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="item.label"></span>
<div class="flex-1 overflow-hidden">
<span
class="block truncate font-medium leading-[18px]"
v-text="item.label"></span>
</div>
<CheckIcon
:class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />

View File

@@ -30,17 +30,22 @@
<Checkbox v-model="searchUserSortByLastLoggedIn" />
<span>{{ t('view.search.user.sort_by_last_logged_in') }}</span>
</label>
<div class="x-friend-list" style="min-height: 500px">
<div style="min-height: 500px">
<div
v-for="user in searchUserResults"
:key="user.id"
class="x-friend-item"
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer hover:bg-muted/50 hover:rounded-lg"
@click="showUserDialog(user.id)">
<div class="avatar">
<img :src="userImage(user, true)" loading="lazy" />
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
class="size-full rounded-full object-cover"
:src="userImage(user, true)"
loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="user.displayName"></span>
<div class="flex-1 overflow-hidden">
<span
class="block truncate font-medium leading-[18px]"
v-text="user.displayName"></span>
<span
v-if="randomUserColours"
class="block truncate text-xs"
@@ -100,17 +105,20 @@
<span>{{ t('view.search.world.community_lab') }}</span>
</label>
</div>
<div class="x-friend-list" style="min-height: 500px">
<div style="min-height: 500px">
<div
v-for="world in searchWorldResults"
:key="world.id"
class="x-friend-item"
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer hover:bg-muted/50 hover:rounded-lg"
@click="showWorldDialog(world.id)">
<div class="avatar">
<img :src="world.thumbnailImageUrl" loading="lazy" />
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
class="size-full rounded-full object-cover"
:src="world.thumbnailImageUrl"
loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="world.name"></span>
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]" v-text="world.name"></span>
<span v-if="world.occupants" class="block truncate text-xs"
>{{ world.authorName }} ({{ world.occupants }})</span
>
@@ -251,18 +259,26 @@
</SelectContent>
</Select>
</div>
<div class="x-friend-list" style="margin-top: 20px; min-height: 500px">
<div style="margin-top: 20px; min-height: 500px">
<div
v-for="avatar in searchAvatarPage"
:key="avatar.id"
class="x-friend-item"
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer hover:bg-muted/50 hover:rounded-lg"
@click="showAvatarDialog(avatar.id)">
<div class="avatar">
<img v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" loading="lazy" />
<img v-else-if="avatar.imageUrl" :src="avatar.imageUrl" loading="lazy" />
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
v-if="avatar.thumbnailImageUrl"
class="size-full rounded-full object-cover"
:src="avatar.thumbnailImageUrl"
loading="lazy" />
<img
v-else-if="avatar.imageUrl"
class="size-full rounded-full object-cover"
:src="avatar.imageUrl"
loading="lazy" />
</div>
<div class="detail">
<span class="name" v-text="avatar.name"></span>
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]" v-text="avatar.name"></span>
<span
v-if="avatar.releaseStatus === 'public'"
class="block truncate text-xs"
@@ -301,17 +317,20 @@
</template>
<template #group>
<div style="min-height: 60px">
<div class="x-friend-list" style="min-height: 500px">
<div style="min-height: 500px">
<div
v-for="group in searchGroupResults"
:key="group.id"
class="x-friend-item"
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer hover:bg-muted/50 hover:rounded-lg"
@click="showGroupDialog(group.id)">
<div class="avatar">
<img :src="getSmallThumbnailUrl(group.iconUrl)" loading="lazy" />
<div class="relative inline-block flex-none size-9 mr-2.5">
<img
class="size-full rounded-full object-cover"
:src="getSmallThumbnailUrl(group.iconUrl)"
loading="lazy" />
</div>
<div class="detail">
<span class="name">
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">
<span v-text="group.name"></span>
<span style="margin-left: 5px; font-weight: normal">({{ group.memberCount }})</span>
<span
@@ -428,16 +447,28 @@
const searchWorldCategoryIndex = ref(null);
const searchWorldResults = ref([]);
/**
*
* @param value
*/
function handleSearchAvatarFilterChange(value) {
searchAvatarFilter.value = value;
searchAvatar();
}
/**
*
* @param value
*/
function handleSearchAvatarFilterRemoteChange(value) {
searchAvatarFilterRemote.value = value;
searchAvatar();
}
/**
*
* @param value
*/
function handleSearchAvatarSortChange(value) {
searchAvatarSort.value = value;
searchAvatar();
@@ -453,10 +484,17 @@
const searchGroupParams = ref({});
const searchGroupResults = ref([]);
/**
*
* @param url
*/
function getSmallThumbnailUrl(url) {
return convertFileUrlToImageUrl(url);
}
/**
*
*/
function handleClearSearch() {
searchUserParams.value = {};
searchWorldParams.value = {};
@@ -469,15 +507,26 @@
clearSearch();
}
/**
*
* @param text
*/
function updateSearchText(text) {
searchText.value = text;
}
/**
*
* @param tabName
*/
function handleSearchTabChange(tabName) {
searchText.value = '';
activeSearchTab.value = tabName;
}
/**
*
*/
function search() {
switch (activeSearchTab.value) {
case 'user':
@@ -495,6 +544,9 @@
}
}
/**
*
*/
async function searchUser() {
searchUserParams.value = {
n: 10,
@@ -506,12 +558,20 @@
await handleMoreSearchUser();
}
/**
*
* @param go
*/
async function handleMoreSearchUser(go = null) {
isSearchUserLoading.value = true;
await moreSearchUser(go, searchUserParams.value);
isSearchUserLoading.value = false;
}
/**
*
* @param ref
*/
function searchWorld(ref) {
searchWorldOption.value = '';
searchWorldCategoryIndex.value = ref?.index ?? null;
@@ -581,12 +641,20 @@
moreSearchWorld();
}
/**
*
* @param index
*/
function handleSearchWorldCategorySelect(index) {
searchWorldCategoryIndex.value = index;
const row = cachedConfig.value?.dynamicWorldRows?.find((r) => r.index === index);
searchWorld(row || {});
}
/**
*
* @param go
*/
function moreSearchWorld(go) {
const params = searchWorldParams.value;
if (go) {
@@ -614,6 +682,9 @@
});
}
/**
*
*/
async function searchAvatar() {
let ref;
isSearchAvatarLoading.value = true;
@@ -712,6 +783,10 @@
searchAvatarResults.value = avatarsArray;
searchAvatarPage.value = avatarsArray.slice(0, 10);
}
/**
*
* @param n
*/
function moreSearchAvatar(n) {
let offset;
if (n === -1) {
@@ -724,6 +799,9 @@
}
searchAvatarPage.value = searchAvatarResults.value.slice(offset, offset + 10);
}
/**
*
*/
async function searchGroup() {
searchGroupParams.value = {
n: 10,
@@ -732,6 +810,10 @@
};
await moreSearchGroup();
}
/**
*
* @param go
*/
async function moreSearchGroup(go) {
const params = searchGroupParams.value;
if (go) {