import React, { PureComponent, ReactNode } from 'react'; import { autoBindMethodsForReact } from 'class-autobind-decorator'; import { AUTOBIND_CFG } from '../../../common/constants'; import classnames from 'classnames'; import Modal from '../base/modal'; import ModalBody from '../base/modal-body'; import ModalHeader from '../base/modal-header'; import ModalFooter from '../base/modal-footer'; import Button from '../base/button'; import PromptButton from '../base/prompt-button'; interface State { title: string; hints: string[]; defaultValue?: string | null; submitName?: string | null; selectText?: boolean | null; upperCase?: boolean | null; validate?: ((arg0: string) => string) | null; hint?: string | null; label?: string | null; placeholder?: string | null; inputType?: string | null; cancelable?: boolean | null; onComplete?: (arg0: string) => Promise | void; onCancel?: (() => void) | null; onDeleteHint?: ((arg0: string) => void) | null; currentValue: string; loading: boolean; } export interface PromptModalOptions { title: string; defaultValue?: string; submitName?: string; selectText?: boolean; upperCase?: boolean; hint?: string; cancelable?: boolean; inputType?: string; placeholder?: string; validate?: (arg0: string) => string; label?: string; hints?: string[]; onComplete?: (arg0: string) => Promise | void; onDeleteHint?: (arg0: string) => void; onCancel?: () => void; } @autoBindMethodsForReact(AUTOBIND_CFG) class PromptModal extends PureComponent<{}, State> { modal: Modal | null = null; _input: HTMLInputElement | null = null; state: State = { title: 'Not Set', hints: [], defaultValue: '', submitName: '', selectText: false, upperCase: false, validate: null, hint: '', label: '', placeholder: '', inputType: '', cancelable: false, onComplete: undefined, onCancel: undefined, onDeleteHint: undefined, currentValue: '', loading: false, } async _done(rawValue: string) { const { onComplete, upperCase } = this.state; const value = upperCase ? rawValue.toUpperCase() : rawValue; await onComplete?.(value); this.hide(); } _handleCancel() { const { onCancel, loading } = this.state; if (loading) { return; } onCancel?.(); } _setInputRef(n: HTMLInputElement) { this._input = n; } _setModalRef(n: Modal) { this.modal = n; } _handleSelectHint(hint: string) { this._done(hint); } _handleDeleteHint(hint: string) { const { onDeleteHint } = this.state; onDeleteHint && onDeleteHint(hint); const hints = this.state.hints.filter(h => h !== hint); this.setState({ hints, }); } async _handleSubmit(e: React.SyntheticEvent) { if (this.state.loading) { return; } this.setState({ loading: true, }); e.preventDefault(); if (this._input) { const result = this._input.type === 'checkbox' ? this._input.checked.toString() : this._input.value; await this._done(result); } this.setState({ loading: false, }); } _handleChange(e: React.SyntheticEvent) { const { validate } = this.state; if (validate) { const errorMessage = validate(e.currentTarget.value); e.currentTarget.setCustomValidity(errorMessage); } } hide() { this.modal && this.modal.hide(); } show({ title, defaultValue, submitName, selectText, upperCase, hint, cancelable, inputType, placeholder, label, hints, onComplete, onCancel, validate, onDeleteHint, }: PromptModalOptions) { this.setState({ currentValue: '', title, onCancel, onDeleteHint, onComplete, defaultValue, submitName, selectText, cancelable: cancelable === undefined ? true : cancelable, placeholder, upperCase, hint, inputType, label, validate, hints: hints || [], loading: false, }); this.modal && this.modal.show(); // Need to do this after render because modal focuses itself too setTimeout(() => { if (!this._input) { return; } if (inputType === 'checkbox') { this._input.checked = !!defaultValue; } else { this._input.value = defaultValue || ''; } this._input.focus(); selectText && this._input && this._input.select(); }, 100); } _renderHintButton(hint: string) { const classes = classnames( 'btn btn--outlined btn--super-duper-compact', 'margin-right-sm margin-top-sm inline-block', ); return (
); } render() { const { submitName, title, hint, inputType, placeholder, label, upperCase, hints, cancelable, loading, } = this.state; const input = ( ); let sanitizedHints: ReactNode[] = []; if (Array.isArray(hints)) { sanitizedHints = hints.slice(0, 15).map(this._renderHintButton); } let field = input; if (label) { const labelClasses = classnames({ 'inline-block': inputType === 'checkbox', }); field = ( ); } const divClassnames = classnames('form-control form-control--wide', { 'form-control--outlined': inputType !== 'checkbox', }); return ( {title}
{field}
{sanitizedHints}
{hint ? `* ${hint}` : ''}
); } } export default PromptModal;