mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-24 16:58:47 +01:00
Experimental j,k,o,esc,backspace shortcuts
This commit is contained in:
parent
36a33e488b
commit
b12b0c588d
3 changed files with 92 additions and 0 deletions
|
@ -41,6 +41,7 @@ a.mention span {
|
|||
overflow-x: hidden;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
overscroll-behavior: contain;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
.deck-container[hidden] {
|
||||
display: block;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Link } from 'preact-router/match';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
|
@ -71,6 +72,88 @@ function Home({ hidden }) {
|
|||
|
||||
const scrollableRef = useRef();
|
||||
|
||||
useHotkeys('j', () => {
|
||||
// focus on next status after active status
|
||||
// Traverses .timeline li .status-link, focus on .status-link
|
||||
const activeStatus = document.activeElement.closest('.status-link');
|
||||
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
||||
if (
|
||||
activeStatus &&
|
||||
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
||||
activeStatusRect.bottom > 0
|
||||
) {
|
||||
const nextStatus = activeStatus.parentElement.nextElementSibling;
|
||||
if (nextStatus) {
|
||||
const statusLink = nextStatus.querySelector('.status-link');
|
||||
if (statusLink) {
|
||||
statusLink.focus();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If active status is not in viewport, get the topmost status-link in viewport
|
||||
const statusLinks = document.querySelectorAll(
|
||||
'.timeline li .status-link',
|
||||
);
|
||||
let topmostStatusLink;
|
||||
for (const statusLink of statusLinks) {
|
||||
const statusLinkRect = statusLink.getBoundingClientRect();
|
||||
if (statusLinkRect.top >= 44) {
|
||||
// 44 is the magic number for header height, not real
|
||||
topmostStatusLink = statusLink;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (topmostStatusLink) {
|
||||
topmostStatusLink.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useHotkeys('k', () => {
|
||||
// focus on previous status after active status
|
||||
// Traverses .timeline li .status-link, focus on .status-link
|
||||
const activeStatus = document.activeElement.closest('.status-link');
|
||||
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
||||
if (
|
||||
activeStatus &&
|
||||
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
||||
activeStatusRect.bottom > 0
|
||||
) {
|
||||
const prevStatus = activeStatus.parentElement.previousElementSibling;
|
||||
if (prevStatus) {
|
||||
const statusLink = prevStatus.querySelector('.status-link');
|
||||
if (statusLink) {
|
||||
statusLink.focus();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If active status is not in viewport, get the topmost status-link in viewport
|
||||
const statusLinks = document.querySelectorAll(
|
||||
'.timeline li .status-link',
|
||||
);
|
||||
let topmostStatusLink;
|
||||
for (const statusLink of statusLinks) {
|
||||
const statusLinkRect = statusLink.getBoundingClientRect();
|
||||
if (statusLinkRect.top >= 44) {
|
||||
// 44 is the magic number for header height, not real
|
||||
topmostStatusLink = statusLink;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (topmostStatusLink) {
|
||||
topmostStatusLink.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useHotkeys(['enter', 'o'], () => {
|
||||
// open active status
|
||||
const activeStatus = document.activeElement.closest('.status-link');
|
||||
if (activeStatus) {
|
||||
activeStatus.click();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
id="home-page"
|
||||
|
@ -165,6 +248,8 @@ function Home({ hidden }) {
|
|||
onChange={(inView) => {
|
||||
if (inView) loadStatuses();
|
||||
}}
|
||||
root={scrollableRef.current}
|
||||
rootMargin="100px 0px"
|
||||
>
|
||||
<Status skeleton />
|
||||
</InView>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import './status.css';
|
||||
|
||||
import debounce from 'just-debounce-it';
|
||||
import { route } from 'preact-router';
|
||||
import { Link } from 'preact-router/match';
|
||||
import {
|
||||
useEffect,
|
||||
|
@ -9,6 +10,7 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
} from 'preact/hooks';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
|
@ -278,6 +280,10 @@ function StatusPage({ id }) {
|
|||
return top > 0 ? 'down' : 'up';
|
||||
}, [heroInView]);
|
||||
|
||||
useHotkeys(['esc', 'backspace'], () => {
|
||||
route(closeLink);
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="deck-backdrop">
|
||||
<Link href={closeLink}></Link>
|
||||
|
|
Loading…
Reference in a new issue