mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-21 15:41:57 +00:00
fix: improve keyboard navigation in text inputs
fix: w2g link pasting
This commit is contained in:
parent
1c76730528
commit
1df63a0716
8 changed files with 25 additions and 15 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ui",
|
"name": "ui",
|
||||||
"version": "6.4.66",
|
"version": "6.4.67",
|
||||||
"license": "BUSL-1.1",
|
"license": "BUSL-1.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@9.15.5",
|
"packageManager": "pnpm@9.15.5",
|
||||||
|
|
|
||||||
4
src/app.d.ts
vendored
4
src/app.d.ts
vendored
|
|
@ -36,8 +36,8 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Navigator {
|
interface Navigator {
|
||||||
userAgentData: {
|
userAgentData?: {
|
||||||
getHighEntropyValues: (keys: string[]) => Promise<Record<string, string>>
|
getHighEntropyValues?: (keys: string[]) => Promise<Record<string, string>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
<Tabs.Trigger tabindex={0} value='extensions'>Extensions</Tabs.Trigger>
|
<Tabs.Trigger tabindex={0} value='extensions'>Extensions</Tabs.Trigger>
|
||||||
<Tabs.Trigger tabindex={0} value='repositories'>Repositories</Tabs.Trigger>
|
<Tabs.Trigger tabindex={0} value='repositories'>Repositories</Tabs.Trigger>
|
||||||
</Tabs.List>
|
</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
|
<Input
|
||||||
class='pl-9 bg-neutral-950 select:bg-accent select:text-accent-foreground shadow-sm no-scale placeholder:opacity-50'
|
class='pl-9 bg-neutral-950 select:bg-accent select:text-accent-foreground shadow-sm no-scale placeholder:opacity-50'
|
||||||
placeholder='Search {value}...'
|
placeholder='Search {value}...'
|
||||||
|
|
|
||||||
|
|
@ -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.
|
* Navigates using D-pad keys.
|
||||||
*/
|
*/
|
||||||
function navigateDPad (direction = 'up') {
|
function navigateDPad (direction = 'up', e: KeyboardEvent) {
|
||||||
const keyboardFocusable = getFocusableElementPositions()
|
const keyboardFocusable = getFocusableElementPositions()
|
||||||
const nofocus = !document.activeElement || document.activeElement === document.body
|
const nofocus = !document.activeElement || document.activeElement === document.body
|
||||||
const currentElement = nofocus ? keyboardFocusable[0]! : getElementPosition(document.activeElement as HTMLElement)
|
const currentElement = nofocus ? keyboardFocusable[0]! : getElementPosition(document.activeElement as HTMLElement)
|
||||||
|
|
||||||
if (nofocus) return focusElement(currentElement.element)
|
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
|
// 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(',') ?? []) {
|
for (const selector of currentElement.element.dataset[direction]?.split(',') ?? []) {
|
||||||
|
|
@ -260,13 +273,13 @@ function navigateDPad (direction = 'up') {
|
||||||
|
|
||||||
function focusElement (element?: HTMLElement | null) {
|
function focusElement (element?: HTMLElement | null) {
|
||||||
if (!element) return false
|
if (!element) return false
|
||||||
const isInput = element.matches('input[type=text], input[type=url], input[type=number], textarea')
|
const isInput = inInputEl(element)
|
||||||
if (isInput) {
|
if (isInput) {
|
||||||
const input = element as HTMLInputElement
|
const input = element
|
||||||
input.readOnly = true
|
input.readOnly = true
|
||||||
}
|
}
|
||||||
element.focus()
|
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.scrollIntoView({ block: 'center', inline: 'center', behavior: 'smooth' })
|
||||||
|
|
||||||
element.dispatchEvent(new CustomEvent('navigate', { bubbles: true, composed: true, detail: { target: element.id, value: element.dataset.value } }))
|
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) {
|
export function navigate (e: KeyboardEvent) {
|
||||||
if (e.key in DirectionKeyMap) {
|
if (e.key in DirectionKeyMap) {
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
inputType.value = 'dpad'
|
inputType.value = 'dpad'
|
||||||
navigateDPad(DirectionKeyMap[e.key as 'ArrowDown' | 'ArrowUp' | 'ArrowLeft' | 'ArrowRight'])
|
navigateDPad(DirectionKeyMap[e.key as 'ArrowDown' | 'ArrowUp' | 'ArrowLeft' | 'ArrowRight'], e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ export const flyAndScale = (
|
||||||
|
|
||||||
export const sleep = (t: number) => new Promise<void>(resolve => setTimeout(resolve, t))
|
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 {
|
export function safeLocalStorage<T> (key: string): T | undefined {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
const imageRx = /\.(jpeg|jpg|gif|png|webp)/i
|
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) {
|
async function handleTransfer (e: { dataTransfer?: DataTransfer | null, clipboardData?: DataTransfer | null } & Event) {
|
||||||
const promises = [...(e.dataTransfer ?? e.clipboardData)!.items].map(item => {
|
const promises = [...(e.dataTransfer ?? e.clipboardData)!.items].map(item => {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
const info = {
|
const info = {
|
||||||
...device,
|
...device,
|
||||||
appInfo: {
|
appInfo: {
|
||||||
userAgent: await navigator.userAgentData.getHighEntropyValues(['architecture', 'platform', 'platformVersion']),
|
userAgent: await navigator.userAgentData?.getHighEntropyValues?.(['architecture', 'platform', 'platformVersion']),
|
||||||
support: SUPPORTS,
|
support: SUPPORTS,
|
||||||
settings: $settings
|
settings: $settings
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
<div class='text-6xl text-center font-bold'>Update Required</div>
|
<div class='text-6xl text-center font-bold'>Update Required</div>
|
||||||
<Separator class='my-6 w-40' />
|
<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>
|
<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}
|
{#await outdatedComponent then name}
|
||||||
{#if name === 'client'}
|
{#if name === 'client'}
|
||||||
{#await native.updateReady()}
|
{#await native.updateReady()}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue