Switch to scss, better scope rules (#170)
Restrict element types and global styling to improve embedability
This commit is contained in:
parent
3d59142ee9
commit
9173bff1ba
|
@ -10,6 +10,12 @@
|
||||||
<link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" />
|
<link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" />
|
||||||
<link rel="shortcut icon" href="img/logoicon.png" />
|
<link rel="shortcut icon" href="img/logoicon.png" />
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.1.1/css/all.css" />
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.1.1/css/all.css" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #c4c1a0;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -3,6 +3,7 @@ module.exports = {
|
||||||
'^js/(.*)': '<rootDir>/js/$1',
|
'^js/(.*)': '<rootDir>/js/$1',
|
||||||
'^test/(.*)': '<rootDir>/test/$1',
|
'^test/(.*)': '<rootDir>/test/$1',
|
||||||
'\\.css$': 'identity-obj-proxy',
|
'\\.css$': 'identity-obj-proxy',
|
||||||
|
'\\.scss$': 'identity-obj-proxy',
|
||||||
},
|
},
|
||||||
'roots': [
|
'roots': [
|
||||||
'js/',
|
'js/',
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'preact/debug';
|
import 'preact/debug';
|
||||||
|
import cs from 'classnames';
|
||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
import { Apple2 } from './Apple2';
|
import { Apple2 } from './Apple2';
|
||||||
|
@ -7,7 +8,8 @@ import { SYSTEM_TYPE_APPLE2E } from '../ui/system';
|
||||||
import { SCREEN_GL } from '../ui/screen';
|
import { SCREEN_GL } from '../ui/screen';
|
||||||
import { defaultSystem, systemTypes } from './util/systems';
|
import { defaultSystem, systemTypes } from './util/systems';
|
||||||
|
|
||||||
import styles from './css/App.module.css';
|
import styles from './css/App.module.scss';
|
||||||
|
import componentStyles from './css/Components.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top level application component, provides the parameters
|
* Top level application component, provides the parameters
|
||||||
|
@ -26,7 +28,7 @@ export const App = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={cs(styles.container, componentStyles.components)}>
|
||||||
<Header e={system.e} />
|
<Header e={system.e} />
|
||||||
<Apple2
|
<Apple2
|
||||||
gl={gl}
|
gl={gl}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { ThunderClock } from './ThunderClock';
|
||||||
import { Videoterm } from './Videoterm';
|
import { Videoterm } from './Videoterm';
|
||||||
import { spawn, Ready } from './util/promises';
|
import { spawn, Ready } from './util/promises';
|
||||||
|
|
||||||
import styles from './css/Apple2.module.css';
|
import styles from './css/Apple2.module.scss';
|
||||||
import { SupportedSectors } from 'js/formats/types';
|
import { SupportedSectors } from 'js/formats/types';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { DiskDragTarget } from './DiskDragTarget';
|
||||||
import { DownloadModal } from './DownloadModal';
|
import { DownloadModal } from './DownloadModal';
|
||||||
import { ErrorModal } from './ErrorModal';
|
import { ErrorModal } from './ErrorModal';
|
||||||
|
|
||||||
import styles from './css/BlockDisk.module.css';
|
import styles from './css/BlockDisk.module.scss';
|
||||||
|
import { ControlButton } from './ControlButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage structure for drive state returned via callbacks.
|
* Storage structure for drive state returned via callbacks.
|
||||||
|
@ -84,12 +85,8 @@ export const BlockDisk = ({ smartPort, number, on, name }: BlockDiskProps) => {
|
||||||
id={`disk${number}`}
|
id={`disk${number}`}
|
||||||
className={cs(styles.diskLight, { [styles.on]: on })}
|
className={cs(styles.diskLight, { [styles.on]: on })}
|
||||||
/>
|
/>
|
||||||
<button title="Load Disk" onClick={onOpenModal}>
|
<ControlButton title="Load Disk" onClick={onOpenModal} icon="folder-open" />
|
||||||
<i className="fas fa-folder-open" />
|
<ControlButton title="Save Disk" onClick={onOpenDownloadModal} icon="save" />
|
||||||
</button>
|
|
||||||
<button title="Save Disk" onClick={onOpenDownloadModal}>
|
|
||||||
<i className="fas fa-save" />
|
|
||||||
</button>
|
|
||||||
<div
|
<div
|
||||||
id={`disk-label${number}`}
|
id={`disk-label${number}`}
|
||||||
className={styles.diskLabel}
|
className={styles.diskLabel}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import SmartPort from 'js/cards/smartport';
|
||||||
import { useHash } from './hooks/useHash';
|
import { useHash } from './hooks/useHash';
|
||||||
import { noAwait } from './util/promises';
|
import { noAwait } from './util/promises';
|
||||||
|
|
||||||
import styles from './css/BlockFileModal.module.css';
|
import styles from './css/BlockFileModal.module.scss';
|
||||||
|
|
||||||
const DISK_TYPES: FilePickerAcceptType[] = [
|
const DISK_TYPES: FilePickerAcceptType[] = [
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ interface BlockFileModalProps {
|
||||||
onClose: (closeBox?: boolean) => void;
|
onClose: (closeBox?: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BlockFileModal = ({ smartPort, driveNo: number, onClose, isOpen } : BlockFileModalProps) => {
|
export const BlockFileModal = ({ smartPort, driveNo: number, onClose, isOpen }: BlockFileModalProps) => {
|
||||||
const [handles, setHandles] = useState<FileSystemFileHandle[]>();
|
const [handles, setHandles] = useState<FileSystemFileHandle[]>();
|
||||||
const [busy, setBusy] = useState<boolean>(false);
|
const [busy, setBusy] = useState<boolean>(false);
|
||||||
const [empty, setEmpty] = useState<boolean>(true);
|
const [empty, setEmpty] = useState<boolean>(true);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||||
import { Apple2 as Apple2Impl } from '../apple2';
|
import { Apple2 as Apple2Impl } from '../apple2';
|
||||||
import type { Stats } from '../apple2';
|
import type { Stats } from '../apple2';
|
||||||
|
|
||||||
import styles from './css/CPUMeter.module.css';
|
import styles from './css/CPUMeter.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for CPUMeter.
|
* Interface for CPUMeter.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { h, JSX } from 'preact';
|
import { h, JSX } from 'preact';
|
||||||
import cs from 'classnames';
|
import cs from 'classnames';
|
||||||
|
|
||||||
import styles from './css/ControlButton.module.css';
|
import styles from './css/ControlButton.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for ControlButton.
|
* Interface for ControlButton.
|
||||||
|
@ -23,7 +23,7 @@ export interface ControlButtonProps {
|
||||||
* @returns Control Button component
|
* @returns Control Button component
|
||||||
*/
|
*/
|
||||||
export const ControlButton = ({ active, icon, title, onClick, ...props }: ControlButtonProps) => (
|
export const ControlButton = ({ active, icon, title, onClick, ...props }: ControlButtonProps) => (
|
||||||
<button onClick={onClick} title={title} {...props} >
|
<button className={styles.iconButton} onClick={onClick} title={title} {...props} >
|
||||||
<i className={cs('fas', `fa-${icon}`, { [styles.active]: active })}></i>
|
<i className={cs('fas', `fa-${icon}`, { [styles.active]: active })}></i>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { CPUMeter } from './CPUMeter';
|
||||||
import { Inset } from './Inset';
|
import { Inset } from './Inset';
|
||||||
import { useHotKey } from './hooks/useHotKey';
|
import { useHotKey } from './hooks/useHotKey';
|
||||||
import { AudioControl } from './AudioControl';
|
import { AudioControl } from './AudioControl';
|
||||||
import { OptionsModal} from './OptionsModal';
|
import { OptionsModal } from './OptionsModal';
|
||||||
import { OptionsContext } from './OptionsContext';
|
import { OptionsContext } from './OptionsContext';
|
||||||
import { Printer } from './Printer';
|
import { Printer } from './Printer';
|
||||||
import { ControlButton } from './ControlButton';
|
import { ControlButton } from './ControlButton';
|
||||||
|
@ -13,7 +13,7 @@ import { JoyStick } from '../ui/joystick';
|
||||||
import { Screen, SCREEN_FULL_PAGE } from '../ui/screen';
|
import { Screen, SCREEN_FULL_PAGE } from '../ui/screen';
|
||||||
import { System } from '../ui/system';
|
import { System } from '../ui/system';
|
||||||
|
|
||||||
import styles from './css/ControlStrip.module.css';
|
import styles from './css/ControlStrip.module.scss';
|
||||||
import Apple2IO from 'js/apple2io';
|
import Apple2IO from 'js/apple2io';
|
||||||
|
|
||||||
const README = 'https://github.com/whscullin/apple2js#readme';
|
const README = 'https://github.com/whscullin/apple2js#readme';
|
||||||
|
@ -90,7 +90,7 @@ export const ControlStrip = ({ apple2, e, toggleDebugger }: ControlStripProps) =
|
||||||
<ControlButton onClick={toggleDebugger} title="Toggle Debugger" icon="bug" />
|
<ControlButton onClick={toggleDebugger} title="Toggle Debugger" icon="bug" />
|
||||||
<AudioControl apple2={apple2} />
|
<AudioControl apple2={apple2} />
|
||||||
<Printer io={io} slot={1} />
|
<Printer io={io} slot={1} />
|
||||||
<div style={{flexGrow: 1}} />
|
<div style={{ flexGrow: 1 }} />
|
||||||
<ControlButton onClick={doReadme} title="About" icon="info" />
|
<ControlButton onClick={doReadme} title="About" icon="info" />
|
||||||
<ControlButton onClick={doShowOptions} title="Options (F4)" icon="cog" />
|
<ControlButton onClick={doShowOptions} title="Options (F4)" icon="cog" />
|
||||||
</Inset>
|
</Inset>
|
||||||
|
|
|
@ -5,10 +5,11 @@ import Disk2 from '../cards/disk2';
|
||||||
import { ErrorModal } from './ErrorModal';
|
import { ErrorModal } from './ErrorModal';
|
||||||
import { FileModal } from './FileModal';
|
import { FileModal } from './FileModal';
|
||||||
|
|
||||||
import styles from './css/DiskII.module.css';
|
import styles from './css/DiskII.module.scss';
|
||||||
import { DiskDragTarget } from './DiskDragTarget';
|
import { DiskDragTarget } from './DiskDragTarget';
|
||||||
import { FLOPPY_FORMATS } from 'js/formats/types';
|
import { FLOPPY_FORMATS } from 'js/formats/types';
|
||||||
import { DownloadModal } from './DownloadModal';
|
import { DownloadModal } from './DownloadModal';
|
||||||
|
import { ControlButton } from './ControlButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage structure for Disk II state returned via callbacks.
|
* Storage structure for Disk II state returned via callbacks.
|
||||||
|
@ -78,12 +79,8 @@ export const DiskII = ({ disk2, number, on, name, side }: DiskIIProps) => {
|
||||||
/>
|
/>
|
||||||
<ErrorModal error={error} setError={setError} />
|
<ErrorModal error={error} setError={setError} />
|
||||||
<div className={cs(styles.diskLight, { [styles.on]: on })} />
|
<div className={cs(styles.diskLight, { [styles.on]: on })} />
|
||||||
<button title="Load Disk" onClick={onOpenModal}>
|
<ControlButton title="Load Disk" onClick={onOpenModal} icon="folder-open" />
|
||||||
<i className="fas fa-folder-open" />
|
<ControlButton title="Save Disk" onClick={onOpenDownloadModal} icon="save" />
|
||||||
</button>
|
|
||||||
<button title="Save Disk" onClick={onOpenDownloadModal}>
|
|
||||||
<i className="fas fa-save" />
|
|
||||||
</button>
|
|
||||||
<div className={styles.diskLabel}>
|
<div className={styles.diskLabel}>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||||
import { DriveNumber, MassStorage } from '../formats/types';
|
import { DriveNumber, MassStorage } from '../formats/types';
|
||||||
import { Modal, ModalContent, ModalFooter } from './Modal';
|
import { Modal, ModalContent, ModalFooter } from './Modal';
|
||||||
|
|
||||||
import styles from './css/DownloadModal.module.css';
|
import styles from './css/DownloadModal.module.scss';
|
||||||
|
|
||||||
interface DownloadModalProps {
|
interface DownloadModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
@ -12,7 +12,7 @@ interface DownloadModalProps {
|
||||||
onClose: (closeBox?: boolean) => void;
|
onClose: (closeBox?: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DownloadModal = ({ massStorage, driveNo, onClose, isOpen } : DownloadModalProps) => {
|
export const DownloadModal = ({ massStorage, driveNo, onClose, isOpen }: DownloadModalProps) => {
|
||||||
const [href, setHref] = useState('');
|
const [href, setHref] = useState('');
|
||||||
const [downloadName, setDownloadName] = useState('');
|
const [downloadName, setDownloadName] = useState('');
|
||||||
const doCancel = useCallback(() => onClose(true), [onClose]);
|
const doCancel = useCallback(() => onClose(true), [onClose]);
|
||||||
|
@ -44,7 +44,7 @@ export const DownloadModal = ({ massStorage, driveNo, onClose, isOpen } : Downlo
|
||||||
<Modal title="Save File" isOpen={isOpen} onClose={onClose}>
|
<Modal title="Save File" isOpen={isOpen} onClose={onClose}>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<div className={styles.modalContent}>
|
<div className={styles.modalContent}>
|
||||||
{ href
|
{href
|
||||||
? (
|
? (
|
||||||
<>
|
<>
|
||||||
<span>Disk Name: {downloadName}</span>
|
<span>Disk Name: {downloadName}</span>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { useHash } from './hooks/useHash';
|
||||||
import { DISK_FORMATS, DRIVE_NUMBERS, SupportedSectors } from 'js/formats/types';
|
import { DISK_FORMATS, DRIVE_NUMBERS, SupportedSectors } from 'js/formats/types';
|
||||||
import { spawn, Ready } from './util/promises';
|
import { spawn, Ready } from './util/promises';
|
||||||
|
|
||||||
import styles from './css/Drives.module.css';
|
import styles from './css/Drives.module.scss';
|
||||||
import { DiskDragTarget } from './DiskDragTarget';
|
import { DiskDragTarget } from './DiskDragTarget';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { h } from 'preact';
|
||||||
import { useCallback } from 'preact/hooks';
|
import { useCallback } from 'preact/hooks';
|
||||||
import { Modal, ModalContent, ModalFooter } from './Modal';
|
import { Modal, ModalContent, ModalFooter } from './Modal';
|
||||||
|
|
||||||
import styles from './css/ErrorModal.module.css';
|
import styles from './css/ErrorModal.module.scss';
|
||||||
|
|
||||||
export interface ErrorProps {
|
export interface ErrorProps {
|
||||||
error: unknown | undefined;
|
error: unknown | undefined;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { noAwait, spawn } from './util/promises';
|
||||||
import { useHash } from './hooks/useHash';
|
import { useHash } from './hooks/useHash';
|
||||||
import { FileChooser, FilePickerAcceptType } from './FileChooser';
|
import { FileChooser, FilePickerAcceptType } from './FileChooser';
|
||||||
|
|
||||||
import styles from './css/FileModal.module.css';
|
import styles from './css/FileModal.module.scss';
|
||||||
|
|
||||||
const DISK_TYPES: FilePickerAcceptType[] = [
|
const DISK_TYPES: FilePickerAcceptType[] = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
|
||||||
import styles from './css/Header.module.css';
|
import styles from './css/Header.module.scss';
|
||||||
|
|
||||||
const README = 'https://github.com/whscullin/apple2js#readme';
|
const README = 'https://github.com/whscullin/apple2js#readme';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { h, ComponentChildren, JSX } from 'preact';
|
import { h, ComponentChildren, JSX } from 'preact';
|
||||||
import cs from 'classnames';
|
import cs from 'classnames';
|
||||||
|
|
||||||
import styles from './css/Inset.module.css';
|
import styles from './css/Inset.module.scss';
|
||||||
|
|
||||||
interface InsetProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
interface InsetProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
||||||
children: ComponentChildren;
|
children: ComponentChildren;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
mapKeyboardEvent
|
mapKeyboardEvent
|
||||||
} from './util/keyboard';
|
} from './util/keyboard';
|
||||||
|
|
||||||
import styles from './css/Keyboard.module.css';
|
import styles from './css/Keyboard.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function for massaging key labels for upper
|
* Convenience function for massaging key labels for upper
|
||||||
|
@ -23,7 +23,7 @@ const buildLabel = (key: string) => {
|
||||||
const small = key.length > 1 && !key.startsWith('&');
|
const small = key.length > 1 && !key.startsWith('&');
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={cs({[styles.small]: small})}
|
className={cs({ [styles.small]: small })}
|
||||||
dangerouslySetInnerHTML={{ __html: key }}
|
dangerouslySetInnerHTML={{ __html: key }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -110,7 +110,7 @@ export interface KeyboardProps {
|
||||||
export const Keyboard = ({ apple2, e }: KeyboardProps) => {
|
export const Keyboard = ({ apple2, e }: KeyboardProps) => {
|
||||||
const [pressed, setPressed] = useState<string[]>([]);
|
const [pressed, setPressed] = useState<string[]>([]);
|
||||||
const [active, setActive] = useState<string[]>(['LOCK']);
|
const [active, setActive] = useState<string[]>(['LOCK']);
|
||||||
const keys = useMemo(() => keysAsTuples(e ? keys2e : keys2 ), [e]);
|
const keys = useMemo(() => keysAsTuples(e ? keys2e : keys2), [e]);
|
||||||
|
|
||||||
// Set global keystroke handler
|
// Set global keystroke handler
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -125,7 +125,7 @@ export const Keyboard = ({ apple2, e }: KeyboardProps) => {
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const {key, keyCode, keyLabel} = mapKeyboardEvent(event, active.includes('LOCK'), active.includes('CTRL'));
|
const { key, keyCode, keyLabel } = mapKeyboardEvent(event, active.includes('LOCK'), active.includes('CTRL'));
|
||||||
setPressed(pressed => pressed.concat([keyLabel]));
|
setPressed(pressed => pressed.concat([keyLabel]));
|
||||||
setActive(active => active.concat([keyLabel]));
|
setActive(active => active.concat([keyLabel]));
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ export const Keyboard = ({ apple2, e }: KeyboardProps) => {
|
||||||
if (!apple2) {
|
if (!apple2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {key, keyLabel} = mapKeyboardEvent(event);
|
const { key, keyLabel } = mapKeyboardEvent(event);
|
||||||
setPressed(pressed => pressed.filter(k => k !== keyLabel));
|
setPressed(pressed => pressed.filter(k => k !== keyLabel));
|
||||||
setActive(active => active.filter(k => k !== keyLabel));
|
setActive(active => active.filter(k => k !== keyLabel));
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { h, ComponentChildren } from 'preact';
|
import { h, ComponentChildren } from 'preact';
|
||||||
|
import cs from 'classnames';
|
||||||
import { createPortal } from 'preact/compat';
|
import { createPortal } from 'preact/compat';
|
||||||
import { useCallback } from 'preact/hooks';
|
import { useCallback } from 'preact/hooks';
|
||||||
import { useHotKey } from './hooks/useHotKey';
|
import { useHotKey } from './hooks/useHotKey';
|
||||||
|
|
||||||
import styles from './css/Modal.module.css';
|
import styles from './css/Modal.module.scss';
|
||||||
|
import componentStyles from './css/Components.module.scss';
|
||||||
|
import { ControlButton } from './ControlButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ModalOverlay creates a semi-transparent overlay in which the
|
* ModalOverlay creates a semi-transparent overlay in which the
|
||||||
|
@ -39,9 +42,9 @@ export const ModalContent = ({ children }: { children: ComponentChildren }) => {
|
||||||
*/
|
*/
|
||||||
export const ModalFooter = ({ children }: { children: ComponentChildren }) => {
|
export const ModalFooter = ({ children }: { children: ComponentChildren }) => {
|
||||||
return (
|
return (
|
||||||
<footer className={styles.modalFooter}>
|
<div role="contentinfo" className={styles.modalFooter}>
|
||||||
{children}
|
{children}
|
||||||
</footer>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,9 +67,7 @@ export const ModalCloseButton = ({ onClose }: ModalCloseButtonProp) => {
|
||||||
useHotKey('Escape', doClose);
|
useHotKey('Escape', doClose);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button onClick={doClose} title="Close">
|
<ControlButton onClick={doClose} title="Close" icon="xmark" />
|
||||||
{'\u2715'}
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,14 +92,14 @@ export interface ModalHeaderProps {
|
||||||
*/
|
*/
|
||||||
export const ModalHeader = ({ onClose, title, icon }: ModalHeaderProps) => {
|
export const ModalHeader = ({ onClose, title, icon }: ModalHeaderProps) => {
|
||||||
return (
|
return (
|
||||||
<header className={styles.modalHeader}>
|
<div role="banner" className={styles.modalHeader}>
|
||||||
<span className={styles.modalTitle}>
|
<span className={styles.modalTitle}>
|
||||||
{icon && <i className={`fa-solid fa-${icon}`} role="img" />}
|
{icon && <i className={`fa-solid fa-${icon}`} role="img" />}
|
||||||
{' '}
|
{' '}
|
||||||
{title}
|
{title}
|
||||||
</span>
|
</span>
|
||||||
{onClose && <ModalCloseButton onClose={onClose} />}
|
{onClose && <ModalCloseButton onClose={onClose} />}
|
||||||
</header>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ export const Modal = ({
|
||||||
return (
|
return (
|
||||||
isOpen ? createPortal((
|
isOpen ? createPortal((
|
||||||
<ModalOverlay>
|
<ModalOverlay>
|
||||||
<div className={styles.modal} role="dialog">
|
<div className={cs(styles.modal, componentStyles.components)} role="dialog">
|
||||||
{title && <ModalHeader title={title} {...props} />}
|
{title && <ModalHeader title={title} {...props} />}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
SelectOption,
|
SelectOption,
|
||||||
} from '../options';
|
} from '../options';
|
||||||
|
|
||||||
import styles from './css/OptionsModal.module.css';
|
import styles from './css/OptionsModal.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean property interface
|
* Boolean property interface
|
||||||
|
@ -29,7 +29,7 @@ interface BooleanProps {
|
||||||
* @param setValue Value setter
|
* @param setValue Value setter
|
||||||
* @returns Boolean component
|
* @returns Boolean component
|
||||||
*/
|
*/
|
||||||
const Boolean = ({ option, value, setValue } : BooleanProps) => {
|
const Boolean = ({ option, value, setValue }: BooleanProps) => {
|
||||||
const { label, name } = option;
|
const { label, name } = option;
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(event: JSX.TargetedMouseEvent<HTMLInputElement>) =>
|
(event: JSX.TargetedMouseEvent<HTMLInputElement>) =>
|
||||||
|
@ -67,7 +67,7 @@ interface SelectProps {
|
||||||
* @param setValue Value setter
|
* @param setValue Value setter
|
||||||
* @returns Select component
|
* @returns Select component
|
||||||
*/
|
*/
|
||||||
const Select = ({ option, value, setValue } : SelectProps) => {
|
const Select = ({ option, value, setValue }: SelectProps) => {
|
||||||
const { label, name } = option;
|
const { label, name } = option;
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(event: JSX.TargetedMouseEvent<HTMLSelectElement>) => {
|
(event: JSX.TargetedMouseEvent<HTMLSelectElement>) => {
|
||||||
|
@ -110,7 +110,7 @@ export interface OptionsModalProps {
|
||||||
export const OptionsModal = ({ isOpen, onClose }: OptionsModalProps) => {
|
export const OptionsModal = ({ isOpen, onClose }: OptionsModalProps) => {
|
||||||
const options = useContext(OptionsContext);
|
const options = useContext(OptionsContext);
|
||||||
const sections = options.getSections();
|
const sections = options.getSections();
|
||||||
const setValue = useCallback(( name: string, value: string | boolean ) => {
|
const setValue = useCallback((name: string, value: string | boolean) => {
|
||||||
options.setOption(name, value);
|
options.setOption(name, value);
|
||||||
}, [options]);
|
}, [options]);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Apple2IO, { slot } from 'js/apple2io';
|
||||||
import Parallel, { ParallelOptions } from 'js/cards/parallel';
|
import Parallel, { ParallelOptions } from 'js/cards/parallel';
|
||||||
import { Modal, ModalContent, ModalFooter } from './Modal';
|
import { Modal, ModalContent, ModalFooter } from './Modal';
|
||||||
|
|
||||||
import styles from './css/Printer.module.css';
|
import styles from './css/Printer.module.scss';
|
||||||
import { ControlButton } from './ControlButton';
|
import { ControlButton } from './ControlButton';
|
||||||
import { byte } from 'js/types';
|
import { byte } from 'js/types';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { Modal, ModalContent } from './Modal';
|
import { Modal, ModalContent } from './Modal';
|
||||||
|
|
||||||
import styles from './css/ProgressModal.module.css';
|
import styles from './css/ProgressModal.module.scss';
|
||||||
|
|
||||||
export interface ErrorProps {
|
export interface ErrorProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -9,7 +9,7 @@ export interface ErrorProps {
|
||||||
total: number | undefined;
|
total: number | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProgressModal = ({ title, current, total } : ErrorProps) => {
|
export const ProgressModal = ({ title, current, total }: ErrorProps) => {
|
||||||
if (current && total) {
|
if (current && total) {
|
||||||
return (
|
return (
|
||||||
<Modal title={title} isOpen={true}>
|
<Modal title={title} isOpen={true}>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { h, Ref } from 'preact';
|
import { h, Ref } from 'preact';
|
||||||
|
|
||||||
import styles from './css/Screen.module.css';
|
import styles from './css/Screen.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Screen properties
|
* Screen properties
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ComponentChild, ComponentChildren, h } from 'preact';
|
||||||
import { useCallback, useState } from 'preact/hooks';
|
import { useCallback, useState } from 'preact/hooks';
|
||||||
import cs from 'classnames';
|
import cs from 'classnames';
|
||||||
|
|
||||||
import styles from './css/Tabs.module.css';
|
import styles from './css/Tabs.module.scss';
|
||||||
|
|
||||||
export interface TabProps {
|
export interface TabProps {
|
||||||
children: ComponentChildren;
|
children: ComponentChildren;
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
body {
|
|
||||||
margin: 16px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: #c4c1a0; /* Pantone 453 */
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.full-page) {
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
a[role="button"] {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
a[role="button"],
|
|
||||||
input[type="file"]::file-selector-button {
|
|
||||||
background: #44372c;
|
|
||||||
color: #fff;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border: 1px outset #66594e;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 15px;
|
|
||||||
min-width: 75px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover,
|
|
||||||
a[role="button"]:hover,
|
|
||||||
input[type="file"]::file-selector-button {
|
|
||||||
background-color: #55473d;
|
|
||||||
border: 1px outset #66594e;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:active,
|
|
||||||
a[role="button"]:active,
|
|
||||||
input[type="file"]::file-selector-button {
|
|
||||||
background-color: #22150a;
|
|
||||||
border: 1px outset #44372c;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus,
|
|
||||||
a[role="button"]:focus,
|
|
||||||
input[type="file"]::file-selector-button {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
appearance: none;
|
|
||||||
background-color: #65594d;
|
|
||||||
border: 1px inset #65594d;
|
|
||||||
padding: 7px;
|
|
||||||
top: 7px;
|
|
||||||
border-radius: 3px;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:checked {
|
|
||||||
background-color: #65594d;
|
|
||||||
border: 1px inset #65594d;
|
|
||||||
color: #0d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:checked::after {
|
|
||||||
content: "\2716";
|
|
||||||
font-size: 12px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 2px;
|
|
||||||
color: #0d0;
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
:global(.full-page) {
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: normal;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -6,8 +6,8 @@
|
||||||
.outer {
|
.outer {
|
||||||
width: 620px;
|
width: 620px;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
|
||||||
|
|
||||||
.outer.ready {
|
&.ready {
|
||||||
display: block;
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
.components {
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
|
||||||
|
a[role="button"] {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
a[role="button"],
|
||||||
|
input[type="file"]::file-selector-button {
|
||||||
|
background: #44372c;
|
||||||
|
color: #fff;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border: 1px outset #66594e;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 15px;
|
||||||
|
min-width: 75px;
|
||||||
|
|
||||||
|
&.iconButton {
|
||||||
|
min-width: 36px;
|
||||||
|
margin: 0 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover,
|
||||||
|
a[role="button"]:hover,
|
||||||
|
input[type="file"]::file-selector-button {
|
||||||
|
background-color: #55473d;
|
||||||
|
border: 1px outset #66594e;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active,
|
||||||
|
a[role="button"]:active,
|
||||||
|
input[type="file"]::file-selector-button {
|
||||||
|
background-color: #22150a;
|
||||||
|
border: 1px outset #44372c;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus,
|
||||||
|
a[role="button"]:focus,
|
||||||
|
input[type="file"]::file-selector-button {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
appearance: none;
|
||||||
|
background-color: #65594d;
|
||||||
|
border: 1px inset #65594d;
|
||||||
|
padding: 7px;
|
||||||
|
top: 7px;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked {
|
||||||
|
background-color: #65594d;
|
||||||
|
border: 1px inset #65594d;
|
||||||
|
color: #0d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked::after {
|
||||||
|
content: "\2716";
|
||||||
|
font-size: 12px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 2px;
|
||||||
|
color: #0d0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
.active {
|
|
||||||
color: #0f0;
|
|
||||||
}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
.active {
|
||||||
|
color: #0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconButton {
|
||||||
|
min-width: 36px !important;
|
||||||
|
margin: 0 2px;
|
||||||
|
}
|
|
@ -17,19 +17,20 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
|
||||||
|
|
||||||
.reset:hover {
|
|
||||||
|
&:hover {
|
||||||
background: #44372c;
|
background: #44372c;
|
||||||
border: 3px outset #66594e;
|
border: 3px outset #66594e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reset:active {
|
&:active {
|
||||||
background-color: #22150a;
|
background-color: #22150a;
|
||||||
border-left: 3px solid #44372c;
|
border-left: 3px solid #44372c;
|
||||||
border-top: 3px solid #44372c;
|
border-top: 3px solid #44372c;
|
||||||
border-right: 3px solid #000;
|
border-right: 3px solid #000;
|
||||||
border-bottom: 3px solid #000;
|
border-bottom: 3px solid #000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.full-page) .reset {
|
:global(.full-page) .reset {
|
|
@ -10,8 +10,3 @@
|
||||||
:global(.full-page) .inset {
|
:global(.full-page) .inset {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inset button {
|
|
||||||
min-width: 36px;
|
|
||||||
margin: 0 2px;
|
|
||||||
}
|
|
|
@ -70,6 +70,26 @@
|
||||||
/* border: 5px outset #66594E; */
|
/* border: 5px outset #66594E; */
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
.active {
|
||||||
|
color: #4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
border-radius: 3px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pressed {
|
.pressed {
|
||||||
|
@ -82,14 +102,6 @@
|
||||||
/* border: 5px outset #44372C; */
|
/* border: 5px outset #44372C; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.key div {
|
|
||||||
border-radius: 3px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vCenter div {
|
.vCenter div {
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
}
|
}
|
||||||
|
@ -99,22 +111,6 @@
|
||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key span {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.key .small {
|
|
||||||
font-size: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.key-SHIFT {
|
|
||||||
width: 53px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
color: #4f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.key-RETURN {
|
.key-RETURN {
|
||||||
width: 52px;
|
width: 52px;
|
||||||
}
|
}
|
||||||
|
@ -134,10 +130,10 @@
|
||||||
background-color: #ffd;
|
background-color: #ffd;
|
||||||
color: black;
|
color: black;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
|
||||||
|
|
||||||
.key-POWER div {
|
div {
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-nbsp {
|
.key-nbsp {
|
|
@ -9,11 +9,11 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: rgb(0 0 0 / 60%);
|
background: rgb(0 0 0 / 60%);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
|
||||||
|
|
||||||
.modalOverlay button {
|
button {
|
||||||
min-width: 36px;
|
min-width: 36px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
|
@ -36,10 +36,10 @@
|
||||||
border: 1px outset #66594e;
|
border: 1px outset #66594e;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
|
||||||
|
|
||||||
.modalHeader button {
|
button {
|
||||||
min-width: 36px;
|
min-width: 36px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modalTitle {
|
.modalTitle {
|
||||||
|
@ -62,10 +62,11 @@
|
||||||
.modalFooter {
|
.modalFooter {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
|
||||||
|
|
||||||
.modalFooter a[role="button"],
|
|
||||||
.modalFooter button {
|
a[role="button"],
|
||||||
|
button {
|
||||||
margin: 0 0 0 5px;
|
margin: 0 0 0 5px;
|
||||||
min-width: 75px;
|
min-width: 75px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -6,13 +6,13 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
}
|
|
||||||
|
|
||||||
.tab.selected {
|
.selected {
|
||||||
background-color: #c4c1a0;
|
background-color: #c4c1a0;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
color: #080;
|
color: #080;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
|
@ -6,8 +6,8 @@ import ApplesoftDecompiler from 'js/applesoft/decompiler';
|
||||||
import { ApplesoftHeap, ApplesoftVariable } from 'js/applesoft/heap';
|
import { ApplesoftHeap, ApplesoftVariable } from 'js/applesoft/heap';
|
||||||
import { Apple2 as Apple2Impl } from 'js/apple2';
|
import { Apple2 as Apple2Impl } from 'js/apple2';
|
||||||
|
|
||||||
import styles from './css/Applesoft.module.css';
|
import styles from './css/Applesoft.module.scss';
|
||||||
import debuggerStyles from './css/Debugger.module.css';
|
import debuggerStyles from './css/Debugger.module.scss';
|
||||||
|
|
||||||
export interface ApplesoftProps {
|
export interface ApplesoftProps {
|
||||||
apple2: Apple2Impl | undefined;
|
apple2: Apple2Impl | undefined;
|
||||||
|
|
|
@ -8,8 +8,8 @@ import { loadLocalBinaryFile } from '../util/files';
|
||||||
import { spawn } from '../util/promises';
|
import { spawn } from '../util/promises';
|
||||||
import { toHex } from 'js/util';
|
import { toHex } from 'js/util';
|
||||||
|
|
||||||
import styles from './css/CPU.module.css';
|
import styles from './css/CPU.module.scss';
|
||||||
import debuggerStyles from './css/Debugger.module.css';
|
import debuggerStyles from './css/Debugger.module.scss';
|
||||||
|
|
||||||
export interface CPUProps {
|
export interface CPUProps {
|
||||||
apple2: Apple2Impl | undefined;
|
apple2: Apple2Impl | undefined;
|
||||||
|
@ -29,7 +29,7 @@ const VALID_PAGE = /^[0-9A-F]{1,2}$/i;
|
||||||
const VALID_ADDRESS = /^[0-9A-F]{1,4}$/i;
|
const VALID_ADDRESS = /^[0-9A-F]{1,4}$/i;
|
||||||
|
|
||||||
const ERROR_ICON = (
|
const ERROR_ICON = (
|
||||||
<div className={styles.invalid}>
|
<div className={styles.errorIcon}>
|
||||||
<i
|
<i
|
||||||
className="fa-solid fa-triangle-exclamation"
|
className="fa-solid fa-triangle-exclamation"
|
||||||
title="Invalid hex address"
|
title="Invalid hex address"
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { Disks } from './Disks';
|
||||||
import { Memory } from './Memory';
|
import { Memory } from './Memory';
|
||||||
import { VideoModes } from './VideoModes';
|
import { VideoModes } from './VideoModes';
|
||||||
|
|
||||||
import styles from './css/Debugger.module.css';
|
import styles from './css/Debugger.module.scss';
|
||||||
|
|
||||||
interface DebuggerProps {
|
interface DebuggerProps {
|
||||||
apple2: Apple2 | undefined;
|
apple2: Apple2 | undefined;
|
||||||
|
|
|
@ -15,8 +15,8 @@ import { FileEntry } from 'js/formats/prodos/file_entry';
|
||||||
import { VDH } from 'js/formats/prodos/vdh';
|
import { VDH } from 'js/formats/prodos/vdh';
|
||||||
import { toHex } from 'js/util';
|
import { toHex } from 'js/util';
|
||||||
|
|
||||||
import styles from './css/Disks.module.css';
|
import styles from './css/Disks.module.scss';
|
||||||
import debuggerStyles from './css/Debugger.module.css';
|
import debuggerStyles from './css/Debugger.module.scss';
|
||||||
import { useCallback, useState } from 'preact/hooks';
|
import { useCallback, useState } from 'preact/hooks';
|
||||||
import { DOS33, FileEntry as DOSEntry, isMaybeDOS33 } from 'js/formats/dos/dos33';
|
import { DOS33, FileEntry as DOSEntry, isMaybeDOS33 } from 'js/formats/dos/dos33';
|
||||||
import createDiskFromDOS from 'js/formats/do';
|
import createDiskFromDOS from 'js/formats/do';
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { h, Fragment } from 'preact';
|
||||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||||
import { Modal, ModalContent, ModalFooter } from '../Modal';
|
import { Modal, ModalContent, ModalFooter } from '../Modal';
|
||||||
|
|
||||||
import styles from './css/FileViewer.module.css';
|
import styles from './css/FileViewer.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binary and text representation of file to be previewed
|
* Binary and text representation of file to be previewed
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { Apple2 as Apple2Impl } from 'js/apple2';
|
||||||
import MMU from 'js/mmu';
|
import MMU from 'js/mmu';
|
||||||
import LanguageCard from 'js/cards/langcard';
|
import LanguageCard from 'js/cards/langcard';
|
||||||
|
|
||||||
import styles from './css/Memory.module.css';
|
import styles from './css/Memory.module.scss';
|
||||||
import debuggerStyles from './css/Debugger.module.css';
|
import debuggerStyles from './css/Debugger.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates the read/write status of a bank
|
* Encapsulates the read/write status of a bank
|
||||||
|
@ -20,7 +20,7 @@ interface ReadWrite {
|
||||||
/**
|
/**
|
||||||
* Encapsulates the read/write status of a language card
|
* Encapsulates the read/write status of a language card
|
||||||
*/
|
*/
|
||||||
interface LC extends ReadWrite {
|
interface LC extends ReadWrite {
|
||||||
bank0: ReadWrite;
|
bank0: ReadWrite;
|
||||||
bank1: ReadWrite;
|
bank1: ReadWrite;
|
||||||
rom: ReadWrite;
|
rom: ReadWrite;
|
||||||
|
@ -204,7 +204,7 @@ interface LanguageCardMapProps {
|
||||||
* @param children label component
|
* @param children label component
|
||||||
* @returns LanguageCard component
|
* @returns LanguageCard component
|
||||||
*/
|
*/
|
||||||
const LanguageCardMap = ({lc, children}: LanguageCardMapProps) => {
|
const LanguageCardMap = ({ lc, children }: LanguageCardMapProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={cs(styles.bank)}>
|
<div className={cs(styles.bank)}>
|
||||||
<div className={cs(styles.lc, rw(lc))}>
|
<div className={cs(styles.lc, rw(lc))}>
|
||||||
|
|
|
@ -5,8 +5,8 @@ import cs from 'classnames';
|
||||||
import { Apple2 as Apple2Impl } from 'js/apple2';
|
import { Apple2 as Apple2Impl } from 'js/apple2';
|
||||||
import { VideoPage } from 'js/videomodes';
|
import { VideoPage } from 'js/videomodes';
|
||||||
|
|
||||||
import styles from './css/VideoModes.module.css';
|
import styles from './css/VideoModes.module.scss';
|
||||||
import debuggerStyles from './css/Debugger.module.css';
|
import debuggerStyles from './css/Debugger.module.scss';
|
||||||
|
|
||||||
export interface VideoModesProps {
|
export interface VideoModesProps {
|
||||||
apple2: Apple2Impl | undefined;
|
apple2: Apple2Impl | undefined;
|
||||||
|
@ -62,13 +62,13 @@ export const VideoModes = ({ apple2 }: VideoModesProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.pages}>
|
<div className={styles.pages}>
|
||||||
<div className={debuggerStyles.row}>
|
<div className={debuggerStyles.row}>
|
||||||
<div className={cs(styles.page, {[styles.active]: (text || !hires) && !page2})}>
|
<div className={cs(styles.page, { [styles.active]: (text || !hires) && !page2 })}>
|
||||||
<div className={debuggerStyles.heading}>
|
<div className={debuggerStyles.heading}>
|
||||||
Text/Lores Page 1
|
Text/Lores Page 1
|
||||||
</div>
|
</div>
|
||||||
<canvas width="560" height="192" ref={canvas1} />
|
<canvas width="560" height="192" ref={canvas1} />
|
||||||
</div>
|
</div>
|
||||||
<div className={cs(styles.page, {[styles.active]: (text || !hires) && page2})}>
|
<div className={cs(styles.page, { [styles.active]: (text || !hires) && page2 })}>
|
||||||
<div className={debuggerStyles.heading}>
|
<div className={debuggerStyles.heading}>
|
||||||
Text/Lores Page 2
|
Text/Lores Page 2
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,13 +76,13 @@ export const VideoModes = ({ apple2 }: VideoModesProps) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={debuggerStyles.row}>
|
<div className={debuggerStyles.row}>
|
||||||
<div className={cs(styles.page, {[styles.active]: (!text && hires) && !page2})}>
|
<div className={cs(styles.page, { [styles.active]: (!text && hires) && !page2 })}>
|
||||||
<div className={debuggerStyles.heading}>
|
<div className={debuggerStyles.heading}>
|
||||||
Hires Page 1
|
Hires Page 1
|
||||||
</div>
|
</div>
|
||||||
<canvas width="560" height="192" ref={canvas3} />
|
<canvas width="560" height="192" ref={canvas3} />
|
||||||
</div>
|
</div>
|
||||||
<div className={cs(styles.page, {[styles.active]: (!text && hires) && page2})}>
|
<div className={cs(styles.page, { [styles.active]: (!text && hires) && page2 })}>
|
||||||
<div className={debuggerStyles.heading}>
|
<div className={debuggerStyles.heading}>
|
||||||
Hires Page 2
|
Hires Page 2
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
.listing {
|
|
||||||
width: calc(100% - 12px);
|
|
||||||
height: 320px;
|
|
||||||
overflow: auto;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.variables {
|
|
||||||
width: 100%;
|
|
||||||
height: 320px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.variables table {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.variables td {
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px inset;
|
|
||||||
white-space: pre;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.internals {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.internals table {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.internals td {
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px inset;
|
|
||||||
white-space: pre;
|
|
||||||
font-family: monospace;
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.internals th {
|
|
||||||
width: 20%;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stack {
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
.listing {
|
||||||
|
width: calc(100% - 12px);
|
||||||
|
height: 320px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variables {
|
||||||
|
width: 100%;
|
||||||
|
height: 320px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px inset;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.internals {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
td {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px inset;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: monospace;
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
width: 20%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack {
|
||||||
|
width: 10em;
|
||||||
|
}
|
|
@ -23,13 +23,13 @@
|
||||||
color: #f00;
|
color: #f00;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.invalid {
|
.errorIcon {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
|
||||||
|
|
||||||
div.invalid i {
|
i {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -9px;
|
top: -9px;
|
||||||
left: -16px;
|
left: -16px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -130,19 +130,19 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.read {
|
.read {
|
||||||
background-color: #8f8;
|
background-color: #8f8;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.write {
|
.write {
|
||||||
background-color: #f88;
|
background-color: #f88;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.read.write {
|
.read.write {
|
||||||
background-color: #88f;
|
background-color: #88f;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.inactive {
|
.inactive {
|
||||||
background-color: #bbb;
|
background-color: #bbb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
"node-forge": "^1.3.0",
|
"node-forge": "^1.3.0",
|
||||||
"raw-loader": "^4.0.0",
|
"raw-loader": "^4.0.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
|
"sass": "^1.57.1",
|
||||||
|
"sass-loader": "^13.2.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"stylelint": "^14.9.1",
|
"stylelint": "^14.9.1",
|
||||||
"stylelint-config-css-modules": "^4.1.0",
|
"stylelint-config-css-modules": "^4.1.0",
|
||||||
|
@ -4960,9 +4962,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001342",
|
"version": "1.0.30001442",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz",
|
||||||
"integrity": "sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==",
|
"integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -7730,6 +7732,12 @@
|
||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immutable": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
|
@ -11631,6 +11639,15 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/klona": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/known-css-properties": {
|
"node_modules/known-css-properties": {
|
||||||
"version": "0.25.0",
|
"version": "0.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
|
||||||
|
@ -13482,6 +13499,61 @@
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/sass": {
|
||||||
|
"version": "1.57.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
|
||||||
|
"integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
"immutable": "^4.0.0",
|
||||||
|
"source-map-js": ">=0.6.2 <2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sass": "sass.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sass-loader": {
|
||||||
|
"version": "13.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz",
|
||||||
|
"integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"klona": "^2.0.4",
|
||||||
|
"neo-async": "^2.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.15.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"fibers": ">= 3.1.0",
|
||||||
|
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||||
|
"sass": "^1.3.0",
|
||||||
|
"sass-embedded": "*",
|
||||||
|
"webpack": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"fibers": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node-sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/saxes": {
|
"node_modules/saxes": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
|
||||||
|
@ -19663,9 +19735,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001342",
|
"version": "1.0.30001442",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz",
|
||||||
"integrity": "sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==",
|
"integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"canvas": {
|
"canvas": {
|
||||||
|
@ -21753,6 +21825,12 @@
|
||||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"immutable": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"import-fresh": {
|
"import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
|
@ -24709,6 +24787,12 @@
|
||||||
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
|
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"klona": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"known-css-properties": {
|
"known-css-properties": {
|
||||||
"version": "0.25.0",
|
"version": "0.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
|
||||||
|
@ -26079,6 +26163,27 @@
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"sass": {
|
||||||
|
"version": "1.57.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
|
||||||
|
"integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
"immutable": "^4.0.0",
|
||||||
|
"source-map-js": ">=0.6.2 <2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sass-loader": {
|
||||||
|
"version": "13.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz",
|
||||||
|
"integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"klona": "^2.0.4",
|
||||||
|
"neo-async": "^2.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"saxes": {
|
"saxes": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
"node-forge": "^1.3.0",
|
"node-forge": "^1.3.0",
|
||||||
"raw-loader": "^4.0.0",
|
"raw-loader": "^4.0.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
|
"sass": "^1.57.1",
|
||||||
|
"sass-loader": "^13.2.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"stylelint": "^14.9.1",
|
"stylelint": "^14.9.1",
|
||||||
"stylelint-config-css-modules": "^4.1.0",
|
"stylelint-config-css-modules": "^4.1.0",
|
||||||
|
|
|
@ -2,3 +2,8 @@ declare module '*.module.css' {
|
||||||
const classes: { [key: string]: string };
|
const classes: { [key: string]: string };
|
||||||
export default classes;
|
export default classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '*.module.scss' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,24 @@ const baseConfig = {
|
||||||
'css-loader'
|
'css-loader'
|
||||||
],
|
],
|
||||||
exclude: /\.module\.css$/
|
exclude: /\.module\.css$/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.module\.s(a|c)ss$/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
import: false,
|
||||||
|
modules: {
|
||||||
|
localIdentName: '[path][name]__[local]',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'sass-loader',
|
||||||
|
}
|
||||||
|
],
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue