diff --git a/packages/react-notion-custom/CONTRIBUTING-KR.md b/packages/react-notion-custom/CONTRIBUTING-KR.md index 78b6274..b61183b 100644 --- a/packages/react-notion-custom/CONTRIBUTING-KR.md +++ b/packages/react-notion-custom/CONTRIBUTING-KR.md @@ -649,7 +649,7 @@ fetchNotionPage(); | Heading 3 | ✅ Yes | `heading_3` | | | Bulleted List Item | ✅ Yes | `bulleted_list_item` | | | Numbered List Item | ✅ Yes | `numbered_list_item` | | -| To-do | ❌ No | `to_do` | | +| To-do | ✅ Yes | `to_do` | | | Toggle | ✅ Yes | `toggle` | | | Quote | ✅ Yes | `quote` | | | Callout | ✅ Yes | `callout` | | diff --git a/packages/react-notion-custom/CONTRIBUTING.md b/packages/react-notion-custom/CONTRIBUTING.md index 2ee3fd6..d270efa 100644 --- a/packages/react-notion-custom/CONTRIBUTING.md +++ b/packages/react-notion-custom/CONTRIBUTING.md @@ -652,7 +652,7 @@ Here's a list of Notion block types currently supported in react-notion-custom. | Heading 3 | ✅ Yes | `heading_3` | | | Bulleted List Item | ✅ Yes | `bulleted_list_item` | | | Numbered List Item | ✅ Yes | `numbered_list_item` | | -| To-do | ❌ No | `to_do` | | +| To-do | ✅ Yes | `to_do` | | | Toggle | ✅ Yes | `toggle` | | | Quote | ✅ Yes | `quote` | | | Callout | ✅ Yes | `callout` | | diff --git a/packages/react-notion-custom/src/lib/components/index.ts b/packages/react-notion-custom/src/lib/components/index.ts index 38b0fea..a719073 100644 --- a/packages/react-notion-custom/src/lib/components/index.ts +++ b/packages/react-notion-custom/src/lib/components/index.ts @@ -11,6 +11,7 @@ import Video from "./video"; import Column from "./column"; import ColumnList from "./column-list"; import Code from "./code"; +import Todo from "./todo"; export { Headings, @@ -26,6 +27,7 @@ export { Column, ColumnList, Code, + Todo, }; export default { @@ -44,4 +46,5 @@ export default { column: Column, column_list: ColumnList, code: Code, + to_do: Todo, }; diff --git a/packages/react-notion-custom/src/lib/components/todo.tsx b/packages/react-notion-custom/src/lib/components/todo.tsx new file mode 100644 index 0000000..1764c47 --- /dev/null +++ b/packages/react-notion-custom/src/lib/components/todo.tsx @@ -0,0 +1,107 @@ +import React from "react"; +import { TodoArgs } from "../types"; +import { getColorCss } from "../utils"; +import RichText from "./internal/rich-text"; + +interface TodoProps extends TodoArgs { + children?: React.ReactNode; +} + +const Todo: React.FC & { CheckBox: typeof TodoCheckBox } = ({ + children, + ...props +}) => { + const { + to_do: { color, rich_text: texts, checked }, + } = props; + + let checkboxElement: React.ReactNode = null; + const otherChildren: React.ReactNode[] = []; + + // Process children to find Todo.Checkbox + React.Children.forEach(children, (child) => { + if (React.isValidElement(child) && child.type === Todo.CheckBox) { + // Use the children of Todo.Checkbox as the checkbox element + checkboxElement = child.props.children; + } else { + otherChildren.push(child); + } + }); + + // If no custom Checkbox provided, use default + if (!checkboxElement) { + checkboxElement = ; + } + + return ( +
+
+ {checkboxElement} +

+ +

+
+ {otherChildren} +
+ ); +}; + +interface CheckBoxProps { + checked: boolean; +} + +const DefaultCheckBox: React.FC = ({ checked }) => { + return ( +
+
+ {checked ? ( + + + + ) : ( + + + + )} +
+
+ ); +}; + +const TodoCheckBox: React.FC<{ children?: React.ReactNode }> = ({ + children, +}) => <>{children}; + +Todo.CheckBox = TodoCheckBox; + +export default Todo; diff --git a/packages/react-notion-custom/src/lib/index.css b/packages/react-notion-custom/src/lib/index.css index ceb42ff..d37729f 100644 --- a/packages/react-notion-custom/src/lib/index.css +++ b/packages/react-notion-custom/src/lib/index.css @@ -907,3 +907,81 @@ padding-bottom: 0px; } } + +.notion-property-checkbox { + width: 16px; + height: 16px; +} + +.notion-property-checkbox-checked { + width: 16px; + height: 16px; + background: var(--select-color-0); +} + +.notion-property-checkbox-checked svg { + position: relative; + display: block; + top: 1px; + left: 1px; + width: 14px; + height: 14px; + fill: #fff; +} + +.notion-property-checkbox-unchecked { + width: 16px; + height: 16px; +} + +.notion-property-checkbox-unchecked svg { + fill: var(--fg-color); +} + +.notion-property-checkbox { + width: 16px; + height: 16px; +} + +.notion-property-checkbox-checked { + width: 16px; + height: 16px; + background: var(--select-color-0); +} + +.notion-property-checkbox-checked svg { + position: relative; + display: block; + top: 1px; + left: 1px; + width: 14px; + height: 14px; + fill: #fff; +} + +.notion-property-checkbox-unchecked { + width: 16px; + height: 16px; +} + +.notion-property-checkbox-unchecked svg { + fill: var(--fg-color); +} + +.notion-to-do-content { + display: flex; +} + +.notion-to-do-checkbox { + display: inline-block; + padding: 8px 6px; +} + +.notion-to-do-text { + padding: 3px 2px; +} + +.notion-to-do-checked .notion-to-do-text { + text-decoration: line-through; + opacity: 0.6; +} diff --git a/packages/story/src/lib/Notion.tsx b/packages/story/src/lib/Notion.tsx index 875b500..c55817b 100644 --- a/packages/story/src/lib/Notion.tsx +++ b/packages/story/src/lib/Notion.tsx @@ -4,13 +4,15 @@ export default function StoryComponent({ blocks, title, cover, + custom, }: { blocks: Block[]; title?: string; cover?: string; + custom?: Record>; }) { return ( - + diff --git a/packages/story/src/stories/todo/todo.json b/packages/story/src/stories/todo/todo.json new file mode 100644 index 0000000..7c62c25 --- /dev/null +++ b/packages/story/src/stories/todo/todo.json @@ -0,0 +1,582 @@ +{ + "object": "page", + "id": "591d29d3-cb19-4273-bb3a-82644ed4faa4", + "created_time": "2022-12-28T23:30:00.000Z", + "last_edited_time": "2023-01-07T04:39:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "cover": { + "type": "external", + "external": { + "url": "https://www.notion.so/images/page-cover/solid_beige.png" + } + }, + "icon": null, + "parent": { + "type": "database_id", + "database_id": "be65d799-9e98-4426-86a6-72072991e27b" + }, + "archived": false, + "properties": { + "HashTags": { + "id": "Hhkx", + "type": "multi_select", + "multi_select": [] + }, + "생성 일시": { + "id": "J%7C%3BZ", + "type": "created_time", + "created_time": "2022-12-28T23:30:00.000Z" + }, + "Slug": { + "id": "S%3A%7B%3E", + "type": "rich_text", + "rich_rich_text": [ + { + "type": "text", + "text": { + "content": "test", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "test", + "href": null + } + ] + }, + "Description": { + "id": "qTV%3E", + "type": "rich_text", + "rich_rich_text": [] + }, + "Status": { + "id": "vu%7C%3B", + "type": "select", + "select": { + "id": "|QrX", + "name": "Publishable", + "color": "green" + } + }, + "Name": { + "id": "title", + "type": "title", + "title": [ + { + "type": "text", + "text": { + "content": "테스트 paragrap", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "테스트 paragrap", + "href": null + } + ] + } + }, + "url": "https://www.notion.so/paragrap-591d29d3cb194273bb3a82644ed4faa4", + "blocks": [ + { + "object": "block", + "id": "06d7c6ab-cb38-4039-bde8-fec3b85ea728", + "parent": { + "type": "page_id", + "page_id": "591d29d3-cb19-4273-bb3a-82644ed4faa4" + }, + "created_time": "2023-01-07T04:10:00.000Z", + "last_edited_time": "2023-01-07T04:38:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "gray_background", + "rich_text": [] + } + }, + { + "object": "block", + "id": "0c0b6e3f-6022-40dc-b4c3-05c2735ee835", + "parent": { + "type": "page_id", + "page_id": "591d29d3-cb19-4273-bb3a-82644ed4faa4" + }, + "created_time": "2023-01-07T04:38:00.000Z", + "last_edited_time": "2023-01-07T04:38:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": true, + "archived": false, + "type": "to_do", + "to_do": { + "checked": false, + "color": "default", + "rich_text": [ + { + "type": "text", + "text": { + "content": "todo1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "todo1", + "href": null + } + ] + }, + "blocks": [ + { + "object": "block", + "id": "f2941a3f-ac21-46fd-92bd-1c36d66b2844", + "parent": { + "type": "block_id", + "block_id": "0c0b6e3f-6022-40dc-b4c3-05c2735ee835" + }, + "created_time": "2023-01-07T04:38:00.000Z", + "last_edited_time": "2023-01-07T04:38:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "blue_background", + "rich_text": [ + { + "type": "text", + "text": { + "content": "nest paragraph", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "nest paragraph", + "href": null + } + ] + } + } + ] + }, + { + "object": "block", + "id": "131d41ef-0959-4e8e-a7bd-3ff6ab5b41b7", + "parent": { + "type": "page_id", + "page_id": "591d29d3-cb19-4273-bb3a-82644ed4faa4" + }, + "created_time": "2023-01-07T04:38:00.000Z", + "last_edited_time": "2023-01-07T04:38:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": true, + "archived": false, + "type": "to_do", + "to_do": { + "checked": true, + "color": "default", + "rich_text": [ + { + "type": "text", + "text": { + "content": "todo2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "todo2", + "href": null + } + ] + }, + "blocks": [ + { + "object": "block", + "id": "9659845e-fe5b-4684-8afd-78c670e90c25", + "parent": { + "type": "block_id", + "block_id": "131d41ef-0959-4e8e-a7bd-3ff6ab5b41b7" + }, + "created_time": "2023-01-07T04:38:00.000Z", + "last_edited_time": "2023-01-07T04:38:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "to_do", + "to_do": { + "checked": false, + "color": "blue_background", + "rich_text": [ + { + "type": "text", + "text": { + "content": "nest todo 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "nest todo 1", + "href": null + } + ] + } + }, + { + "object": "block", + "id": "071e179b-4104-4939-9c5f-76a4f4426940", + "parent": { + "type": "block_id", + "block_id": "131d41ef-0959-4e8e-a7bd-3ff6ab5b41b7" + }, + "created_time": "2023-01-07T04:38:00.000Z", + "last_edited_time": "2023-01-07T04:39:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "to_do", + "to_do": { + "checked": true, + "color": "default", + "rich_text": [ + { + "type": "text", + "text": { + "content": "nest todo 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "nest todo 2", + "href": null + } + ] + } + } + ] + }, + { + "object": "block", + "id": "e77001a4-9b1b-40ea-9349-a73d022e7d87", + "parent": { + "type": "page_id", + "page_id": "591d29d3-cb19-4273-bb3a-82644ed4faa4" + }, + "created_time": "2023-01-07T03:39:00.000Z", + "last_edited_time": "2023-01-07T04:10:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "gray_background", + "rich_text": [] + } + }, + { + "object": "block", + "id": "f938d548-9cdb-4250-8f51-2b49af2598f8", + "parent": { + "type": "page_id", + "page_id": "591d29d3-cb19-4273-bb3a-82644ed4faa4" + }, + "created_time": "2023-01-07T04:08:00.000Z", + "last_edited_time": "2023-01-07T04:39:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": true, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "gray_background", + "rich_text": [] + }, + "blocks": [ + { + "object": "block", + "id": "4f450777-4131-4750-ae22-fa36f3bd7169", + "parent": { + "type": "block_id", + "block_id": "f938d548-9cdb-4250-8f51-2b49af2598f8" + }, + "created_time": "2023-01-07T03:39:00.000Z", + "last_edited_time": "2023-01-07T04:36:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "blue_background", + "rich_text": [ + { + "type": "text", + "text": { + "content": "nest paragraph", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "nest paragraph", + "href": null + } + ] + } + }, + { + "object": "block", + "id": "e3755c16-6e1b-4efb-9f88-a9e5df023933", + "parent": { + "type": "block_id", + "block_id": "f938d548-9cdb-4250-8f51-2b49af2598f8" + }, + "created_time": "2023-01-07T04:39:00.000Z", + "last_edited_time": "2023-01-07T04:39:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "to_do", + "to_do": { + "checked": false, + "color": "purple_background", + "rich_text": [ + { + "type": "text", + "text": { + "content": "todo1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "todo1", + "href": null + } + ] + } + }, + { + "object": "block", + "id": "598b1910-1bf5-4c1f-a0e0-a8a0ce7608c6", + "parent": { + "type": "block_id", + "block_id": "f938d548-9cdb-4250-8f51-2b49af2598f8" + }, + "created_time": "2023-01-07T04:39:00.000Z", + "last_edited_time": "2023-01-07T04:39:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "to_do", + "to_do": { + "checked": true, + "color": "default", + "rich_text": [ + { + "type": "text", + "text": { + "content": "todo2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "todo2", + "href": null + } + ] + } + } + ] + }, + { + "object": "block", + "id": "a199ef95-dd63-4812-a43c-1f8a84ac6fd3", + "parent": { + "type": "page_id", + "page_id": "591d29d3-cb19-4273-bb3a-82644ed4faa4" + }, + "created_time": "2023-01-07T04:37:00.000Z", + "last_edited_time": "2023-01-07T04:37:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "default", + "rich_text": [] + } + }, + { + "object": "block", + "id": "23d49ee9-d260-4b92-9ba8-999570eb91ee", + "parent": { + "type": "page_id", + "page_id": "591d29d3-cb19-4273-bb3a-82644ed4faa4" + }, + "created_time": "2023-01-07T04:30:00.000Z", + "last_edited_time": "2023-01-07T04:30:00.000Z", + "created_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "last_edited_by": { + "object": "user", + "id": "95fc0174-8fc6-4114-8e45-f67eacd99f07" + }, + "has_children": false, + "archived": false, + "type": "paragraph", + "paragraph": { + "color": "default", + "rich_text": [] + } + } + ] +} \ No newline at end of file diff --git a/packages/story/src/stories/todo/todo.stories.tsx b/packages/story/src/stories/todo/todo.stories.tsx new file mode 100644 index 0000000..81c7cb2 --- /dev/null +++ b/packages/story/src/stories/todo/todo.stories.tsx @@ -0,0 +1,64 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import Component from "../../lib/Notion"; +import json from "./todo.json"; +import { Todo, TodoArgs } from "react-notion-custom"; + +const blocks = json.blocks as any; + +const meta: Meta = { + title: "Blocks/Todo", + component: Component, +}; + +export default meta; +type Story = StoryObj; + +export const TodoStory: Story = { + name: "Todo", + args: { + title: "Todo", + blocks: blocks, + }, +}; + +type TodoProps = TodoArgs & { + children?: React.ReactNode; +}; + +const CustomTodo = (props: TodoProps) => { + return ( + + + + + + ); +}; + +export const CustomTodoStory: Story = { + name: "Custom Todo", + args: { + title: "Custom Todo", + blocks: blocks, + custom: { + to_do: CustomTodo, + }, + }, +};