fix: improve keyboard navigation in text inputs

fix: w2g link pasting
This commit is contained in:
ThaUnknown 2025-07-20 02:13:14 +02:00
parent 1c76730528
commit 1df63a0716
No known key found for this signature in database
8 changed files with 25 additions and 15 deletions

View file

@ -1,6 +1,6 @@
{
"name": "ui",
"version": "6.4.66",
"version": "6.4.67",
"license": "BUSL-1.1",
"private": true,
"packageManager": "pnpm@9.15.5",

4
src/app.d.ts vendored
View file

@ -36,8 +36,8 @@ declare global {
}
interface Navigator {
userAgentData: {
getHighEntropyValues: (keys: string[]) => Promise<Record<string, string>>
userAgentData?: {
getHighEntropyValues?: (keys: string[]) => Promise<Record<string, string>>
}
}

View file

@ -51,7 +51,7 @@
<Tabs.Trigger tabindex={0} value='extensions'>Extensions</Tabs.Trigger>
<Tabs.Trigger tabindex={0} value='repositories'>Repositories</Tabs.Trigger>
</Tabs.List>
<div class='flex items-center relative scale-parent md:max-w-72 w-full'>
<div class='flex items-center relative scale-parent md:max-w-56 w-full'>
<Input
class='pl-9 bg-neutral-950 select:bg-accent select:text-accent-foreground shadow-sm no-scale placeholder:opacity-50'
placeholder='Search {value}...'

View file

@ -213,15 +213,28 @@ function getElementsInDesiredDirection (keyboardFocusable: ElementPosition[], cu
})
}
// is input utility class
function inInputEl (element: HTMLElement): element is HTMLInputElement {
return element.matches('input, textarea')
}
/**
* Navigates using D-pad keys.
*/
function navigateDPad (direction = 'up') {
function navigateDPad (direction = 'up', e: KeyboardEvent) {
const keyboardFocusable = getFocusableElementPositions()
const nofocus = !document.activeElement || document.activeElement === document.body
const currentElement = nofocus ? keyboardFocusable[0]! : getElementPosition(document.activeElement as HTMLElement)
if (nofocus) return focusElement(currentElement.element)
if (inInputEl(currentElement.element)) {
const input = currentElement.element
if (direction === 'left' && input.selectionStart !== 0) return
if (direction === 'right' && input.selectionEnd !== input.value.length) return
}
e.preventDefault()
e.stopPropagation()
// allow overrides via data attributes ex: <div data-up="#id, #id2"?> but order them, as querySelectorAll returns them in order of appearance rather than order of selectors
for (const selector of currentElement.element.dataset[direction]?.split(',') ?? []) {
@ -260,13 +273,13 @@ function navigateDPad (direction = 'up') {
function focusElement (element?: HTMLElement | null) {
if (!element) return false
const isInput = element.matches('input[type=text], input[type=url], input[type=number], textarea')
const isInput = inInputEl(element)
if (isInput) {
const input = element as HTMLInputElement
const input = element
input.readOnly = true
}
element.focus()
if (isInput) setTimeout(() => { (element as HTMLInputElement).readOnly = false })
if (isInput) setTimeout(() => { element.readOnly = false })
element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'smooth' })
element.dispatchEvent(new CustomEvent('navigate', { bubbles: true, composed: true, detail: { target: element.id, value: element.dataset.value } }))
@ -280,10 +293,8 @@ document.addEventListener('keydown', navigate)
export function navigate (e: KeyboardEvent) {
if (e.key in DirectionKeyMap) {
e.preventDefault()
e.stopPropagation()
inputType.value = 'dpad'
navigateDPad(DirectionKeyMap[e.key as 'ArrowDown' | 'ArrowUp' | 'ArrowLeft' | 'ArrowRight'])
navigateDPad(DirectionKeyMap[e.key as 'ArrowDown' | 'ArrowUp' | 'ArrowLeft' | 'ArrowRight'], e)
}
}

View file

@ -101,7 +101,7 @@ export const flyAndScale = (
export const sleep = (t: number) => new Promise<void>(resolve => setTimeout(resolve, t))
export const highEntropyValues = 'userAgentData' in navigator && navigator.userAgentData.getHighEntropyValues(['architecture', 'platform', 'platformVersion'])
export const highEntropyValues = 'userAgentData' in navigator && navigator.userAgentData?.getHighEntropyValues?.(['architecture', 'platform', 'platformVersion'])
export function safeLocalStorage<T> (key: string): T | undefined {
try {

View file

@ -22,7 +22,7 @@
const imageRx = /\.(jpeg|jpg|gif|png|webp)/i
const w2gRx = /hayase(?:(?:\.watch)|(?::\/))\/w2g\/(.+)/
const w2gRx = /hayas.?ee?(?:(?:\.watch)|(?::\/))?\/w2g\/(.+)/
async function handleTransfer (e: { dataTransfer?: DataTransfer | null, clipboardData?: DataTransfer | null } & Event) {
const promises = [...(e.dataTransfer ?? e.clipboardData)!.items].map(item => {

View file

@ -30,7 +30,7 @@
const info = {
...device,
appInfo: {
userAgent: await navigator.userAgentData.getHighEntropyValues(['architecture', 'platform', 'platformVersion']),
userAgent: await navigator.userAgentData?.getHighEntropyValues?.(['architecture', 'platform', 'platformVersion']),
support: SUPPORTS,
settings: $settings
}

View file

@ -17,7 +17,6 @@
<div class='text-6xl text-center font-bold'>Update Required</div>
<Separator class='my-6 w-40' />
<div class='text-xl text-wrap max-w-full text-center mb-6'>A mandatory update is available for the {#await outdatedComponent then name}{name}{/await}.<br />Please update to continue.</div>
{#await outdatedComponent then name}
{#if name === 'client'}
{#await native.updateReady()}