replace el-select

This commit is contained in:
pa
2026-01-13 13:41:11 +09:00
committed by Natsumi
parent 4fbae859f4
commit 9e693e0e97
7 changed files with 290 additions and 151 deletions
+31 -13
View File
@@ -14,19 +14,24 @@
<Switch v-model="feedTable.vip" @update:modelValue="feedTableLookup" /> <Switch v-model="feedTable.vip" @update:modelValue="feedTableLookup" />
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<el-select <Select
v-model="feedTable.filter"
multiple multiple
clearable :model-value="Array.isArray(feedTable.filter) ? feedTable.filter : []"
style="flex: 1" @update:modelValue="handleFeedFilterChange">
:placeholder="t('view.feed.filter_placeholder')" <SelectTrigger class="w-full" style="flex: 1">
@change="feedTableLookup"> <SelectValue :placeholder="t('view.feed.filter_placeholder')" />
<el-option </SelectTrigger>
v-for="type in ['GPS', 'Online', 'Offline', 'Status', 'Avatar', 'Bio']" <SelectContent>
:key="type" <SelectGroup>
:label="t('view.feed.filters.' + type)" <SelectItem
:value="type"></el-option> v-for="type in ['GPS', 'Online', 'Offline', 'Status', 'Avatar', 'Bio']"
</el-select> :key="type"
:value="type">
{{ t('view.feed.filters.' + type) }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<InputGroupField <InputGroupField
v-model="feedTable.search" v-model="feedTable.search"
:placeholder="t('view.feed.search_placeholder')" :placeholder="t('view.feed.search_placeholder')"
@@ -45,9 +50,17 @@
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue
} from '../../components/ui/select';
import { useAppearanceSettingsStore, useFeedStore, useVrcxStore } from '../../stores'; import { useAppearanceSettingsStore, useFeedStore, useVrcxStore } from '../../stores';
import { InputGroupField } from '../../components/ui/input-group';
import { DataTableLayout } from '../../components/ui/data-table'; import { DataTableLayout } from '../../components/ui/data-table';
import { InputGroupField } from '../../components/ui/input-group';
import { Switch } from '../../components/ui/switch'; import { Switch } from '../../components/ui/switch';
import { columns as baseColumns } from './columns.jsx'; import { columns as baseColumns } from './columns.jsx';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight'; import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
@@ -105,6 +118,11 @@
} }
}; };
function handleFeedFilterChange(value) {
feedTable.value.filter = Array.isArray(value) ? value : [];
feedTableLookup();
}
watch(pageSize, (size) => { watch(pageSize, (size) => {
if (pagination.value.pageSize === size) { if (pagination.value.pageSize === size) {
return; return;
+32 -14
View File
@@ -6,20 +6,32 @@
<TooltipWrapper side="bottom" :content="t('view.friend_list.favorites_only_tooltip')"> <TooltipWrapper side="bottom" :content="t('view.friend_list.favorites_only_tooltip')">
<Switch v-model="friendsListSearchFilterVIP" @update:modelValue="friendsListSearchChange" /> <Switch v-model="friendsListSearchFilterVIP" @update:modelValue="friendsListSearchChange" />
</TooltipWrapper> </TooltipWrapper>
<el-select <Select
v-model="friendsListSearchFilters"
multiple multiple
clearable :model-value="Array.isArray(friendsListSearchFilters) ? friendsListSearchFilters : []"
collapse-tags @update:modelValue="handleFriendListFilterChange">
style="margin: 0 10px; width: 150px" <SelectTrigger style="margin: 0 10px; width: 150px">
:placeholder="t('view.friend_list.filter_placeholder')" <SelectValue :placeholder="t('view.friend_list.filter_placeholder')" />
@change="friendsListSearchChange"> </SelectTrigger>
<el-option <SelectContent>
v-for="type in ['Display Name', 'User Name', 'Rank', 'Status', 'Bio', 'Note', 'Memo']" <SelectGroup>
:key="type" <SelectItem
:label="type" v-for="type in [
:value="type"></el-option> 'Display Name',
</el-select> 'User Name',
'Rank',
'Status',
'Bio',
'Note',
'Memo'
]"
:key="type"
:value="type">
{{ type }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<InputGroupField <InputGroupField
v-model="friendsListSearch" v-model="friendsListSearch"
:placeholder="t('view.friend_list.search_placeholder')" :placeholder="t('view.friend_list.search_placeholder')"
@@ -259,11 +271,12 @@
</template> </template>
<script setup> <script setup>
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { computed, nextTick, reactive, ref, watch } from 'vue'; import { computed, nextTick, reactive, ref, watch } from 'vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ElMessageBox } from 'element-plus';
import { InputGroupField } from '@/components/ui/input-group'; import { InputGroupField } from '@/components/ui/input-group';
import { Progress } from '@/components/ui/progress'; import { Progress } from '@/components/ui/progress';
import { ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -568,6 +581,11 @@
friendsListTable.data = sortedData; friendsListTable.data = sortedData;
} }
function handleFriendListFilterChange(value) {
friendsListSearchFilters.value = Array.isArray(value) ? value : [];
friendsListSearchChange();
}
</script> </script>
<style scoped> <style scoped>
+38 -19
View File
@@ -8,26 +8,33 @@
:on-page-size-change="handlePageSizeChange"> :on-page-size-change="handlePageSizeChange">
<template #toolbar> <template #toolbar>
<div style="margin: 0 0 10px; display: flex; align-items: center"> <div style="margin: 0 0 10px; display: flex; align-items: center">
<el-select <Select
v-model="friendLogTable.filters[0].value"
multiple multiple
clearable :model-value="
style="flex: 1" Array.isArray(friendLogTable.filters?.[0]?.value) ? friendLogTable.filters[0].value : []
:placeholder="t('view.friend_log.filter_placeholder')" "
@change="saveTableFilters"> @update:modelValue="handleFriendLogFilterChange">
<el-option <SelectTrigger class="w-full" style="flex: 1">
v-for="type in [ <SelectValue :placeholder="t('view.friend_log.filter_placeholder')" />
'Friend', </SelectTrigger>
'Unfriend', <SelectContent>
'FriendRequest', <SelectGroup>
'CancelFriendRequest', <SelectItem
'DisplayName', v-for="type in [
'TrustLevel' 'Friend',
]" 'Unfriend',
:key="type" 'FriendRequest',
:label="t('view.friend_log.filters.' + type)" 'CancelFriendRequest',
:value="type" /> 'DisplayName',
</el-select> 'TrustLevel'
]"
:key="type"
:value="type">
{{ t('view.friend_log.filters.' + type) }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<InputGroupField <InputGroupField
v-model="friendLogTable.filters[1].value" v-model="friendLogTable.filters[1].value"
:placeholder="t('view.friend_log.search_placeholder')" :placeholder="t('view.friend_log.search_placeholder')"
@@ -46,6 +53,14 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue
} from '../../components/ui/select';
import { useAppearanceSettingsStore, useFriendStore, useVrcxStore } from '../../stores'; import { useAppearanceSettingsStore, useFriendStore, useVrcxStore } from '../../stores';
import { DataTableLayout } from '../../components/ui/data-table'; import { DataTableLayout } from '../../components/ui/data-table';
import { InputGroupField } from '../../components/ui/input-group'; import { InputGroupField } from '../../components/ui/input-group';
@@ -129,6 +144,10 @@
function saveTableFilters() { function saveTableFilters() {
configRepository.setString('VRCX_friendLogTableFilters', JSON.stringify(friendLogTable.value.filters[0].value)); configRepository.setString('VRCX_friendLogTableFilters', JSON.stringify(friendLogTable.value.filters[0].value));
} }
function handleFriendLogFilterChange(value) {
friendLogTable.value.filters[0].value = Array.isArray(value) ? value : [];
saveTableFilters();
}
function deleteFriendLogPrompt(row) { function deleteFriendLogPrompt(row) {
ElMessageBox.confirm('Continue? Delete Log', 'Confirm', { ElMessageBox.confirm('Continue? Delete Log', 'Confirm', {
confirmButtonText: 'Confirm', confirmButtonText: 'Confirm',
+32 -21
View File
@@ -14,28 +14,33 @@
<Switch v-model="gameLogTable.vip" @update:modelValue="gameLogTableLookup" /> <Switch v-model="gameLogTable.vip" @update:modelValue="gameLogTableLookup" />
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<el-select <Select
v-model="gameLogTable.filter"
multiple multiple
clearable :model-value="Array.isArray(gameLogTable.filter) ? gameLogTable.filter : []"
style="flex: 1" @update:modelValue="handleGameLogFilterChange">
:placeholder="t('view.game_log.filter_placeholder')" <SelectTrigger class="w-full" style="flex: 1">
@change="gameLogTableLookup"> <SelectValue :placeholder="t('view.game_log.filter_placeholder')" />
<el-option </SelectTrigger>
v-for="type in [ <SelectContent>
'Location', <SelectGroup>
'OnPlayerJoined', <SelectItem
'OnPlayerLeft', v-for="type in [
'VideoPlay', 'Location',
'Event', 'OnPlayerJoined',
'External', 'OnPlayerLeft',
'StringLoad', 'VideoPlay',
'ImageLoad' 'Event',
]" 'External',
:key="type" 'StringLoad',
:label="t('view.game_log.filters.' + type)" 'ImageLoad'
:value="type"></el-option> ]"
</el-select> :key="type"
:value="type">
{{ t('view.game_log.filters.' + type) }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<InputGroupField <InputGroupField
v-model="gameLogTable.search" v-model="gameLogTable.search"
:placeholder="t('view.game_log.search_placeholder')" :placeholder="t('view.game_log.search_placeholder')"
@@ -50,6 +55,7 @@
</template> </template>
<script setup> <script setup>
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
@@ -152,6 +158,11 @@
onDeletePrompt: deleteGameLogEntryPrompt onDeletePrompt: deleteGameLogEntryPrompt
}); });
function handleGameLogFilterChange(value) {
gameLogTable.value.filter = Array.isArray(value) ? value : [];
gameLogTableLookup();
}
const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes); const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes);
const pageSize = computed(() => const pageSize = computed(() =>
gameLogTable.value.pageSizeLinked ? appearanceSettingsStore.tablePageSize : gameLogTable.value.pageSize gameLogTable.value.pageSizeLinked ? appearanceSettingsStore.tablePageSize : gameLogTable.value.pageSize
+25 -13
View File
@@ -1,19 +1,25 @@
<template> <template>
<div class="x-container" ref="moderationRef"> <div class="x-container" ref="moderationRef">
<div class="tool-slot"> <div class="tool-slot">
<el-select <Select
v-model="playerModerationTable.filters[0].value"
@change="saveTableFilters()"
multiple multiple
clearable :model-value="
style="flex: 1" Array.isArray(playerModerationTable.filters?.[0]?.value)
:placeholder="t('view.moderation.filter_placeholder')"> ? playerModerationTable.filters[0].value
<el-option : []
v-for="item in moderationTypes" "
:key="item" @update:modelValue="handleModerationFilterChange">
:label="t('view.moderation.filters.' + item)" <SelectTrigger class="w-full" style="flex: 1">
:value="item" /> <SelectValue :placeholder="t('view.moderation.filter_placeholder')" />
</el-select> </SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem v-for="item in moderationTypes" :key="item" :value="item">
{{ t('view.moderation.filters.' + item) }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<InputGroupField <InputGroupField
v-model="playerModerationTable.filters[1].value" v-model="playerModerationTable.filters[1].value"
:placeholder="t('view.moderation.search_placeholder')" :placeholder="t('view.moderation.search_placeholder')"
@@ -42,10 +48,11 @@
</template> </template>
<script setup> <script setup>
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { InputGroupField } from '@/components/ui/input-group';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import { InputGroupField } from '@/components/ui/input-group';
import { Refresh } from '@element-plus/icons-vue'; import { Refresh } from '@element-plus/icons-vue';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@@ -89,6 +96,11 @@
); );
} }
function handleModerationFilterChange(value) {
playerModerationTable.value.filters[0].value = Array.isArray(value) ? value : [];
saveTableFilters();
}
async function deletePlayerModeration(row) { async function deletePlayerModeration(row) {
const args = await playerModerationRequest.deletePlayerModeration({ const args = await playerModerationRequest.deletePlayerModeration({
moderated: row.targetUserId, moderated: row.targetUserId,
+48 -33
View File
@@ -9,39 +9,48 @@
:on-page-size-change="handlePageSizeChange"> :on-page-size-change="handlePageSizeChange">
<template #toolbar> <template #toolbar>
<div style="margin: 0 0 10px; display: flex; align-items: center"> <div style="margin: 0 0 10px; display: flex; align-items: center">
<el-select <Select
v-model="notificationTable.filters[0].value"
multiple multiple
clearable :model-value="
style="flex: 1" Array.isArray(notificationTable.filters?.[0]?.value)
:placeholder="t('view.notification.filter_placeholder')" ? notificationTable.filters[0].value
@change="saveTableFilters"> : []
<el-option "
v-for="type in [ @update:modelValue="handleNotificationFilterChange">
'requestInvite', <SelectTrigger class="w-full" style="flex: 1">
'invite', <SelectValue :placeholder="t('view.notification.filter_placeholder')" />
'requestInviteResponse', </SelectTrigger>
'inviteResponse', <SelectContent>
'friendRequest', <SelectGroup>
'ignoredFriendRequest', <SelectItem
'message', v-for="type in [
'boop', 'requestInvite',
'event.announcement', 'invite',
'groupChange', 'requestInviteResponse',
'group.announcement', 'inviteResponse',
'group.informative', 'friendRequest',
'group.invite', 'ignoredFriendRequest',
'group.joinRequest', 'message',
'group.transfer', 'boop',
'group.queueReady', 'event.announcement',
'moderation.warning.group', 'groupChange',
'moderation.report.closed', 'group.announcement',
'instance.closed' 'group.informative',
]" 'group.invite',
:key="type" 'group.joinRequest',
:label="t('view.notification.filters.' + type)" 'group.transfer',
:value="type" /> 'group.queueReady',
</el-select> 'moderation.warning.group',
'moderation.report.closed',
'instance.closed'
]"
:key="type"
:value="type">
{{ t('view.notification.filters.' + type) }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<InputGroupField <InputGroupField
v-model="notificationTable.filters[1].value" v-model="notificationTable.filters[1].value"
:placeholder="t('view.notification.search_placeholder')" :placeholder="t('view.notification.search_placeholder')"
@@ -73,10 +82,11 @@
</template> </template>
<script setup> <script setup>
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { InputGroupField } from '@/components/ui/input-group';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import { InputGroupField } from '@/components/ui/input-group';
import { Refresh } from '@element-plus/icons-vue'; import { Refresh } from '@element-plus/icons-vue';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@@ -272,6 +282,11 @@
); );
} }
function handleNotificationFilterChange(value) {
notificationTable.value.filters[0].value = Array.isArray(value) ? value : [];
saveTableFilters();
}
function openNotificationLink(link) { function openNotificationLink(link) {
if (!link) { if (!link) {
return; return;
+84 -38
View File
@@ -1,45 +1,63 @@
<template> <template>
<div class="x-aside-container"> <div class="x-aside-container">
<div style="display: flex; align-items: baseline"> <div style="display: flex; align-items: baseline">
<el-select <div style="flex: 1; padding: 10px; padding-left: 0">
clearable <Popover v-model:open="isQuickSearchOpen">
:placeholder="t('side_panel.search_placeholder')" <PopoverTrigger as-child>
filterable <Input
remote v-model="quickSearchQuery"
:remote-method="quickSearchRemoteMethod" :placeholder="t('side_panel.search_placeholder')"
popper-class="x-quick-search" @focus="handleQuickSearchFocus" />
style="flex: 1; padding: 10px; padding-left: 0" </PopoverTrigger>
@change="quickSearchChange"> <PopoverContent
<el-option v-for="item in quickSearchItems" :key="item.value" :value="item.value" :label="item.label"> side="bottom"
<div class="x-friend-item"> align="start"
<template v-if="item.ref"> class="x-quick-search-popover w-(--reka-popover-trigger-width) p-2"
<div class="detail"> @open-auto-focus.prevent
<span class="name" :style="{ color: item.ref.$userColour }">{{ @close-auto-focus.prevent>
item.ref.displayName <div class="max-h-80 overflow-auto">
}}</span> <button
<span v-if="!item.ref.isFriend" class="extra"></span> v-for="item in quickSearchItems"
<span v-else-if="item.ref.state === 'offline'" class="extra">{{ :key="item.value"
t('side_panel.search_result_active') type="button"
}}</span> class="w-full bg-transparent p-0 text-left"
<span v-else-if="item.ref.state === 'active'" class="extra">{{ @mousedown.prevent
t('side_panel.search_result_offline') @click="handleQuickSearchSelect(item.value)">
}}</span> <div class="x-friend-item">
<Location <template v-if="item.ref">
v-else <div class="detail">
class="extra" <span class="name" :style="{ color: item.ref.$userColour }">{{
:location="item.ref.location" item.ref.displayName
:traveling="item.ref.travelingToLocation" }}</span>
:link="false" /> <span v-if="!item.ref.isFriend" class="extra"></span>
<span v-else-if="item.ref.state === 'offline'" class="extra">{{
t('side_panel.search_result_active')
}}</span>
<span v-else-if="item.ref.state === 'active'" class="extra">{{
t('side_panel.search_result_offline')
}}</span>
<Location
v-else
class="extra"
:location="item.ref.location"
:traveling="item.ref.travelingToLocation"
:link="false" />
</div>
<img :src="userImage(item.ref)" class="avatar" loading="lazy" />
</template>
<span v-else>
{{ t('side_panel.search_result_more') }}
<span style="font-weight: bold">{{ item.label }}</span>
</span>
</div>
</button>
<div v-if="quickSearchItems.length === 0" class="px-2 py-2 text-xs opacity-70">
No results
</div> </div>
<img :src="userImage(item.ref)" class="avatar" loading="lazy" /> </div>
</template> </PopoverContent>
<span v-else> </Popover>
{{ t('side_panel.search_result_more') }} </div>
<span style="font-weight: bold">{{ item.label }}</span>
</span>
</div>
</el-option>
</el-select>
<div> <div>
<TooltipWrapper side="bottom" :content="t('side_panel.refresh_tooltip')"> <TooltipWrapper side="bottom" :content="t('side_panel.refresh_tooltip')">
<Button <Button
@@ -76,7 +94,10 @@
</template> </template>
<script setup> <script setup>
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { ref, watch } from 'vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Refresh } from '@element-plus/icons-vue'; import { Refresh } from '@element-plus/icons-vue';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@@ -94,6 +115,31 @@
const { quickSearchItems } = storeToRefs(useSearchStore()); const { quickSearchItems } = storeToRefs(useSearchStore());
const { inGameGroupOrder, groupInstances } = storeToRefs(useGroupStore()); const { inGameGroupOrder, groupInstances } = storeToRefs(useGroupStore());
const { t } = useI18n(); const { t } = useI18n();
const quickSearchQuery = ref('');
const isQuickSearchOpen = ref(false);
watch(
quickSearchQuery,
(value) => {
quickSearchRemoteMethod(String(value ?? ''));
},
{ immediate: true }
);
function handleQuickSearchFocus() {
isQuickSearchOpen.value = true;
quickSearchRemoteMethod(String(quickSearchQuery.value ?? ''));
}
function handleQuickSearchSelect(value) {
if (!value) {
return;
}
isQuickSearchOpen.value = false;
quickSearchQuery.value = '';
quickSearchChange(String(value));
}
</script> </script>
<style scoped> <style scoped>