@tightknitai/block-kitchen 0.10.24 → 0.10.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -140,6 +140,7 @@ Given no height to work with — a plain `<div>` in ordinary document flow — i
140
140
  | `previewHooks` | `PreviewHooks` | no | Hooks forwarded to `slack-blocks-to-jsx`'s `<Message>` for resolving user / channel / emoji directives. |
141
141
  | `blockActions` | `BlockAction[]` | no | Host buttons added to every block's action toolbar, before Edit / Duplicate / Delete. Each is `{ id, label, icon, onSelect }`, and `onSelect` receives the block's `{ index, childIndex }`. See [Adding your own block actions](#adding-your-own-block-actions-blockactions). |
142
142
  | `renderBlockAdornment` | `(position: BlockPosition) => ReactNode` | no | Host content shown beside every block, at its right edge, all the time (unlike `blockActions`, which show on hover), e.g. a count of comments on the block. Return `null` for a block with nothing to show. See [Adding your own block actions](#adding-your-own-block-actions-blockactions). |
143
+ | `blockEditors` | `CustomBlockEditor[]` | no | Host forms that replace the built-in editor for the blocks they match. Each is `{ id, label, match, component }`. The block stays ordinary Block Kit. See [Editing a block your way](#editing-a-block-your-way-blockeditors). |
143
144
  | `customEmojis` | `CustomEmoji[]` | no | Workspace custom emoji (`{ name, url, alias }`) the preview resolves. Entries with a `url` render `:name:` as the workspace image; alias entries (`url: null`) fall back to their target emoji. Render-only — never serialized into the emitted Block Kit JSON. A caller-supplied `previewHooks.emoji` takes precedence. |
144
145
  | `mentions` | `{ searchUsers, getUserName?, searchChannels?, getChannelName? }` | no | Opt-in rich-text mentions. `@` and the Mention button search users; when `searchChannels` is provided, `#` and the Channel button search channels. Picks serialize as Slack `user` / `channel` elements. See [Mentions](#mentions-opt-in). |
145
146
  | `palette` | `PaletteSection[]` | no | The left-hand palette of draggable variants. Defaults to `defaultPalette`. Spread it to filter, reorder, or add your own pre-configured variants — see [Customizing the palette](#customizing-the-palette). |
@@ -150,6 +151,7 @@ Given no height to work with — a plain `<div>` in ordinary document flow — i
150
151
  | `paletteSearchPlaceholder` | `string` | no | Placeholder text for the palette search input. Defaults to `'Search blocks…'`. Useful for localization. |
151
152
  | `allowedSurfaces` | `PreviewSurface[]` | no | Allowlist of preview surfaces (`'message'`, `'modal'`, `'app_home'`). Defaults to `['message']` — surface dropdown is hidden when only one surface is allowed. The first entry is the initial selection. |
152
153
  | `showThemeControl` | `boolean` | no | Defaults to `true`. When `false`, the toolbar's light/dark toggle is hidden and the theme stays at `defaultPreviewTheme`. Ignored when `previewTheme` is set (a controlled theme always hides the toggle). |
154
+ | `undoRedoShortcuts` | `boolean` | no | Defaults to `true`: `Ctrl`/`Cmd` + `Z` undoes and `Ctrl`/`Cmd` + `Shift` + `Z` (or `Ctrl` + `Y`) redoes, like the toolbar's Undo / Redo buttons. Pass `false` to leave those keys to the host. See [Undo & redo](#undo--redo). |
153
155
  | `defaultPreviewTheme` | `'light' \| 'dark'` | no | Initial (uncontrolled) preview theme. Pass the host app's current theme so the preview opens matched to the consuming app's appearance. Ignored when `previewTheme` is provided. |
154
156
  | `previewTheme` | `'light' \| 'dark'` | no | Controlled preview theme. When set, the preview renders in this theme, follows it reactively, and the toolbar's light/dark toggle is hidden so the host app fully owns the theme. Leave unset to keep the preview uncontrolled (seeded from `defaultPreviewTheme`, toggle shown). |
155
157
  | `sendButtonLabel` | `string` | no | Label and accessible name for the toolbar's Send button (which opens the send dialog). Defaults to `'Review & send'` (the dialog is the review step). Override it for product-specific copy, e.g. `'Send to channel…'`. |
@@ -489,6 +491,41 @@ the canvas, for example to scroll to it or highlight it:
489
491
  document.querySelector(`.bk-root [data-bk-block-index="${index}"]:not([data-bk-child-index])`);
490
492
  ```
491
493
 
494
+ ## Editing a block your way (`blockEditors`)
495
+
496
+ Some blocks mean something to your app beyond their Block Kit shape: a
497
+ "Share on LinkedIn" button, say, whose URL is built from the text of a post.
498
+ Editing that URL by hand in the built-in buttons form is no way to write a
499
+ post. `blockEditors` lets the host supply the form instead:
500
+
501
+ ```tsx
502
+ const LINKEDIN_EDITOR: CustomBlockEditor = {
503
+ id: "linkedin-share",
504
+ label: "Share on LinkedIn",
505
+ match: (block) => isLinkedInShareBlock(block), // your own check
506
+ component: ({ block, onChange }) => (
507
+ <Textarea
508
+ value={postTextFrom(block)}
509
+ onChange={(e) => onChange(withPostText(block, e.target.value))}
510
+ />
511
+ ),
512
+ };
513
+
514
+ <BlockKitchen blockEditors={[LINKEDIN_EDITOR]} {...rest} />;
515
+ ```
516
+
517
+ When a block's edit popover opens, the first editor whose `match` accepts the
518
+ block replaces the built-in form, under the heading "Edit {label}". The
519
+ popover keeps its validation errors, Block ID field and Done button. Blocks no
520
+ editor matches keep the built-in form, and so does every block when the prop
521
+ is left out.
522
+
523
+ The block itself stays ordinary Block Kit, so the preview, validation, the
524
+ JSON view and sending all treat it like any other. That also means `match` has
525
+ to recognize the block from what it holds (a URL, an `action_id`): a message
526
+ loaded back from Slack carries nothing else. Keep the array referentially
527
+ stable (module scope or `useMemo`).
528
+
492
529
  ## Customizing the palette
493
530
 
494
531
  On desktop the palette is a rail down the left-hand side, with a chevron handle on its outer border that hides and restores it — collapsed, the preview gets the full width, and the rail comes back with its open sections and search exactly as they were. Below the `md` breakpoint there's no rail: the toolbar's **Blocks** button opens the palette as a bottom sheet instead, and picking a block closes it.
@@ -525,6 +562,28 @@ const PALETTE: readonly PaletteSection[] = [
525
562
 
526
563
  Variant `id`s must be unique across the array — the drag-drop lookup keys by id.
527
564
 
565
+ ### Badges and locked variants
566
+
567
+ A variant can carry a `badge`, a small tag beside its label such as the plan
568
+ it belongs to. Set `locked` as well to show a block without letting the user
569
+ add it: it can't be dragged, has no add button, and picking it calls
570
+ `onLockedSelect`, for example to open an upgrade prompt.
571
+
572
+ ```tsx
573
+ const shareVariant: PaletteVariant = {
574
+ id: "linkedin_share",
575
+ label: "Share on LinkedIn",
576
+ factory: () => linkedInShareBlock(),
577
+ badge: { label: "Enterprise", icon: Crown },
578
+ locked: !plan.includesLinkedIn,
579
+ onLockedSelect: () => openUpgradeDialog(),
580
+ };
581
+ ```
582
+
583
+ A locked variant without `onLockedSelect` is inert. In the mobile sheet,
584
+ picking one closes the sheet first, so whatever the host opens isn't stacked
585
+ on top of it.
586
+
528
587
  ### Simple vs advanced palette
529
588
 
530
589
  By default the palette opens on everything it has: every section, plus the search input. For consumers whose users are writing ordinary messages rather than building interactive apps, `paletteMode="simple"` opens on a short starter list instead — no sections, no search — behind an **Advanced** link that swaps in the full palette (and back).
@@ -728,8 +787,8 @@ Defense-in-depth: blocks are validated against [slack-block-kit-validator](https
728
787
  Every edit to the draft is undoable — adding, deleting, duplicating,
729
788
  reordering, dragging in and out of containers, editing a block's fields, and
730
789
  the toolbar's **Clear** and **View JSON → apply**. The toolbar shows **Undo**
731
- and **Redo** buttons (disabled when there's nothing to step to), and the
732
- keyboard shortcuts work whenever focus is inside the builder:
790
+ and **Redo** buttons (disabled when there's nothing to step to), and these
791
+ keyboard shortcuts do the same:
733
792
 
734
793
  | Shortcut | Action |
735
794
  |---|---|
@@ -743,10 +802,23 @@ Notes:
743
802
  character at a time. Editing a different block, or any structural change,
744
803
  starts a new step.
745
804
  - **Native text undo is preserved.** While a text input, textarea, or the
746
- inline rich-text editor is focused, `Ctrl/Cmd+Z` performs the browser's
747
- own per-character undo — the builder only takes over when focus is on the
748
- canvas or toolbar. The shortcut is scoped to the builder, so it never
749
- hijacks the host app's own `Ctrl/Cmd+Z`.
805
+ inline rich-text editor is focused (including the fields in a block's
806
+ editor popover), `Ctrl/Cmd+Z` performs the browser's own per-character
807
+ undo. The builder only takes over when focus is on the canvas or toolbar.
808
+ - **The shortcuts are scoped to the builder**, so they don't hijack the host
809
+ page's own `Ctrl/Cmd+Z`. They act while focus is inside the builder, or
810
+ while nothing on the page has focus (focus is on `<body>`, or on a host
811
+ element wrapping the builder) and the last click landed in the builder.
812
+ That second case covers a click on the canvas background, or deleting a
813
+ block, which unmounts the Delete button that had focus. After a click
814
+ elsewhere on the page, or with focus in one of the host's own fields, the
815
+ keys are left alone.
816
+ - With nothing to undo or redo, or when something else already handled the
817
+ key press (`defaultPrevented`), the shortcut does nothing and doesn't call
818
+ `preventDefault`.
819
+ - **Opting out.** Pass `undoRedoShortcuts={false}` to turn the shortcuts off
820
+ and leave those keys to the host. The toolbar buttons still work, and
821
+ their tooltips stop mentioning the shortcuts.
750
822
  - **Loading is a fresh baseline.** [Loading an existing
751
823
  message](#loading-an-existing-message-opt-in) (or "open as new") starts a
752
824
  new document with an empty history — undo won't step back across the load
@@ -1,5 +1,5 @@
1
- import { S as SupportedBlock } from './palette-Bup6i-9c.cjs';
2
- export { aa as SupportedBlockType } from './palette-Bup6i-9c.cjs';
1
+ import { S as SupportedBlock } from './palette-DTE9U5si.cjs';
2
+ export { ad as SupportedBlockType } from './palette-DTE9U5si.cjs';
3
3
  import 'react';
4
4
  import 'slack-web-api-client';
5
5
 
package/dist/helpers.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SupportedBlock } from './palette-Bup6i-9c.js';
2
- export { aa as SupportedBlockType } from './palette-Bup6i-9c.js';
1
+ import { S as SupportedBlock } from './palette-DTE9U5si.js';
2
+ export { ad as SupportedBlockType } from './palette-DTE9U5si.js';
3
3
  import 'react';
4
4
  import 'slack-web-api-client';
5
5