import { Meta, Canvas } from '@storybook/addon-docs/blocks'
import * as BlockNodesStories from './BlockNodes.stories'

<Meta of={BlockNodesStories} />

# BlockNodes

The Tiptap nodes an editor of a block document needs beside StarterKit: an id
on every block, a block it cannot edit kept whole, a reference, a file, a
checklist item and a callout.

<Canvas of={BlockNodesStories.Default} />

## When

- **An editor that saves a block document** (SPEC §13.3) — anywhere a comment
  can be anchored to one of its blocks. `BlockId` keeps each block's id across
  every edit, so the anchor still finds its block after a save.
- Always with `@estiva-app/protocol`'s `toEditorDocument` to load the document
  and `fromEditorDocument` (or `publishableFromEditor`) to save it. The node
  names and the `blockId` attribute are that translation's.

## When not

- Reading a document → **RichText**. Nothing here draws a document that is not
  being edited.
- A message composer that writes marker text. It has no block ids to keep.

## How

```tsx
import type { JSONContent } from '@tiptap/core'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import {
  AttachmentNode,
  BlockId,
  Callout,
  Checklist,
  ReferenceNode,
  UnknownBlock,
  isChecklistTick,
  type AttachmentViewProps,
  type ReferenceViewProps,
} from '@estiva-app/ui/editor'

// toEditorDocument(documentForEditing(value, format)), from @estiva-app/protocol
const opened: JSONContent = { type: 'doc', content: [] }

const Reference = ({ uri }: ReferenceViewProps) => <span>{uri}</span>
const File = ({ attrs }: AttachmentViewProps) => <span>{String(attrs.filename)}</span>

const editor = useEditor({
  extensions: [
    StarterKit,
    BlockId,
    Checklist,
    Callout,
    UnknownBlock,
    ReferenceNode.configure({ view: Reference }),
    AttachmentNode.configure({ view: File }),
  ],
  content: opened,
  // A tick is a click, so no blur follows it: save it now.
  onUpdate: ({ transaction }) => {
    if (isChecklistTick(transaction)) console.log('save now')
  },
})

<EditorContent editor={editor} />
```

- **Load and save through protocol.** `toEditorDocument(documentForEditing(value, format))`
  opens the stored value; `publishableFromEditor(editor.getJSON())` is what a
  save writes, with its `contentFormat`.
- **Add TableKit** (`@tiptap/extension-table`) beside StarterKit. The
  translation opens a table as `table`, `tableRow`, `tableHeader` and
  `tableCell` nodes, and ProseMirror drops a node its schema does not have —
  the save would delete the table.
- `ReferenceNode`'s `view` gets the `uri` and draws the thing it points at,
  resolved by the app. Without one, the URI is drawn as text.
- `AttachmentNode`'s `view` gets the block's `imeta` fields as `attrs`.
  Without one, the file's name is drawn as text.
- `UnknownBlock` draws a block the editor has no design for as its text, in a
  box. It cannot be changed here, only moved or deleted, and it is saved back
  exactly as it arrived.
- `Checklist` is a list item with `checked` (§13.3): a box before it that
  ticks with one click. `[ ] ` or `[] ` at the start of a line makes one,
  `[x] ` a ticked one; `/`'s Checklist does too, in a block editor
  (`formatSection('block')`). A change that is a tick says so
  (`isChecklistTick`): save it at once, as no blur follows a click on the box.
  The box is named by its item's words, and ticks only while the editor is
  editable — `setEditable` is followed, not read once. Without `Checklist`, a
  save drops the tick and keeps the item, and `/` does not offer Checklist.
- `Callout` is a quote with a `tone` (§13.3), drawn on its tone's wash as
  `RichText` draws it. `/` offers Note and Warning in a block editor that has
  it; the other tones are read and kept as written.

<Canvas of={BlockNodesStories.WithoutViews} />

<Canvas of={BlockNodesStories.ChecklistAndCallouts} />

## What it owns

Nothing. It only draws. Clicks and keys are yours.

## Keys

| Key | In a checklist item |
| --- | --- |
| Enter | Starts the next item, unticked. At the start of the item, its text moves down with its tick. On an empty item, leaves the list. |
| Backspace | At the start of the item, takes its box away; again, joins it to the item above. |
| Space after `[ ]`, `[]` or `[x]` | At the start of a line, makes a checklist item (ticked for `[x]`). |
