forked from extern/bruno
feat: modal
This commit is contained in:
parent
81f9668375
commit
1f1e8a602e
@ -0,0 +1,122 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
&.modal--animate-out{
|
||||
animation: fade-out 0.5s forwards cubic-bezier(.19,1,.22,1);
|
||||
|
||||
.grafnode-modal-card {
|
||||
animation: fade-and-slide-out-from-top .50s forwards cubic-bezier(.19,1,.22,1);
|
||||
}
|
||||
}
|
||||
|
||||
&.grafnode-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.grafnode-modal-card {
|
||||
animation-duration: .85s;
|
||||
animation-delay: .1s;
|
||||
background: var(--color-background-top);
|
||||
border-radius: var(--border-radius);
|
||||
position: relative;
|
||||
z-index: 1003;
|
||||
max-width: calc(100% - var(--spacing-base-unit));
|
||||
box-shadow: var(--box-shadow-base);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
will-change: opacity,transform;
|
||||
flex-grow: 0;
|
||||
margin: 3vh 10vw;
|
||||
margin-top: 50px;
|
||||
|
||||
&.modal-sm {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
&.modal-md {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
&.modal-lg {
|
||||
min-width: 800px;
|
||||
}
|
||||
|
||||
&.modal-xl {
|
||||
min-width: 1140px;
|
||||
}
|
||||
|
||||
animation: fade-and-slide-in-from-top .50s forwards cubic-bezier(.19,1,.22,1);
|
||||
}
|
||||
|
||||
.grafnode-modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
text-transform: uppercase;
|
||||
color: rgb(86 86 86);
|
||||
background-color: #f1f1f1;
|
||||
font-size: 0.75rem;
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
|
||||
.close {
|
||||
font-size: 1.3rem;
|
||||
line-height: 1;
|
||||
color: #000;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
opacity: 0.5;
|
||||
margin-top: -2px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grafnode-modal-content {
|
||||
flex-grow: 1;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.grafnode-modal-backdrop {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
position: fixed;
|
||||
will-change: opacity;
|
||||
background: transparent;
|
||||
|
||||
&:before{
|
||||
content: "";
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
opacity: .4;
|
||||
top: 0;
|
||||
background: black;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
animation: fade-in .1s forwards cubic-bezier(.19,1,.22,1);
|
||||
}
|
||||
|
||||
.grafnode-modal-footer {
|
||||
background-color: white;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
94
packages/grafnode-components/src/components/Modal/index.js
Normal file
94
packages/grafnode-components/src/components/Modal/index.js
Normal file
@ -0,0 +1,94 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const ModalHeader = ({title, handleCancel}) => (
|
||||
<div className="grafnode-modal-header">
|
||||
{title ? <div className="grafnode-modal-heade-title">{title}</div> : null}
|
||||
{handleCancel ? (
|
||||
<div className="close cursor-pointer" onClick={handleCancel ? () => handleCancel() : null}>
|
||||
×
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
const ModalContent = ({children}) => (
|
||||
<div className="grafnode-modal-content px-4 py-6">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
const ModalFooter = ({confirmText, cancelText, handleSubmit, handleCancel, confirmDisabled}) => {
|
||||
confirmText = confirmText || 'Save';
|
||||
cancelText = cancelText || 'Cancel';
|
||||
|
||||
return (
|
||||
<div className="flex justify-end p-4 grafnode-modal-footer">
|
||||
<span className="mr-2">
|
||||
<button type="button" onClick={handleCancel} className="btn btn-sm btn-close">
|
||||
{cancelText}
|
||||
</button>
|
||||
</span>
|
||||
<span className="">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary" disabled={confirmDisabled} onClick={handleSubmit} >
|
||||
{confirmText}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ConfirmModal = ({
|
||||
size,
|
||||
title,
|
||||
confirmText,
|
||||
cancelText,
|
||||
handleCancel,
|
||||
handleConfirm,
|
||||
children,
|
||||
confirmDisabled
|
||||
}) => {
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const escFunction = (event) => {
|
||||
const escKeyCode = 27;
|
||||
if (event.keyCode === escKeyCode) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setIsClosing(true);
|
||||
setTimeout(() => handleCancel(), 500);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', escFunction, false);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', escFunction, false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
let classes = 'grafnode-modal';
|
||||
if (isClosing) {
|
||||
classes += ' modal--animate-out';
|
||||
}
|
||||
return (
|
||||
<StyledWrapper className={classes}>
|
||||
<div className={`grafnode-modal-card modal-${size}`}>
|
||||
<ModalHeader title={title} handleCancel={() => closeModal()} />
|
||||
<ModalContent>{children}</ModalContent>
|
||||
<ModalFooter
|
||||
confirmText={confirmText}
|
||||
cancelText={cancelText}
|
||||
handleCancel={() => closeModal()}
|
||||
handleSubmit={handleConfirm}
|
||||
confirmDisabled={confirmDisabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="grafnode-modal-backdrop" onClick={() => closeModal()} />
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmModal;
|
@ -1,11 +1,14 @@
|
||||
import React, { forwardRef, useRef } from 'react';
|
||||
import React, { useState, forwardRef, useRef } from 'react';
|
||||
import Collections from './Collections';
|
||||
import Modal from '../Modal';
|
||||
import Navbar from '../Navbar';
|
||||
import Dropdown from '../Dropdown';
|
||||
import { IconBox, IconSearch, IconDots } from '@tabler/icons';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const Sidebar = ({collections, actions, dispatch, activeRequestTabId}) => {
|
||||
const [modalOpen, setModalOpen] = useState(true);
|
||||
|
||||
const menuDropdownTippyRef = useRef();
|
||||
const onMenuDropdownCreate = (ref) => menuDropdownTippyRef.current = ref;
|
||||
const MenuIcon = forwardRef((props, ref) => {
|
||||
@ -16,8 +19,27 @@ const Sidebar = ({collections, actions, dispatch, activeRequestTabId}) => {
|
||||
);
|
||||
});
|
||||
|
||||
const handleCancel = () => {
|
||||
setModalOpen(false);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
setModalOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
{modalOpen && (
|
||||
<Modal
|
||||
size="md"
|
||||
title='Create Collection'
|
||||
handleCancel={handleCancel}
|
||||
handleConfirm={handleConfirm}
|
||||
>
|
||||
Hello
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
<Navbar />
|
||||
|
||||
<div className="mt-4 px-2 py-1 flex collection-title">
|
||||
@ -28,6 +50,7 @@ const Sidebar = ({collections, actions, dispatch, activeRequestTabId}) => {
|
||||
<div>
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setModalOpen(true);
|
||||
}}>
|
||||
Create Collection
|
||||
</div>
|
||||
|
49
packages/grafnode-run/src/globalStyles.js
Normal file
49
packages/grafnode-run/src/globalStyles.js
Normal file
@ -0,0 +1,49 @@
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-and-slide-in-from-top {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-30px);
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-and-slide-out-from-top {
|
||||
from {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
to {
|
||||
opacity: 2;
|
||||
-webkit-transform: translateY(-30px);
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default GlobalStyle;
|
@ -1,5 +1,6 @@
|
||||
import Head from 'next/head';
|
||||
import Main from 'pageComponents/Main';
|
||||
import GlobalStyle from '../globalStyles';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@ -9,6 +10,8 @@ export default function Home() {
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<GlobalStyle />
|
||||
|
||||
<main>
|
||||
<Main />
|
||||
</main>
|
||||
|
Loading…
Reference in New Issue
Block a user