import type { Meta, StoryObj } from '@storybook/react-vite' import { EditorContent, useEditor } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import { AttachmentCard } from './AttachmentCard' import { InlineChip } from './InlineChip' import { richTextClassName } from './RichText' import { AttachmentNode, BlockId, Callout, Checklist, ReferenceNode, UnknownBlock, type AttachmentViewProps, type ReferenceViewProps } from './BlockNodes' /** * The nodes an editor of a block document adds to StarterKit: an id on every * block, a block it cannot edit kept whole, a reference and a file. The app * draws the reference and the file with its own views. */ const meta = { title: 'Primitives/BlockNodes', parameters: { layout: 'padded' }, } satisfies Meta export default meta type Story = StoryObj /** What an app hands `ReferenceNode`: here a chip with a fixed label. */ function Chip({ uri }: ReferenceViewProps) { return Item one } /** What an app hands `AttachmentNode`: here the package's card. */ function File({ attrs }: AttachmentViewProps) { return } /* The editor's own JSON, as `@estiva-app/protocol`'s `toEditorDocument` hands it over: an id on each block, and an unknown block carrying its text. */ const DOCUMENT = { type: 'doc', content: [ { type: 'paragraph', attrs: { blockId: 'b1' }, content: [ { type: 'text', text: 'A paragraph, with a reference in it: ' }, { type: 'reference', attrs: { uri: 'nostr:naddr1example', marks: [] } }, ], }, { type: 'unknownBlock', attrs: { blockId: 'b2', source: { type: 'widget', id: 'b2' }, text: 'A block this editor has no design for, kept as it is.' }, }, { type: 'attachment', attrs: { blockId: 'b3', url: '', m: 'application/pdf', x: '', size: 20480, filename: 'Label.pdf' } }, { type: 'paragraph', attrs: { blockId: 'b4' }, content: [{ type: 'text', text: 'Type here: every block keeps its id.' }] }, ], } function WithAnEditor({ views }: { views: boolean }) { const editor = useEditor({ extensions: [ StarterKit, BlockId, UnknownBlock, ReferenceNode.configure({ view: views ? Chip : null }), AttachmentNode.configure({ view: views ? File : null }), ], content: DOCUMENT, editorProps: { attributes: { class: richTextClassName('default', 'outline-none'), 'aria-label': 'Text' } }, }) return } /** A reference and a file drawn by the app's views, and a block kept whole. */ export const Default: Story = { render: () => , } /** No views given: the reference is its URI and the file its name, as text. */ export const WithoutViews: Story = { render: () => , } const item = (blockId: string, text: string, checked?: boolean) => ({ type: 'listItem', attrs: { blockId, ...(checked === undefined ? {} : { checked }) }, content: [{ type: 'paragraph', content: [{ type: 'text', text }] }], }) function WithChecklist() { const editor = useEditor({ extensions: [StarterKit, BlockId, Checklist, Callout], content: { type: 'doc', content: [ { type: 'bulletList', attrs: { blockId: 'l1' }, content: [item('i1', 'Ticked: click the box', true), item('i2', 'Enter starts the next one unticked', false), item('i3', 'A plain item')] }, { type: 'paragraph', attrs: { blockId: 'p1' }, content: [{ type: 'text', text: 'Type [ ] and a space at the start of a line for a checklist.' }] }, { type: 'blockquote', attrs: { blockId: 'q1', tone: 'note' }, content: [{ type: 'paragraph', content: [{ type: 'text', text: 'A note.' }] }] }, { type: 'blockquote', attrs: { blockId: 'q2', tone: 'warning' }, content: [{ type: 'paragraph', content: [{ type: 'text', text: 'A warning.' }] }] }, ], }, editorProps: { attributes: { class: richTextClassName('default', 'outline-none'), 'aria-label': 'Text' } }, }) return } /** A checklist and two callouts, in the editor: the box ticks with one click, as where it is read (RIC-19). */ export const ChecklistAndCallouts: Story = { render: () => , }