mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-02 09:15:22 +03:00
keyboard navigation
This commit is contained in:
parent
313deb986b
commit
486e3bebdb
@ -1,12 +1,14 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { useEffect, useState, useCallback, useMemo } from 'preact/hooks';
|
import { useEffect, useState, useCallback, useMemo, useRef } from 'preact/hooks';
|
||||||
import ArrowRight from '../icons/ArrowRight';
|
import ArrowRight from '../icons/ArrowRight';
|
||||||
import ArrowRightDouble from '../icons/ArrowRightDouble';
|
import ArrowRightDouble from '../icons/ArrowRightDouble';
|
||||||
|
|
||||||
const oneDay = 60 * 60 * 24 * 1000;
|
const oneDay = 60 * 60 * 24 * 1000;
|
||||||
const todayTimestamp = Date.now() - (Date.now() % oneDay) + new Date().getTimezoneOffset() * 1000 * 60;
|
const todayTimestamp = Date.now() - (Date.now() % oneDay) + new Date().getTimezoneOffset() * 1000 * 60;
|
||||||
|
|
||||||
const Calender = ({ onChange, calenderRef }) => {
|
const Calender = ({ onChange, calenderRef, close }) => {
|
||||||
|
const keyRef = useRef([]);
|
||||||
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const month = date.getMonth();
|
const month = date.getMonth();
|
||||||
@ -38,6 +40,7 @@ const Calender = ({ onChange, calenderRef }) => {
|
|||||||
timeRange: { before: null, after: null },
|
timeRange: { before: null, after: null },
|
||||||
monthDetails: null,
|
monthDetails: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getNumberOfDays = useCallback((year, month) => {
|
const getNumberOfDays = useCallback((year, month) => {
|
||||||
return 40 - new Date(year, month, 40).getDate();
|
return 40 - new Date(year, month, 40).getDate();
|
||||||
}, []);
|
}, []);
|
||||||
@ -90,6 +93,7 @@ const Calender = ({ onChange, calenderRef }) => {
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// setState((prev) => ({ ...prev, selectedDay: todayTimestamp, monthDetails: monthArray }));
|
||||||
return monthArray;
|
return monthArray;
|
||||||
},
|
},
|
||||||
[getNumberOfDays, getDayDetails]
|
[getNumberOfDays, getDayDetails]
|
||||||
@ -99,6 +103,13 @@ const Calender = ({ onChange, calenderRef }) => {
|
|||||||
setState((prev) => ({ ...prev, selectedDay: todayTimestamp, monthDetails: getMonthDetails(year, month) }));
|
setState((prev) => ({ ...prev, selectedDay: todayTimestamp, monthDetails: getMonthDetails(year, month) }));
|
||||||
}, [year, month, getMonthDetails]);
|
}, [year, month, getMonthDetails]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// add refs for keyboard navigation
|
||||||
|
if (state.monthDetails) {
|
||||||
|
keyRef.current = keyRef.current.slice(0, state.monthDetails.length);
|
||||||
|
}
|
||||||
|
}, [state.monthDetails]);
|
||||||
|
|
||||||
const isCurrentDay = (day) => day.timestamp === todayTimestamp;
|
const isCurrentDay = (day) => day.timestamp === todayTimestamp;
|
||||||
|
|
||||||
const isSelectedRange = useCallback(
|
const isSelectedRange = useCallback(
|
||||||
@ -203,13 +214,40 @@ const Calender = ({ onChange, calenderRef }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleKeydown = (e, day, index) => {
|
||||||
|
if ((keyRef.current && e.key === 'Enter') || e.keyCode === 32) {
|
||||||
|
onDateClick(day);
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowLeft') {
|
||||||
|
index > 0 && keyRef.current[index - 1].focus();
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowRight') {
|
||||||
|
index < 41 && keyRef.current[index + 1].focus();
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
index > 6 && keyRef.current[index - 7].focus();
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
index < 36 && keyRef.current[index + 7].focus();
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const renderCalendar = () => {
|
const renderCalendar = () => {
|
||||||
const days =
|
const days =
|
||||||
state.monthDetails &&
|
state.monthDetails &&
|
||||||
state.monthDetails.map((day, index) => {
|
state.monthDetails.map((day, idx) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => onDateClick(day)}
|
onClick={() => onDateClick(day)}
|
||||||
|
onkeydown={(e) => handleKeydown(e, day, idx)}
|
||||||
|
ref={(ref) => (keyRef.current[idx] = ref)}
|
||||||
|
autoFocus={isCurrentDay(day)}
|
||||||
|
tabIndex={idx}
|
||||||
className={`h-12 w-12 float-left flex flex-shrink justify-center items-center cursor-pointer ${
|
className={`h-12 w-12 float-left flex flex-shrink justify-center items-center cursor-pointer ${
|
||||||
day.month !== 0 ? ' opacity-50 bg-gray-700 dark:bg-gray-700 pointer-events-none' : ''
|
day.month !== 0 ? ' opacity-50 bg-gray-700 dark:bg-gray-700 pointer-events-none' : ''
|
||||||
}
|
}
|
||||||
@ -217,7 +255,7 @@ const Calender = ({ onChange, calenderRef }) => {
|
|||||||
${isSelectedRange(day) ? ' bg-blue-600 dark:hover:bg-blue-600' : ''}
|
${isSelectedRange(day) ? ' bg-blue-600 dark:hover:bg-blue-600' : ''}
|
||||||
${isLastDayInRange(day) ? ' rounded-r-xl ' : ''}
|
${isLastDayInRange(day) ? ' rounded-r-xl ' : ''}
|
||||||
${isCurrentDay(day) && !isLastDayInRange(day) ? 'rounded-full bg-gray-100 dark:hover:bg-gray-100 ' : ''}`}
|
${isCurrentDay(day) && !isLastDayInRange(day) ? 'rounded-full bg-gray-100 dark:hover:bg-gray-100 ' : ''}`}
|
||||||
key={index}
|
key={idx}
|
||||||
>
|
>
|
||||||
<div className="font-light">
|
<div className="font-light">
|
||||||
<span className="text-gray-400">{day.date}</span>
|
<span className="text-gray-400">{day.date}</span>
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export default function RelativeModal({
|
|||||||
|
|
||||||
const handleKeydown = useCallback(
|
const handleKeydown = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
const focusable = ref.current.querySelectorAll('[tabindex]');
|
const focusable = ref.current && ref.current.querySelectorAll('[tabindex]');
|
||||||
if (event.key === 'Tab' && focusable.length) {
|
if (event.key === 'Tab' && focusable.length) {
|
||||||
if (event.shiftKey && document.activeElement === focusable[0]) {
|
if (event.shiftKey && document.activeElement === focusable[0]) {
|
||||||
focusable[focusable.length - 1].focus();
|
focusable[focusable.length - 1].focus();
|
||||||
|
|||||||
@ -120,12 +120,14 @@ export default function Select({
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'ArrowDown': {
|
case 'ArrowDown': {
|
||||||
|
event.preventDefault();
|
||||||
const newIndex = focused + 1;
|
const newIndex = focused + 1;
|
||||||
newIndex < options.length && setFocused(newIndex);
|
newIndex < options.length && setFocused(newIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'ArrowUp': {
|
case 'ArrowUp': {
|
||||||
|
event.preventDefault();
|
||||||
const newIndex = focused - 1;
|
const newIndex = focused - 1;
|
||||||
newIndex > -1 && setFocused(newIndex);
|
newIndex > -1 && setFocused(newIndex);
|
||||||
break;
|
break;
|
||||||
@ -153,12 +155,14 @@ export default function Select({
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'ArrowDown': {
|
case 'ArrowDown': {
|
||||||
|
event.preventDefault();
|
||||||
const newIndex = focused + 1;
|
const newIndex = focused + 1;
|
||||||
newIndex < options.length && setFocused(newIndex);
|
newIndex < options.length && setFocused(newIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'ArrowUp': {
|
case 'ArrowUp': {
|
||||||
|
event.preventDefault();
|
||||||
const newIndex = focused - 1;
|
const newIndex = focused - 1;
|
||||||
newIndex > -1 && setFocused(newIndex);
|
newIndex > -1 && setFocused(newIndex);
|
||||||
break;
|
break;
|
||||||
@ -167,7 +171,7 @@ export default function Select({
|
|||||||
// no default
|
// no default
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onChange, options, showMenu, setShowMenu, setFocused, focused, selected]
|
[onChange, options, showMenu, setShowMenu, setFocused, focused, selected, paramName]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDismiss = useCallback(() => {
|
const handleDismiss = useCallback(() => {
|
||||||
@ -206,7 +210,7 @@ export default function Select({
|
|||||||
/>
|
/>
|
||||||
{showDatePicker && (
|
{showDatePicker && (
|
||||||
<Menu className="rounded-t-none" onDismiss={handleDismiss} relativeTo={ref}>
|
<Menu className="rounded-t-none" onDismiss={handleDismiss} relativeTo={ref}>
|
||||||
<Calender onChange={handleDateRange} calenderRef={calenderRef} />
|
<Calender onChange={handleDateRange} calenderRef={calenderRef} close={() => setShowDatePicker(false)} />
|
||||||
</Menu>
|
</Menu>
|
||||||
)}
|
)}
|
||||||
{showMenu ? (
|
{showMenu ? (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user