diff --git a/web/components/button.react.js b/web/components/button.react.js --- a/web/components/button.react.js +++ b/web/components/button.react.js @@ -12,7 +12,7 @@ | 'danger' | 'round'; -type Props = { +export type ButtonProps = { +onClick: (event: SyntheticEvent<HTMLButtonElement>) => mixed, +children: React.Node, +variant?: ButtonVariant, @@ -21,7 +21,7 @@ +className?: string, }; -function Button(props: Props): React.Node { +function Button(props: ButtonProps): React.Node { const { onClick, children, diff --git a/web/modals/threads/settings/submit-button.css b/web/modals/threads/settings/submit-button.css new file mode 100644 --- /dev/null +++ b/web/modals/threads/settings/submit-button.css @@ -0,0 +1,23 @@ +.container { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; +} + +.error { + text-align: center; + font-size: 16px; + color: var(--error); + font-style: italic; + min-height: 18px; + + margin-top: auto; + padding: 5px; +} + +.button { + width: 100%; + align-self: flex-end; + min-height: 44.5px; +} diff --git a/web/modals/threads/settings/submit-button.react.js b/web/modals/threads/settings/submit-button.react.js new file mode 100644 --- /dev/null +++ b/web/modals/threads/settings/submit-button.react.js @@ -0,0 +1,54 @@ +// @flow + +import classnames from 'classnames'; +import * as React from 'react'; + +import type { ButtonProps } from '../../../components/button.react'; +import Button from '../../../components/button.react'; +import css from './submit-button.css'; + +type Props = { + ...ButtonProps, + +errorMessage?: string, + +containerClassName?: string, +}; + +function SubmitButton(props: Props): React.Node { + const { + children, + containerClassName = '', + errorMessage, + onClick, + variant, + type, + disabled = false, + className = '', + } = props; + + const containerStyle = classnames({ + [css.container]: true, + [containerClassName]: !!containerClassName, + }); + + const buttonStyle = classnames({ + [css.button]: true, + [className]: !!className, + }); + + return ( + <div className={containerStyle}> + <div className={css.error}> {errorMessage} </div> + <Button + type={type} + className={buttonStyle} + onClick={onClick} + disabled={disabled} + variant={variant} + > + {children} + </Button> + </div> + ); +} + +export default SubmitButton; diff --git a/web/modals/threads/settings/thread-settings-delete-tab.css b/web/modals/threads/settings/thread-settings-delete-tab.css --- a/web/modals/threads/settings/thread-settings-delete-tab.css +++ b/web/modals/threads/settings/thread-settings-delete-tab.css @@ -19,13 +19,15 @@ margin-bottom: 12px; } +form.container { + height: 100%; + display: flex; + flex-direction: column; +} + .deletion_warning { font-weight: var(--bold); color: var(--fg); font-size: var(--s-font-14); margin-bottom: 16px; } - -.delete_button { - width: 100%; -} diff --git a/web/modals/threads/settings/thread-settings-delete-tab.react.js b/web/modals/threads/settings/thread-settings-delete-tab.react.js --- a/web/modals/threads/settings/thread-settings-delete-tab.react.js +++ b/web/modals/threads/settings/thread-settings-delete-tab.react.js @@ -13,16 +13,17 @@ useServerCall, } from 'lib/utils/action-utils'; -import Button from '../../../components/button.react'; import SWMansionIcon from '../../../SWMansionIcon.react'; import Input from '../../input.react'; import { useModalContext } from '../../modal-provider.react'; +import SubmitButton from './submit-button.react'; import css from './thread-settings-delete-tab.css'; type ThreadSettingsDeleteTabProps = { +threadSettingsOperationInProgress: boolean, +threadInfo: ThreadInfo, +setErrorMessage: SetState<string>, + +errorMessage: string, }; function ThreadSettingsDeleteTab( @@ -32,6 +33,7 @@ threadSettingsOperationInProgress, threadInfo, setErrorMessage, + errorMessage, } = props; const modalContext = useModalContext(); @@ -83,7 +85,7 @@ ); return ( - <form method="POST"> + <form method="POST" className={css.container}> <div> <SWMansionIcon icon="warning-circle" size={22} /> <p className={css.deletion_warning}> @@ -107,14 +109,14 @@ /> </div> </div> - <Button + <SubmitButton + errorMessage={errorMessage} onClick={onDelete} variant="danger" disabled={threadSettingsOperationInProgress} - className={css.delete_button} > Delete - </Button> + </SubmitButton> </form> ); } diff --git a/web/modals/threads/settings/thread-settings-general-tab.css b/web/modals/threads/settings/thread-settings-general-tab.css --- a/web/modals/threads/settings/thread-settings-general-tab.css +++ b/web/modals/threads/settings/thread-settings-general-tab.css @@ -27,7 +27,7 @@ margin-bottom: 12px; } -.save_button { - width: 100%; - min-height: 46px; +form.container { + display: flex; + flex-direction: column; } diff --git a/web/modals/threads/settings/thread-settings-general-tab.react.js b/web/modals/threads/settings/thread-settings-general-tab.react.js --- a/web/modals/threads/settings/thread-settings-general-tab.react.js +++ b/web/modals/threads/settings/thread-settings-general-tab.react.js @@ -20,10 +20,10 @@ } from 'lib/utils/action-utils'; import { firstLine } from 'lib/utils/string-utils'; -import Button from '../../../components/button.react'; import LoadingIndicator from '../../../loading-indicator.react'; import Input from '../../input.react'; import ColorSelector from '../color-selector.react'; +import SubmitButton from './submit-button.react'; import css from './thread-settings-general-tab.css'; type ThreadSettingsGeneralTabProps = { @@ -33,6 +33,7 @@ +queuedChanges: ThreadChanges, +setQueuedChanges: SetState<ThreadChanges>, +setErrorMessage: SetState<string>, + +errorMessage: string, }; function ThreadSettingsGeneralTab( props: ThreadSettingsGeneralTabProps, @@ -44,6 +45,7 @@ queuedChanges, setQueuedChanges, setErrorMessage, + errorMessage, } = props; const dispatchActionPromise = useDispatchActionPromise(); @@ -145,7 +147,7 @@ }, [threadSettingsOperationInProgress]); return ( - <form method="POST"> + <form method="POST" className={css.container}> <div> <div className={css.form_title}>Chat name</div> <div className={css.form_content}> @@ -182,14 +184,14 @@ /> </div> </div> - <Button + <SubmitButton + errorMessage={errorMessage} type="submit" onClick={onSubmit} disabled={threadSettingsOperationInProgress || !changeQueued} - className={css.save_button} > {saveButtonContent} - </Button> + </SubmitButton> </form> ); } diff --git a/web/modals/threads/settings/thread-settings-modal.css b/web/modals/threads/settings/thread-settings-modal.css --- a/web/modals/threads/settings/thread-settings-modal.css +++ b/web/modals/threads/settings/thread-settings-modal.css @@ -2,18 +2,23 @@ display: flex; flex-direction: column; width: 383px; - height: 539px; + height: 558px; overflow: hidden; } div.tab_body { padding: 20px; overflow: auto; + height: 100%; + display: grid; } div.modal_form_error { display: flex; + align-items: flex-end; justify-content: center; - padding-top: 8px; font-size: 16px; - color: red; + color: var(--error); font-style: italic; + min-height: 26px; + height: 100%; + padding: 5px; } diff --git a/web/modals/threads/settings/thread-settings-modal.react.js b/web/modals/threads/settings/thread-settings-modal.react.js --- a/web/modals/threads/settings/thread-settings-modal.react.js +++ b/web/modals/threads/settings/thread-settings-modal.react.js @@ -144,8 +144,8 @@ queuedChanges={queuedChanges} setQueuedChanges={setQueuedChanges} setErrorMessage={setErrorMessage} + errorMessage={errorMessage} /> - <div className={css.modal_form_error}>{errorMessage}</div> </div> </Tabs.Item>, ]; @@ -169,8 +169,8 @@ queuedChanges={queuedChanges} setQueuedChanges={setQueuedChanges} setErrorMessage={setErrorMessage} + errorMessage={errorMessage} /> - <div className={css.modal_form_error}>{errorMessage}</div> </div> </Tabs.Item>, ); @@ -200,8 +200,8 @@ threadSettingsOperationInProgress={changeInProgress} threadInfo={threadInfo} setErrorMessage={setErrorMessage} + errorMessage={errorMessage} /> - <div className={css.modal_form_error}>{errorMessage}</div> </div> </Tabs.Item>, ); diff --git a/web/modals/threads/settings/thread-settings-privacy-tab.css b/web/modals/threads/settings/thread-settings-privacy-tab.css --- a/web/modals/threads/settings/thread-settings-privacy-tab.css +++ b/web/modals/threads/settings/thread-settings-privacy-tab.css @@ -8,6 +8,7 @@ padding: 12px 0; } -.save_button { - width: 100%; +form.container { + display: flex; + flex-direction: column; } diff --git a/web/modals/threads/settings/thread-settings-privacy-tab.react.js b/web/modals/threads/settings/thread-settings-privacy-tab.react.js --- a/web/modals/threads/settings/thread-settings-privacy-tab.react.js +++ b/web/modals/threads/settings/thread-settings-privacy-tab.react.js @@ -18,10 +18,10 @@ useServerCall, } from 'lib/utils/action-utils'; -import Button from '../../../components/button.react'; import EnumSettingsOption from '../../../components/enum-settings-option.react'; import SWMansionIcon from '../../../SWMansionIcon.react'; import { useModalContext } from '../../modal-provider.react'; +import SubmitButton from './submit-button.react'; import css from './thread-settings-privacy-tab.css'; const { COMMUNITY_OPEN_SUBTHREAD, COMMUNITY_SECRET_SUBTHREAD } = threadTypes; @@ -48,6 +48,7 @@ +queuedChanges: ThreadChanges, +setQueuedChanges: SetState<ThreadChanges>, +setErrorMessage: SetState<string>, + +errorMessage: string, }; function ThreadSettingsPrivacyTab( props: ThreadSettingsPrivacyTabProps, @@ -58,6 +59,7 @@ queuedChanges, setQueuedChanges, setErrorMessage, + errorMessage, } = props; const modalContext = useModalContext(); @@ -138,7 +140,7 @@ ); return ( - <form method="POST"> + <form method="POST" className={css.container}> <div className={css.form_title}>Chat type</div> <div className={css.enum_container}> <EnumSettingsOption @@ -162,14 +164,14 @@ /> </div> - <Button + <SubmitButton type="submit" onClick={onSubmit} disabled={threadSettingsOperationInProgress || !changeQueued} - className={css.save_button} + errorMessage={errorMessage} > Save - </Button> + </SubmitButton> </form> ); }