mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
parent
203d89b8d9
commit
303838f63d
@ -162,7 +162,7 @@ th {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.inset button, .modal-overlay button {
|
||||
.inset button {
|
||||
min-width: 36px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
@ -5,8 +5,7 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<link rel="shortcut icon" href="img/logoicon.png" />
|
||||
<link rel="stylesheet" type="text/css" href="css/apple2.css" />
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.2/css/all.css" />
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.1.1/css/all.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
@ -2,6 +2,7 @@ module.exports = {
|
||||
'moduleNameMapper': {
|
||||
'^js/(.*)': '<rootDir>/js/$1',
|
||||
'^test/(.*)': '<rootDir>/test/$1',
|
||||
'\\.css$': 'identity-obj-proxy',
|
||||
},
|
||||
'roots': [
|
||||
'js/',
|
||||
|
@ -7,6 +7,8 @@ import { SYSTEM_TYPE_APPLE2E } from '../ui/system';
|
||||
import { SCREEN_GL } from '../ui/screen';
|
||||
import { defaultSystem, systemTypes } from './util/systems';
|
||||
|
||||
import './css/App.module.css';
|
||||
|
||||
/**
|
||||
* Top level application component, provides the parameters
|
||||
* needed by the Apple2 component to bootstrap itself.
|
||||
|
@ -14,6 +14,8 @@ import { Slinky } from './Slinky';
|
||||
import { ThunderClock } from './ThunderClock';
|
||||
import { ErrorModal } from './ErrorModal';
|
||||
|
||||
import styles from './css/Apple2.module.css';
|
||||
|
||||
/**
|
||||
* Interface for the Apple2 component.
|
||||
*/
|
||||
@ -64,7 +66,7 @@ export const Apple2 = (props: Apple2Props) => {
|
||||
}, [props]);
|
||||
|
||||
return (
|
||||
<div className={cs('outer', { apple2e: e})}>
|
||||
<div className={cs(styles.outer, { apple2e: e })}>
|
||||
<Screen screen={screen} />
|
||||
<Mouse cpu={cpu} screen={screen} io={io} slot={4} />
|
||||
<Slinky io={io} slot={4} />
|
||||
|
@ -3,6 +3,8 @@ import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { Apple2 as Apple2Impl } from '../apple2';
|
||||
import type { Stats } from '../apple2';
|
||||
|
||||
import styles from './css/CPUMeter.module.css';
|
||||
|
||||
/**
|
||||
* Interface for CPUMeter.
|
||||
*/
|
||||
@ -63,7 +65,7 @@ export const CPUMeter = ({ apple2 }: CPUMeterProps) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div id="khz" onClick={onClick}>
|
||||
<div className={styles.khz} onClick={onClick}>
|
||||
{mode === 0 && `${khz} Khz`}
|
||||
{mode === 1 && `${fps} fps`}
|
||||
{mode === 2 && `${rps} rps`}
|
||||
|
@ -13,6 +13,8 @@ import { JoyStick } from '../ui/joystick';
|
||||
import { Screen, SCREEN_FULL_PAGE } from '../ui/screen';
|
||||
import { System } from '../ui/system';
|
||||
|
||||
import styles from './css/ControlStrip.module.css';
|
||||
|
||||
const README = 'https://github.com/whscullin/apple2js#readme';
|
||||
|
||||
interface ControlStripProps {
|
||||
@ -77,7 +79,7 @@ export const ControlStrip = ({ apple2, e }: ControlStripProps) => {
|
||||
useHotKey('F12', doReset);
|
||||
|
||||
return (
|
||||
<div id="reset-row">
|
||||
<div className={styles.resetRow}>
|
||||
<OptionsModal isOpen={showOptions} onClose={doCloseOptions} />
|
||||
<Inset>
|
||||
<CPUMeter apple2={apple2} />
|
||||
@ -88,7 +90,7 @@ export const ControlStrip = ({ apple2, e }: ControlStripProps) => {
|
||||
<ControlButton onClick={doShowOptions} title="Options (F4)" icon="cog" />
|
||||
</Inset>
|
||||
{e && (
|
||||
<div id="reset" onClick={doReset}>
|
||||
<div className={styles.reset} onClick={doReset}>
|
||||
Reset
|
||||
</div>
|
||||
)}
|
||||
|
@ -7,6 +7,8 @@ import { loadJSON, loadHttpFile, getHashParts } from './util/files';
|
||||
import { ErrorModal } from './ErrorModal';
|
||||
import { useHash } from './hooks/useHash';
|
||||
|
||||
import styles from './css/DiskII.module.css';
|
||||
|
||||
/**
|
||||
* Storage structure for Disk II state returned via callbacks.
|
||||
*/
|
||||
@ -73,20 +75,14 @@ export const DiskII = ({ disk2, number, on, name, side }: DiskIIProps) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="disk">
|
||||
<div className={styles.disk}>
|
||||
<FileModal disk2={disk2} number={number} onClose={doClose} isOpen={modalOpen} />
|
||||
<ErrorModal error={error} setError={setError} />
|
||||
<div
|
||||
id={`disk${number}`}
|
||||
className={cs('disk-light', { on })}
|
||||
/>
|
||||
<div className={cs(styles.diskLight, { on })} />
|
||||
<button title="Load Disk">
|
||||
<i className="fas fa-folder-open" onClick={onOpenModal} />
|
||||
</button>
|
||||
<div
|
||||
id={`disk-label${number}`}
|
||||
className="disk-label"
|
||||
>
|
||||
<div className={styles.diskLabel}>
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,6 +2,8 @@ import { h } from 'preact';
|
||||
import { useCallback } from 'preact/hooks';
|
||||
import { Modal, ModalContent, ModalFooter } from './Modal';
|
||||
|
||||
import styles from './css/ErrorModal.module.css';
|
||||
|
||||
export interface ErrorProps {
|
||||
error: unknown | undefined;
|
||||
setError: (error: string | undefined) => void;
|
||||
@ -30,7 +32,7 @@ export const ErrorModal = ({ error, setError }: ErrorProps) => {
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalContent>
|
||||
<div style={{ width: 320, fontSize: '1.1em', padding: '5px 11px' }}>
|
||||
<div className={styles.errorModal}>
|
||||
{errorStr}
|
||||
</div>
|
||||
</ModalContent>
|
||||
|
@ -11,6 +11,8 @@ import { noAwait } from './util/promises';
|
||||
import { useHash } from './hooks/useHash';
|
||||
import { FileChooser, FilePickerAcceptType } from './FileChooser';
|
||||
|
||||
import styles from './css/FileModal.module.css';
|
||||
|
||||
const DISK_TYPES: FilePickerAcceptType[] = [
|
||||
{
|
||||
description: 'Disk Images',
|
||||
@ -106,7 +108,7 @@ export const FileModal = ({ disk2, number, onClose, isOpen }: FileModalProps) =>
|
||||
<>
|
||||
<Modal title="Open File" isOpen={isOpen}>
|
||||
<ModalContent>
|
||||
<div id="load-modal">
|
||||
<div className={styles.loadModal}>
|
||||
<select multiple onChange={doSelectCategory}>
|
||||
{categoryNames.map((name) => (
|
||||
<option key={name}>{name}</option>
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
import styles from './css/Header.module.css';
|
||||
|
||||
const README = 'https://github.com/whscullin/apple2js#readme';
|
||||
|
||||
/**
|
||||
@ -16,11 +18,13 @@ export interface HeaderProps {
|
||||
*/
|
||||
export const Header = ({ e }: HeaderProps) => {
|
||||
return (
|
||||
<div id="header">
|
||||
<div className={styles.header}>
|
||||
<a href={README} rel="noreferrer" target="_blank">
|
||||
<img src="img/badge.png" id="badge" />
|
||||
<img src="img/badge.png" className={styles.badge} />
|
||||
</a>
|
||||
<div id="subtitle">An Apple {e ? '//e' : ']['} Emulator in JavaScript</div>
|
||||
<div className={styles.subtitle}>
|
||||
An Apple {e ? '//e' : ']['} Emulator in JavaScript
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { h, ComponentChildren } from 'preact';
|
||||
|
||||
import styles from './css/Inset.module.css';
|
||||
|
||||
/**
|
||||
* Convenience component for a nice beveled border.
|
||||
*
|
||||
* @returns Inset component
|
||||
*/
|
||||
export const Inset = ({ children }: { children: ComponentChildren }) => (
|
||||
<div className="inset">
|
||||
<div className={styles.inset}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -10,6 +10,8 @@ import {
|
||||
keysAsTuples
|
||||
} from './util/keyboard';
|
||||
|
||||
import styles from './css/Keyboard.module.css';
|
||||
|
||||
/**
|
||||
* Convenience function for massaging key labels for upper
|
||||
* and lower case
|
||||
@ -21,7 +23,7 @@ const buildLabel = (key: string) => {
|
||||
const small = key.length > 1 && !key.startsWith('&');
|
||||
return (
|
||||
<span
|
||||
className={cs({ small })}
|
||||
className={cs({[styles.small]: small})}
|
||||
dangerouslySetInnerHTML={{ __html: key }}
|
||||
/>
|
||||
);
|
||||
@ -60,19 +62,21 @@ export const Key = ({
|
||||
}: KeyProps) => {
|
||||
const keyName = lower.replace(/[&#;]/g, '');
|
||||
const center =
|
||||
lower === 'LOCK' ?
|
||||
'v-center2' :
|
||||
(upper === lower && upper.length > 1 ?
|
||||
'v-center'
|
||||
: ''
|
||||
);
|
||||
lower === 'LOCK'
|
||||
? styles.vCenter2
|
||||
: (upper === lower && upper.length > 1)
|
||||
? styles.vCenter
|
||||
: '';
|
||||
return (
|
||||
<div
|
||||
className={cs(
|
||||
'key',
|
||||
`key-${keyName}`,
|
||||
styles.key,
|
||||
styles[`key-${keyName}`],
|
||||
center,
|
||||
{ pressed, active },
|
||||
{
|
||||
[styles.pressed]: pressed,
|
||||
[styles.active]: active,
|
||||
},
|
||||
)}
|
||||
data-key1={lower}
|
||||
data-key2={upper}
|
||||
@ -209,13 +213,13 @@ export const Keyboard = ({ apple2, e }: KeyboardProps) => {
|
||||
/>;
|
||||
|
||||
const rows = keys.map((row, idx) =>
|
||||
<div key={idx} className={`row row${idx}`}>
|
||||
<div key={idx} className={cs(styles.row, styles[`row${idx}`])}>
|
||||
{row.map(bindKey)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div id="keyboard" style={{ marginLeft: e ? 0 : 15 }}>
|
||||
<div className={styles.keyboard}>
|
||||
{rows}
|
||||
</div>
|
||||
);
|
||||
|
@ -3,64 +3,7 @@ import { createPortal } from 'preact/compat';
|
||||
import { useCallback } from 'preact/hooks';
|
||||
import { useHotKey } from './hooks/useHotKey';
|
||||
|
||||
/**
|
||||
* Temporary JS styling while I figure out how I really want
|
||||
* to do it.
|
||||
*/
|
||||
const modalOverlayStyle = {
|
||||
position: 'fixed',
|
||||
left: '0',
|
||||
right: '0',
|
||||
top: '0',
|
||||
bottom: '0',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'rgba(0,0,0,0.6)',
|
||||
zIndex: 1,
|
||||
};
|
||||
|
||||
const modalStyle = {
|
||||
backgroundColor: '#c4c1a0',
|
||||
padding: '10px',
|
||||
maxHeight: '100vh',
|
||||
borderRadius: '4px',
|
||||
overflowY: 'auto',
|
||||
boxSizing: 'border-box',
|
||||
};
|
||||
|
||||
const modalHeaderStyle = {
|
||||
display: 'flex',
|
||||
fontSize: '14px',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
background: '#44372C',
|
||||
color: '#fff',
|
||||
padding: '5px 11px',
|
||||
border: '1px outset #66594E',
|
||||
borderRadius: '3px',
|
||||
};
|
||||
|
||||
const modalTitleStyle = {
|
||||
marginTop: 0,
|
||||
marginBottom: 0,
|
||||
fontWeight: 600,
|
||||
fontSize: '1.25rem',
|
||||
lineHeight: 1.25,
|
||||
color: '#fff',
|
||||
boxSizing: 'border-box',
|
||||
};
|
||||
|
||||
const modalContentStyle = {
|
||||
marginTop: '10px',
|
||||
marginBottom: '10px',
|
||||
lineHeight: 1.5,
|
||||
color: '#000'
|
||||
};
|
||||
|
||||
const modalFooterStyle = {
|
||||
textAlign: 'right'
|
||||
};
|
||||
import styles from './css/Modal.module.css';
|
||||
|
||||
/**
|
||||
* ModalOverlay creates a semi-transparent overlay in which the
|
||||
@ -70,7 +13,7 @@ const modalFooterStyle = {
|
||||
*/
|
||||
export const ModalOverlay = ({ children }: { children: ComponentChildren }) => {
|
||||
return (
|
||||
<div style={modalOverlayStyle} className="modal-overlay">
|
||||
<div className={styles.modalOverlay}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@ -83,7 +26,7 @@ export const ModalOverlay = ({ children }: { children: ComponentChildren }) => {
|
||||
*/
|
||||
export const ModalContent = ({ children }: { children: ComponentChildren }) => {
|
||||
return (
|
||||
<div style={modalContentStyle}>
|
||||
<div className={styles.modalContent}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@ -96,7 +39,7 @@ export const ModalContent = ({ children }: { children: ComponentChildren }) => {
|
||||
*/
|
||||
export const ModalFooter = ({ children }: { children: ComponentChildren }) => {
|
||||
return (
|
||||
<footer style={modalFooterStyle}>
|
||||
<footer className={styles.modalFooter}>
|
||||
{children}
|
||||
</footer>
|
||||
);
|
||||
@ -148,9 +91,9 @@ export interface ModalHeaderProps {
|
||||
*/
|
||||
export const ModalHeader = ({ onClose, title, icon }: ModalHeaderProps) => {
|
||||
return (
|
||||
<header style={modalHeaderStyle}>
|
||||
<span style={modalTitleStyle}>
|
||||
{icon && <i className={`fas fa-${icon}`} role="img" />}
|
||||
<header className={styles.modalHeader}>
|
||||
<span className={styles.modalTitle}>
|
||||
{icon && <i className={`fa-solid fa-${icon}`} role="img" />}
|
||||
{' '}
|
||||
{title}
|
||||
</span>
|
||||
@ -189,7 +132,7 @@ export const Modal = ({
|
||||
return (
|
||||
isOpen ? createPortal((
|
||||
<ModalOverlay>
|
||||
<div style={modalStyle} role="dialog">
|
||||
<div className={styles.modal} role="dialog">
|
||||
{title && <ModalHeader title={title} {...props} />}
|
||||
{children}
|
||||
</div>
|
||||
|
@ -11,6 +11,8 @@ import {
|
||||
SelectOption,
|
||||
} from '../options';
|
||||
|
||||
import styles from './css/OptionsModal.module.css';
|
||||
|
||||
/**
|
||||
* Boolean property interface
|
||||
*/
|
||||
@ -153,7 +155,7 @@ export const OptionsModal = ({ isOpen, onClose }: OptionsModalProps) => {
|
||||
return (
|
||||
<Modal title="Options" isOpen={isOpen} onClose={onClose}>
|
||||
<ModalContent>
|
||||
<div id="options-modal">
|
||||
<div className={styles.optionsModal}>
|
||||
{sections.map(makeSection)}
|
||||
</div>
|
||||
<i>* Reload page to take effect</i>
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { h, Ref } from 'preact';
|
||||
|
||||
import styles from './css/Screen.module.css';
|
||||
|
||||
/**
|
||||
* Screen properties
|
||||
*/
|
||||
@ -16,9 +18,14 @@ export interface ScreenProps {
|
||||
*/
|
||||
export const Screen = ({ screen }: ScreenProps) => {
|
||||
return (
|
||||
<div id="display">
|
||||
<div className="overscan">
|
||||
<canvas id="screen" width="592" height="416" ref={screen} />
|
||||
<div className={styles.display}>
|
||||
<div className={styles.overscan}>
|
||||
<canvas
|
||||
className={styles.screen}
|
||||
width="592"
|
||||
height="416"
|
||||
ref={screen}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
34
js/components/css/App.module.css
Normal file
34
js/components/css/App.module.css
Normal file
@ -0,0 +1,34 @@
|
||||
body {
|
||||
margin: 16px 0;
|
||||
font-size: 14px;
|
||||
background-color: #c4c1a0; /* Pantone 453 */
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
:global(.full-page) {
|
||||
background: black;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #44372C;
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
border: 1px outset #66594E;
|
||||
border-radius: 3px;
|
||||
font-size: 15px;
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #55473D;
|
||||
border: 1px outset #66594E;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: #22150A;
|
||||
border: 1px outset #44372C;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
4
js/components/css/Apple2.module.css
Normal file
4
js/components/css/Apple2.module.css
Normal file
@ -0,0 +1,4 @@
|
||||
.outer {
|
||||
margin: auto;
|
||||
width: 620px;
|
||||
}
|
14
js/components/css/CPUMeter.module.css
Normal file
14
js/components/css/CPUMeter.module.css
Normal file
@ -0,0 +1,14 @@
|
||||
.khz {
|
||||
margin-right: 4px;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
font-family: Courier;
|
||||
font-size: 12px;
|
||||
width: 60px;
|
||||
height: 15px;
|
||||
text-align: right;
|
||||
background: #000;
|
||||
color: #0f0;
|
||||
border: 2px inset #888;
|
||||
border-radius: 4px;
|
||||
}
|
41
js/components/css/ControlStrip.module.css
Normal file
41
js/components/css/ControlStrip.module.css
Normal file
@ -0,0 +1,41 @@
|
||||
.reset {
|
||||
background: #44372C;
|
||||
border-left: 3px solid #65594D;
|
||||
border-top: 3px solid #65594D;
|
||||
border-right: 3px solid #110E0D;
|
||||
border-bottom: 3px solid #110E0D;
|
||||
/* border: 5px outset #66594E; */
|
||||
border-radius: 3px;
|
||||
color: white;
|
||||
font: 9px Helvetica;
|
||||
height: 42px;
|
||||
padding: 0;
|
||||
margin-left: 10px;
|
||||
width: 42px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.reset:hover {
|
||||
background: #44372C;
|
||||
border: 3px outset #66594E;
|
||||
}
|
||||
|
||||
.reset:active {
|
||||
background-color: #22150A;
|
||||
border-left: 3px solid #44372C;
|
||||
border-top: 3px solid #44372C;
|
||||
border-right: 3px solid #000000;
|
||||
border-bottom: 3px solid #000000;
|
||||
}
|
||||
|
||||
:global(.full-page) .reset {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.resetRow {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin: 10px 0;
|
||||
}
|
29
js/components/css/DiskII.module.css
Normal file
29
js/components/css/DiskII.module.css
Normal file
@ -0,0 +1,29 @@
|
||||
.disk {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.diskLight {
|
||||
margin: 5px;
|
||||
background-image: url(../../../css/red-off-16.png);
|
||||
flex-shrink: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.diskLight.on {
|
||||
background-image: url(../../../css/red-on-16.png);
|
||||
}
|
||||
|
||||
.diskLabel {
|
||||
color: #000;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
margin-right: 0.5em;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
flex-grow: 1;
|
||||
}
|
5
js/components/css/ErrorModal.module.css
Normal file
5
js/components/css/ErrorModal.module.css
Normal file
@ -0,0 +1,5 @@
|
||||
.errorModal {
|
||||
width: 320px;
|
||||
font-size: 1.1em;
|
||||
padding: 5px 11px;
|
||||
}
|
5
js/components/css/FileModal.module.css
Normal file
5
js/components/css/FileModal.module.css
Normal file
@ -0,0 +1,5 @@
|
||||
.loadModal select {
|
||||
width: 250px;
|
||||
height: 300px;
|
||||
font-size: 14px;
|
||||
}
|
30
js/components/css/Header.module.css
Normal file
30
js/components/css/Header.module.css
Normal file
@ -0,0 +1,30 @@
|
||||
.header {
|
||||
width: 580px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.header img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
:global(.full-page) .header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.badge {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
padding: 3px 0 0 10;
|
||||
color: black;
|
||||
font-family: "Adobe Garamond Pro",Garamond,Times;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.motter {
|
||||
font-family: "Motter Tektura";
|
||||
font-size: 12px;
|
||||
}
|
18
js/components/css/Inset.module.css
Normal file
18
js/components/css/Inset.module.css
Normal file
@ -0,0 +1,18 @@
|
||||
.inset {
|
||||
border-radius: 6px;
|
||||
border: 3px inset #f0edd0;
|
||||
padding: 6px;
|
||||
margin: 10px auto;
|
||||
display: flex;
|
||||
width: 604px;
|
||||
}
|
||||
|
||||
:global(.full-page) .inset {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.inset button {
|
||||
width: 36px;
|
||||
margin: 0 2px;
|
||||
}
|
210
js/components/css/Keyboard.module.css
Normal file
210
js/components/css/Keyboard.module.css
Normal file
@ -0,0 +1,210 @@
|
||||
.keyboard {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
:global(.apple2e) .keyboard {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 42px;
|
||||
font-family: Helvetica;
|
||||
width: 570px;
|
||||
}
|
||||
|
||||
:global(.apple2e) .row {
|
||||
width: 610px;
|
||||
}
|
||||
|
||||
.row0 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.row2 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.row3 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.row4 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
:global(.apple2e) .row0 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
:global(.apple2e) .row2 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
:global(.apple2e) .row3 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
:global(.apple2e) .row4 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.key {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: #44372C;
|
||||
color: white;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
border-left: 5px solid #65594D;
|
||||
border-top: 5px solid #65594D;
|
||||
border-right: 5px solid #110E0D;
|
||||
border-bottom: 5px solid #110E0D;
|
||||
/* border: 5px outset #66594E; */
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pressed {
|
||||
background-color: #22150A;
|
||||
border-left: 5px solid #44372C;
|
||||
border-top: 5px solid #44372C;
|
||||
border-right: 5px solid #000000;
|
||||
border-bottom: 5px solid #000000;
|
||||
/* border: 5px outset #44372C; */
|
||||
}
|
||||
|
||||
.key div {
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vCenter div {
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.vCenter2 div {
|
||||
bottom: 5px;
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
.key span {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.key .small {
|
||||
font-size: 7px;
|
||||
}
|
||||
|
||||
.key-SHIFT {
|
||||
width: 53px;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #4f4;
|
||||
}
|
||||
|
||||
.key-RETURN {
|
||||
width: 52px;
|
||||
}
|
||||
|
||||
.key-DELETE {
|
||||
width: 47px;
|
||||
}
|
||||
|
||||
.key-TAB {
|
||||
width: 47px;
|
||||
}
|
||||
|
||||
.key-POWER {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border: 2px groove black;
|
||||
background-color: #ffd;
|
||||
color: black;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.key-POWER div {
|
||||
bottom: 15px;
|
||||
}
|
||||
|
||||
.key-nbsp {
|
||||
margin-left: 62px;
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
:global(.apple2e) .key-CTRL {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
:global(.apple2e) .key-RETURN {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
:global(.apple2e) .key-SHIFT {
|
||||
width: 81px;
|
||||
}
|
||||
|
||||
:global(.apple2e) .key-nbsp {
|
||||
margin-left: 0;
|
||||
width: 215px;
|
||||
}
|
||||
|
||||
.key-POW {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border: 2px groove black;
|
||||
background-color: #ffd;
|
||||
color: black;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.key-POW div {
|
||||
bottom: 15px;
|
||||
}
|
||||
|
||||
.key-POW span {
|
||||
background: #4f4;
|
||||
color: #4f4;
|
||||
}
|
||||
|
||||
.key-OPEN_APPLE div {
|
||||
background-image: url(../../../img/open-apple24.png);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
bottom: 1px;
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
.key-OPEN_APPLE span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.key-OPEN_APPLE.active div {
|
||||
background-image: url(../../../img/open-apple24-green.png);
|
||||
}
|
||||
|
||||
.key-CLOSED_APPLE div {
|
||||
background-image: url(../../../img/closed-apple24.png);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
bottom: 1px;
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
.key-CLOSED_APPLE.active div {
|
||||
background-image: url(../../../img/closed-apple24-green.png);
|
||||
}
|
||||
|
||||
.key-CLOSED_APPLE span {
|
||||
display: none;
|
||||
}
|
63
js/components/css/Modal.module.css
Normal file
63
js/components/css/Modal.module.css
Normal file
@ -0,0 +1,63 @@
|
||||
.modalOverlay {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0,0,0,0.6);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.modalOverlay button {
|
||||
min-width: 36px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background-color: #c4c1a0;
|
||||
padding: 10px;
|
||||
max-height: 100vh;
|
||||
border-radius: 4px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.modalHeader {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #44372C;
|
||||
color: #fff;
|
||||
padding: 5px 11px;
|
||||
border: 1px outset #66594E;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.modalHeader button {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.modalTitle {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.25;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.5;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.modalFooter {
|
||||
text-align: right;
|
||||
}
|
11
js/components/css/OptionsModal.module.css
Normal file
11
js/components/css/OptionsModal.module.css
Normal file
@ -0,0 +1,11 @@
|
||||
.optionsModal {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.optionsModal h3 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.optionsModal li {
|
||||
list-style-type: none;
|
||||
}
|
105
js/components/css/Screen.module.css
Normal file
105
js/components/css/Screen.module.css
Normal file
@ -0,0 +1,105 @@
|
||||
|
||||
:global(.mono) {
|
||||
filter: url('#green');
|
||||
}
|
||||
|
||||
.display {
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
:global(.full-page) .display {
|
||||
width: 100vw;
|
||||
height: 68.5714vw; /* 384px / 560px * 100% */
|
||||
max-height: 100vh;
|
||||
max-width: 145.83vh; /* 560px / 384px * 100% */
|
||||
padding: 0;
|
||||
border: 0;
|
||||
position: fixed;
|
||||
top:0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
:global(.full-page) .overscan {
|
||||
margin: initial;
|
||||
padding: 0;
|
||||
width: initial;
|
||||
height: 384px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.overscan {
|
||||
margin: auto;
|
||||
position: relative;
|
||||
background-color: black;
|
||||
width: 592px;
|
||||
height: 416px;
|
||||
border: 6px inset #f0edd0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
:global(.full-page) .overscan {
|
||||
margin: initial;
|
||||
padding: 0;
|
||||
width: initial;
|
||||
height: 384px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:global(.scanlines):after {
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
background-image: repeating-linear-gradient(to bottom, transparent 0, transparent 1px, rgba(0,0,0,0.5) 1px, rgba(0,0,0,0.5) 2px);
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
:global(.full-page) :global(.scanlines):after {
|
||||
background-image: repeating-linear-gradient(to bottom, transparent 0, transparent 0.25vh, rgba(0,0,0,0.5) 0.25vh, rgba(0,0,0,0.5) 0.5vh);
|
||||
}
|
||||
|
||||
.screen {
|
||||
cursor: crosshair;
|
||||
-moz-image-rendering: -moz-crisp-edges;
|
||||
-webkit-image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: optimizeSpeed;
|
||||
width: 592px;
|
||||
height: 416px;
|
||||
touch-action: manipulation;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.screen.mouseMode {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.screen:-webkit-full-screen {
|
||||
background-color: black;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
:global(.full-page) .screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
@ -93,7 +93,7 @@ export class JoyStick implements OptionHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
const s = document.querySelector<HTMLDivElement>('#screen')!;
|
||||
const s = document.querySelector<HTMLDivElement>('canvas')!;
|
||||
const offset = s.getBoundingClientRect();
|
||||
let x = (evt.pageX - offset.left) / s.clientWidth;
|
||||
let y = (evt.pageY - offset.top) / s.clientHeight;
|
||||
|
374
package-lock.json
generated
374
package-lock.json
generated
@ -32,16 +32,19 @@
|
||||
"ajv": "^6.12.0",
|
||||
"babel-jest": "^27.2.4",
|
||||
"canvas": "^2.8.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"eslint": "^8.16.0",
|
||||
"eslint-plugin-jest": "^26.4.3",
|
||||
"eslint-plugin-react": "^7.30.0",
|
||||
"eslint-plugin-react-hooks": "^4.5.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^27.2.4",
|
||||
"jest-image-snapshot": "^4.5.1",
|
||||
"node-forge": "^1.3.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-loader": "^9.3.0",
|
||||
"typescript": "^4.6.4",
|
||||
@ -5230,6 +5233,47 @@
|
||||
"source-map-resolve": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-loader": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz",
|
||||
"integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"icss-utils": "^5.1.0",
|
||||
"postcss": "^8.4.7",
|
||||
"postcss-modules-extract-imports": "^3.0.0",
|
||||
"postcss-modules-local-by-default": "^4.0.0",
|
||||
"postcss-modules-scope": "^3.0.0",
|
||||
"postcss-modules-values": "^4.0.0",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-loader/node_modules/semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/css.escape": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
|
||||
@ -5245,6 +5289,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/cssom": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
||||
@ -7026,6 +7082,12 @@
|
||||
"integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/harmony-reflect": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
|
||||
"integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
@ -7270,6 +7332,30 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/icss-utils": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
|
||||
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/identity-obj-proxy": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
|
||||
"integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"harmony-reflect": "^1.4.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
@ -11512,6 +11598,18 @@
|
||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
@ -12008,6 +12106,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
|
||||
@ -12097,6 +12201,108 @@
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-modules-extract-imports": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
||||
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-modules-local-by-default": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
|
||||
"integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"icss-utils": "^5.0.0",
|
||||
"postcss-selector-parser": "^6.0.2",
|
||||
"postcss-value-parser": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-modules-scope": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
|
||||
"integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "^6.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-modules-values": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
|
||||
"integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"icss-utils": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.7.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.7.1.tgz",
|
||||
@ -12932,6 +13138,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-resolve": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
|
||||
@ -13185,6 +13400,22 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/style-loader": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
|
||||
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
@ -18655,12 +18886,45 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"css-loader": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz",
|
||||
"integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"icss-utils": "^5.1.0",
|
||||
"postcss": "^8.4.7",
|
||||
"postcss-modules-extract-imports": "^3.0.0",
|
||||
"postcss-modules-local-by-default": "^4.0.0",
|
||||
"postcss-modules-scope": "^3.0.0",
|
||||
"postcss-modules-values": "^4.0.0",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"css.escape": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
|
||||
"integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
|
||||
"dev": true
|
||||
},
|
||||
"cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"cssom": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
||||
@ -20006,6 +20270,12 @@
|
||||
"integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
|
||||
"dev": true
|
||||
},
|
||||
"harmony-reflect": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
|
||||
"integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
@ -20200,6 +20470,22 @@
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"icss-utils": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
|
||||
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"identity-obj-proxy": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
|
||||
"integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"harmony-reflect": "^1.4.6"
|
||||
}
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
@ -23413,6 +23699,12 @@
|
||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
|
||||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
@ -23777,6 +24069,12 @@
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
|
||||
@ -23846,6 +24144,69 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"postcss-modules-extract-imports": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
||||
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"postcss-modules-local-by-default": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
|
||||
"integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"icss-utils": "^5.0.0",
|
||||
"postcss-selector-parser": "^6.0.2",
|
||||
"postcss-value-parser": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"postcss-modules-scope": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
|
||||
"integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"postcss-selector-parser": "^6.0.4"
|
||||
}
|
||||
},
|
||||
"postcss-modules-values": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
|
||||
"integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"icss-utils": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.7.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.7.1.tgz",
|
||||
@ -24498,6 +24859,12 @@
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-resolve": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
|
||||
@ -24705,6 +25072,13 @@
|
||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||
"dev": true
|
||||
},
|
||||
"style-loader": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
|
||||
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
|
@ -41,16 +41,19 @@
|
||||
"ajv": "^6.12.0",
|
||||
"babel-jest": "^27.2.4",
|
||||
"canvas": "^2.8.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"eslint": "^8.16.0",
|
||||
"eslint-plugin-jest": "^26.4.3",
|
||||
"eslint-plugin-react": "^7.30.0",
|
||||
"eslint-plugin-react-hooks": "^4.5.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^27.2.4",
|
||||
"jest-image-snapshot": "^4.5.1",
|
||||
"node-forge": "^1.3.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-loader": "^9.3.0",
|
||||
"typescript": "^4.6.4",
|
||||
|
@ -39,6 +39,7 @@
|
||||
"include": [
|
||||
"js/**/*",
|
||||
"test/**/*",
|
||||
"types/**/*",
|
||||
"*.config.js"
|
||||
]
|
||||
}
|
||||
|
4
types/styles.d.ts
vendored
Normal file
4
types/styles.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module '*.module.css' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
@ -15,6 +15,28 @@ const baseConfig = {
|
||||
],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
import: false,
|
||||
modules: true
|
||||
}
|
||||
}
|
||||
],
|
||||
include: /\.module\.css$/
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader'
|
||||
],
|
||||
exclude: /\.module\.css$/
|
||||
}
|
||||
],
|
||||
},
|
||||
output: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user