Skip to content

Commit

Permalink
🧩 ui Create reusable modal provider (#43)
Browse files Browse the repository at this point in the history
* 🧩 `ui` Add `ModalProvider` with custom hook

* 🔧 `worxpace` Migrate `ModalProvider` to new one & fix related components
  • Loading branch information
steeeee0223 committed Jul 18, 2024
1 parent 0f2aaa0 commit b7a0e39
Show file tree
Hide file tree
Showing 19 changed files with 250 additions and 104 deletions.
24 changes: 24 additions & 0 deletions apps/storybook/src/stories/custom/modal-provider/_components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useModal } from "@acme/ui/custom";
import { Button, Dialog, DialogClose, DialogContent } from "@acme/ui/shadcn";

export const Modal = () => {
const { isOpen, setClose } = useModal();
return (
<Dialog open={isOpen} onOpenChange={setClose}>
<DialogContent className="flex items-center text-2xl font-bold">
Modal
<DialogClose />
</DialogContent>
</Dialog>
);
};

export const Trigger = () => {
const { setOpen } = useModal();
const handleClick = () => void setOpen(<Modal />);
return (
<Button onClick={handleClick} variant="outline">
Open
</Button>
);
};
21 changes: 21 additions & 0 deletions apps/storybook/src/stories/custom/modal-provider/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Meta, StoryObj } from "@storybook/react";

import { ModalProvider } from "@acme/ui/custom";

import { Trigger } from "./_components";

const meta = {
title: "custom/Modal Provider",
component: ModalProvider,
parameters: { layout: "centered" },
tags: ["autodocs"],
} satisfies Meta<typeof ModalProvider>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
children: <Trigger />,
},
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
"use client";

import { useEffect, type PropsWithChildren } from "react";
import { useParams, usePathname, useRouter } from "next/navigation";
import { useParams, usePathname } from "next/navigation";

import { TreeProvider } from "@acme/ui/custom";
import { useNavControl } from "@acme/ui/hooks";

import { DocHeaderSkeleton, Room } from "~/components";
import { usePlatform } from "~/hooks/use-platform";
import Navbar, { NavbarSkeleton } from "./navbar";
import SearchCommand from "./search-command";
import { Sidebar } from "./sidebar";

const DocsProvider = ({ children }: PropsWithChildren) => {
const router = useRouter();
const { toToolsPage } = usePlatform();
const pathname = usePathname();
const params = useParams<{
documentId?: string;
Expand Down Expand Up @@ -51,11 +51,6 @@ const DocsProvider = ({ children }: PropsWithChildren) => {
"trash:kanban",
"trash:whiteboard",
];
const onClickItem = (id: string, group: string | null) => {
if (group === "document") router.push(`/documents/${id}`);
if (group === "kanban") router.push(`/kanban/${id}`);
if (group === "whiteboard") router.push(`/whiteboard/${id}`);
};
const isItemActive = (id: string, group: string | null) => {
if (group === "document") return params.documentId === id;
if (group === "kanban") return params.boardId === id;
Expand All @@ -67,7 +62,8 @@ const DocsProvider = ({ children }: PropsWithChildren) => {
<TreeProvider
className="flex h-full dark:bg-[#1F1F1F]"
groups={groups}
onClickItem={onClickItem}
onClickItem={toToolsPage}
// onClickItem={onClickItem}
isItemActive={isItemActive}
>
<Sidebar
Expand All @@ -87,10 +83,7 @@ const DocsProvider = ({ children }: PropsWithChildren) => {
isMobile={isMobile}
onResetWidth={resetWidth}
/>
<main className="h-full flex-1 overflow-y-auto">
<SearchCommand />
{children}
</main>
<main className="h-full flex-1 overflow-y-auto">{children}</main>
</Room>
</TreeProvider>
);
Expand Down
37 changes: 29 additions & 8 deletions apps/worxpace/src/app/(platform)/(tools)/_components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,28 @@
/* eslint-disable jsx-a11y/interactive-supports-focus */
"use client";

import { forwardRef, type ForwardedRef, type MouseEventHandler } from "react";
import {
forwardRef,
useCallback,
useEffect,
type ForwardedRef,
type MouseEventHandler,
} from "react";
import { useRouter } from "next/navigation";
import { useAuth, useOrganizationList } from "@clerk/nextjs";
import { ChevronsLeft } from "lucide-react";
import { toast } from "sonner";
import useSWRMutation from "swr/mutation";

import { CRUDItem as Item, useTree } from "@acme/ui/custom";
import { CRUDItem as Item, useModal, useTree } from "@acme/ui/custom";
import { cn } from "@acme/ui/lib";
import { WorkspaceSwitcher } from "@acme/ui/notion";
import { Popover, PopoverContent, PopoverTrigger } from "@acme/ui/shadcn";

import { archiveDocument, createDocument } from "~/actions";
import { SearchCommand, SettingsModal } from "~/components/modal";
import { theme } from "~/constants/theme";
import { useClient, usePages, useSearch, useSettings } from "~/hooks";
import { useClient, usePages } from "~/hooks";
import { toIconInfo } from "~/lib";
import DocList from "./doc-list";
import TrashBox from "./trash-box";
Expand Down Expand Up @@ -55,9 +62,11 @@ export const Sidebar = forwardRef(function Sidebar(
};
const handleLogout = () =>
signOut(() => router.push("/select-role")).catch((e) => console.log(e));
/** Search & Settings */
const search = useSearch();
const settings = useSettings();
/** Modals */
const { setOpen } = useModal();
const handleSettings = () => void setOpen(<SettingsModal />);
// eslint-disable-next-line react-hooks/exhaustive-deps
const handleSearch = useCallback(() => void setOpen(<SearchCommand />), []);
/** Docs */
const { dispatch, onClickItem } = useTree();
const onError = (e: Error) => toast.error(e.message);
Expand Down Expand Up @@ -98,6 +107,18 @@ export const Sidebar = forwardRef(function Sidebar(
},
);

useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
handleSearch();
}
};

addEventListener("keydown", down);
return () => removeEventListener("keydown", down);
}, [handleSearch]);

return (
<aside
ref={ref}
Expand All @@ -123,13 +144,13 @@ export const Sidebar = forwardRef(function Sidebar(
<Item
label="Search"
icon={{ type: "lucide", name: "search" }}
onClick={search.onOpen}
onClick={handleSearch}
shortcut="⌘K"
/>
<Item
label="Settings"
icon={{ type: "lucide", name: "settings" }}
onClick={settings.onOpen}
onClick={handleSettings}
shortcut="⌘,"
/>
<Item
Expand Down
7 changes: 4 additions & 3 deletions apps/worxpace/src/app/(platform)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import type { PropsWithChildren } from "react";
import { ClerkProvider } from "@clerk/nextjs";
import { Toaster } from "sonner";

import { ModalProvider, WorxpaceProvider } from "~/components/providers";
import { ModalProvider } from "@acme/ui/custom";

import { WorxpaceProvider } from "~/components/providers";
import { EdgeStoreProvider } from "~/hooks";

export default function PlatformLayout({ children }: PropsWithChildren) {
Expand All @@ -11,8 +13,7 @@ export default function PlatformLayout({ children }: PropsWithChildren) {
<EdgeStoreProvider>
<Toaster />
<WorxpaceProvider>
<ModalProvider />
{children}
<ModalProvider>{children}</ModalProvider>
</WorxpaceProvider>
</EdgeStoreProvider>
</ClerkProvider>
Expand Down
1 change: 1 addition & 0 deletions apps/worxpace/src/components/modal/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./confirm-modal";
export { default as SearchCommand } from "./search-command";
export * from "./settings-modal";
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"use client";

import { useEffect, useState } from "react";
import { toast } from "sonner";
import useSWR from "swr";
import { stableHash } from "swr/_internal";

import { type Document } from "@acme/prisma";
import { IconBlock, useTree } from "@acme/ui/custom";
import { IconBlock, useModal } from "@acme/ui/custom";
import {
CommandDialog,
CommandEmpty,
Expand All @@ -16,49 +15,33 @@ import {
CommandList,
} from "@acme/ui/shadcn";

import { useClient, useSearch } from "~/hooks";
import { useClient } from "~/hooks";
import { usePlatform } from "~/hooks/use-platform";
import { fetchUrl, toIconInfo } from "~/lib";

const SearchCommand = () => {
/** Auth */
const { workspace: name, userId, workspaceId } = useClient();
/** Search */
const { toggle, isOpen, onClose } = useSearch();
const { isOpen, setClose } = useModal();
/** Select */
const { onClickItem } = useTree();
const { toToolsPage } = usePlatform();
const handleSelect = (id: string, group: string | null) => {
onClickItem?.(id, group);
onClose();
toToolsPage(id, group);
setClose();
};
/** Mount */
const [isMounted, setIsMounted] = useState(false);
const { data: documents } = useSWR<Document[], Error>(
userId && isMounted ? `doc:${workspaceId}:search` : null,
userId && isOpen ? `doc:${workspaceId}:search` : null,
(_key) => fetchUrl(`/api/documents?archived=${false}`),
{ onError: (e) => toast.error(e.message) },
);
useEffect(() => {
setIsMounted(true);
}, []);
/** Keyboard event */
useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
toggle();
}
};

addEventListener("keydown", down);
return () => removeEventListener("keydown", down);
}, [toggle]);

if (!isMounted) return null;
return (
<CommandDialog
className="z-[99999] pb-1"
open={isOpen}
onOpenChange={onClose}
onOpenChange={setClose}
>
<CommandInput placeholder={`Search ${name}'s WorXpace...`} />
<CommandList>
Expand Down
7 changes: 3 additions & 4 deletions apps/worxpace/src/components/modal/settings-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useModal } from "@acme/ui/custom";
import {
Dialog,
DialogContent,
Expand All @@ -8,13 +9,11 @@ import {
ThemeToggle,
} from "@acme/ui/shadcn";

import { useSettings } from "~/hooks";

export const SettingsModal = () => {
const settings = useSettings();
const { isOpen, setClose } = useModal();

return (
<Dialog open={settings.isOpen} onOpenChange={settings.onClose}>
<Dialog open={isOpen} onOpenChange={setClose}>
<DialogContent className="z-[99999]">
<DialogHeader className="border-b pb-3">
<h2 className="text-lg font-medium">My settings</h2>
Expand Down
1 change: 0 additions & 1 deletion apps/worxpace/src/components/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "./modal-provider";
export * from "./worxpace-provider";
19 changes: 0 additions & 19 deletions apps/worxpace/src/components/providers/modal-provider.tsx

This file was deleted.

3 changes: 1 addition & 2 deletions apps/worxpace/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ export * from "./use-client";
export * from "./use-edgestore";
export * from "./use-history";
export * from "./use-page";
export * from "./use-search";
export * from "./use-settings";
export * from "./use-platform";
13 changes: 13 additions & 0 deletions apps/worxpace/src/hooks/use-platform.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use client";

import { useRouter } from "next/navigation";

export const usePlatform = () => {
const router = useRouter();
const toToolsPage = (id: string, group: string | null) => {
if (group === "document") router.push(`/documents/${id}`);
if (group === "kanban") router.push(`/kanban/${id}`);
if (group === "whiteboard") router.push(`/whiteboard/${id}`);
};
return { toToolsPage };
};
15 changes: 0 additions & 15 deletions apps/worxpace/src/hooks/use-search.ts

This file was deleted.

13 changes: 0 additions & 13 deletions apps/worxpace/src/hooks/use-settings.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/ui/src/components/custom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from "./kanban";
export * from "./hint";
export * from "./icon-block";
export * from "./icon-picker";
export * from "./modal-provider";
export * from "./spinner";
export * from "./tree";
export * from "./unsplash";
Loading

0 comments on commit b7a0e39

Please sign in to comment.