Skip to content

Commit

Permalink
🔄 Merge pull request #1 from steeeee0223/feature/ui
Browse files Browse the repository at this point in the history
Merge from Feature/UI: add custom components
  • Loading branch information
steeeee0223 authored Jan 15, 2024
2 parents fe67fbc + 123db51 commit 9c2143c
Show file tree
Hide file tree
Showing 79 changed files with 4,205 additions and 455 deletions.
5 changes: 4 additions & 1 deletion apps/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwind-merge": "^2.2.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"uuid": "^9.0.1"
},
"devDependencies": {
"@acme/eslint-config": "workspace:^0.2.0",
Expand All @@ -48,8 +49,10 @@
"@types/node": "^20.10.6",
"@types/react": "^18.2.46",
"@types/react-dom": "^18.2.18",
"@types/uuid": "^9.0.7",
"autoprefixer": "^10.4.16",
"eslint": "^8.56.0",
"eslint-plugin-storybook": "^0.6.15",
"postcss": "^8.4.32",
"prettier": "^3.1.1",
"storybook": "^7.6.6",
Expand Down
103 changes: 51 additions & 52 deletions apps/storybook/src/stories/blocknote/block-editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,79 @@
"use client";

import { useTheme } from "next-themes";
import { HandIcon } from "lucide-react";
import { BlockNoteEditor } from "@blocknote/core";
import type { BlockNoteEditor } from "@blocknote/core";
import type { ReactSlashMenuItem } from "@blocknote/react";
import {
BlockNoteView,
ReactSlashMenuItem,
getDefaultReactSlashMenuItems,
useBlockNote,
BlockNoteView,
getDefaultReactSlashMenuItems,
useBlockNote,
} from "@blocknote/react";
import { HandIcon } from "lucide-react";
import { useTheme } from "next-themes";

import "@blocknote/core/style.css";

export interface BlockEditorProps {
initialContent?: string | null;
editable?: boolean;
className?: string;
onChange?: (value: string) => void;
onUpload?: (file: File) => Promise<string>;
initialContent?: string | null;
editable?: boolean;
className?: string;
onChange?: (value: string) => void;
onUpload?: (file: File) => Promise<string>;
}

// Command to insert "Hello World" in bold in a new block below.
const insertHelloWorld = (editor: BlockNoteEditor) => {
// Block that the text cursor is currently in.
const currentBlock = editor.getTextCursorPosition().block;
// Block that the text cursor is currently in.
const currentBlock = editor.getTextCursorPosition().block;

// New block we want to insert.
const helloWorldBlock = {
type: "paragraph",
content: [
{ type: "text", text: "Hello World", styles: { bold: true } },
],
};
// New block we want to insert.
const helloWorldBlock = {
type: "paragraph",
content: [{ type: "text", text: "Hello World", styles: { bold: true } }],
};

// Inserting the new block after the current one.
editor.insertBlocks([helloWorldBlock], currentBlock, "after");
// Inserting the new block after the current one.
editor.insertBlocks([helloWorldBlock], currentBlock, "after");
};

// Custom Slash Menu item which executes the above function.
const insertHelloWorldItem: ReactSlashMenuItem = {
name: "Insert Hello World",
execute: insertHelloWorld,
aliases: ["helloworld", "hw"],
group: "Other",
icon: <HandIcon size={18} />,
hint: "Used to insert a block with 'Hello World' below.",
name: "Insert Hello World",
execute: insertHelloWorld,
aliases: ["helloworld", "hw"],
group: "Other",
icon: <HandIcon size={18} />,
hint: "Used to insert a block with 'Hello World' below.",
};

// List containing all default Slash Menu Items, as well as our custom one.
const customSlashMenuItemList = [
...getDefaultReactSlashMenuItems(),
insertHelloWorldItem,
...getDefaultReactSlashMenuItems(),
insertHelloWorldItem,
];

export const BlockEditor = ({
onChange,
onUpload,
initialContent,
editable,
className,
onChange,
onUpload,
initialContent,
editable,
className,
}: BlockEditorProps) => {
const { resolvedTheme } = useTheme();
const { resolvedTheme } = useTheme();

const editor: BlockNoteEditor = useBlockNote({
editable,
initialContent: initialContent ? JSON.parse(initialContent) : undefined,
onEditorContentChange: (editor) =>
onChange?.(JSON.stringify(editor.topLevelBlocks, null, 0)),
uploadFile: onUpload,
slashMenuItems: customSlashMenuItemList,
});
const editor: BlockNoteEditor = useBlockNote({
editable,
initialContent: initialContent ? JSON.parse(initialContent) : undefined,
onEditorContentChange: (editor) =>
onChange?.(JSON.stringify(editor.topLevelBlocks, null, 0)),
uploadFile: onUpload,
slashMenuItems: customSlashMenuItemList,
});

return (
<BlockNoteView
className={className}
editor={editor}
theme={resolvedTheme === "dark" ? "dark" : "light"}
/>
);
return (
<BlockNoteView
className={className}
editor={editor}
theme={resolvedTheme === "dark" ? "dark" : "light"}
/>
);
};
25 changes: 25 additions & 0 deletions apps/storybook/src/stories/custom/activity-item.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Meta, StoryObj } from "@storybook/react";

import { ActivityItem } from "@acme/ui/components";

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

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
data: {
username: "John Doe",
avatar: "https://github.com/shadcn.png",
action: "CREATE",
entity: { entityId: "1", title: "README.md", type: "note" },
createdAt: new Date(),
},
},
};
30 changes: 30 additions & 0 deletions apps/storybook/src/stories/custom/cover-picker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Meta, StoryObj } from "@storybook/react";
import { ImageIcon } from "lucide-react";

import { Button, CoverPicker } from "@acme/ui/components";

const meta = {
title: "custom/Cover Picker",
component: CoverPicker,
tags: ["autodocs"],
} satisfies Meta<typeof CoverPicker>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
asChild: true,
onUploadChange: async (file) => console.log(`Uploading file: ${file.name}`),
onRemove: async () => console.log(`Removing file`),
onUnsplash: async (url) => console.log(`Uploading unsplash image: ${url}`),
children: (
<>
<Button variant="outline">
<ImageIcon className="mr-2 h-4 w-4" />
Change cover
</Button>
</>
),
},
};
22 changes: 22 additions & 0 deletions apps/storybook/src/stories/custom/cover.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from "@storybook/react";
import image from "@/stories/assets/addon-library.png";

import { Cover } from "@acme/ui/components";

const meta = {
title: "custom/Cover",
component: Cover,
tags: ["autodocs"],
} satisfies Meta<typeof Cover>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
url: image.src,
onUnsplash: async (url) => console.log(`Uploading unsplash image: ${url}`),
onUploadChange: async (file) => console.log(`Uploading file: ${file.name}`),
onRemove: async () => console.log(`Removing file`),
},
};
61 changes: 61 additions & 0 deletions apps/storybook/src/stories/custom/crud-item.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Folder, PlusCircle, SearchIcon, SettingsIcon } from "lucide-react";

import { CRUDItem } from "@acme/ui/components";

const meta = {
title: "custom/Crud Item",
component: CRUDItem,
parameters: { layout: "centered" },
tags: ["autodocs"],
argTypes: {},
render: (args) => {
return (
<div className="group/sidebar relative z-[99999] flex h-full w-60 flex-col overflow-y-auto bg-secondary">
<CRUDItem {...args} />
</div>
);
},
} satisfies Meta<typeof CRUDItem>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
label: "New Page",
icon: PlusCircle,
onCreate: () => alert("Added New Page"),
},
};
export const Search: Story = {
args: {
label: "Search",
icon: SearchIcon,
onClick: () => alert("Opened Search Bar"),
shortcut: "⌘K",
},
};
export const Settings: Story = {
args: {
label: "Search",
icon: SettingsIcon,
onClick: () => alert("Opened Settings Block"),
shortcut: "⌘,",
},
};
export const TreeItem: Story = {
args: {
id: "test-id",
username: "John Doe",
label: "Folder",
icon: Folder,
onClick: () => alert(`Clicked item`),
active: true,
level: 0,
expanded: true,
onExpand: () => alert(`Expanded item`),
onCreate: () => alert(`Created item`),
onDelete: () => alert(`Deleted item`),
},
};
22 changes: 22 additions & 0 deletions apps/storybook/src/stories/custom/hint.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from "@storybook/react";
import { HelpCircle } from "lucide-react";

import { Hint } from "@acme/ui/components";

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

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
// sideOffset: 40,
description: "Shows some messages",
children: <HelpCircle className="h-4 w-4" />,
},
};
47 changes: 47 additions & 0 deletions apps/storybook/src/stories/custom/icon-picker/crud-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useState } from "react";
import { Smile, X } from "lucide-react";

import { Button, IconPicker } from "@acme/ui/components";

const CrudIcon = () => {
const [icon, setIcon] = useState("");
const onUpdateIcon = (icon: string) => {
setIcon(icon);
};
const onRemoveIcon = () => setIcon("");

return (
<div className="group relative pl-[54px]">
{icon ? (
<div className="group/icon flex items-center gap-x-2 pt-6">
<IconPicker onChange={onUpdateIcon}>
<p className="text-6xl transition hover:opacity-75">{icon}</p>
</IconPicker>
<Button
onClick={onRemoveIcon}
className="rounded-full text-xs text-muted-foreground opacity-0 transition group-hover/icon:opacity-100"
variant="outline"
size="icon"
>
<X className="w04 h-4" />
</Button>
</div>
) : (
<div className="flex items-center gap-x-2 py-4">
<IconPicker asChild onChange={onUpdateIcon}>
<Button
className="text-xs text-muted-foreground"
variant="ghost"
size="sm"
>
<Smile className="mr-2 h-4 w-4" />
Add icon
</Button>
</IconPicker>
</div>
)}
</div>
);
};

export default CrudIcon;
19 changes: 19 additions & 0 deletions apps/storybook/src/stories/custom/icon-picker/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from "@storybook/react";

import { IconPicker } from "@acme/ui/components";

import CrudIcon from "./crud-icon";

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

type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: () => <CrudIcon />,
};
Loading

0 comments on commit 9c2143c

Please sign in to comment.