@tightknitai/block-kitchen 0.10.20 → 0.10.21
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 +13 -4
- package/dist/helpers.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.cjs +268 -81
- 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 +269 -82
- package/dist/index.js.map +1 -1
- package/dist/{palette-4BJw_nd7.d.cts → palette-CXXqlwIH.d.cts} +29 -9
- package/dist/{palette-4BJw_nd7.d.ts → palette-CXXqlwIH.d.ts} +29 -9
- package/dist/palette.d.cts +1 -1
- package/dist/palette.d.ts +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { B as BlockKitchenProps, S as SupportedBlock, C as ChannelOption, a as SendAsUserStatus, b as SendPayload, c as SendExtrasContext, T as Template, P as PreviewSurface, d as PreviewTheme } from './palette-
|
|
4
|
-
export { A as AlertBlock, e as AlertLevel, f as BlockKitchenBaseProps, g as BlockKitchenComposeOnlyProps, h as BlockKitchenSendProps, i as BrandPreset, j as BrandTheme, k as BrandTokens, l as BuilderBlock, m as CardBlock, n as CarouselBlock, o as CartesianChart, p as Chart, q as ChartAxisConfig, r as ChartDataPoint, s as ChartSeries, t as ChartType, u as ContainerBlock, v as ContainerChildBlock, w as ContextActionsBlock, x as ContextActionsElement, y as CustomEmoji, D as DataTableActionCell, z as DataTableBlock, E as DataTableCell, F as DataTableRawNumberCell, G as DataVisualizationBlock, H as EditableVia, I as FeedbackButtonSubobject, J as FeedbackButtonsElement, K as HeaderLevel, L as IconButtonElement, M as IconButtonIcon, N as InputBlock, O as LoadMessageInput, Q as LoadResult, R as LoadedMessage, U as LoadingConfig, V as MarkdownBlock, W as
|
|
3
|
+
import { B as BlockKitchenProps, S as SupportedBlock, C as ChannelOption, a as SendAsUserStatus, b as SendPayload, c as SendExtrasContext, T as Template, P as PreviewSurface, d as PreviewTheme } from './palette-CXXqlwIH.cjs';
|
|
4
|
+
export { A as AlertBlock, e as AlertLevel, f as BlockKitchenBaseProps, g as BlockKitchenComposeOnlyProps, h as BlockKitchenSendProps, i as BrandPreset, j as BrandTheme, k as BrandTokens, l as BuilderBlock, m as CardBlock, n as CarouselBlock, o as CartesianChart, p as Chart, q as ChartAxisConfig, r as ChartDataPoint, s as ChartSeries, t as ChartType, u as ContainerBlock, v as ContainerChildBlock, w as ContextActionsBlock, x as ContextActionsElement, y as CustomEmoji, D as DataTableActionCell, z as DataTableBlock, E as DataTableCell, F as DataTableRawNumberCell, G as DataVisualizationBlock, H as EditableVia, I as FeedbackButtonSubobject, J as FeedbackButtonsElement, K as HeaderLevel, L as IconButtonElement, M as IconButtonIcon, N as InputBlock, O as LoadMessageInput, Q as LoadResult, R as LoadedMessage, U as LoadingConfig, V as MarkdownBlock, W as MentionChannel, X as MentionUser, Y as MentionsConfig, Z as PaletteMode, _ as PaletteSection, $ as PaletteVariant, a0 as PieChart, a1 as PieChartSegment, a2 as PlanBlock, a3 as PreviewHooks, a4 as PrimaryActionConfig, a5 as PrimaryActionContext, a6 as RecentMessage, a7 as SendResult, a8 as SupportedBlockType, a9 as SupportedHeaderBlock, aa as TableBlock, ab as TableCell, ac as TableColumnSetting, ad as TableRawTextCell, ae as TaskCardBlock, af as TaskCardStatus, ag as UpdatePayload, ah as UpdateResult, ai as UrlSourceElement, aj as ValidationSummary, ak as VideoBlock, al as defaultPalette, am as extraAlertVariant, an as legacyInputVariants } from './palette-CXXqlwIH.cjs';
|
|
5
5
|
export { decodeBlocksFromString, encodeBlocksToString, toSlackBlocks } from './helpers.cjs';
|
|
6
6
|
import 'slack-web-api-client';
|
|
7
7
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { B as BlockKitchenProps, S as SupportedBlock, C as ChannelOption, a as SendAsUserStatus, b as SendPayload, c as SendExtrasContext, T as Template, P as PreviewSurface, d as PreviewTheme } from './palette-
|
|
4
|
-
export { A as AlertBlock, e as AlertLevel, f as BlockKitchenBaseProps, g as BlockKitchenComposeOnlyProps, h as BlockKitchenSendProps, i as BrandPreset, j as BrandTheme, k as BrandTokens, l as BuilderBlock, m as CardBlock, n as CarouselBlock, o as CartesianChart, p as Chart, q as ChartAxisConfig, r as ChartDataPoint, s as ChartSeries, t as ChartType, u as ContainerBlock, v as ContainerChildBlock, w as ContextActionsBlock, x as ContextActionsElement, y as CustomEmoji, D as DataTableActionCell, z as DataTableBlock, E as DataTableCell, F as DataTableRawNumberCell, G as DataVisualizationBlock, H as EditableVia, I as FeedbackButtonSubobject, J as FeedbackButtonsElement, K as HeaderLevel, L as IconButtonElement, M as IconButtonIcon, N as InputBlock, O as LoadMessageInput, Q as LoadResult, R as LoadedMessage, U as LoadingConfig, V as MarkdownBlock, W as
|
|
3
|
+
import { B as BlockKitchenProps, S as SupportedBlock, C as ChannelOption, a as SendAsUserStatus, b as SendPayload, c as SendExtrasContext, T as Template, P as PreviewSurface, d as PreviewTheme } from './palette-CXXqlwIH.js';
|
|
4
|
+
export { A as AlertBlock, e as AlertLevel, f as BlockKitchenBaseProps, g as BlockKitchenComposeOnlyProps, h as BlockKitchenSendProps, i as BrandPreset, j as BrandTheme, k as BrandTokens, l as BuilderBlock, m as CardBlock, n as CarouselBlock, o as CartesianChart, p as Chart, q as ChartAxisConfig, r as ChartDataPoint, s as ChartSeries, t as ChartType, u as ContainerBlock, v as ContainerChildBlock, w as ContextActionsBlock, x as ContextActionsElement, y as CustomEmoji, D as DataTableActionCell, z as DataTableBlock, E as DataTableCell, F as DataTableRawNumberCell, G as DataVisualizationBlock, H as EditableVia, I as FeedbackButtonSubobject, J as FeedbackButtonsElement, K as HeaderLevel, L as IconButtonElement, M as IconButtonIcon, N as InputBlock, O as LoadMessageInput, Q as LoadResult, R as LoadedMessage, U as LoadingConfig, V as MarkdownBlock, W as MentionChannel, X as MentionUser, Y as MentionsConfig, Z as PaletteMode, _ as PaletteSection, $ as PaletteVariant, a0 as PieChart, a1 as PieChartSegment, a2 as PlanBlock, a3 as PreviewHooks, a4 as PrimaryActionConfig, a5 as PrimaryActionContext, a6 as RecentMessage, a7 as SendResult, a8 as SupportedBlockType, a9 as SupportedHeaderBlock, aa as TableBlock, ab as TableCell, ac as TableColumnSetting, ad as TableRawTextCell, ae as TaskCardBlock, af as TaskCardStatus, ag as UpdatePayload, ah as UpdateResult, ai as UrlSourceElement, aj as ValidationSummary, ak as VideoBlock, al as defaultPalette, am as extraAlertVariant, an as legacyInputVariants } from './palette-CXXqlwIH.js';
|
|
5
5
|
export { decodeBlocksFromString, encodeBlocksToString, toSlackBlocks } from './helpers.js';
|
|
6
6
|
import 'slack-web-api-client';
|
|
7
7
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, pointerWithin, closestCenter, DndContext, DragOverlay, useDroppable, useDraggable } from '@dnd-kit/core';
|
|
2
2
|
import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
|
3
|
-
import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, Loader2, ExternalLink, AlertTriangle, ChevronRight, ChevronLeft, GripVertical, ChevronsUpDown, Check, Sun, Moon, MessageSquare, AppWindow, Home, Plus, ChevronDown, BookOpen, Undo2, Redo2, Trash2, Code2, Pencil, Send, Search, Copy, Info, CheckCircle2, LayoutGrid, ArrowUp, ArrowDown, Type, Bold, Italic, Strikethrough, Underline, Code, AtSign, List, ListOrdered, Quote,
|
|
3
|
+
import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, Loader2, ExternalLink, AlertTriangle, ChevronRight, ChevronLeft, GripVertical, ChevronsUpDown, Check, Sun, Moon, MessageSquare, AppWindow, Home, Plus, ChevronDown, BookOpen, Undo2, Redo2, Trash2, Code2, Pencil, Send, Search, Copy, Info, CheckCircle2, LayoutGrid, ArrowUp, ArrowDown, Type, Bold, Italic, Strikethrough, Underline, Code, AtSign, Hash, List, ListOrdered, Quote, Smile, Link as Link$1 } from 'lucide-react';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { createContext, useContext, useState, useRef, useEffect, useCallback, useMemo, useId } from 'react';
|
|
6
6
|
import { clsx } from 'clsx';
|
|
@@ -21,6 +21,7 @@ import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
|
21
21
|
import Link from '@tiptap/extension-link';
|
|
22
22
|
import { Node as Node$1, ReactNodeViewRenderer, mergeAttributes, Extension, NodeViewWrapper, useEditor, EditorContent } from '@tiptap/react';
|
|
23
23
|
import StarterKit from '@tiptap/starter-kit';
|
|
24
|
+
import { PluginKey } from '@tiptap/pm/state';
|
|
24
25
|
import { Suggestion, exitSuggestion } from '@tiptap/suggestion';
|
|
25
26
|
|
|
26
27
|
// src/components/block-kitchen.tsx
|
|
@@ -5244,15 +5245,16 @@ function makeEmojiImportResolver(customByName, index = getEmojiIndex()) {
|
|
|
5244
5245
|
|
|
5245
5246
|
// src/lib/rich-text-tiptap.ts
|
|
5246
5247
|
var USER_MENTION_NODE = "userMention";
|
|
5248
|
+
var CHANNEL_MENTION_NODE = "channelMention";
|
|
5247
5249
|
var SUPPORTED_BLOCK_KINDS = /* @__PURE__ */ new Set([
|
|
5248
5250
|
"rich_text_section",
|
|
5249
5251
|
"rich_text_list",
|
|
5250
5252
|
"rich_text_quote",
|
|
5251
5253
|
"rich_text_preformatted"
|
|
5252
5254
|
]);
|
|
5253
|
-
var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link", "emoji", "user"]);
|
|
5255
|
+
var SUPPORTED_INLINE_KINDS = /* @__PURE__ */ new Set(["text", "link", "emoji", "user", "channel"]);
|
|
5254
5256
|
var SUPPORTED_TEXT_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "underline", "code"]);
|
|
5255
|
-
var
|
|
5257
|
+
var SUPPORTED_MENTION_STYLE_KEYS = /* @__PURE__ */ new Set(["bold", "italic", "strike", "underline"]);
|
|
5256
5258
|
var MAX_LIST_INDENT = 8;
|
|
5257
5259
|
function detectLossy(block) {
|
|
5258
5260
|
const out = [];
|
|
@@ -5307,10 +5309,13 @@ function scanInlines(inlines, where, out) {
|
|
|
5307
5309
|
}
|
|
5308
5310
|
}
|
|
5309
5311
|
}
|
|
5310
|
-
if (inline.type === "user") {
|
|
5312
|
+
if (inline.type === "user" || inline.type === "channel") {
|
|
5313
|
+
if (inline.type === "channel" && !inline.channel_id) {
|
|
5314
|
+
out.push({ label: "Channel without id", where: sub });
|
|
5315
|
+
}
|
|
5311
5316
|
for (const k of Object.keys(inline.style ?? {})) {
|
|
5312
|
-
if (!
|
|
5313
|
-
out.push({ label:
|
|
5317
|
+
if (!SUPPORTED_MENTION_STYLE_KEYS.has(k)) {
|
|
5318
|
+
out.push({ label: `${inline.type === "channel" ? "Channel" : "Mention"} style: ${k}`, where: sub });
|
|
5314
5319
|
}
|
|
5315
5320
|
}
|
|
5316
5321
|
}
|
|
@@ -5436,6 +5441,20 @@ function inlinesToPM(inlines, resolveEmoji) {
|
|
|
5436
5441
|
node.marks = marks;
|
|
5437
5442
|
}
|
|
5438
5443
|
out.push(node);
|
|
5444
|
+
} else if (inline.type === "channel") {
|
|
5445
|
+
const channel = inline;
|
|
5446
|
+
if (!channel.channel_id) {
|
|
5447
|
+
continue;
|
|
5448
|
+
}
|
|
5449
|
+
const node = {
|
|
5450
|
+
type: CHANNEL_MENTION_NODE,
|
|
5451
|
+
attrs: { channelId: channel.channel_id, name: null }
|
|
5452
|
+
};
|
|
5453
|
+
const marks = styleToMarks(channel.style);
|
|
5454
|
+
if (marks.length > 0) {
|
|
5455
|
+
node.marks = marks;
|
|
5456
|
+
}
|
|
5457
|
+
out.push(node);
|
|
5439
5458
|
} else if (inline.type === "link") {
|
|
5440
5459
|
const link = inline;
|
|
5441
5460
|
const text = link.text || link.url || "";
|
|
@@ -5575,13 +5594,26 @@ function proseMirrorInlinesToRichTextElements(nodes) {
|
|
|
5575
5594
|
continue;
|
|
5576
5595
|
}
|
|
5577
5596
|
const user = { type: "user", user_id: userId };
|
|
5578
|
-
const style2 =
|
|
5597
|
+
const style2 = mentionStyle(node.marks ?? []);
|
|
5579
5598
|
if (Object.keys(style2).length > 0) {
|
|
5580
5599
|
user.style = style2;
|
|
5581
5600
|
}
|
|
5582
5601
|
out.push(user);
|
|
5583
5602
|
continue;
|
|
5584
5603
|
}
|
|
5604
|
+
if (node.type === CHANNEL_MENTION_NODE) {
|
|
5605
|
+
const channelId = String(node.attrs?.channelId ?? "");
|
|
5606
|
+
if (!channelId) {
|
|
5607
|
+
continue;
|
|
5608
|
+
}
|
|
5609
|
+
const channel = { type: "channel", channel_id: channelId };
|
|
5610
|
+
const style2 = mentionStyle(node.marks ?? []);
|
|
5611
|
+
if (Object.keys(style2).length > 0) {
|
|
5612
|
+
channel.style = style2;
|
|
5613
|
+
}
|
|
5614
|
+
out.push(channel);
|
|
5615
|
+
continue;
|
|
5616
|
+
}
|
|
5585
5617
|
if (node.type === "hardBreak") {
|
|
5586
5618
|
out.push({ type: "text", text: "\n" });
|
|
5587
5619
|
continue;
|
|
@@ -5636,10 +5668,10 @@ function marksToStyle(marks) {
|
|
|
5636
5668
|
}
|
|
5637
5669
|
return style;
|
|
5638
5670
|
}
|
|
5639
|
-
function
|
|
5671
|
+
function mentionStyle(marks) {
|
|
5640
5672
|
const style = {};
|
|
5641
5673
|
for (const [key, value] of Object.entries(marksToStyle(marks))) {
|
|
5642
|
-
if (
|
|
5674
|
+
if (SUPPORTED_MENTION_STYLE_KEYS.has(key)) {
|
|
5643
5675
|
style[key] = value;
|
|
5644
5676
|
}
|
|
5645
5677
|
}
|
|
@@ -5668,50 +5700,52 @@ function sameStyle(a, b) {
|
|
|
5668
5700
|
}
|
|
5669
5701
|
return ak.every((k, i) => k === bk[i]);
|
|
5670
5702
|
}
|
|
5671
|
-
function
|
|
5672
|
-
|
|
5673
|
-
const label = `:${name}:`;
|
|
5674
|
-
return /* @__PURE__ */ jsx(NodeViewWrapper, { as: "span", className: "bk-emoji-node", "data-emoji": name, title: label, children: src ? /* @__PURE__ */ jsx(
|
|
5675
|
-
"img",
|
|
5676
|
-
{
|
|
5677
|
-
src,
|
|
5678
|
-
alt: label,
|
|
5679
|
-
style: {
|
|
5680
|
-
display: "inline-block",
|
|
5681
|
-
width: "1.25em",
|
|
5682
|
-
height: "1.25em",
|
|
5683
|
-
verticalAlign: "-0.25em",
|
|
5684
|
-
objectFit: "contain"
|
|
5685
|
-
}
|
|
5686
|
-
}
|
|
5687
|
-
) : /* @__PURE__ */ jsx("span", { "aria-label": label, role: "img", children: unicode ? codepointsToGlyph(unicode) : label }) });
|
|
5703
|
+
function channelMentionLabel(attrs, getChannelName) {
|
|
5704
|
+
return getChannelName?.(attrs.channelId) ?? attrs.name ?? attrs.channelId;
|
|
5688
5705
|
}
|
|
5689
|
-
|
|
5690
|
-
|
|
5706
|
+
function ChannelMentionNodeView({ node }) {
|
|
5707
|
+
const mentions = useMentions();
|
|
5708
|
+
const attrs = node.attrs;
|
|
5709
|
+
return /* @__PURE__ */ jsxs(NodeViewWrapper, { as: "span", className: "bk-channel-mention", "data-channel-mention": attrs.channelId, children: [
|
|
5710
|
+
"#",
|
|
5711
|
+
channelMentionLabel(attrs, mentions?.getChannelName)
|
|
5712
|
+
] });
|
|
5713
|
+
}
|
|
5714
|
+
var ChannelMentionNode = Node$1.create({
|
|
5715
|
+
name: CHANNEL_MENTION_NODE,
|
|
5691
5716
|
group: "inline",
|
|
5692
5717
|
inline: true,
|
|
5693
5718
|
atom: true,
|
|
5694
5719
|
selectable: true,
|
|
5695
5720
|
addAttributes() {
|
|
5696
5721
|
return {
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5722
|
+
channelId: {
|
|
5723
|
+
default: "",
|
|
5724
|
+
parseHTML: (element) => element.getAttribute("data-channel-mention") ?? "",
|
|
5725
|
+
renderHTML: (attributes) => ({ "data-channel-mention": attributes.channelId })
|
|
5726
|
+
},
|
|
5727
|
+
name: {
|
|
5728
|
+
default: null,
|
|
5729
|
+
parseHTML: (element) => element.getAttribute("data-channel-name"),
|
|
5730
|
+
renderHTML: (attributes) => attributes.name ? { "data-channel-name": attributes.name } : {}
|
|
5731
|
+
}
|
|
5701
5732
|
};
|
|
5702
5733
|
},
|
|
5703
5734
|
parseHTML() {
|
|
5704
|
-
return [{ tag: "span[data-
|
|
5735
|
+
return [{ tag: "span[data-channel-mention]" }];
|
|
5705
5736
|
},
|
|
5706
|
-
renderHTML({ HTMLAttributes }) {
|
|
5707
|
-
|
|
5708
|
-
|
|
5737
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
5738
|
+
return ["span", mergeAttributes(HTMLAttributes), `#${channelMentionLabel(node.attrs)}`];
|
|
5739
|
+
},
|
|
5740
|
+
renderText({ node }) {
|
|
5741
|
+
return `#${channelMentionLabel(node.attrs)}`;
|
|
5709
5742
|
},
|
|
5710
5743
|
addNodeView() {
|
|
5711
|
-
return ReactNodeViewRenderer(
|
|
5744
|
+
return ReactNodeViewRenderer(ChannelMentionNodeView);
|
|
5712
5745
|
}
|
|
5713
5746
|
});
|
|
5714
5747
|
var MENTION_SEARCH_DEBOUNCE_MS = 200;
|
|
5748
|
+
var USER_MENTION_SUGGESTION_PLUGIN_KEY = new PluginKey("userMentionSuggestion");
|
|
5715
5749
|
function insertUserMention(editor, range, user) {
|
|
5716
5750
|
const to = editor.state.doc.textBetween(range.to, Math.min(range.to + 1, editor.state.doc.content.size)) === " " ? range.to + 1 : range.to;
|
|
5717
5751
|
editor.chain().focus().insertContentAt({ from: range.from, to }, [
|
|
@@ -5720,13 +5754,16 @@ function insertUserMention(editor, range, user) {
|
|
|
5720
5754
|
]).run();
|
|
5721
5755
|
}
|
|
5722
5756
|
function insertMentionTrigger(editor) {
|
|
5757
|
+
insertSuggestionTrigger(editor, "@");
|
|
5758
|
+
}
|
|
5759
|
+
function insertSuggestionTrigger(editor, char) {
|
|
5723
5760
|
const { from } = editor.state.selection;
|
|
5724
5761
|
const before = from > 1 ? editor.state.doc.textBetween(from - 1, from, void 0, "\uFFFC") : "";
|
|
5725
5762
|
const needsSpace = before !== "" && !/\s/.test(before);
|
|
5726
|
-
editor.chain().focus().insertContent(needsSpace ?
|
|
5763
|
+
editor.chain().focus().insertContent(needsSpace ? ` ${char}` : char).run();
|
|
5727
5764
|
}
|
|
5728
5765
|
function dismissMentionSuggestion(editor) {
|
|
5729
|
-
exitSuggestion(editor.view);
|
|
5766
|
+
exitSuggestion(editor.view, USER_MENTION_SUGGESTION_PLUGIN_KEY);
|
|
5730
5767
|
}
|
|
5731
5768
|
var MentionSuggestion = Extension.create({
|
|
5732
5769
|
name: "userMentionSuggestion",
|
|
@@ -5742,6 +5779,7 @@ var MentionSuggestion = Extension.create({
|
|
|
5742
5779
|
return [
|
|
5743
5780
|
Suggestion({
|
|
5744
5781
|
editor: this.editor,
|
|
5782
|
+
pluginKey: USER_MENTION_SUGGESTION_PLUGIN_KEY,
|
|
5745
5783
|
char: "@",
|
|
5746
5784
|
debounce: MENTION_SEARCH_DEBOUNCE_MS,
|
|
5747
5785
|
allow: () => isEnabled(),
|
|
@@ -5752,6 +5790,90 @@ var MentionSuggestion = Extension.create({
|
|
|
5752
5790
|
];
|
|
5753
5791
|
}
|
|
5754
5792
|
});
|
|
5793
|
+
|
|
5794
|
+
// src/lib/tiptap-channel-suggestion.ts
|
|
5795
|
+
var CHANNEL_MENTION_SUGGESTION_PLUGIN_KEY = new PluginKey("channelMentionSuggestion");
|
|
5796
|
+
function insertChannelMention(editor, range, channel) {
|
|
5797
|
+
const to = editor.state.doc.textBetween(range.to, Math.min(range.to + 1, editor.state.doc.content.size)) === " " ? range.to + 1 : range.to;
|
|
5798
|
+
editor.chain().focus().insertContentAt({ from: range.from, to }, [
|
|
5799
|
+
{ type: CHANNEL_MENTION_NODE, attrs: { channelId: channel.id, name: channel.name } },
|
|
5800
|
+
{ type: "text", text: " " }
|
|
5801
|
+
]).run();
|
|
5802
|
+
}
|
|
5803
|
+
function insertChannelTrigger(editor) {
|
|
5804
|
+
insertSuggestionTrigger(editor, "#");
|
|
5805
|
+
}
|
|
5806
|
+
function dismissChannelSuggestion(editor) {
|
|
5807
|
+
exitSuggestion(editor.view, CHANNEL_MENTION_SUGGESTION_PLUGIN_KEY);
|
|
5808
|
+
}
|
|
5809
|
+
var ChannelSuggestion = Extension.create({
|
|
5810
|
+
name: "channelMentionSuggestion",
|
|
5811
|
+
addOptions() {
|
|
5812
|
+
return {
|
|
5813
|
+
isEnabled: () => false,
|
|
5814
|
+
search: async () => [],
|
|
5815
|
+
render: () => ({})
|
|
5816
|
+
};
|
|
5817
|
+
},
|
|
5818
|
+
addProseMirrorPlugins() {
|
|
5819
|
+
const { isEnabled, search, render } = this.options;
|
|
5820
|
+
return [
|
|
5821
|
+
Suggestion({
|
|
5822
|
+
editor: this.editor,
|
|
5823
|
+
pluginKey: CHANNEL_MENTION_SUGGESTION_PLUGIN_KEY,
|
|
5824
|
+
char: "#",
|
|
5825
|
+
debounce: MENTION_SEARCH_DEBOUNCE_MS,
|
|
5826
|
+
allow: () => isEnabled(),
|
|
5827
|
+
items: ({ query, signal }) => search(query, signal),
|
|
5828
|
+
command: ({ editor, range, props }) => insertChannelMention(editor, range, props),
|
|
5829
|
+
render
|
|
5830
|
+
})
|
|
5831
|
+
];
|
|
5832
|
+
}
|
|
5833
|
+
});
|
|
5834
|
+
function EmojiNodeView({ node }) {
|
|
5835
|
+
const { name, src, unicode } = node.attrs;
|
|
5836
|
+
const label = `:${name}:`;
|
|
5837
|
+
return /* @__PURE__ */ jsx(NodeViewWrapper, { as: "span", className: "bk-emoji-node", "data-emoji": name, title: label, children: src ? /* @__PURE__ */ jsx(
|
|
5838
|
+
"img",
|
|
5839
|
+
{
|
|
5840
|
+
src,
|
|
5841
|
+
alt: label,
|
|
5842
|
+
style: {
|
|
5843
|
+
display: "inline-block",
|
|
5844
|
+
width: "1.25em",
|
|
5845
|
+
height: "1.25em",
|
|
5846
|
+
verticalAlign: "-0.25em",
|
|
5847
|
+
objectFit: "contain"
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5850
|
+
) : /* @__PURE__ */ jsx("span", { "aria-label": label, role: "img", children: unicode ? codepointsToGlyph(unicode) : label }) });
|
|
5851
|
+
}
|
|
5852
|
+
var EmojiNode = Node$1.create({
|
|
5853
|
+
name: "emoji",
|
|
5854
|
+
group: "inline",
|
|
5855
|
+
inline: true,
|
|
5856
|
+
atom: true,
|
|
5857
|
+
selectable: true,
|
|
5858
|
+
addAttributes() {
|
|
5859
|
+
return {
|
|
5860
|
+
name: { default: "" },
|
|
5861
|
+
src: { default: null },
|
|
5862
|
+
unicode: { default: null },
|
|
5863
|
+
skinTone: { default: null }
|
|
5864
|
+
};
|
|
5865
|
+
},
|
|
5866
|
+
parseHTML() {
|
|
5867
|
+
return [{ tag: "span[data-emoji]" }];
|
|
5868
|
+
},
|
|
5869
|
+
renderHTML({ HTMLAttributes }) {
|
|
5870
|
+
const name = HTMLAttributes.name ?? "";
|
|
5871
|
+
return ["span", mergeAttributes({ "data-emoji": name }, HTMLAttributes), `:${name}:`];
|
|
5872
|
+
},
|
|
5873
|
+
addNodeView() {
|
|
5874
|
+
return ReactNodeViewRenderer(EmojiNodeView);
|
|
5875
|
+
}
|
|
5876
|
+
});
|
|
5755
5877
|
function userMentionLabel(attrs, getUserName) {
|
|
5756
5878
|
return getUserName?.(attrs.userId) ?? attrs.name ?? attrs.userId;
|
|
5757
5879
|
}
|
|
@@ -6087,6 +6209,7 @@ function nextMentionPickerState(props, prev) {
|
|
|
6087
6209
|
}
|
|
6088
6210
|
function MentionPicker({
|
|
6089
6211
|
editor,
|
|
6212
|
+
kind,
|
|
6090
6213
|
state,
|
|
6091
6214
|
keyHandlerRef,
|
|
6092
6215
|
onDismiss
|
|
@@ -6099,6 +6222,27 @@ function MentionPicker({
|
|
|
6099
6222
|
setActiveIndex(0);
|
|
6100
6223
|
}
|
|
6101
6224
|
const listRef = useRef(null);
|
|
6225
|
+
const listboxId = useId();
|
|
6226
|
+
const activeOptionId = items[activeIndex] ? `${listboxId}-${activeIndex}` : void 0;
|
|
6227
|
+
useEffect(() => {
|
|
6228
|
+
const root = editor.view.dom;
|
|
6229
|
+
root.setAttribute("role", "combobox");
|
|
6230
|
+
root.setAttribute("aria-autocomplete", "list");
|
|
6231
|
+
root.setAttribute("aria-expanded", "true");
|
|
6232
|
+
root.setAttribute("aria-controls", listboxId);
|
|
6233
|
+
if (activeOptionId) {
|
|
6234
|
+
root.setAttribute("aria-activedescendant", activeOptionId);
|
|
6235
|
+
} else {
|
|
6236
|
+
root.removeAttribute("aria-activedescendant");
|
|
6237
|
+
}
|
|
6238
|
+
return () => {
|
|
6239
|
+
root.removeAttribute("role");
|
|
6240
|
+
root.removeAttribute("aria-autocomplete");
|
|
6241
|
+
root.removeAttribute("aria-expanded");
|
|
6242
|
+
root.removeAttribute("aria-controls");
|
|
6243
|
+
root.removeAttribute("aria-activedescendant");
|
|
6244
|
+
};
|
|
6245
|
+
}, [activeOptionId, editor, listboxId]);
|
|
6102
6246
|
useEffect(() => {
|
|
6103
6247
|
keyHandlerRef.current = (event) => {
|
|
6104
6248
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
@@ -6109,11 +6253,11 @@ function MentionPicker({
|
|
|
6109
6253
|
return true;
|
|
6110
6254
|
}
|
|
6111
6255
|
if (event.key === "Enter" || event.key === "Tab") {
|
|
6112
|
-
const
|
|
6113
|
-
if (!
|
|
6256
|
+
const item = items[activeIndex];
|
|
6257
|
+
if (!item) {
|
|
6114
6258
|
return false;
|
|
6115
6259
|
}
|
|
6116
|
-
command(
|
|
6260
|
+
command(item);
|
|
6117
6261
|
return true;
|
|
6118
6262
|
}
|
|
6119
6263
|
return false;
|
|
@@ -6160,29 +6304,39 @@ function MentionPicker({
|
|
|
6160
6304
|
e.preventDefault();
|
|
6161
6305
|
}
|
|
6162
6306
|
},
|
|
6163
|
-
children: /* @__PURE__ */ jsx(
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6307
|
+
children: /* @__PURE__ */ jsx(
|
|
6308
|
+
"div",
|
|
6309
|
+
{
|
|
6310
|
+
ref: listRef,
|
|
6311
|
+
id: listboxId,
|
|
6312
|
+
role: "listbox",
|
|
6313
|
+
"aria-label": kind === "channel" ? "Channels to mention" : "People to mention",
|
|
6314
|
+
children: items.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-2 py-3 text-center text-xs text-muted-foreground", children: loading ? "Searching\u2026" : `No ${kind === "channel" ? "channels" : "people"} match that search.` }) : items.map((item, index) => (
|
|
6315
|
+
// biome-ignore lint/a11y/useFocusableInteractive: focus stays in the editor, which drives this list through the suggestion plugin's key handling.
|
|
6316
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: the keyboard path is the editor's, forwarded via keyHandlerRef.
|
|
6317
|
+
/* @__PURE__ */ jsxs(
|
|
6318
|
+
"div",
|
|
6319
|
+
{
|
|
6320
|
+
id: `${listboxId}-${index}`,
|
|
6321
|
+
role: "option",
|
|
6322
|
+
"aria-selected": index === activeIndex,
|
|
6323
|
+
onPointerDown: (e) => e.preventDefault(),
|
|
6324
|
+
onClick: () => command(item),
|
|
6325
|
+
onMouseMove: () => setActiveIndex(index),
|
|
6326
|
+
className: cn(
|
|
6327
|
+
"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-foreground",
|
|
6328
|
+
index === activeIndex && "bg-accent"
|
|
6329
|
+
),
|
|
6330
|
+
children: [
|
|
6331
|
+
kind === "channel" ? /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(Hash, { className: "h-3.5 w-3.5" }) }) : /* @__PURE__ */ jsx(MentionAvatar, { user: item }),
|
|
6332
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: kind === "channel" ? `#${item.name}` : item.name })
|
|
6333
|
+
]
|
|
6334
|
+
},
|
|
6335
|
+
item.id
|
|
6336
|
+
)
|
|
6337
|
+
))
|
|
6338
|
+
}
|
|
6339
|
+
)
|
|
6186
6340
|
}
|
|
6187
6341
|
)
|
|
6188
6342
|
]
|
|
@@ -6779,7 +6933,7 @@ function LossyBanner({ reasons }) {
|
|
|
6779
6933
|
] });
|
|
6780
6934
|
}
|
|
6781
6935
|
var RICH_TEXT_INPUT_RULE_ALLOWLIST = ["bold", "italic", "strike", "code"];
|
|
6782
|
-
function buildRichTextExtensions(mentionSuggestion) {
|
|
6936
|
+
function buildRichTextExtensions(mentionSuggestion, channelSuggestion) {
|
|
6783
6937
|
return [
|
|
6784
6938
|
StarterKit.configure({
|
|
6785
6939
|
heading: false,
|
|
@@ -6813,10 +6967,11 @@ function buildRichTextExtensions(mentionSuggestion) {
|
|
|
6813
6967
|
shouldAutoLink: hasExplicitSafeScheme,
|
|
6814
6968
|
HTMLAttributes: { rel: "noreferrer noopener", target: "_blank" }
|
|
6815
6969
|
}),
|
|
6816
|
-
// Always registered, so mentions already in the content round-trip with
|
|
6817
|
-
// or without the picker.
|
|
6970
|
+
// Always registered, so mentions already in the content round-trip with or without pickers.
|
|
6818
6971
|
UserMentionNode,
|
|
6819
|
-
|
|
6972
|
+
ChannelMentionNode,
|
|
6973
|
+
...mentionSuggestion ? [MentionSuggestion.configure(mentionSuggestion)] : [],
|
|
6974
|
+
...channelSuggestion ? [ChannelSuggestion.configure(channelSuggestion)] : []
|
|
6820
6975
|
];
|
|
6821
6976
|
}
|
|
6822
6977
|
function RichTextWysiwygEditor({ block, onChange }) {
|
|
@@ -6844,24 +6999,39 @@ function RichTextWysiwygEditor({ block, onChange }) {
|
|
|
6844
6999
|
[onChange]
|
|
6845
7000
|
);
|
|
6846
7001
|
const mentions = useMentions();
|
|
6847
|
-
const
|
|
7002
|
+
const userMentionsEnabled = mentions !== null;
|
|
7003
|
+
const channelMentionsEnabled = mentions?.searchChannels !== void 0;
|
|
6848
7004
|
const mentionsRef = useRef(mentions);
|
|
6849
7005
|
useEffect(() => {
|
|
6850
7006
|
mentionsRef.current = mentions;
|
|
6851
7007
|
}, [mentions]);
|
|
6852
7008
|
const [mentionPicker, setMentionPicker] = useState(null);
|
|
6853
7009
|
const mentionKeyHandlerRef = useRef(() => false);
|
|
7010
|
+
const [channelPicker, setChannelPicker] = useState(null);
|
|
7011
|
+
const channelKeyHandlerRef = useRef(() => false);
|
|
6854
7012
|
const editor = useEditor({
|
|
6855
|
-
extensions: buildRichTextExtensions(
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
7013
|
+
extensions: buildRichTextExtensions(
|
|
7014
|
+
{
|
|
7015
|
+
isEnabled: () => mentionsRef.current !== null,
|
|
7016
|
+
search: async (query, signal) => await mentionsRef.current?.searchUsers(query, { signal }) ?? [],
|
|
7017
|
+
render: () => ({
|
|
7018
|
+
onStart: (props) => setMentionPicker((prev) => nextMentionPickerState(props, prev)),
|
|
7019
|
+
onUpdate: (props) => setMentionPicker((prev) => nextMentionPickerState(props, prev)),
|
|
7020
|
+
onExit: () => setMentionPicker(null),
|
|
7021
|
+
onKeyDown: ({ event }) => mentionKeyHandlerRef.current(event)
|
|
7022
|
+
})
|
|
7023
|
+
},
|
|
7024
|
+
{
|
|
7025
|
+
isEnabled: () => mentionsRef.current?.searchChannels !== void 0,
|
|
7026
|
+
search: async (query, signal) => await mentionsRef.current?.searchChannels?.(query, { signal }) ?? [],
|
|
7027
|
+
render: () => ({
|
|
7028
|
+
onStart: (props) => setChannelPicker((prev) => nextMentionPickerState(props, prev)),
|
|
7029
|
+
onUpdate: (props) => setChannelPicker((prev) => nextMentionPickerState(props, prev)),
|
|
7030
|
+
onExit: () => setChannelPicker(null),
|
|
7031
|
+
onKeyDown: ({ event }) => channelKeyHandlerRef.current(event)
|
|
7032
|
+
})
|
|
7033
|
+
}
|
|
7034
|
+
),
|
|
6865
7035
|
enableInputRules: RICH_TEXT_INPUT_RULE_ALLOWLIST,
|
|
6866
7036
|
content: richTextToProseMirror(block, resolveEmoji),
|
|
6867
7037
|
editorProps: {
|
|
@@ -6896,7 +7066,8 @@ function RichTextWysiwygEditor({ block, onChange }) {
|
|
|
6896
7066
|
Toolbar,
|
|
6897
7067
|
{
|
|
6898
7068
|
editor,
|
|
6899
|
-
mention:
|
|
7069
|
+
mention: userMentionsEnabled ? { active: mentionPicker !== null, insert: () => insertMentionTrigger(editor) } : null,
|
|
7070
|
+
channel: channelMentionsEnabled ? { active: channelPicker !== null, insert: () => insertChannelTrigger(editor) } : null
|
|
6900
7071
|
}
|
|
6901
7072
|
),
|
|
6902
7073
|
/* @__PURE__ */ jsx(EditorContent, { editor }),
|
|
@@ -6904,14 +7075,29 @@ function RichTextWysiwygEditor({ block, onChange }) {
|
|
|
6904
7075
|
MentionPicker,
|
|
6905
7076
|
{
|
|
6906
7077
|
editor,
|
|
7078
|
+
kind: "user",
|
|
6907
7079
|
state: mentionPicker,
|
|
6908
7080
|
keyHandlerRef: mentionKeyHandlerRef,
|
|
6909
7081
|
onDismiss: () => dismissMentionSuggestion(editor)
|
|
6910
7082
|
}
|
|
7083
|
+
),
|
|
7084
|
+
channelPicker && /* @__PURE__ */ jsx(
|
|
7085
|
+
MentionPicker,
|
|
7086
|
+
{
|
|
7087
|
+
editor,
|
|
7088
|
+
kind: "channel",
|
|
7089
|
+
state: channelPicker,
|
|
7090
|
+
keyHandlerRef: channelKeyHandlerRef,
|
|
7091
|
+
onDismiss: () => dismissChannelSuggestion(editor)
|
|
7092
|
+
}
|
|
6911
7093
|
)
|
|
6912
7094
|
] });
|
|
6913
7095
|
}
|
|
6914
|
-
function Toolbar({
|
|
7096
|
+
function Toolbar({
|
|
7097
|
+
editor,
|
|
7098
|
+
mention,
|
|
7099
|
+
channel
|
|
7100
|
+
}) {
|
|
6915
7101
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-0.5 rounded-md border bg-muted/30 p-1", children: [
|
|
6916
7102
|
/* @__PURE__ */ jsx(
|
|
6917
7103
|
ToolbarButton,
|
|
@@ -6971,6 +7157,7 @@ function Toolbar({ editor, mention }) {
|
|
|
6971
7157
|
}
|
|
6972
7158
|
),
|
|
6973
7159
|
mention && /* @__PURE__ */ jsx(ToolbarButton, { label: "Mention", active: mention.active, onClick: mention.insert, Icon: AtSign }),
|
|
7160
|
+
channel && /* @__PURE__ */ jsx(ToolbarButton, { label: "Channel", active: channel.active, onClick: channel.insert, Icon: Hash }),
|
|
6974
7161
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
6975
7162
|
/* @__PURE__ */ jsx(
|
|
6976
7163
|
ToolbarButton,
|