mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-09 01:13:55 +00:00
1849b4d39f
* chore: migrate eslint to oxlint * chore: migrate prettier to oxfmt * chore: migrate typescript * chore: toThrow should have a expected value * chore: cast test value as Day * chore: small title fix * chore: mocks should be hoisted * chore: incorrect async useage * chore: test should be inside description * chore: test sohuld include an expeced * chore: oxfmt --------- Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
197 lines
5.2 KiB
TypeScript
197 lines
5.2 KiB
TypeScript
import { ComponentProps, useEffect, useState } from 'react';
|
|
import { useSearchParams } from 'react-router';
|
|
|
|
import { isStringBoolean } from '../../../views/common/viewUtils';
|
|
import Checkbox from '../checkbox/Checkbox';
|
|
import Input from '../input/input/Input';
|
|
import Select, { SelectOption } from '../select/Select';
|
|
import Switch from '../switch/Switch';
|
|
import InlineColourPicker from './InlineColourPicker';
|
|
import { ParamField } from './viewParams.types';
|
|
|
|
import style from './ParamInput.module.scss';
|
|
|
|
interface ParamInputProps {
|
|
paramField: ParamField;
|
|
}
|
|
|
|
export default function ParamInput({ paramField }: ParamInputProps) {
|
|
const [searchParams] = useSearchParams();
|
|
const { id, type, defaultValue } = paramField;
|
|
|
|
if (type === 'persist') {
|
|
if (!paramField.values || !paramField.values.length) {
|
|
return null;
|
|
}
|
|
return <input hidden name={id} readOnly value={paramField.values.join(',')} />;
|
|
}
|
|
|
|
if (type === 'option') {
|
|
const optionFromParams = searchParams.get(id);
|
|
const defaultOptionValue = optionFromParams || defaultValue;
|
|
|
|
if (paramField.values.length === 0) {
|
|
return <span className={style.empty}>No options available</span>;
|
|
}
|
|
|
|
return <ControlledSelect id={id} initialValue={defaultOptionValue} options={paramField.values} />;
|
|
}
|
|
|
|
if (type === 'multi-option') {
|
|
const optionFromParams = searchParams.getAll(id);
|
|
|
|
return (
|
|
<MultiOption
|
|
paramField={paramField}
|
|
options={optionFromParams.length ? optionFromParams : (paramField.defaultValue ?? [''])}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (type === 'boolean') {
|
|
return <ControlledSwitch id={id} initialValue={isStringBoolean(searchParams.get(id)) ?? defaultValue} />;
|
|
}
|
|
|
|
if (type === 'number') {
|
|
const { placeholder } = paramField;
|
|
const defaultNumberValue = searchParams.get(id) ?? defaultValue;
|
|
|
|
return (
|
|
<Input
|
|
height='large'
|
|
type='number'
|
|
step='any'
|
|
name={id}
|
|
defaultValue={defaultNumberValue}
|
|
placeholder={placeholder}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (type === 'colour') {
|
|
return <InlineColourPicker name={id} value={searchParams.get(id) ?? defaultValue} />;
|
|
}
|
|
|
|
const defaultStringValue = searchParams.get(id) ?? defaultValue ?? '';
|
|
const { placeholder } = paramField;
|
|
|
|
return <ControlledInput id={id} initialValue={defaultStringValue} placeholder={placeholder} />;
|
|
}
|
|
|
|
interface EditFormMultiOptionProps {
|
|
paramField: ParamField & { type: 'multi-option' };
|
|
options: string[];
|
|
}
|
|
|
|
function MultiOption({ paramField, options }: EditFormMultiOptionProps) {
|
|
const { id, values } = paramField;
|
|
const [paramState, setParamState] = useState<string[]>(options);
|
|
|
|
// synchronise options
|
|
useEffect(() => {
|
|
setParamState(options);
|
|
}, [options]);
|
|
|
|
const toggleValue = (value: string, checked: boolean) => {
|
|
if (checked) {
|
|
setParamState((prev) => [...prev, value]);
|
|
} else {
|
|
setParamState((prev) => prev.filter((v) => v !== value));
|
|
}
|
|
};
|
|
|
|
if (values.length === 0) {
|
|
return <span className={style.empty}>No options available</span>;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<input name={id} hidden readOnly value={paramState.join(',')} />
|
|
<div className={style.inline}>
|
|
{values.map((option) => {
|
|
return (
|
|
<label
|
|
key={option.value}
|
|
className={style.toggleSelect}
|
|
style={{
|
|
'--user-bg': option.colour,
|
|
}}
|
|
>
|
|
<Checkbox
|
|
checked={paramState.includes(option.value)}
|
|
onCheckedChange={(checked) => toggleValue(option.value, checked as boolean)}
|
|
/>
|
|
{option.label}
|
|
</label>
|
|
);
|
|
})}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
interface ControlledSwitchProps {
|
|
id: string;
|
|
initialValue: boolean;
|
|
}
|
|
function ControlledSwitch({ id, initialValue }: ControlledSwitchProps) {
|
|
const [checked, setChecked] = useState(initialValue);
|
|
|
|
// synchronise checked state
|
|
useEffect(() => {
|
|
setChecked(initialValue);
|
|
}, [initialValue]);
|
|
|
|
return <Switch size='large' name={id} checked={checked} onCheckedChange={setChecked} />;
|
|
}
|
|
|
|
interface ControlledSelectProps {
|
|
id: string;
|
|
initialValue?: string;
|
|
options: SelectOption[];
|
|
}
|
|
function ControlledSelect({ id, initialValue, options }: ControlledSelectProps) {
|
|
const [selected, setSelected] = useState(initialValue);
|
|
|
|
// synchronise selected state
|
|
useEffect(() => {
|
|
setSelected(initialValue);
|
|
}, [initialValue]);
|
|
|
|
return (
|
|
<Select
|
|
size='large'
|
|
name={id}
|
|
options={options}
|
|
value={selected}
|
|
onValueChange={(value) => {
|
|
if (value === null) return;
|
|
setSelected(value);
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
interface ControlledInputProps<T extends number | string> extends ComponentProps<typeof Input> {
|
|
id: string;
|
|
initialValue: T;
|
|
}
|
|
function ControlledInput<T extends number | string>({ id, initialValue, ...inputProps }: ControlledInputProps<T>) {
|
|
const [value, setValue] = useState(initialValue);
|
|
|
|
// synchronise selected state
|
|
useEffect(() => {
|
|
setValue(initialValue);
|
|
}, [initialValue]);
|
|
|
|
return (
|
|
<Input
|
|
height='large'
|
|
name={id}
|
|
value={value}
|
|
onChange={(event) => setValue(event.target.value as T)}
|
|
{...inputProps}
|
|
/>
|
|
);
|
|
}
|