diff --git a/client/components/Link.tsx b/client/components/Link.tsx index 9f2e376..e96f89b 100644 --- a/client/components/Link.tsx +++ b/client/components/Link.tsx @@ -1,5 +1,5 @@ import type { LinkProps } from "@geist-ui/core" -import GeistLink from "@geist-ui/core/dist/link" +import { Link as GeistLink } from "@geist-ui/core" import { useRouter } from "next/router"; const Link = (props: LinkProps) => { diff --git a/client/components/auth/index.tsx b/client/components/auth/index.tsx index 43b1d28..5f48f3c 100644 --- a/client/components/auth/index.tsx +++ b/client/components/auth/index.tsx @@ -4,6 +4,7 @@ import styles from './auth.module.css' import { useRouter } from 'next/router' import Link from '../Link' import Cookies from "js-cookie"; +import useSignedIn from '@lib/hooks/use-signed-in' const NO_EMPTY_SPACE_REGEX = /^\S*$/; const ERROR_MESSAGE = "Provide a non empty username and a password with at least 6 characters"; @@ -17,7 +18,7 @@ const Auth = ({ page }: { page: "signup" | "signin" }) => { const [errorMsg, setErrorMsg] = useState(''); const [requiresServerPassword, setRequiresServerPassword] = useState(false); const signingIn = page === 'signin' - + const { signin } = useSignedIn(); useEffect(() => { async function fetchRequiresPass() { if (!signingIn) { @@ -37,7 +38,7 @@ const Auth = ({ page }: { page: "signup" | "signin" }) => { const handleJson = (json: any) => { - Cookies.set('drift-token', json.token); + signin(json.token) Cookies.set('drift-userid', json.userId); router.push('/') @@ -65,7 +66,6 @@ const Auth = ({ page }: { page: "signup" | "signin" }) => { handleJson(json) } catch (err: any) { - console.log(err) setErrorMsg(err.message ?? "Something went wrong") } } diff --git a/client/components/button-dropdown/dropdown.module.css b/client/components/button-dropdown/dropdown.module.css new file mode 100644 index 0000000..dd03da0 --- /dev/null +++ b/client/components/button-dropdown/dropdown.module.css @@ -0,0 +1,26 @@ +.main { + margin-bottom: 2rem; +} + +.dropdown { + position: relative; + display: inline-block; + vertical-align: middle; + cursor: pointer; + padding: 0; + border: 0; + background: transparent; +} + +.dropdownContent { + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; + box-shadow: 0 3px 12px rgba(0, 0, 0, 0.15); +} + +.icon { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/client/components/button-dropdown/index.tsx b/client/components/button-dropdown/index.tsx new file mode 100644 index 0000000..7000059 --- /dev/null +++ b/client/components/button-dropdown/index.tsx @@ -0,0 +1,116 @@ +import Button from "@components/button" +import React, { useCallback, useEffect } from "react" +import { useState } from "react" +import styles from './dropdown.module.css' +import DownIcon from '@geist-ui/icons/arrowDown' +type Props = { + type?: "primary" | "secondary" + loading?: boolean + disabled?: boolean + className?: string + iconHeight?: number +} + +type Attrs = Omit, keyof Props> +type ButtonDropdownProps = Props & Attrs + +const ButtonDropdown: React.FC> = ({ + type, + className, + disabled, + loading, + iconHeight = 24, + ...props +}) => { + const [visible, setVisible] = useState(false) + const [dropdown, setDropdown] = useState(null) + + const onClick = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + setVisible(!visible) + } + + const onBlur = () => { + setVisible(false) + } + + const onMouseDown = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + } + + const onMouseUp = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + } + + const onMouseLeave = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + setVisible(false) + } + + const onKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Escape") { + setVisible(false) + } + } + + const onClickOutside = useCallback(() => (e: React.MouseEvent) => { + if (dropdown && !dropdown.contains(e.target as Node)) { + setVisible(false) + } + }, [dropdown]) + + useEffect(() => { + if (visible) { + document.addEventListener("mousedown", onClickOutside) + } else { + document.removeEventListener("mousedown", onClickOutside) + } + + return () => { + document.removeEventListener("mousedown", onClickOutside) + } + }, [visible, onClickOutside]) + + if (!Array.isArray(props.children)) { + return null + } + + return ( +
+
+ {props.children[0]} + +
+ { + visible && ( +
+
+ {props.children.slice(1)} + +
+
+ ) + } +
+ ) + + + +} + +export default ButtonDropdown \ No newline at end of file diff --git a/client/components/button/button.module.css b/client/components/button/button.module.css new file mode 100644 index 0000000..381f396 --- /dev/null +++ b/client/components/button/button.module.css @@ -0,0 +1,62 @@ +.button { + user-select: none; + cursor: pointer; + border-radius: var(--radius); + color: var(--input-fg); + font-weight: 400; + font-size: 1.1rem; + background: var(--input-bg); + border: var(--input-border); + height: 2rem; + display: flex; + align-items: center; + padding: var(--gap-quarter) var(--gap-half); + transition: background-color var(--transition), color var(--transition); + width: 100%; + height: var(--input-height); +} + +/* +--input-height: 2.5rem; +--input-border: 1px solid var(--light-gray); +--input-border-focus: 1px solid var(--gray); +--input-border-error: 1px solid var(--red); +--input-bg: var(--bg); +--input-fg: var(--fg); +--input-placeholder-fg: var(--light-gray); */ + +.button:hover, +.button:focus { + outline: none; + background: var(--input-bg-hover); + border: var(--input-border-focus); +} + +.button[disabled] { + cursor: not-allowed; + background: var(--lighter-gray); + color: var(--gray); +} + +.secondary { + background: var(--bg); + color: var(--fg); +} + +/* +--bg: #131415; + --fg: #fafbfc; + --gray: #666; + --light-gray: #444; + --lighter-gray: #222; + --lightest-gray: #1a1a1a; + --article-color: #eaeaea; + --header-bg: rgba(19, 20, 21, 0.45); + --gray-alpha: rgba(255, 255, 255, 0.5); + --selection: rgba(255, 255, 255, 0.99); + */ + +.primary { + background: var(--fg); + color: var(--bg); +} diff --git a/client/components/button/index.tsx b/client/components/button/index.tsx new file mode 100644 index 0000000..0e85a79 --- /dev/null +++ b/client/components/button/index.tsx @@ -0,0 +1,28 @@ +import styles from './button.module.css' +import { forwardRef, Ref } from 'react' + +type Props = React.HTMLProps & { + children: React.ReactNode + buttonType?: 'primary' | 'secondary' + className?: string + onClick?: (e: React.MouseEvent) => void +} + +// eslint-disable-next-line react/display-name +const Button = forwardRef( + ({ children, onClick, className, buttonType = 'primary', type = 'button', disabled = false, ...props }, ref) => { + return ( + + ) + } +) + +export default Button diff --git a/client/components/document/formatting-icons/index.tsx b/client/components/document/formatting-icons/index.tsx index 279162d..1ef79c8 100644 --- a/client/components/document/formatting-icons/index.tsx +++ b/client/components/document/formatting-icons/index.tsx @@ -1,11 +1,10 @@ -import ButtonGroup from "@geist-ui/core/dist/button-group" -import Button from "@geist-ui/core/dist/button" import Bold from '@geist-ui/icons/bold' import Italic from '@geist-ui/icons/italic' import Link from '@geist-ui/icons/link' import ImageIcon from '@geist-ui/icons/image' import { RefObject, useCallback, useMemo } from "react" import styles from '../document.module.css' +import { Button, ButtonGroup } from "@geist-ui/core" // TODO: clean up diff --git a/client/components/document/index.tsx b/client/components/document/index.tsx index b34f1b1..d8200c8 100644 --- a/client/components/document/index.tsx +++ b/client/components/document/index.tsx @@ -1,11 +1,4 @@ -import Button from "@geist-ui/core/dist/button" -import Card from "@geist-ui/core/dist/card" -import ButtonGroup from "@geist-ui/core/dist/button-group" -import Input from "@geist-ui/core/dist/input" -import Spacer from "@geist-ui/core/dist/spacer" -import Tabs from "@geist-ui/core/dist/tabs" -import Textarea from "@geist-ui/core/dist/textarea" -import Tooltip from "@geist-ui/core/dist/tooltip" + import { ChangeEvent, memo, useCallback, useMemo, useRef, useState } from "react" import styles from './document.module.css' @@ -15,9 +8,8 @@ import ExternalLink from '@geist-ui/icons/externalLink' import FormattingIcons from "./formatting-icons" import Skeleton from "react-loading-skeleton" -import dynamic from "next/dynamic"; - -const MarkdownPreview = dynamic(() => import("../preview")) +import { Button, ButtonGroup, Card, Input, Spacer, Tabs, Textarea, Tooltip } from "@geist-ui/core" +import Preview from "@components/preview" // import Link from "next/link" type Props = { @@ -74,13 +66,6 @@ const Document = ({ remove, editable, title, content, setTitle, setContent, init setTab(newTab as 'edit' | 'preview') } - const getType = useCallback(() => { - if (!title) return - const pathParts = title.split(".") - const language = pathParts.length > 1 ? pathParts[pathParts.length - 1] : "" - return language - }, [title]) - const onTitleChange = useCallback((event: ChangeEvent) => setTitle ? setTitle(event.target.value) : null, [setTitle]) const removeFile = useCallback(() => (remove?: () => void) => { @@ -140,14 +125,14 @@ const Document = ({ remove, editable, title, content, setTitle, setContent, init
{tab === 'edit' && editable && } - {rawLink && } + {rawLink && id && } {/* */} -
+