diff options
author | 2021-01-06 08:00:31 +0300 | |
---|---|---|
committer | 2021-01-06 09:36:01 +0300 | |
commit | 996c14afb9d81e962ef66b99bd869bce4f3688f0 (patch) | |
tree | ea19a979c62a0da3685a1ea44a624ee2863320cc /src/components | |
parent | Fixes Model Casing (diff) |
Breaks Up CSS Into Components
Moves the styles from the CSS file, into emotion CSS in each component's
file to make navigation easier, and keep CSS and JSX together.Drops
raw-loader dependency.
Signed-off-by: Hassan Abouelela <[email protected]>
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/InputTypes/Checkbox.tsx | 62 | ||||
-rw-r--r-- | src/components/InputTypes/Radio.tsx | 4 | ||||
-rw-r--r-- | src/components/InputTypes/Range.tsx | 101 | ||||
-rw-r--r-- | src/components/InputTypes/Select.tsx | 109 | ||||
-rw-r--r-- | src/components/InputTypes/ShortText.tsx | 3 | ||||
-rw-r--r-- | src/components/InputTypes/TextArea.tsx | 14 | ||||
-rw-r--r-- | src/components/Question.tsx | 61 | ||||
-rw-r--r-- | src/components/ScrollToTop.tsx | 3 |
8 files changed, 315 insertions, 42 deletions
diff --git a/src/components/InputTypes/Checkbox.tsx b/src/components/InputTypes/Checkbox.tsx index ed02b83..07872d6 100644 --- a/src/components/InputTypes/Checkbox.tsx +++ b/src/components/InputTypes/Checkbox.tsx @@ -1,6 +1,7 @@ /** @jsx jsx */ -import { jsx } from "@emotion/react"; +import { jsx, css } from "@emotion/react"; import React, { ChangeEvent } from "react"; +import colors from "../../colors"; interface CheckboxProps { index: number, @@ -8,13 +9,66 @@ interface CheckboxProps { handler: (event: ChangeEvent<HTMLInputElement>) => void } +const generalStyles = css` + label { + display: inline-block; + position: relative; + top: 0.25em; + + width: 1em; + height: 1em; + + margin: 1rem 0.5rem 0 0; + border: whitesmoke 0.2rem solid; + border-radius: 25%; + + transition: background-color 300ms; + } + + .unselected { + background-color: white; + } + + .unselected:hover { + background-color: lightgray; + } + + input { + position: absolute; + opacity: 0; + height: 0; + width: 0; + } + + .checkmark { + position: absolute; + } +`; + +const activeStyles = css` + .selected { + background-color: ${colors.blurple}; + } + + .selected .checkmark { + width: 0.30rem; + height: 0.60rem; + left: 0.25em; + + border: solid white; + border-width: 0 0.2rem 0.2rem 0; + + transform: rotate(45deg); + } +`; + export default function Checkbox(props: CheckboxProps): JSX.Element { return ( - <label> - <label className="unselected_checkbox_label checkbox_label unselectable"> + <label css={[generalStyles, activeStyles]}> + <label className="unselected"> <input type="checkbox" value={props.option} name={`${("000" + props.index).slice(-4)}. ${props.option}`} onChange={props.handler}/> - <span className="checkmark_span"/> + <span className="checkmark"/> </label> {props.option}<br/> </label> diff --git a/src/components/InputTypes/Radio.tsx b/src/components/InputTypes/Radio.tsx index 81f8375..be0f60e 100644 --- a/src/components/InputTypes/Radio.tsx +++ b/src/components/InputTypes/Radio.tsx @@ -1,5 +1,5 @@ /** @jsx jsx */ -import { jsx } from "@emotion/react"; +import { jsx, css } from "@emotion/react"; import React, { ChangeEvent } from "react"; interface RadioProps { @@ -11,7 +11,7 @@ interface RadioProps { export default function Radio(props: RadioProps): JSX.Element { return ( <label> - <input type="radio" name={props.question_id} className="radio" onChange={props.handler}/> + <input type="radio" name={props.question_id} css={css`margin: 1rem 0.5rem 0 0;`} onChange={props.handler}/> {props.option}<br/> </label> ); diff --git a/src/components/InputTypes/Range.tsx b/src/components/InputTypes/Range.tsx index a0260ad..af46b05 100644 --- a/src/components/InputTypes/Range.tsx +++ b/src/components/InputTypes/Range.tsx @@ -1,6 +1,7 @@ /** @jsx jsx */ -import { jsx } from "@emotion/react"; +import { jsx, css } from "@emotion/react"; import React from "react"; +import colors from "../../colors"; interface RangeProps { question_id: string, @@ -8,13 +9,12 @@ interface RangeProps { state_dict: Map<string, string | boolean | null> } -let last_selection: Element; - interface handler_props { state_dict: Map<string, string | boolean | null>, ref: React.RefObject<HTMLLabelElement> } +let last_selection: Element; function handler(this: handler_props): void { if (last_selection) { last_selection.classList.toggle("selected"); @@ -29,23 +29,108 @@ function handler(this: handler_props): void { this.state_dict.set("value", value); } +const containerStyles = css` + display: flex; + justify-content: space-between; + position: relative; + width: 100%; + + @media (max-width: 800px) { + width: 20%; + display: block; + margin: 0 auto; + + label span { + margin-left: 0; + transform: translateY(1.6rem) translateX(2rem); + } + } +`; + +const optionStyles = css` + display: inline-block; + transform: translateX(-50%); + margin: 0 50%; + + white-space: nowrap; + + transition: transform 300ms; +`; + +const rangeDotStyles = css` + .range_dot { + width: 0.8rem; + height: 0.8rem; + background-color: whitesmoke; + + border: 0.2rem solid whitesmoke; + border-radius: 50%; + + transition: background-color 300ms; + } + + .range_dot.selected { + background-color: ${colors.blurple}; + } + + @media (max-width: 800px) { + .range_dot { + margin-bottom: 1.5rem; + } + } +`; + +const sliderContainerStyles = css` + display: flex; + justify-content: center; + width: 100%; + + position: absolute; + z-index: -1; + + top: 2rem; + + transition: all 300ms; + + @media (max-width: 800px) { + width: 0.5rem; + height: 88%; + + left: 0.32rem; + + background: whitesmoke; + } +`; + +const sliderStyles = css` + width: 98%; /* Needs to be slightly smaller than container to work on all devices */ + height: 0.5rem; + background-color: whitesmoke; + + transition: transform 300ms; + + @media (max-width: 800px) { + display: none; + } +`; + export default function Range(props: RangeProps): JSX.Element { const range = props.options.map((option, index) => { const ref: React.RefObject<HTMLLabelElement> = React.createRef(); return ( - <label key={index} ref={ref} onClick={handler.bind({state_dict: props.state_dict, ref: ref})}> - <span>{option}</span> + <label key={index} ref={ref} css={css`width: 1rem;`} onClick={handler.bind({state_dict: props.state_dict, ref})}> + <span css={optionStyles}>{option}</span> <div className="range_dot"/> </label> ); }); return ( - <div className="range"> + <div css={[containerStyles, rangeDotStyles]}> { range } - <div className="range_slider_container"> - <div className="range_slider"/> + <div css={sliderContainerStyles}> + <div css={sliderStyles}/> </div> </div> ); diff --git a/src/components/InputTypes/Select.tsx b/src/components/InputTypes/Select.tsx index 355f5d0..de763bf 100644 --- a/src/components/InputTypes/Select.tsx +++ b/src/components/InputTypes/Select.tsx @@ -1,5 +1,5 @@ /** @jsx jsx */ -import { jsx } from "@emotion/react"; +import { jsx, css } from "@emotion/react"; import React from "react"; interface SelectProps { @@ -12,11 +12,86 @@ interface HandlerProps { ref: React.RefObject<HTMLDivElement> } -class Select extends React.Component<SelectProps> { - constructor(props: SelectProps) { - super(props); - } +const containerStyles = css` + .container { + display: inline-block; + position: relative; + + width: min(20rem, 90%); + height: 100%; + min-height: 2rem; + + background: whitesmoke; + + color: black; + text-align: center; + + margin-bottom: 0; + + border: 0.1rem solid black; + border-radius: 8px; + + transition: border-radius 400ms; + } + + .container.active { + height: auto; + border-radius: 8px 8px 0 0; + } +`; + +const arrowStyles = css` + .arrow { + display: inline-block; + height: 0.5rem; + width: 0.5rem; + + position: relative; + float: right; + right: 1em; + top: 0.7rem; + + border: solid black; + border-width: 0 0.2rem 0.2rem 0; + transform: rotate(45deg); + transition: transform 400ms; + } + + .active .arrow { + transform: translateY(40%) rotate(225deg); + } +`; + +const optionContainer = css` + .option_container { + display: block; + position: absolute; + width: 100%; + + /* Need to account for margin */ + left: -0.1rem; + + visibility: hidden; + opacity: 0; + + background: whitesmoke; + overflow: hidden; + + border: 0.1rem solid black; + border-radius: 0 0 8px 8px; + border-top: none; + + transition: opacity 400ms, visibility 400ms; + } + + .active .option_container { + visibility: visible; + opacity: 1; + } +`; + +class Select extends React.Component<SelectProps> { click_handler(this: HandlerProps, event: React.MouseEvent<HTMLDivElement, MouseEvent>): void { if (!this.ref.current) { return; @@ -43,18 +118,24 @@ class Select extends React.Component<SelectProps> { render(): JSX.Element { const container_ref: React.RefObject<HTMLDivElement> = React.createRef(); - return ( - <div className="select_container" ref={container_ref} onClick={this.click_handler.bind({ref: container_ref, props: this.props})}> - <span className="select_arrow"/> - <span className="selected_option">...</span> - <div className="select_options_container"> - <div className="select_options"> - { this.props.options.map((option, index) => <div key={index}><hr/><div id="option">{option}</div></div>) } - </div> + const element: JSX.Element = ( + <div className="container" ref={container_ref} onClick={this.click_handler.bind({ref: container_ref, props: this.props})}> + <span className="arrow"/> + <span className="selected_option" css={css`display: block; padding: 0.5rem 0;`}>...</span> + + <div className="option_container"> + { this.props.options.map((option, index) => ( + <div css={css`:hover { background-color: lightgray; }`} key={index}> + <hr css={css`margin: 0 1rem;`}/> + <div id="option" css={css`padding: 0.75rem;`}>{option}</div> + </div> + )) } </div> </div> ); + + return <div css={[containerStyles, arrowStyles, optionContainer]}>{ element }</div>; } } -export default Select;
\ No newline at end of file +export default Select; diff --git a/src/components/InputTypes/ShortText.tsx b/src/components/InputTypes/ShortText.tsx index 182571a..1e38bcd 100644 --- a/src/components/InputTypes/ShortText.tsx +++ b/src/components/InputTypes/ShortText.tsx @@ -1,11 +1,12 @@ /** @jsx jsx */ import { jsx } from "@emotion/react"; import React, { ChangeEvent } from "react"; +import { textInputs } from "../../commonStyles"; interface ShortTextProps { handler: (event: ChangeEvent<HTMLInputElement>) => void } export default function ShortText(props: ShortTextProps): JSX.Element { - return <input type="text" className="short_text" placeholder="Enter Text..." onChange={props.handler}/>; + return <input type="text" css={textInputs} placeholder="Enter Text..." onChange={props.handler}/>; } diff --git a/src/components/InputTypes/TextArea.tsx b/src/components/InputTypes/TextArea.tsx index 550adea..2b7a5f6 100644 --- a/src/components/InputTypes/TextArea.tsx +++ b/src/components/InputTypes/TextArea.tsx @@ -1,11 +1,21 @@ /** @jsx jsx */ -import { jsx } from "@emotion/react"; +import { jsx, css } from "@emotion/react"; import React, { ChangeEvent } from "react"; +import { textInputs } from "../../commonStyles"; interface TextAreaProps { handler: (event: ChangeEvent<HTMLTextAreaElement>) => void } +const styles = css` + min-height: 20rem; + min-width: 40%; + width: 100%; + box-sizing: border-box; + + padding: 1rem; +`; + export default function TextArea(props: TextAreaProps): JSX.Element { - return <textarea className="text_area" placeholder="Enter Text..." onChange={props.handler}/>; + return <textarea css={[textInputs, styles]} placeholder="Enter Text..." onChange={props.handler}/>; } diff --git a/src/components/Question.tsx b/src/components/Question.tsx index 1c0fb31..66c1668 100644 --- a/src/components/Question.tsx +++ b/src/components/Question.tsx @@ -1,8 +1,9 @@ /** @jsx jsx */ -import { jsx } from "@emotion/react"; +import { jsx, css } from "@emotion/react"; import React, { ChangeEvent } from "react"; import { Question, QuestionType } from "../api/question"; +import { selectable } from "../commonStyles"; import create_input from "./InputTypes"; const _skip_normal_state: Array<QuestionType> = [ @@ -37,6 +38,7 @@ class RenderedQuestion extends React.Component<QuestionProp> { this.props.public_state.set(target, value); } + // This is here to allow dynamic selection between the general handler, and the textarea handler. handler(_: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void {} // eslint-disable-line normal_handler(event: ChangeEvent<HTMLInputElement>): void { @@ -71,8 +73,8 @@ class RenderedQuestion extends React.Component<QuestionProp> { // Toggle checkbox class if (event.target.type == "checkbox" && event.target.parentElement !== null) { - event.target.parentElement.classList.toggle("unselected_checkbox_label"); - event.target.parentElement.classList.toggle("selected_checkbox_label"); + event.target.parentElement.classList.toggle("unselected"); + event.target.parentElement.classList.toggle("selected"); } } @@ -109,17 +111,56 @@ class RenderedQuestion extends React.Component<QuestionProp> { const question = this.props.question; if (question.type === QuestionType.Section) { - return <div> - <h1 className="selectable">{question.name}</h1> - { question.data["text"] ? <h3 className="selectable">{question.data["text"]}</h3> : "" } - <hr className="section_header"/> + const styles = css` + h1 { + margin-bottom: 0; + } + + h3 { + margin-top: 0; + } + + h1, h3 { + text-align: center; + padding: 0 2rem; + } + + @media (max-width: 500px) { + h1, h3 { + padding: 0; + } + } + `; + + return <div css={styles}> + <h1 css={selectable}>{question.name}</h1> + { question.data["text"] ? <h3 css={selectable}>{question.data["text"]}</h3> : "" } + <hr css={css`color: gray; margin: 3rem 0;`}/> </div>; + } else { + const requiredStarStyles = css` + span { + display: none; + } + + .required { + display: inline-block; + position: relative; + + color: red; + + top: -0.2rem; + margin-left: 0.2rem; + } + `; + return <div> - <h2 className="selectable"> - {question.name}<span id={question.required ? "required" : ""} className="required_star">*</span> + <h2 css={[selectable, requiredStarStyles]}> + {question.name}<span className={question.required ? "required" : ""}>*</span> </h2> - { create_input(this.props, this.handler) }<hr/> + { create_input(this.props, this.handler) } + <hr css={css`color: gray; margin: 3rem 0;`}/> </div>; } } diff --git a/src/components/ScrollToTop.tsx b/src/components/ScrollToTop.tsx index 6af938d..4888dec 100644 --- a/src/components/ScrollToTop.tsx +++ b/src/components/ScrollToTop.tsx @@ -1,6 +1,7 @@ /** @jsx jsx */ import { jsx, css } from "@emotion/react"; import React from "react"; +import colors from "../colors"; const styles = css` width: 2.5rem; @@ -10,7 +11,7 @@ const styles = css` bottom: 3rem; right: 3rem; - background-color: #7289DA; /* Blurple */ + background-color: ${colors.blurple}; border-radius: 50%; opacity: 0; |