mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
React linters (#117)
* React hook linter * React linting * Simplify config
This commit is contained in:
parent
52a1c65fe4
commit
e525e12c3c
|
@ -1,6 +1,8 @@
|
||||||
{
|
{
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"plugins": ["@typescript-eslint/eslint-plugin"],
|
"plugins": [
|
||||||
|
"@typescript-eslint/eslint-plugin"
|
||||||
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
"error",
|
"error",
|
||||||
|
@ -69,7 +71,9 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"@typescript-eslint/require-await": ["error"]
|
"@typescript-eslint/require-await": ["error"],
|
||||||
|
"react-hooks/rules-of-hooks": "error",
|
||||||
|
"react-hooks/exhaustive-deps": "error"
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"builtin": true,
|
"builtin": true,
|
||||||
|
@ -80,7 +84,11 @@
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"project": "./tsconfig.json"
|
"project": "./tsconfig.json"
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended"
|
||||||
|
],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
|
@ -140,7 +148,8 @@
|
||||||
"ignorePatterns": ["coverage/**/*"],
|
"ignorePatterns": ["coverage/**/*"],
|
||||||
"settings": {
|
"settings": {
|
||||||
"react": {
|
"react": {
|
||||||
"pragma": "h"
|
"pragma": "h",
|
||||||
|
"version": "16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ export const Apple2 = (props: Apple2Props) => {
|
||||||
apple2.run();
|
apple2.run();
|
||||||
}).catch(error => console.error(error));
|
}).catch(error => console.error(error));
|
||||||
}
|
}
|
||||||
}, []);
|
}, [props]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cs('outer', { apple2e: e})}>
|
<div className={cs('outer', { apple2e: e})}>
|
||||||
|
|
|
@ -32,13 +32,13 @@ export const AudioControl = ({ apple2 }: AudioControlProps) => {
|
||||||
setAudio(audio);
|
setAudio(audio);
|
||||||
setAudioEnabled(audio.isEnabled());
|
setAudioEnabled(audio.isEnabled());
|
||||||
}
|
}
|
||||||
}, [apple2]);
|
}, [apple2, options]);
|
||||||
|
|
||||||
const doToggleSound = useCallback(() => {
|
const doToggleSound = useCallback(() => {
|
||||||
const on = !audio?.isEnabled();
|
const on = !audio?.isEnabled();
|
||||||
options.setOption(SOUND_ENABLED_OPTION, on);
|
options.setOption(SOUND_ENABLED_OPTION, on);
|
||||||
setAudioEnabled(on);
|
setAudioEnabled(on);
|
||||||
}, [audio]);
|
}, [audio, options]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ControlButton
|
<ControlButton
|
||||||
|
|
|
@ -20,6 +20,6 @@ export interface ControlButtonProps {
|
||||||
*/
|
*/
|
||||||
export const ControlButton = ({ icon, title, onClick, ...props }: ControlButtonProps) => (
|
export const ControlButton = ({ icon, title, onClick, ...props }: ControlButtonProps) => (
|
||||||
<button onClick={onClick} title={title} {...props} >
|
<button onClick={onClick} title={title} {...props} >
|
||||||
<i class={`fas fa-${icon}`}></i>
|
<i className={`fas fa-${icon}`}></i>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -47,7 +47,7 @@ export const ControlStrip = ({ apple2, e }: ControlStripProps) => {
|
||||||
const screen = new Screen(vm);
|
const screen = new Screen(vm);
|
||||||
options.addOptions(screen);
|
options.addOptions(screen);
|
||||||
}
|
}
|
||||||
}, [apple2]);
|
}, [apple2, e, options]);
|
||||||
|
|
||||||
const doReset = useCallback(() =>
|
const doReset = useCallback(() =>
|
||||||
apple2?.reset()
|
apple2?.reset()
|
||||||
|
@ -70,7 +70,7 @@ export const ControlStrip = ({ apple2, e }: ControlStripProps) => {
|
||||||
SCREEN_FULL_PAGE,
|
SCREEN_FULL_PAGE,
|
||||||
!options.getOption(SCREEN_FULL_PAGE)
|
!options.getOption(SCREEN_FULL_PAGE)
|
||||||
)
|
)
|
||||||
, []);
|
, [options]);
|
||||||
|
|
||||||
useHotKey('F2', doToggleFullPage);
|
useHotKey('F2', doToggleFullPage);
|
||||||
useHotKey('F4', doShowOptions);
|
useHotKey('F4', doShowOptions);
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const DiskII = ({ disk2, number, on, name, side }: DiskIIProps) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [disk2]);
|
}, [disk2, number]);
|
||||||
|
|
||||||
const doClose = useCallback(() => {
|
const doClose = useCallback(() => {
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
|
@ -74,7 +74,7 @@ export const DiskII = ({ disk2, number, on, name, side }: DiskIIProps) => {
|
||||||
className={cs('disk-light', { on })}
|
className={cs('disk-light', { on })}
|
||||||
/>
|
/>
|
||||||
<button title="Load Disk">
|
<button title="Load Disk">
|
||||||
<i class="fas fa-folder-open" onClick={onOpenModal} />
|
<i className="fas fa-folder-open" onClick={onOpenModal} />
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
id={`disk-label${number}`}
|
id={`disk-label${number}`}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export const Drives = ({ io, sectors }: DrivesProps) => {
|
||||||
io.setSlot(6, disk2);
|
io.setSlot(6, disk2);
|
||||||
setDisk2(disk2);
|
setDisk2(disk2);
|
||||||
}
|
}
|
||||||
}, [io]);
|
}, [io, sectors]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -55,7 +55,7 @@ const InputFileChooser = ({
|
||||||
onChange(newFiles);
|
onChange(newFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, []);
|
}, [onChange]);
|
||||||
|
|
||||||
const extraProps = useMemo<ExtraProps>(() => {
|
const extraProps = useMemo<ExtraProps>(() => {
|
||||||
// Accept all of the given MIME types and extensions. An argument
|
// Accept all of the given MIME types and extensions. An argument
|
||||||
|
@ -131,14 +131,14 @@ const FilePickerChooser = ({
|
||||||
} finally {
|
} finally {
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [accept, busy, onChange]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedFilename(
|
setSelectedFilename(
|
||||||
fileHandlesRef.current?.length
|
fileHandlesRef.current?.length
|
||||||
? fileHandlesRef.current[0].name
|
? fileHandlesRef.current[0].name
|
||||||
: 'No file selected');
|
: 'No file selected');
|
||||||
}, [fileHandlesRef.current]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -184,7 +184,7 @@ export const FileChooser = ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onChange(handles);
|
onChange(handles);
|
||||||
}, []);
|
}, [onChange]);
|
||||||
|
|
||||||
const onChangeForPicker = useCallback((fileHandles: FileSystemFileHandle[]) => {
|
const onChangeForPicker = useCallback((fileHandles: FileSystemFileHandle[]) => {
|
||||||
const handles: FileSystemFileHandleLike[] = [];
|
const handles: FileSystemFileHandleLike[] = [];
|
||||||
|
@ -198,7 +198,7 @@ export const FileChooser = ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onChange(handles);
|
onChange(handles);
|
||||||
}, []);
|
}, [onChange]);
|
||||||
|
|
||||||
return control === 'picker'
|
return control === 'picker'
|
||||||
? (
|
? (
|
||||||
|
|
|
@ -50,7 +50,7 @@ export const FileModal = ({ disk2, number, onClose, isOpen }: FileModalProps) =>
|
||||||
const [handles, setHandles] = useState<FileSystemFileHandleLike[]>();
|
const [handles, setHandles] = useState<FileSystemFileHandleLike[]>();
|
||||||
const [filename, setFilename] = useState<string>();
|
const [filename, setFilename] = useState<string>();
|
||||||
|
|
||||||
const doCancel = useCallback(() => onClose(true), []);
|
const doCancel = useCallback(() => onClose(true), [onClose]);
|
||||||
|
|
||||||
const doOpen = useCallback(async () => {
|
const doOpen = useCallback(async () => {
|
||||||
const hashParts = getHashParts();
|
const hashParts = getHashParts();
|
||||||
|
@ -109,12 +109,12 @@ export const FileModal = ({ disk2, number, onClose, isOpen }: FileModalProps) =>
|
||||||
<div id="load-modal">
|
<div id="load-modal">
|
||||||
<select multiple onChange={doSelectCategory}>
|
<select multiple onChange={doSelectCategory}>
|
||||||
{categoryNames.map((name) => (
|
{categoryNames.map((name) => (
|
||||||
<option>{name}</option>
|
<option key={name}>{name}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<select multiple onChange={doSelectFilename}>
|
<select multiple onChange={doSelectFilename}>
|
||||||
{disks.map((disk) => (
|
{disks.map((disk) => (
|
||||||
<option value={disk.filename}>
|
<option key={disk.filename} value={disk.filename}>
|
||||||
{disk.name}
|
{disk.name}
|
||||||
{disk.disk ? ` - ${disk.disk}` : ''}
|
{disk.disk ? ` - ${disk.disk}` : ''}
|
||||||
</option>
|
</option>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
|
||||||
|
const README = 'https://github.com/whscullin/apple2js#readme';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Header component properties.
|
* Header component properties.
|
||||||
*/
|
*/
|
||||||
|
@ -15,7 +17,7 @@ export interface HeaderProps {
|
||||||
export const Header = ({ e }: HeaderProps) => {
|
export const Header = ({ e }: HeaderProps) => {
|
||||||
return (
|
return (
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<a href="https://github.com/whscullin/apple2js#readme" target="_blank">
|
<a href={README} rel="noreferrer" target="_blank">
|
||||||
<img src="img/badge.png" id="badge" />
|
<img src="img/badge.png" id="badge" />
|
||||||
</a>
|
</a>
|
||||||
<div id="subtitle">An Apple {e ? '//e' : ']['} Emulator in JavaScript</div>
|
<div id="subtitle">An Apple {e ? '//e' : ']['} Emulator in JavaScript</div>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { h, FunctionalComponent } from 'preact';
|
import { h, ComponentChildren } from 'preact';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience component for a nice beveled border.
|
* Convenience component for a nice beveled border.
|
||||||
*
|
*
|
||||||
* @returns Inset component
|
* @returns Inset component
|
||||||
*/
|
*/
|
||||||
export const Inset: FunctionalComponent = ({ children }) => (
|
export const Inset = ({ children }: { children: ComponentChildren }) => (
|
||||||
<div className="inset">
|
<div className="inset">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -209,7 +209,7 @@ export const Keyboard = ({ apple2, e }: KeyboardProps) => {
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
const rows = keys.map((row, idx) =>
|
const rows = keys.map((row, idx) =>
|
||||||
<div className={`row row${idx}`}>
|
<div key={idx} className={`row row${idx}`}>
|
||||||
{row.map(bindKey)}
|
{row.map(bindKey)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { h, FunctionalComponent } from 'preact';
|
import { h, ComponentChildren } from 'preact';
|
||||||
import { useCallback } from 'preact/hooks';
|
import { useCallback } from 'preact/hooks';
|
||||||
import { useHotKey } from './hooks/useHotKey';
|
import { useHotKey } from './hooks/useHotKey';
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ const modalFooterStyle = {
|
||||||
*
|
*
|
||||||
* @returns ModalOverlay component
|
* @returns ModalOverlay component
|
||||||
*/
|
*/
|
||||||
export const ModalOverlay: FunctionalComponent = ({ children }) => {
|
export const ModalOverlay = ({ children }: { children: ComponentChildren }) => {
|
||||||
return (
|
return (
|
||||||
<div style={modalOverlayStyle}>
|
<div style={modalOverlayStyle}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -80,7 +80,7 @@ export const ModalOverlay: FunctionalComponent = ({ children }) => {
|
||||||
*
|
*
|
||||||
* @returns ModalContent component
|
* @returns ModalContent component
|
||||||
*/
|
*/
|
||||||
export const ModalContent: FunctionalComponent = ({ children }) => {
|
export const ModalContent = ({ children }: { children: ComponentChildren }) => {
|
||||||
return (
|
return (
|
||||||
<div style={modalContentStyle}>
|
<div style={modalContentStyle}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -93,7 +93,7 @@ export const ModalContent: FunctionalComponent = ({ children }) => {
|
||||||
*
|
*
|
||||||
* @returns ModalFooter component
|
* @returns ModalFooter component
|
||||||
*/
|
*/
|
||||||
export const ModalFooter: FunctionalComponent = ({ children }) => {
|
export const ModalFooter = ({ children }: { children: ComponentChildren }) => {
|
||||||
return (
|
return (
|
||||||
<div style={modalFooterStyle}>
|
<div style={modalFooterStyle}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -160,6 +160,7 @@ export interface ModalProps {
|
||||||
onClose?: (closeBox?: boolean) => void;
|
onClose?: (closeBox?: boolean) => void;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
|
children: ComponentChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,12 +173,12 @@ export interface ModalProps {
|
||||||
* @param onClose Close callback
|
* @param onClose Close callback
|
||||||
* @returns Modal component
|
* @returns Modal component
|
||||||
*/
|
*/
|
||||||
export const Modal: FunctionalComponent<ModalProps> = ({
|
export const Modal = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
children,
|
children,
|
||||||
title,
|
title,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}: ModalProps) => {
|
||||||
return (
|
return (
|
||||||
isOpen ? (
|
isOpen ? (
|
||||||
<ModalOverlay>
|
<ModalOverlay>
|
||||||
|
|
|
@ -31,7 +31,7 @@ export const Mouse = ({ cpu, screen, io, slot }: MouseProps) => {
|
||||||
const mouse = new MouseCard(cpu, mouseUI);
|
const mouse = new MouseCard(cpu, mouseUI);
|
||||||
io.setSlot(slot, mouse);
|
io.setSlot(slot, mouse);
|
||||||
}
|
}
|
||||||
}, [cpu, io]);
|
}, [cpu, io, screen, slot]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ export interface ScreenProps {
|
||||||
export const Screen = ({ screen }: ScreenProps) => {
|
export const Screen = ({ screen }: ScreenProps) => {
|
||||||
return (
|
return (
|
||||||
<div id="display">
|
<div id="display">
|
||||||
<div class="overscan">
|
<div className="overscan">
|
||||||
<canvas id="screen" width="592" height="416" ref={screen} />
|
<canvas id="screen" width="592" height="416" ref={screen} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const Slinky = ({ io, slot }: SlinkyProps) => {
|
||||||
const slinky = new RAMFactor(1024 * 1024);
|
const slinky = new RAMFactor(1024 * 1024);
|
||||||
io.setSlot(slot, slinky);
|
io.setSlot(slot, slinky);
|
||||||
}
|
}
|
||||||
}, [io]);
|
}, [io, slot]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ export const ThunderClock = ({ io, slot }: ThunderClockProps) => {
|
||||||
const clock = new ThunderClockCard();
|
const clock = new ThunderClockCard();
|
||||||
io.setSlot(slot, clock);
|
io.setSlot(slot, clock);
|
||||||
}
|
}
|
||||||
}, [io]);
|
}, [io, slot]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
1172
package-lock.json
generated
1172
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -40,7 +40,9 @@
|
||||||
"ajv": "^6.12.0",
|
"ajv": "^6.12.0",
|
||||||
"babel-jest": "^27.2.4",
|
"babel-jest": "^27.2.4",
|
||||||
"canvas": "^2.8.0",
|
"canvas": "^2.8.0",
|
||||||
"eslint": "^8.15.0",
|
"eslint": "^8.16.0",
|
||||||
|
"eslint-plugin-react": "^7.30.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.5.0",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"jest": "^27.2.4",
|
"jest": "^27.2.4",
|
||||||
"jest-image-snapshot": "^4.5.1",
|
"jest-image-snapshot": "^4.5.1",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user