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:
kubectl
2026-01-19 01:33:01 +01:00
committed by GitHub
parent 29b83c5b89
commit cacbf742d1
5 changed files with 79 additions and 9 deletions

View File

@@ -34,7 +34,6 @@ html.dark .x-container {
background: var(--sidebar);
}
.x-friend-list {
padding: 0 10px;
overflow: hidden auto;
@@ -205,7 +204,6 @@ img.friends-list-avatar {
margin-right: 2px;
}
.x-friend-item:hover {
border-radius: 8px;
}
@@ -225,7 +223,6 @@ img.friends-list-avatar {
width: 167px;
}
i.x-user-status,
i.x-status-icon {
display: inline-block;
@@ -337,3 +334,13 @@ i.x-status-icon.red {
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;
}

View File

@@ -34,7 +34,9 @@
<TableBody>
<template v-if="table.getRowModel().rows?.length">
<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
v-for="cell in row.getVisibleCells()"
:key="cell.id"
@@ -125,6 +127,11 @@
} from '../pagination';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../table';
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({
table: {

View File

@@ -592,6 +592,8 @@
"table_page_sizes": "Table Page Sizes",
"table_page_sizes_error": "Page size must be a number between 1 and 1000",
"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_standard": "Standard",
"table_density_comfortable": "Comfortable",

View File

@@ -104,6 +104,9 @@ export const useAppearanceSettingsStore = defineStore(
);
});
const isDataTableStriped = ref(false);
const showPointerOnHover = ref(false);
const clampInt = (value, min, max) => {
const n = parseInt(value, 10);
return Math.min(max, Math.max(min, n));
@@ -150,6 +153,8 @@ export const useAppearanceSettingsStore = defineStore(
trustColorConfig,
notificationIconDotConfig,
navIsCollapsedConfig,
dataTableStripedConfig,
showPointerOnHoverConfig,
appFontFamilyConfig,
lastDarkThemeConfig
] = await Promise.all([
@@ -207,6 +212,8 @@ export const useAppearanceSettingsStore = defineStore(
),
configRepository.getBool('VRCX_notificationIconDot', true),
configRepository.getBool('VRCX_navIsCollapsed', true),
configRepository.getBool('VRCX_dataTableStriped', false),
configRepository.getBool('VRCX_showPointerOnHover', false),
configRepository.getString(
'VRCX_fontFamily',
APP_FONT_DEFAULT_KEY
@@ -300,6 +307,10 @@ export const useAppearanceSettingsStore = defineStore(
);
}
isNavCollapsed.value = navIsCollapsedConfig;
isDataTableStriped.value = dataTableStripedConfig;
showPointerOnHover.value = showPointerOnHoverConfig;
applyPointerHoverClass();
await configRepository.remove('VRCX_navWidth');
@@ -738,6 +749,34 @@ export const useAppearanceSettingsStore = defineStore(
applyTableDensity(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
*/
@@ -910,6 +949,8 @@ export const useAppearanceSettingsStore = defineStore(
isSideBarTabShow,
notificationIconDot,
isNavCollapsed,
isDataTableStriped,
showPointerOnHover,
setAppLanguage,
setDisplayVRCPlusIconsAsAvatar,
@@ -935,6 +976,8 @@ export const useAppearanceSettingsStore = defineStore(
setHideUserMemos,
setHideUnfriends,
setRandomUserColours,
toggleStripedDataTable,
togglePointerOnHover,
setTableDensity,
setTrustColor,
tryInitUserColours,

View File

@@ -180,11 +180,19 @@
</ListboxRoot>
</Popover>
</div>
<simple-switch
:label="t('view.settings.appearance.appearance.striped_data_table_mode')"
:value="isDataTableStriped"
@change="toggleStripedDataTable" />
<div class="options-container-item">
<Button size="sm" variant="outline" @click="promptMaxTableSizeDialog">{{
t('view.settings.appearance.appearance.table_max_size')
}}</Button>
</div>
<simple-switch
:label="t('view.settings.appearance.appearance.toggle_pointer_on_hover')"
:value="showPointerOnHover"
@change="togglePointerOnHover" />
</div>
<div class="options-container">
<span class="header">{{ t('view.settings.appearance.timedate.header') }}</span>
@@ -449,12 +457,11 @@
import { toast } from 'vue-sonner';
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 { useAppearanceSettingsStore, useFavoriteStore, useVrStore } from '../../../../stores';
import { getLanguageName, languageCodes } from '../../../../localization';
import { APP_FONT_FAMILIES } from '../../../../shared/constants';
import SimpleSwitch from '../SimpleSwitch.vue';
const { t } = useI18n();
@@ -485,7 +492,9 @@
randomUserColours,
trustColor,
notificationIconDot,
tablePageSizes
tablePageSizes,
isDataTableStriped,
showPointerOnHover
} = storeToRefs(appearanceSettingsStore);
const appLanguageDisplayName = computed(() => getLanguageName(String(appLanguage.value)));
@@ -514,6 +523,8 @@
promptMaxTableSizeDialog,
setNotificationIconDot,
setTablePageSizes,
toggleStripedDataTable,
togglePointerOnHover,
setAppFontFamily
} = appearanceSettingsStore;