forked from Github/frigate
test(web): add eslint and PR lint validation
This commit is contained in:
committed by
Blake Blackshear
parent
513a099c24
commit
daa759cc55
@@ -12,16 +12,14 @@ import { useLayoutEffect, useCallback, useRef, useState } from 'preact/hooks';
|
||||
|
||||
// We would typically preserve these in component state
|
||||
// But need to avoid too many re-renders
|
||||
let ticking = false;
|
||||
let lastScrollY = window.scrollY;
|
||||
|
||||
export default function AppBar({ title }) {
|
||||
const [show, setShow] = useState(true);
|
||||
const [atZero, setAtZero] = useState(window.scrollY === 0);
|
||||
const [_, setDrawerVisible] = useState(true);
|
||||
const [showMoreMenu, setShowMoreMenu] = useState(false);
|
||||
const { currentMode, persistedMode, setDarkMode } = useDarkMode();
|
||||
const { showDrawer, setShowDrawer } = useDrawer();
|
||||
const { setDarkMode } = useDarkMode();
|
||||
const { setShowDrawer } = useDrawer();
|
||||
|
||||
const handleSelectDarkMode = useCallback(
|
||||
(value, label) => {
|
||||
@@ -37,15 +35,11 @@ export default function AppBar({ title }) {
|
||||
(event) => {
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
// if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
setShow(scrollY <= 0 || lastScrollY > scrollY);
|
||||
setAtZero(scrollY === 0);
|
||||
ticking = false;
|
||||
lastScrollY = scrollY;
|
||||
});
|
||||
ticking = true;
|
||||
// }
|
||||
},
|
||||
[setShow]
|
||||
);
|
||||
@@ -55,7 +49,7 @@ export default function AppBar({ title }) {
|
||||
return () => {
|
||||
document.removeEventListener('scroll', scrollListener);
|
||||
};
|
||||
}, []);
|
||||
}, [scrollListener]);
|
||||
|
||||
const handleShowMenu = useCallback(() => {
|
||||
setShowMoreMenu(true);
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function AutoUpdatingCameraImage({ camera, searchParams, showFps
|
||||
},
|
||||
loadTime > MIN_LOAD_TIMEOUT_MS ? 1 : MIN_LOAD_TIMEOUT_MS
|
||||
);
|
||||
}, [key, searchParams, setFps]);
|
||||
}, [key, setFps]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { h } from 'preact';
|
||||
import ActivityIndicator from './ActivityIndicator';
|
||||
import { useApiHost, useConfig } from '../api';
|
||||
import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'preact/hooks';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
||||
|
||||
export default function CameraImage({ camera, onload, searchParams = '' }) {
|
||||
const { data: config } = useConfig();
|
||||
@@ -22,14 +22,11 @@ export default function CameraImage({ camera, onload, searchParams = '' }) {
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [setAvailableWidth, width]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) {
|
||||
return;
|
||||
}
|
||||
resizeObserver.observe(containerRef.current);
|
||||
}, [resizeObserver, containerRef.current]);
|
||||
}, [resizeObserver, containerRef]);
|
||||
|
||||
const scaledHeight = useMemo(() => Math.min(Math.ceil(availableWidth / aspectRatio), height), [
|
||||
availableWidth,
|
||||
@@ -38,26 +35,28 @@ export default function CameraImage({ camera, onload, searchParams = '' }) {
|
||||
]);
|
||||
const scaledWidth = useMemo(() => Math.ceil(scaledHeight * aspectRatio), [scaledHeight, aspectRatio]);
|
||||
|
||||
const img = useMemo(() => new Image(), [camera]);
|
||||
const img = useMemo(() => new Image(), []);
|
||||
img.onload = useCallback(
|
||||
(event) => {
|
||||
setHasLoaded(true);
|
||||
const ctx = canvasRef.current.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0, scaledWidth, scaledHeight);
|
||||
if (canvasRef.current) {
|
||||
const ctx = canvasRef.current.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0, scaledWidth, scaledHeight);
|
||||
}
|
||||
onload && onload(event);
|
||||
},
|
||||
[setHasLoaded, onload, canvasRef.current]
|
||||
[img, scaledHeight, scaledWidth, setHasLoaded, onload, canvasRef]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!scaledHeight || !canvasRef.current) {
|
||||
if (scaledHeight === 0 || !canvasRef.current) {
|
||||
return;
|
||||
}
|
||||
img.src = `${apiHost}/api/${name}/latest.jpg?h=${scaledHeight}${searchParams ? `&${searchParams}` : ''}`;
|
||||
}, [apiHost, name, img, searchParams, scaledHeight]);
|
||||
}, [apiHost, canvasRef, name, img, searchParams, scaledHeight]);
|
||||
|
||||
return (
|
||||
<div className="relative" ref={containerRef}>
|
||||
<div className="relative w-full" ref={containerRef}>
|
||||
<canvas height={scaledHeight} ref={canvasRef} width={scaledWidth} />
|
||||
{!hasLoaded ? (
|
||||
<div className="absolute inset-0 flex justify-center" style={`height: ${scaledHeight}px`}>
|
||||
|
||||
@@ -26,14 +26,14 @@ export default function Box({
|
||||
{media || header ? (
|
||||
<Element href={href} {...props}>
|
||||
{media}
|
||||
<div class="p-4 pb-2">{header ? <Heading size="base">{header}</Heading> : null}</div>
|
||||
<div className="p-4 pb-2">{header ? <Heading size="base">{header}</Heading> : null}</div>
|
||||
</Element>
|
||||
) : null}
|
||||
{buttons.length || content ? (
|
||||
<div class="pl-4 pb-2">
|
||||
<div className="pl-4 pb-2">
|
||||
{content || null}
|
||||
{buttons.length ? (
|
||||
<div class="flex space-x-4 -ml-2">
|
||||
<div className="flex space-x-4 -ml-2">
|
||||
{buttons.map(({ name, href }) => (
|
||||
<Button key={name} href={href} type="text">
|
||||
{name}
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function LinkedLogo() {
|
||||
return (
|
||||
<Heading size="lg">
|
||||
<a className="transition-colors flex items-center space-x-4 dark:text-white hover:text-blue-500" href="/">
|
||||
<div class="w-10">
|
||||
<div className="w-10">
|
||||
<Logo />
|
||||
</div>
|
||||
Frigate
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
import RelativeModal from './RelativeModal';
|
||||
import { useCallback, useEffect } from 'preact/hooks';
|
||||
import { useCallback } from 'preact/hooks';
|
||||
|
||||
export default function Menu({ className, children, onDismiss, relativeTo, widthRelative }) {
|
||||
return relativeTo ? (
|
||||
@@ -21,21 +21,12 @@ export function MenuItem({ focus, icon: Icon, label, onSelect, value }) {
|
||||
onSelect && onSelect(value, label);
|
||||
}, [onSelect, value, label]);
|
||||
|
||||
const handleKeydown = useCallback(
|
||||
(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
onSelect && onSelect(value, label);
|
||||
}
|
||||
},
|
||||
[onSelect, value, label]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex space-x-2 p-2 px-5 hover:bg-gray-200 dark:hover:bg-gray-800 dark:hover:text-white cursor-pointer ${
|
||||
focus ? 'bg-gray-200 dark:bg-gray-800 dark:text-white' : ''
|
||||
}`}
|
||||
onclick={handleClick}
|
||||
onClick={handleClick}
|
||||
role="option"
|
||||
>
|
||||
{Icon ? (
|
||||
@@ -43,7 +34,7 @@ export function MenuItem({ focus, icon: Icon, label, onSelect, value }) {
|
||||
<Icon />
|
||||
</div>
|
||||
) : null}
|
||||
<div class="whitespace-nowrap">{label}</div>
|
||||
<div className="whitespace-nowrap">{label}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h, Fragment } from 'preact';
|
||||
import { Link } from 'preact-router/match';
|
||||
import { useCallback, useState } from 'preact/hooks';
|
||||
import { useCallback } from 'preact/hooks';
|
||||
import { useDrawer } from '../context';
|
||||
|
||||
export default function NavigationDrawer({ children, header }) {
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function RelativeModal({
|
||||
return;
|
||||
}
|
||||
},
|
||||
[ref.current]
|
||||
[ref]
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -84,7 +84,7 @@ export default function RelativeModal({
|
||||
const focusable = ref.current.querySelector('[tabindex]');
|
||||
focusable && focusable.focus();
|
||||
}
|
||||
}, [relativeTo && relativeTo.current, ref && ref.current, widthRelative]);
|
||||
}, [relativeTo, ref, widthRelative]);
|
||||
|
||||
useEffect(() => {
|
||||
if (position.top >= 0) {
|
||||
@@ -92,7 +92,7 @@ export default function RelativeModal({
|
||||
} else {
|
||||
setShow(false);
|
||||
}
|
||||
}, [show, position.top, ref.current]);
|
||||
}, [show, position, ref]);
|
||||
|
||||
const menu = (
|
||||
<Fragment>
|
||||
@@ -102,7 +102,7 @@ export default function RelativeModal({
|
||||
className={`z-10 bg-white dark:bg-gray-700 dark:text-white absolute shadow-lg rounded w-auto h-auto transition-all duration-75 transform scale-90 opacity-0 overflow-scroll ${
|
||||
show ? 'scale-100 opacity-100' : ''
|
||||
} ${className}`}
|
||||
onkeydown={handleKeydown}
|
||||
onKeyDown={handleKeydown}
|
||||
role={role}
|
||||
ref={ref}
|
||||
style={position.top >= 0 ? position : null}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function Select({ label, onChange, options: inputOptions = [], se
|
||||
onChange && onChange(value, label);
|
||||
setShowMenu(false);
|
||||
},
|
||||
[onChange]
|
||||
[onChange, options]
|
||||
);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
@@ -38,32 +38,34 @@ export default function Select({ label, onChange, options: inputOptions = [], se
|
||||
const handleKeydown = useCallback(
|
||||
(event) => {
|
||||
switch (event.key) {
|
||||
case 'Enter': {
|
||||
if (!showMenu) {
|
||||
setShowMenu(true);
|
||||
setFocused(selected);
|
||||
} else {
|
||||
setSelected(focused);
|
||||
onChange && onChange(options[focused].value, options[focused].label);
|
||||
setShowMenu(false);
|
||||
}
|
||||
break;
|
||||
case 'Enter': {
|
||||
if (!showMenu) {
|
||||
setShowMenu(true);
|
||||
setFocused(selected);
|
||||
} else {
|
||||
setSelected(focused);
|
||||
onChange && onChange(options[focused].value, options[focused].label);
|
||||
setShowMenu(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ArrowDown': {
|
||||
const newIndex = focused + 1;
|
||||
newIndex < options.length && setFocused(newIndex);
|
||||
break;
|
||||
}
|
||||
case 'ArrowDown': {
|
||||
const newIndex = focused + 1;
|
||||
newIndex < options.length && setFocused(newIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ArrowUp': {
|
||||
const newIndex = focused - 1;
|
||||
newIndex > -1 && setFocused(newIndex);
|
||||
break;
|
||||
}
|
||||
case 'ArrowUp': {
|
||||
const newIndex = focused - 1;
|
||||
newIndex > -1 && setFocused(newIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
// no default
|
||||
}
|
||||
},
|
||||
[setShowMenu, setFocused, focused, selected]
|
||||
[onChange, options, showMenu, setShowMenu, setFocused, focused, selected]
|
||||
);
|
||||
|
||||
const handleDismiss = useCallback(() => {
|
||||
@@ -80,7 +82,8 @@ export default function Select({ label, onChange, options: inputOptions = [], se
|
||||
setSelected(selectedIndex);
|
||||
setFocused(selectedIndex);
|
||||
}
|
||||
}, [propSelected]);
|
||||
// DO NOT include `selected`
|
||||
}, [options, propSelected]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
||||
@@ -2,9 +2,7 @@ import { h } from 'preact';
|
||||
import { useCallback, useState } from 'preact/hooks';
|
||||
|
||||
export default function Switch({ checked, id, onChange }) {
|
||||
const [internalState, setInternalState] = useState(checked);
|
||||
const [isFocused, setFocused] = useState(false);
|
||||
const [isHovered, setHovered] = useState(false);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(event) => {
|
||||
@@ -25,12 +23,12 @@ export default function Switch({ checked, id, onChange }) {
|
||||
|
||||
return (
|
||||
<label
|
||||
for={id}
|
||||
htmlFor={id}
|
||||
className={`flex items-center justify-center ${onChange ? 'cursor-pointer' : 'cursor-not-allowed'}`}
|
||||
>
|
||||
<div
|
||||
onmouseover={handleFocus}
|
||||
onmouseout={handleBlur}
|
||||
onMouseOver={handleFocus}
|
||||
onMouseOut={handleBlur}
|
||||
className={`w-8 h-5 relative ${!onChange ? 'opacity-60' : ''}`}
|
||||
>
|
||||
<div className="relative overflow-hidden">
|
||||
@@ -38,7 +36,7 @@ export default function Switch({ checked, id, onChange }) {
|
||||
className="absolute left-48"
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
tabindex="0"
|
||||
tabIndex="0"
|
||||
id={id}
|
||||
type="checkbox"
|
||||
onChange={handleChange}
|
||||
|
||||
@@ -30,7 +30,7 @@ export function Tr({ children, className = '' }) {
|
||||
|
||||
export function Th({ children, className = '', colspan }) {
|
||||
return (
|
||||
<th className={`border-b border-gray-400 p-2 px-1 lg:p-4 text-left ${className}`} colspan={colspan}>
|
||||
<th className={`border-b border-gray-400 p-2 px-1 lg:p-4 text-left ${className}`} colSpan={colspan}>
|
||||
{children}
|
||||
</th>
|
||||
);
|
||||
@@ -38,7 +38,7 @@ export function Th({ children, className = '', colspan }) {
|
||||
|
||||
export function Td({ children, className = '', colspan }) {
|
||||
return (
|
||||
<td className={`p-2 px-1 lg:p-4 ${className}`} colspan={colspan}>
|
||||
<td className={`p-2 px-1 lg:p-4 ${className}`} colSpan={colspan}>
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
|
||||
@@ -43,12 +43,12 @@ export default function TextField({
|
||||
[onChangeText, setValue]
|
||||
);
|
||||
|
||||
// Reset the state if the prop value changes
|
||||
useEffect(() => {
|
||||
if (propValue !== value) {
|
||||
setValue(propValue);
|
||||
}
|
||||
}, [propValue, setValue]);
|
||||
// DO NOT include `value`
|
||||
}, [propValue, setValue]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const labelMoved = isFocused || value !== '';
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function TextField({
|
||||
>
|
||||
<label className="flex space-x-2 items-center">
|
||||
{LeadingIcon ? (
|
||||
<div class="w-10 h-full">
|
||||
<div className="w-10 h-full">
|
||||
<LeadingIcon />
|
||||
</div>
|
||||
) : null}
|
||||
@@ -72,8 +72,8 @@ export default function TextField({
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
onInput={handleChange}
|
||||
readonly={readonly}
|
||||
tabindex="0"
|
||||
readOnly={readonly}
|
||||
tabIndex="0"
|
||||
type={keyboardType}
|
||||
value={value}
|
||||
{...props}
|
||||
@@ -87,7 +87,7 @@ export default function TextField({
|
||||
</div>
|
||||
</div>
|
||||
{TrailingIcon ? (
|
||||
<div class="w-10 h-10">
|
||||
<div className="w-10 h-10">
|
||||
<TrailingIcon />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user