Remove geist-ui, add loading prop to button, convert header to CSS
This commit is contained in:
parent
fc79f7df4d
commit
ce0c442273
33 changed files with 253 additions and 392 deletions
|
@ -36,12 +36,15 @@
|
||||||
background-color: var(--lighter-gray);
|
background-color: var(--lighter-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content li a {
|
.content .listItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
justify-content: space-between;
|
||||||
padding: var(--gap-half) var(--gap);
|
|
||||||
color: var(--dark-gray);
|
color: var(--dark-gray);
|
||||||
|
text-decoration: none;
|
||||||
|
/* vertical alignment */
|
||||||
|
padding: var(--gap-quarter) 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import Button from "@components/button"
|
|
||||||
import { Popover } from "@components/popover"
|
import { Popover } from "@components/popover"
|
||||||
import ShiftBy from "@components/shift-by"
|
|
||||||
import { codeFileExtensions } from "@lib/constants"
|
import { codeFileExtensions } from "@lib/constants"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import type { File } from "lib/server/prisma"
|
import type { File } from "lib/server/prisma"
|
||||||
|
@ -32,10 +30,8 @@ const FileDropdown = ({ files }: { files: File[] }) => {
|
||||||
<ul className={styles.content}>
|
<ul className={styles.content}>
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<li key={item.id}>
|
<li key={item.id}>
|
||||||
<a href={`#${item.title}`}>
|
<a href={`#${item.title}`} className={styles.listItem}>
|
||||||
<ShiftBy y={5}>
|
<span className={styles.fileIcon}>{item.icon}</span>
|
||||||
<span className={styles.fileIcon}>{item.icon}</span>
|
|
||||||
</ShiftBy>
|
|
||||||
<span className={styles.fileTitle}>
|
<span className={styles.fileTitle}>
|
||||||
{item.title ? item.title : "Untitled"}
|
{item.title ? item.title : "Untitled"}
|
||||||
</span>
|
</span>
|
||||||
|
@ -58,7 +54,9 @@ const FileDropdown = ({ files }: { files: File[] }) => {
|
||||||
Jump to {files.length} {files.length === 1 ? "file" : "files"}
|
Jump to {files.length} {files.length === 1 ? "file" : "files"}
|
||||||
</span>
|
</span>
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content className={styles.contentWrapper}>{content}</Popover.Content>
|
<Popover.Content className={styles.contentWrapper}>
|
||||||
|
{content}
|
||||||
|
</Popover.Content>
|
||||||
</Popover>
|
</Popover>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import Button from "@components/button"
|
||||||
import Input from "@components/input"
|
import Input from "@components/input"
|
||||||
import ButtonDropdown from "@components/button-dropdown"
|
import ButtonDropdown from "@components/button-dropdown"
|
||||||
import { useToasts } from "@components/toasts"
|
import { useToasts } from "@components/toasts"
|
||||||
|
|
||||||
const emptyDoc = {
|
const emptyDoc = {
|
||||||
title: "",
|
title: "",
|
||||||
content: "",
|
content: "",
|
||||||
|
@ -30,19 +31,20 @@ export type Document = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Post = ({
|
const Post = ({
|
||||||
initialPost,
|
initialPost: stringifiedInitialPost,
|
||||||
newPostParent
|
newPostParent
|
||||||
}: {
|
}: {
|
||||||
initialPost?: PostWithFiles
|
initialPost?: string
|
||||||
newPostParent?: string
|
newPostParent?: string
|
||||||
}) => {
|
}) => {
|
||||||
|
const initialPost = JSON.parse(stringifiedInitialPost || "{}") as PostWithFiles
|
||||||
const { setToast } = useToasts()
|
const { setToast } = useToasts()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [title, setTitle] = useState(
|
const [title, setTitle] = useState(
|
||||||
getTitleForPostCopy(initialPost?.title) || ""
|
getTitleForPostCopy(initialPost?.title) || ""
|
||||||
)
|
)
|
||||||
const [description, setDescription] = useState(initialPost?.description || "")
|
const [description, setDescription] = useState(initialPost?.description || "")
|
||||||
const [expiresAt, setExpiresAt] = useState(initialPost?.expiresAt)
|
const [expiresAt, setExpiresAt] = useState<Date>()
|
||||||
|
|
||||||
const defaultDocs: Document[] = initialPost
|
const defaultDocs: Document[] = initialPost
|
||||||
? initialPost.files?.map((doc) => ({
|
? initialPost.files?.map((doc) => ({
|
||||||
|
@ -212,16 +214,6 @@ const Post = ({
|
||||||
else setDocs((docs) => [...docs, ...files])
|
else setDocs((docs) => [...docs, ...files])
|
||||||
}
|
}
|
||||||
|
|
||||||
// pasted files
|
|
||||||
// const files = e.clipboardData.files as File[]
|
|
||||||
// if (files.length) {
|
|
||||||
// const docs = Array.from(files).map((file) => ({
|
|
||||||
// title: file.name,
|
|
||||||
// content: '',
|
|
||||||
// id: generateUUID()
|
|
||||||
// }))
|
|
||||||
// }
|
|
||||||
|
|
||||||
const onPaste = (e: ClipboardEvent) => {
|
const onPaste = (e: ClipboardEvent) => {
|
||||||
const pastedText = e.clipboardData?.getData("text")
|
const pastedText = e.clipboardData?.getData("text")
|
||||||
|
|
||||||
|
@ -259,8 +251,7 @@ const Post = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// 150 so the post dropdown doesn't overflow
|
<div style={{ paddingBottom: 200 }}>
|
||||||
<div style={{ paddingBottom: 150 }}>
|
|
||||||
<Title title={title} onChange={onChangeTitle} />
|
<Title title={title} onChange={onChangeTitle} />
|
||||||
<Description description={description} onChange={onChangeDescription} />
|
<Description description={description} onChange={onChangeDescription} />
|
||||||
<FileDropzone setDocs={uploadDocs} />
|
<FileDropzone setDocs={uploadDocs} />
|
||||||
|
@ -315,6 +306,7 @@ const Post = ({
|
||||||
height={40}
|
height={40}
|
||||||
width={251}
|
width={251}
|
||||||
onClick={() => onSubmit("unlisted")}
|
onClick={() => onSubmit("unlisted")}
|
||||||
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
Create Unlisted
|
Create Unlisted
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -25,8 +25,10 @@ const NewFromExisting = async ({
|
||||||
withFiles: true,
|
withFiles: true,
|
||||||
withAuthor: false
|
withAuthor: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const serialized = JSON.stringify(post)
|
||||||
|
|
||||||
return <NewPost initialPost={post} newPostParent={id} />
|
return <NewPost initialPost={serialized} newPostParent={id} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NewFromExisting
|
export default NewFromExisting
|
||||||
|
|
|
@ -27,6 +27,7 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor }: Props) => {
|
||||||
const [post, setPost] = useState<PostWithFilesAndAuthor>(
|
const [post, setPost] = useState<PostWithFilesAndAuthor>(
|
||||||
typeof initialPost === "string" ? JSON.parse(initialPost) : initialPost
|
typeof initialPost === "string" ? JSON.parse(initialPost) : initialPost
|
||||||
)
|
)
|
||||||
|
|
||||||
const [visibility, setVisibility] = useState<string>(post.visibility)
|
const [visibility, setVisibility] = useState<string>(post.visibility)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
@ -63,6 +64,10 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor }: Props) => {
|
||||||
}
|
}
|
||||||
}, [isAuthor, post.expiresAt, router])
|
}, [isAuthor, post.expiresAt, router])
|
||||||
|
|
||||||
|
if (isProtected) {
|
||||||
|
return <PasswordModalPage setPost={setPost} postId={post.id} />
|
||||||
|
}
|
||||||
|
|
||||||
const download = async () => {
|
const download = async () => {
|
||||||
if (!post.files) return
|
if (!post.files) return
|
||||||
const downloadZip = (await import("client-zip")).downloadZip
|
const downloadZip = (await import("client-zip")).downloadZip
|
||||||
|
@ -89,12 +94,9 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor }: Props) => {
|
||||||
const viewParentClick = () => {
|
const viewParentClick = () => {
|
||||||
router.push(`/post/${post.parentId}`)
|
router.push(`/post/${post.parentId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAvailable = !isProtected && post.title
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isAvailable && <PasswordModalPage setPost={setPost} postId={post.id} />}
|
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<span className={styles.buttons}>
|
<span className={styles.buttons}>
|
||||||
<ButtonGroup verticalIfMobile>
|
<ButtonGroup verticalIfMobile>
|
||||||
|
|
|
@ -30,14 +30,19 @@ const DownloadButton = ({ rawLink }: { rawLink?: string }) => {
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<Button iconRight={<Download />} aria-label="Download" />
|
<Button
|
||||||
|
iconRight={<Download />}
|
||||||
|
aria-label="Download"
|
||||||
|
style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content="Open raw in new tab">
|
<Tooltip content="Open raw in new tab">
|
||||||
<Link href={rawLink || ""} target="_blank" rel="noopener noreferrer">
|
<Link href={rawLink || ""} target="_blank" rel="noopener noreferrer">
|
||||||
<Button
|
<Button
|
||||||
iconRight={<ExternalLink />}
|
iconLeft={<ExternalLink />}
|
||||||
aria-label="Open raw file in new tab"
|
aria-label="Open raw file in new tab"
|
||||||
|
style={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default async function Head({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSeo
|
<PageSeo
|
||||||
title={`${post.title} - Drift`}
|
title={post.title}
|
||||||
description={post.description || undefined}
|
description={post.description || undefined}
|
||||||
isPrivate={false}
|
isPrivate={false}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -47,7 +47,10 @@ const getPost = async (id: string) => {
|
||||||
|
|
||||||
if (post.visibility === "protected" && !isAuthorOrAdmin) {
|
if (post.visibility === "protected" && !isAuthorOrAdmin) {
|
||||||
return {
|
return {
|
||||||
// post,
|
post: {
|
||||||
|
visibility: "protected",
|
||||||
|
id: post.id
|
||||||
|
},
|
||||||
isProtected: true,
|
isProtected: true,
|
||||||
isAuthor: isAuthorOrAdmin
|
isAuthor: isAuthorOrAdmin
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Tooltip from "@components/tooltip"
|
'use client'
|
||||||
|
// import Tooltip from "@components/tooltip"
|
||||||
import { timeAgo } from "@lib/time-ago"
|
import { timeAgo } from "@lib/time-ago"
|
||||||
import { useMemo, useState, useEffect } from "react"
|
import { useMemo, useState, useEffect } from "react"
|
||||||
import Badge from "../badge"
|
import Badge from "../badge"
|
||||||
|
@ -14,14 +15,15 @@ const CreatedAgoBadge = ({ createdAt }: { createdAt: string | Date }) => {
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [createdDate])
|
}, [createdDate])
|
||||||
|
|
||||||
const formattedTime = `${createdDate.toLocaleDateString()} ${createdDate.toLocaleTimeString()}`
|
// const formattedTime = `${createdDate.toLocaleDateString()} ${createdDate.toLocaleTimeString()}`
|
||||||
return (
|
return (
|
||||||
<Badge type="secondary">
|
// TODO: investigate tooltip
|
||||||
{" "}
|
// <Tooltip content={formattedTime}>
|
||||||
<Tooltip content={formattedTime}>
|
<Badge type="secondary">
|
||||||
|
{" "}
|
||||||
<>{time}</>
|
<>{time}</>
|
||||||
</Tooltip>
|
</Badge>
|
||||||
</Badge>
|
// </Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const VisibilityControl = ({ postId, visibility, setVisibility }: Props) => {
|
const VisibilityControl = ({ postId, visibility, setVisibility }: Props) => {
|
||||||
const [isSubmitting, setSubmitting] = useState(false)
|
const [isSubmitting, setSubmitting] = useState<string | null>()
|
||||||
const [passwordModalVisible, setPasswordModalVisible] = useState(false)
|
const [passwordModalVisible, setPasswordModalVisible] = useState(false)
|
||||||
const { setToast } = useToasts()
|
const { setToast } = useToasts()
|
||||||
|
|
||||||
|
@ -47,53 +47,53 @@ const VisibilityControl = ({ postId, visibility, setVisibility }: Props) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setPasswordModalVisible(false)
|
setPasswordModalVisible(false)
|
||||||
const timeout = setTimeout(() => setSubmitting(true), 100)
|
const timeout = setTimeout(() => setSubmitting(visibility), 100)
|
||||||
|
|
||||||
await sendRequest(visibility, password)
|
await sendRequest(visibility, password)
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
setSubmitting(false)
|
setSubmitting(null)
|
||||||
},
|
},
|
||||||
[sendRequest]
|
[sendRequest]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onClosePasswordModal = () => {
|
const onClosePasswordModal = () => {
|
||||||
setPasswordModalVisible(false)
|
setPasswordModalVisible(false)
|
||||||
setSubmitting(false)
|
setSubmitting(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitPassword = (password: string) => onSubmit("protected", password)
|
const submitPassword = (password: string) => onSubmit("protected", password)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isSubmitting ? (
|
<ButtonGroup verticalIfMobile>
|
||||||
<Spinner />
|
<Button
|
||||||
) : (
|
disabled={visibility === "private"}
|
||||||
<ButtonGroup verticalIfMobile>
|
onClick={() => onSubmit("private")}
|
||||||
<Button
|
>
|
||||||
disabled={visibility === "private"}
|
{isSubmitting === "private" ? <Spinner /> : "Make Private"}
|
||||||
onClick={() => onSubmit("private")}
|
</Button>
|
||||||
>
|
<Button
|
||||||
Make Private
|
disabled={visibility === "public"}
|
||||||
</Button>
|
onClick={() => onSubmit("public")}
|
||||||
<Button
|
>
|
||||||
disabled={visibility === "public"}
|
{isSubmitting === "public" ? <Spinner /> : "Make Public"}
|
||||||
onClick={() => onSubmit("public")}
|
</Button>
|
||||||
>
|
<Button
|
||||||
Make Public
|
disabled={visibility === "unlisted"}
|
||||||
</Button>
|
onClick={() => onSubmit("unlisted")}
|
||||||
<Button
|
>
|
||||||
disabled={visibility === "unlisted"}
|
{isSubmitting === "unlisted" ? <Spinner /> : "Make Unlisted"}
|
||||||
onClick={() => onSubmit("unlisted")}
|
</Button>
|
||||||
>
|
<Button onClick={() => onSubmit("protected")}>
|
||||||
Unlist
|
{isSubmitting === "protected" ? (
|
||||||
</Button>
|
<Spinner />
|
||||||
<Button onClick={() => onSubmit("protected")}>
|
) : visibility === "protected" ? (
|
||||||
{visibility === "protected"
|
"Change Password"
|
||||||
? "Change Password"
|
) : (
|
||||||
: "Protect with Password"}
|
"Protect with Password"
|
||||||
</Button>
|
)}
|
||||||
</ButtonGroup>
|
</Button>
|
||||||
)}
|
</ButtonGroup>
|
||||||
<PasswordModal
|
<PasswordModal
|
||||||
creating={true}
|
creating={true}
|
||||||
isOpen={passwordModalVisible}
|
isOpen={passwordModalVisible}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.verticalIfMobile {
|
.verticalIfMobile {
|
||||||
flex-direction: column;
|
flex-direction: column !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.verticalIfMobile.button-group > button:first-of-type {
|
.verticalIfMobile.button-group > button:first-of-type {
|
||||||
|
|
|
@ -11,9 +11,9 @@ export default function ButtonGroup({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
props.className,
|
|
||||||
styles["button-group"],
|
styles["button-group"],
|
||||||
verticalIfMobile && styles.verticalIfMobile
|
verticalIfMobile && styles.verticalIfMobile,
|
||||||
|
props.className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import styles from "./button.module.css"
|
import styles from "./button.module.css"
|
||||||
import { forwardRef, Ref } from "react"
|
import { forwardRef, Ref } from "react"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
|
import { Spinner } from "@components/spinner"
|
||||||
|
|
||||||
type Props = React.HTMLProps<HTMLButtonElement> & {
|
type Props = React.HTMLProps<HTMLButtonElement> & {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
|
@ -13,6 +14,7 @@ type Props = React.HTMLProps<HTMLButtonElement> & {
|
||||||
width?: string | number
|
width?: string | number
|
||||||
padding?: string | number
|
padding?: string | number
|
||||||
margin?: string | number
|
margin?: string | number
|
||||||
|
loading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
|
@ -31,6 +33,7 @@ const Button = forwardRef<HTMLButtonElement, Props>(
|
||||||
width,
|
width,
|
||||||
padding,
|
padding,
|
||||||
margin,
|
margin,
|
||||||
|
loading,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
|
@ -39,7 +42,7 @@ const Button = forwardRef<HTMLButtonElement, Props>(
|
||||||
<button
|
<button
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={`${styles.button} ${styles[type]} ${className || ""}`}
|
className={`${styles.button} ${styles[type]} ${className || ""}`}
|
||||||
disabled={disabled}
|
disabled={disabled || loading}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
style={{ height, width, margin, padding }}
|
style={{ height, width, margin, padding }}
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -47,10 +50,22 @@ const Button = forwardRef<HTMLButtonElement, Props>(
|
||||||
{children && iconLeft && (
|
{children && iconLeft && (
|
||||||
<span className={clsx(styles.icon, styles.iconLeft)}>{iconLeft}</span>
|
<span className={clsx(styles.icon, styles.iconLeft)}>{iconLeft}</span>
|
||||||
)}
|
)}
|
||||||
{children ? (
|
{!loading &&
|
||||||
children
|
(children ? (
|
||||||
) : (
|
children
|
||||||
<span className={styles.icon}>{iconLeft || iconRight}</span>
|
) : (
|
||||||
|
<span className={styles.icon}>{iconLeft || iconRight}</span>
|
||||||
|
))}
|
||||||
|
{loading && (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spinner />
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
{children && iconRight && (
|
{children && iconRight && (
|
||||||
<span className={clsx(styles.icon, styles.iconRight)}>
|
<span className={clsx(styles.icon, styles.iconRight)}>
|
||||||
|
|
|
@ -30,44 +30,6 @@
|
||||||
box-shadow: inset 0 -1px 0 var(--fg);
|
box-shadow: inset 0 -1px 0 var(--fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1000;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background: var(--bg);
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 70px;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile button {
|
|
||||||
z-index: 1000;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
margin-top: var(--gap);
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 650px) {
|
|
||||||
.tabs {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls button:active,
|
|
||||||
.controls button:focus,
|
|
||||||
.controls button:hover {
|
|
||||||
outline: 1px solid rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -86,3 +48,33 @@
|
||||||
.active button {
|
.active button {
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttonGroup,
|
||||||
|
.mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
.wrapper [data-tab="github"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile {
|
||||||
|
margin-top: var(--gap);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonGroup {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownItem a,
|
||||||
|
.dropdownItem button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
"use client"
|
"use client"
|
||||||
import { useBodyScroll, useMediaQuery } from "@geist-ui/core/dist"
|
|
||||||
|
|
||||||
import { useEffect, useState } from "react"
|
|
||||||
import styles from "./header.module.css"
|
import styles from "./header.module.css"
|
||||||
|
|
||||||
// import useUserData from "@lib/hooks/use-user-data"
|
// import useUserData from "@lib/hooks/use-user-data"
|
||||||
import Link from "next/link"
|
import Link from "@components/link"
|
||||||
import { usePathname } from "next/navigation"
|
import { usePathname } from "next/navigation"
|
||||||
import { signOut } from "next-auth/react"
|
import { signOut } from "next-auth/react"
|
||||||
import Button from "@components/button"
|
import Button from "@components/button"
|
||||||
|
@ -23,6 +20,11 @@ import {
|
||||||
UserPlus,
|
UserPlus,
|
||||||
UserX
|
UserX
|
||||||
} from "react-feather"
|
} from "react-feather"
|
||||||
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
|
||||||
|
import buttonStyles from "@components/button/button.module.css"
|
||||||
|
import ButtonGroup from "@components/button-group"
|
||||||
|
import { useEffect, useMemo, useState } from "react"
|
||||||
|
import Skeleton from "@components/skeleton"
|
||||||
|
|
||||||
type Tab = {
|
type Tab = {
|
||||||
name: string
|
name: string
|
||||||
|
@ -34,38 +36,52 @@ type Tab = {
|
||||||
|
|
||||||
const Header = ({ signedIn = false, isAdmin = false }) => {
|
const Header = ({ signedIn = false, isAdmin = false }) => {
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const [expanded, setExpanded] = useState<boolean>(false)
|
// wait to mount before rendering
|
||||||
const [, setBodyHidden] = useBodyScroll(null, { scrollLayer: true })
|
const [isHydrated, setHydrated] = useState(false)
|
||||||
const isMobile = useMediaQuery("xs", { match: "down" })
|
const { setTheme, resolvedTheme } = useTheme()
|
||||||
// const { status } = useSession()
|
|
||||||
// const signedIn = status === "authenticated"
|
|
||||||
const { setTheme, theme } = useTheme()
|
|
||||||
useEffect(() => {
|
|
||||||
setBodyHidden(expanded)
|
|
||||||
}, [expanded, setBodyHidden])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMobile) {
|
setHydrated(true)
|
||||||
setExpanded(false)
|
}, [])
|
||||||
|
|
||||||
|
const getButton = (tab: Tab) => {
|
||||||
|
const isActive = pathname === tab.href
|
||||||
|
const activeStyle = isActive ? styles.active : ""
|
||||||
|
if (tab.onClick) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={tab.value}
|
||||||
|
iconLeft={tab.icon}
|
||||||
|
onClick={tab.onClick}
|
||||||
|
className={clsx(styles.tab, activeStyle)}
|
||||||
|
aria-label={tab.name}
|
||||||
|
aria-current={isActive ? "page" : undefined}
|
||||||
|
data-tab={tab.value}
|
||||||
|
>
|
||||||
|
{tab.name ? tab.name : undefined}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
} else if (tab.href) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={tab.value}
|
||||||
|
href={tab.href}
|
||||||
|
className={clsx(styles.tab, activeStyle)}
|
||||||
|
data-tab={tab.value}
|
||||||
|
>
|
||||||
|
<Button iconLeft={tab.icon}>{tab.name ? tab.name : undefined}</Button>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [isMobile])
|
}
|
||||||
|
|
||||||
const getPages = () => {
|
const pages = useMemo(() => {
|
||||||
const defaultPages: Tab[] = [
|
const defaultPages: Tab[] = [
|
||||||
{
|
{
|
||||||
name: isMobile ? "GitHub" : "",
|
name: "GitHub",
|
||||||
href: "https://github.com/maxleiter/drift",
|
href: "https://github.com/maxleiter/drift",
|
||||||
icon: <GitHub />,
|
icon: <GitHub />,
|
||||||
value: "github"
|
value: "github"
|
||||||
},
|
|
||||||
{
|
|
||||||
name: isMobile ? "Change theme" : "",
|
|
||||||
onClick: function () {
|
|
||||||
if (typeof window !== "undefined")
|
|
||||||
setTheme(theme === "light" ? "dark" : "light")
|
|
||||||
},
|
|
||||||
icon: theme === "light" ? <Moon /> : <Sun />,
|
|
||||||
value: "theme"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -78,6 +94,15 @@ const Header = ({ signedIn = false, isAdmin = false }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultPages.push({
|
||||||
|
name: "Theme",
|
||||||
|
onClick: function () {
|
||||||
|
setTheme(resolvedTheme === "light" ? "dark" : "light")
|
||||||
|
},
|
||||||
|
icon: isHydrated ? (resolvedTheme === "light" ? <Moon /> : <Sun />) : <></>,
|
||||||
|
value: "theme"
|
||||||
|
})
|
||||||
|
|
||||||
if (signedIn)
|
if (signedIn)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -131,46 +156,16 @@ const Header = ({ signedIn = false, isAdmin = false }) => {
|
||||||
},
|
},
|
||||||
...defaultPages
|
...defaultPages
|
||||||
]
|
]
|
||||||
}
|
}, [isAdmin, isHydrated, resolvedTheme, signedIn, setTheme])
|
||||||
|
|
||||||
const pages = getPages()
|
// // TODO: this should not be necessary.
|
||||||
|
// if (!clientHydrated) {
|
||||||
const onTabChange = (tab: string) => {
|
// return (
|
||||||
if (typeof window === "undefined") return
|
// <header>
|
||||||
const match = pages.find((page) => page.value === tab)
|
// <div className={styles.tabs}>{getPages(true).map(getButton)}</div>
|
||||||
if (match?.onClick) {
|
// </header>
|
||||||
match.onClick()
|
// )
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
const getButton = (tab: Tab) => {
|
|
||||||
const isActive = pathname === tab.href
|
|
||||||
const activeStyle = isActive ? styles.active : ""
|
|
||||||
if (tab.onClick) {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={tab.value}
|
|
||||||
iconLeft={tab.icon}
|
|
||||||
onClick={() => onTabChange(tab.value)}
|
|
||||||
className={clsx(styles.tab, activeStyle)}
|
|
||||||
aria-label={tab.name}
|
|
||||||
aria-current={isActive ? "page" : undefined}
|
|
||||||
>
|
|
||||||
{tab.name ? tab.name : undefined}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
} else if (tab.href) {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={tab.value}
|
|
||||||
href={tab.href}
|
|
||||||
className={clsx(styles.tab, activeStyle)}
|
|
||||||
>
|
|
||||||
<Button iconLeft={tab.icon}>{tab.name ? tab.name : undefined}</Button>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttons = pages.map(getButton)
|
const buttons = pages.map(getButton)
|
||||||
|
|
||||||
|
@ -179,17 +174,28 @@ const Header = ({ signedIn = false, isAdmin = false }) => {
|
||||||
<div className={styles.tabs}>
|
<div className={styles.tabs}>
|
||||||
<div className={styles.buttons}>{buttons}</div>
|
<div className={styles.buttons}>{buttons}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.controls}>
|
<DropdownMenu.Root>
|
||||||
<Button onClick={() => setExpanded(!expanded)} aria-label="Menu">
|
<DropdownMenu.Trigger
|
||||||
<Menu />
|
className={clsx(buttonStyles.button, styles.mobile)}
|
||||||
</Button>
|
asChild
|
||||||
</div>
|
>
|
||||||
{/* setExpanded should occur elsewhere; we don't want to close if they change themes */}
|
<Button aria-label="Menu">
|
||||||
{isMobile && expanded && (
|
<Menu />
|
||||||
<div className={styles.mobile} onClick={() => setExpanded(!expanded)}>
|
</Button>
|
||||||
{buttons}
|
</DropdownMenu.Trigger>
|
||||||
</div>
|
<DropdownMenu.Portal>
|
||||||
)}
|
<DropdownMenu.Content className={styles.contentWrapper}>
|
||||||
|
{buttons.map((button) => (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
key={button?.key}
|
||||||
|
className={styles.dropdownItem}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
|
import clsx from "clsx"
|
||||||
import styles from "./note.module.css"
|
import styles from "./note.module.css"
|
||||||
|
|
||||||
const Note = ({
|
const Note = ({
|
||||||
type = "info",
|
type = "info",
|
||||||
children,
|
children,
|
||||||
|
className,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
type: "info" | "warning" | "error"
|
type: "info" | "warning" | "error"
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
} & React.ComponentProps<"div">) => (
|
} & React.ComponentProps<"div">) => (
|
||||||
<div className={`${styles.note} ${styles[type]}`} {...props}>
|
<div className={clsx(className, styles.note, styles[type])} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,13 +9,14 @@ type PageSeoProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const PageSeo = ({
|
const PageSeo = ({
|
||||||
title = "Drift",
|
title: pageTitle,
|
||||||
description = "A self-hostable clone of GitHub Gist",
|
description = "A self-hostable clone of GitHub Gist",
|
||||||
isPrivate = false
|
isPrivate = false
|
||||||
}: PageSeoProps) => {
|
}: PageSeoProps) => {
|
||||||
|
const title = `Drift${pageTitle ? ` - ${pageTitle}` : ""}`
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<title>Drift{title ? ` - ${title}` : ""}</title>
|
<title>{title}</title>
|
||||||
<meta charSet="utf-8" />
|
<meta charSet="utf-8" />
|
||||||
{!isPrivate && <meta name="description" content={description} />}
|
{!isPrivate && <meta name="description" content={description} />}
|
||||||
{isPrivate && <meta name="robots" content="noindex" />}
|
{isPrivate && <meta name="robots" content="noindex" />}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
.page {
|
.page {
|
||||||
max-width: 100vw;
|
max-width: var(--main-content);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 0 calc(1.34 * 16px) 0 calc(1.34 * 16px);
|
padding: 0 calc(1.34 * 16px) 0 calc(1.34 * 16px);
|
||||||
margin: 0 auto 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ const PasswordModal = ({
|
||||||
onEscapeKeyDown={onClose}
|
onEscapeKeyDown={onClose}
|
||||||
>
|
>
|
||||||
<Dialog.Title>
|
<Dialog.Title>
|
||||||
{creating ? "Create a password" : "Enter password"}
|
{creating ? "Add a password" : "Enter password"}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<Dialog.Description>
|
<Dialog.Description>
|
||||||
{creating
|
{creating
|
||||||
|
|
|
@ -4,14 +4,16 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0,0,0,0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
background: var(--gray-alpha);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
background-color: var(--bg);
|
background-color: var(--bg);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
|
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px,
|
||||||
|
hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -20,7 +22,7 @@
|
||||||
max-width: 450px;
|
max-width: 450px;
|
||||||
max-height: 85vh;
|
max-height: 85vh;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
animation: contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
animation: contentShow 100ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +33,7 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--gap-quarter);
|
gap: var(--gap);
|
||||||
margin-bottom: var(--gap-half);
|
margin-bottom: var(--gap-half);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
// https://www.joshwcomeau.com/snippets/react-components/shift-by/
|
|
||||||
type Props = {
|
|
||||||
x?: number
|
|
||||||
y?: number
|
|
||||||
children: React.ReactNode
|
|
||||||
}
|
|
||||||
|
|
||||||
function ShiftBy({ x = 0, y = 0, children }: Props) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
transform: `translate(${x}px, ${y}px)`,
|
|
||||||
display: "inline-block"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default ShiftBy
|
|
|
@ -1,5 +1,5 @@
|
||||||
import PageSeo from "@components/page-seo"
|
import PageSeo from "@components/page-seo"
|
||||||
|
|
||||||
export default function RootHead() {
|
export default function RootHead() {
|
||||||
return <PageSeo title="Drift" />
|
return <PageSeo />
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import "@styles/globals.css"
|
import "@styles/globals.css"
|
||||||
import { LayoutWrapper } from "./root-layout-wrapper"
|
import { LayoutWrapper } from "./root-layout-wrapper"
|
||||||
import styles from "@styles/Home.module.css"
|
|
||||||
import { getSession } from "@lib/server/session"
|
import { getSession } from "@lib/server/session"
|
||||||
import { ServerThemeProvider } from "@wits/next-themes"
|
import { ServerThemeProvider } from "@wits/next-themes"
|
||||||
|
|
||||||
|
@ -14,7 +13,6 @@ export default async function RootLayout({ children }: RootLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<ServerThemeProvider
|
<ServerThemeProvider
|
||||||
enableSystem={true}
|
enableSystem={true}
|
||||||
defaultTheme="dark"
|
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
cookieName={"drift-theme"}
|
cookieName={"drift-theme"}
|
||||||
attribute="data-theme"
|
attribute="data-theme"
|
||||||
|
@ -22,7 +20,7 @@ export default async function RootLayout({ children }: RootLayoutProps) {
|
||||||
>
|
>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head />
|
<head />
|
||||||
<body className={styles.main}>
|
<body>
|
||||||
<LayoutWrapper
|
<LayoutWrapper
|
||||||
signedIn={Boolean(session?.user)}
|
signedIn={Boolean(session?.user)}
|
||||||
isAdmin={session?.user.role === "admin"}
|
isAdmin={session?.user.role === "admin"}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import PageSeo from "@components/page-seo"
|
import PageSeo from "@components/page-seo"
|
||||||
|
|
||||||
export default function Head() {
|
export default function Head() {
|
||||||
return <PageSeo title="Drift - Your profile" isPrivate />
|
return <PageSeo title="Your profile" isPrivate />
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import Page from "@components/page"
|
||||||
import { Toasts } from "@components/toasts"
|
import { Toasts } from "@components/toasts"
|
||||||
import * as RadixTooltip from "@radix-ui/react-tooltip"
|
import * as RadixTooltip from "@radix-ui/react-tooltip"
|
||||||
import { ThemeProvider } from "@wits/next-themes"
|
import { ThemeProvider } from "@wits/next-themes"
|
||||||
import { Toaster } from "react-hot-toast"
|
|
||||||
|
|
||||||
export function LayoutWrapper({
|
export function LayoutWrapper({
|
||||||
children,
|
children,
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--gap);
|
||||||
|
max-width: 300px;
|
||||||
|
margin-top: var(--gap);
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import Note from "@components/note"
|
||||||
import { useToasts } from "@components/toasts"
|
import { useToasts } from "@components/toasts"
|
||||||
import { User } from "next-auth"
|
import { User } from "next-auth"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
import styles from "./profile.module.css"
|
||||||
|
|
||||||
const Profile = ({ user }: { user: User }) => {
|
const Profile = ({ user }: { user: User }) => {
|
||||||
// TODO: make this displayName, requires fetching user from DB as session doesnt have it
|
// TODO: make this displayName, requires fetching user from DB as session doesnt have it
|
||||||
|
@ -63,15 +64,7 @@ const Profile = ({ user }: { user: User }) => {
|
||||||
<Note type="warning">
|
<Note type="warning">
|
||||||
This information will be publicly available on your profile
|
This information will be publicly available on your profile
|
||||||
</Note>
|
</Note>
|
||||||
<form
|
<form onSubmit={onSubmit} className={styles.form}>
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: "var(--gap)",
|
|
||||||
maxWidth: "300px"
|
|
||||||
}}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="displayName">Display name</label>
|
<label htmlFor="displayName">Display name</label>
|
||||||
<Input
|
<Input
|
||||||
|
@ -81,6 +74,8 @@ const Profile = ({ user }: { user: User }) => {
|
||||||
value={name || ""}
|
value={name || ""}
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
aria-label="Display name"
|
aria-label="Display name"
|
||||||
|
minLength={1}
|
||||||
|
maxLength={32}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -95,20 +90,7 @@ const Profile = ({ user }: { user: User }) => {
|
||||||
aria-label="Email"
|
aria-label="Email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* <div>
|
<Button type="submit">Submit</Button>
|
||||||
<label htmlFor="bio">Biography (max 250 characters)</label>
|
|
||||||
<textarea
|
|
||||||
id="bio"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
maxLength={250}
|
|
||||||
placeholder="I enjoy..."
|
|
||||||
value={bio}
|
|
||||||
onChange={handleBioChange}
|
|
||||||
/>
|
|
||||||
</div> */}
|
|
||||||
<Button type="submit">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import PageSeo from "@components/page-seo"
|
import PageSeo from "@components/page-seo"
|
||||||
|
|
||||||
export default function Head() {
|
export default function Head() {
|
||||||
return <PageSeo title="Drift - Settings" isPrivate />
|
return <PageSeo title="Settings" isPrivate />
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
.wrapper {
|
|
||||||
height: 100% !important;
|
|
||||||
padding-bottom: var(--small-gap) !important;
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
max-width: var(--main-content) !important;
|
|
||||||
margin: 0 auto !important;
|
|
||||||
padding: 0 0 !important;
|
|
||||||
}
|
|
|
@ -44,7 +44,7 @@
|
||||||
--header-bg: rgba(19, 20, 21, 0.45);
|
--header-bg: rgba(19, 20, 21, 0.45);
|
||||||
--gray-alpha: rgba(255, 255, 255, 0.5);
|
--gray-alpha: rgba(255, 255, 255, 0.5);
|
||||||
--selection: rgba(255, 255, 255, 0.99);
|
--selection: rgba(255, 255, 255, 0.99);
|
||||||
--border: var(--lighter-gray);
|
--border: var(--light-gray);
|
||||||
--warning: #ff6700;
|
--warning: #ff6700;
|
||||||
--link: #3291ff;
|
--link: #3291ff;
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
--gray: #888;
|
--gray: #888;
|
||||||
|
|
||||||
--light-gray: #dedede;
|
--light-gray: #dedede;
|
||||||
--lighter-gray: #f5f5f5;
|
--lighter-gray: #f2f2f2;
|
||||||
--lightest-gray: #fafafa;
|
--lightest-gray: #fafafa;
|
||||||
--darker-gray: #555;
|
--darker-gray: #555;
|
||||||
--darkest-gray: #222;
|
--darkest-gray: #222;
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
|
|
||||||
// TODO: replace this with an accessible alternative
|
// TODO: replace this with an accessible alternative
|
||||||
*:focus-visible {
|
*:focus-visible {
|
||||||
outline: none;
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import config from "@lib/config"
|
||||||
const providers: NextAuthOptions["providers"] = [
|
const providers: NextAuthOptions["providers"] = [
|
||||||
GitHubProvider({
|
GitHubProvider({
|
||||||
clientId: config.github_client_id,
|
clientId: config.github_client_id,
|
||||||
clientSecret: config.github_client_secret
|
clientSecret: config.github_client_secret,
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
"jest": "jest"
|
"jest": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@geist-ui/core": "^2.3.8",
|
|
||||||
"@next-auth/prisma-adapter": "^1.0.5",
|
"@next-auth/prisma-adapter": "^1.0.5",
|
||||||
"@next/eslint-plugin-next": "13.0.5-canary.3",
|
"@next/eslint-plugin-next": "13.0.5-canary.3",
|
||||||
"@prisma/client": "^4.6.1",
|
"@prisma/client": "^4.6.1",
|
||||||
|
@ -26,20 +25,16 @@
|
||||||
"@wits/next-themes": "^0.2.12",
|
"@wits/next-themes": "^0.2.12",
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
"client-zip": "2.2.1",
|
"client-zip": "2.2.1",
|
||||||
"cookies-next": "^2.1.1",
|
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"next": "13.0.6-canary.2",
|
"next": "13.0.6-canary.2",
|
||||||
"next-auth": "^4.17.0",
|
"next-auth": "^4.17.0",
|
||||||
"prisma": "^4.6.1",
|
"prisma": "^4.6.1",
|
||||||
"rc-table": "7.24.1",
|
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-datepicker": "4.8.0",
|
"react-datepicker": "4.8.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-dropzone": "14.2.3",
|
"react-dropzone": "14.2.3",
|
||||||
"react-feather": "^2.0.10",
|
"react-feather": "^2.0.10",
|
||||||
"react-hot-toast": "^2.4.0",
|
"react-hot-toast": "^2.4.0",
|
||||||
"server-only": "^0.0.1",
|
|
||||||
"swr": "1.3.0",
|
|
||||||
"textarea-markdown-editor": "0.1.13",
|
"textarea-markdown-editor": "0.1.13",
|
||||||
"ts-jest": "^29.0.3"
|
"ts-jest": "^29.0.3"
|
||||||
},
|
},
|
||||||
|
@ -54,7 +49,6 @@
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.27.0",
|
"eslint": "8.27.0",
|
||||||
"eslint-config-next": "13.0.3",
|
"eslint-config-next": "13.0.3",
|
||||||
"katex": "^0.16.3",
|
|
||||||
"next-unused": "0.0.6",
|
"next-unused": "0.0.6",
|
||||||
"prettier": "2.6.2",
|
"prettier": "2.6.2",
|
||||||
"typescript": "4.6.4",
|
"typescript": "4.6.4",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
lockfileVersion: 5.4
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@geist-ui/core': ^2.3.8
|
|
||||||
'@next-auth/prisma-adapter': ^1.0.5
|
'@next-auth/prisma-adapter': ^1.0.5
|
||||||
'@next/bundle-analyzer': 12.1.6
|
'@next/bundle-analyzer': 12.1.6
|
||||||
'@next/eslint-plugin-next': 13.0.5-canary.3
|
'@next/eslint-plugin-next': 13.0.5-canary.3
|
||||||
|
@ -21,34 +20,28 @@ specifiers:
|
||||||
bcrypt: ^5.1.0
|
bcrypt: ^5.1.0
|
||||||
client-zip: 2.2.1
|
client-zip: 2.2.1
|
||||||
clsx: ^1.2.1
|
clsx: ^1.2.1
|
||||||
cookies-next: ^2.1.1
|
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-config-next: 13.0.3
|
eslint-config-next: 13.0.3
|
||||||
jest: ^29.3.1
|
jest: ^29.3.1
|
||||||
katex: ^0.16.3
|
|
||||||
next: 13.0.6-canary.2
|
next: 13.0.6-canary.2
|
||||||
next-auth: ^4.17.0
|
next-auth: ^4.17.0
|
||||||
next-unused: 0.0.6
|
next-unused: 0.0.6
|
||||||
prettier: 2.6.2
|
prettier: 2.6.2
|
||||||
prisma: ^4.6.1
|
prisma: ^4.6.1
|
||||||
rc-table: 7.24.1
|
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-datepicker: 4.8.0
|
react-datepicker: 4.8.0
|
||||||
react-dom: 18.2.0
|
react-dom: 18.2.0
|
||||||
react-dropzone: 14.2.3
|
react-dropzone: 14.2.3
|
||||||
react-feather: ^2.0.10
|
react-feather: ^2.0.10
|
||||||
react-hot-toast: ^2.4.0
|
react-hot-toast: ^2.4.0
|
||||||
server-only: ^0.0.1
|
|
||||||
sharp: ^0.31.2
|
sharp: ^0.31.2
|
||||||
swr: 1.3.0
|
|
||||||
textarea-markdown-editor: 0.1.13
|
textarea-markdown-editor: 0.1.13
|
||||||
ts-jest: ^29.0.3
|
ts-jest: ^29.0.3
|
||||||
typescript: 4.6.4
|
typescript: 4.6.4
|
||||||
typescript-plugin-css-modules: 3.4.0
|
typescript-plugin-css-modules: 3.4.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@geist-ui/core': 2.3.8_biqbaboplfbrettd7655fr4n2y
|
|
||||||
'@next-auth/prisma-adapter': 1.0.5_o53gfpk3vz2btjrokqfjjwwn3m
|
'@next-auth/prisma-adapter': 1.0.5_o53gfpk3vz2btjrokqfjjwwn3m
|
||||||
'@next/eslint-plugin-next': 13.0.5-canary.3
|
'@next/eslint-plugin-next': 13.0.5-canary.3
|
||||||
'@prisma/client': 4.6.1_prisma@4.6.1
|
'@prisma/client': 4.6.1_prisma@4.6.1
|
||||||
|
@ -61,20 +54,16 @@ dependencies:
|
||||||
'@wits/next-themes': 0.2.12_hzq4dmqplfkom7c35ucps6atz4
|
'@wits/next-themes': 0.2.12_hzq4dmqplfkom7c35ucps6atz4
|
||||||
bcrypt: 5.1.0
|
bcrypt: 5.1.0
|
||||||
client-zip: 2.2.1
|
client-zip: 2.2.1
|
||||||
cookies-next: 2.1.1
|
|
||||||
jest: 29.3.1_@types+node@17.0.23
|
jest: 29.3.1_@types+node@17.0.23
|
||||||
next: 13.0.6-canary.2_biqbaboplfbrettd7655fr4n2y
|
next: 13.0.6-canary.2_biqbaboplfbrettd7655fr4n2y
|
||||||
next-auth: 4.17.0_hzq4dmqplfkom7c35ucps6atz4
|
next-auth: 4.17.0_hzq4dmqplfkom7c35ucps6atz4
|
||||||
prisma: 4.6.1
|
prisma: 4.6.1
|
||||||
rc-table: 7.24.1_biqbaboplfbrettd7655fr4n2y
|
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
|
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
react-dropzone: 14.2.3_react@18.2.0
|
react-dropzone: 14.2.3_react@18.2.0
|
||||||
react-feather: 2.0.10_react@18.2.0
|
react-feather: 2.0.10_react@18.2.0
|
||||||
react-hot-toast: 2.4.0_biqbaboplfbrettd7655fr4n2y
|
react-hot-toast: 2.4.0_biqbaboplfbrettd7655fr4n2y
|
||||||
server-only: 0.0.1
|
|
||||||
swr: 1.3.0_react@18.2.0
|
|
||||||
textarea-markdown-editor: 0.1.13_biqbaboplfbrettd7655fr4n2y
|
textarea-markdown-editor: 0.1.13_biqbaboplfbrettd7655fr4n2y
|
||||||
ts-jest: 29.0.3_7hcmezpa7bajbjecov7p46z4aa
|
ts-jest: 29.0.3_7hcmezpa7bajbjecov7p46z4aa
|
||||||
|
|
||||||
|
@ -92,7 +81,6 @@ devDependencies:
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-config-next: 13.0.3_hsmo2rtalirsvadpuxki35bq2i
|
eslint-config-next: 13.0.3_hsmo2rtalirsvadpuxki35bq2i
|
||||||
katex: 0.16.3
|
|
||||||
next-unused: 0.0.6
|
next-unused: 0.0.6
|
||||||
prettier: 2.6.2
|
prettier: 2.6.2
|
||||||
typescript: 4.6.4
|
typescript: 4.6.4
|
||||||
|
@ -490,17 +478,6 @@ packages:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@geist-ui/core/2.3.8_biqbaboplfbrettd7655fr4n2y:
|
|
||||||
resolution: {integrity: sha512-OKwGgTA4+fBM41eQbqDoUj4XBycZbYH7Ynrn6LPO5yKX7zeWPu/R7HN3vB4/oHt34VTDQI5sDNb1SirHvNyB5w==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.9.0'
|
|
||||||
react-dom: '>=16.9.0'
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.20.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0_react@18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@humanwhocodes/config-array/0.11.7:
|
/@humanwhocodes/config-array/0.11.7:
|
||||||
resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==}
|
resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
|
@ -1498,10 +1475,6 @@ packages:
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/cookie/0.4.1:
|
|
||||||
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/debug/4.1.7:
|
/@types/debug/4.1.7:
|
||||||
resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
|
resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1554,10 +1527,6 @@ packages:
|
||||||
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/node/16.18.3:
|
|
||||||
resolution: {integrity: sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/node/17.0.23:
|
/@types/node/17.0.23:
|
||||||
resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==}
|
resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==}
|
||||||
|
|
||||||
|
@ -2320,6 +2289,7 @@ packages:
|
||||||
/commander/8.3.0:
|
/commander/8.3.0:
|
||||||
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||||
engines: {node: '>= 12'}
|
engines: {node: '>= 12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/commondir/1.0.1:
|
/commondir/1.0.1:
|
||||||
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
||||||
|
@ -2340,24 +2310,11 @@ packages:
|
||||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/cookie/0.4.2:
|
|
||||||
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cookie/0.5.0:
|
/cookie/0.5.0:
|
||||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/cookies-next/2.1.1:
|
|
||||||
resolution: {integrity: sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==}
|
|
||||||
dependencies:
|
|
||||||
'@types/cookie': 0.4.1
|
|
||||||
'@types/node': 16.18.3
|
|
||||||
cookie: 0.4.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/copy-anything/2.0.6:
|
/copy-anything/2.0.6:
|
||||||
resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
|
resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4517,13 +4474,6 @@ packages:
|
||||||
commander: 8.3.0
|
commander: 8.3.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/katex/0.16.3:
|
|
||||||
resolution: {integrity: sha512-3EykQddareoRmbtNiNEDgl3IGjryyrp2eg/25fHDEnlHymIDi33bptkMv6K4EOC2LZCybLW/ZkEo6Le+EM9pmA==}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
commander: 8.3.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/kleur/3.0.3:
|
/kleur/3.0.3:
|
||||||
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -5918,49 +5868,6 @@ packages:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rc-resize-observer/1.2.0_biqbaboplfbrettd7655fr4n2y:
|
|
||||||
resolution: {integrity: sha512-6W+UzT3PyDM0wVCEHfoW3qTHPTvbdSgiA43buiy8PzmeMnfgnDeb9NjdimMXMl3/TcrvvWl5RRVdp+NqcR47pQ==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.9.0'
|
|
||||||
react-dom: '>=16.9.0'
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.20.1
|
|
||||||
classnames: 2.3.2
|
|
||||||
rc-util: 5.24.4_biqbaboplfbrettd7655fr4n2y
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0_react@18.2.0
|
|
||||||
resize-observer-polyfill: 1.5.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/rc-table/7.24.1_biqbaboplfbrettd7655fr4n2y:
|
|
||||||
resolution: {integrity: sha512-DRWpv5z5pmOaTmy5GqWoskeV1thaOu5HuD+2f61b/CkbBqlgJR3cygc5R/Qvd2uVW6pHU0lYulhmz0VLVFm+rw==}
|
|
||||||
engines: {node: '>=8.x'}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.9.0'
|
|
||||||
react-dom: '>=16.9.0'
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.20.1
|
|
||||||
classnames: 2.3.2
|
|
||||||
rc-resize-observer: 1.2.0_biqbaboplfbrettd7655fr4n2y
|
|
||||||
rc-util: 5.24.4_biqbaboplfbrettd7655fr4n2y
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0_react@18.2.0
|
|
||||||
shallowequal: 1.1.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/rc-util/5.24.4_biqbaboplfbrettd7655fr4n2y:
|
|
||||||
resolution: {integrity: sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.9.0'
|
|
||||||
react-dom: '>=16.9.0'
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.20.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0_react@18.2.0
|
|
||||||
react-is: 16.13.1
|
|
||||||
shallowequal: 1.1.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/rc/1.2.8:
|
/rc/1.2.8:
|
||||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -6314,10 +6221,6 @@ packages:
|
||||||
resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==}
|
resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/resize-observer-polyfill/1.5.1:
|
|
||||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/resolve-cwd/3.0.0:
|
/resolve-cwd/3.0.0:
|
||||||
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
|
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -6462,18 +6365,10 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
lru-cache: 6.0.0
|
lru-cache: 6.0.0
|
||||||
|
|
||||||
/server-only/0.0.1:
|
|
||||||
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/set-blocking/2.0.0:
|
/set-blocking/2.0.0:
|
||||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/shallowequal/1.1.0:
|
|
||||||
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/sharp/0.31.2:
|
/sharp/0.31.2:
|
||||||
resolution: {integrity: sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==}
|
resolution: {integrity: sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==}
|
||||||
engines: {node: '>=14.15.0'}
|
engines: {node: '>=14.15.0'}
|
||||||
|
@ -6775,14 +6670,6 @@ packages:
|
||||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
/swr/1.3.0_react@18.2.0:
|
|
||||||
resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^16.11.0 || ^17.0.0 || ^18.0.0
|
|
||||||
dependencies:
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tapable/1.1.3:
|
/tapable/1.1.3:
|
||||||
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
|
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
Loading…
Reference in a new issue