import React, { useState } from "react"; import { Button, Collapse, Form, Modal, ModalProps } from "react-bootstrap"; import { FormattedMessage, useIntl } from "react-intl"; import { PropsWithChildren } from "react-router/node_modules/@types/react"; import { Icon } from "../Shared"; import { StringListInput } from "../Shared/StringListInput"; interface ISetting { id?: string; className?: string; heading?: React.ReactNode; headingID?: string; subHeadingID?: string; subHeading?: React.ReactNode; tooltipID?: string; onClick?: React.MouseEventHandler; disabled?: boolean; } export const Setting: React.FC> = ({ id, className, heading, headingID, subHeadingID, subHeading, children, tooltipID, onClick, disabled, }) => { const intl = useIntl(); function renderHeading() { if (headingID) { return intl.formatMessage({ id: headingID }); } return heading; } function renderSubHeading() { if (subHeadingID) { return (
{intl.formatMessage({ id: subHeadingID })}
); } if (subHeading) { return
{subHeading}
; } } const tooltip = tooltipID ? intl.formatMessage({ id: tooltipID }) : undefined; const disabledClassName = disabled ? "disabled" : ""; return (

{renderHeading()}

{renderSubHeading()}
{children}
); }; interface ISettingGroup { settingProps?: ISetting; topLevel?: JSX.Element; collapsible?: boolean; collapsedDefault?: boolean; } export const SettingGroup: React.FC> = ({ settingProps, topLevel, collapsible, collapsedDefault, children, }) => { const [open, setOpen] = useState(!collapsedDefault); function renderCollapseButton() { if (!collapsible) return; return ( ); } function onDivClick(e: React.MouseEvent) { if (!collapsible) return; // ensure button was not clicked let target: HTMLElement | null = e.target as HTMLElement; while (target && target !== e.currentTarget) { if ( target.nodeName.toLowerCase() === "button" || target.nodeName.toLowerCase() === "a" ) { // button clicked, swallow event return; } target = target.parentElement; } setOpen(!open); } return (
{topLevel} {renderCollapseButton()}
{children}
); }; interface IBooleanSetting extends ISetting { id: string; checked?: boolean; onChange: (v: boolean) => void; } export const BooleanSetting: React.FC = (props) => { const { id, disabled, checked, onChange, ...settingProps } = props; return ( onChange(!checked)} /> ); }; interface ISelectSetting extends ISetting { value?: string | number | string[] | undefined; onChange: (v: string) => void; } export const SelectSetting: React.FC> = ({ id, headingID, subHeadingID, value, children, onChange, }) => { return ( onChange(e.currentTarget.value)} > {children} ); }; interface IDialogSetting extends ISetting { buttonText?: string; buttonTextID?: string; value?: T; renderValue?: (v: T | undefined) => JSX.Element; onChange: () => void; } export const ChangeButtonSetting = (props: IDialogSetting) => { const { id, className, headingID, tooltipID, subHeadingID, subHeading, value, onChange, renderValue, buttonText, buttonTextID, disabled, } = props; const intl = useIntl(); const tooltip = tooltipID ? intl.formatMessage({ id: tooltipID }) : undefined; const disabledClassName = disabled ? "disabled" : ""; return (

{headingID ? intl.formatMessage({ id: headingID }) : undefined}

{renderValue ? renderValue(value) : undefined}
{subHeadingID ? (
{intl.formatMessage({ id: subHeadingID })}
) : subHeading ? (
{subHeading}
) : undefined}
); }; export interface ISettingModal { heading?: string; headingID?: string; subHeadingID?: string; subHeading?: React.ReactNode; value: T | undefined; close: (v?: T) => void; renderField: (value: T | undefined, setValue: (v?: T) => void) => JSX.Element; modalProps?: ModalProps; } export const SettingModal = (props: ISettingModal) => { const { heading, headingID, subHeading, subHeadingID, value, close, renderField, modalProps, } = props; const intl = useIntl(); const [currentValue, setCurrentValue] = useState(value); return ( close()} id="setting-dialog" {...modalProps}>
{ close(currentValue); e.preventDefault(); }} > {headingID ? : heading} {renderField(currentValue, setCurrentValue)} {subHeadingID ? (
{intl.formatMessage({ id: subHeadingID })}
) : subHeading ? (
{subHeading}
) : undefined}
); }; interface IModalSetting extends ISetting { value: T | undefined; buttonText?: string; buttonTextID?: string; onChange: (v: T) => void; renderField: (value: T | undefined, setValue: (v?: T) => void) => JSX.Element; renderValue?: (v: T | undefined) => JSX.Element; modalProps?: ModalProps; } export const ModalSetting = (props: IModalSetting) => { const { id, className, value, headingID, subHeadingID, subHeading, onChange, renderField, renderValue, tooltipID, buttonText, buttonTextID, modalProps, disabled, } = props; const [showModal, setShowModal] = useState(false); return ( <> {showModal ? ( headingID={headingID} subHeadingID={subHeadingID} subHeading={subHeading} value={value} renderField={renderField} close={(v) => { if (v !== undefined) onChange(v); setShowModal(false); }} {...modalProps} /> ) : undefined} id={id} className={className} disabled={disabled} buttonText={buttonText} buttonTextID={buttonTextID} headingID={headingID} tooltipID={tooltipID} subHeadingID={subHeadingID} subHeading={subHeading} value={value} onChange={() => setShowModal(true)} renderValue={renderValue} /> ); }; interface IStringSetting extends ISetting { value: string | undefined; onChange: (v: string) => void; } export const StringSetting: React.FC = (props) => { return ( {...props} renderField={(value, setValue) => ( ) => setValue(e.currentTarget.value) } /> )} renderValue={(value) => {value}} /> ); }; interface INumberSetting extends ISetting { value: number | undefined; onChange: (v: number) => void; } export const NumberSetting: React.FC = (props) => { return ( {...props} renderField={(value, setValue) => ( ) => setValue(Number.parseInt(e.currentTarget.value || "0", 10)) } /> )} renderValue={(value) => {value}} /> ); }; interface IStringListSetting extends ISetting { value: string[] | undefined; defaultNewValue?: string; onChange: (v: string[]) => void; } export const StringListSetting: React.FC = (props) => { return ( {...props} renderField={(value, setValue) => ( )} renderValue={(value) => (
{value?.map((v, i) => ( // eslint-disable-next-line react/no-array-index-key
{v}
))}
)} /> ); }; interface IConstantSetting extends ISetting { value?: T; renderValue?: (v: T | undefined) => JSX.Element; } export const ConstantSetting = (props: IConstantSetting) => { const { id, headingID, subHeading, subHeadingID, renderValue, value } = props; const intl = useIntl(); return (

{headingID ? intl.formatMessage({ id: headingID }) : undefined}

{renderValue ? renderValue(value) : value}
{subHeadingID ? (
{intl.formatMessage({ id: subHeadingID })}
) : subHeading ? (
{subHeading}
) : undefined}
); };