diff --git a/app/controllers/api/v1/contacts_controller.rb b/app/controllers/api/v1/contacts_controller.rb index 9b6050af6..312c642b3 100644 --- a/app/controllers/api/v1/contacts_controller.rb +++ b/app/controllers/api/v1/contacts_controller.rb @@ -8,20 +8,15 @@ def enum end def create - @contact = Contact.new(contact_params) - - if @contact.save - head :created - else - render :new - end + Contact.create!(contact_params) + head :created end private # Never trust parameters from the scary internet, only allow the white list through. def contact_params - params.require(:contact).permit( + params.permit( :corner, :message, :nickname, diff --git a/app/models/contact.rb b/app/models/contact.rb index 8ea5b9452..08ad6336f 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -18,6 +18,7 @@ class Contact < ApplicationRecord validates :corner, presence: true validates :message, presence: true validates :department, presence: true + validates :grade, presence: true bind_inum :corner, ContactCorners bind_inum :department, ContactDepartments diff --git a/frontend/package.json b/frontend/package.json index 43dea5bf4..e237458d8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,41 +12,45 @@ "lint": "npx tslint --fix -c ./tslint.json 'src/**/*{.ts,.tsx}'" }, "devDependencies": { + "@types/react": "^16.8.17", + "@types/react-dom": "^16.8.4", + "@types/react-router-dom": "^4.3.3", + "@types/react-slick": "^0.23.4", + "@types/styled-components": "^4.1.15", "awesome-typescript-loader": "^5.0.0", "babel-loader": "^7.1.4", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", + "clean-webpack-plugin": "^1.0.0", "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "prettier": "^1.15.3", "source-map-loader": "^0.2.3", "style-loader": "^0.21.0", + "tslint": "^5.11.0", + "tslint-config-prettier": "^1.17.0", + "tslint-plugin-prettier": "^2.0.1", "typescript": "^3.4.5", + "url-loader": "^1.0.1", "webpack": "^4.10.2", - "webpack-cli": "^2.1.4" + "webpack-cli": "^2.1.4", + "webpack-dev-server": "^3.1.10", + "webpack-manifest-plugin": "^2.0.4", + "webpack-merge": "^4.1.4" }, "dependencies": { - "@types/react": "^16.3.16", - "@types/react-dom": "^16.0.5", - "@types/react-router-dom": "^4.2.7", - "@types/react-slick": "^0.23.4", - "@types/styled-components": "^4.1.6", + "axios": "0.19.0-beta.1", "babel-polyfill": "^6.26.0", - "clean-webpack-plugin": "^1.0.0", - "file-loader": "^1.1.11", + "mobx": "^5.9.4", + "mobx-react": "^5.4.4", + "mobx-react-lite": "^1.3.2", "normalize.css": "^8.0.0", - "prettier": "^1.15.3", - "react": "^16.4.0", - "react-dom": "^16.4.0", - "react-router-dom": "^4.3.1", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "react-router-dom": "^5.0.0", "react-slick": "^0.24.0", "react-twitter-widgets": "^1.7.1", "slick-carousel": "^1.8.1", - "styled-components": "^4.1.3", - "tslint": "^5.11.0", - "tslint-config-prettier": "^1.17.0", - "tslint-plugin-prettier": "^2.0.1", - "url-loader": "^1.0.1", - "webpack-dev-server": "^3.1.10", - "webpack-manifest-plugin": "^2.0.4", - "webpack-merge": "^4.1.4" + "styled-components": "^4.2.0" } } diff --git a/frontend/src/api/ContactApi.ts b/frontend/src/api/ContactApi.ts new file mode 100644 index 000000000..5bdd9338b --- /dev/null +++ b/frontend/src/api/ContactApi.ts @@ -0,0 +1,28 @@ +import RestClient from "./RestClient"; + +export interface IContactEnum { + corners: { [key: string]: number }; + departments: { [key: string]: number }; + grades: { [key: string]: number }; +} + +export default class ContactApi { + public restClient: RestClient; + + constructor(restClient: RestClient) { + this.restClient = restClient; + } + + public saveContact( + json: object, + succussed: (res: object) => void, + errored: (err: object) => void, + always = () => {} + ) { + return this.restClient.post("/api/v1/contacts", json, succussed, errored, always); + } + + public fetchContactEnum() { + return this.restClient.get("/api/v1/contacts/enum"); + } +} diff --git a/frontend/src/api/RestClient.ts b/frontend/src/api/RestClient.ts new file mode 100644 index 000000000..0092f26a4 --- /dev/null +++ b/frontend/src/api/RestClient.ts @@ -0,0 +1,94 @@ +import axios, { AxiosInstance } from "axios"; + +export default class RestClient { + public axios: AxiosInstance; + + constructor(baseUrl: string) { + const csrfToken = (document.querySelector("meta[name=csrf-token]") as HTMLMetaElement).content; + + this.axios = axios.create({ + baseURL: baseUrl, + timeout: 1000, + headers: { + "Content-Type": "application/json", + "X-CSRF-TOKEN": csrfToken + } + }); + + // TODO: Productionではログを流さないようにする + this.axios.interceptors.response.use( + response => { + const { config, data, status } = response; + const { method, params, url } = config; + + console.group(`${method ? method.toUpperCase() : "undefined method"}:${status} - ${url}`); + if (params) console.table(params); + console.log(data); + console.groupEnd(); + + return response; + }, + error => { + console.log(error); + return Promise.reject(error); + } + ); + } + + public async get( + path: string, + params?: object, + successed?: (res: object) => void, + errored?: (res: object) => void, + always: () => any = () => {} + ) { + try { + const response = await this.axios.get(path, { params }); + if (successed) successed(response); + return response; + } catch (error) { + if (errored) errored(error); + throw error; + } finally { + always(); + } + } + + public async post( + path: string, + params?: object, + successed?: (res: object) => void, + errored?: (res: object) => void, + always: () => any = () => {} + ) { + try { + const response = await this.axios.post(path, params); + if (successed) successed(response); + return response; + } catch (error) { + if (errored) errored(error); + throw error; + } finally { + always(); + } + } + + public async delete( + path: string, + params?: object, + successed?: (res: object) => void, + errored?: (res: object) => void, + always: () => any = () => {} + ) { + try { + const response = await this.axios.delete(path, { data: { params } }); + if (successed) successed(response); + return response; + } catch (error) { + if (errored) errored(error); + throw error; + } finally { + always(); + } + } +} diff --git a/frontend/src/commons/ChkButtonBase.tsx b/frontend/src/commons/ChkButtonBase.tsx index 20c37f5f4..aed913edf 100644 --- a/frontend/src/commons/ChkButtonBase.tsx +++ b/frontend/src/commons/ChkButtonBase.tsx @@ -14,29 +14,29 @@ interface IPropsChkButtonBase extends IPropsCss { to?: string; text: string | React.ReactNode; name?: string; + onClick?: (e: any) => any; } const ChkButtonBase = (props: IPropsChkButtonBase) => { - const { to, text, name } = props; - const { color, bgcolor, border } = props; + const { to, text, name, color, bgcolor, border, onClick, ...otherProps } = props; if (to) { if (to.startsWith("http")) { return ( - + {text} ); } else { return ( - + {text} ); } } else { return ( - + {text} ); diff --git a/frontend/src/commons/color.ts b/frontend/src/commons/color.ts index 7e3e19c6c..bbf43caac 100644 --- a/frontend/src/commons/color.ts +++ b/frontend/src/commons/color.ts @@ -12,5 +12,7 @@ export const chkColors: IColor = { white: "#ffffff", placeholder: "#b0bec5", aqua: "#b3dfe2", - darkenAqua: "#50AAB7" + darkenAqua: "#50AAB7", + error: "#ff4b42", + disabled: "#cccccc" }; diff --git a/frontend/src/components/ContactForm.tsx b/frontend/src/components/ContactForm.tsx new file mode 100644 index 000000000..0af3ef823 --- /dev/null +++ b/frontend/src/components/ContactForm.tsx @@ -0,0 +1,202 @@ +import * as React from "react"; +import styled from "styled-components"; +import { observer } from "mobx-react-lite"; + +import ContactStore from "../stores/ContactStore"; + +import ChkButtonBase from "../commons/ChkButtonBase"; + +import { validateEmail } from "../utils/validation"; + +import TextInput from "./Forms/TextInput"; +import Select from "./Forms/Select"; +import CheckBox from "./Forms/CheckBox"; + +import CircleSpinner from "./Spinners/CircleSpinner"; + +interface IProp { + contactStore: ContactStore; + successed: (res: object) => void; + failed: (res: object) => void; + alert: { + message: string; + status?: string; + }; +} + +export default observer((props: IProp) => { + const { contactStore } = props; + const { contactEnum } = contactStore; + + React.useEffect(() => { + contactStore.fetchContactEnum(); + }, []); + + const [name, setName] = React.useState(""); + const [corner, setCorner] = React.useState(""); + const [department, setDepartment] = React.useState(""); + const [grade, setGrade] = React.useState(""); + const [email, setEmail] = React.useState(""); + const [nickname, setNickname] = React.useState(""); + + const [message, setMessage] = React.useState(""); + const [readable, setReadable] = React.useState(false); + + const [sendable, setSendable] = React.useState(false); + + React.useEffect( + () => { + if (corner && department && grade && !validateEmail(email).errorMessage && message) { + setSendable(true); + } else { + setSendable(false); + } + }, + [corner, department, grade, email, message] + ); + + const createContact = (e: any) => { + e.preventDefault(); + + if (!sendable) return; + + const contact = contactStore.createContact({ + name, + corner, + department, + grade, + email, + nickname, + message, + readable + }); + + contact.save(props.successed, props.failed); + }; + + return contactEnum ? ( + <> + {props.alert.status === "successed" && ( + +

{props.alert.message}

+
+ )} + {props.alert.status === "failed" && ( + +

{props.alert.message}

+
+ )} + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ラジオ内でメッセージを読み上げてもいい場合はチェックをつけてください + + + + + + + ) : ( + + ); +}); + +const InlineWrapper = styled.div` + margin: 20px; +`; + +const InlineHalfWrapper = styled.div` + width: 50%; + display: inline-block; +`; + +const ContactFormButton = styled(ChkButtonBase)` + width: 40%; + margin: 0 auto; + cursor: ${(props: { sendable: boolean }) => (props.sendable ? "pointer" : "default")}; +`; + +const AlertBar = styled.div` + width: 100%; + margin: 10px auto; + line-height: 1rem; + padding: 5px; + vertical-align: middle; + border: 3px solid #000; + color: #000; + text-align: center; +`; + +const SuccessedAlertBar = styled(AlertBar)` + border: 3px solid #3ed986; + color: #000; +`; + +const FailedAlertBar = styled(AlertBar)` + border: 3px solid #d95f3f; + color: #d95f3f; +`; diff --git a/frontend/src/components/Forms/CheckBox.tsx b/frontend/src/components/Forms/CheckBox.tsx new file mode 100644 index 000000000..2fffa8155 --- /dev/null +++ b/frontend/src/components/Forms/CheckBox.tsx @@ -0,0 +1,63 @@ +import * as React from "react"; +import styled from "styled-components"; + +export interface ITextInputProps { + children: string; + checked: boolean; + name?: string; + onChange?(value: boolean): void; +} + +export default ({ checked, name, onChange, children }: ITextInputProps) => { + const [currentChecked, setCurrentChecked] = React.useState(checked); + const [inFocus, setInFocus] = React.useState(false); + + const handleChange = React.useCallback((e: React.ChangeEvent) => { + const data = e.target.checked; + setCurrentChecked(data); + + if (onChange) { + onChange(data); + } + }, []); + + const handleFocus = React.useCallback(() => { + setInFocus(true); + }, []); + + // call: did handleFoucus + const handleBlur = React.useCallback(() => { + setInFocus(false); + }, []); + + React.useEffect( + () => { + if (checked !== null) { + setCurrentChecked(checked); + } + }, + [checked] + ); + + const props = { + onFocus: handleFocus, + onBlur: handleBlur, + id: name, + checked: currentChecked, + onChange: handleChange, + styledFocus: inFocus ? true : false + }; + + return ( + + ); +}; + +// TODO: focus時とそうでない時で背景かアウトラインのデザインを変える +const StyledCheckBox = styled.input` + margin-left: 20px; + margin-right: 10px; +`; diff --git a/frontend/src/components/Forms/Select.tsx b/frontend/src/components/Forms/Select.tsx new file mode 100644 index 000000000..b69abe94b --- /dev/null +++ b/frontend/src/components/Forms/Select.tsx @@ -0,0 +1,80 @@ +import * as React from "react"; +import styled from "styled-components"; + +// TODO: childrenの型が分からない +export interface ITextInputProps { + children?: any; + value?: string; + name?: string; + optionElements?: { [key: string]: string | number }; + onChange?(value: string): void; +} + +export default ({ value, name, optionElements, onChange, children }: ITextInputProps) => { + const [currentValue, setCurrentValue] = React.useState(value); + const [inFocus, setInFocus] = React.useState(false); + + const handleChange = React.useCallback((e: React.ChangeEvent) => { + const data = e.target.value; + setCurrentValue(data); + + if (onChange) { + onChange(data); + } + }, []); + + const handleFocus = React.useCallback(() => { + setInFocus(true); + }, []); + + // call: did handleFoucus + const handleBlur = React.useCallback(() => { + setInFocus(false); + }, []); + + React.useEffect( + () => { + if (value != null) { + setCurrentValue(value); + } + }, + [value] + ); + + const props = { + onFocus: handleFocus, + onBlur: handleBlur, + id: name, + value: currentValue, + onChange: handleChange, + styledFocus: inFocus ? true : false + }; + + return ( + + {children && children} + {optionElements && + Object.keys(optionElements).map(v => ( + + ))} + + ); +}; + +// TODO: focus時とそうでない時で背景かアウトラインのデザインを変える +const StyledSelect = styled.select` + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + width: 100%; + line-height: 1.5rem; + padding: 5px; + padding-left: 30px; + border: 2px solid #00afec; + border-radius: 1.5rem; + background: none transparent; + vertical-align: middle; + color: #00afec; +`; diff --git a/frontend/src/components/Forms/TextInput.tsx b/frontend/src/components/Forms/TextInput.tsx new file mode 100644 index 000000000..85c547d0a --- /dev/null +++ b/frontend/src/components/Forms/TextInput.tsx @@ -0,0 +1,125 @@ +import * as React from "react"; +import styled, { css } from "styled-components"; + +import { chkColors } from "../../commons/color"; +import { IValidationResult, ValidationMethod } from "../../utils/validation"; + +type TextInputType = "text" | "password" | "email"; + +export interface ITextInputProps { + value?: string; + name?: string; + placeholder?: string; + type?: TextInputType; + multiLine?: boolean; + validations?: ValidationMethod[]; + onChange?(value: string): void; +} + +export default ({ value, name, placeholder, multiLine, onChange, validations, type = "text" }: ITextInputProps) => { + const [currentInputValue, setCurrentInputValue] = React.useState(""); + const [validationErrors, setValidationErrors] = React.useState([] as IValidationResult[]); + const [inFocus, setInFocus] = React.useState(false); + + const handleChange = React.useCallback((e: React.ChangeEvent) => { + const data = e.target.value; + setCurrentInputValue(data); + + if (onChange) { + onChange(data); + } + }, []); + + React.useEffect( + () => { + if (validations) { + if (!currentInputValue) return; + + const newErrors: IValidationResult[] = []; + validations.forEach(validation => { + newErrors.push(validation(currentInputValue)); + }); + setValidationErrors(newErrors); + } + }, + [currentInputValue, inFocus] + ); + + const handleFocus = React.useCallback(() => { + setInFocus(true); + }, []); + + // call: did handleFoucus + const handleBlur = React.useCallback(() => { + setInFocus(false); + }, []); + + React.useEffect( + () => { + if (value != null) { + setCurrentInputValue(value); + } + }, + [value] + ); + + const props = { + type, + placeholder, + onFocus: handleFocus, + onBlur: handleBlur, + id: name, + value: currentInputValue, + onChange: handleChange, + styledFocus: inFocus ? true : false, + validationError: validationErrors.filter(v => v.errorMessage).length > 0 ? true : false + }; + + return ( +
+ {multiLine ? ( + <> + + {validationErrors.length > 0 ? validationErrors.map((v, i) => {v.errorMessage}) : null} + + ) : ( + <> + + {validationErrors.length > 0 ? validationErrors.map((v, i) => {v.errorMessage}) : null} + + )} +
+ ); +}; + +const style = css` + margin: 5px; + margin-left: 0; + margin-right: 0; + padding: 5px; + padding-left: 30px; + line-height: 1.5rem; + width: 100%; + border: 2px solid ${(props: any) => (props.validationError ? chkColors.error : "#00afec")}; + border-radius: 1.5rem; + + ::placeholder { + color: #00afec; + opacity: 1; + } + ::-ms-input-placeholder { + color: #00afec; + } + + &:-webkit-autofill { + box-shadow: 0 0 0 1000px #fff inset; + } +`; + +const StyledInput = styled.input` + ${style}; +`; + +const StyledTextarea = styled.textarea` + ${style} +`; diff --git a/frontend/src/components/Spinners/CircleSpinner.tsx b/frontend/src/components/Spinners/CircleSpinner.tsx new file mode 100644 index 000000000..1cf3a064d --- /dev/null +++ b/frontend/src/components/Spinners/CircleSpinner.tsx @@ -0,0 +1,43 @@ +import * as React from "react"; +import styled, { keyframes } from "styled-components"; + +import { chkColors } from "../../commons/color"; + +interface IProps { + size?: number; + color?: string; + isLoading?: boolean; +} + +export default ({ size = 35, color = chkColors.blue, isLoading = true }: IProps) => { + return isLoading ? ( + + + + ) : null; +}; + +const Wrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; + +const clip = keyframes` + 0% {transform: rotate(0deg) scale(1)} + 50% {transform: rotate(180deg) scale(0.8)} + 100% {transform: rotate(360deg) scale(1)} +`; + +const StyledSpinner = styled.div` + background: transparent !important; + width: ${(props: IProps) => `${props.size}px`}; + height: ${(props: IProps) => `${props.size}px`}; + border-radius: 100%; + border: 2px solid; + border-color: ${(props: IProps) => props.color}; + border-bottom-color: transparent; + display: inline-block; + animation: ${clip} 1.3s 0s infinite linear; + animation-fill-mode: both; +`; diff --git a/frontend/src/entries/Contact.tsx b/frontend/src/entries/Contact.tsx index 08588076f..189c77d06 100644 --- a/frontend/src/entries/Contact.tsx +++ b/frontend/src/entries/Contact.tsx @@ -1,52 +1,95 @@ import * as React from "react"; import styled from "styled-components"; +import { withRouter, RouteComponentProps } from "react-router-dom"; +import { inject } from "mobx-react"; -import { media } from "./../commons/style"; -import ChkButtonBase from "../commons/ChkButtonBase"; - -import { HeroArea } from "./../components/HeroArea"; -import { ContactHeroContent } from "./../components/ContactHeroContent"; - -export const Contact = () => ( -
- } /> - - - お問い合わせフォーム -
- - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-); +import RootStore from "../stores/RootStore"; + +import { media } from "../commons/style"; + +import { HeroArea } from "../components/HeroArea"; +import { ContactHeroContent } from "../components/ContactHeroContent"; +import ContactForm from "../components/ContactForm"; + +interface IProp { + rootStore?: RootStore; +} + +interface IState { + alert: { + message: string; + status?: string; + }; +} + +@inject("rootStore") +class Contact extends React.Component { + constructor(props: any) { + super(props); + this.state = { + alert: { + message: "", + status: undefined + } + }; + + this.successSendContact = this.successSendContact.bind(this); + this.failSendContact = this.failSendContact.bind(this); + } + + public render() { + const rootStore = this.props.rootStore!; + + return ( +
+ } /> + +
+ お問い合わせフォーム + + +
+
+
+ ); + } + + public successSendContact(_: object) { + this.setState({ + alert: { status: "successed", message: "おたよりを送信しました。 5秒後に自動でトップページに戻ります。" } + }); + setTimeout(() => console.log(this.props.history.push("/")), 5000); + } + + public failSendContact(_: object) { + this.setState({ + alert: { + status: "failed", + message: "おたよりの送信に失敗しました。 * がついている項目は全て記入して再送信してください。" + } + }); + } +} const ContactFormWrapper = styled.div` width: 100%; display: flex; justify-content: center; align-items: center; + + > div { + width: 50%; + margin-bottom: 100px; + + @media ${media.mobile} { + width: 90%; + } + } `; const ContactFormTitle = styled.div` @@ -58,47 +101,4 @@ const ContactFormTitle = styled.div` color: #00afec; `; -const ContactForm = styled.div` - width: 50%; - margin-bottom: 100px; - - @media ${media.mobile} { - width: 90%; - } -`; - -const ContactFormInputWrapper = styled.div` - margin: 20px; -`; - -const ContactFormInput = styled.input` - margin: 5px; - margin-left: 0; - margin-right: 0; - padding: 5px; - padding-left: 30px; - line-height: 1.5rem; - width: 100%; - border: 2px solid #00afec; - border-radius: 1.5rem; - - ::placeholder { - color: #00afec; - opacity: 1; - } - ::-ms-input-placeholder { - color: #00afec; - } -`; - -const ContactFormInputHalf = styled(ContactFormInput)` - width: 50%; -`; - -const ContactFormSendButtonWrapper = styled.div` - margin: 0 auto; - margin-top: 40px; - width: 30%; -`; - -const ContactFormSendButton = styled(ChkButtonBase)``; +export default withRouter(Contact); diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 235d4707c..2c41b94e0 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -3,16 +3,21 @@ import "normalize.css"; import * as React from "react"; import * as ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom"; +import { Provider } from "mobx-react"; import GlobalStyle from "./commons/style"; import { App } from "./App"; +import RootStore from "./stores/RootStore"; +// TODO(euglena1215): URLのproductionとの切り替え方法を考える ReactDOM.render( - - <> - - - - , + + + <> + + + + + , document.getElementById("app") ); diff --git a/frontend/src/layouts/Main.tsx b/frontend/src/layouts/Main.tsx index 883f55254..8f9241a83 100644 --- a/frontend/src/layouts/Main.tsx +++ b/frontend/src/layouts/Main.tsx @@ -3,7 +3,8 @@ import { Switch, Route } from "react-router-dom"; import { Index } from "./../entries/Index"; import { RadioHistory } from "./../entries/RadioHistory"; -import { Contact } from "./../entries/Contact"; + +import Contact from "./../entries/Contact"; import { Blog } from "./../entries/Blog"; import { Personality } from "./../entries/Personality"; diff --git a/frontend/src/models/ContactModel.ts b/frontend/src/models/ContactModel.ts new file mode 100644 index 000000000..8834be5c8 --- /dev/null +++ b/frontend/src/models/ContactModel.ts @@ -0,0 +1,49 @@ +import { computed } from "mobx"; +import ContactStore from "../stores/ContactStore"; + +export default class ContactModel { + public store: ContactStore; + + public readable: boolean; + public corner: number; + public message: string; + public nickname?: string; + public name?: string; + public email: string; + public department: number; + public grade: number; + + constructor(store: ContactStore, json: any) { + this.store = store; + + const { readable, corner, message, nickname, name, email, department, grade } = json; + this.readable = readable; + this.corner = corner; + this.message = message; + this.nickname = nickname; + this.name = name; + this.email = email; + this.department = department; + this.grade = grade; + } + + public save(successed: (res: object) => void, failed: (res: object) => void) { + console.log(this.toJson); + return this.store.transportLayer.saveContact(this.toJson, successed, failed); + } + + @computed + get toJson() { + const { readable, corner, message, nickname, name, email, department, grade } = this; + return { + readable, + corner, + message, + nickname, + name, + email, + department, + grade + }; + } +} diff --git a/frontend/src/stores/ContactStore.ts b/frontend/src/stores/ContactStore.ts new file mode 100644 index 000000000..3f9ebaf55 --- /dev/null +++ b/frontend/src/stores/ContactStore.ts @@ -0,0 +1,27 @@ +import { action, observable } from "mobx"; + +import ContactApi, { IContactEnum } from "../api/ContactApi"; +import ContactModel from "../models/ContactModel"; + +export default class ContactStore { + @observable public contactEnum?: IContactEnum; + + public transportLayer: ContactApi; + + constructor(transportLayer: ContactApi) { + this.transportLayer = transportLayer; + } + + public createContact(json: object) { + return new ContactModel(this, json); + } + + public async fetchContactEnum() { + const enums = await this.transportLayer.fetchContactEnum(); + this.setContactEnum(enums.data); + } + + @action public setContactEnum(enums: any) { + this.contactEnum = enums; + } +} diff --git a/frontend/src/stores/RootStore.ts b/frontend/src/stores/RootStore.ts new file mode 100644 index 000000000..a610cbbbc --- /dev/null +++ b/frontend/src/stores/RootStore.ts @@ -0,0 +1,14 @@ +import ContactStore from "./ContactStore"; +import ContactApi from "../api/ContactApi"; +import RestClient from "../api/RestClient"; + +export default class RootStore { + public contactStore: ContactStore; + + constructor(baseUrl: string) { + const restClient = new RestClient(baseUrl); + + const contactApi = new ContactApi(restClient); + this.contactStore = new ContactStore(contactApi); + } +} diff --git a/frontend/src/utils/validation.ts b/frontend/src/utils/validation.ts new file mode 100644 index 000000000..f5665e754 --- /dev/null +++ b/frontend/src/utils/validation.ts @@ -0,0 +1,21 @@ +const EMAIL_REGEXP = /^([\w])+([\w\._-])*@([\w_-])+\.([\w\._-]+)+$/; + +export interface IValidationResult { + errorMessage: string; +} + +export const validateEmail = (email: string): IValidationResult => { + if (!email) { + return { + errorMessage: "メールアドレスを入力してください。" + }; + } + if (!EMAIL_REGEXP.test(email)) { + return { + errorMessage: "有効なメールアドレスではありません。" + }; + } + return { errorMessage: "" }; +}; + +export type ValidationMethod = typeof validateEmail; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index a054bb0af..ce9d4f5d6 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -12,6 +12,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "experimentalDecorators": true, "newLine": "LF" }, "include": ["src/**/*"], diff --git a/frontend/tslint.json b/frontend/tslint.json index aa9fa601d..62d49eb49 100644 --- a/frontend/tslint.json +++ b/frontend/tslint.json @@ -12,6 +12,8 @@ true, "ignore-same-line" ], + "no-console": false, + "no-empty": false, "no-unused-expression": [ true, "allow-fast-null-checks" @@ -29,6 +31,7 @@ true, "double" ], + "interface-name": false, "prettier": true }, "jsRules": { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 1e0a00182..b9ccf8e4a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -14,6 +14,12 @@ dependencies: "@babel/types" "^7.0.0" +"@babel/runtime@^7.1.2": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d" + dependencies: + regenerator-runtime "^0.13.2" + "@babel/types@^7.0.0": version "7.3.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.0.tgz#61dc0b336a93badc02bf5f69c4cd8e1353f2ffc0" @@ -61,31 +67,34 @@ version "4.7.2" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220" -"@types/node@*": - version "10.12.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" - "@types/prop-types@*": - version "15.5.8" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce" + version "15.7.1" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" + +"@types/react-dom@^16.8.4": + version "16.8.4" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.4.tgz#7fb7ba368857c7aa0f4e4511c4710ca2c5a12a88" + dependencies: + "@types/react" "*" -"@types/react-dom@^16.0.5": - version "16.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.11.tgz#bd10ccb0d9260343f4b9a49d4f7a8330a5c1f081" +"@types/react-native@*": + version "0.57.57" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.57.57.tgz#4b59068acf6e542ac8e1e02e8a639b47e3e02c02" dependencies: + "@types/prop-types" "*" "@types/react" "*" -"@types/react-router-dom@^4.2.7": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.1.tgz#71fe2918f8f60474a891520def40a63997dafe04" +"@types/react-router-dom@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.3.tgz#7837e3e9fefbc84a8f6c8a51dca004f4e83e94e3" dependencies: "@types/history" "*" "@types/react" "*" "@types/react-router" "*" "@types/react-router@*": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-4.4.3.tgz#ea68b4021cb576866f83365b2201411537423d50" + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.0.0.tgz#22ae8f55d8af770ea1f755218936f01bfe1bfe27" dependencies: "@types/history" "*" "@types/react" "*" @@ -96,19 +105,19 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.3.16": - version "16.7.20" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.20.tgz#13ae752c012710d0fa800985ca809814b51d3b58" +"@types/react@*", "@types/react@^16.8.17": + version "16.8.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.17.tgz#f287b76a5badb93bc9aa3f54521a3eb53d6c2374" dependencies: "@types/prop-types" "*" csstype "^2.2.0" -"@types/styled-components@^4.1.6": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-4.1.6.tgz#9aa1d47dbc6bae540083869bcc6c639c6e9af0fe" +"@types/styled-components@^4.1.15": + version "4.1.15" + resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-4.1.15.tgz#532b6738ec633b2911e0470522c23e3c658952f2" dependencies: - "@types/node" "*" "@types/react" "*" + "@types/react-native" "*" csstype "^2.2.0" "@webassemblyjs/ast@1.5.9": @@ -471,6 +480,13 @@ awesome-typescript-loader@^5.0.0: source-map-support "^0.5.3" webpack-log "^1.2.0" +axios@0.19.0-beta.1: + version "0.19.0-beta.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0-beta.1.tgz#3d6a9ee75885d1fd39e108df9a4fb2e48e1af1e8" + dependencies: + follow-redirects "^1.4.1" + is-buffer "^2.0.2" + babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -1810,6 +1826,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-context@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3" + dependencies: + fbjs "^0.8.0" + gud "^1.0.0" + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -1936,8 +1959,8 @@ csso@~2.3.1: source-map "^0.5.3" csstype@^2.2.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.1.tgz#4cfbf637a577497036ebcd7e32647ef19a0b8076" + version "2.6.4" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.4.tgz#d585a6062096e324e7187f80e04f92bd0f00e37f" cyclist@~0.2.2: version "0.2.2" @@ -1977,7 +2000,7 @@ debug@=3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" -debug@^3.2.5: +debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" dependencies: @@ -2511,7 +2534,7 @@ faye-websocket@~0.11.1: dependencies: websocket-driver ">=0.5.1" -fbjs@^0.8.16: +fbjs@^0.8.0, fbjs@^0.8.16: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" dependencies: @@ -2637,6 +2660,12 @@ follow-redirects@^1.0.0: dependencies: debug "=3.1.0" +follow-redirects@^1.4.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" + dependencies: + debug "^3.2.6" + for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -2896,6 +2925,10 @@ grouped-queue@^0.3.3: dependencies: lodash "^4.17.2" +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + handle-thing@^1.2.5: version "1.2.5" resolved "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" @@ -2983,15 +3016,16 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" -history@^4.7.2: - version "4.7.2" - resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b" +history@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" dependencies: - invariant "^2.2.1" + "@babel/runtime" "^7.1.2" loose-envify "^1.2.0" resolve-pathname "^2.2.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" value-equal "^0.4.0" - warning "^3.0.0" hmac-drbg@^1.0.0: version "1.0.1" @@ -3001,9 +3035,11 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" + dependencies: + react-is "^16.7.0" home-or-tmp@^2.0.0: version "2.0.0" @@ -3212,7 +3248,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -3272,6 +3308,10 @@ is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" +is-buffer@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + is-builtin-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" @@ -3835,7 +3875,7 @@ long@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: @@ -3945,9 +3985,9 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^1.1.0" -memoize-one@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" +memoize-one@^5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.4.tgz#005928aced5c43d890a4dfab18ca908b0ec92cbc" memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" @@ -4108,6 +4148,21 @@ mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: dependencies: minimist "0.0.8" +mobx-react-lite@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-1.3.2.tgz#4366048b5d283d12a82053367638b5d79281951f" + +mobx-react@^5.4.4: + version "5.4.4" + resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-5.4.4.tgz#b3de9c6eabcd0ed8a40036888cb0221ab9568b80" + dependencies: + hoist-non-react-statics "^3.0.0" + react-lifecycles-compat "^3.0.2" + +mobx@^5.9.4: + version "5.9.4" + resolved "https://registry.yarnpkg.com/mobx/-/mobx-5.9.4.tgz#1dee92aba33f67b7baeeb679e3bd376a12e55812" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -4990,14 +5045,14 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.3.0, prop-types@^15.6.2: +prop-types@^15.3.0: version "15.6.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: loose-envify "^1.3.1" object-assign "^4.1.1" -prop-types@^15.5.4, prop-types@^15.6.1: +prop-types@^15.5.4: version "15.6.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: @@ -5005,6 +5060,14 @@ prop-types@^15.5.4, prop-types@^15.6.1: loose-envify "^1.3.1" object-assign "^4.1.1" +prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + proxy-addr@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" @@ -5135,41 +5198,53 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.4.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8" +react-dom@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.12.0" + scheduler "^0.13.6" react-is@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa" -react-router-dom@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6" - dependencies: - history "^4.7.2" - invariant "^2.2.4" - loose-envify "^1.3.1" - prop-types "^15.6.1" - react-router "^4.3.1" - warning "^4.0.1" +react-is@^16.7.0, react-is@^16.8.1: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" -react-router@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.3.1.tgz#aada4aef14c809cb2e686b05cee4742234506c4e" +react-lifecycles-compat@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + +react-router-dom@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.0.tgz#542a9b86af269a37f0b87218c4c25ea8dcf0c073" dependencies: - history "^4.7.2" - hoist-non-react-statics "^2.5.0" - invariant "^2.2.4" + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.0.tgz#349863f769ffc2fa10ee7331a4296e86bc12879d" + dependencies: + "@babel/runtime" "^7.1.2" + create-react-context "^0.2.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" loose-envify "^1.3.1" path-to-regexp "^1.7.0" - prop-types "^15.6.1" - warning "^4.0.1" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" react-slick@^0.24.0: version "0.24.0" @@ -5190,14 +5265,14 @@ react-twitter-widgets@^1.7.1: prop-types "^15.3.0" scriptjs "^2.5.8" -react@^16.4.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381" +react@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.12.0" + scheduler "^0.13.6" read-chunk@^2.1.0: version "2.1.0" @@ -5293,6 +5368,10 @@ regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + regenerator-transform@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" @@ -5502,9 +5581,9 @@ sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -scheduler@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b" +scheduler@^0.13.6: + version "0.13.6" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -5961,16 +6040,16 @@ style-loader@^0.21.0: loader-utils "^1.1.0" schema-utils "^0.4.5" -styled-components@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.1.3.tgz#4472447208e618b57e84deaaeb6acd34a5e0fe9b" +styled-components@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.2.0.tgz#811fbbec4d64c7189f6c7482b9eb6fefa7fefef7" dependencies: "@babel/helper-module-imports" "^7.0.0" "@emotion/is-prop-valid" "^0.7.3" "@emotion/unitless" "^0.7.0" babel-plugin-styled-components ">= 1" css-to-react-native "^2.2.2" - memoize-one "^4.0.0" + memoize-one "^5.0.0" prop-types "^15.5.4" react-is "^16.6.0" stylis "^3.5.0" @@ -6083,6 +6162,14 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tiny-invariant@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463" + +tiny-warning@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -6398,18 +6485,6 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" - dependencies: - loose-envify "^1.0.0" - -warning@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.1.tgz#66ce376b7fbfe8a887c22bdf0e7349d73d397745" - dependencies: - loose-envify "^1.0.0" - watchpack@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"