replace dropdown

This commit is contained in:
pa
2026-01-08 20:19:37 +09:00
committed by Natsumi
parent 914fea6ccf
commit 3d37cebefc
7 changed files with 237 additions and 197 deletions

View File

@@ -46,7 +46,7 @@
v-for="entry in item.children"
:key="entry.index"
:index="entry.index"
class="pl-8!"
class="pl-9!"
:class="{ notify: isEntryNotified(entry) }"
@click="handleSubmenuClick(entry, item.index)">
<i v-show="entry.icon" :class="entry.icon"></i>
@@ -254,7 +254,6 @@
import { useRouter } from 'vue-router';
import {
useAdvancedSettingsStore,
useAppearanceSettingsStore,
useAuthStore,
useSearchStore,
@@ -262,7 +261,6 @@
useVRCXUpdaterStore
} from '../stores';
import { THEME_CONFIG, links, navDefinitions } from '../shared/constants';
import { getSentry } from '../plugin';
import { openExternalLink } from '../shared/utils';
import { useThemePrimaryColor } from '../composables/useElementTheme';

View File

@@ -15,10 +15,12 @@
variant="outline"
size="sm"
:model-value="newInstanceDialog.accessType"
@update:model-value="(value) => {
newInstanceDialog.accessType = value;
buildInstance();
}">
@update:model-value="
(value) => {
newInstanceDialog.accessType = value;
buildInstance();
}
">
<ToggleGroupItem value="public">{{
t('dialog.new_instance.access_type_public')
}}</ToggleGroupItem>
@@ -48,18 +50,24 @@
variant="outline"
size="sm"
:model-value="newInstanceDialog.groupAccessType"
@update:model-value="(value) => {
newInstanceDialog.groupAccessType = value;
buildInstance();
}">
@update:model-value="
(value) => {
newInstanceDialog.groupAccessType = value;
buildInstance();
}
">
<ToggleGroupItem
value="members"
:disabled="!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-open-create')"
:disabled="
!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-open-create')
"
>{{ t('dialog.new_instance.group_access_type_members') }}</ToggleGroupItem
>
<ToggleGroupItem
value="plus"
:disabled="!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-plus-create')"
:disabled="
!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-plus-create')
"
>{{ t('dialog.new_instance.group_access_type_plus') }}</ToggleGroupItem
>
<ToggleGroupItem
@@ -79,10 +87,12 @@
variant="outline"
size="sm"
:model-value="newInstanceDialog.region"
@update:model-value="(value) => {
newInstanceDialog.region = value;
buildInstance();
}">
@update:model-value="
(value) => {
newInstanceDialog.region = value;
buildInstance();
}
">
<ToggleGroupItem value="US West">{{ t('dialog.new_instance.region_usw') }}</ToggleGroupItem>
<ToggleGroupItem value="US East">{{ t('dialog.new_instance.region_use') }}</ToggleGroupItem>
<ToggleGroupItem value="Europe">{{ t('dialog.new_instance.region_eu') }}</ToggleGroupItem>
@@ -198,10 +208,12 @@
variant="outline"
size="sm"
:model-value="newInstanceDialog.accessType"
@update:model-value="(value) => {
newInstanceDialog.accessType = value;
buildLegacyInstance();
}">
@update:model-value="
(value) => {
newInstanceDialog.accessType = value;
buildLegacyInstance();
}
">
<ToggleGroupItem value="public">{{
t('dialog.new_instance.access_type_public')
}}</ToggleGroupItem>
@@ -231,10 +243,12 @@
variant="outline"
size="sm"
:model-value="newInstanceDialog.groupAccessType"
@update:model-value="(value) => {
newInstanceDialog.groupAccessType = value;
buildLegacyInstance();
}">
@update:model-value="
(value) => {
newInstanceDialog.groupAccessType = value;
buildLegacyInstance();
}
">
<ToggleGroupItem value="members">{{
t('dialog.new_instance.group_access_type_members')
}}</ToggleGroupItem>
@@ -253,10 +267,12 @@
variant="outline"
size="sm"
:model-value="newInstanceDialog.region"
@update:model-value="(value) => {
newInstanceDialog.region = value;
buildLegacyInstance();
}">
@update:model-value="
(value) => {
newInstanceDialog.region = value;
buildLegacyInstance();
}
">
<ToggleGroupItem value="US West">{{ t('dialog.new_instance.region_usw') }}</ToggleGroupItem>
<ToggleGroupItem value="US East">{{ t('dialog.new_instance.region_use') }}</ToggleGroupItem>
<ToggleGroupItem value="Europe">{{ t('dialog.new_instance.region_eu') }}</ToggleGroupItem>
@@ -524,8 +540,6 @@
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import { ToggleGroup, ToggleGroupItem } from '../ui/toggle-group';
import {
copyToClipboard,
getLaunchURL,
@@ -545,6 +559,7 @@
useUserStore
} from '../../stores';
import { groupRequest, instanceRequest, worldRequest } from '../../api';
import { ToggleGroup, ToggleGroupItem } from '../ui/toggle-group';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
import InviteDialog from './InviteDialog/InviteDialog.vue';

View File

@@ -17,45 +17,44 @@
class="favorites-toolbar__search"
:placeholder="t('view.favorite.avatars.search')"
@input="searchAvatarFavorites" />
<el-dropdown ref="avatarToolbarMenuRef" trigger="click" :hide-on-click="false">
<el-button :icon="MoreFilled" size="small" circle />
<template #dropdown>
<el-dropdown-menu class="favorites-dropdown">
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Scale</span>
<span class="favorites-dropdown__control-value">{{ avatarCardScalePercent }}%</span>
</div>
<Slider
v-model="avatarCardScaleValue"
class="favorites-dropdown__slider"
:min="avatarCardScaleSlider.min"
:max="avatarCardScaleSlider.max"
:step="avatarCardScaleSlider.step" />
</li>
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Spacing</span>
<span class="favorites-dropdown__control-value">
{{ avatarCardSpacingPercent }}%
</span>
</div>
<Slider
v-model="avatarCardSpacingValue"
class="favorites-dropdown__slider"
:min="avatarCardSpacingSlider.min"
:max="avatarCardSpacingSlider.max"
:step="avatarCardSpacingSlider.step" />
</li>
<el-dropdown-item @click="handleAvatarImportClick">
{{ t('view.favorite.import') }}
</el-dropdown-item>
<el-dropdown-item divided @click="handleAvatarExportClick">
{{ t('view.favorite.export') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<DropdownMenu v-model:open="avatarToolbarMenuOpen">
<DropdownMenuTrigger as-child>
<el-button :icon="MoreFilled" size="small" circle />
</DropdownMenuTrigger>
<DropdownMenuContent class="favorites-dropdown">
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Scale</span>
<span class="favorites-dropdown__control-value">{{ avatarCardScalePercent }}%</span>
</div>
<Slider
v-model="avatarCardScaleValue"
class="favorites-dropdown__slider"
:min="avatarCardScaleSlider.min"
:max="avatarCardScaleSlider.max"
:step="avatarCardScaleSlider.step" />
</li>
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Spacing</span>
<span class="favorites-dropdown__control-value"> {{ avatarCardSpacingPercent }}% </span>
</div>
<Slider
v-model="avatarCardSpacingValue"
class="favorites-dropdown__slider"
:min="avatarCardSpacingSlider.min"
:max="avatarCardSpacingSlider.max"
:step="avatarCardSpacingSlider.step" />
</li>
<DropdownMenuSeparator />
<DropdownMenuItem @click="handleAvatarImportClick">
{{ t('view.favorite.import') }}
</DropdownMenuItem>
<DropdownMenuItem @click="handleAvatarExportClick">
{{ t('view.favorite.export') }}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<el-splitter class="favorites-splitter" @resize-end="handleAvatarSplitterResize">
@@ -487,6 +486,13 @@
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '../../components/ui/dropdown-menu';
import { useAppearanceSettingsStore, useAvatarStore, useFavoriteStore, useUserStore } from '../../stores';
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover';
import { avatarRequest, favoriteRequest } from '../../api';
@@ -597,7 +603,7 @@
const avatarEditMode = ref(false);
const selectedGroup = ref(null);
const activeGroupMenu = ref(null);
const avatarToolbarMenuRef = ref();
const avatarToolbarMenuOpen = ref(false);
const isCreatingLocalGroup = ref(false);
const newLocalGroupName = ref('');
const newLocalGroupInput = ref(null);
@@ -632,7 +638,7 @@
const historyGroupMenuKey = 'history';
const closeAvatarToolbarMenu = () => {
avatarToolbarMenuRef.value?.handleClose?.();
avatarToolbarMenuOpen.value = false;
};
function handleAvatarImportClick() {

View File

@@ -17,47 +17,44 @@
class="favorites-toolbar__search"
:placeholder="t('view.favorite.worlds.search')"
@input="searchFriendFavorites" />
<el-dropdown ref="friendToolbarMenuRef" trigger="click" :hide-on-click="false">
<el-button :icon="MoreFilled" size="small" circle @click.stop />
<template #dropdown>
<el-dropdown-menu class="favorites-dropdown">
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Scale</span>
<span class="favorites-dropdown__control-value">
{{ friendCardScalePercent }}%
</span>
</div>
<Slider
v-model="friendCardScaleValue"
class="favorites-dropdown__slider"
:min="friendCardScaleSlider.min"
:max="friendCardScaleSlider.max"
:step="friendCardScaleSlider.step" />
</li>
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Spacing</span>
<span class="favorites-dropdown__control-value">
{{ friendCardSpacingPercent }}%
</span>
</div>
<Slider
v-model="friendCardSpacingValue"
class="favorites-dropdown__slider"
:min="friendCardSpacingSlider.min"
:max="friendCardSpacingSlider.max"
:step="friendCardSpacingSlider.step" />
</li>
<el-dropdown-item @click="handleFriendImportClick">
{{ t('view.favorite.import') }}
</el-dropdown-item>
<el-dropdown-item divided @click="handleFriendExportClick">
{{ t('view.favorite.export') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<DropdownMenu v-model:open="friendToolbarMenuOpen">
<DropdownMenuTrigger as-child>
<el-button :icon="MoreFilled" size="small" circle />
</DropdownMenuTrigger>
<DropdownMenuContent class="favorites-dropdown">
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Scale</span>
<span class="favorites-dropdown__control-value"> {{ friendCardScalePercent }}% </span>
</div>
<Slider
v-model="friendCardScaleValue"
class="favorites-dropdown__slider"
:min="friendCardScaleSlider.min"
:max="friendCardScaleSlider.max"
:step="friendCardScaleSlider.step" />
</li>
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Spacing</span>
<span class="favorites-dropdown__control-value"> {{ friendCardSpacingPercent }}% </span>
</div>
<Slider
v-model="friendCardSpacingValue"
class="favorites-dropdown__slider"
:min="friendCardSpacingSlider.min"
:max="friendCardSpacingSlider.max"
:step="friendCardSpacingSlider.step" />
</li>
<DropdownMenuSeparator />
<DropdownMenuItem @click="handleFriendImportClick">
{{ t('view.favorite.import') }}
</DropdownMenuItem>
<DropdownMenuItem @click="handleFriendExportClick">
{{ t('view.favorite.export') }}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<el-splitter class="favorites-splitter" @resize-end="handleFriendSplitterResize">
@@ -279,6 +276,13 @@
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '../../components/ui/dropdown-menu';
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover';
import { useAppearanceSettingsStore, useFavoriteStore, useUserStore } from '../../stores';
import { Badge } from '../../components/ui/badge';
@@ -363,7 +367,7 @@
const friendEditMode = ref(false);
const selectedGroup = ref(null);
const activeGroupMenu = ref(null);
const friendToolbarMenuRef = ref();
const friendToolbarMenuOpen = ref(false);
const sortFav = computed({
get() {
@@ -380,7 +384,7 @@
const isRemoteGroupSelected = computed(() => selectedGroup.value?.type === 'remote');
const closeFriendToolbarMenu = () => {
friendToolbarMenuRef.value?.handleClose?.();
friendToolbarMenuOpen.value = false;
};
function handleFriendImportClick() {

View File

@@ -17,47 +17,44 @@
class="favorites-toolbar__search"
:placeholder="t('view.favorite.worlds.search')"
@input="searchWorldFavorites" />
<el-dropdown ref="worldToolbarMenuRef" trigger="click" :hide-on-click="false">
<el-button :icon="MoreFilled" size="small" circle />
<template #dropdown>
<el-dropdown-menu class="favorites-dropdown">
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Scale</span>
<span class="favorites-dropdown__control-value">
{{ worldCardScalePercent }}%
</span>
</div>
<Slider
v-model="worldCardScaleValue"
class="favorites-dropdown__slider"
:min="worldCardScaleSlider.min"
:max="worldCardScaleSlider.max"
:step="worldCardScaleSlider.step" />
</li>
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Spacing</span>
<span class="favorites-dropdown__control-value">
{{ worldCardSpacingPercent }}%
</span>
</div>
<Slider
v-model="worldCardSpacingValue"
class="favorites-dropdown__slider"
:min="worldCardSpacingSlider.min"
:max="worldCardSpacingSlider.max"
:step="worldCardSpacingSlider.step" />
</li>
<el-dropdown-item @click="handleWorldImportClick">
{{ t('view.favorite.import') }}
</el-dropdown-item>
<el-dropdown-item divided @click="handleWorldExportClick">
{{ t('view.favorite.export') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<DropdownMenu v-model:open="worldToolbarMenuOpen">
<DropdownMenuTrigger as-child>
<el-button :icon="MoreFilled" size="small" circle />
</DropdownMenuTrigger>
<DropdownMenuContent class="favorites-dropdown">
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Scale</span>
<span class="favorites-dropdown__control-value"> {{ worldCardScalePercent }}% </span>
</div>
<Slider
v-model="worldCardScaleValue"
class="favorites-dropdown__slider"
:min="worldCardScaleSlider.min"
:max="worldCardScaleSlider.max"
:step="worldCardScaleSlider.step" />
</li>
<li class="favorites-dropdown__control" @click.stop>
<div class="favorites-dropdown__control-header">
<span>Spacing</span>
<span class="favorites-dropdown__control-value"> {{ worldCardSpacingPercent }}% </span>
</div>
<Slider
v-model="worldCardSpacingValue"
class="favorites-dropdown__slider"
:min="worldCardSpacingSlider.min"
:max="worldCardSpacingSlider.max"
:step="worldCardSpacingSlider.step" />
</li>
<DropdownMenuSeparator />
<DropdownMenuItem @click="handleWorldImportClick">
{{ t('view.favorite.import') }}
</DropdownMenuItem>
<DropdownMenuItem @click="handleWorldExportClick">
{{ t('view.favorite.export') }}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<el-splitter class="favorites-splitter" @resize-end="handleWorldSplitterResize">
@@ -406,6 +403,13 @@
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '../../components/ui/dropdown-menu';
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover';
import { useAppearanceSettingsStore, useFavoriteStore, useWorldStore } from '../../stores';
import { favoriteRequest, worldRequest } from '../../api';
@@ -522,7 +526,7 @@
const worldEditMode = ref(false);
const activeGroupMenu = ref(null);
const localFavoritesScrollbarRef = ref(null);
const worldToolbarMenuRef = ref();
const worldToolbarMenuOpen = ref(false);
const localFavoritesLoadingMore = ref(false);
const hasWorldSelection = computed(() => selectedFavoriteWorlds.value.length > 0);
const hasSearchInput = computed(() => worldFavoriteSearch.value.trim().length > 0);
@@ -533,7 +537,7 @@
const localGroupMenuKey = (key) => `local:${key}`;
const closeWorldToolbarMenu = () => {
worldToolbarMenuRef.value?.handleClose?.();
worldToolbarMenuOpen.value = false;
};
function handleWorldImportClick() {

View File

@@ -1,40 +1,46 @@
<template>
<el-dropdown trigger="hover" size="small" style="margin-left: 5px" :persistent="false">
<div>
<DropdownMenu v-model:open="moveDropdownOpen" style="margin-left: 5px">
<DropdownMenuTrigger as-child>
<el-button type="default" :icon="Back" size="small" circle></el-button>
</div>
<template #dropdown>
</DropdownMenuTrigger>
<DropdownMenuContent class="favorites-dropdown">
<span style="font-weight: bold; display: block; text-align: center">
{{ t(tooltipContent) }}
</span>
<el-dropdown-menu>
<template v-for="groupAPI in favoriteGroup" :key="groupAPI.name">
<el-dropdown-item
v-if="isLocalFavorite || groupAPI.name !== currentGroup?.name"
style="display: block; margin: 10px 0"
:disabled="groupAPI.count >= groupAPI.capacity"
@click="handleDropdownItemClick(groupAPI)">
{{ groupAPI.displayName }} ({{ groupAPI.count }} / {{ groupAPI.capacity }})
</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
<DropdownMenuSeparator />
<DropdownMenuItem
v-for="groupAPI in favoriteGroupList"
:key="groupAPI.name"
v-if="isLocalFavorite || groupAPI?.name !== currentGroup?.name"
style="display: block; margin: 10px 0"
:disabled="groupAPI.count >= groupAPI.capacity"
@click="handleDropdownItemClick(groupAPI)">
{{ groupAPI.displayName }} ({{ groupAPI.count }} / {{ groupAPI.capacity }})
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</template>
<script setup>
import { computed, ref } from 'vue';
import { Back } from '@element-plus/icons-vue';
import { computed } from 'vue';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '../../../components/ui/dropdown-menu';
import { favoriteRequest } from '../../../api';
const { t } = useI18n();
const props = defineProps({
favoriteGroup: {
type: Object,
type: [Array, Object],
required: true
},
currentGroup: {
@@ -58,8 +64,15 @@
const tooltipContent = computed(() =>
props.isLocalFavorite ? t('view.favorite.copy_tooltip') : t('view.favorite.move_tooltip')
);
const favoriteGroupList = computed(() => {
const rawGroup = props.favoriteGroup;
const source = Array.isArray(rawGroup) ? rawGroup : Array.isArray(rawGroup?.value) ? rawGroup.value : [];
return source.filter((entry) => entry && typeof entry === 'object' && typeof entry.name === 'string');
});
const moveDropdownOpen = ref(false);
function handleDropdownItemClick(groupAPI) {
moveDropdownOpen.value = false;
if (props.isLocalFavorite) {
if (props.type === 'world') {
addFavoriteWorld(groupAPI);

View File

@@ -73,26 +73,26 @@
</div>
<div class="options-container-item">
<span class="name">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on') }}</span>
<ToggleGroup
type="single"
required
variant="outline"
size="sm"
:model-value="overlayHand"
@update:model-value="
setOverlayHand($event);
saveOpenVROption();
">
<ToggleGroupItem value="1">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_left')
}}</ToggleGroupItem>
<ToggleGroupItem value="2">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_right')
}}</ToggleGroupItem>
<ToggleGroupItem value="0">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_both')
}}</ToggleGroupItem>
</ToggleGroup>
<ToggleGroup
type="single"
required
variant="outline"
size="sm"
:model-value="overlayHand"
@update:model-value="
setOverlayHand($event);
saveOpenVROption();
">
<ToggleGroupItem value="1">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_left')
}}</ToggleGroupItem>
<ToggleGroupItem value="2">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_right')
}}</ToggleGroupItem>
<ToggleGroupItem value="0">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_both')
}}</ToggleGroupItem>
</ToggleGroup>
</div>
<simple-switch
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.grey_background')"