feat: modal system, "create channel" button
This commit is contained in:
parent
ec4c98b760
commit
3c216557db
21 changed files with 367 additions and 72 deletions
|
@ -3,9 +3,11 @@
|
|||
"version": "0.1.0",
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"framer-motion": "^4.1.17",
|
||||
"react": "^17.0.2",
|
||||
"react-content-loader": "^6.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-feather": "^2.0.9",
|
||||
"react-media-hook": "^0.4.9",
|
||||
"react-redux": "^7.2.5",
|
||||
"react-router-dom": "^5.3.0",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<body>
|
||||
<noscript>Sorry, but JavaScript is required.</noscript>
|
||||
|
||||
<div id="modal-root"></div>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -6,5 +6,9 @@ const { log: authLog } = Logger([ 'Authenticator' ]);
|
|||
|
||||
export function login() {
|
||||
authLog('Logging in through gateway...');
|
||||
if (gateway.handshakeCompleted) {
|
||||
authLog("Gateway connection already exists, tearing down existing one...");
|
||||
gateway.ws.close();
|
||||
}
|
||||
return gateway.connect(getToken());
|
||||
};
|
||||
|
|
58
src/components/Modal.js
Normal file
58
src/components/Modal.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { m } from "framer-motion";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
const modalRoot = document.getElementById('modal-root');
|
||||
|
||||
const modalAnimation = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
ease: "easeIn",
|
||||
},
|
||||
},
|
||||
exit: {
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
ease: "easeOut",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function Modal({ width=600, height=400, alignItems="default", title, children, onClose }) {
|
||||
return createPortal(
|
||||
<>
|
||||
<m.div
|
||||
className="backdrop"
|
||||
onClick={onClose}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<m.div
|
||||
className={ alignItems === "center" ? "modal-centered" : "modal" }
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
variants={modalAnimation}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
exit="exit"
|
||||
style={{
|
||||
width: width,
|
||||
height: height
|
||||
}}
|
||||
>
|
||||
{ (title) && <span className="modal-title">{ title }</span> }
|
||||
{children}
|
||||
</m.div>
|
||||
</m.div>
|
||||
</>,
|
||||
modalRoot
|
||||
);
|
||||
}
|
|
@ -68,7 +68,7 @@ export default function Create() {
|
|||
<div id="login-container">
|
||||
<h1>One more thing!</h1>
|
||||
<p>You need a special code to sign up here!</p>
|
||||
<label htmlFor="specialcode">Special Code</label>
|
||||
<label htmlFor="specialcode" className="label">Special Code</label>
|
||||
<br />
|
||||
<input type="password" name="specialcode" className="text-input" onChange={ ({ target }) => setSpecialCodeInput(target.value) } />
|
||||
<button id="login-submit" className="button" onClick={ doCreateAccount }>Continue</button>
|
||||
|
@ -84,11 +84,11 @@ export default function Create() {
|
|||
<span className="greeter-branding-name">sign up</span>
|
||||
<div className="center">
|
||||
<div id="login-container">
|
||||
<label htmlFor="username">Username</label>
|
||||
<label htmlFor="username" className="label">Username</label>
|
||||
<br />
|
||||
<input type="text" name="username" className="text-input" onChange={ ({ target }) => setUsernameInput(target.value) } />
|
||||
<br />
|
||||
<label htmlFor="password">Password</label>
|
||||
<label htmlFor="password" className="label">Password</label>
|
||||
<br />
|
||||
<input type="password" name="password" className="text-input" onChange={ ({ target }) => setPasswordInput(target.value) } />
|
||||
<br />
|
||||
|
|
|
@ -56,11 +56,11 @@ export default function Login() {
|
|||
<span className="greeter-branding-name">log in</span>
|
||||
<div className="center">
|
||||
<div id="login-container">
|
||||
<label htmlFor="username">Username</label>
|
||||
<label htmlFor="username" className="label">Username</label>
|
||||
<br />
|
||||
<input type="text" name="username" className="text-input" onChange={ ({ target }) => setUsernameInput(target.value) } />
|
||||
<br />
|
||||
<label htmlFor="password">Password</label>
|
||||
<label htmlFor="password" className="label">Password</label>
|
||||
<br />
|
||||
<input type="password" name="password" className="text-input" onChange={ ({ target }) => setPasswordInput(target.value) } />
|
||||
<br />
|
||||
|
|
92
src/components/channel/ChannelCreateButton.js
Normal file
92
src/components/channel/ChannelCreateButton.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
import { AnimatePresence } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
|
||||
import Modal from "../Modal";
|
||||
import { authenticated } from '../../api/request';
|
||||
import { getCreateChannelError } from "../../common/util/errors";
|
||||
import { login } from "../../api/authenticator";
|
||||
|
||||
export default function ChannelCreateButton() {
|
||||
const [ isDialogOpen, setIsDialogOpen ] = useState(false);
|
||||
const [ channelNameInput, setChannelNameInput ] = useState();
|
||||
const [ info, setInfo ] = useState(null);
|
||||
|
||||
const handleClose = () => {
|
||||
setIsDialogOpen(false);
|
||||
setInfo(null);
|
||||
};
|
||||
|
||||
const createChannel = async () => {
|
||||
setInfo("creating channel...");
|
||||
const { json, isOK } = await authenticated('/api/v1/content/channel/create', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: channelNameInput
|
||||
})
|
||||
});
|
||||
|
||||
if (!isOK && json) {
|
||||
setInfo(getCreateChannelError(json));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOK) {
|
||||
setInfo("Something went wrong");
|
||||
return;
|
||||
}
|
||||
|
||||
login(); // ugly: we need to relog in order to see the new channel
|
||||
handleClose();
|
||||
};
|
||||
|
||||
let modalContent;
|
||||
if (info) {
|
||||
modalContent = (
|
||||
<>
|
||||
<div className="center grow col-flex">
|
||||
<span className="greeter-branding-name">{ info }</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
modalContent = (
|
||||
<>
|
||||
<div className="input-group">
|
||||
<label htmlFor="channel-name" className="label" style={{ float: "left" }}>Channel Name</label>
|
||||
<input type="text" name="channel-name" className="text-input" onChange={ ({ target }) => setChannelNameInput(target.value) } />
|
||||
</div>
|
||||
<div className="full-width">
|
||||
<button style={{ float: "right" }} id="login-submit" className="button-pressed" onClick={ createChannel }>Create</button>
|
||||
<button style={{ float: "left" }} className="button" onClick={ handleClose }>Cancel</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <>
|
||||
<button className="button button-channel" onClick={() => setIsDialogOpen(true)}>
|
||||
<div className="profile-link">
|
||||
<div className="profile-picture add-channel" alt="Profile">
|
||||
<span className="default-channel-styled-text">+</span>
|
||||
</div>
|
||||
<span className="profile-username">New Channel</span>
|
||||
</div>
|
||||
</button>
|
||||
{/* ugly: AnimatePresence is needed to animate the modal closing */}
|
||||
<AnimatePresence>
|
||||
{(isDialogOpen) && <Modal
|
||||
width="500px"
|
||||
height="300px"
|
||||
title="Create a channel"
|
||||
alignItems="center"
|
||||
onClose={ handleClose }
|
||||
>
|
||||
{ modalContent }
|
||||
</Modal>}
|
||||
</AnimatePresence>
|
||||
</>;
|
||||
}
|
|
@ -2,6 +2,7 @@ import { connect } from 'react-redux'
|
|||
|
||||
import ChannelListLoader from './ChannelListLoader';
|
||||
import ChannelButton from './ChannelButton';
|
||||
import ChannelCreateButton from './ChannelCreateButton';
|
||||
|
||||
function ChannelList({ selectedChannelId, channels }) {
|
||||
if (!channels) {
|
||||
|
@ -14,6 +15,7 @@ function ChannelList({ selectedChannelId, channels }) {
|
|||
return (
|
||||
<div className="channel-list">
|
||||
{ channels.map((channel) => ( <ChannelButton key={ channel._id } channel={ channel } selected={ (channel._id === selectedChannelId) } /> )) }
|
||||
<ChannelCreateButton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ export default function ChannelMessageView({ messages, channelId }) {
|
|||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(loadOlderMessages, [channelId, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -45,9 +45,6 @@ function App({ user, fullscreenMessage }) {
|
|||
<Route path="/channels/:channelId"
|
||||
render={() => <LoggedInMount />}
|
||||
/>
|
||||
<Route path="/user/:userId"
|
||||
render={() => <LoggedInMount />}
|
||||
/>
|
||||
|
||||
<Route path="/">
|
||||
{ user && <LoggedInMount /> }
|
||||
|
|
|
@ -4,16 +4,14 @@ import { useParams } from "react-router-dom";
|
|||
import Sidebar from "../Sidebar";
|
||||
import ChannelView from "../channel/ChannelView";
|
||||
import GradientBanner from "../GradientBanner";
|
||||
import UserView from "../user/UserView";
|
||||
|
||||
function LoggedInMount({ gradientBannerNotificationText }) {
|
||||
const { channelId, userId } = useParams();
|
||||
const { channelId } = useParams();
|
||||
return <>
|
||||
<Sidebar />
|
||||
<div className="col-flex">
|
||||
<GradientBanner text={ gradientBannerNotificationText }/>
|
||||
{ (channelId) && <ChannelView channelId={ channelId } /> }
|
||||
{ (userId) && <UserView userId={ userId } /> }
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,39 @@
|
|||
import UserProfile from './UserProfileLink';
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import UserProfile from "./UserProfileLink";
|
||||
import { authenticated } from "../../api/request";
|
||||
import Modal from "../Modal";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
|
||||
export default function ChannelUserButton({ user, subtext }) {
|
||||
const history = useHistory();
|
||||
const [ isPromptOpen, setIsPromptOpen ] = useState(false);
|
||||
const [userObject, setUserObject] = useState(null);
|
||||
|
||||
const handleClick = () => {
|
||||
history.push(`/user/${user._id}`);
|
||||
};
|
||||
useEffect(() => {
|
||||
authenticated(`/api/v1/users/user/${user._id}/info`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
}).then(({ isOK, json }) => {
|
||||
if (isOK) {
|
||||
setUserObject(json.user);
|
||||
}
|
||||
});
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<button className="button button-channel" onClick={ handleClick }>
|
||||
<UserProfile subtext={ subtext } user={ user } size="32" />
|
||||
</button>
|
||||
<>
|
||||
<AnimatePresence>
|
||||
{(isPromptOpen) && <Modal onClose={ () => setIsPromptOpen(false) } alignItems="center" width="150px" height="150px">
|
||||
<UserProfile subtext={ subtext } user={ user } size="128" />
|
||||
<span className="label">{ user.status === 1 ? "Online" : "Offline" }</span>
|
||||
<span className="label">{ userObject ? userObject.role.toLowerCase() : "loading..." }</span>
|
||||
</Modal>}
|
||||
</AnimatePresence>
|
||||
<button className="button button-channel" onClick={ () => setIsPromptOpen(true) }>
|
||||
<UserProfile subtext={ subtext } user={ user } size="32" />
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { authenticated } from "../../api/request";
|
||||
import UserProfile from "./UserProfileLink";
|
||||
import ProfileLinkLoader from "../ProfileLinkLoader";
|
||||
|
||||
export default function UserView({ userId }) {
|
||||
const [userObject, setUserObject] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
authenticated(`/api/v1/users/user/${userId}/info`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
}).then(({ isOK, json }) => {
|
||||
if (isOK) {
|
||||
setUserObject(json.user);
|
||||
}
|
||||
});
|
||||
}, [userId]);
|
||||
|
||||
let view = null;
|
||||
if (userObject) {
|
||||
view = <>
|
||||
<UserProfile user={ userObject } size="32" />
|
||||
{(userObject.role === "ADMIN") && <span style={{ padding: "12px" }}>Admin</span>}
|
||||
{(userObject.role === "USER") && <span style={{ padding: "12px" }}>User</span>}
|
||||
</>
|
||||
} else {
|
||||
view = <>
|
||||
<ProfileLinkLoader />
|
||||
</>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="center grow">
|
||||
<div className="user-view center">
|
||||
{view}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -4,11 +4,14 @@ import App from './components/main/App';
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { LazyMotion, domAnimation } from "framer-motion"
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={ store }>
|
||||
<App />
|
||||
<LazyMotion features={ domAnimation }>
|
||||
<App />
|
||||
</LazyMotion>
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
|
|
|
@ -35,6 +35,11 @@ body {
|
|||
max-height: 100vh;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#modal-root {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button, input, optgroup, select, textarea {
|
||||
|
|
|
@ -62,6 +62,14 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.full-height {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.profile-badge {
|
||||
margin: 16px;
|
||||
}
|
||||
|
@ -87,3 +95,68 @@
|
|||
font-size: 3em;
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
|
||||
|
||||
.modal {
|
||||
min-width: 100px;
|
||||
min-height: 100px;
|
||||
max-width: 50%;
|
||||
max-height: 50%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
padding: 1.5em;
|
||||
border-radius: 1em;
|
||||
background-color: var(--accent-color-dark);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.modal {
|
||||
/* !important is used here because the height and width can be set using inline styles in the Modal component */
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-centered {
|
||||
@extend .modal;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.74rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: #0000008f;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999999999;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
margin: 32px;
|
||||
text-align: initial;
|
||||
}
|
|
@ -14,10 +14,13 @@
|
|||
color: var(--default-text-color);
|
||||
}
|
||||
|
||||
&.no-messages-icon {
|
||||
border-radius: 0;
|
||||
width: 16em;
|
||||
height: 16em;
|
||||
&.add-channel {
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--create-channel-background);
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
|
||||
&.default-user {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
.text-input {
|
||||
margin: 6px;
|
||||
padding: 6px;
|
||||
padding: 14px;
|
||||
border: none;
|
||||
min-width: 220px;
|
||||
color: var(--default-text-color);
|
||||
border-radius: var(--default-button-border-radius);
|
||||
background-color: var(--message-box-color);
|
||||
flex-grow: 1;
|
||||
|
||||
&.message-input {
|
||||
border-radius: var(--message-box-border-radius);
|
||||
|
@ -16,5 +16,6 @@
|
|||
padding-left: 16px;
|
||||
margin-top: 6px;
|
||||
font-size: 16px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
|
@ -4,4 +4,10 @@
|
|||
|
||||
.elevated-2 {
|
||||
box-shadow: rgba(0, 0, 0, 0.50) 0px 25px 50px -4px;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
color: var(--darker-text-color);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,13 @@
|
|||
hsl(225, 35%, 40%)
|
||||
);
|
||||
|
||||
--create-channel-background: linear-gradient(
|
||||
to top right,
|
||||
hsl(75, 35%, 40%),
|
||||
hsl(150, 35%, 40%),
|
||||
hsl(200, 35%, 40%)
|
||||
);
|
||||
|
||||
--default-scrollbar-color: var(--accent-color);
|
||||
--default-scrollbar-color-track: var(--background-color);
|
||||
--default-scrollbar-width: 1px;
|
||||
|
@ -28,7 +35,7 @@
|
|||
--channel-top-bar-color-accent: var(--background-color);
|
||||
--channel-top-bar-color: var(--background-color);
|
||||
--sidebar-background-color: hsl(230, 12%, 12%);
|
||||
--elevation-box-shadow: 0 1px 0 0 hsla(230, 12%, 8%, 0.2), 0 2px 0 0 hsla(230, 12%, 8%, 0.2), 0 3px 0 0 hsla(230, 12%, 10%, 0.1);
|
||||
--elevation-box-shadow: 0 2px 0 0 hsla(230, 12%, 8%, 0.167), 0 2px 0 0 hsla(230, 12%, 8%, 0.08), 0 3px 0 0 hsla(230, 12%, 10%, 0.07);
|
||||
--message-box-color: var(--accent-color);
|
||||
|
||||
--button-color: var(--accent-color-dark);
|
||||
|
|
64
yarn.lock
64
yarn.lock
|
@ -1219,6 +1219,18 @@
|
|||
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
|
||||
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
|
||||
|
||||
"@emotion/is-prop-valid@^0.8.2":
|
||||
version "0.8.8"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
|
||||
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
|
||||
dependencies:
|
||||
"@emotion/memoize" "0.7.4"
|
||||
|
||||
"@emotion/memoize@0.7.4":
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
|
||||
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
||||
|
||||
"@eslint/eslintrc@^0.4.3":
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
|
||||
|
@ -5061,6 +5073,26 @@ fragment-cache@^0.2.1:
|
|||
dependencies:
|
||||
map-cache "^0.2.2"
|
||||
|
||||
framer-motion@^4.1.17:
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.17.tgz#4029469252a62ea599902e5a92b537120cc89721"
|
||||
integrity sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==
|
||||
dependencies:
|
||||
framesync "5.3.0"
|
||||
hey-listen "^1.0.8"
|
||||
popmotion "9.3.6"
|
||||
style-value-types "4.1.4"
|
||||
tslib "^2.1.0"
|
||||
optionalDependencies:
|
||||
"@emotion/is-prop-valid" "^0.8.2"
|
||||
|
||||
framesync@5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.3.0.tgz#0ecfc955e8f5a6ddc8fdb0cc024070947e1a0d9b"
|
||||
integrity sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==
|
||||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
fresh@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
|
@ -5413,6 +5445,11 @@ hex-color-regex@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
|
||||
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
|
||||
|
||||
hey-listen@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
|
||||
integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
|
||||
|
||||
history@^4.9.0:
|
||||
version "4.10.1"
|
||||
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
|
||||
|
@ -8036,6 +8073,16 @@ pnp-webpack-plugin@1.6.4:
|
|||
dependencies:
|
||||
ts-pnp "^1.1.6"
|
||||
|
||||
popmotion@9.3.6:
|
||||
version "9.3.6"
|
||||
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.3.6.tgz#b5236fa28f242aff3871b9e23721f093133248d1"
|
||||
integrity sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==
|
||||
dependencies:
|
||||
framesync "5.3.0"
|
||||
hey-listen "^1.0.8"
|
||||
style-value-types "4.1.4"
|
||||
tslib "^2.1.0"
|
||||
|
||||
portfinder@^1.0.26:
|
||||
version "1.0.28"
|
||||
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
|
||||
|
@ -9017,6 +9064,13 @@ react-error-overlay@^6.0.9:
|
|||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
|
||||
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
|
||||
|
||||
react-feather@^2.0.9:
|
||||
version "2.0.9"
|
||||
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-2.0.9.tgz#6e42072130d2fa9a09d4476b0e61b0ed17814480"
|
||||
integrity sha512-yMfCGRkZdXwIs23Zw/zIWCJO3m3tlaUvtHiXlW+3FH7cIT6fiK1iJ7RJWugXq7Fso8ZaQyUm92/GOOHXvkiVUw==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
|
@ -10267,6 +10321,14 @@ style-loader@1.3.0:
|
|||
loader-utils "^2.0.0"
|
||||
schema-utils "^2.7.0"
|
||||
|
||||
style-value-types@4.1.4:
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-4.1.4.tgz#80f37cb4fb024d6394087403dfb275e8bb627e75"
|
||||
integrity sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==
|
||||
dependencies:
|
||||
hey-listen "^1.0.8"
|
||||
tslib "^2.1.0"
|
||||
|
||||
stylehacks@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
|
||||
|
@ -10580,7 +10642,7 @@ tslib@^1.8.1:
|
|||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.3:
|
||||
tslib@^2.0.3, tslib@^2.1.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
|
Loading…
Reference in a new issue