Merge branch 'master' into clean-up-nginx-skylink-cache-related-code
This commit is contained in:
commit
72c141a0b6
|
@ -0,0 +1,50 @@
|
||||||
|
import { useCallback, useRef, useState } from "react";
|
||||||
|
import copy from "copy-text-to-clipboard";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { useClickAway } from "react-use";
|
||||||
|
|
||||||
|
import { CopyIcon } from "./Icons";
|
||||||
|
|
||||||
|
const Button = styled.button.attrs({
|
||||||
|
className: "relative inline-flex items-center hover:text-primary",
|
||||||
|
})``;
|
||||||
|
|
||||||
|
const TooltipContainer = styled.div.attrs(({ $visible }) => ({
|
||||||
|
className: `absolute left-full top-1/2 z-10
|
||||||
|
bg-white rounded border border-primary/30 shadow-md
|
||||||
|
pointer-events-none transition-opacity duration-150 ease-in-out
|
||||||
|
${$visible ? "opacity-100" : "opacity-0"}`,
|
||||||
|
}))`
|
||||||
|
transform: translateY(-50%);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TooltipContent = styled.div.attrs({
|
||||||
|
className: "bg-primary-light/10 text-palette-600 py-2 px-4 ",
|
||||||
|
})``;
|
||||||
|
|
||||||
|
export const CopyButton = ({ value }) => {
|
||||||
|
const containerRef = useRef();
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const [timer, setTimer] = useState(null);
|
||||||
|
|
||||||
|
const handleCopy = useCallback(() => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
copy(value);
|
||||||
|
setCopied(true);
|
||||||
|
|
||||||
|
setTimer(setTimeout(() => setCopied(false), 150000));
|
||||||
|
}, [value, timer]);
|
||||||
|
|
||||||
|
useClickAway(containerRef, () => setCopied(false));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} className="inline-flex relative overflow-visible pr-2">
|
||||||
|
<Button onClick={handleCopy} className={copied ? "text-primary" : ""}>
|
||||||
|
<CopyIcon size={16} />
|
||||||
|
</Button>
|
||||||
|
<TooltipContainer $visible={copied}>
|
||||||
|
<TooltipContent>Copied to clipboard</TooltipContent>
|
||||||
|
</TooltipContainer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,74 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { useMedia } from "react-use";
|
||||||
|
|
||||||
|
import theme from "../../lib/theme";
|
||||||
|
|
||||||
|
import { ContainerLoadingIndicator } from "../LoadingIndicator";
|
||||||
|
import { Select, SelectOption } from "../Select";
|
||||||
|
import { Switch } from "../Switch";
|
||||||
|
import { TextInputIcon } from "../TextInputIcon/TextInputIcon";
|
||||||
|
import { SearchIcon } from "../Icons";
|
||||||
|
|
||||||
|
import FileTable from "./FileTable";
|
||||||
|
import useFormattedFilesData from "./useFormattedFilesData";
|
||||||
|
|
||||||
|
const FileList = ({ type }) => {
|
||||||
|
const isMediumScreenOrLarger = useMedia(`(min-width: ${theme.screens.md})`);
|
||||||
|
const { data, error } = useSWR(`user/${type}?pageSize=10`);
|
||||||
|
const items = useFormattedFilesData(data?.items || []);
|
||||||
|
|
||||||
|
const setFilter = (name, value) => console.log("filter", name, "set to", value);
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
return (
|
||||||
|
<div className="flex w-full h-full justify-center items-center text-palette-400">
|
||||||
|
{/* TODO: proper error message */}
|
||||||
|
{!data && !error && <ContainerLoadingIndicator />}
|
||||||
|
{!data && error && <p>An error occurred while loading this data.</p>}
|
||||||
|
{data && <p>No {type} found.</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4 pt-4">
|
||||||
|
<div className="flex flex-col gap-4 lg:flex-row justify-between items-center">
|
||||||
|
<TextInputIcon
|
||||||
|
className="w-full lg:w-[280px] xl:w-[420px]"
|
||||||
|
placeholder="Search"
|
||||||
|
icon={<SearchIcon size={20} />}
|
||||||
|
onChange={console.log.bind(console)}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-row items-center uppercase font-light text-sm gap-4">
|
||||||
|
<Switch onChange={(value) => setFilter("showSmallFiles", value)} className="mr-8">
|
||||||
|
<span className="underline decoration-dashed underline-offset-2 decoration-2 decoration-gray-300">
|
||||||
|
Show small files
|
||||||
|
</span>
|
||||||
|
</Switch>
|
||||||
|
<div>
|
||||||
|
<span className="pr-2">File type:</span>
|
||||||
|
<Select onChange={(value) => setFilter("type", value)}>
|
||||||
|
<SelectOption value="all" label="All" />
|
||||||
|
<SelectOption value="mp4" label=".mp4" />
|
||||||
|
<SelectOption value="pdf" label=".pdf" />
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="pr-2">Sort:</span>
|
||||||
|
<Select onChange={(value) => setFilter("type", value)}>
|
||||||
|
<SelectOption value="size-desc" label="Biggest size" />
|
||||||
|
<SelectOption value="size-asc" label="Smallest size" />
|
||||||
|
<SelectOption value="date-desc" label="Latest" />
|
||||||
|
<SelectOption value="date-asc" label="Oldest" />
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* TODO: mobile view (it's not tabular) */}
|
||||||
|
{isMediumScreenOrLarger ? <FileTable items={items} /> : "Mobile view"}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileList;
|
|
@ -0,0 +1,111 @@
|
||||||
|
import { CogIcon, ShareIcon } from "../Icons";
|
||||||
|
import { PopoverMenu } from "../PopoverMenu/PopoverMenu";
|
||||||
|
import { Table, TableBody, TableCell, TableHead, TableHeadCell, TableRow } from "../Table";
|
||||||
|
import { CopyButton } from "../CopyButton";
|
||||||
|
|
||||||
|
const buildShareMenu = (item) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: "Facebook",
|
||||||
|
callback: () => {
|
||||||
|
console.info("share to Facebook", item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Twitter",
|
||||||
|
callback: () => {
|
||||||
|
console.info("share to Twitter", item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Discord",
|
||||||
|
callback: () => {
|
||||||
|
console.info("share to Discord", item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildOptionsMenu = (item) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: "Preview",
|
||||||
|
callback: () => {
|
||||||
|
console.info("preview", item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Download",
|
||||||
|
callback: () => {
|
||||||
|
console.info("download", item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Unpin",
|
||||||
|
callback: () => {
|
||||||
|
console.info("unpin", item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Report",
|
||||||
|
callback: () => {
|
||||||
|
console.info("report", item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function FileTable({ items }) {
|
||||||
|
return (
|
||||||
|
<Table style={{ tableLayout: "fixed" }}>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow noHoverEffect>
|
||||||
|
<TableHeadCell className="w-[240px] xl:w-[360px]">Name</TableHeadCell>
|
||||||
|
<TableHeadCell className="w-[80px]">Type</TableHeadCell>
|
||||||
|
<TableHeadCell className="w-[80px]" align="right">
|
||||||
|
Size
|
||||||
|
</TableHeadCell>
|
||||||
|
<TableHeadCell className="w-[180px]">Uploaded</TableHeadCell>
|
||||||
|
<TableHeadCell className="hidden lg:table-cell">Skylink</TableHeadCell>
|
||||||
|
<TableHeadCell className="w-[100px]">Activity</TableHeadCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{items.map((item) => {
|
||||||
|
const { id, name, type, size, date, skylink } = item;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={id}>
|
||||||
|
<TableCell className="w-[240px] xl:w-[360px]">{name}</TableCell>
|
||||||
|
<TableCell className="w-[80px]">{type}</TableCell>
|
||||||
|
<TableCell className="w-[80px]" align="right">
|
||||||
|
{size}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="w-[180px]">{date}</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell pr-6 !overflow-visible">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<CopyButton value={skylink} className="mr-2" />
|
||||||
|
<span className="w-full inline-block truncate">{skylink}</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="w-[100px] !overflow-visible">
|
||||||
|
<div className="flex text-palette-600 gap-4">
|
||||||
|
<PopoverMenu options={buildShareMenu(item)} openClassName="text-primary">
|
||||||
|
<button>
|
||||||
|
<ShareIcon size={22} />
|
||||||
|
</button>
|
||||||
|
</PopoverMenu>
|
||||||
|
<PopoverMenu options={buildOptionsMenu(item)} openClassName="text-primary">
|
||||||
|
<button>
|
||||||
|
<CogIcon />
|
||||||
|
</button>
|
||||||
|
</PopoverMenu>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./FileList";
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import prettyBytes from "pretty-bytes";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const parseFileName = (fileName) => {
|
||||||
|
const lastDotIndex = Math.max(0, fileName.lastIndexOf(".")) || Infinity;
|
||||||
|
|
||||||
|
return [fileName.substr(0, lastDotIndex), fileName.substr(lastDotIndex)];
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatItem = ({ size, name: rawFileName, uploadedOn, downloadedOn, ...rest }) => {
|
||||||
|
const [name, type] = parseFileName(rawFileName);
|
||||||
|
const date = dayjs(uploadedOn || downloadedOn).format("MM/DD/YYYY; HH:MM");
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
date,
|
||||||
|
size: prettyBytes(size),
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useFormattedFilesData = (items) => useMemo(() => items.map(formatItem), [items]);
|
||||||
|
|
||||||
|
export default useFormattedFilesData;
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { withIconProps } from "../withIconProps";
|
||||||
|
|
||||||
|
export const CopyIcon = withIconProps(({ size, ...props }) => (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M26.35,11.29A5.65,5.65,0,0,1,32,16.94v9.41A5.65,5.65,0,0,1,26.35,32H16.94a5.65,5.65,0,0,1-5.65-5.65V16.94a5.65,5.65,0,0,1,5.65-5.65Zm0,3.77H16.94a1.88,1.88,0,0,0-1.88,1.88v9.41a1.89,1.89,0,0,0,1.88,1.89h9.41a1.89,1.89,0,0,0,1.89-1.89V16.94A1.89,1.89,0,0,0,26.35,15.06ZM16.22,0A4.49,4.49,0,0,1,20.7,4.18V5.79A1.89,1.89,0,0,1,17,6V4.49a.73.73,0,0,0-.58-.71l-.14,0H4.49a.74.74,0,0,0-.71.58l0,.15V16.22a.73.73,0,0,0,.58.71H5.79A1.88,1.88,0,0,1,6,20.69l-.22,0H4.49A4.49,4.49,0,0,1,0,16.53v-12A4.48,4.48,0,0,1,4.18,0h12Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
));
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { withIconProps } from "../withIconProps";
|
||||||
|
|
||||||
|
export const SearchIcon = withIconProps(({ size, ...props }) => (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M9,0a9,9,0,0,1,7,14.62l3.68,3.67a1,1,0,0,1-1.32,1.5l-.1-.08L14.62,16A9,9,0,1,1,9,0ZM9,2a7,7,0,1,0,4.87,12l.07-.09.09-.07A7,7,0,0,0,9,2Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
));
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { withIconProps } from "../withIconProps";
|
||||||
|
|
||||||
|
export const ShareIcon = withIconProps(({ size, ...props }) => (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 29.09 32"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
shapeRendering="geometricPrecision"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M24.73,0a5.82,5.82,0,1,1-4.14,9.91l-7.72,4.51a5.85,5.85,0,0,1,0,3.16l7.73,4.5a5.81,5.81,0,1,1-1.47,2.51l-7.72-4.5a5.82,5.82,0,1,1,0-8.22l0,0L19.13,7.4a5.82,5.82,0,0,1,4-7.18A5.69,5.69,0,0,1,24.73,0Zm0,23.27a2.93,2.93,0,0,0-2.43,1.3,1,1,0,0,1-.07.15l-.09.14-.05.09A2.91,2.91,0,1,0,26,23.54,2.86,2.86,0,0,0,24.73,23.27ZM7.27,13.09a2.91,2.91,0,1,0,2.51,4.37l0-.05A2.93,2.93,0,0,0,10.18,16a2.89,2.89,0,0,0-.4-1.46v0A2.9,2.9,0,0,0,7.27,13.09ZM24.73,2.91a2.92,2.92,0,0,0-2.55,4.32l0,0v0a2.91,2.91,0,1,0,2.5-4.4Z"
|
||||||
|
transform="translate(-1.46 0)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
));
|
|
@ -9,3 +9,6 @@ export * from "./icons/CircledErrorIcon";
|
||||||
export * from "./icons/CircledProgressIcon";
|
export * from "./icons/CircledProgressIcon";
|
||||||
export * from "./icons/CircledArrowUpIcon";
|
export * from "./icons/CircledArrowUpIcon";
|
||||||
export * from "./icons/PlusIcon";
|
export * from "./icons/PlusIcon";
|
||||||
|
export * from "./icons/SearchIcon";
|
||||||
|
export * from "./icons/CopyIcon";
|
||||||
|
export * from "./icons/ShareIcon";
|
||||||
|
|
|
@ -4,7 +4,7 @@ const propTypes = {
|
||||||
/**
|
/**
|
||||||
* Size of the icon's bounding box.
|
* Size of the icon's bounding box.
|
||||||
*/
|
*/
|
||||||
size: PropTypes.number,
|
size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { Children, cloneElement, useRef, useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { useClickAway } from "react-use";
|
||||||
|
import styled, { css, keyframes } from "styled-components";
|
||||||
|
|
||||||
|
const dropDown = keyframes`
|
||||||
|
0% {
|
||||||
|
transform: scaleY(0);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: scaleY(1.1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Container = styled.div.attrs({ className: "relative inline-flex" })``;
|
||||||
|
|
||||||
|
const Flyout = styled.ul.attrs({
|
||||||
|
className: `absolute right-0 z-10 py-2
|
||||||
|
border rounded bg-white
|
||||||
|
overflow-hidden pointer-events-none
|
||||||
|
shadow-md shadow-palette-200/50
|
||||||
|
pointer-events-auto h-auto overflow-visible border-primary`,
|
||||||
|
})`
|
||||||
|
top: calc(100% + 2px);
|
||||||
|
animation: ${css`
|
||||||
|
${dropDown} 0.1s ease-in-out
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Option = styled.li.attrs({
|
||||||
|
className: `font-sans text-xs uppercase
|
||||||
|
relative pl-3 pr-5 py-1
|
||||||
|
text-palette-600 cursor-pointer
|
||||||
|
hover:text-primary hover:font-normal
|
||||||
|
active:text-primary active:font-normal
|
||||||
|
|
||||||
|
before:content-[initial] before:absolute before:left-0 before:h-3 before:w-0.5 before:bg-primary before:top-1.5
|
||||||
|
hover:before:content-['']`,
|
||||||
|
})``;
|
||||||
|
|
||||||
|
export const PopoverMenu = ({ options, children, openClassName, ...props }) => {
|
||||||
|
const containerRef = useRef();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
useClickAway(containerRef, () => setOpen(false));
|
||||||
|
|
||||||
|
const handleChoice = (callback) => () => {
|
||||||
|
setOpen(false);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container ref={containerRef} {...props}>
|
||||||
|
{Children.only(
|
||||||
|
cloneElement(children, {
|
||||||
|
onClick: () => setOpen((open) => !open),
|
||||||
|
className: `${children.props.className ?? ""} ${open ? openClassName : ""}`,
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
{open && (
|
||||||
|
<Flyout>
|
||||||
|
{options.map(({ label, callback }) => (
|
||||||
|
<Option key={label} onClick={handleChoice(callback)}>
|
||||||
|
{label}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Flyout>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PopoverMenu.propTypes = {
|
||||||
|
/**
|
||||||
|
* Accepts a single child node that will become a menu toggle.
|
||||||
|
*/
|
||||||
|
children: PropTypes.element.isRequired,
|
||||||
|
/**
|
||||||
|
* Positions in the menu
|
||||||
|
*/
|
||||||
|
options: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
callback: PropTypes.func.isRequired,
|
||||||
|
})
|
||||||
|
).isRequired,
|
||||||
|
};
|
|
@ -21,9 +21,11 @@ const dropDown = keyframes`
|
||||||
|
|
||||||
const Container = styled.div.attrs({ className: "relative inline-flex" })``;
|
const Container = styled.div.attrs({ className: "relative inline-flex" })``;
|
||||||
|
|
||||||
const Trigger = styled.button.attrs(({ placeholder }) => ({
|
const Trigger = styled.button.attrs(({ $placeholder }) => ({
|
||||||
className: `flex items-center cursor-pointer ${placeholder ? "text-palette-300" : ""}`,
|
className: `flex items-center cursor-pointer font-bold ${$placeholder ? "text-palette-300" : ""}`,
|
||||||
}))``;
|
}))`
|
||||||
|
text-transform: inherit;
|
||||||
|
`;
|
||||||
|
|
||||||
const TriggerIcon = styled(ChevronDownIcon).attrs({
|
const TriggerIcon = styled(ChevronDownIcon).attrs({
|
||||||
className: "transition-transform text-primary",
|
className: "transition-transform text-primary",
|
||||||
|
@ -32,13 +34,14 @@ const TriggerIcon = styled(ChevronDownIcon).attrs({
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Flyout = styled.ul.attrs(({ open }) => ({
|
const Flyout = styled.ul.attrs(({ open }) => ({
|
||||||
className: `absolute top-[20px] right-0
|
className: `absolute right-0 z-10
|
||||||
p-0 h-0 border rounded bg-white
|
p-0 border rounded bg-white
|
||||||
overflow-hidden pointer-events-none
|
overflow-hidden pointer-events-none
|
||||||
shadow-md shadow-palette-200/50
|
shadow-md shadow-palette-200/50
|
||||||
${open ? "pointer-events-auto h-auto overflow-visible border-primary" : ""}
|
${open ? "pointer-events-auto h-auto overflow-visible border-primary" : ""}
|
||||||
${open ? "visible" : "invisible"}`,
|
${open ? "visible" : "invisible"}`,
|
||||||
}))`
|
}))`
|
||||||
|
top: calc(100% + 2px);
|
||||||
animation: ${({ open }) =>
|
animation: ${({ open }) =>
|
||||||
open
|
open
|
||||||
? css`
|
? css`
|
||||||
|
@ -47,7 +50,7 @@ const Flyout = styled.ul.attrs(({ open }) => ({
|
||||||
: "none"};
|
: "none"};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Select = ({ defaultValue, children, onChange, placeholder }) => {
|
export const Select = ({ defaultValue, children, onChange, placeholder, ...props }) => {
|
||||||
const selectRef = useRef();
|
const selectRef = useRef();
|
||||||
const options = useMemo(() => Children.toArray(children).filter(({ type }) => type === SelectOption), [children]);
|
const options = useMemo(() => Children.toArray(children).filter(({ type }) => type === SelectOption), [children]);
|
||||||
const [state, dispatch] = useSelectReducer({ defaultValue, placeholder, options });
|
const [state, dispatch] = useSelectReducer({ defaultValue, placeholder, options });
|
||||||
|
@ -65,8 +68,8 @@ export const Select = ({ defaultValue, children, onChange, placeholder }) => {
|
||||||
const activeLabel = activeOption?.props?.label ?? null;
|
const activeLabel = activeOption?.props?.label ?? null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container ref={selectRef}>
|
<Container ref={selectRef} {...props}>
|
||||||
<Trigger placeholder={!activeLabel && placeholder} onClick={toggle}>
|
<Trigger $placeholder={!activeLabel && placeholder} onClick={toggle} className={state.open ? "text-primary" : ""}>
|
||||||
{activeLabel ?? placeholder} <TriggerIcon open={state.open} />
|
{activeLabel ?? placeholder} <TriggerIcon open={state.open} />
|
||||||
</Trigger>
|
</Trigger>
|
||||||
<Flyout role="listbox" open={state.open}>
|
<Flyout role="listbox" open={state.open}>
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
.react-switch-checkbox {
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-switch-label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 44px;
|
|
||||||
height: 22px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 11px;
|
|
||||||
@apply border-palette-200;
|
|
||||||
border-width: 1px;
|
|
||||||
position: relative;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-switch-label .react-switch-button {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 2px;
|
|
||||||
left: 2px;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-switch-checkbox:checked + .react-switch-label .react-switch-button {
|
|
||||||
left: calc(100% - 2px);
|
|
||||||
transform: translateX(-100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-switch-label:active .react-switch-button {
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
|
@ -1,37 +1,86 @@
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import "./Switch.css";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
|
||||||
|
const Container = styled.div.attrs({
|
||||||
|
className: "inline-flex items-center gap-1 cursor-pointer select-none",
|
||||||
|
})``;
|
||||||
|
|
||||||
|
const Checkbox = styled.input.attrs({
|
||||||
|
type: "checkbox",
|
||||||
|
className: `h-0 w-0 hidden`,
|
||||||
|
})``;
|
||||||
|
|
||||||
|
const Label = styled.label.attrs({
|
||||||
|
className: "cursor-pointer inline-flex items-center gap-2",
|
||||||
|
})`
|
||||||
|
&:active .toggle-pin {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Toggle = styled.span.attrs({
|
||||||
|
className: `flex flex-row items-center justify-between
|
||||||
|
w-[44px] h-[22px] bg-white rounded-full
|
||||||
|
border border-palette-200 relative cursor-pointer`,
|
||||||
|
})`
|
||||||
|
&:active .toggle-pin {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TogglePin = styled.span.attrs(({ $checked }) => ({
|
||||||
|
className: `toggle-pin
|
||||||
|
absolute top-[2px] w-4 h-4 rounded-full
|
||||||
|
transition-[width_left] active:w-5
|
||||||
|
|
||||||
|
${$checked ? "checked bg-primary" : "bg-palette-200"}`,
|
||||||
|
}))`
|
||||||
|
left: 2px;
|
||||||
|
|
||||||
|
&.checked {
|
||||||
|
left: calc(100% - 2px);
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Switch = ({ children, defaultChecked, onChange, ...props }) => {
|
||||||
|
const id = useMemo(nanoid, [onChange]);
|
||||||
|
const [checked, setChecked] = useState(defaultChecked);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onChange(checked);
|
||||||
|
}, [checked, onChange]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Primary UI component for user interaction
|
|
||||||
*/
|
|
||||||
export const Switch = ({ isOn, handleToggle }) => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container {...props}>
|
||||||
<input
|
<Checkbox checked={checked} onChange={(ev) => setChecked(ev.target.checked)} id={id} />
|
||||||
checked={isOn}
|
<Label htmlFor={id}>
|
||||||
onChange={handleToggle}
|
<Toggle>
|
||||||
className="react-switch-checkbox"
|
<TogglePin $checked={checked} />
|
||||||
id={`react-switch-new`}
|
</Toggle>
|
||||||
type="checkbox"
|
{children}
|
||||||
/>
|
</Label>
|
||||||
<label className={"react-switch-label"} htmlFor={`react-switch-new`}>
|
</Container>
|
||||||
<span className={`react-switch-button ${isOn ? "bg-primary" : "bg-palette-200"}`} />
|
|
||||||
</label>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Switch.propTypes = {
|
Switch.propTypes = {
|
||||||
/**
|
/**
|
||||||
* Switch's current value
|
* Should the checkbox be checked by default?
|
||||||
*/
|
*/
|
||||||
isOn: PropTypes.bool,
|
defaultChecked: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Element to be rendered as the switch label
|
||||||
|
*/
|
||||||
|
children: PropTypes.element,
|
||||||
/**
|
/**
|
||||||
* Function to execute on change
|
* Function to execute on change
|
||||||
*/
|
*/
|
||||||
handleToggle: PropTypes.func,
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
Switch.defaultProps = {
|
Switch.defaultProps = {
|
||||||
isOn: false,
|
defaultChecked: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,10 +13,10 @@ const Template = (args) => <Switch {...args} />;
|
||||||
export const SwitchTrue = Template.bind({});
|
export const SwitchTrue = Template.bind({});
|
||||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||||
SwitchTrue.args = {
|
SwitchTrue.args = {
|
||||||
isOn: true,
|
defaultChecked: true,
|
||||||
};
|
};
|
||||||
export const SwitchFalse = Template.bind({});
|
export const SwitchFalse = Template.bind({});
|
||||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||||
SwitchFalse.args = {
|
SwitchFalse.args = {
|
||||||
isOn: false,
|
defaultChecked: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
const Container = styled.div.attrs({
|
const Container = styled.div.attrs({
|
||||||
className: "p-1 max-w-full overflow-x-auto",
|
className: "p-1 max-w-full",
|
||||||
})``;
|
})``;
|
||||||
|
|
||||||
const StyledTable = styled.table.attrs({
|
const StyledTable = styled.table.attrs({
|
||||||
|
|
|
@ -4,7 +4,8 @@ import styled from "styled-components";
|
||||||
* Accepts all HMTL attributes a `<th>` element does.
|
* Accepts all HMTL attributes a `<th>` element does.
|
||||||
*/
|
*/
|
||||||
export const TableHeadCell = styled.th.attrs({
|
export const TableHeadCell = styled.th.attrs({
|
||||||
className: `px-6 py-2.5 truncate h-tableRow
|
className: `first:pl-6 last:pr-6 px-2 py-4
|
||||||
|
truncate h-tableRow
|
||||||
text-palette-600 font-sans font-light text-xs
|
text-palette-600 font-sans font-light text-xs
|
||||||
first:rounded-l-sm last:rounded-r-sm`,
|
first:rounded-l-sm last:rounded-r-sm`,
|
||||||
})`
|
})`
|
||||||
|
|
|
@ -34,7 +34,11 @@ const Body = styled.div.attrs({ className: "grow min-h-0" })``;
|
||||||
export const Tabs = ({ defaultTab, children, variant }) => {
|
export const Tabs = ({ defaultTab, children, variant }) => {
|
||||||
const getTabId = usePrefixedTabIds();
|
const getTabId = usePrefixedTabIds();
|
||||||
const { tabs, panels, tabsRefs } = useTabsChildren(children, getTabId);
|
const { tabs, panels, tabsRefs } = useTabsChildren(children, getTabId);
|
||||||
const defaultTabId = useMemo(() => getTabId(defaultTab || tabs[0].props.id), [getTabId, defaultTab, tabs]);
|
const defaultTabId = useMemo(() => {
|
||||||
|
const requestedTabIsPresent = tabs.find(({ props }) => props.id === defaultTab);
|
||||||
|
|
||||||
|
return getTabId(requestedTabIsPresent ? defaultTab : tabs[0].props.id);
|
||||||
|
}, [getTabId, defaultTab, tabs]);
|
||||||
const [activeTabId, setActiveTabId] = useState(defaultTabId);
|
const [activeTabId, setActiveTabId] = useState(defaultTabId);
|
||||||
const [activeTabRef, setActiveTabRef] = useState(tabsRefs[activeTabId]);
|
const [activeTabRef, setActiveTabRef] = useState(tabsRefs[activeTabId]);
|
||||||
const isActive = (id) => id === activeTabId;
|
const isActive = (id) => id === activeTabId;
|
||||||
|
|
|
@ -1,20 +1,45 @@
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import cn from "classnames";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { PlusIcon } from "../Icons";
|
||||||
|
|
||||||
|
export const TextInputIcon = ({ className, icon, placeholder, onChange }) => {
|
||||||
|
const inputRef = useRef();
|
||||||
|
const [focused, setFocused] = useState(false);
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onChange(value);
|
||||||
|
}, [value, onChange]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Primary UI component for user interaction
|
|
||||||
*/
|
|
||||||
export const TextInputIcon = ({ icon, position, placeholder }) => {
|
|
||||||
return (
|
return (
|
||||||
<div className={"flex flex-row items-center px-textInputIcon h-textInput rounded-full bg-palette-100"}>
|
<div
|
||||||
{position === "left" ? <div className={"w-buttonIconLg h-buttonIconLg"}>{icon}</div> : null}
|
className={cn(
|
||||||
<input
|
"grid-flow-col inline-grid grid-cols-[2rem_1fr_1.5rem] items-center rounded-full bg-palette-100 px-4 py-2",
|
||||||
placeholder={placeholder}
|
className,
|
||||||
className={
|
{
|
||||||
"w-full focus:outline-none mx-textInputHorizontal rounded-full bg-transparent " +
|
"outline outline-1 outline-primary": focused,
|
||||||
"placeholder-palette-400 text-content tracking-inputPlaceholder text-textInput"
|
|
||||||
}
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>{icon}</div>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
value={value}
|
||||||
|
onFocus={() => setFocused(true)}
|
||||||
|
onBlur={() => setFocused(false)}
|
||||||
|
onChange={(event) => setValue(event.target.value)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className="focus:outline-none bg-transparent placeholder:text-palette-400"
|
||||||
/>
|
/>
|
||||||
{position === "right" ? <div className={"w-buttonIconLg h-buttonIconLg"}>{icon}</div> : null}
|
{value && (
|
||||||
|
<PlusIcon
|
||||||
|
size={14}
|
||||||
|
role="button"
|
||||||
|
className="justify-self-end text-palette-400 rotate-45"
|
||||||
|
onClick={() => setValue("")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -23,13 +48,13 @@ TextInputIcon.propTypes = {
|
||||||
/**
|
/**
|
||||||
* Icon to place in text input
|
* Icon to place in text input
|
||||||
*/
|
*/
|
||||||
icon: PropTypes.element,
|
icon: PropTypes.element.isRequired,
|
||||||
/**
|
|
||||||
* Side to place icon
|
|
||||||
*/
|
|
||||||
position: PropTypes.oneOf(["left", "right"]),
|
|
||||||
/**
|
/**
|
||||||
* Input placeholder
|
* Input placeholder
|
||||||
*/
|
*/
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Function to be called whenever value changes
|
||||||
|
*/
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { TextInputIcon } from "./TextInputIcon";
|
import { TextInputIcon } from "./TextInputIcon";
|
||||||
import { CogIcon } from "../Icons";
|
import { SearchIcon } from "../Icons";
|
||||||
|
import { Panel } from "../Panel";
|
||||||
|
|
||||||
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
||||||
export default {
|
export default {
|
||||||
|
@ -9,19 +10,21 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
|
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
|
||||||
const Template = (args) => <TextInputIcon {...args} />;
|
const Template = (args) => (
|
||||||
|
<Panel>
|
||||||
|
<TextInputIcon {...args} />
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
|
||||||
export const IconLeft = Template.bind({});
|
export const IconLeft = Template.bind({});
|
||||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||||
IconLeft.args = {
|
IconLeft.args = {
|
||||||
icon: <CogIcon />,
|
icon: <SearchIcon size={20} />,
|
||||||
position: "left",
|
|
||||||
placeholder: "Search",
|
placeholder: "Search",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IconRight = Template.bind({});
|
export const IconRight = Template.bind({});
|
||||||
IconRight.args = {
|
IconRight.args = {
|
||||||
icon: <CogIcon />,
|
icon: <SearchIcon size={20} />,
|
||||||
position: "right",
|
|
||||||
placeholder: "Search",
|
placeholder: "Search",
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,28 @@ import * as React from "react";
|
||||||
|
|
||||||
import DashboardLayout from "../layouts/DashboardLayout";
|
import DashboardLayout from "../layouts/DashboardLayout";
|
||||||
|
|
||||||
|
import { Panel } from "../components/Panel";
|
||||||
|
import { Tab, TabPanel, Tabs } from "../components/Tabs";
|
||||||
|
import FileList from "../components/FileList/FileList";
|
||||||
|
import { useSearchParam } from "react-use";
|
||||||
|
|
||||||
const FilesPage = () => {
|
const FilesPage = () => {
|
||||||
return <>FILES</>;
|
const defaultTab = useSearchParam("tab");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Panel title="Files">
|
||||||
|
<Tabs defaultTab={defaultTab || "uploads"}>
|
||||||
|
<Tab id="uploads" title="Uploads" />
|
||||||
|
<Tab id="downloads" title="Downloads" />
|
||||||
|
<TabPanel tabId="uploads" className="pt-4">
|
||||||
|
<FileList type="uploads" />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel tabId="downloads" className="pt-4">
|
||||||
|
<FileList type="downloads" />
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
FilesPage.Layout = DashboardLayout;
|
FilesPage.Layout = DashboardLayout;
|
||||||
|
|
|
@ -28,6 +28,7 @@ module.exports = {
|
||||||
borderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
borderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
textColor: (theme) => ({ ...theme("colors"), ...colors }),
|
textColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
placeholderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
placeholderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
|
outlineColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ["Sora", ...defaultTheme.fontFamily.sans],
|
sans: ["Sora", ...defaultTheme.fontFamily.sans],
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/sora": "4.5.3",
|
"@fontsource/sora": "4.5.3",
|
||||||
"@fontsource/source-sans-pro": "4.5.3",
|
"@fontsource/source-sans-pro": "4.5.4",
|
||||||
"@stripe/react-stripe-js": "1.7.0",
|
"@stripe/react-stripe-js": "1.7.0",
|
||||||
"@stripe/stripe-js": "1.24.0",
|
"@stripe/stripe-js": "1.24.0",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-toastify": "8.2.0",
|
"react-toastify": "8.2.0",
|
||||||
"skynet-js": "3.0.2",
|
"skynet-js": "3.0.2",
|
||||||
"stripe": "8.207.0",
|
"stripe": "8.209.0",
|
||||||
"swr": "1.2.2",
|
"swr": "1.2.2",
|
||||||
"yup": "0.32.11"
|
"yup": "0.32.11"
|
||||||
},
|
},
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
"@tailwindcss/forms": "0.5.0",
|
"@tailwindcss/forms": "0.5.0",
|
||||||
"@tailwindcss/typography": "0.5.2",
|
"@tailwindcss/typography": "0.5.2",
|
||||||
"autoprefixer": "10.4.2",
|
"autoprefixer": "10.4.2",
|
||||||
"eslint": "8.10.0",
|
"eslint": "8.11.0",
|
||||||
"eslint-config-next": "12.1.0",
|
"eslint-config-next": "12.1.0",
|
||||||
"postcss": "8.4.8",
|
"postcss": "8.4.8",
|
||||||
"prettier": "2.5.1",
|
"prettier": "2.5.1",
|
||||||
|
|
|
@ -38,16 +38,16 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.2.0":
|
"@eslint/eslintrc@^1.2.1":
|
||||||
version "1.2.0"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.0.tgz#7ce1547a5c46dfe56e1e45c3c9ed18038c721c6a"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6"
|
||||||
integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==
|
integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.12.4"
|
ajv "^6.12.4"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
espree "^9.3.1"
|
espree "^9.3.1"
|
||||||
globals "^13.9.0"
|
globals "^13.9.0"
|
||||||
ignore "^4.0.6"
|
ignore "^5.2.0"
|
||||||
import-fresh "^3.2.1"
|
import-fresh "^3.2.1"
|
||||||
js-yaml "^4.1.0"
|
js-yaml "^4.1.0"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
@ -58,10 +58,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@fontsource/sora/-/sora-4.5.3.tgz#987c9b43acb00c9e3fa5377ebcedfd4ec9b760a7"
|
resolved "https://registry.yarnpkg.com/@fontsource/sora/-/sora-4.5.3.tgz#987c9b43acb00c9e3fa5377ebcedfd4ec9b760a7"
|
||||||
integrity sha512-0ipYkroLonvChAyLajgIt6mImXMhvjrHwD5g7iX2ZR1eJ4hLDnwq6haW5pSeehe79lPjgp0BX6ZHivFIP0xR2g==
|
integrity sha512-0ipYkroLonvChAyLajgIt6mImXMhvjrHwD5g7iX2ZR1eJ4hLDnwq6haW5pSeehe79lPjgp0BX6ZHivFIP0xR2g==
|
||||||
|
|
||||||
"@fontsource/source-sans-pro@4.5.3":
|
"@fontsource/source-sans-pro@4.5.4":
|
||||||
version "4.5.3"
|
version "4.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/@fontsource/source-sans-pro/-/source-sans-pro-4.5.3.tgz#bdb1eeed5db70bcd1f68cd1e8c859834f0e6bc67"
|
resolved "https://registry.yarnpkg.com/@fontsource/source-sans-pro/-/source-sans-pro-4.5.4.tgz#51510723ff40f446c7800f133e9ae604ae2f38d7"
|
||||||
integrity sha512-9xWGu3ArKsjf6+WVrNoCUywybTB3rIidpvOI2tByQpzYVOupFUv6qohyrjDrVvPb6XHJQTD0NIzisR7RKhiP7A==
|
integrity sha512-+YYw6HRvH9wYE+U2Hvxyossg+MHPApAj7VIjEqaXenNeNQa4U3uPD0e7pc+1Gic3srCQATN15O3S9WSFLXTmwQ==
|
||||||
|
|
||||||
"@humanwhocodes/config-array@^0.9.2":
|
"@humanwhocodes/config-array@^0.9.2":
|
||||||
version "0.9.2"
|
version "0.9.2"
|
||||||
|
@ -911,12 +911,12 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
||||||
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
||||||
|
|
||||||
eslint@8.10.0:
|
eslint@8.11.0:
|
||||||
version "8.10.0"
|
version "8.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37"
|
||||||
integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
|
integrity sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/eslintrc" "^1.2.0"
|
"@eslint/eslintrc" "^1.2.1"
|
||||||
"@humanwhocodes/config-array" "^0.9.2"
|
"@humanwhocodes/config-array" "^0.9.2"
|
||||||
ajv "^6.10.0"
|
ajv "^6.10.0"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
|
@ -1247,11 +1247,6 @@ ieee754@^1.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
|
|
||||||
ignore@^4.0.6:
|
|
||||||
version "4.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
|
||||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
|
||||||
|
|
||||||
ignore@^5.1.4, ignore@^5.2.0:
|
ignore@^5.1.4, ignore@^5.2.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
|
||||||
|
@ -2255,10 +2250,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||||
|
|
||||||
stripe@8.207.0:
|
stripe@8.209.0:
|
||||||
version "8.207.0"
|
version "8.209.0"
|
||||||
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.207.0.tgz#4b7002f19cecefbc3c48f09f6658c39e359f99c1"
|
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.209.0.tgz#a8f34132fb4140bdf9152943b15c641ad99cd3b1"
|
||||||
integrity sha512-ZCjdqN2adGfrC5uAAo0v7IquzaiQ3+pDzB324/iV3Q3Deiot9VO7KMVSNVx/0i6E6ywhgV33ko3FMT7iUgxKYA==
|
integrity sha512-ozfs8t0fxA/uvCK1DNvitSdEublOHK5CTRsrd2AWWk9LogjXcfkxmtz3KGSSQd+jyA2+rbee9TMzhJ6aabQ5WQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" ">=8.1.0"
|
"@types/node" ">=8.1.0"
|
||||||
qs "^6.6.0"
|
qs "^6.6.0"
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
"gatsby": "4.9.2",
|
"gatsby": "4.9.2",
|
||||||
"gatsby-background-image": "1.6.0",
|
"gatsby-background-image": "1.6.0",
|
||||||
"gatsby-plugin-image": "2.9.0",
|
"gatsby-plugin-image": "2.9.0",
|
||||||
"gatsby-plugin-manifest": "4.9.0",
|
"gatsby-plugin-manifest": "4.9.1",
|
||||||
"gatsby-plugin-postcss": "5.9.0",
|
"gatsby-plugin-postcss": "5.9.0",
|
||||||
"gatsby-plugin-react-helmet": "5.9.0",
|
"gatsby-plugin-react-helmet": "5.9.0",
|
||||||
"gatsby-plugin-robots-txt": "1.7.0",
|
"gatsby-plugin-robots-txt": "1.7.0",
|
||||||
"gatsby-plugin-sharp": "4.9.0",
|
"gatsby-plugin-sharp": "4.9.0",
|
||||||
"gatsby-plugin-sitemap": "5.9.0",
|
"gatsby-plugin-sitemap": "5.9.0",
|
||||||
"gatsby-plugin-svgr": "3.0.0-beta.0",
|
"gatsby-plugin-svgr": "3.0.0-beta.0",
|
||||||
"gatsby-source-filesystem": "4.9.0",
|
"gatsby-source-filesystem": "4.9.1",
|
||||||
"gatsby-transformer-sharp": "4.9.0",
|
"gatsby-transformer-sharp": "4.9.0",
|
||||||
"gatsby-transformer-yaml": "4.9.0",
|
"gatsby-transformer-yaml": "4.9.0",
|
||||||
"gbimage-bridge": "0.2.1",
|
"gbimage-bridge": "0.2.1",
|
||||||
|
|
|
@ -6197,10 +6197,10 @@ gatsby-cli@^4.9.0:
|
||||||
yoga-layout-prebuilt "^1.10.0"
|
yoga-layout-prebuilt "^1.10.0"
|
||||||
yurnalist "^2.1.0"
|
yurnalist "^2.1.0"
|
||||||
|
|
||||||
gatsby-core-utils@^3.8.2, gatsby-core-utils@^3.9.0:
|
gatsby-core-utils@^3.8.2, gatsby-core-utils@^3.9.0, gatsby-core-utils@^3.9.1:
|
||||||
version "3.9.0"
|
version "3.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-3.9.0.tgz#7be5969622e44c4475cb14f1ac64b49a4072ab6c"
|
resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-3.9.1.tgz#a4c1bb2021a7e7c06b4aad8d71c9c76ca9cdc21f"
|
||||||
integrity sha512-SvPnr86oXTY3ldbQ4QAkEew3BQE9vlzUXcXVJqTOhMUeGEz2kibBFUmVp8ia9Y1eOD+K/0xXQ54jUqaResj69w==
|
integrity sha512-DNf7NhhH0WrFuoBvyURjsw4w+eKbp1GlRA0cchYHJwVTaDPvLvX1o7zxN76xIBx+m0kttpnO3KuJ9LDOSli3ag==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.15.4"
|
"@babel/runtime" "^7.15.4"
|
||||||
ci-info "2.0.0"
|
ci-info "2.0.0"
|
||||||
|
@ -6299,13 +6299,13 @@ gatsby-plugin-image@2.9.0:
|
||||||
objectFitPolyfill "^2.3.5"
|
objectFitPolyfill "^2.3.5"
|
||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
gatsby-plugin-manifest@4.9.0:
|
gatsby-plugin-manifest@4.9.1:
|
||||||
version "4.9.0"
|
version "4.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.9.0.tgz#ddfd6e8d8597df7bdef07d527b08430508837ecb"
|
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.9.1.tgz#20513c7d942b424795b802506893c0f909c69b7a"
|
||||||
integrity sha512-aRoY9pan+7rR1SGoGcm1039A9tWy0w2oS+s7GmNJqqBHJqN2JZUlvJrqjU4ZdyoZ1/DXx9zuzUQDzgiEBFJ2xw==
|
integrity sha512-Fye2vr7ioc7ETVKdCfpbc5ByU28+EB7ocqSORbazPgAT8OiPazpaBAYm98BONceuK3WaxGoEXMsmwmNBIIPjRA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.15.4"
|
"@babel/runtime" "^7.15.4"
|
||||||
gatsby-core-utils "^3.9.0"
|
gatsby-core-utils "^3.9.1"
|
||||||
gatsby-plugin-utils "^3.3.0"
|
gatsby-plugin-utils "^3.3.0"
|
||||||
semver "^7.3.5"
|
semver "^7.3.5"
|
||||||
sharp "^0.30.1"
|
sharp "^0.30.1"
|
||||||
|
@ -6427,16 +6427,16 @@ gatsby-sharp@^0.3.0:
|
||||||
"@types/sharp" "^0.29.5"
|
"@types/sharp" "^0.29.5"
|
||||||
sharp "^0.30.1"
|
sharp "^0.30.1"
|
||||||
|
|
||||||
gatsby-source-filesystem@4.9.0:
|
gatsby-source-filesystem@4.9.1:
|
||||||
version "4.9.0"
|
version "4.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-4.9.0.tgz#8cf6f3f67cc97f8a75e284814f444a0eea3263e6"
|
resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-4.9.1.tgz#e619d8a482b0477c28225ffce9c28cbb0606ce67"
|
||||||
integrity sha512-woSxEgYeZSVZSpxm+FwB+RjRIyhcix1AR9766W4yk5RwYH2wciF2OfxRC73WW/o/v1ztzeW6RoqIIY+GBXaA1A==
|
integrity sha512-2HS9+5i+F7tRgxBiv8Op9xK/jvd5DGUfedFsJ6/6sfoXUBddowvW4rVEj4XO42TsIQJe7eVj7FfzfqzSqQN8ow==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.15.4"
|
"@babel/runtime" "^7.15.4"
|
||||||
chokidar "^3.5.2"
|
chokidar "^3.5.2"
|
||||||
file-type "^16.5.3"
|
file-type "^16.5.3"
|
||||||
fs-extra "^10.0.0"
|
fs-extra "^10.0.0"
|
||||||
gatsby-core-utils "^3.9.0"
|
gatsby-core-utils "^3.9.1"
|
||||||
got "^9.6.0"
|
got "^9.6.0"
|
||||||
md5-file "^5.0.0"
|
md5-file "^5.0.0"
|
||||||
mime "^2.5.2"
|
mime "^2.5.2"
|
||||||
|
|
Reference in New Issue