Skip to content

Commit

Permalink
🧩 ui Refactor IconBlock (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
steeeee0223 authored Nov 6, 2024
2 parents 3269f4e + 71c0507 commit 0a91fc2
Show file tree
Hide file tree
Showing 50 changed files with 702 additions and 719 deletions.
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,23 @@

### 🆕 Core Updates

- Update `turbo` task dependencies
- `repo` Update `turbo` task dependencies
- `worxpace`
- Fix: page routing & published page
- Fix: window origin
- Fix: workspace default icon
- Remove all `useTree`'s
- Remove API: `/api/documents`
- `ui`
- Rename `@swy/ui/custom` → `@swy/ui/shared`
- Move `@swy/ui/dnd` → `@swy/ui/shared`
- Move `@swy/ui/form` → `@swy/ui/shared`
- Rename & move packages
1. `@swy/ui/custom` → `@swy/ui/shared`
2. `@swy/ui/dnd` → `@swy/ui/shared`
3. `@swy/ui/form` → `@swy/ui/shared`
- Refactor components: `IconBlock`
- Rename: `IconPicker` → `EmojiPicker`
- Remove unused shadcn components
- Remove path alias
- Remove `stable-hash`
- 🆕 `notion`
- New package from `@swy/ui/notion`
- Mocking data at `@swy/notion/mock`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from "react";
import { Smile, X } from "lucide-react";

import { Button } from "@swy/ui/shadcn";
import { IconPicker } from "@swy/ui/shared";
import { EmojiPicker } from "@swy/ui/shared";

const CrudIcon = () => {
const [icon, setIcon] = useState("");
Expand All @@ -15,9 +15,9 @@ const CrudIcon = () => {
<div className="group relative pl-[54px]">
{icon ? (
<div className="group/icon flex items-center gap-x-2 pt-6">
<IconPicker onChange={onUpdateIcon}>
<EmojiPicker onChange={onUpdateIcon}>
<p className="text-6xl transition hover:opacity-75">{icon}</p>
</IconPicker>
</EmojiPicker>
<Button
onClick={onRemoveIcon}
className="rounded-full text-xs text-muted opacity-0 transition group-hover/icon:opacity-100 dark:text-muted-dark"
Expand All @@ -28,12 +28,12 @@ const CrudIcon = () => {
</div>
) : (
<div className="flex items-center gap-x-2 py-4">
<IconPicker asChild onChange={onUpdateIcon}>
<EmojiPicker asChild onChange={onUpdateIcon}>
<Button className="text-xs" variant="hint" size="sm">
<Smile className="mr-2 size-4" />
Add icon
</Button>
</IconPicker>
</EmojiPicker>
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import type { Meta, StoryObj } from "@storybook/react";

import { IconPicker } from "@swy/ui/shared";
import { EmojiPicker } from "@swy/ui/shared";

import CrudIcon from "./crud-icon";

const meta = {
title: "shared/Icon Picker",
component: IconPicker,
title: "shared/Emoji Picker",
component: EmojiPicker,
parameters: { layout: "centered" },
tags: ["autodocs"],
} satisfies Meta<typeof IconPicker>;
} satisfies Meta<typeof EmojiPicker>;
export default meta;

type Story = StoryObj<typeof meta>;
Expand Down
17 changes: 8 additions & 9 deletions apps/storybook/src/stories/shared/icon-block.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,27 @@ export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
export const Emoji: Story = {
args: {
size: "md",
defaultIcon: { type: "emoji", emoji: "🚀" },
icon: { type: "emoji", emoji: "🚀" },
},
};
export const NonEditable: Story = {
export const Lucide: Story = {
args: {
size: "md",
defaultIcon: { type: "emoji", emoji: "🚀" },
editable: false,
icon: { type: "lucide", name: "badge-euro", color: "#3e9392" },
},
};
export const Lucide: Story = {
export const ImageUrl: Story = {
args: {
size: "md",
defaultIcon: { type: "lucide", name: "badge-euro", color: "#3e9392" },
icon: { type: "file", url: "https://github.com/shadcn.png" },
},
};
export const ImageUrl: Story = {
export const Text: Story = {
args: {
size: "md",
defaultIcon: { type: "file", url: "https://github.com/shadcn.png" },
icon: { type: "text", text: "John" },
},
};
29 changes: 29 additions & 0 deletions apps/storybook/src/stories/shared/icon-menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useState } from "react";
import type { Meta, StoryObj } from "@storybook/react";

import { IconBlock, IconMenu, type IconInfo } from "@swy/ui/shared";

const meta = {
title: "shared/Icon Menu",
component: IconMenu,
tags: ["autodocs"],
} satisfies Meta<typeof IconMenu>;
export default meta;

type Story = StoryObj<typeof meta>;

const Template: Story["render"] = () => {
const [icon, setIcon] = useState<IconInfo>({ type: "text", text: "S" });
return (
<IconMenu
onSelect={setIcon}
onRemove={() => setIcon({ type: "text", text: " " })}
>
<IconBlock icon={icon} size="lg" />
</IconMenu>
);
};

export const Default: Story = {
render: Template,
};
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ export default function Page({ params: { token } }: Params) {
<>
<div className="flex w-[320px] flex-col items-center gap-4">
<IconBlock
editable={false}
defaultIcon={toIconInfo(data.workspace.icon)}
icon={toIconInfo(data.workspace.icon, data.workspace.name)}
size="lg"
/>
<h1 className="flex w-full flex-col text-center text-xl font-medium">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export default function Page() {
await createWorkspace({
createdBy: account.id,
name: `${name}'s Workspace`,
icon: { type: "lucide", src: "coffee", color: "#9F6B53" },
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,7 @@ const DocumentPage = ({ params: { documentId } }: Params) => {
<Cover preview unsplashAPIKey="" url={page.coverImage?.url ?? null} />
<div className="mx-auto md:max-w-3xl lg:max-w-4xl">
<div className="group relative pl-[54px]">
{page.icon && (
<IconBlock
defaultIcon={toIconInfo(page.icon)}
editable={false}
size="lg"
/>
)}
{page.icon && <IconBlock icon={toIconInfo(page.icon)} size="lg" />}
<div className="flex items-center gap-x-1 py-4 opacity-0 group-hover:opacity-100" />
<div className="break-words pb-[11.5px] text-5xl font-bold text-[#3F3F3F] outline-none dark:text-[#CFCFCF]">
{page.title}
Expand Down
18 changes: 13 additions & 5 deletions apps/worxpace/src/components/block-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
"use client";

import type { BlockNoteEditor, PartialBlock } from "@blocknote/core";
import type { PartialBlock } from "@blocknote/core";
import { BlockNoteView } from "@blocknote/mantine";
import { useCreateBlockNote } from "@blocknote/react";
import { useTheme } from "next-themes";

import { useTheme } from "@swy/ui/shadcn";

import { schema, type CustomEditor } from "~/components/blocknote";
import { CustomSlashMenu } from "./blocknote";

import "@blocknote/mantine/style.css";
import "@blocknote/core/fonts/inter.css";
Expand All @@ -24,7 +28,8 @@ const Editor = ({
}: EditorProps) => {
const { resolvedTheme } = useTheme();

const editor: BlockNoteEditor = useCreateBlockNote({
const editor: CustomEditor = useCreateBlockNote({
schema,
initialContent: initialContent
? (JSON.parse(initialContent) as PartialBlock[])
: undefined,
Expand All @@ -34,11 +39,14 @@ const Editor = ({
return (
<div>
<BlockNoteView
editable={editable}
editor={editor}
slashMenu={false}
editable={editable}
onChange={() => onChange?.(JSON.stringify(editor.document))}
theme={resolvedTheme === "dark" ? "dark" : "light"}
/>
>
<CustomSlashMenu editor={editor} />
</BlockNoteView>
</div>
);
};
Expand Down
50 changes: 28 additions & 22 deletions apps/worxpace/src/components/blocknote/callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
import { createReactBlockSpec } from "@blocknote/react";

import { cn } from "@swy/ui/lib";
import { IconBlock, type IconBlockProps, type IconInfo } from "@swy/ui/shared";
import { IconBlock, IconMenu, type IconInfo } from "@swy/ui/shared";

export interface CalloutBlockSpec {
type: "callout";
Expand All @@ -36,7 +36,7 @@ const getBlockIcon = (
case "file":
return { type, url };
default:
return { type: "emoji", emoji: " " };
return { type: "text", text: ">" };
}
};

Expand All @@ -61,30 +61,36 @@ export const Callout = createReactBlockSpec<
{
render: (props) => {
const backgroundColor = props.block.props.backgroundColor;
const iconBlockProps: IconBlockProps = {
defaultIcon: getBlockIcon(props.block),
onSelect: (icon) =>
props.editor.updateBlock(props.block, {
type: "callout",
props: icon,
}),
onRemove: () =>
props.editor.updateBlock(props.block, {
type: "callout",
props: {
type: "emoji",
name: "",
url: "",
emoji: "🚧",
color: "default",
},
}),
};

return (
<div className={cn("flex w-full p-4 pl-3", `bg-[${backgroundColor}]`)}>
<div className="mt-0.5 shrink-0">
<IconBlock {...iconBlockProps} />
{props.editor.isEditable ? (
<IconMenu
onSelect={(icon) =>
props.editor.updateBlock(props.block, {
type: "callout",
props: icon,
})
}
onRemove={() =>
props.editor.updateBlock(props.block, {
type: "callout",
props: {
type: "emoji",
name: "",
url: "",
emoji: "🚧",
color: "default",
},
})
}
>
<IconBlock icon={getBlockIcon(props.block)} />
</IconMenu>
) : (
<IconBlock icon={getBlockIcon(props.block)} />
)}
</div>
{/* Rich text field for user to type in */}
<div className="w-full min-w-0 pl-2" ref={props.contentRef} />
Expand Down
3 changes: 2 additions & 1 deletion apps/worxpace/src/components/collab-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { useEffect, useState } from "react";
import type { PartialBlock } from "@blocknote/core";
import { BlockNoteView } from "@blocknote/mantine";
import { useCreateBlockNote } from "@blocknote/react";
import { useTheme } from "next-themes";
import * as Y from "yjs";

import { useTheme } from "@swy/ui/shadcn";

import "@blocknote/core/fonts/inter.css";
import "@blocknote/react/style.css";
import "@blocknote/mantine/style.css";
Expand Down
2 changes: 1 addition & 1 deletion apps/worxpace/src/hooks/use-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const useSettings = (
onSuccess: ({ id, name, icon }) =>
dispatch({
type: "update",
payload: { id, name, icon: toIconInfo(icon) },
payload: { id, name, icon: toIconInfo(icon, name) },
}),
});
const { trigger: deleteAccount } = useSWRMutation(key, $delAccount, options);
Expand Down
13 changes: 7 additions & 6 deletions apps/worxpace/src/lib/data-transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ import type {
SettingsStore,
Workspace as WorkspaceData,
} from "@swy/notion";
import { generateDefaultIcon } from "@swy/notion";
import type { Account, Icon, Membership, Workspace } from "@swy/prisma";
import type { IconInfo, TreeItem } from "@swy/ui/shared";
import { Plan, Role } from "@swy/validators";

import type { AccountMemberships, WorkspaceMembership } from "./account";
import type { DetailedDocument } from "./types";

export function toIconInfo(icon?: Icon | null, defaultIcon?: string): IconInfo {
if (!icon) return generateDefaultIcon(defaultIcon);
export function toIconInfo(icon?: Icon | null, fallback?: string): IconInfo {
if (!icon) return { type: "text", text: fallback ?? " " };
const { type, src, color } = icon;
switch (type) {
case "emoji":
Expand All @@ -27,14 +26,16 @@ export function toIconInfo(icon?: Icon | null, defaultIcon?: string): IconInfo {
}
}

export function toIcon(info: IconInfo): Icon {
export function toIcon(info: IconInfo): Icon | null {
switch (info.type) {
case "emoji":
return { type: info.type, src: info.emoji, color: null };
case "lucide":
return { type: info.type, src: info.name, color: info.color ?? null };
case "file":
return { type: info.type, src: info.url, color: null };
default:
return null;
}
}

Expand Down Expand Up @@ -79,7 +80,7 @@ export function toWorkspaceList(
return workspaces.map(({ workspace }) => ({
id: workspace.id,
name: workspace.name,
icon: toIconInfo(workspace.icon),
icon: toIconInfo(workspace.icon, workspace.name),
role: Role[
workspace.memberships.find(
(membership) => membership.accountId === accountId,
Expand All @@ -104,7 +105,7 @@ export function toSettingsStore(
id: workspace.id,
name: workspace.name,
role: Role[membership!.role],
icon: toIconInfo(workspace.icon),
icon: toIconInfo(workspace.icon, workspace.name),
inviteLink: `${origin}/invite/${workspace.inviteToken}`,
domain: workspace.domain,
plan: Plan[workspace.plan],
Expand Down
Loading

0 comments on commit 0a91fc2

Please sign in to comment.