diff --git a/.prettierrc b/.prettierrc index 5a938ce..1f17d0d 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,8 @@ { - "tabWidth": 4, - "useTabs": false + "tabWidth": 4, + "useTabs": false, + "trailingComma": "es5", + "arrowParens": "always", + "semi": true, + "singleQuote": false } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..54d0e52 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.formatOnType": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} diff --git a/package.json b/package.json index 2c1138a..d7706dd 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "react-dom": "17.0.2", "react-firebase-hooks": "^3.0.4", "react-firebaseui": "^5.0.2", - "styled-components": "^5.3.3" + "react-query": "^3.29.1", + "styled-components": "^5.3.3", + "zustand": "^3.6.2" }, "devDependencies": { "@types/node": "16.11.6", diff --git a/public/reload-svgrepo-com.svg b/public/reload-svgrepo-com.svg new file mode 100644 index 0000000..42b0dcc --- /dev/null +++ b/public/reload-svgrepo-com.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/Links.tsx b/src/components/Links.tsx index 702cf0b..f17fb78 100644 --- a/src/components/Links.tsx +++ b/src/components/Links.tsx @@ -11,11 +11,13 @@ export const Links: FunctionComponent = ({ links }) => { return ( - {links.map((l, i) => ( -
  • - {extractHostname(l)} -
  • - ))} + {links + .filter((x) => x) + .map((l, i) => ( +
  • + {extractHostname(l)} +
  • + ))}
    ); }; @@ -29,6 +31,10 @@ const LinkList = styled.ul` `; const extractHostname = (url: string) => { - const u = new URL(url); - return `${u.hostname}`; + try { + const u = new URL(url); + return `${u.hostname}`; + } catch { + return undefined; + } }; diff --git a/src/components/Recipe.tsx b/src/components/Recipe.tsx index 46d1e4f..1b2d6ef 100644 --- a/src/components/Recipe.tsx +++ b/src/components/Recipe.tsx @@ -1,12 +1,13 @@ import React, { FunctionComponent } from "react"; +import { useStore } from "store/useStore"; import styled from "styled-components"; -import { Recipe as RecipeType } from "../models/recipie"; import { weekdays } from "../models/weekdays"; import { Links } from "./Links"; import { TagList } from "./TagList"; +import ReloadIcon from "public/reload-svgrepo-com.svg"; +import Image from "next/image"; type Props = { - recipe: RecipeType; weekday: typeof weekdays[number]; backgroundColor: string; color: string; @@ -21,17 +22,36 @@ const weekdayTranslations: { [weekday in typeof weekdays[number]]: string } = { }; export const Recipe: FunctionComponent = ({ - recipe, weekday, backgroundColor, color, }) => { + const { randomize, weekdays } = useStore(); + const recipe = weekdays[weekday]; + return ( - {weekdayTranslations[weekday]} - {recipe.title} - - + {recipe && ( + <> + + + {weekdayTranslations[weekday]} + + randomize(weekday)}> + {"Reload"} + + + {recipe.title} + + + + )} ); }; @@ -48,13 +68,20 @@ const Container = styled.div<{ backgroundColor: string; color: string }>` padding: 30px; color: ${({ color }) => color}; background-color: ${({ backgroundColor }) => backgroundColor}; + overflow: hidden; +`; + +const DayOfTheWeekContainer = styled.div` + grid-area: dayOfTheWeek; + align-self: end; + display: grid; + grid-template-columns: max-content min-content; + justify-content: space-between; `; const DayOfTheWeek = styled.small` margin: 0; padding: 0; - grid-area: dayOfTheWeek; - align-self: end; font-weight: normal; font-size: 2vmax; text-transform: lowercase; @@ -66,4 +93,22 @@ const Title = styled.h2` grid-area: title; font-size: 4vmax; font-weight: bold; + word-wrap: break-word; +`; + +const RandomizeButton = styled.button` + width: 20px; + height: 20px; + padding: 0; + background: transparent; + border: 0; + transform: rotate(0deg); + transition: transform 400ms cubic-bezier(0.68, -0.55, 0.27, 1.55); + color: white; + img { + filter: drop-shadow(1px 1px 1px black); + } + &:hover { + transform: rotate(180deg); + } `; diff --git a/src/components/RecipeList.tsx b/src/components/RecipeList.tsx index 2ffebce..6db855c 100644 --- a/src/components/RecipeList.tsx +++ b/src/components/RecipeList.tsx @@ -1,35 +1,49 @@ import * as React from "react"; -import { useEffect, useState } from "react"; -import styled, { css, DefaultTheme, useTheme } from "styled-components"; -import { getRecipies } from "firebase/__mocks__/clientApp"; -import { Recipe as RecipeType } from "models/recipie"; +import { useEffect } from "react"; +import styled, { DefaultTheme, useTheme } from "styled-components"; import { weekdays } from "models/weekdays"; import { Recipe } from "components/Recipe"; import { Info } from "components/Info"; import { mediaQueries } from "styles/theme"; +import { useStore } from "store/useStore"; export const RecipeList: React.FunctionComponent = () => { - const [recipes, setRecipes] = useState(); const theme = useTheme(); + const colors = colorMappings(theme); + const { initialize, weekdays, allRecipes } = useStore(); useEffect(() => { - (async () => { - const data = await getRecipies({ count: 5 }); - setRecipes(data); - })(); - }, []); - const colors = colorMappings(theme); + initialize(); + }, [initialize]); + return ( - {recipes?.map((r, i) => ( - - ))} + + + + + + ); diff --git a/src/components/TagList.tsx b/src/components/TagList.tsx index a18fbcb..f628f8c 100644 --- a/src/components/TagList.tsx +++ b/src/components/TagList.tsx @@ -8,16 +8,18 @@ export const TagList: FunctionComponent<{ tags: string[] }> = ({ tags }) => { return ( - {tags.map((t, i) => ( - {t} - ))} + {tags + .filter((x) => x) + .map((t, i) => ( + {t} + ))} ); }; const Tags = styled.div` display: flex; - gap: 1rem; + gap: 0.5rem; justify-self: end; align-self: end; `; @@ -29,4 +31,5 @@ const Tag = styled.i` background-color: rgba(0, 0, 0, 0.3); color: white; padding: 5px 7px; + font-size: 1vmax; `; diff --git a/src/firebase/__mocks__/clientApp.ts b/src/firebase/__mocks__/clientApp.ts index 866d2fb..d32fb60 100644 --- a/src/firebase/__mocks__/clientApp.ts +++ b/src/firebase/__mocks__/clientApp.ts @@ -8,6 +8,10 @@ export async function getRecipies({ return mockData.slice(0, count); } +export async function allRecipies(): Promise { + return mockData; +} + export async function addRecipe({ recipe }: { recipe: Recipe }) {} const mockData: Recipe[] = [ @@ -55,4 +59,40 @@ const mockData: Recipe[] = [ title: "Fishtacos", tags: ["fisk"], }, + { + id: "6", + recipe_links: [ + "https://www.koket.se/mitt-kok/jennie-wallden/klassisk-kottfarssas-med-spaghetti", + "https://www.arla.se/recept/kottfarssas/", + "https://www.tasteline.se/recept/kottfarssas/", + ], + title: "Hamburgare", + tags: ["snabbt", "barnvänligt"], + }, + { + id: "7", + recipe_links: [], + title: "Pyttipanna", + tags: ["snabbt", "barnvänligt"], + }, + { + id: "8", + recipe_links: [ + "https://www.koket.se/mitt-kok/jennie-wallden/klassisk-kottfarssas-med-spaghetti", + "https://www.arla.se/recept/kottfarssas/", + "https://www.tasteline.se/recept/kottfarssas/", + ], + title: "Köttbullar med potatismos och gräddsås", + tags: ["snabbt", "barnvänligt"], + }, + { + id: "9", + recipe_links: [ + "https://www.koket.se/mitt-kok/jennie-wallden/klassisk-kottfarssas-med-spaghetti", + "https://www.arla.se/recept/kottfarssas/", + "https://www.tasteline.se/recept/kottfarssas/", + ], + title: "Pizza", + tags: ["snabbt", "barnvänligt"], + }, ]; diff --git a/src/firebase/clientApp.ts b/src/firebase/clientApp.ts index 335ff5f..909e244 100644 --- a/src/firebase/clientApp.ts +++ b/src/firebase/clientApp.ts @@ -45,6 +45,13 @@ export async function getRecipies({ return recipes; } +export async function allRecipies(): Promise { + const recipesCol = collection(db, "recipes"); + const recipeList = await getDocs(recipesCol); + + return recipeList.docs.map(mapDocumentToRecipe); +} + async function getRandomRecipe({ excludeIds, }: { @@ -61,11 +68,13 @@ async function getRandomRecipe({ const q = query(recipesCol, ...queryConstraints, limit(1)); const recipeList = await getDocs(q); - return recipeList.docs.map((x) => { - return { id: x.id, ...x.data() } as Recipe; - }); + return recipeList.docs.map(mapDocumentToRecipe); } export async function addRecipe({ recipe }: { recipe: Recipe }) { await addDoc(collection(db, "recipes"), recipe); } + +const mapDocumentToRecipe = (x: QueryDocumentSnapshot) => { + return { id: x.id, ...x.data() } as Recipe; +}; diff --git a/src/firebase/util.ts b/src/firebase/util.ts index c15b8fe..332e118 100644 --- a/src/firebase/util.ts +++ b/src/firebase/util.ts @@ -1,3 +1,5 @@ +import { randomInt } from "util/random"; + export class AutoId { static newId(): string { // Alphanumeric characters @@ -22,12 +24,6 @@ export class AutoId { } } -function randomInt({ min, max }: { min?: number; max: number }) { - min ??= 0; - - return Math.floor(Math.random() * (max - min) + min); -} - export function uniquesByKey(array: Array, byKey: keyof T) { const uniques = [...new Map(array.map((x) => [x[byKey], x])).values()]; return uniques; diff --git a/src/hooks/useRecipe.ts b/src/hooks/useRecipe.ts deleted file mode 100644 index 93051e0..0000000 --- a/src/hooks/useRecipe.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { useCollection } from "react-firebase-hooks/firestore"; - -export const useRecipes = () => { - return useCollection(); -}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3741e95..87c21a3 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -2,12 +2,17 @@ import "../styles/globals.css"; import type { AppProps } from "next/app"; import { ThemeProvider } from "styled-components"; import { theme } from "../styles/theme"; +import { QueryClient, QueryClientProvider } from "react-query"; +import { ReactQueryDevtools } from "react-query/devtools"; -console.log(theme); +const queryClient = new QueryClient(); function MyApp({ Component, pageProps }: AppProps) { return ( - + + + + ); } diff --git a/src/store/useStore.ts b/src/store/useStore.ts new file mode 100644 index 0000000..1ad47a1 --- /dev/null +++ b/src/store/useStore.ts @@ -0,0 +1,71 @@ +import { Recipe } from "models/recipie"; +import { weekdays } from "models/weekdays"; +import create from "zustand"; +import { allRecipies } from "firebase/clientApp"; +import { randomInt } from "util/random"; + +type State = { + allRecipes: Recipe[]; + initialize: () => Promise; + randomize: (weekday: typeof weekdays[number]) => void; + weekdays: { [weekday in typeof weekdays[number]]?: Recipe }; +}; + +export const useStore = create((set, get) => ({ + allRecipes: [], + initialize: async () => { + const { allRecipes } = get(); + if (allRecipes?.length) { + console.log("already initialized"); + return; + } + + const recipes = await allRecipies(); + set({ allRecipes: recipes }); + + const { randomize } = get(); + weekdays.map((w) => { + randomize(w); + }); + }, + randomize: (weekday) => { + const { weekdays, allRecipes } = get(); + + const excludeIds = Object.keys(weekdays) + .map((w) => { + return weekdays[w as keyof typeof weekdays]?.id; + }) + .filter((x) => x) as string[]; + + const newState = { ...weekdays }; + newState[weekday] = randomizeRecipe({ allRecipes, excludeIds }); + + set({ weekdays: newState }); + }, + weekdays: { + monday: undefined, + tuesday: undefined, + wednesday: undefined, + thursday: undefined, + friday: undefined, + }, +})); + +const randomizeRecipe = ({ + allRecipes, + excludeIds, +}: { + allRecipes: Recipe[]; + excludeIds: string[]; +}) => { + if (!allRecipes) { + return undefined; + } + + const filteredData = allRecipes.filter((x) => + excludeIds ? excludeIds.indexOf(x.id) < 0 : true + ); + + const index = randomInt({ max: filteredData?.length - 1 }); + return filteredData[index]; +}; diff --git a/src/util/random.ts b/src/util/random.ts new file mode 100644 index 0000000..40db18b --- /dev/null +++ b/src/util/random.ts @@ -0,0 +1,5 @@ +export function randomInt({ min, max }: { min?: number; max: number }) { + min ??= 0; + + return Math.floor(Math.random() * (max - min) + min); +} diff --git a/yarn.lock b/yarn.lock index 8e292f2..335b36e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -115,6 +115,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.0.tgz#e27b977f2e2088ba24748bf99b5e1dece64e4f0b" + integrity sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -1086,6 +1093,11 @@ base64-js@^1.0.2: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +big-integer@^1.6.16: + version "1.6.50" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.50.tgz#299a4be8bd441c73dcc492ed46b7169c34e92e70" + integrity sha512-+O2uoQWFRo8ysZNo/rjtri2jIwjr3XfeAgRjAUADRqGG+ZITvyn8J1kvXLTaKVr3hhGXk+f23tKfdzmklVM9vQ== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -1121,6 +1133,20 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -1523,6 +1549,11 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +detect-node@^2.0.4, detect-node@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + dialog-polyfill@^0.4.7: version "0.4.10" resolved "https://registry.yarnpkg.com/dialog-polyfill/-/dialog-polyfill-0.4.10.tgz#c4ea68a0deed4abb59a6a2a025c548b278cd532e" @@ -2487,6 +2518,11 @@ jest-worker@27.0.0-next.5: merge-stream "^2.0.0" supports-color "^8.0.0" +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -2652,6 +2688,14 @@ make-dir@^3.0.2: dependencies: semver "^6.0.0" +match-sorter@^6.0.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda" + integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + material-design-lite@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/material-design-lite/-/material-design-lite-1.3.0.tgz#d004ce3fee99a1eeb74a78b8a325134a5f1171d3" @@ -2684,6 +2728,11 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -2729,6 +2778,13 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8= + dependencies: + big-integer "^1.6.16" + nanoid@^3.1.23: version "3.1.30" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" @@ -2910,6 +2966,11 @@ object.values@^1.1.4, object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3236,6 +3297,15 @@ react-is@^16.7.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-query@^3.29.1: + version "3.29.1" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.29.1.tgz#d228687ca354b51a41b08e7fabb45a88f3b06357" + integrity sha512-EKhI4YokmB1Ccnv1vu8w5L9VpKUnznzLLpaMgaJx1xEnBvGTdUrpNFFxuGiDuiQOg3hi6TvSHVTfo/Nws3kTpg== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + react-refresh@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" @@ -3311,6 +3381,11 @@ regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U= + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -3347,7 +3422,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -3816,6 +3891,14 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -4012,3 +4095,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.6.2.tgz#76bd927c8f82c14d7aae9f592323371b583e564a" + integrity sha512-Jw+tM8T3koFjwLLj8ihlYcMqp3nHfpRNOrc/qqwFhmVp7nmrBD/N73bEb5cjveFKNQtAt+7n2S+gnIbhrV/4qA==