kopia lustrzana https://github.com/cheeaun/phanpy
Scoped keyboard shortcuts
rodzic
4520822f1f
commit
db428c04d1
|
@ -21,7 +21,6 @@ function Home({ hidden }) {
|
||||||
useTitle('Home', '/');
|
useTitle('Home', '/');
|
||||||
const { masto, instance } = api();
|
const { masto, instance } = api();
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
const isHomeLocation = snapStates.currentLocation === '/';
|
|
||||||
const [uiState, setUIState] = useState('default');
|
const [uiState, setUIState] = useState('default');
|
||||||
const [showMore, setShowMore] = useState(false);
|
const [showMore, setShowMore] = useState(false);
|
||||||
|
|
||||||
|
@ -149,121 +148,103 @@ function Home({ hidden }) {
|
||||||
|
|
||||||
const scrollableRef = useRef();
|
const scrollableRef = useRef();
|
||||||
|
|
||||||
useHotkeys(
|
const jRef = useHotkeys('j, shift+j', (_, handler) => {
|
||||||
'j, shift+j',
|
// focus on next status after active status
|
||||||
(_, handler) => {
|
// Traverses .timeline li .status-link, focus on .status-link
|
||||||
// focus on next status after active status
|
const activeStatus = document.activeElement.closest(
|
||||||
// Traverses .timeline li .status-link, focus on .status-link
|
'.status-link, .status-boost-link',
|
||||||
const activeStatus = document.activeElement.closest(
|
);
|
||||||
|
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
||||||
|
const allStatusLinks = Array.from(
|
||||||
|
scrollableRef.current.querySelectorAll(
|
||||||
'.status-link, .status-boost-link',
|
'.status-link, .status-boost-link',
|
||||||
);
|
),
|
||||||
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
);
|
||||||
const allStatusLinks = Array.from(
|
if (
|
||||||
scrollableRef.current.querySelectorAll(
|
activeStatus &&
|
||||||
'.status-link, .status-boost-link',
|
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
||||||
),
|
activeStatusRect.bottom > 0
|
||||||
);
|
) {
|
||||||
if (
|
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
||||||
activeStatus &&
|
let nextStatus = allStatusLinks[activeStatusIndex + 1];
|
||||||
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
if (handler.shift) {
|
||||||
activeStatusRect.bottom > 0
|
// get next status that's not .status-boost-link
|
||||||
) {
|
nextStatus = allStatusLinks.find(
|
||||||
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
(statusLink, index) =>
|
||||||
let nextStatus = allStatusLinks[activeStatusIndex + 1];
|
index > activeStatusIndex &&
|
||||||
if (handler.shift) {
|
!statusLink.classList.contains('status-boost-link'),
|
||||||
// get next status that's not .status-boost-link
|
);
|
||||||
nextStatus = allStatusLinks.find(
|
|
||||||
(statusLink, index) =>
|
|
||||||
index > activeStatusIndex &&
|
|
||||||
!statusLink.classList.contains('status-boost-link'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (nextStatus) {
|
|
||||||
nextStatus.focus();
|
|
||||||
nextStatus.scrollIntoViewIfNeeded?.();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If active status is not in viewport, get the topmost status-link in viewport
|
|
||||||
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
|
||||||
const statusLinkRect = statusLink.getBoundingClientRect();
|
|
||||||
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
|
||||||
});
|
|
||||||
if (topmostStatusLink) {
|
|
||||||
topmostStatusLink.focus();
|
|
||||||
topmostStatusLink.scrollIntoViewIfNeeded?.();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
if (nextStatus) {
|
||||||
{
|
nextStatus.focus();
|
||||||
enabled: isHomeLocation,
|
nextStatus.scrollIntoViewIfNeeded?.();
|
||||||
},
|
}
|
||||||
);
|
} else {
|
||||||
|
// If active status is not in viewport, get the topmost status-link in viewport
|
||||||
|
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
||||||
|
const statusLinkRect = statusLink.getBoundingClientRect();
|
||||||
|
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
||||||
|
});
|
||||||
|
if (topmostStatusLink) {
|
||||||
|
topmostStatusLink.focus();
|
||||||
|
topmostStatusLink.scrollIntoViewIfNeeded?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
useHotkeys(
|
const kRef = useHotkeys('k, shift+k', (_, handler) => {
|
||||||
'k, shift+k',
|
// focus on previous status after active status
|
||||||
(_, handler) => {
|
// Traverses .timeline li .status-link, focus on .status-link
|
||||||
// focus on previous status after active status
|
const activeStatus = document.activeElement.closest(
|
||||||
// Traverses .timeline li .status-link, focus on .status-link
|
'.status-link, .status-boost-link',
|
||||||
const activeStatus = document.activeElement.closest(
|
);
|
||||||
|
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
||||||
|
const allStatusLinks = Array.from(
|
||||||
|
scrollableRef.current.querySelectorAll(
|
||||||
'.status-link, .status-boost-link',
|
'.status-link, .status-boost-link',
|
||||||
);
|
),
|
||||||
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
);
|
||||||
const allStatusLinks = Array.from(
|
if (
|
||||||
scrollableRef.current.querySelectorAll(
|
activeStatus &&
|
||||||
'.status-link, .status-boost-link',
|
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
||||||
),
|
activeStatusRect.bottom > 0
|
||||||
);
|
) {
|
||||||
if (
|
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
||||||
activeStatus &&
|
let prevStatus = allStatusLinks[activeStatusIndex - 1];
|
||||||
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
if (handler.shift) {
|
||||||
activeStatusRect.bottom > 0
|
// get prev status that's not .status-boost-link
|
||||||
) {
|
prevStatus = allStatusLinks.findLast(
|
||||||
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
(statusLink, index) =>
|
||||||
let prevStatus = allStatusLinks[activeStatusIndex - 1];
|
index < activeStatusIndex &&
|
||||||
if (handler.shift) {
|
!statusLink.classList.contains('status-boost-link'),
|
||||||
// get prev status that's not .status-boost-link
|
);
|
||||||
prevStatus = allStatusLinks.findLast(
|
|
||||||
(statusLink, index) =>
|
|
||||||
index < activeStatusIndex &&
|
|
||||||
!statusLink.classList.contains('status-boost-link'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (prevStatus) {
|
|
||||||
prevStatus.focus();
|
|
||||||
prevStatus.scrollIntoViewIfNeeded?.();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If active status is not in viewport, get the topmost status-link in viewport
|
|
||||||
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
|
||||||
const statusLinkRect = statusLink.getBoundingClientRect();
|
|
||||||
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
|
||||||
});
|
|
||||||
if (topmostStatusLink) {
|
|
||||||
topmostStatusLink.focus();
|
|
||||||
topmostStatusLink.scrollIntoViewIfNeeded?.();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
if (prevStatus) {
|
||||||
{
|
prevStatus.focus();
|
||||||
enabled: isHomeLocation,
|
prevStatus.scrollIntoViewIfNeeded?.();
|
||||||
},
|
}
|
||||||
);
|
} else {
|
||||||
|
// If active status is not in viewport, get the topmost status-link in viewport
|
||||||
|
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
||||||
|
const statusLinkRect = statusLink.getBoundingClientRect();
|
||||||
|
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
||||||
|
});
|
||||||
|
if (topmostStatusLink) {
|
||||||
|
topmostStatusLink.focus();
|
||||||
|
topmostStatusLink.scrollIntoViewIfNeeded?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
useHotkeys(
|
const oRef = useHotkeys(['enter', 'o'], () => {
|
||||||
['enter', 'o'],
|
// open active status
|
||||||
() => {
|
const activeStatus = document.activeElement.closest(
|
||||||
// open active status
|
'.status-link, .status-boost-link',
|
||||||
const activeStatus = document.activeElement.closest(
|
);
|
||||||
'.status-link, .status-boost-link',
|
if (activeStatus) {
|
||||||
);
|
activeStatus.click();
|
||||||
if (activeStatus) {
|
}
|
||||||
activeStatus.click();
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: isHomeLocation,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
scrollDirection,
|
scrollDirection,
|
||||||
|
@ -306,6 +287,12 @@ function Home({ hidden }) {
|
||||||
const [showUpdatesButton, setShowUpdatesButton] = useState(false);
|
const [showUpdatesButton, setShowUpdatesButton] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isNewAndTop = snapStates.homeNew.length > 0 && reachStart;
|
const isNewAndTop = snapStates.homeNew.length > 0 && reachStart;
|
||||||
|
console.log(
|
||||||
|
'isNewAndTop',
|
||||||
|
isNewAndTop,
|
||||||
|
snapStates.homeNew.length,
|
||||||
|
reachStart,
|
||||||
|
);
|
||||||
setShowUpdatesButton(isNewAndTop);
|
setShowUpdatesButton(isNewAndTop);
|
||||||
}, [snapStates.homeNew.length]);
|
}, [snapStates.homeNew.length]);
|
||||||
|
|
||||||
|
@ -315,7 +302,12 @@ function Home({ hidden }) {
|
||||||
id="home-page"
|
id="home-page"
|
||||||
class="deck-container"
|
class="deck-container"
|
||||||
hidden={hidden}
|
hidden={hidden}
|
||||||
ref={scrollableRef}
|
ref={(node) => {
|
||||||
|
scrollableRef.current = node;
|
||||||
|
jRef.current = node;
|
||||||
|
kRef.current = node;
|
||||||
|
oRef.current = node;
|
||||||
|
}}
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
>
|
>
|
||||||
<div class="timeline-deck deck">
|
<div class="timeline-deck deck">
|
||||||
|
|
Ładowanie…
Reference in New Issue