@tightknitai/block-kitchen 0.10.23 → 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 +92 -6
- package/dist/helpers.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.cjs +508 -335
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +510 -337
- package/dist/index.js.map +1 -1
- package/dist/{palette-DFTL0Han.d.cts → palette-DTE9U5si.d.cts} +76 -1
- package/dist/{palette-DFTL0Han.d.ts → palette-DTE9U5si.d.ts} +76 -1
- package/dist/palette.cjs.map +1 -1
- package/dist/palette.d.cts +1 -1
- package/dist/palette.d.ts +1 -1
- package/dist/palette.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,6 +139,8 @@ Given no height to work with — a plain `<div>` in ordinary document flow — i
|
|
|
139
139
|
| `confirmUpdateLabel` | `string` | no | Label for the update dialog's final confirm button. Defaults to `'Update message'` (shows `'Updating…'` while in flight). |
|
|
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
|
+
| `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). |
|
|
142
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. |
|
|
143
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). |
|
|
144
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). |
|
|
@@ -149,6 +151,7 @@ Given no height to work with — a plain `<div>` in ordinary document flow — i
|
|
|
149
151
|
| `paletteSearchPlaceholder` | `string` | no | Placeholder text for the palette search input. Defaults to `'Search blocks…'`. Useful for localization. |
|
|
150
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. |
|
|
151
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). |
|
|
152
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. |
|
|
153
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). |
|
|
154
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…'`. |
|
|
@@ -467,6 +470,19 @@ top-level block). Map it onto the blocks `onChange` last reported; builder
|
|
|
467
470
|
ids never leave the package. An `svg` icon is sized to match the built-in
|
|
468
471
|
ones.
|
|
469
472
|
|
|
473
|
+
For something that should show all the time, like a count of comments on
|
|
474
|
+
each block, pass `renderBlockAdornment`. It's called with the same position
|
|
475
|
+
and renders beside the block, at its right edge; return `null` for a block
|
|
476
|
+
with nothing to show:
|
|
477
|
+
|
|
478
|
+
```tsx
|
|
479
|
+
<BlockKitchen
|
|
480
|
+
renderBlockAdornment={({ index, childIndex }) =>
|
|
481
|
+
childIndex === null && counts[index] ? <CommentCount count={counts[index]} /> : null
|
|
482
|
+
}
|
|
483
|
+
/>
|
|
484
|
+
```
|
|
485
|
+
|
|
470
486
|
Each block's row also carries `data-bk-block-index` (plus
|
|
471
487
|
`data-bk-child-index` inside a container), so the host can find a block on
|
|
472
488
|
the canvas, for example to scroll to it or highlight it:
|
|
@@ -475,6 +491,41 @@ the canvas, for example to scroll to it or highlight it:
|
|
|
475
491
|
document.querySelector(`.bk-root [data-bk-block-index="${index}"]:not([data-bk-child-index])`);
|
|
476
492
|
```
|
|
477
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
|
+
|
|
478
529
|
## Customizing the palette
|
|
479
530
|
|
|
480
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.
|
|
@@ -511,6 +562,28 @@ const PALETTE: readonly PaletteSection[] = [
|
|
|
511
562
|
|
|
512
563
|
Variant `id`s must be unique across the array — the drag-drop lookup keys by id.
|
|
513
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
|
+
|
|
514
587
|
### Simple vs advanced palette
|
|
515
588
|
|
|
516
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).
|
|
@@ -714,8 +787,8 @@ Defense-in-depth: blocks are validated against [slack-block-kit-validator](https
|
|
|
714
787
|
Every edit to the draft is undoable — adding, deleting, duplicating,
|
|
715
788
|
reordering, dragging in and out of containers, editing a block's fields, and
|
|
716
789
|
the toolbar's **Clear** and **View JSON → apply**. The toolbar shows **Undo**
|
|
717
|
-
and **Redo** buttons (disabled when there's nothing to step to), and
|
|
718
|
-
keyboard shortcuts
|
|
790
|
+
and **Redo** buttons (disabled when there's nothing to step to), and these
|
|
791
|
+
keyboard shortcuts do the same:
|
|
719
792
|
|
|
720
793
|
| Shortcut | Action |
|
|
721
794
|
|---|---|
|
|
@@ -729,10 +802,23 @@ Notes:
|
|
|
729
802
|
character at a time. Editing a different block, or any structural change,
|
|
730
803
|
starts a new step.
|
|
731
804
|
- **Native text undo is preserved.** While a text input, textarea, or the
|
|
732
|
-
inline rich-text editor is focused
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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.
|
|
736
822
|
- **Loading is a fresh baseline.** [Loading an existing
|
|
737
823
|
message](#loading-an-existing-message-opt-in) (or "open as new") starts a
|
|
738
824
|
new document with an empty history — undo won't step back across the load
|
package/dist/helpers.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SupportedBlock } from './palette-
|
|
2
|
-
export {
|
|
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-
|
|
2
|
-
export {
|
|
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
|
|