Skip to content

Commit

Permalink
🚀 worxpace 25 - Add block type: callout (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
steeeee0223 authored Apr 30, 2024
1 parent afe9b14 commit 79ac399
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 3 deletions.
96 changes: 96 additions & 0 deletions apps/worxpace/src/components/blocknote/callout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"use client";

import { defaultProps } from "@blocknote/core";
import type {
BlockFromConfig,
InlineContentSchema,
PropSpec,
StyleSchema,
} from "@blocknote/core";
import { createReactBlockSpec } from "@blocknote/react";

import { IconBlock, type IconBlockProps, type IconInfo } from "@acme/ui/custom";

import { cn } from "@/lib/utils";

export interface CalloutBlockSpec {
type: "callout";
readonly propSchema: typeof defaultProps & {
type: PropSpec<"emoji" | "lucide" | "file">;
name: PropSpec<string>;
emoji: PropSpec<string>;
url: PropSpec<string>;
color: PropSpec<string>;
};
content: "inline";
}

const getBlockIcon = (
block: BlockFromConfig<CalloutBlockSpec, InlineContentSchema, StyleSchema>,
): IconInfo => {
const { type, color, name, emoji, url } = block.props;
switch (type) {
case "emoji":
return { type, emoji };
case "lucide":
return { type, name, color } as IconInfo;
case "file":
return { type, url };
default:
return { type: "emoji", emoji: " " };
}
};

// The Callout block.
export const Callout = createReactBlockSpec<
CalloutBlockSpec,
InlineContentSchema,
StyleSchema
>(
{
type: "callout",
propSchema: {
...defaultProps,
type: { default: "emoji" },
emoji: { default: "🚧" },
name: { default: "" },
url: { default: "" },
color: { default: "default" },
},
content: "inline",
},
{
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} />
</div>
{/* Rich text field for user to type in */}
<div className="w-full min-w-0 pl-2" ref={props.contentRef} />
</div>
);
},
},
);
4 changes: 4 additions & 0 deletions apps/worxpace/src/components/blocknote/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./schema";
export * from "./slash-menu";
/** Custom Blocks */
export * from "./callout";
30 changes: 30 additions & 0 deletions apps/worxpace/src/components/blocknote/schema.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
BlockNoteSchema,
defaultBlockSpecs,
insertOrUpdateBlock,
} from "@blocknote/core";
import { Construction } from "lucide-react";

import { Callout } from "./callout";

// Our schema with block specs, which contain the configs and implementations for blocks
// that we want our editor to use.
export const schema = BlockNoteSchema.create({
blockSpecs: {
// Adds all default blocks.
...defaultBlockSpecs,
// Adds the Callout block.
callout: Callout,
},
});
export type CustomEditor = typeof schema.BlockNoteEditor;

// Slash menu item to insert an Callout block
export const insertCallout = (editor: CustomEditor) => ({
title: "Callout",
onItemClick: () => insertOrUpdateBlock(editor, { type: "callout" }),
aliases: ["callout", "notification", "emphasize"],
group: "Other",
icon: <Construction size={18} />,
subtext: "Make writing stand out.",
});
29 changes: 29 additions & 0 deletions apps/worxpace/src/components/blocknote/slash-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { filterSuggestionItems } from "@blocknote/core";

import "@blocknote/core/fonts/inter.css";

import {
getDefaultReactSlashMenuItems,
SuggestionMenuController,
} from "@blocknote/react";

import { insertCallout, type CustomEditor } from "./schema";

interface CustomSlashMenuProps {
editor: CustomEditor;
}

export const CustomSlashMenu = ({ editor }: CustomSlashMenuProps) => {
return (
<SuggestionMenuController
triggerCharacter={"/"}
getItems={async (query) =>
// Gets all default slash menu items and `insertCallout` item.
filterSuggestionItems(
[...getDefaultReactSlashMenuItems(editor), insertCallout(editor)],
query,
)
}
/>
);
};
12 changes: 9 additions & 3 deletions apps/worxpace/src/components/collab-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useEffect, useState } from "react";
import type { BlockNoteEditor, PartialBlock } from "@blocknote/core";
import type { PartialBlock } from "@blocknote/core";
import { BlockNoteView, useCreateBlockNote } from "@blocknote/react";
import LiveblocksProvider from "@liveblocks/yjs";
import { useTheme } from "next-themes";
Expand All @@ -10,8 +10,10 @@ import * as Y from "yjs";
import "@blocknote/core/fonts/inter.css";
import "@blocknote/react/style.css";

import { schema, type CustomEditor } from "~/components/blocknote";
import { connectionIdToColor } from "~/lib";
import { useRoom, useSelf } from "~/liveblocks.config";
import { CustomSlashMenu } from "./blocknote";

interface EditorProps {
initialContent?: string | null;
Expand Down Expand Up @@ -60,7 +62,8 @@ function BlockNote({
// Get user info from Liveblocks authentication endpoint
const userInfo = useSelf();

const editor: BlockNoteEditor = useCreateBlockNote({
const editor: CustomEditor = useCreateBlockNote({
schema,
initialContent: initialContent
? (JSON.parse(initialContent) as PartialBlock[])
: undefined,
Expand All @@ -83,10 +86,13 @@ function BlockNote({
<div>
<BlockNoteView
editor={editor}
slashMenu={false}
editable={editable}
onChange={() => onChange?.(JSON.stringify(editor.document))}
theme={resolvedTheme === "dark" ? "dark" : "light"}
/>
>
<CustomSlashMenu editor={editor} />
</BlockNoteView>
</div>
);
}

0 comments on commit 79ac399

Please sign in to comment.