diff --git a/.eslintrc.js b/.eslintrc.js index df128ea..66385bf 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -41,5 +41,7 @@ module.exports = { 'react/jsx-props-no-spreading': 'off', 'react/function-component-definition': 'off', 'react/require-default-props': 'off', + 'no-plusplus': 'off', + 'no-param-reassign': 'off,', }, }; diff --git a/package.json b/package.json index d73b795..c1f8369 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,19 @@ "dependencies": { "@emotion/react": "^11.8.2", "@emotion/styled": "^11.8.1", + "@reduxjs/toolkit": "^1.8.0", + "@types/react-redux": "^7.1.23", "@types/yup": "^0.29.13", "emotion-normalize": "^11.0.1", "firebase": "^9.6.8", "formik": "^2.2.9", "next": "12.1.0", + "next-redux-wrapper": "^7.0.5", "react": "17.0.2", "react-dom": "17.0.2", "react-icons": "^4.3.1", "react-loading-icons": "^1.0.8", + "react-redux": "^7.2.6", "yup": "^0.32.11" }, "devDependencies": { @@ -51,6 +55,7 @@ "eslint-plugin-react-hooks": "^4.3.0", "firebase-tools": "^10.3.0", "prettier": "^2.5.1", + "redux-logger": "^3.0.6", "typescript": "4.6.2" } } diff --git a/src/api/.keep b/src/api/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/api/firebaseConfig.ts b/src/api/firebaseConfig.ts new file mode 100644 index 0000000..3848df5 --- /dev/null +++ b/src/api/firebaseConfig.ts @@ -0,0 +1,9 @@ +export const firebaseConfig = { + apiKey: 'AIzaSyAqYYl92NFHBIwpT3l-e0Z3tXkXNt-WRAA', + authDomain: 'hanspoon-31cd9.firebaseapp.com', + projectId: 'hanspoon-31cd9', + storageBucket: 'hanspoon-31cd9.appspot.com', + messagingSenderId: '301891970054', + appId: '1:301891970054:web:4538a1e0b0a74fbff64f87', + measurementId: 'G-XB0QC0WFV5', +}; diff --git a/src/components/SkeletonCard/SkeletonCard.stories.tsx b/src/components/SkeletonCard/SkeletonCard.stories.tsx new file mode 100644 index 0000000..c8bc913 --- /dev/null +++ b/src/components/SkeletonCard/SkeletonCard.stories.tsx @@ -0,0 +1,47 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import { SkeletonCard } from './SkeletonCard'; + +export default { + title: 'SkeletonCard', + component: SkeletonCard, + args: { + type: 'square', + background: 'white', + hasSummary: true, + headingPosition: 'topLeft', + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Default = Template.bind({}); + +export const WideType = Template.bind({}); + +WideType.args = { + type: 'wide', +}; + +export const NoBackground = Template.bind({}); + +NoBackground.args = { + background: 'none', +}; + +export const NoSummary = Template.bind({}); + +NoSummary.args = { + hasSummary: false, +}; + +export const BottomLeft = Template.bind({}); + +BottomLeft.args = { + headingPosition: 'bottomLeft', +}; + +export const BottomCenter = Template.bind({}); + +BottomCenter.args = { + headingPosition: 'bottomCenter', +}; diff --git a/src/components/SkeletonCard/SkeletonCard.styled.tsx b/src/components/SkeletonCard/SkeletonCard.styled.tsx new file mode 100644 index 0000000..dd6dfb1 --- /dev/null +++ b/src/components/SkeletonCard/SkeletonCard.styled.tsx @@ -0,0 +1,126 @@ +import styled from '@emotion/styled'; +import { css, keyframes } from '@emotion/react'; +import { + SkeletonSummaryProps, + SkeletonTitleProps, + SkeletonTypeProps, + SkeletonWrapperProps, +} from './SkeletonCard.types'; +import { media, pxToRem } from 'utils'; + +const skeleton = keyframes` + 0% { + background-color: rgba(165, 165, 165, 0.1); + } + + 50% { + background-color: rgba(165, 165, 165, 0.3); + } + + 100% { + background-color: rgba(165, 165, 165, 0.1); + } +`; + +const inlineBlock = css` + display: flex; + justify-content: center; +`; + +const squareType = css` + width: ${pxToRem(200)}; + height: ${pxToRem(200)}; + background: gray; +`; + +const wideType = css` + width: 100%; + height: 50vw; + background: gray; + + ${media.desktop} { + width: 100%; + height: 250px; + } +`; + +const hasSummaryTrue = css` + display: block; + width: 100%; + height: ${pxToRem(20)}; + background: gray; + position: relative; + overflow: hidden; + + -webkit-animation: ${skeleton} 1.8s infinite ease-in-out; + animation: ${skeleton} 1.8s infinite ease-in-out; +`; + +const hasSummaryFalse = css` + display: none; +`; + +const bottomLeft = css` + background: gray; + left: 0; +`; +const bottomCenter = css` + background: gray; + align-self: center; +`; + +const topLeft = css` + background: gray; + order: -1; +`; + +export const SkeletonContainer = styled.div` + ${(props) => props.$type === 'square' && inlineBlock} +`; + +export const SkeletonCardWrapper = styled.div` + ${(props) => props.$type === 'square' && inlineBlock}; + + display: flex; + flex-direction: column; + padding: ${pxToRem(16)}; + position: relative; + + background: ${(props) => (props.$background === 'white' ? 'white' : 'none')}; + box-shadow: ${(props) => props.$background === 'white' && '0px 4px 4px rgba(0, 0, 0, 0.25);'}; +`; + +export const SkeletonImage = styled.div` + ${(props) => (props.$type === 'square' ? squareType : wideType)} + + overflow: hidden; + position: relative; + + -webkit-animation: ${skeleton} 1.8s infinite ease-in-out; + animation: ${skeleton} 1.8s infinite ease-in-out; +`; + +export const SkeletonTitle = styled.div` + ${(props) => + props.$headingPosition === 'topLeft' + ? topLeft + : props.$headingPosition === 'bottomCenter' + ? bottomCenter + : bottomLeft} + + margin: ${pxToRem(16)} 0 !important; + width: 70%; + max-width: 100%; + height: ${pxToRem(20)}; + background: gray; + display: block; + overflow: hidden; + position: relative; + + -webkit-animation: ${skeleton} 1.8s infinite ease-in-out; + animation: ${skeleton} 1.8s infinite ease-in-out; +`; + +export const SkeletonSummary = styled.div` + ${(props) => (props.$hasSummary ? hasSummaryTrue : hasSummaryFalse)} +`; diff --git a/src/components/SkeletonCard/SkeletonCard.tsx b/src/components/SkeletonCard/SkeletonCard.tsx new file mode 100644 index 0000000..24d8eef --- /dev/null +++ b/src/components/SkeletonCard/SkeletonCard.tsx @@ -0,0 +1,20 @@ +import { + SkeletonCardWrapper, + SkeletonContainer, + SkeletonImage, + SkeletonSummary, + SkeletonTitle, +} from './SkeletonCard.styled'; +import { SkeletonCardProps } from './SkeletonCard.types'; + +export const SkeletonCard = ({ type, background, hasSummary, headingPosition }: SkeletonCardProps): JSX.Element => { + return ( + + + + + + + + ); +}; diff --git a/src/components/SkeletonCard/SkeletonCard.types.ts b/src/components/SkeletonCard/SkeletonCard.types.ts new file mode 100644 index 0000000..0c6c22b --- /dev/null +++ b/src/components/SkeletonCard/SkeletonCard.types.ts @@ -0,0 +1,27 @@ +type SkeletonType = 'wide' | 'square'; +type SkeletonBackground = 'white' | 'none'; +type SkeletonHeadingPosition = 'bottomLeft' | 'bottomCenter' | 'topLeft'; + +export interface SkeletonCardProps { + type: SkeletonType; + background: SkeletonBackground; + hasSummary: boolean; + headingPosition: SkeletonHeadingPosition; +} + +export interface SkeletonTypeProps { + $type: SkeletonType; +} + +export interface SkeletonWrapperProps { + $type: SkeletonType; + $background: SkeletonBackground; +} + +export interface SkeletonTitleProps { + $headingPosition: SkeletonHeadingPosition; +} + +export interface SkeletonSummaryProps { + $hasSummary: boolean; +} diff --git a/src/components/index.ts b/src/components/index.ts index 488ee3c..d337ac0 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -3,4 +3,5 @@ export * from './Logo/Logo'; export * from './Loading/Loading'; export * from './Layout/Layout'; export * from './Header/Header'; -export * from './EmptyPage/EmptyPage' \ No newline at end of file +export * from './EmptyPage/EmptyPage'; +export * from './SkeletonCard/SkeletonCard'; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 2f7bc64..b1382ac 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -3,14 +3,17 @@ import { ThemeProvider } from '@emotion/react'; import { theme } from 'theme/theme'; import { GlobalStyle } from 'styles/GlobalStyle'; import { Layout } from '../components'; +import { StoreProvider } from 'store'; function MyApp({ Component, pageProps }: AppProps) { return ( - - - - + + + + + + ); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index ee3655b..ce52bf1 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,8 +1,14 @@ import type { NextPage } from 'next'; +import { useGetRandomRecipeQuery } from 'store/services'; import Head from 'next/head'; import Image from 'next/image'; const Home: NextPage = () => { + const { data, error, isLoading } = useGetRandomRecipeQuery(1); + console.log(data); + console.log(error); + console.log(isLoading); + return
Home
; }; diff --git a/src/store/index.tsx b/src/store/index.tsx new file mode 100644 index 0000000..1d30656 --- /dev/null +++ b/src/store/index.tsx @@ -0,0 +1,15 @@ +import { configureStore } from '@reduxjs/toolkit'; +import { ReactNode } from 'react'; +import { Provider } from 'react-redux'; +import { twoSpoonApi } from 'store/services'; + +export const store = configureStore({ + reducer: { + [twoSpoonApi.reducerPath]: twoSpoonApi.reducer, + }, + middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(twoSpoonApi.middleware), +}); + +export const StoreProvider = (props: { children: ReactNode }) => { + return ; +}; diff --git a/src/store/services/index.ts b/src/store/services/index.ts new file mode 100644 index 0000000..06de4c9 --- /dev/null +++ b/src/store/services/index.ts @@ -0,0 +1,24 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; +import { RandomRecipeQuery } from './types/queries'; + +export const twoSpoonApi = createApi({ + reducerPath: 'twoSpoonApi', + baseQuery: fetchBaseQuery({ + baseUrl: 'https://spoonacular-recipe-food-nutrition-v1.p.rapidapi.com/', + prepareHeaders: (headers) => { + headers.set('content-type', 'application/json'); + headers.set('x-rapidapi-host', `${process.env.NEXT_PUBLIC_RAPID_API_HOST}`); + headers.set('x-rapidapi-key', `${process.env.NEXT_PUBLIC_RAPID_API_KEY}`); + return headers; + }, + }), + + endpoints: (builder) => ({ + getRandomRecipe: builder.query({ + query: (number = 1) => `recipes/random?number=${number}`, + // transformResponse: (response: { data: RandomRecipeQuery }) => response.data, + }), + }), +}); + +export const { useGetRandomRecipeQuery } = twoSpoonApi; diff --git a/src/store/services/types/queries.ts b/src/store/services/types/queries.ts new file mode 100644 index 0000000..fca0f69 --- /dev/null +++ b/src/store/services/types/queries.ts @@ -0,0 +1,10 @@ +export interface RandomRecipe { + id: number; + title: string; + summary: string; + image: string; +} + +export interface RandomRecipeQuery { + recipes: RandomRecipe[]; +} diff --git a/src/store/slices/recipeReducer.ts b/src/store/slices/recipeReducer.ts new file mode 100644 index 0000000..aaa23ac --- /dev/null +++ b/src/store/slices/recipeReducer.ts @@ -0,0 +1,21 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { RandomRecipe, RandomRecipeQuery } from 'store/services/types/queries'; + +interface RecipeState { + // savedRecipes: RandomRecipeQuery[]; + recipes: RandomRecipe; + // curIndex: number; +} + +// const initialState: RecipeState = { recipes: {} as RandomRecipe, curIndex: 0 }; + +// export const recipeSlice = createSlice({ +// name: 'recipe', +// initialState, +// reducers: { +// nextRandomRecipe(state, payload) { +// state.curIndex++; +// state.recipes = +// }, +// }, +// }); diff --git a/src/utils/style.ts b/src/utils/style.ts index 6c6764f..a224257 100644 --- a/src/utils/style.ts +++ b/src/utils/style.ts @@ -4,4 +4,5 @@ export const pxToRem = (px: number, base = 16): string => { export const media = { mobile: '@media(max-width:767px)', + desktop: '@media(min-width: 768px)', }; diff --git a/yarn.lock b/yarn.lock index c04ae18..aba9976 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1075,7 +1075,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== @@ -2131,6 +2131,16 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= +"@reduxjs/toolkit@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.8.0.tgz#8ae875e481ed97e4a691aafa034f876bfd0413c4" + integrity sha512-cdfHWfcvLyhBUDicoFwG1u32JqvwKDxLxDd7zSmSoFw/RhYLOygIRtmaMjPRUUHmVmmAGAvquLLsKKU/677kSQ== + dependencies: + immer "^9.0.7" + redux "^4.1.2" + redux-thunk "^2.4.1" + reselect "^4.1.5" + "@rushstack/eslint-patch@^1.0.8": version "1.1.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz#7f698254aadf921e48dda8c0a6b304026b8a9323" @@ -3116,6 +3126,14 @@ dependencies: "@types/unist" "*" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^5.0.0": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" @@ -3235,6 +3253,16 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== +"@types/react-redux@^7.1.20", "@types/react-redux@^7.1.23": + version "7.1.23" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.23.tgz#3c2bb1bcc698ae69d70735f33c5a8e95f41ac528" + integrity sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-syntax-highlighter@11.0.5": version "11.0.5" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" @@ -5595,6 +5623,11 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-diff@^0.3.5: + version "0.3.8" + resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84" + integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ= + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -7843,7 +7876,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -8039,6 +8072,11 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= +immer@^9.0.7: + version "9.0.12" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.12.tgz#2d33ddf3ee1d247deab9d707ca472c8c942a0f20" + integrity sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA== + import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -9915,6 +9953,11 @@ netmask@^2.0.1: resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== +next-redux-wrapper@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/next-redux-wrapper/-/next-redux-wrapper-7.0.5.tgz#109cd3fe02183b18fbd094924cfcbd21262039dc" + integrity sha512-UFXdAWG5i+GFT8+Hoqpx3GArkPh34fVWF9YoA2VSHlBzsrPtnRd7NWM6FNSYUennpommTpWJ09mu+r/1UxyIkg== + next-tick@1, next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" @@ -11320,6 +11363,18 @@ react-popper@^2.2.4: react-fast-compare "^3.0.1" warning "^4.0.2" +react-redux@^7.2.6: + version "7.2.6" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa" + integrity sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ== + dependencies: + "@babel/runtime" "^7.15.4" + "@types/react-redux" "^7.1.20" + hoist-non-react-statics "^3.3.2" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^17.0.2" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -11459,6 +11514,25 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" +redux-logger@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" + integrity sha1-91VZZvMJjzyIYExEnPC69XeCdL8= + dependencies: + deep-diff "^0.3.5" + +redux-thunk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714" + integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q== + +redux@^4.0.0, redux@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104" + integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw== + dependencies: + "@babel/runtime" "^7.9.2" + refractor@^3.1.0: version "3.6.0" resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" @@ -11686,6 +11760,11 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +reselect@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6" + integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"