use custom input/buttons on postlist/new page

This commit is contained in:
Max Leiter 2022-11-15 22:52:25 -08:00
parent dfe0d39fa0
commit 45c2e59105
16 changed files with 99 additions and 74 deletions

View file

@ -5,7 +5,7 @@ import { Note, Text } from "@geist-ui/core/dist"
export default function ExpiredPage() { export default function ExpiredPage() {
return ( return (
<Note type="error" label={false}> <Note type="error" label={false}>
<Text h4>Error: The Drift you&apos;re trying to view has expired.</Text> <h2>Error: The Drift you&apos;re trying to view has expired.</h2>
</Note> </Note>
) )
} }

View file

@ -1,4 +1,4 @@
import { Text, useMediaQuery, useTheme, useToasts } from "@geist-ui/core/dist" import { useMediaQuery, useTheme, useToasts } from "@geist-ui/core/dist"
import { useDropzone } from "react-dropzone" import { useDropzone } from "react-dropzone"
import styles from "./drag-and-drop.module.css" import styles from "./drag-and-drop.module.css"
import generateUUID from "@lib/generate-uuid" import generateUUID from "@lib/generate-uuid"
@ -79,7 +79,7 @@ function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
<ul> <ul>
{errors.map((e) => ( {errors.map((e) => (
<li key={e.code}> <li key={e.code}>
<Text>{e.message}</Text> {e.message}
</li> </li>
))} ))}
</ul> </ul>
@ -100,12 +100,12 @@ function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
{!isDragActive && ( {!isDragActive && (
<p style={{color: "var(--gray)"}}>Drag some files here, or {verb} to select files</p> <p style={{color: "var(--gray)"}}>Drag some files here, or {verb} to select files</p>
)} )}
{isDragActive && <Text p>Release to drop the files here</Text>} {isDragActive && <p>Release to drop the files here</p>}
</div> </div>
{fileRejections.length > 0 && ( {fileRejections.length > 0 && (
<ul className={styles.error}> <ul className={styles.error}>
{/* <Button style={{ float: 'right' }} type="abort" onClick={() => fileRejections.splice(0, fileRejections.length)} auto iconRight={<XCircle />}></Button> */} {/* <Button style={{ float: 'right' }} type="abort" onClick={() => fileRejections.splice(0, fileRejections.length)} auto iconRight={<XCircle />}></Button> */}
<Text h5>There was a problem with one or more of your files.</Text> <p>There was a problem with one or more of your files.</p>
{fileRejectionItems} {fileRejectionItems}
</ul> </ul>
)} )}

View file

@ -37,6 +37,7 @@
.actionWrapper .actions { .actionWrapper .actions {
position: absolute; position: absolute;
right: 0; right: 0;
top: 4px;
} }
@media (max-width: 768px) { @media (max-width: 768px) {

View file

@ -7,9 +7,10 @@ import List from "@geist-ui/icons/list"
import ImageIcon from "@geist-ui/icons/image" import ImageIcon from "@geist-ui/icons/image"
import { RefObject, useMemo } from "react" import { RefObject, useMemo } from "react"
import styles from "../document.module.css" import styles from "../document.module.css"
import { Button, ButtonGroup } from "@geist-ui/core/dist"
import { TextareaMarkdownRef } from "textarea-markdown-editor" import { TextareaMarkdownRef } from "textarea-markdown-editor"
import Tooltip from "@components/tooltip" import Tooltip from "@components/tooltip"
import Button from "@components/button"
import ButtonGroup from "@components/button-group"
// TODO: clean up // TODO: clean up
@ -69,11 +70,8 @@ const FormattingIcons = ({
key={name} key={name}
> >
<Button <Button
auto
scale={2 / 3}
px={0.6}
aria-label={name} aria-label={name}
icon={icon} iconRight={icon}
onMouseDown={(e) => e.preventDefault()} onMouseDown={(e) => e.preventDefault()}
onClick={action} onClick={action}
/> />

View file

@ -11,8 +11,9 @@ import Trash from "@geist-ui/icons/trash"
import FormattingIcons from "./formatting-icons" import FormattingIcons from "./formatting-icons"
import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor" import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor"
import { Button, Input, Spacer, Tabs, Textarea } from "@geist-ui/core/dist" import { Input, Tabs, Textarea } from "@geist-ui/core/dist"
import Preview from "../../../../components/preview" import Preview from "../../../../components/preview"
import Button from "@components/button"
// import Link from "next/link" // import Link from "next/link"
type Props = { type Props = {
@ -103,12 +104,11 @@ const Document = ({
/> />
{remove && ( {remove && (
<Button <Button
type="abort" iconLeft={<Trash />}
ghost
icon={<Trash />}
auto
height={"36px"} height={"36px"}
width={"36px"} width={"48px"}
padding={0}
margin={0}
onClick={() => removeFile(remove)} onClick={() => removeFile(remove)}
/> />
)} )}

View file

@ -1,6 +1,6 @@
"use client" "use client"
import { Button, useToasts, Input, ButtonDropdown } from "@geist-ui/core/dist" import { useToasts, Input, ButtonDropdown } from "@geist-ui/core/dist"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { useCallback, useState } from "react" import { useCallback, useState } from "react"
import generateUUID from "@lib/generate-uuid" import generateUUID from "@lib/generate-uuid"
@ -14,6 +14,7 @@ import { PostWithFiles } from "@lib/server/prisma"
import PasswordModal from "../../../components/password-modal" import PasswordModal from "../../../components/password-modal"
import Title from "./title" import Title from "./title"
import FileDropzone from "./drag-and-drop" import FileDropzone from "./drag-and-drop"
import Button from "@components/button"
const emptyDoc = { const emptyDoc = {
title: "", title: "",
content: "", content: "",
@ -162,21 +163,19 @@ const Post = ({
const onChangeExpiration = (date: Date) => setExpiresAt(date) const onChangeExpiration = (date: Date) => setExpiresAt(date)
const onChangeTitle = const onChangeTitle = (e: ChangeEvent<HTMLInputElement>) => {
(e: ChangeEvent<HTMLInputElement>) => { setTitle(e.target.value)
setTitle(e.target.value) }
}
const onChangeDescription = const onChangeDescription = (e: ChangeEvent<HTMLInputElement>) => {
(e: ChangeEvent<HTMLInputElement>) => { setDescription(e.target.value)
setDescription(e.target.value) }
}
const updateDocTitle = (i: number) => (title: string) => { const updateDocTitle = (i: number) => (title: string) => {
setDocs((docs) => setDocs((docs) =>
docs.map((doc, index) => (i === index ? { ...doc, title } : doc)) docs.map((doc, index) => (i === index ? { ...doc, title } : doc))
) )
} }
const updateDocContent = (i: number) => (content: string) => { const updateDocContent = (i: number) => (content: string) => {
setDocs((docs) => setDocs((docs) =>
@ -278,6 +277,9 @@ const Post = ({
]) ])
}} }}
type="default" type="default"
style={{
flex: 1
}}
> >
Add a File Add a File
</Button> </Button>

View file

@ -0,0 +1,13 @@
.button-group {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: stretch;
align-content: stretch;
}
.button-group > * {
flex: 1 1 auto;
margin: 0;
}

View file

@ -0,0 +1,14 @@
import styles from "./button-group.module.css"
export default function ButtonGroup({
children,
...props
}: {
children: React.ReactNode | React.ReactNode[]
} & React.HTMLAttributes<HTMLDivElement>) {
return (
<div className={styles["button-group"]} {...props}>
{children}
</div>
)
}

View file

@ -9,14 +9,14 @@
border-radius: var(--radius); border-radius: var(--radius);
border: 1px solid var(--border); border: 1px solid var(--border);
padding: var(--gap-half) var(--gap); padding: var(--gap-half) var(--gap);
color: var(--darker-gray);
} }
.button:hover, .button:hover,
.button:focus { .button:focus {
outline: none;
color: var(--hover); color: var(--hover);
background: var(--hover-bg); background: var(--hover-bg);
border: var(--); border: 1px solid var(--darker-gray);
} }
.button[disabled] { .button[disabled] {

View file

@ -2,12 +2,16 @@ import styles from "./button.module.css"
import { forwardRef, Ref } from "react" import { forwardRef, Ref } from "react"
type Props = React.HTMLProps<HTMLButtonElement> & { type Props = React.HTMLProps<HTMLButtonElement> & {
children: React.ReactNode children?: React.ReactNode
buttonType?: "primary" | "secondary" buttonType?: "primary" | "secondary"
className?: string className?: string
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
iconRight?: React.ReactNode iconRight?: React.ReactNode
iconLeft?: React.ReactNode iconLeft?: React.ReactNode
height?: string | number
width?: string | number
padding?: string | number
margin?: string | number
} }
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
@ -22,6 +26,10 @@ const Button = forwardRef<HTMLButtonElement, Props>(
disabled = false, disabled = false,
iconRight, iconRight,
iconLeft, iconLeft,
height,
width,
padding,
margin,
...props ...props
}, },
ref ref
@ -32,15 +40,16 @@ const Button = forwardRef<HTMLButtonElement, Props>(
className={`${styles.button} ${styles[type]} ${className || ""}`} className={`${styles.button} ${styles[type]} ${className || ""}`}
disabled={disabled} disabled={disabled}
onClick={onClick} onClick={onClick}
style={{ height, width, margin, padding }}
{...props} {...props}
> >
{iconLeft && ( {children && iconLeft && (
<span className={`${styles.icon} ${styles.iconLeft}`}> <span className={`${styles.icon} ${styles.iconLeft}`}>
{iconLeft} {iconLeft}
</span> </span>
)} )}
{children} {children ? children : <span className={`${styles.icon}`}>{iconLeft || iconRight}</span>}
{iconRight && ( {children && iconRight && (
<span className={`${styles.icon} ${styles.iconRight}`}> <span className={`${styles.icon} ${styles.iconRight}`}>
{iconRight} {iconRight}
</span> </span>

View file

@ -18,18 +18,16 @@
background: var(--bg); background: var(--bg);
} }
.tabs .buttons > a:hover, .tabs button,
.tabs .buttons > button:hover { .tabs a {
color: var(--fg); color: var(--darker-gray);
box-shadow: inset 0 -1px 0 var(--fg); transition: color, box-shadow 0.2s ease-in-out;
transition: box-shadow 0.2s ease-in-out;
} }
.tabs a:active, .tabs .buttons a:hover,
.tabs a:focus, .tabs .buttons button:hover {
.tabs button:active, color: var(--fg);
.tabs button:focus { box-shadow: inset 0 -1px 0 var(--fg);
color: var(--darker-gray);
} }
.mobile { .mobile {

View file

@ -2,7 +2,6 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
width: 100%;
height: 100%; height: 100%;
font-size: 1rem; font-size: 1rem;
} }
@ -55,3 +54,9 @@
margin-bottom: var(--gap); margin-bottom: var(--gap);
} }
} }
.input:disabled {
background-color: var(--lighter-gray);
color: var(--fg);
cursor: not-allowed;
}

View file

@ -1,7 +1,5 @@
"use client" "use client"
import { Button, Input, Text } from "@geist-ui/core/dist"
import styles from "./post-list.module.css" import styles from "./post-list.module.css"
import ListItemSkeleton from "./list-item-skeleton" import ListItemSkeleton from "./list-item-skeleton"
import ListItem from "./list-item" import ListItem from "./list-item"
@ -9,6 +7,8 @@ import { ChangeEvent, useCallback, useEffect, useState } from "react"
import useDebounce from "@lib/hooks/use-debounce" import useDebounce from "@lib/hooks/use-debounce"
import Link from "@components/link" import Link from "@components/link"
import type { PostWithFiles } from "@lib/server/prisma" import type { PostWithFiles } from "@lib/server/prisma"
import Input from "@components/input"
import Button from "@components/button"
type Props = { type Props = {
initialPosts: string | PostWithFiles[] initialPosts: string | PostWithFiles[]
@ -106,13 +106,13 @@ const PostList = ({
<div className={styles.container}> <div className={styles.container}>
<div className={styles.searchContainer}> <div className={styles.searchContainer}>
<Input <Input
scale={3 / 2}
placeholder="Search..." placeholder="Search..."
onChange={handleSearchChange} onChange={handleSearchChange}
disabled={Boolean(!posts?.length)} disabled={Boolean(!posts?.length)}
style={{ maxWidth: 300 }}
/> />
</div> </div>
{!posts && <Text type="error">Failed to load.</Text>} {!posts && <p style={{color: 'var(--warning)'}}>Failed to load.</p>}
{!posts?.length && searching && ( {!posts?.length && searching && (
<ul> <ul>
<li> <li>
@ -124,13 +124,13 @@ const PostList = ({
</ul> </ul>
)} )}
{posts?.length === 0 && posts && ( {posts?.length === 0 && posts && (
<Text type="secondary"> <p>
No posts found. Create one{" "} No posts found. Create one{" "}
<Link colored href="/new"> <Link colored href="/new">
here here
</Link> </Link>
. .
</Text> </p>
)} )}
{posts?.length > 0 && ( {posts?.length > 0 && (
<div> <div>

View file

@ -1,5 +1,4 @@
import VisibilityBadge from "../badges/visibility-badge" import VisibilityBadge from "../badges/visibility-badge"
import { Divider, Button } from "@geist-ui/core/dist"
import FadeIn from "@components/fade-in" import FadeIn from "@components/fade-in"
import Trash from "@geist-ui/icons/trash" import Trash from "@geist-ui/icons/trash"
import ExpirationBadge from "@components/badges/expiration-badge" import ExpirationBadge from "@components/badges/expiration-badge"
@ -14,6 +13,7 @@ import type { File } from "@lib/server/prisma"
import Tooltip from "@components/tooltip" import Tooltip from "@components/tooltip"
import Badge from "@components/badges/badge" import Badge from "@components/badges/badge"
import Card from "@components/card" import Card from "@components/card"
import Button from "@components/button"
// TODO: isOwner should default to false so this can be used generically // TODO: isOwner should default to false so this can be used generically
const ListItem = ({ const ListItem = ({
@ -41,7 +41,7 @@ const ListItem = ({
<Card style={{ overflowY: "scroll" }}> <Card style={{ overflowY: "scroll" }}>
<> <>
<div className={styles.title}> <div className={styles.title}>
<h3 style={{ display: "inline-block" }}> <h3 style={{ display: "inline-block", margin: 0 }}>
<Link <Link
colored colored
style={{ marginRight: "var(--gap)" }} style={{ marginRight: "var(--gap)" }}
@ -55,17 +55,17 @@ const ListItem = ({
{post.parentId && ( {post.parentId && (
<Tooltip content={"View parent"}> <Tooltip content={"View parent"}>
<Button <Button
auto iconRight={<Parent />}
icon={<Parent />}
onClick={viewParentClick} onClick={viewParentClick}
height={38}
/> />
</Tooltip> </Tooltip>
)} )}
<Tooltip content={"Make a copy"}> <Tooltip content={"Make a copy"}>
<Button auto iconRight={<Edit />} onClick={editACopy} /> <Button iconRight={<Edit />} onClick={editACopy} height={38} />
</Tooltip> </Tooltip>
<Tooltip content={"Delete"}> <Tooltip content={"Delete"}>
<Button iconRight={<Trash />} onClick={deletePost} auto /> <Button iconRight={<Trash />} onClick={deletePost} height={38} />
</Tooltip> </Tooltip>
</span> </span>
)} )}
@ -86,7 +86,7 @@ const ListItem = ({
<ExpirationBadge postExpirationDate={post.expiresAt} /> <ExpirationBadge postExpirationDate={post.expiresAt} />
</div> </div>
</> </>
<Divider h="1px" my={2} /> <hr />
<> <>
{post?.files?.map((file: File) => { {post?.files?.map((file: File) => {
return ( return (

View file

@ -27,7 +27,6 @@
"jest": "^29.3.1", "jest": "^29.3.1",
"next": "13.0.3", "next": "13.0.3",
"next-auth": "^4.16.4", "next-auth": "^4.16.4",
"next-themes": "^0.2.1",
"rc-table": "7.24.1", "rc-table": "7.24.1",
"react": "18.2.0", "react": "18.2.0",
"react-datepicker": "4.8.0", "react-datepicker": "4.8.0",

View file

@ -25,7 +25,6 @@ specifiers:
katex: ^0.16.3 katex: ^0.16.3
next: 13.0.3 next: 13.0.3
next-auth: ^4.16.4 next-auth: ^4.16.4
next-themes: ^0.2.1
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
@ -58,7 +57,6 @@ dependencies:
jest: 29.3.1_@types+node@17.0.23 jest: 29.3.1_@types+node@17.0.23
next: 13.0.3_biqbaboplfbrettd7655fr4n2y next: 13.0.3_biqbaboplfbrettd7655fr4n2y
next-auth: 4.16.4_ogpkrxaz2lg6nectum6dl66tn4 next-auth: 4.16.4_ogpkrxaz2lg6nectum6dl66tn4
next-themes: 0.2.1_ogpkrxaz2lg6nectum6dl66tn4
rc-table: 7.24.1_biqbaboplfbrettd7655fr4n2y 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
@ -5117,18 +5115,6 @@ packages:
uuid: 8.3.2 uuid: 8.3.2
dev: false dev: false
/next-themes/0.2.1_ogpkrxaz2lg6nectum6dl66tn4:
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
peerDependencies:
next: '*'
react: '*'
react-dom: '*'
dependencies:
next: 13.0.3_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
/next-unused/0.0.6: /next-unused/0.0.6:
resolution: {integrity: sha512-dHFNNBanFq4wvYrULtsjfWyZ6BzOnr5VYI9EYMGAZYF2vkAhFpj2JOuT5Wu2o3LbFSG92PmAZnSUF/LstF82pA==} resolution: {integrity: sha512-dHFNNBanFq4wvYrULtsjfWyZ6BzOnr5VYI9EYMGAZYF2vkAhFpj2JOuT5Wu2o3LbFSG92PmAZnSUF/LstF82pA==}
hasBin: true hasBin: true