mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-19 14:53:50 +02:00
feat: add a setting to enable pointer on hover (#1585)
* feat: added striped table mode for visual clarity also added a settings toggle to revert to original behavior * fix: add pointer on hover behind a toggle * fix: add `x-link` class to the selector * fix: indicate that this is global forceful overide
This commit is contained in:
13
src/app.css
13
src/app.css
@@ -34,7 +34,6 @@ html.dark .x-container {
|
|||||||
background: var(--sidebar);
|
background: var(--sidebar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.x-friend-list {
|
.x-friend-list {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
overflow: hidden auto;
|
overflow: hidden auto;
|
||||||
@@ -205,7 +204,6 @@ img.friends-list-avatar {
|
|||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.x-friend-item:hover {
|
.x-friend-item:hover {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
@@ -225,7 +223,6 @@ img.friends-list-avatar {
|
|||||||
width: 167px;
|
width: 167px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
i.x-user-status,
|
i.x-user-status,
|
||||||
i.x-status-icon {
|
i.x-status-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -337,3 +334,13 @@ i.x-status-icon.red {
|
|||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*FIXME: this is a nasty temporary hack*/
|
||||||
|
.force-pointer-on-hover a,
|
||||||
|
.force-pointer-on-hover button,
|
||||||
|
.force-pointer-on-hover [role='button'],
|
||||||
|
.force-pointer-on-hover label,
|
||||||
|
.force-pointer-on-hover summary,
|
||||||
|
.force-pointer-on-hover .x-link,
|
||||||
|
.force-pointer-on-hover input[type='submit'] {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,7 +34,9 @@
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
<template v-if="table.getRowModel().rows?.length">
|
<template v-if="table.getRowModel().rows?.length">
|
||||||
<template v-for="row in table.getRowModel().rows" :key="row.id">
|
<template v-for="row in table.getRowModel().rows" :key="row.id">
|
||||||
<TableRow @click="handleRowClick(row)">
|
<TableRow
|
||||||
|
@click="handleRowClick(row)"
|
||||||
|
:class="isDataTableStriped ? 'even:bg-muted/20' : ''">
|
||||||
<TableCell
|
<TableCell
|
||||||
v-for="cell in row.getVisibleCells()"
|
v-for="cell in row.getVisibleCells()"
|
||||||
:key="cell.id"
|
:key="cell.id"
|
||||||
@@ -125,6 +127,11 @@
|
|||||||
} from '../pagination';
|
} from '../pagination';
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../table';
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../table';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select';
|
||||||
|
import { useAppearanceSettingsStore } from '@/stores/';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
|
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||||
|
const { isDataTableStriped } = storeToRefs(appearanceSettingsStore);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
table: {
|
table: {
|
||||||
|
|||||||
@@ -592,6 +592,8 @@
|
|||||||
"table_page_sizes": "Table Page Sizes",
|
"table_page_sizes": "Table Page Sizes",
|
||||||
"table_page_sizes_error": "Page size must be a number between 1 and 1000",
|
"table_page_sizes_error": "Page size must be a number between 1 and 1000",
|
||||||
"show_notification_icon_dot": "Show Tray Notification Dot",
|
"show_notification_icon_dot": "Show Tray Notification Dot",
|
||||||
|
"striped_data_table_mode": "Striped Table Mode",
|
||||||
|
"toggle_pointer_on_hover": "Force pointer on hover",
|
||||||
"table_density": "Table Density",
|
"table_density": "Table Density",
|
||||||
"table_density_standard": "Standard",
|
"table_density_standard": "Standard",
|
||||||
"table_density_comfortable": "Comfortable",
|
"table_density_comfortable": "Comfortable",
|
||||||
|
|||||||
@@ -104,6 +104,9 @@ export const useAppearanceSettingsStore = defineStore(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isDataTableStriped = ref(false);
|
||||||
|
const showPointerOnHover = ref(false);
|
||||||
|
|
||||||
const clampInt = (value, min, max) => {
|
const clampInt = (value, min, max) => {
|
||||||
const n = parseInt(value, 10);
|
const n = parseInt(value, 10);
|
||||||
return Math.min(max, Math.max(min, n));
|
return Math.min(max, Math.max(min, n));
|
||||||
@@ -150,6 +153,8 @@ export const useAppearanceSettingsStore = defineStore(
|
|||||||
trustColorConfig,
|
trustColorConfig,
|
||||||
notificationIconDotConfig,
|
notificationIconDotConfig,
|
||||||
navIsCollapsedConfig,
|
navIsCollapsedConfig,
|
||||||
|
dataTableStripedConfig,
|
||||||
|
showPointerOnHoverConfig,
|
||||||
appFontFamilyConfig,
|
appFontFamilyConfig,
|
||||||
lastDarkThemeConfig
|
lastDarkThemeConfig
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
@@ -207,6 +212,8 @@ export const useAppearanceSettingsStore = defineStore(
|
|||||||
),
|
),
|
||||||
configRepository.getBool('VRCX_notificationIconDot', true),
|
configRepository.getBool('VRCX_notificationIconDot', true),
|
||||||
configRepository.getBool('VRCX_navIsCollapsed', true),
|
configRepository.getBool('VRCX_navIsCollapsed', true),
|
||||||
|
configRepository.getBool('VRCX_dataTableStriped', false),
|
||||||
|
configRepository.getBool('VRCX_showPointerOnHover', false),
|
||||||
configRepository.getString(
|
configRepository.getString(
|
||||||
'VRCX_fontFamily',
|
'VRCX_fontFamily',
|
||||||
APP_FONT_DEFAULT_KEY
|
APP_FONT_DEFAULT_KEY
|
||||||
@@ -300,6 +307,10 @@ export const useAppearanceSettingsStore = defineStore(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
isNavCollapsed.value = navIsCollapsedConfig;
|
isNavCollapsed.value = navIsCollapsedConfig;
|
||||||
|
isDataTableStriped.value = dataTableStripedConfig;
|
||||||
|
showPointerOnHover.value = showPointerOnHoverConfig;
|
||||||
|
|
||||||
|
applyPointerHoverClass();
|
||||||
|
|
||||||
await configRepository.remove('VRCX_navWidth');
|
await configRepository.remove('VRCX_navWidth');
|
||||||
|
|
||||||
@@ -738,6 +749,34 @@ export const useAppearanceSettingsStore = defineStore(
|
|||||||
applyTableDensity(tableDensity.value);
|
applyTableDensity(tableDensity.value);
|
||||||
configRepository.setString('VRCX_tableDensity', tableDensity.value);
|
configRepository.setString('VRCX_tableDensity', tableDensity.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleStripedDataTable() {
|
||||||
|
isDataTableStriped.value = !isDataTableStriped.value;
|
||||||
|
configRepository.setBool(
|
||||||
|
'VRCX_dataTableStriped',
|
||||||
|
isDataTableStriped.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this is nasty, there should be a better way of doing this
|
||||||
|
function applyPointerHoverClass() {
|
||||||
|
const classList = document.documentElement.classList;
|
||||||
|
classList.remove('force-pointer-on-hover');
|
||||||
|
|
||||||
|
if (showPointerOnHover.value) {
|
||||||
|
classList.add('force-pointer-on-hover');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePointerOnHover() {
|
||||||
|
showPointerOnHover.value = !showPointerOnHover.value;
|
||||||
|
configRepository.setBool(
|
||||||
|
'VRCX_showPointerOnHover',
|
||||||
|
showPointerOnHover.value
|
||||||
|
);
|
||||||
|
applyPointerHoverClass();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} color
|
* @param {object} color
|
||||||
*/
|
*/
|
||||||
@@ -910,6 +949,8 @@ export const useAppearanceSettingsStore = defineStore(
|
|||||||
isSideBarTabShow,
|
isSideBarTabShow,
|
||||||
notificationIconDot,
|
notificationIconDot,
|
||||||
isNavCollapsed,
|
isNavCollapsed,
|
||||||
|
isDataTableStriped,
|
||||||
|
showPointerOnHover,
|
||||||
|
|
||||||
setAppLanguage,
|
setAppLanguage,
|
||||||
setDisplayVRCPlusIconsAsAvatar,
|
setDisplayVRCPlusIconsAsAvatar,
|
||||||
@@ -935,6 +976,8 @@ export const useAppearanceSettingsStore = defineStore(
|
|||||||
setHideUserMemos,
|
setHideUserMemos,
|
||||||
setHideUnfriends,
|
setHideUnfriends,
|
||||||
setRandomUserColours,
|
setRandomUserColours,
|
||||||
|
toggleStripedDataTable,
|
||||||
|
togglePointerOnHover,
|
||||||
setTableDensity,
|
setTableDensity,
|
||||||
setTrustColor,
|
setTrustColor,
|
||||||
tryInitUserColours,
|
tryInitUserColours,
|
||||||
|
|||||||
@@ -180,11 +180,19 @@
|
|||||||
</ListboxRoot>
|
</ListboxRoot>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
<simple-switch
|
||||||
|
:label="t('view.settings.appearance.appearance.striped_data_table_mode')"
|
||||||
|
:value="isDataTableStriped"
|
||||||
|
@change="toggleStripedDataTable" />
|
||||||
<div class="options-container-item">
|
<div class="options-container-item">
|
||||||
<Button size="sm" variant="outline" @click="promptMaxTableSizeDialog">{{
|
<Button size="sm" variant="outline" @click="promptMaxTableSizeDialog">{{
|
||||||
t('view.settings.appearance.appearance.table_max_size')
|
t('view.settings.appearance.appearance.table_max_size')
|
||||||
}}</Button>
|
}}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<simple-switch
|
||||||
|
:label="t('view.settings.appearance.appearance.toggle_pointer_on_hover')"
|
||||||
|
:value="showPointerOnHover"
|
||||||
|
@change="togglePointerOnHover" />
|
||||||
</div>
|
</div>
|
||||||
<div class="options-container">
|
<div class="options-container">
|
||||||
<span class="header">{{ t('view.settings.appearance.timedate.header') }}</span>
|
<span class="header">{{ t('view.settings.appearance.timedate.header') }}</span>
|
||||||
@@ -449,12 +457,11 @@
|
|||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { useAppearanceSettingsStore, useFavoriteStore, useVrStore } from '@/stores';
|
||||||
|
import { getLanguageName, languageCodes } from '@/localization';
|
||||||
|
import { APP_FONT_FAMILIES } from '@/shared/constants';
|
||||||
import PresetColorPicker from '@/components/PresetColorPicker.vue';
|
import PresetColorPicker from '@/components/PresetColorPicker.vue';
|
||||||
|
|
||||||
import { useAppearanceSettingsStore, useFavoriteStore, useVrStore } from '../../../../stores';
|
|
||||||
import { getLanguageName, languageCodes } from '../../../../localization';
|
|
||||||
import { APP_FONT_FAMILIES } from '../../../../shared/constants';
|
|
||||||
|
|
||||||
import SimpleSwitch from '../SimpleSwitch.vue';
|
import SimpleSwitch from '../SimpleSwitch.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -485,7 +492,9 @@
|
|||||||
randomUserColours,
|
randomUserColours,
|
||||||
trustColor,
|
trustColor,
|
||||||
notificationIconDot,
|
notificationIconDot,
|
||||||
tablePageSizes
|
tablePageSizes,
|
||||||
|
isDataTableStriped,
|
||||||
|
showPointerOnHover
|
||||||
} = storeToRefs(appearanceSettingsStore);
|
} = storeToRefs(appearanceSettingsStore);
|
||||||
|
|
||||||
const appLanguageDisplayName = computed(() => getLanguageName(String(appLanguage.value)));
|
const appLanguageDisplayName = computed(() => getLanguageName(String(appLanguage.value)));
|
||||||
@@ -514,6 +523,8 @@
|
|||||||
promptMaxTableSizeDialog,
|
promptMaxTableSizeDialog,
|
||||||
setNotificationIconDot,
|
setNotificationIconDot,
|
||||||
setTablePageSizes,
|
setTablePageSizes,
|
||||||
|
toggleStripedDataTable,
|
||||||
|
togglePointerOnHover,
|
||||||
setAppFontFamily
|
setAppFontFamily
|
||||||
} = appearanceSettingsStore;
|
} = appearanceSettingsStore;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user