Skip to content

Commit

Permalink
Refactor NewNoteDialog
Browse files Browse the repository at this point in the history
  • Loading branch information
colebemis committed Dec 30, 2022
1 parent c31bca9 commit a4d289d
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 33 deletions.
16 changes: 5 additions & 11 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
"plugin:@typescript-eslint/recommended",
"plugin:storybook/recommended"
],
"ignorePatterns": [
"**/*.typegen.ts"
],
"ignorePatterns": ["**/*.typegen.ts"],
"overrides": [],
"parser": "@typescript-eslint/parser",
"parserOptions": {
Expand All @@ -26,26 +24,22 @@
"version": "detect"
}
},
"plugins": [
"react",
"react-hooks",
"jsx-a11y",
"@typescript-eslint"
],
"plugins": ["react", "react-hooks", "jsx-a11y", "@typescript-eslint"],
"rules": {
"react/react-in-jsx-scope": "off",
"react/display-name": "off",
"react/prop-types": "off",
"react/no-unescaped-entities": "off",
"react/jsx-no-constructed-context-values": "error",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"args": "none",
"ignoreRestSiblings": true,
"destructuredArrayIgnorePattern": "^_"
}
],
"@typescript-eslint/ban-ts-comment": "off"
]
}
}
157 changes: 150 additions & 7 deletions src/components/new-note-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@ import { IconButton } from "./button"
import { ComposeFillIcon24, ComposeIcon24 } from "./icons"
import { NoteForm } from "./note-form"

const NewNoteDialogContext = React.createContext<{
isOpen: boolean
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>
toggle: () => void
position: { x: number; y: number }
setPosition: React.Dispatch<React.SetStateAction<{ x: number; y: number }>>
editorRef: React.MutableRefObject<EditorView | undefined>
triggerRef: React.RefObject<HTMLButtonElement>
focusNoteCard: (id: string) => void
focusNoteEditor: () => void
focusPrevActiveElement: () => void
}>({
isOpen: false,
setIsOpen: () => {},
toggle: () => {},
position: { x: 0, y: 0 },
setPosition: () => {},
editorRef: { current: undefined },
triggerRef: React.createRef(),
focusNoteCard: () => {},
focusNoteEditor: () => {},
focusPrevActiveElement: () => {},
})

const DIALOG_WIDTH = 480

function initialPosition() {
Expand All @@ -17,20 +41,19 @@ function initialPosition() {
}
}

// TODO: Support mobile viewports
export function NewNoteDialog({ tooltipSide }: { tooltipSide?: TooltipContentProps["side"] }) {
function Provider({ children }: { children: React.ReactNode }) {
const triggerRef = React.useRef<HTMLButtonElement>(null)
const prevActiveElement = React.useRef<HTMLElement>()
const [isOpen, setIsOpen] = React.useState(false)
const [position, setPosition] = React.useState(() => initialPosition())
const codeMirrorViewRef = React.useRef<EditorView>()
const editorRef = React.useRef<EditorView>()

const focusPrevActiveElement = React.useCallback(() => {
prevActiveElement.current?.focus()
}, [])

const focusNoteEditor = React.useCallback(() => {
codeMirrorViewRef.current?.focus()
editorRef.current?.focus()
}, [])

const focusNoteCard = React.useCallback(
Expand Down Expand Up @@ -71,9 +94,105 @@ export function NewNoteDialog({ tooltipSide }: { tooltipSide?: TooltipContentPro
return () => window.removeEventListener("keydown", onKeyDown)
}, [toggle])

const contextValue = React.useMemo(
() => ({
isOpen,
setIsOpen,
toggle,
position,
setPosition,
editorRef,
triggerRef,
focusNoteCard,
focusNoteEditor,
focusPrevActiveElement,
}),
[
isOpen,
setIsOpen,
toggle,
position,
setPosition,
editorRef,
triggerRef,
focusNoteCard,
focusNoteEditor,
focusPrevActiveElement,
],
)

return (
<NewNoteDialogContext.Provider value={contextValue}>{children}</NewNoteDialogContext.Provider>
)
}

// TODO: Support mobile viewports
function Dialog({ tooltipSide }: { tooltipSide?: TooltipContentProps["side"] }) {
const {
isOpen,
setIsOpen,
position,
setPosition,
editorRef,
focusNoteCard,
focusNoteEditor,
focusPrevActiveElement,
} = React.useContext(NewNoteDialogContext)
// const triggerRef = React.useRef<HTMLButtonElement>(null)
// const prevActiveElement = React.useRef<HTMLElement>()
// const [isOpen, setIsOpen] = React.useState(false)
// const [position, setPosition] = React.useState(() => initialPosition())
// const codeMirrorViewRef = React.useRef<EditorView>()

// const focusPrevActiveElement = React.useCallback(() => {
// prevActiveElement.current?.focus()
// }, [])

// const focusNoteEditor = React.useCallback(() => {
// codeMirrorViewRef.current?.focus()
// }, [])

// const focusNoteCard = React.useCallback(
// (id: string) => {
// const noteElement = document.querySelector(`[data-note-id="${id}"]`)
// if (noteElement instanceof HTMLElement) {
// noteElement.focus()
// } else {
// // Fallback to previous active element
// focusPrevActiveElement()
// }
// },
// [focusPrevActiveElement],
// )

// const toggle = React.useCallback(() => {
// if (isOpen) {
// setIsOpen(false)
// focusPrevActiveElement()
// } else {
// prevActiveElement.current = document.activeElement as HTMLElement
// setIsOpen(true)
// setPosition(initialPosition())
// setTimeout(() => focusNoteEditor())
// }
// }, [isOpen, focusPrevActiveElement, focusNoteEditor])

// React.useEffect(() => {
// function onKeyDown(event: KeyboardEvent) {
// // Toggle with `command + i`
// if (event.key === "i" && event.metaKey) {
// toggle()
// event.preventDefault()
// }
// }

// window.addEventListener("keydown", onKeyDown)
// return () => window.removeEventListener("keydown", onKeyDown)
// }, [toggle])

return (
<>
<IconButton
{/* <IconButton
ref={triggerRef}
className={clsx("w-full", isOpen && "text-text")}
aria-label="New note"
Expand All @@ -82,7 +201,7 @@ export function NewNoteDialog({ tooltipSide }: { tooltipSide?: TooltipContentPro
onClick={toggle}
>
{isOpen ? <ComposeFillIcon24 /> : <ComposeIcon24 />}
</IconButton>
</IconButton> */}
{isOpen ? (
<Portal.Root>
<DraggableCore
Expand All @@ -104,7 +223,7 @@ export function NewNoteDialog({ tooltipSide }: { tooltipSide?: TooltipContentPro
<NoteForm
elevation={2}
editorMinHeight={160}
codeMirrorViewRef={codeMirrorViewRef}
codeMirrorViewRef={editorRef}
onSubmit={({ id }) => {
setIsOpen(false)
setTimeout(() => focusNoteCard(id))
Expand All @@ -121,3 +240,27 @@ export function NewNoteDialog({ tooltipSide }: { tooltipSide?: TooltipContentPro
</>
)
}

function Trigger({
className,
tooltipSide,
}: {
className?: string
tooltipSide?: TooltipContentProps["side"]
}) {
const { isOpen, toggle, triggerRef } = React.useContext(NewNoteDialogContext)
return (
<IconButton
ref={triggerRef}
className={clsx(isOpen && "text-text", className)}
aria-label="New note"
shortcut={["⌘", "I"]}
tooltipSide={tooltipSide}
onClick={toggle}
>
{isOpen ? <ComposeFillIcon24 /> : <ComposeIcon24 />}
</IconButton>
)
}

export const NewNoteDialog = Object.assign(Dialog, { Provider, Trigger })
5 changes: 4 additions & 1 deletion src/components/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ function NavBar({ direction }: { direction: "horizontal" | "vertical" }) {
</NavLink>
</li>
<li className={clsx(direction === "vertical" ? "flex-grow-0" : "flex-grow")}>
<NewNoteDialog tooltipSide={direction === "vertical" ? "right" : "top"} />
<NewNoteDialog.Trigger
className="w-full"
tooltipSide={direction === "vertical" ? "right" : "top"}
/>
</li>
<li className={clsx(direction === "vertical" ? "mt-auto flex-grow-0" : "flex-grow")}>
<DropdownMenu modal={false}>
Expand Down
32 changes: 18 additions & 14 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Tooltip from "@radix-ui/react-tooltip"
import React from "react"
import ReactDOM from "react-dom/client"
import { BrowserRouter, Route, Routes } from "react-router-dom"
import { NewNoteDialog } from "./components/new-note-dialog"
import { PermissionDialog } from "./components/permission-dialog"
import { Root } from "./components/root"
import { GlobalStateProvider } from "./global-state"
Expand All @@ -18,20 +19,23 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<GlobalStateProvider>
<Tooltip.Provider>
<PermissionDialog />
<BrowserRouter>
<Routes>
<Route path="/file" element={<FilePage />} />
<Route path="/" element={<Root />}>
<Route index element={<NotesPage />} />
<Route path=":id" element={<NotePage />} />
<Route path="tags" element={<TagsPage />} />
<Route path="tags/:name" element={<TagPage />} />
<Route path="dates/:date" element={<DatePage />} />
<Route path="graph" element={<GraphPage />} />
</Route>
</Routes>
</BrowserRouter>
<NewNoteDialog.Provider>
<NewNoteDialog />
<PermissionDialog />
<BrowserRouter>
<Routes>
<Route path="/file" element={<FilePage />} />
<Route path="/" element={<Root />}>
<Route index element={<NotesPage />} />
<Route path=":id" element={<NotePage />} />
<Route path="tags" element={<TagsPage />} />
<Route path="tags/:name" element={<TagPage />} />
<Route path="dates/:date" element={<DatePage />} />
<Route path="graph" element={<GraphPage />} />
</Route>
</Routes>
</BrowserRouter>
</NewNoteDialog.Provider>
</Tooltip.Provider>
</GlobalStateProvider>
</React.StrictMode>,
Expand Down

0 comments on commit a4d289d

Please sign in to comment.