apple2js/js/components/OptionsModal.tsx
Will Scullin 303838f63d
Use CSS modules (#123)
* Use CSS modules

* Fix tests

* Feedback
2022-06-03 15:30:39 -07:00

169 lines
4.3 KiB
TypeScript

import { h, Fragment, JSX } from 'preact';
import { useCallback, useContext } from 'preact/hooks';
import { Modal, ModalContent, ModalFooter } from './Modal';
import { OptionsContext } from './OptionsContext';
import {
BOOLEAN_OPTION,
SELECT_OPTION,
BooleanOption,
Option,
OptionSection,
SelectOption,
} from '../options';
import styles from './css/OptionsModal.module.css';
/**
* Boolean property interface
*/
interface BooleanProps {
option: BooleanOption;
value: boolean;
setValue: (name: string, value: boolean) => void;
}
/**
*
* @param option Boolean option
* @param value Current value
* @param setValue Value setter
* @returns Boolean component
*/
const Boolean = ({ option, value, setValue } : BooleanProps) => {
const { label, name } = option;
const onChange = useCallback(
(event: JSX.TargetedMouseEvent<HTMLInputElement>) =>
setValue(name, event.currentTarget.checked)
, [name, setValue]
);
return (
<li>
<input
type="checkbox"
checked={value}
onChange={onChange}
/>
<label>{label}</label>
</li>
);
};
/**
* Select property interface
*/
interface SelectProps {
option: SelectOption;
value: string;
setValue: (name: string, value: string) => void;
}
/**
* Select component that provides a dropdown to choose between
* options.
*
* @param option Select option
* @param value Current value
* @param setValue Value setter
* @returns Select component
*/
const Select = ({ option, value, setValue } : SelectProps) => {
const { label, name } = option;
const onChange = useCallback(
(event: JSX.TargetedMouseEvent<HTMLSelectElement>) => {
setValue(name, event.currentTarget.value);
},
[name, setValue]
);
const makeOption = (option: { name: string; value: string }) => (
<option selected={option.value === value} value={option.value}>
{option.name}
</option>
);
return (
<li>
<select onChange={onChange}>
{option.values.map(makeOption)}
</select>
<label>{label}</label>
</li>
);
};
/**
* OptionsModal properties
*/
export interface OptionsModalProps {
isOpen: boolean;
onClose: (closeBox?: boolean) => void;
}
/**
* Modal to allow editing of various component provided
* options, like screen and cpu type
*
* @param Modal params
* @returns OptionsModal component
*/
export const OptionsModal = ({ isOpen, onClose }: OptionsModalProps) => {
const options = useContext(OptionsContext);
const sections = options.getSections();
const setValue = useCallback(( name: string, value: string | boolean ) => {
options.setOption(name, value);
}, [options]);
const makeOption = (option: Option) => {
const { name, type } = option;
const value = options.getOption(name);
switch (type) {
case BOOLEAN_OPTION:
return (
<Boolean
option={option as BooleanOption}
value={value as boolean}
setValue={setValue}
/>
);
case SELECT_OPTION:
return (
<Select
option={option as SelectOption}
value={value as string}
setValue={setValue}
/>
);
default:
break;
}
};
const makeSection = (section: OptionSection) => {
return (
<>
<h3>{section.name}</h3>
<ul>
{section.options.map(makeOption)}
</ul>
</>
);
};
const doClose = useCallback(() => onClose(), [onClose]);
return (
<Modal title="Options" isOpen={isOpen} onClose={onClose}>
<ModalContent>
<div className={styles.optionsModal}>
{sections.map(makeSection)}
</div>
<i>* Reload page to take effect</i>
</ModalContent>
<ModalFooter>
<button onClick={doClose}>Close</button>
</ModalFooter>
</Modal>
);
};