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