Merge pull request #1760 from SkynetLabs/dashboard-v2-main-screen
Dashboard V2: main screen
This commit is contained in:
commit
8e648a32a0
|
@ -3,6 +3,7 @@ module.exports = {
|
|||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"storybook-addon-gatsby",
|
||||
{
|
||||
name: "@storybook/addon-postcss",
|
||||
options: {
|
||||
|
|
|
@ -9,6 +9,7 @@ module.exports = {
|
|||
"gatsby-plugin-react-helmet",
|
||||
"gatsby-plugin-sharp",
|
||||
"gatsby-transformer-sharp",
|
||||
"gatsby-plugin-styled-components",
|
||||
"gatsby-plugin-postcss",
|
||||
{
|
||||
resolve: "gatsby-source-filesystem",
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import * as React from "react";
|
||||
import "@fontsource/sora/300.css"; // light
|
||||
import "@fontsource/sora/400.css"; // normal
|
||||
import "@fontsource/sora/500.css"; // medium
|
||||
import "@fontsource/sora/600.css"; // semibold
|
||||
import "@fontsource/source-sans-pro/400.css"; // normal
|
||||
import "@fontsource/source-sans-pro/600.css"; // semibold
|
||||
import "./src/styles/global.css";
|
||||
|
||||
export function wrapPageElement({ element, props }) {
|
||||
const Layout = element.type.Layout ?? React.Fragment;
|
||||
return <Layout {...props}>{element}</Layout>;
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
"gatsby": "^4.6.2",
|
||||
"gatsby-plugin-postcss": "^5.7.0",
|
||||
"postcss": "^8.4.6",
|
||||
"pretty-bytes": "^6.0.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-helmet": "^6.1.0",
|
||||
|
@ -44,20 +45,23 @@
|
|||
"autoprefixer": "^10.4.2",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.2.3",
|
||||
"babel-plugin-preval": "^5.1.0",
|
||||
"babel-plugin-styled-components": "^2.0.2",
|
||||
"eslint": "^8.9.0",
|
||||
"eslint-config-react-app": "^7.0.0",
|
||||
"eslint-plugin-storybook": "^0.5.6",
|
||||
"gatsby-plugin-alias-imports": "^1.0.5",
|
||||
"gatsby-plugin-image": "^2.6.0",
|
||||
"gatsby-plugin-preval": "^1.0.0",
|
||||
"gatsby-plugin-provide-react": "^1.0.2",
|
||||
"gatsby-plugin-react-helmet": "^5.6.0",
|
||||
"gatsby-plugin-sharp": "^4.6.0",
|
||||
"gatsby-plugin-styled-components": "^5.7.0",
|
||||
"gatsby-plugin-styled-components": "^5.8.0",
|
||||
"gatsby-source-filesystem": "^4.6.0",
|
||||
"gatsby-transformer-sharp": "^4.6.0",
|
||||
"prettier": "2.5.1",
|
||||
"react-is": "^17.0.2",
|
||||
"storybook-addon-gatsby": "^0.0.5",
|
||||
"styled-components": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import * as React from "react";
|
||||
import fileSize from "pretty-bytes";
|
||||
import { Link } from "gatsby";
|
||||
|
||||
import { GraphBar } from "./GraphBar";
|
||||
import { UsageGraph } from "./UsageGraph";
|
||||
|
||||
// TODO: get real data
|
||||
const useUsageData = () => ({
|
||||
files: {
|
||||
used: 19_521,
|
||||
limit: 20_000,
|
||||
},
|
||||
storage: {
|
||||
used: 23_000_000_000,
|
||||
limit: 1_000_000_000_000,
|
||||
},
|
||||
});
|
||||
|
||||
const size = (bytes) => {
|
||||
const text = fileSize(bytes, { maximumFractionDigits: 1 });
|
||||
const [value, unit] = text.split(" ");
|
||||
|
||||
return {
|
||||
text,
|
||||
value,
|
||||
unit,
|
||||
};
|
||||
};
|
||||
|
||||
export default function CurrentUsage() {
|
||||
const { files, storage } = useUsageData();
|
||||
|
||||
const storageUsage = size(storage.used);
|
||||
const storageLimit = size(storage.limit);
|
||||
const filesUsedLabel = React.useMemo(() => ({ value: files.used, unit: "files" }), [files.used]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h4>
|
||||
{storageUsage.text} of {storageLimit.text}
|
||||
</h4>
|
||||
<p className="text-palette-400">
|
||||
{files.used} of {files.limit} files
|
||||
</p>
|
||||
<div className="relative mt-7 font-sans uppercase text-xs">
|
||||
<div className="flex place-content-between">
|
||||
<span>Storage</span>
|
||||
<span>{storageLimit.text}</span>
|
||||
</div>
|
||||
<UsageGraph>
|
||||
<GraphBar value={storage.used} limit={storage.limit} label={storageUsage} />
|
||||
<GraphBar value={files.used} limit={files.limit} label={filesUsedLabel} />
|
||||
</UsageGraph>
|
||||
<div className="flex place-content-between">
|
||||
<span>Files</span>
|
||||
<span className="inline-flex place-content-between w-[37%]">
|
||||
<Link
|
||||
to="/upgrade"
|
||||
className="text-primary underline-offset-3 decoration-dotted hover:text-primary-light hover:underline"
|
||||
>
|
||||
UPGRADE
|
||||
</Link>{" "}
|
||||
{/* TODO: proper URL */}
|
||||
<span>{files.limit}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
const Bar = styled.div.attrs({
|
||||
className: `relative flex justify-end h-4 bg-primary rounded-l rounded-r-lg`,
|
||||
})`
|
||||
min-width: 1rem;
|
||||
width: ${({ $percentage }) => $percentage}%;
|
||||
`;
|
||||
|
||||
const BarTip = styled.span.attrs({
|
||||
className: "relative w-4 h-4 border-2 rounded-full bg-white border-primary",
|
||||
})``;
|
||||
|
||||
const BarLabel = styled.span.attrs({
|
||||
className: "bg-white rounded border-2 border-palette-200 px-3 whitespace-nowrap absolute shadow",
|
||||
})`
|
||||
right: max(0%, ${({ $percentage }) => 100 - $percentage}%);
|
||||
top: -0.5rem;
|
||||
transform: translateX(50%);
|
||||
`;
|
||||
|
||||
export const GraphBar = ({ value, limit, label }) => {
|
||||
const percentage = typeof limit !== "number" || limit === 0 ? 0 : (value / limit) * 100;
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center">
|
||||
<Bar $percentage={percentage}>
|
||||
<BarTip />
|
||||
</Bar>
|
||||
<BarLabel $percentage={percentage}>
|
||||
<span className="font-sora text-lg">{label.value}</span> <span>{label.unit}</span>
|
||||
</BarLabel>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
export const UsageGraph = styled.div.attrs({
|
||||
className: "w-full my-3 grid grid-flow-row grid-rows-2",
|
||||
})`
|
||||
height: 146px;
|
||||
background: url(/images/usage-graph-bg.svg) no-repeat;
|
||||
background-size: cover;
|
||||
`;
|
|
@ -0,0 +1,3 @@
|
|||
import CurrentUsage from "./CurrentUsage";
|
||||
|
||||
export default CurrentUsage;
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from "react";
|
||||
import { Table, TableBody, TableCell, TableRow } from "../Table";
|
||||
|
||||
export default function ActivityTable({ data }) {
|
||||
return (
|
||||
<Table style={{ tableLayout: "fixed" }}>
|
||||
<TableBody>
|
||||
{data.map(({ name, type, size, uploaded, skylink }) => (
|
||||
<TableRow key={skylink}>
|
||||
<TableCell>{name}</TableCell>
|
||||
<TableCell className="w-[80px]">{type}</TableCell>
|
||||
<TableCell className="w-[80px]" align="right">
|
||||
{size}
|
||||
</TableCell>
|
||||
<TableCell className="w-[180px]">{uploaded}</TableCell>
|
||||
<TableCell>{skylink}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import * as React from "react";
|
||||
import { Panel } from "../Panel";
|
||||
import { Tab, TabPanel, Tabs } from "../Tabs";
|
||||
import ActivityTable from "./ActivityTable";
|
||||
import useRecentActivityData from "./useActivityData";
|
||||
|
||||
export default function LatestActivity() {
|
||||
const { downloads, uploads } = useRecentActivityData();
|
||||
|
||||
return (
|
||||
<Panel title="Latest activity">
|
||||
<Tabs>
|
||||
<Tab id="uploads" title="Uploads" />
|
||||
<Tab id="downloads" title="Downloads" />
|
||||
<TabPanel tabId="uploads" className="pt-4">
|
||||
<ActivityTable data={uploads} />
|
||||
</TabPanel>
|
||||
<TabPanel tabId="downloads" className="pt-4">
|
||||
<ActivityTable data={downloads} />
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Panel>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import LatestActivity from "./LatestActivity";
|
||||
|
||||
export default LatestActivity;
|
|
@ -0,0 +1,54 @@
|
|||
const UPLOADS_DATA = [
|
||||
{
|
||||
name: "At_vereo_eos_censes",
|
||||
type: ".mp4",
|
||||
size: "2.45 MB",
|
||||
uploaded: "a few seconds ago",
|
||||
skylink: "_HyFqH632Rmy99c93idTtBVXeRDgaDAAWg6Bmm5P1izriu",
|
||||
},
|
||||
{
|
||||
name: "Miriam Klein IV",
|
||||
type: ".pdf",
|
||||
size: "7.52 MB",
|
||||
uploaded: "01/04/2021; 17:11",
|
||||
skylink: "_izriuHyFqH632Rmy99c93idTtBVXeRDgaDAAWg6Bmm5P1",
|
||||
},
|
||||
{
|
||||
name: "tmp/QmWR6eVDVkwhAYq7X99w4xT9KNKBzwK39Fj1PDmr4ZnzMm/QmWR6eVDVkwhAYq7X99w4xT9KNKBzwK39Fj1PDmr4ZnzMm",
|
||||
type: ".doc",
|
||||
size: "8.15 MB",
|
||||
uploaded: "10/26/2020; 7:21",
|
||||
skylink: "_VXeRDgaDAAWg6Bmm5P1izriuHyFqH632Rmy99c93idTtB",
|
||||
},
|
||||
{
|
||||
name: "Perm_London",
|
||||
type: ".avi",
|
||||
size: "225.6 MB",
|
||||
uploaded: "09/12/2020; 19:28",
|
||||
skylink: "_eRDgaDAAWg6Bmm5P1izriuHyFqH632Rmy99c93idTtBVX",
|
||||
},
|
||||
{
|
||||
name: "Santa_Clara",
|
||||
type: ".pdf",
|
||||
size: "7.52 MB",
|
||||
uploaded: "09/12/2020; 19:23",
|
||||
skylink: "_AWg6Bmm5P1izriuHyFqH632Rmy99c93idTtBVXeRDgaDA",
|
||||
},
|
||||
{
|
||||
name: "Marysa_Labrone",
|
||||
type: ".doc",
|
||||
size: "8.15 MB",
|
||||
uploaded: "09/12/2020; 19:21",
|
||||
skylink: "_P1izriuHyFqH632Rmy99c93idTtBVXeRDgaDAAWg6Bmm5",
|
||||
},
|
||||
];
|
||||
|
||||
const DOWNLOADS_DATA = UPLOADS_DATA.slice().reverse();
|
||||
|
||||
// TODO: get real data
|
||||
export default function useRecentActivityData() {
|
||||
return {
|
||||
uploads: UPLOADS_DATA,
|
||||
downloads: DOWNLOADS_DATA,
|
||||
};
|
||||
}
|
|
@ -1,21 +1,78 @@
|
|||
import { Link } from "gatsby";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { screen } from "../../lib/cssHelpers";
|
||||
import { DropdownMenu, DropdownMenuLink } from "../DropdownMenu";
|
||||
import { CogIcon, LockClosedIcon, SkynetLogoIcon } from "../Icons";
|
||||
import { PageContainer } from "../PageContainer";
|
||||
|
||||
import { NavBarLink, NavBarSection } from ".";
|
||||
|
||||
const NavBarContainer = styled.div.attrs({
|
||||
className: `grid sticky top-0 bg-white`,
|
||||
})``;
|
||||
|
||||
const NavBarBody = styled.nav.attrs({
|
||||
className: "grid h-[80px] font-sans font-light text-sm",
|
||||
className: "grid font-sans font-light text-xs sm:text-sm",
|
||||
})`
|
||||
height: 100px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 60px 40px;
|
||||
grid-template-areas:
|
||||
"logo dropdown"
|
||||
"navigation navigation";
|
||||
|
||||
${screen(
|
||||
"sm",
|
||||
`
|
||||
height: 80px;
|
||||
grid-template-columns: auto max-content 1fr;
|
||||
grid-template-areas: "logo navigation dropdown";
|
||||
grid-template-rows: auto;
|
||||
`
|
||||
)}
|
||||
|
||||
.navigation-area {
|
||||
grid-area: navigation;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.logo-area {
|
||||
grid-area: logo;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.dropdown-area {
|
||||
grid-area: dropdown;
|
||||
}
|
||||
`;
|
||||
|
||||
export const NavBar = (props) => (
|
||||
export const NavBar = () => (
|
||||
<NavBarContainer>
|
||||
<PageContainer>
|
||||
<NavBarBody {...props} />
|
||||
<PageContainer className="px-0">
|
||||
<NavBarBody>
|
||||
<NavBarSection className="logo-area pl-2 pr-4 md:px-0 md:w-[110px] justify-center sm:justify-start">
|
||||
<SkynetLogoIcon size={48} />
|
||||
</NavBarSection>
|
||||
<NavBarSection className="navigation-area border-t border-palette-100">
|
||||
<NavBarLink to="/" as={Link} activeClassName="!border-b-primary">
|
||||
Dashboard
|
||||
</NavBarLink>
|
||||
<NavBarLink to="/files" as={Link} activeClassName="!border-b-primary">
|
||||
Files
|
||||
</NavBarLink>
|
||||
<NavBarLink to="/payments" as={Link} activeClassName="!border-b-primary">
|
||||
Payments
|
||||
</NavBarLink>
|
||||
</NavBarSection>
|
||||
<NavBarSection className="dropdown-area justify-end">
|
||||
<DropdownMenu title="My account">
|
||||
<DropdownMenuLink href="/settings" icon={CogIcon} label="Settings" />
|
||||
<DropdownMenuLink href="/logout" icon={LockClosedIcon} label="Log out" />
|
||||
</DropdownMenu>
|
||||
</NavBarSection>
|
||||
</NavBarBody>
|
||||
</PageContainer>
|
||||
</NavBarContainer>
|
||||
);
|
||||
|
|
|
@ -9,17 +9,7 @@ export default {
|
|||
},
|
||||
};
|
||||
|
||||
const Template = (props) => (
|
||||
<NavBar {...props}>
|
||||
<NavBarSection>
|
||||
<NavBarLink href="/dashboard" active>
|
||||
Dashboard
|
||||
</NavBarLink>
|
||||
<NavBarLink href="/files">Files</NavBarLink>
|
||||
<NavBarLink href="/payments">Payments</NavBarLink>
|
||||
</NavBarSection>
|
||||
</NavBar>
|
||||
);
|
||||
const Template = (props) => <NavBar {...props} />;
|
||||
|
||||
export const DashboardTopNavigation = Template.bind({});
|
||||
DashboardTopNavigation.args = {};
|
||||
|
|
|
@ -3,7 +3,7 @@ import styled from "styled-components";
|
|||
|
||||
export const NavBarLink = styled.a.attrs(({ active }) => ({
|
||||
className: `
|
||||
min-w-[168px]
|
||||
sm:min-w-[133px] lg:min-w-[168px]
|
||||
flex h-full items-center justify-center
|
||||
border-x border-x-palette-100 border-b-2
|
||||
text-palette-600 transition-colors hover:bg-palette-100/50
|
||||
|
|
|
@ -2,7 +2,7 @@ import PropTypes from "prop-types";
|
|||
import styled from "styled-components";
|
||||
|
||||
export const PageContainer = styled.div.attrs({
|
||||
className: `mx-auto w-page md:w-page-md lg:w-page-lg xl:w-page-xl`,
|
||||
className: `mx-auto w-page lg:w-page-lg xl:w-page-xl px-2 md:px-16 lg:px-0`,
|
||||
})``;
|
||||
|
||||
PageContainer.propTypes = {
|
||||
|
|
|
@ -25,7 +25,7 @@ Panel.propTypes = {
|
|||
/**
|
||||
* Label of the panel
|
||||
*/
|
||||
title: PropTypes.string,
|
||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
};
|
||||
|
||||
Panel.defaultProps = {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import PropTypes from "prop-types";
|
||||
|
||||
export default function Bullets({ visibleSlides, activeIndex, allSlides, changeSlide }) {
|
||||
if (allSlides <= visibleSlides) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-3 pt-6">
|
||||
{Array(allSlides - visibleSlides + 1)
|
||||
.fill(null)
|
||||
.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
className={`rounded-full w-3 h-3 ${activeIndex === index ? "bg-primary" : "border-2 cursor-pointer"}`}
|
||||
onClick={() => changeSlide(index)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Bullets.propTypes = {
|
||||
allSlides: PropTypes.number.isRequired,
|
||||
activeIndex: PropTypes.number.isRequired,
|
||||
visibleSlides: PropTypes.number.isRequired,
|
||||
changeSlide: PropTypes.func.isRequired,
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
import styled from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const Slide = styled.div.attrs(({ isVisible }) => ({
|
||||
className: `slider-slide transition-opacity ${isVisible ? "" : "opacity-50 cursor-pointer"}`,
|
||||
}))``;
|
||||
|
||||
Slide.propTypes = {
|
||||
isVisible: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default Slide;
|
|
@ -0,0 +1,127 @@
|
|||
import * as React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled, { css } from "styled-components";
|
||||
import theme from "../../lib/theme";
|
||||
|
||||
import useActiveBreakpoint from "./useActiveBreakpoint";
|
||||
import Bullets from "./Bullets";
|
||||
import Slide from "./Slide";
|
||||
|
||||
const Container = styled.div.attrs({
|
||||
className: "slider w-full",
|
||||
})``;
|
||||
|
||||
/**
|
||||
* Styles applied to the movable element when the number of slide elements
|
||||
* exceeds the number of visible slides for the current breakpoint
|
||||
* */
|
||||
const scrollableStyles = css`
|
||||
${({ $allSlides, $visibleSlides, $activeIndex }) => `
|
||||
transform: translateX(calc(-1 * ${$activeIndex} * ((100% + 1rem) / ${$visibleSlides})));
|
||||
grid-template-columns: repeat(${$allSlides}, calc((100% - ${$visibleSlides - 1}rem) / ${$visibleSlides}));
|
||||
`}
|
||||
`;
|
||||
|
||||
const Scroller = styled.div.attrs({
|
||||
className: "slider-scroller grid gap-4 transition-transform",
|
||||
})`
|
||||
${({ $scrollable }) => ($scrollable ? scrollableStyles : "")}
|
||||
`;
|
||||
|
||||
const Slider = ({ slides, breakpoints }) => {
|
||||
const { visibleSlides, scrollable } = useActiveBreakpoint(breakpoints);
|
||||
const [activeIndex, setActiveIndex] = React.useState(0);
|
||||
const changeSlide = React.useCallback(
|
||||
(index) => {
|
||||
setActiveIndex(Math.min(index, slides.length - visibleSlides)); // Don't let it scroll too far
|
||||
},
|
||||
[slides, visibleSlides, setActiveIndex]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const maxIndex = slides.length - visibleSlides;
|
||||
|
||||
// Make sure to not scroll too far when screen size changes.
|
||||
if (activeIndex > maxIndex) {
|
||||
setActiveIndex(maxIndex);
|
||||
}
|
||||
}, [slides.length, visibleSlides, activeIndex]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Scroller
|
||||
$visibleSlides={visibleSlides}
|
||||
$allSlides={slides.length}
|
||||
$activeIndex={activeIndex}
|
||||
$scrollable={scrollable}
|
||||
>
|
||||
{slides.map((slide, index) => {
|
||||
const isVisible = index >= activeIndex && index < activeIndex + visibleSlides;
|
||||
|
||||
return (
|
||||
<div key={`slide-${index}`}>
|
||||
<Slide
|
||||
isVisible={isVisible || !scrollable}
|
||||
onClick={scrollable && !isVisible ? () => changeSlide(index) : null}
|
||||
>
|
||||
{slide}
|
||||
</Slide>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Scroller>
|
||||
{scrollable && (
|
||||
<Bullets
|
||||
activeIndex={activeIndex}
|
||||
allSlides={slides.length}
|
||||
visibleSlides={visibleSlides}
|
||||
changeSlide={changeSlide}
|
||||
/>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
Slider.propTypes = {
|
||||
slides: PropTypes.arrayOf(PropTypes.node.isRequired),
|
||||
breakpoints: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
/**
|
||||
* Breakpoint name as defined in Tailwind config. If not defined, config
|
||||
* will be applied to all non-configured screen sizes.
|
||||
*/
|
||||
name: PropTypes.string,
|
||||
/**
|
||||
* Number of slides visible for a given breakpoint.
|
||||
*/
|
||||
visibleSlides: PropTypes.number.isRequired,
|
||||
/**
|
||||
* Whether or not the list should be scrollable horizontally at the given breakpoint.
|
||||
* If set to false, all slides will be visible & rendered in a column.
|
||||
*/
|
||||
scrollable: PropTypes.bool.isRequired,
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
Slider.defaultProps = {
|
||||
breakpoints: [
|
||||
{
|
||||
name: "xl",
|
||||
scrollable: true,
|
||||
visibleSlides: 3,
|
||||
},
|
||||
{
|
||||
name: "md",
|
||||
scrollable: true,
|
||||
visibleSlides: 2,
|
||||
},
|
||||
{
|
||||
// For the smallest screens, we won't scroll but instead stack the slides vertically.
|
||||
scrollable: false,
|
||||
visibleSlides: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Slider;
|
|
@ -0,0 +1 @@
|
|||
export * from "./Slider";
|
|
@ -0,0 +1,38 @@
|
|||
import { useEffect, useMemo, useCallback, useState } from "react";
|
||||
import { useWindowSize } from "react-use";
|
||||
|
||||
import theme from "../../lib/theme";
|
||||
|
||||
const { screens } = theme;
|
||||
|
||||
export default function useActiveBreakpoint(breakpoints) {
|
||||
const { width: windowWidth } = useWindowSize();
|
||||
const monitoredBreakpoints = useMemo(
|
||||
() =>
|
||||
breakpoints
|
||||
.slice()
|
||||
// Map breakpoint names to their min-width configured in Tailwind
|
||||
.map(({ name, ...config }) => {
|
||||
// If breakpoint name is not configured,
|
||||
// we'll apply this config to all unmatched breakpoints.
|
||||
const minWidth = screens[name] ? parseInt(screens[name], 10) : -Infinity;
|
||||
|
||||
return { minWidth, ...config };
|
||||
})
|
||||
// Since our breakpoints are setup with min-width rule, we need to sort them from largest to smallest
|
||||
.sort(({ minWidth: widthA }, { minWidth: widthB }) => widthB - widthA),
|
||||
[breakpoints]
|
||||
);
|
||||
const findActiveBreakpoint = useCallback(
|
||||
() => monitoredBreakpoints.find((breakpoint) => windowWidth >= breakpoint.minWidth),
|
||||
[monitoredBreakpoints, windowWidth]
|
||||
);
|
||||
|
||||
const [activeBreakpoint, setActiveBreakpoint] = useState(findActiveBreakpoint());
|
||||
|
||||
useEffect(() => {
|
||||
setActiveBreakpoint(findActiveBreakpoint());
|
||||
}, [findActiveBreakpoint]);
|
||||
|
||||
return activeBreakpoint;
|
||||
}
|
|
@ -4,7 +4,7 @@ import styled from "styled-components";
|
|||
* Accepts all HMTL attributes a `<td>` element does.
|
||||
*/
|
||||
export const TableCell = styled.td.attrs({
|
||||
className: `px-6 py-4 h-tableRow truncate
|
||||
className: `first:pl-6 last:pr-6 px-2 py-4 h-tableRow truncate
|
||||
text-palette-600 even:text-palette-400
|
||||
first:rounded-l-sm last:rounded-r-sm`,
|
||||
})`
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { useWindowSize } from "react-use";
|
||||
|
||||
const Wrapper = styled.div.attrs({
|
||||
className: "absolute left-0 bottom-0 w-full h-0.5 bg-palette-200",
|
||||
|
@ -15,6 +16,7 @@ const Indicator = styled.div.attrs({
|
|||
export const ActiveTabIndicator = ({ tabRef }) => {
|
||||
const [position, setPosition] = useState(0);
|
||||
const [width, setWidth] = useState(0);
|
||||
const { width: windowWidth } = useWindowSize();
|
||||
|
||||
useEffect(() => {
|
||||
if (!tabRef?.current) {
|
||||
|
@ -24,7 +26,7 @@ export const ActiveTabIndicator = ({ tabRef }) => {
|
|||
const { offsetLeft, offsetWidth } = tabRef.current;
|
||||
setPosition(offsetLeft);
|
||||
setWidth(offsetWidth);
|
||||
}, [tabRef]);
|
||||
}, [tabRef, windowWidth]);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
|
@ -33,6 +35,9 @@ export const ActiveTabIndicator = ({ tabRef }) => {
|
|||
);
|
||||
};
|
||||
|
||||
// Needed, because we're using an Element constant here which Gatsby doesn't recognize during build time.
|
||||
if (typeof window !== "undefined") {
|
||||
ActiveTabIndicator.propTypes = {
|
||||
tabRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.instanceOf(Element) })]),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,50 +1,23 @@
|
|||
import * as React from "react";
|
||||
import { Link } from "gatsby";
|
||||
|
||||
import styled from "styled-components";
|
||||
import { DropdownMenu, DropdownMenuLink } from "../components/DropdownMenu";
|
||||
|
||||
import { PageContainer } from "../components/PageContainer";
|
||||
import { CogIcon, SkynetLogoIcon, LockClosedIcon } from "../components/Icons";
|
||||
import { NavBar, NavBarLink, NavBarSection } from "../components/Navbar";
|
||||
import { NavBar } from "../components/Navbar";
|
||||
import { Footer } from "../components/Footer";
|
||||
|
||||
const Layout = styled.div.attrs({
|
||||
className: "h-screen overflow-hidden",
|
||||
className: "min-h-screen overflow-hidden",
|
||||
})`
|
||||
background-image: url(/images/dashboard-bg.svg);
|
||||
background-position: -300px -280px;
|
||||
|
||||
.navbar {
|
||||
grid-template-columns: auto max-content 1fr;
|
||||
}
|
||||
background-position: center -280px;
|
||||
background-repeat: no-repeat;
|
||||
`;
|
||||
|
||||
const DashboardLayout = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<Layout>
|
||||
<NavBar className="navbar">
|
||||
<NavBarSection className="w-[110px] justify-start">
|
||||
<SkynetLogoIcon size={48} />
|
||||
</NavBarSection>
|
||||
<NavBarSection>
|
||||
<NavBarLink to="/" as={Link} activeClassName="!border-b-primary">
|
||||
Dashboard
|
||||
</NavBarLink>
|
||||
<NavBarLink to="/files" as={Link} activeClassName="!border-b-primary">
|
||||
Files
|
||||
</NavBarLink>
|
||||
<NavBarLink to="/payments" as={Link} activeClassName="!border-b-primary">
|
||||
Payments
|
||||
</NavBarLink>
|
||||
</NavBarSection>
|
||||
<NavBarSection className="justify-end">
|
||||
<DropdownMenu title="My account">
|
||||
<DropdownMenuLink href="/settings" icon={CogIcon} label="Settings" />
|
||||
<DropdownMenuLink href="/logout" icon={LockClosedIcon} label="Log out" />
|
||||
</DropdownMenu>
|
||||
</NavBarSection>
|
||||
</NavBar>
|
||||
<NavBar />
|
||||
<PageContainer>
|
||||
<main className="mt-14">{children}</main>
|
||||
</PageContainer>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { css } from "styled-components";
|
||||
import theme from "./theme";
|
||||
|
||||
export const screen = (breakpoint, style) => {
|
||||
const { screens } = theme;
|
||||
const minWidth = screens[breakpoint];
|
||||
|
||||
if (typeof minWidth === "undefined") {
|
||||
throw ReferenceError(
|
||||
`Screen "${breakpoint}" is not defined in Tailwind config. Available values are: ${Object.keys(screens).join(
|
||||
", "
|
||||
)}.`
|
||||
);
|
||||
}
|
||||
|
||||
return css`
|
||||
@media (min-width: ${minWidth}) {
|
||||
${style}
|
||||
}
|
||||
`;
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
// @preval
|
||||
// This file is pre-evaluated on build-time, so the config is only resolved once
|
||||
// and then included in the bundle for us to use in dynamic styles.
|
||||
import resolveConfig from "tailwindcss/resolveConfig";
|
||||
|
||||
import tailwindConfig from "../../tailwind.config.js";
|
||||
|
||||
export default resolveConfig(tailwindConfig).theme;
|
|
@ -1,9 +1,67 @@
|
|||
import * as React from "react";
|
||||
import { useMedia } from "react-use";
|
||||
|
||||
import theme from "../lib/theme";
|
||||
import { ArrowRightIcon } from "../components/Icons";
|
||||
import { Panel } from "../components/Panel";
|
||||
import { Tab, TabPanel, Tabs } from "../components/Tabs";
|
||||
import LatestActivity from "../components/LatestActivity/LatestActivity";
|
||||
import DashboardLayout from "../layouts/DashboardLayout";
|
||||
import Slider from "../components/Slider/Slider";
|
||||
import CurrentUsage from "../components/CurrentUsage";
|
||||
|
||||
const IndexPage = () => {
|
||||
return <>Dashboard</>;
|
||||
const showRecentActivity = useMedia(`(min-width: ${theme.screens.md})`);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full">
|
||||
<Slider
|
||||
slides={[
|
||||
<Panel title="Upload">
|
||||
<Tabs variant="fill">
|
||||
<Tab id="files" title="Files" />
|
||||
<Tab id="directory" title="Directory" />
|
||||
<TabPanel tabId="files">
|
||||
<div className="w-full py-16 bg-palette-100/50 text-center">Upload files...</div>
|
||||
</TabPanel>
|
||||
<TabPanel tabId="directory">
|
||||
<div className="w-full py-16 bg-palette-100/50 text-center">Upload a directory...</div>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Panel>,
|
||||
<Panel
|
||||
title={
|
||||
<>
|
||||
<ArrowRightIcon /> Usage
|
||||
</>
|
||||
}
|
||||
>
|
||||
<CurrentUsage />
|
||||
</Panel>,
|
||||
<Panel
|
||||
title={
|
||||
<>
|
||||
<ArrowRightIcon /> Current plan
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ul>
|
||||
<li>Current</li>
|
||||
<li>Plan</li>
|
||||
<li>Info</li>
|
||||
</ul>
|
||||
</Panel>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
{showRecentActivity && (
|
||||
<div className="mt-10">
|
||||
<LatestActivity />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
IndexPage.Layout = DashboardLayout;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 378 146" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="8" height="8">
|
||||
<path d="M-2,2 l6,-6
|
||||
M0,8 l8,-8
|
||||
M7,10 l6,-6" stroke="#d4dddb" />
|
||||
</pattern>
|
||||
<style>.cls-1{fill:#f5f7f7;}.cls-2{fill:#d4dddb; opacity: 0.5;}.cls-3{fill:url(#diagonalHatch);}.cls-4{fill:#80e3af;}</style>
|
||||
</defs>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<rect class="cls-1" width="2" height="146" rx="1"/>
|
||||
<rect class="cls-1" x="4" width="4" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="10" width="6" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="18" width="8" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="28" width="10" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="40" width="12" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="54" width="14" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="70" width="16" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="88" width="18" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="108" width="20" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="130" width="22" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="154" width="24" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="180" width="26" height="146" rx="2"/>
|
||||
<rect class="cls-1" x="208" width="28" height="146" rx="2"/>
|
||||
<rect class="cls-2" x="238" width="30" height="146" rx="2"/>
|
||||
<rect class="cls-2" x="270" width="32" height="146" rx="2"/>
|
||||
<rect class="cls-2" x="304" width="34" height="146" rx="2"/>
|
||||
<rect class="cls-3" x="340" width="38" height="146" rx="2"/>
|
||||
<path class="cls-4" d="M304,144h34a0,0,0,0,1,0,0v0a2,2,0,0,1-2,2H306a2,2,0,0,1-2-2v0A0,0,0,0,1,304,144Z"/>
|
||||
<path class="cls-4" d="M270,144h32a0,0,0,0,1,0,0v0a2,2,0,0,1-2,2H272a2,2,0,0,1-2-2v0A0,0,0,0,1,270,144Z"/>
|
||||
<path class="cls-4" d="M238,144h30a0,0,0,0,1,0,0v0a2,2,0,0,1-2,2H240a2,2,0,0,1-2-2v0A0,0,0,0,1,238,144Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -338,7 +338,7 @@
|
|||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.15.5", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3", "@babel/parser@^7.7.0":
|
||||
"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.15.5", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3", "@babel/parser@^7.7.0":
|
||||
version "7.17.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0"
|
||||
integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==
|
||||
|
@ -1139,7 +1139,7 @@
|
|||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.0.0-beta.49", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.15.4", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.15.4", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
|
||||
version "7.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b"
|
||||
integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==
|
||||
|
@ -3156,6 +3156,39 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc"
|
||||
integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==
|
||||
|
||||
"@types/babel__core@^7.1.12":
|
||||
version "7.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8"
|
||||
integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.1.0"
|
||||
"@babel/types" "^7.0.0"
|
||||
"@types/babel__generator" "*"
|
||||
"@types/babel__template" "*"
|
||||
"@types/babel__traverse" "*"
|
||||
|
||||
"@types/babel__generator@*":
|
||||
version "7.6.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7"
|
||||
integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@types/babel__template@*":
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969"
|
||||
integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.1.0"
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@types/babel__traverse@*":
|
||||
version "7.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43"
|
||||
integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/cacheable-request@^6.0.1":
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9"
|
||||
|
@ -4679,6 +4712,16 @@ babel-plugin-polyfill-regenerator@^0.3.0:
|
|||
dependencies:
|
||||
"@babel/helper-define-polyfill-provider" "^0.3.1"
|
||||
|
||||
babel-plugin-preval@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-preval/-/babel-plugin-preval-5.1.0.tgz#6efb89bf6b97af592cd1400c6df49c0e9e6ab027"
|
||||
integrity sha512-G5R+xmo5LS41A4UyZjOjV0mp9AvkuCyUOAJ6TOv/jTZS+VKh7L7HUDRcCSOb0YCM/u0fFarh7Diz0wjY8rFNFg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@types/babel__core" "^7.1.12"
|
||||
babel-plugin-macros "^3.0.1"
|
||||
require-from-string "^2.0.2"
|
||||
|
||||
babel-plugin-react-docgen@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz#7cc8e2f94e8dc057a06e953162f0810e4e72257b"
|
||||
|
@ -8159,6 +8202,11 @@ gatsby-plugin-postcss@^5.7.0:
|
|||
"@babel/runtime" "^7.15.4"
|
||||
postcss-loader "^4.3.0"
|
||||
|
||||
gatsby-plugin-preval@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gatsby-plugin-preval/-/gatsby-plugin-preval-1.0.0.tgz#b0e9dcc9ef568cb6ca998f7211b5365824b97201"
|
||||
integrity sha512-HpPp2bdA4nZsuD3R++GRhM9BPlFp8+ilkXIo53hNr14OlHXxrgimh9lqVVvP3q4JriHh+bYcvmfqm6msIsxxLg==
|
||||
|
||||
gatsby-plugin-provide-react@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/gatsby-plugin-provide-react/-/gatsby-plugin-provide-react-1.0.2.tgz#e50bb311cd8ef5855c6d94f708266ab117c77a15"
|
||||
|
@ -8196,10 +8244,10 @@ gatsby-plugin-sharp@^4.6.0:
|
|||
svgo "1.3.2"
|
||||
uuid "3.4.0"
|
||||
|
||||
gatsby-plugin-styled-components@^5.7.0:
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/gatsby-plugin-styled-components/-/gatsby-plugin-styled-components-5.7.0.tgz#8ba7b4ddb1722dcd0efd4fc6f1a8e62f47be012c"
|
||||
integrity sha512-mX8N4nqIX0Ow/pUSORUb8WlKvgX7foCoWZ0AifyBOFnhBCbRWYTsXFWwiea6jCnST5V61b2TOFpjIHcvHvc9aQ==
|
||||
gatsby-plugin-styled-components@^5.8.0:
|
||||
version "5.8.0"
|
||||
resolved "https://registry.yarnpkg.com/gatsby-plugin-styled-components/-/gatsby-plugin-styled-components-5.8.0.tgz#5d8c81802ed9266435aa1145451bfb3ac582ad37"
|
||||
integrity sha512-4ma9PgOr3U5TUX6uwAqFW+VX+fDxmt1y4oM3ArfZufaiQvZJ52cuf/uiyI+Tx1DJebcypEpR5dXYVl9ZX1bUHg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.15.4"
|
||||
|
||||
|
@ -12402,6 +12450,11 @@ pretty-bytes@^5.4.1:
|
|||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
||||
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
|
||||
|
||||
pretty-bytes@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-6.0.0.tgz#928be2ad1f51a2e336add8ba764739f9776a8140"
|
||||
integrity sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==
|
||||
|
||||
pretty-error@^2.1.1, pretty-error@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6"
|
||||
|
@ -14061,6 +14114,11 @@ store2@^2.12.0:
|
|||
resolved "https://registry.yarnpkg.com/store2/-/store2-2.13.1.tgz#fae7b5bb9d35fc53dc61cd262df3abb2f6e59022"
|
||||
integrity sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==
|
||||
|
||||
storybook-addon-gatsby@^0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/storybook-addon-gatsby/-/storybook-addon-gatsby-0.0.5.tgz#94f5b67bab8659d0248b65e60dabc3702818ce8b"
|
||||
integrity sha512-18f8Kc6mx8mEFfqY2DgF9ayDfmM58+9IjJqIxGV4bA4r2EtB/Q1LDNELIJmpLLyA5NrSvECxCqzLu7jNBlWgmA==
|
||||
|
||||
stream-browserify@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
|
||||
|
|
Reference in New Issue