import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks'
import * as CommentBoxStories from './CommentBox.stories'

<Meta of={CommentBoxStories} />

# CommentBox

The one box every app writes a comment, a reply and a line of a conversation in: `@`, `!@`, `[` and `/`, the keys, drafts, files, editing, sending and a refused send. The app supplies its people, its `[` sections, its `/` Insert rows and a send.

<Canvas of={CommentBoxStories.Empty} />

## When

- **Writing anything that is sent**, in every app: a comment, a reply, a line in a conversation. The same box writes all of them.
- `showsAtOnce` when the app shows a send before the relay answers: the box empties at once, and a refusal puts the words back. Without it the box holds the words, shut, until the answer.
- `edit` to change something already sent: the same box with Cancel and Save, its keys on the left, and the same selection strip over selected words.
- `upload` to offer files: the paperclip, `/` → Upload, a pasted picture and a dropped file.
- `under` to show, while something is written, the one card it will be sent with (✕ takes it off) and the live part of each block link, drawn as the app draws them under a sent message. An edit box shows the card and no part.
- `closed` where nothing can be written (archived, signed out): a line saying why, in the box's place. No greyed box.
- `marked` when the app has its own reason for the bar down the text, besides an urgent mention, which always draws it.
- `onEditor` when something outside the box writes into it: the app keeps the editor it is handed and inserts into the box last used.

<Canvas of={CommentBoxStories.Refused} />

<Canvas of={CommentBoxStories.WithAFile} />

<Canvas of={CommentBoxStories.WithAnAppsOwnRows} />

<Canvas of={CommentBoxStories.LongDraft} />

<Canvas of={CommentBoxStories.CardUnderTheBox} />

## When not

- Writing a document in place → the editor itself, with **EditorBlockHandle** and **SelectionToolbar**. Nothing is sent.
- One line of plain text in a form → **TextInput** or **Textarea**.

## How

```tsx
import { CommentBox, commentBoxRows } from '@estiva-app/ui/editor'

// composerDraft, createDraftStore, draftKeys and composerText: @estiva-app/conversation, which this package does not depend on.
const draft = composerDraft(createDraftStore(localStorage), draftKeys.thread(rootId), labels)

<CommentBox
  key={rootId}
  label="Reply"
  placeholder="Reply…"
  people={people}
  references={sectionsFor}
  insert={[commentBoxRows.file, commentBoxRows.mention, commentBoxRows.urgent, commentBoxRows.reference]}
  code="block"
  draft={draft}
  upload={uploadOne}
  send={async (body, files) => {
    const { text, urgent } = composerText(body)
    return publish(text, urgent, files) // { accepted: true } or { accepted: false, reason }
  }}
/>
```

- **It speaks the editor's document.** The text a document sends, and the document an edit or a draft opens with, are `@estiva-app/conversation`'s: `composerText`, `composerDocument`, `composerDraft`. Never write marker text here.
- **`send` answers.** Resolve `{ accepted: false, reason }` and the words come back with "Not sent: *reason*". A thrown send is a refusal too.
- **`label` is required**: the editor's accessible name.
- **`insert` is the app's Insert rows**, after the shared Format section: `commentBoxRows` and the app's own. An edit drops Upload and Urgent itself; with no `upload` there is no Upload (`commentBoxRows.file` is that row).
- **Give the box a `key` per destination.** The editor, the rows and `code` are made once, as the box opens; a new thread is a new box.
- A send shown at once and refused after its box went away writes the words into its draft instead.
- **`under.read` is the app's reading of the draft**: `@estiva-app/conversation`'s `boxReadOf(body, context)` says which file gets the card and which parts show; the app adds its own view of each. Call `under.changed`'s listener when what a link names comes back. `send`'s third argument is `{ card }`, the card still shown, which the app writes with `embedsToSend` (or `embedsToEdit` for `onSave`).
- **An edit box's `read` passes `previous: namedBefore(<the message's text>)`**, so only a file the edit names first gets a card. A card the message already has, or one taken off with ✕ before or after sending, never shows again in the edit box, and Save leaves it as it was.
- **A link chip for a pasted app URL** is the app's `LinkChips`, passed in `extensions`.
- **`keepCommentBoxWarm({ extensions, referenceView })`**, called once as the app starts, builds an editor while the page is idle, and the next empty box given those same `extensions` and `referenceView` (the same objects, in the same order) takes it, so it opens at once. A box opening with a draft or an edit, or with other extensions, builds its own. Keep `extensions` a module constant: a new array per render never matches.
- **`onEditor` gets `null` as the box goes**, so a launcher never writes into a box that is gone.

## What it owns

It does these by itself. Write none of them around it.

| It does | So you never write |
|---|---|
| `@`, `!@`, `[` and `/`, with their lists and chips | the triggers and their extensions |
| Enter and Ctrl/Cmd+Enter send; Escape cancels an edit | key handlers |
| Holding the words until the answer, or showing the send at once | a sending state |
| A refusal: the words back, and a line under the box | a toast, or putting text back |
| The draft, written as you type and forgotten once sent | an update listener |
| Files: picking, pasting, dropping, uploading, failing; Send waits for an upload and holds on a failed file until its ✕ removes it | a file list |
| The card under the box, its ✕, and a ✕ forgotten once the link is taken out; a refused send puts back what ✕ took off | a card state |
| A URL pasted over words links them when a message can carry them as a link (one line of plain words, no bracket, no chip, no `)` in the URL); otherwise the URL goes in after the words. The caret goes after the link, so the next Enter sends. Typing after linked words is not linked | a paste handler |
| A long draft stops at half the window and scrolls inside the box, in the send box and the edit box | a height or a scroll container |
| The bar down the text while an urgent mention is in the box | an urgent check |
| The read-only line | a disabled box |

## Keys

| Key | What it does |
|---|---|
| Enter | Sends; saves an edit. With a list open, picks the row. Holds while a file is uploading or did not upload. |
| Ctrl/Cmd+Enter | Sends, as Enter does. |
| Shift+Enter | A new line; in a list, a new item. |
| Escape | Cancels an edit. With a list open, or words selected under the strip, closes that first. |

<Controls />
