diff options
Diffstat (limited to 'src/settings/components/ui')
-rw-r--r-- | src/settings/components/ui/AddButton.scss | 13 | ||||
-rw-r--r-- | src/settings/components/ui/AddButton.tsx | 31 | ||||
-rw-r--r-- | src/settings/components/ui/DeleteButton.scss | 13 | ||||
-rw-r--r-- | src/settings/components/ui/DeleteButton.tsx | 30 | ||||
-rw-r--r-- | src/settings/components/ui/Input.scss | 29 | ||||
-rw-r--r-- | src/settings/components/ui/Input.tsx | 89 | ||||
-rw-r--r-- | src/settings/components/ui/Radio.tsx | 33 | ||||
-rw-r--r-- | src/settings/components/ui/Text.tsx | 54 | ||||
-rw-r--r-- | src/settings/components/ui/TextArea.tsx | 56 |
9 files changed, 178 insertions, 170 deletions
diff --git a/src/settings/components/ui/AddButton.scss b/src/settings/components/ui/AddButton.scss deleted file mode 100644 index beb5688..0000000 --- a/src/settings/components/ui/AddButton.scss +++ /dev/null @@ -1,13 +0,0 @@ -.ui-add-button { - border: none; - padding: 4; - display: inline; - background: none; - font-weight: bold; - color: green; - cursor: pointer; - - &:hover { - color: darkgreen; - } -} diff --git a/src/settings/components/ui/AddButton.tsx b/src/settings/components/ui/AddButton.tsx index c15a732..8cf4300 100644 --- a/src/settings/components/ui/AddButton.tsx +++ b/src/settings/components/ui/AddButton.tsx @@ -1,19 +1,24 @@ -import "./AddButton.scss"; import React from "react"; +import styled from "styled-components"; -type Props = React.AllHTMLAttributes<HTMLInputElement>; +const Button = styled.input` + border: none; + padding: 4; + display: inline; + background: none; + font-weight: bold; + color: green; + cursor: pointer; -class AddButton extends React.Component<Props> { - render() { - return ( - <input - className="ui-add-button" - type="button" - value="✚" - {...this.props} - /> - ); + &:hover { + color: darkgreen; } -} +`; + +type Props = React.InputHTMLAttributes<HTMLInputElement>; + +const AddButton: React.FC<Props> = (props) => ( + <Button type="button" value="✚" {...props} /> +); export default AddButton; diff --git a/src/settings/components/ui/DeleteButton.scss b/src/settings/components/ui/DeleteButton.scss deleted file mode 100644 index 5932a72..0000000 --- a/src/settings/components/ui/DeleteButton.scss +++ /dev/null @@ -1,13 +0,0 @@ - -.ui-delete-button { - border: none; - padding: 4; - display: inline; - background: none; - color: red; - cursor: pointer; - - &:hover { - color: darkred; - } -} diff --git a/src/settings/components/ui/DeleteButton.tsx b/src/settings/components/ui/DeleteButton.tsx index df8976e..ce0183b 100644 --- a/src/settings/components/ui/DeleteButton.tsx +++ b/src/settings/components/ui/DeleteButton.tsx @@ -1,19 +1,23 @@ -import "./DeleteButton.scss"; import React from "react"; +import styled from "styled-components"; -type Props = React.AllHTMLAttributes<HTMLInputElement>; +const Button = styled.input` + border: none; + padding: 4; + display: inline; + background: none; + color: red; + cursor: pointer; -class DeleteButton extends React.Component<Props> { - render() { - return ( - <input - className="ui-delete-button" - type="button" - value="✖" - {...this.props} - /> - ); + &:hover { + color: darkred; } -} +`; + +type Props = React.InputHTMLAttributes<HTMLInputElement>; + +const DeleteButton: React.FC<Props> = (props) => ( + <Button type="button" value="✖" {...props} /> +); export default DeleteButton; diff --git a/src/settings/components/ui/Input.scss b/src/settings/components/ui/Input.scss deleted file mode 100644 index ad4daf8..0000000 --- a/src/settings/components/ui/Input.scss +++ /dev/null @@ -1,29 +0,0 @@ -.settings-ui-input { - page-break-inside: avoid; - - * { - page-break-inside: avoid; - } - - label { - font-weight: bold; - min-width: 14rem; - display: inline-block; - } - - input[type='text'] { - padding: 4px; - width: 8rem; - } - - input.input-crror, - textarea.input-error { - box-shadow: 0 0 2px red; - } - - &-error { - font-weight: bold; - color: red; - min-height: 1.5em; - } -} diff --git a/src/settings/components/ui/Input.tsx b/src/settings/components/ui/Input.tsx deleted file mode 100644 index 0e24277..0000000 --- a/src/settings/components/ui/Input.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React from "react"; -import "./Input.scss"; - -interface Props extends React.AllHTMLAttributes<HTMLElement> { - name: string; - type: string; - error?: string; - label: string; - value: string; - onValueChange?: (name: string, value: string) => void; - onBlur?: (e: React.FocusEvent<Element>) => void; -} - -class Input extends React.Component<Props> { - renderText(props: Props) { - const inputClassName = props.error ? "input-error" : ""; - const pp = { ...props }; - delete pp.onValueChange; - return ( - <div className="settings-ui-input"> - <label htmlFor={props.id}>{props.label}</label> - <input - className={inputClassName} - onChange={this.bindOnChange.bind(this)} - {...pp} - /> - </div> - ); - } - - renderRadio(props: Props) { - const inputClassName = props.error ? "input-error" : ""; - const pp = { ...props }; - delete pp.onValueChange; - return ( - <div className="settings-ui-input"> - <label> - <input - className={inputClassName} - onChange={this.bindOnChange.bind(this)} - {...pp} - /> - {props.label} - </label> - </div> - ); - } - - renderTextArea(props: Props) { - const inputClassName = props.error ? "input-error" : ""; - const pp = { ...props }; - delete pp.onValueChange; - return ( - <div className="settings-ui-input"> - <label htmlFor={props.id}>{props.label}</label> - <textarea - className={inputClassName} - onChange={this.bindOnChange.bind(this)} - {...pp} - /> - <p className="settings-ui-input-error">{this.props.error}</p> - </div> - ); - } - - render() { - const { type } = this.props; - - switch (this.props.type) { - case "text": - return this.renderText(this.props); - case "radio": - return this.renderRadio(this.props); - case "textarea": - return this.renderTextArea(this.props); - default: - console.warn(`Unsupported input type ${type}`); - } - return null; - } - - bindOnChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) { - if (this.props.onValueChange) { - this.props.onValueChange(e.target.name, e.target.value); - } - } -} - -export default Input; diff --git a/src/settings/components/ui/Radio.tsx b/src/settings/components/ui/Radio.tsx new file mode 100644 index 0000000..c0d4dd9 --- /dev/null +++ b/src/settings/components/ui/Radio.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import styled from "styled-components"; + +const Container = styled.div` + font-family: system-ui; +`; + +interface Props extends React.InputHTMLAttributes<HTMLInputElement> { + label: string; + onValueChange?: (name: string, value: string) => void; +} + +const Radio: React.FC<Props> = (props) => { + const onChange = (e: React.ChangeEvent<HTMLInputElement>) => { + if (props.onValueChange) { + props.onValueChange(e.target.name, e.target.value); + } + }; + + const pp = { ...props }; + delete pp.onValueChange; + + return ( + <Container> + <label htmlFor={props.id}> + <input type="radio" onChange={onChange} {...pp} /> + {props.label} + </label> + </Container> + ); +}; + +export default Radio; diff --git a/src/settings/components/ui/Text.tsx b/src/settings/components/ui/Text.tsx new file mode 100644 index 0000000..700b08a --- /dev/null +++ b/src/settings/components/ui/Text.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import styled from "styled-components"; + +const Container = styled.div` + page-break-inside: avoid; +`; + +const Input = styled.input<{ hasError: boolean }>` + padding: 4px; + width: 8rem; + box-shadow: ${({ hasError }) => (hasError ? "0 0 2px red" : "none")}; +`; + +const Label = styled.label` + font-weight: bold; + min-width: 14rem; + display: inline-block; +`; + +interface Props extends React.HTMLAttributes<HTMLElement> { + name: string; + error?: string; + label: string; + value: string; + onValueChange?: (name: string, value: string) => void; +} + +const Text: React.FC<Props> = (props) => { + const onChange = (e: React.ChangeEvent<HTMLInputElement>) => { + if (props.onValueChange) { + props.onValueChange(e.target.name, e.target.value); + } + }; + + const pp = { ...props }; + delete pp.onValueChange; + + return ( + <Container> + <Label> + {props.label} + <br /> + <Input + type="text" + hasError={props.error !== undefined} + onChange={onChange} + {...pp} + /> + </Label> + </Container> + ); +}; + +export default Text; diff --git a/src/settings/components/ui/TextArea.tsx b/src/settings/components/ui/TextArea.tsx new file mode 100644 index 0000000..9dcd5be --- /dev/null +++ b/src/settings/components/ui/TextArea.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import styled from "styled-components"; + +const Container = styled.div` + page-break-inside: avoid; +`; + +const Label = styled.label` + font-weight: bold; + min-width: 14rem; + display: inline-block; +`; + +const ErrorableTextArea = styled.textarea<{ hasError: boolean }>` + box-shadow: ${({ hasError }) => (hasError ? "0 0 2px red" : "none")}; + font-family: monospace; + font-family: monospace; + width: 100%; + min-height: 64ex; + resize: vertical; +`; + +const ErrorMessage = styled.p` + font-weight: bold; + color: red; + min-height: 1.5em; +`; + +interface Props extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { + error?: string; + label: string; + onValueChange?: (name: string, value: string) => void; +} + +const TextArea: React.FC<Props> = (props) => { + const onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { + if (props.onValueChange) { + props.onValueChange(e.target.name, e.target.value); + } + }; + + const hasError = typeof props.error !== "undefined" && props.error !== ""; + const pp = { ...props }; + delete pp.onValueChange; + return ( + <Container> + <Label htmlFor={props.id}>{props.label}</Label> + <ErrorableTextArea hasError={hasError} onChange={onChange} {...pp} /> + {hasError ? ( + <ErrorMessage role="alert">{props.error}</ErrorMessage> + ) : null} + </Container> + ); +}; + +export default TextArea; |