mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-24 17:23:50 +02:00
custom fonts
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div class="favorites-search-card__detail">
|
||||
<div class="favorites-search-card__title">
|
||||
<span class="name">{{ localFavFakeRef.name }}</span>
|
||||
<span class="name text-sm">{{ localFavFakeRef.name }}</span>
|
||||
<span class="favorites-search-card__badges">
|
||||
<TooltipWrapper
|
||||
v-if="favorite.deleted"
|
||||
@@ -23,7 +23,7 @@
|
||||
</TooltipWrapper>
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-xs">{{ localFavFakeRef.authorName }}</span>
|
||||
<span class="text-xs text-gray-600">{{ localFavFakeRef.authorName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="favorites-search-card__actions">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
:traveling="favorite.ref.travelingToLocation"
|
||||
:link="false" />
|
||||
</div>
|
||||
<span v-else class="text-xs">{{ favorite.ref.statusDescription }}</span>
|
||||
<span v-else class="text-xs text-gray-600">{{ favorite.ref.statusDescription }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="favorites-search-card__actions">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div class="favorites-search-card__detail" v-once>
|
||||
<div class="favorites-search-card__title">
|
||||
<span class="name">{{ props.favorite.ref.name }}</span>
|
||||
<span class="name text-sm">{{ props.favorite.ref.name }}</span>
|
||||
<span
|
||||
v-if="favorite.deleted || favorite.ref.releaseStatus === 'private'"
|
||||
class="favorites-search-card__badges">
|
||||
@@ -29,7 +29,7 @@
|
||||
class="h-4 w-4" />
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-xs">
|
||||
<span class="text-xs text-gray-600">
|
||||
{{ props.favorite.ref.authorName }}
|
||||
<template v-if="props.favorite.ref.occupants"> ({{ props.favorite.ref.occupants }}) </template>
|
||||
</span>
|
||||
|
||||
@@ -31,7 +31,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
|
||||
},
|
||||
{
|
||||
accessorKey: 'created_at',
|
||||
size: 100,
|
||||
size: 120,
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -65,7 +65,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
|
||||
{
|
||||
accessorKey: 'type',
|
||||
|
||||
size: 120,
|
||||
size: 160,
|
||||
header: () => t('table.friendLog.type'),
|
||||
cell: ({ row }) => {
|
||||
const type = row.getValue('type');
|
||||
|
||||
@@ -60,40 +60,29 @@
|
||||
</div>
|
||||
<ScrollArea v-if="settingsReady" ref="scrollbarRef" class="friend-view__scroll">
|
||||
<div v-if="virtualRows.length" class="friend-view__virtual" :style="virtualListStyle">
|
||||
<div class="friend-view__virtual-spacer" :style="virtualSpacerStyle">
|
||||
<div
|
||||
v-for="vRow in virtualItems"
|
||||
:key="String(virtualRows[vRow.index]?.key ?? vRow.key)"
|
||||
class="friend-view__virtual-row"
|
||||
:data-index="vRow.index"
|
||||
:ref="(el) => onVirtualRowRef(el)"
|
||||
:style="virtualRowStyle(vRow.start)">
|
||||
<template v-if="virtualRows[vRow.index]?.type === 'header'">
|
||||
<header class="friend-view__instance-header">
|
||||
<Location
|
||||
class="text-xs"
|
||||
:location="virtualRows[vRow.index].instanceId"
|
||||
style="display: inline" />
|
||||
<span class="friend-view__instance-count">{{ virtualRows[vRow.index].count }}</span>
|
||||
</header>
|
||||
</template>
|
||||
<div v-for="row in virtualRows" :key="String(row.key)" class="friend-view__virtual-row">
|
||||
<template v-if="row.type === 'header'">
|
||||
<header class="friend-view__instance-header">
|
||||
<Location class="text-xs" :location="row.instanceId" style="display: inline" />
|
||||
<span class="friend-view__instance-count">{{ row.count }}</span>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<template v-else-if="virtualRows[vRow.index]?.type === 'divider'">
|
||||
<div class="friend-view__divider"><span class="friend-view__divider-text"></span></div>
|
||||
</template>
|
||||
<template v-else-if="row.type === 'divider'">
|
||||
<div class="friend-view__divider"><span class="friend-view__divider-text"></span></div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="friend-view__row">
|
||||
<FriendLocationCard
|
||||
v-for="item in virtualRows[vRow.index]?.items ?? []"
|
||||
:key="item.key"
|
||||
:friend="item.friend"
|
||||
:card-scale="cardScale"
|
||||
:card-spacing="cardSpacing"
|
||||
:display-instance-info="item.displayInstanceInfo" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="friend-view__row">
|
||||
<FriendLocationCard
|
||||
v-for="item in row.items ?? []"
|
||||
:key="item.key"
|
||||
:friend="item.friend"
|
||||
:card-scale="cardScale"
|
||||
:card-spacing="cardSpacing"
|
||||
:display-instance-info="item.displayInstanceInfo" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="friend-view__empty">{{ t('view.friends_locations.no_matching_friends') }}</div>
|
||||
@@ -113,7 +102,6 @@
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useVirtualizer } from '@tanstack/vue-virtual';
|
||||
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover';
|
||||
import { Slider } from '../../components/ui/slider';
|
||||
@@ -598,46 +586,6 @@
|
||||
return rows;
|
||||
});
|
||||
|
||||
const estimatedRowHeight = computed(() => {
|
||||
const base = 148;
|
||||
return Math.max(64, Math.round(base * cardScale.value * cardSpacing.value));
|
||||
});
|
||||
|
||||
const virtualizerRef = useVirtualizer(
|
||||
computed(() => ({
|
||||
count: virtualRows.value.length,
|
||||
getScrollElement: () => scrollViewportRef.value,
|
||||
estimateSize: (index) => {
|
||||
const row = virtualRows.value[index];
|
||||
if (row?.type === 'header') return 34;
|
||||
if (row?.type === 'divider') return 18;
|
||||
return estimatedRowHeight.value;
|
||||
},
|
||||
overscan: 10
|
||||
}))
|
||||
);
|
||||
|
||||
const virtualizer = computed(() => virtualizerRef.value);
|
||||
const virtualItems = computed(() => virtualizer.value?.getVirtualItems?.() ?? []);
|
||||
|
||||
const virtualSpacerStyle = computed(() => {
|
||||
const height = `${virtualizer.value?.getTotalSize?.() ?? 0}px`;
|
||||
return `height:${height};position:relative;width:100%;`;
|
||||
});
|
||||
|
||||
function virtualRowStyle(start) {
|
||||
const y = Number(start) || 0;
|
||||
return `transform:translateY(${y}px);position:absolute;top:0;left:0;width:100%;`;
|
||||
}
|
||||
|
||||
function onVirtualRowRef(el) {
|
||||
const target = el?.$el ?? el;
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
virtualizer.value?.measureElement?.(/** @type {Element} */ (target));
|
||||
}
|
||||
|
||||
const virtualListStyle = computed(() => {
|
||||
const styleFn = gridStyle.value;
|
||||
const total = filteredFriends.value.length;
|
||||
@@ -650,11 +598,9 @@
|
||||
});
|
||||
|
||||
watch([searchTerm, activeSegment], () => {
|
||||
virtualizer.value?.scrollToOffset?.(0);
|
||||
nextTick(() => {
|
||||
resolveScrollViewport();
|
||||
updateGridWidth();
|
||||
virtualizer.value?.measure?.();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -666,11 +612,9 @@
|
||||
activeSegment.value = 'online';
|
||||
}
|
||||
|
||||
virtualizer.value?.scrollToOffset?.(0);
|
||||
nextTick(() => {
|
||||
resolveScrollViewport();
|
||||
updateGridWidth();
|
||||
virtualizer.value?.measure?.();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -680,7 +624,6 @@
|
||||
nextTick(() => {
|
||||
resolveScrollViewport();
|
||||
updateGridWidth();
|
||||
virtualizer.value?.measure?.();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -691,7 +634,6 @@
|
||||
}
|
||||
nextTick(() => {
|
||||
updateGridWidth();
|
||||
virtualizer.value?.measure?.();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -700,7 +642,6 @@
|
||||
resolveScrollViewport();
|
||||
setupResizeHandling();
|
||||
updateGridWidth();
|
||||
virtualizer.value?.measure?.();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -740,7 +681,6 @@
|
||||
resolveScrollViewport();
|
||||
setupResizeHandling();
|
||||
updateGridWidth();
|
||||
virtualizer.value?.measure?.();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -791,6 +731,8 @@
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
row-gap: calc(var(--friend-card-gap) - 4px);
|
||||
}
|
||||
|
||||
.friend-view__virtual-spacer {
|
||||
@@ -803,10 +745,13 @@
|
||||
}
|
||||
|
||||
.friend-view__row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(
|
||||
var(--friend-grid-columns, 1),
|
||||
minmax(var(--friend-card-min-width, 200px), var(--friend-card-target-width, 1fr))
|
||||
);
|
||||
gap: var(--friend-card-gap, 14px);
|
||||
align-items: stretch;
|
||||
justify-content: start;
|
||||
padding: 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -98,8 +98,10 @@
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
width: 100%;
|
||||
max-width: var(--friend-card-target-width, 220px);
|
||||
min-width: var(--friend-card-min-width, 220px);
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(calc(-2px * var(--card-scale)));
|
||||
@@ -118,9 +120,6 @@
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.friend-card__avatar {
|
||||
}
|
||||
|
||||
.friend-card__status-dot {
|
||||
position: absolute;
|
||||
top: calc(8px * var(--card-scale));
|
||||
|
||||
@@ -175,8 +175,6 @@
|
||||
});
|
||||
|
||||
const {
|
||||
panelGroupRef,
|
||||
asidePanelRef,
|
||||
asideDefaultSize,
|
||||
asideMaxSize,
|
||||
mainDefaultSize,
|
||||
|
||||
@@ -32,7 +32,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
|
||||
},
|
||||
{
|
||||
accessorKey: 'created',
|
||||
size: 100,
|
||||
size: 120,
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -65,7 +65,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
|
||||
},
|
||||
{
|
||||
accessorKey: 'type',
|
||||
size: 100,
|
||||
size: 140,
|
||||
header: () => t('table.moderation.type'),
|
||||
cell: ({ row }) => {
|
||||
const type = row.getValue('type');
|
||||
|
||||
@@ -32,6 +32,25 @@
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="options-container-item">
|
||||
<span class="name">{{ t('view.settings.appearance.appearance.font_family') }}</span>
|
||||
<Select :model-value="appFontFamily" @update:modelValue="setAppFontFamily">
|
||||
<SelectTrigger size="sm">
|
||||
<SelectValue
|
||||
:placeholder="t(`view.settings.appearance.appearance.font_family_${appFontFamily}`)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem
|
||||
v-for="fontKey in appFontFamilyOptions"
|
||||
:key="fontKey"
|
||||
:value="fontKey">
|
||||
{{ t(`view.settings.appearance.appearance.font_family_${fontKey}`) }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div v-if="!isLinux" class="options-container-item">
|
||||
<span class="name">{{ t('view.settings.appearance.appearance.zoom') }}</span>
|
||||
<NumberField
|
||||
@@ -431,7 +450,7 @@
|
||||
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useVrStore } from '../../../../stores';
|
||||
import { getLanguageName, languageCodes } from '../../../../localization';
|
||||
import { THEME_CONFIG } from '../../../../shared/constants';
|
||||
import { APP_FONT_FAMILIES, THEME_CONFIG } from '../../../../shared/constants';
|
||||
|
||||
import SimpleSwitch from '../SimpleSwitch.vue';
|
||||
|
||||
@@ -444,6 +463,7 @@
|
||||
appLanguage,
|
||||
themeMode,
|
||||
displayVRCPlusIconsAsAvatar,
|
||||
appFontFamily,
|
||||
hideNicknames,
|
||||
showInstanceIdInLocation,
|
||||
isAgeGatedInstancesVisible,
|
||||
@@ -492,9 +512,12 @@
|
||||
changeAppLanguage,
|
||||
promptMaxTableSizeDialog,
|
||||
setNotificationIconDot,
|
||||
setTablePageSizes
|
||||
setTablePageSizes,
|
||||
setAppFontFamily
|
||||
} = appearanceSettingsStore;
|
||||
|
||||
const appFontFamilyOptions = APP_FONT_FAMILIES;
|
||||
|
||||
const zoomLevel = ref(100);
|
||||
const isLinux = computed(() => LINUX);
|
||||
let cleanupWheel = null;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
}}</span>
|
||||
<template v-else>
|
||||
<div v-if="friend.pendingOffline" class="text-xs">
|
||||
<AlertTriangle class="inline-block" /> {{ t('side_panel.pending_offline') }}
|
||||
{{ t('side_panel.pending_offline') }}
|
||||
</div>
|
||||
<template v-else-if="isGroupByInstance">
|
||||
<div class="flex items-center">
|
||||
@@ -65,9 +65,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { AlertTriangle, Loader2, Trash2 } from 'lucide-vue-next';
|
||||
import { Loader2, Trash2 } from 'lucide-vue-next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
Reference in New Issue
Block a user