@nkootstra/block-kit 0.3.2 → 0.3.4
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/dist/index.js +150 -34
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6325,6 +6325,19 @@ const InputBlockContext = createContext(false);
|
|
|
6325
6325
|
function useInInputBlock() {
|
|
6326
6326
|
return useContext(InputBlockContext);
|
|
6327
6327
|
}
|
|
6328
|
+
/** The id of the error shown under an `input` block's element, while it has one. */
|
|
6329
|
+
const InputErrorContext = createContext(void 0);
|
|
6330
|
+
/**
|
|
6331
|
+
* Props for the element's focusable control: an `input` block with an error marks it invalid,
|
|
6332
|
+
* which also gives it Slack's red focus ring, and describes it with the error text.
|
|
6333
|
+
*/
|
|
6334
|
+
function useInvalidProps() {
|
|
6335
|
+
const errorId = useContext(InputErrorContext);
|
|
6336
|
+
return errorId ? {
|
|
6337
|
+
"aria-invalid": true,
|
|
6338
|
+
"aria-describedby": errorId
|
|
6339
|
+
} : {};
|
|
6340
|
+
}
|
|
6328
6341
|
//#endregion
|
|
6329
6342
|
//#region src/elements/Popover.tsx
|
|
6330
6343
|
/** Space between the anchor and the popover, as in Slack's menus. */
|
|
@@ -6419,6 +6432,7 @@ function DatePicker({ element, blockId }) {
|
|
|
6419
6432
|
const [open, setOpen] = useState(false);
|
|
6420
6433
|
const rootRef = useRef(null);
|
|
6421
6434
|
const inputRef = useFocusOnLoad(element);
|
|
6435
|
+
const invalid = useInvalidProps();
|
|
6422
6436
|
const loadFocus = useRef(element.focus_on_load === true);
|
|
6423
6437
|
const actionId = element.action_id ?? "";
|
|
6424
6438
|
useEffect(() => {
|
|
@@ -6472,6 +6486,7 @@ function DatePicker({ element, blockId }) {
|
|
|
6472
6486
|
placeholder: element.placeholder?.text ?? "Select a date",
|
|
6473
6487
|
"aria-haspopup": "dialog",
|
|
6474
6488
|
"aria-expanded": open,
|
|
6489
|
+
...invalid,
|
|
6475
6490
|
ref: inputRef,
|
|
6476
6491
|
onFocus: () => {
|
|
6477
6492
|
if (loadFocus.current) loadFocus.current = false;
|
|
@@ -7044,19 +7059,26 @@ const FORMATTING_GROUPS = [
|
|
|
7044
7059
|
}
|
|
7045
7060
|
]
|
|
7046
7061
|
];
|
|
7047
|
-
/** The
|
|
7048
|
-
*
|
|
7062
|
+
/** The formatting bar Slack shows above a modal's rich-text editor. The editor here is plain text,
|
|
7063
|
+
* so its buttons are there to look like Slack's but marked unavailable; they dim until the editor
|
|
7064
|
+
* has focus, as Slack's do. */
|
|
7049
7065
|
function RichTextToolbar() {
|
|
7050
7066
|
return /* @__PURE__ */ jsx("div", {
|
|
7051
7067
|
className: "sbk-rich-text-input__toolbar",
|
|
7052
|
-
|
|
7053
|
-
|
|
7068
|
+
role: "toolbar",
|
|
7069
|
+
"aria-orientation": "horizontal",
|
|
7070
|
+
"aria-label": "Formatting",
|
|
7071
|
+
children: FORMATTING_GROUPS.map((group, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [i > 0 && /* @__PURE__ */ jsx("span", { className: "sbk-rich-text-input__separator" }), group.map((icon) => /* @__PURE__ */ jsx("button", {
|
|
7072
|
+
type: "button",
|
|
7054
7073
|
className: "sbk-rich-text-input__tool",
|
|
7055
|
-
|
|
7074
|
+
"aria-label": icon.label,
|
|
7075
|
+
"aria-disabled": "true",
|
|
7076
|
+
tabIndex: -1,
|
|
7056
7077
|
children: /* @__PURE__ */ jsx("svg", {
|
|
7057
7078
|
viewBox: "0 0 20 20",
|
|
7058
7079
|
width: "18",
|
|
7059
7080
|
height: "18",
|
|
7081
|
+
"aria-hidden": "true",
|
|
7060
7082
|
children: /* @__PURE__ */ jsx("path", {
|
|
7061
7083
|
fill: "currentColor",
|
|
7062
7084
|
fillRule: icon.evenOdd ? "evenodd" : void 0,
|
|
@@ -7067,11 +7089,52 @@ function RichTextToolbar() {
|
|
|
7067
7089
|
}, icon.label))] }, group[0]?.label))
|
|
7068
7090
|
});
|
|
7069
7091
|
}
|
|
7092
|
+
/** Slack's "Aa" (show formatting) and emoji glyphs, 20-unit paths as Slack ships them. */
|
|
7093
|
+
const FORMATTING_ICON = "M6.941 3.952c-.459-1.378-2.414-1.363-2.853.022l-4.053 12.8a.75.75 0 0 0 1.43.452l1.101-3.476h6.06l1.163 3.487a.75.75 0 1 0 1.423-.474zm1.185 8.298L5.518 4.427 3.041 12.25zm6.198-5.537a4.74 4.74 0 0 1 3.037-.081A3.74 3.74 0 0 1 20 10.208V17a.75.75 0 0 1-1.5 0v-.745a8 8 0 0 1-2.847 1.355 3 3 0 0 1-3.15-1.143C10.848 14.192 12.473 11 15.287 11H18.5v-.792c0-.984-.641-1.853-1.581-2.143a3.24 3.24 0 0 0-2.077.056l-.242.089a2.22 2.22 0 0 0-1.34 1.382l-.048.145a.75.75 0 0 1-1.423-.474l.048-.145a3.72 3.72 0 0 1 2.244-2.315zM18.5 12.5h-3.213c-1.587 0-2.504 1.801-1.57 3.085.357.491.98.717 1.572.57a6.5 6.5 0 0 0 2.47-1.223l.741-.593z";
|
|
7094
|
+
const EMOJI_ICON = "M2.5 10a7.5 7.5 0 1 1 15 0 7.5 7.5 0 0 1-15 0M10 1a9 9 0 1 0 0 18 9 9 0 0 0 0-18M7.5 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3M14 8a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m-6.385 3.766a.75.75 0 1 0-1.425.468C6.796 14.08 8.428 15 10.027 15s3.23-.92 3.838-2.766a.75.75 0 1 0-1.425-.468c-.38 1.155-1.38 1.734-2.413 1.734s-2.032-.58-2.412-1.734";
|
|
7095
|
+
function Glyph({ d }) {
|
|
7096
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
7097
|
+
viewBox: "0 0 20 20",
|
|
7098
|
+
width: "18",
|
|
7099
|
+
height: "18",
|
|
7100
|
+
"aria-hidden": "true",
|
|
7101
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
7102
|
+
fill: "currentColor",
|
|
7103
|
+
fillRule: "evenodd",
|
|
7104
|
+
clipRule: "evenodd",
|
|
7105
|
+
d
|
|
7106
|
+
})
|
|
7107
|
+
});
|
|
7108
|
+
}
|
|
7109
|
+
/** The row Slack draws under the editor: "Aa" shows or hides the formatting bar (underlined while
|
|
7110
|
+
* it's shown), and an emoji button, unavailable until block-kit has an emoji picker. */
|
|
7111
|
+
function RichTextComposerActions({ formatting, onToggleFormatting }) {
|
|
7112
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
7113
|
+
className: "sbk-rich-text-input__footer",
|
|
7114
|
+
role: "toolbar",
|
|
7115
|
+
"aria-orientation": "horizontal",
|
|
7116
|
+
"aria-label": "Composer actions",
|
|
7117
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
7118
|
+
type: "button",
|
|
7119
|
+
className: "sbk-rich-text-input__action sbk-rich-text-input__action--formatting",
|
|
7120
|
+
"aria-label": "Show formatting",
|
|
7121
|
+
"aria-pressed": formatting,
|
|
7122
|
+
onClick: onToggleFormatting,
|
|
7123
|
+
children: /* @__PURE__ */ jsx(Glyph, { d: FORMATTING_ICON })
|
|
7124
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
7125
|
+
type: "button",
|
|
7126
|
+
className: "sbk-rich-text-input__action",
|
|
7127
|
+
"aria-label": "Emoji",
|
|
7128
|
+
"aria-disabled": "true",
|
|
7129
|
+
tabIndex: -1,
|
|
7130
|
+
children: /* @__PURE__ */ jsx(Glyph, { d: EMOJI_ICON })
|
|
7131
|
+
})]
|
|
7132
|
+
});
|
|
7133
|
+
}
|
|
7070
7134
|
//#endregion
|
|
7071
7135
|
//#region src/elements/RichTextInput.tsx
|
|
7072
|
-
/** Wraps plain text into the `rich_text` block shape Slack uses for this element's value.
|
|
7073
|
-
*
|
|
7074
|
-
* Slack's (inert) formatting toolbar. */
|
|
7136
|
+
/** Wraps plain text into the `rich_text` block shape Slack uses for this element's value. The
|
|
7137
|
+
* editor is plain text; Slack's formatting bar and composer row are drawn around it for parity. */
|
|
7075
7138
|
function toRichText(text) {
|
|
7076
7139
|
return {
|
|
7077
7140
|
type: "rich_text",
|
|
@@ -7097,8 +7160,11 @@ function RichTextInput({ element, blockId }) {
|
|
|
7097
7160
|
const { setValue, dispatch } = useBlockKit();
|
|
7098
7161
|
const ref = useRef(null);
|
|
7099
7162
|
useFocusOnLoad(element, ref);
|
|
7163
|
+
const invalid = useInvalidProps();
|
|
7100
7164
|
const actionId = element.action_id ?? "";
|
|
7101
7165
|
const initialText = fromRichText(element.initial_value);
|
|
7166
|
+
const [formatting, setFormatting] = useState(true);
|
|
7167
|
+
const [empty, setEmpty] = useState(initialText === "");
|
|
7102
7168
|
useEffect(() => {
|
|
7103
7169
|
if (element.initial_value) setValue(blockId, actionId, {
|
|
7104
7170
|
type: "rich_text_input",
|
|
@@ -7106,7 +7172,9 @@ function RichTextInput({ element, blockId }) {
|
|
|
7106
7172
|
});
|
|
7107
7173
|
}, []);
|
|
7108
7174
|
function onInput() {
|
|
7109
|
-
const
|
|
7175
|
+
const text = ref.current?.innerText ?? "";
|
|
7176
|
+
setEmpty(text.trim() === "");
|
|
7177
|
+
const richText = toRichText(text);
|
|
7110
7178
|
setValue(blockId, actionId, {
|
|
7111
7179
|
type: "rich_text_input",
|
|
7112
7180
|
rich_text_value: richText
|
|
@@ -7118,24 +7186,39 @@ function RichTextInput({ element, blockId }) {
|
|
|
7118
7186
|
rich_text_value: richText
|
|
7119
7187
|
});
|
|
7120
7188
|
}
|
|
7189
|
+
const placeholder = element.placeholder?.text;
|
|
7121
7190
|
return /* @__PURE__ */ jsxs("div", {
|
|
7122
7191
|
className: "sbk-rich-text-input",
|
|
7123
|
-
children: [
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7192
|
+
children: [
|
|
7193
|
+
formatting && /* @__PURE__ */ jsx(RichTextToolbar, {}),
|
|
7194
|
+
/* @__PURE__ */ jsxs("div", {
|
|
7195
|
+
className: "sbk-rich-text-input__body",
|
|
7196
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
7197
|
+
ref,
|
|
7198
|
+
className: "sbk-rich-text-input__editor",
|
|
7199
|
+
contentEditable: true,
|
|
7200
|
+
suppressContentEditableWarning: true,
|
|
7201
|
+
role: "textbox",
|
|
7202
|
+
"aria-multiline": "true",
|
|
7203
|
+
...invalid,
|
|
7204
|
+
"aria-label": placeholder ?? actionId,
|
|
7205
|
+
onInput,
|
|
7206
|
+
style: {
|
|
7207
|
+
minHeight: element.min_lines ? `${element.min_lines * 22}px` : void 0,
|
|
7208
|
+
maxHeight: element.max_lines ? `${element.max_lines * 22}px` : void 0
|
|
7209
|
+
},
|
|
7210
|
+
children: initialText
|
|
7211
|
+
}), placeholder && empty && /* @__PURE__ */ jsx("div", {
|
|
7212
|
+
className: "sbk-rich-text-input__placeholder",
|
|
7213
|
+
"aria-hidden": "true",
|
|
7214
|
+
children: placeholder
|
|
7215
|
+
})]
|
|
7216
|
+
}),
|
|
7217
|
+
/* @__PURE__ */ jsx(RichTextComposerActions, {
|
|
7218
|
+
formatting,
|
|
7219
|
+
onToggleFormatting: () => setFormatting((shown) => !shown)
|
|
7220
|
+
})
|
|
7221
|
+
]
|
|
7139
7222
|
});
|
|
7140
7223
|
}
|
|
7141
7224
|
//#endregion
|
|
@@ -7209,6 +7292,7 @@ function Select({ element, blockId }) {
|
|
|
7209
7292
|
const rootRef = useRef(null);
|
|
7210
7293
|
const triggerRef = useRef(null);
|
|
7211
7294
|
useFocusOnLoad(element, triggerRef);
|
|
7295
|
+
const invalid = useInvalidProps();
|
|
7212
7296
|
const listRef = useRef(null);
|
|
7213
7297
|
const remote = source === "external" && loadOptions !== void 0;
|
|
7214
7298
|
const minQueryLength = element.min_query_length ?? DEFAULT_MIN_QUERY_LENGTH;
|
|
@@ -7367,6 +7451,7 @@ function Select({ element, blockId }) {
|
|
|
7367
7451
|
onClick: () => setOpen((o) => !o),
|
|
7368
7452
|
"aria-haspopup": "listbox",
|
|
7369
7453
|
"aria-expanded": open,
|
|
7454
|
+
...invalid,
|
|
7370
7455
|
children: [multi ? items.length > 0 ? /* @__PURE__ */ jsx("span", {
|
|
7371
7456
|
className: "sbk-select__chips",
|
|
7372
7457
|
children: items.map((item) => /* @__PURE__ */ jsxs("span", {
|
|
@@ -7386,6 +7471,12 @@ function Select({ element, blockId }) {
|
|
|
7386
7471
|
e.stopPropagation();
|
|
7387
7472
|
removeItem(item.id);
|
|
7388
7473
|
},
|
|
7474
|
+
onKeyDown: (e) => {
|
|
7475
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
7476
|
+
e.preventDefault();
|
|
7477
|
+
e.stopPropagation();
|
|
7478
|
+
removeItem(item.id);
|
|
7479
|
+
},
|
|
7389
7480
|
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
7390
7481
|
})]
|
|
7391
7482
|
}, item.id))
|
|
@@ -7496,6 +7587,7 @@ function TextInput({ element, blockId }) {
|
|
|
7496
7587
|
const { setValue, dispatch, surface } = useBlockKit();
|
|
7497
7588
|
const sizeClass = surface === "message" ? "" : " sbk-text-input--medium";
|
|
7498
7589
|
const [value, setLocalValue] = useState(element.initial_value ?? "");
|
|
7590
|
+
const invalid = useInvalidProps();
|
|
7499
7591
|
const focusRef = useFocusOnLoad(element);
|
|
7500
7592
|
const actionId = element.action_id ?? "";
|
|
7501
7593
|
const dispatchEnabled = element.__dispatchAction === true;
|
|
@@ -7539,7 +7631,8 @@ function TextInput({ element, blockId }) {
|
|
|
7539
7631
|
placeholder,
|
|
7540
7632
|
onKeyDown,
|
|
7541
7633
|
ref: focusRef,
|
|
7542
|
-
"aria-label": element.action_id
|
|
7634
|
+
"aria-label": element.action_id,
|
|
7635
|
+
...invalid
|
|
7543
7636
|
};
|
|
7544
7637
|
const leadingIcon = element.type === "email_text_input" ? /* @__PURE__ */ jsx(EmailIcon, { className: "sbk-text-input__icon" }) : element.type === "url_text_input" ? /* @__PURE__ */ jsx(LinkGlyphIcon, { className: "sbk-text-input__icon" }) : null;
|
|
7545
7638
|
if (element.multiline) return /* @__PURE__ */ jsx("textarea", {
|
|
@@ -7583,6 +7676,7 @@ function TimePicker({ element, blockId }) {
|
|
|
7583
7676
|
const rootRef = useRef(null);
|
|
7584
7677
|
const triggerRef = useRef(null);
|
|
7585
7678
|
useFocusOnLoad(element, triggerRef);
|
|
7679
|
+
const invalid = useInvalidProps();
|
|
7586
7680
|
const listRef = useRef(null);
|
|
7587
7681
|
const actionId = element.action_id ?? "";
|
|
7588
7682
|
useEffect(() => {
|
|
@@ -7634,6 +7728,7 @@ function TimePicker({ element, blockId }) {
|
|
|
7634
7728
|
onClick: () => setOpen((o) => !o),
|
|
7635
7729
|
"aria-haspopup": "listbox",
|
|
7636
7730
|
"aria-expanded": open,
|
|
7731
|
+
...invalid,
|
|
7637
7732
|
children: [
|
|
7638
7733
|
/* @__PURE__ */ jsx(ClockIcon, { className: "sbk-timepicker__icon" }),
|
|
7639
7734
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -9491,6 +9586,7 @@ function Input({ block, blockId }) {
|
|
|
9491
9586
|
__dispatchAction: block.dispatch_action === true
|
|
9492
9587
|
};
|
|
9493
9588
|
const error = errors[blockId];
|
|
9589
|
+
const errorId = useId();
|
|
9494
9590
|
const showDispatchHint = dispatchHint(block);
|
|
9495
9591
|
const showOptional = block.optional && surface !== "message";
|
|
9496
9592
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -9507,13 +9603,16 @@ function Input({ block, blockId }) {
|
|
|
9507
9603
|
className: "sbk-input__element",
|
|
9508
9604
|
children: /* @__PURE__ */ jsx(InputBlockContext.Provider, {
|
|
9509
9605
|
value: true,
|
|
9510
|
-
children:
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9606
|
+
children: /* @__PURE__ */ jsx(InputErrorContext.Provider, {
|
|
9607
|
+
value: error ? errorId : void 0,
|
|
9608
|
+
children: block.dispatch_action === true ? /* @__PURE__ */ jsx(Element, {
|
|
9609
|
+
element,
|
|
9610
|
+
blockId
|
|
9611
|
+
}) : /* @__PURE__ */ jsx(SuppressActions, { children: /* @__PURE__ */ jsx(Element, {
|
|
9612
|
+
element,
|
|
9613
|
+
blockId
|
|
9614
|
+
}) })
|
|
9615
|
+
})
|
|
9517
9616
|
})
|
|
9518
9617
|
}),
|
|
9519
9618
|
block.hint && /* @__PURE__ */ jsx("div", {
|
|
@@ -9539,6 +9638,7 @@ function Input({ block, blockId }) {
|
|
|
9539
9638
|
}),
|
|
9540
9639
|
error && /* @__PURE__ */ jsx("div", {
|
|
9541
9640
|
className: "sbk-input__error",
|
|
9641
|
+
id: errorId,
|
|
9542
9642
|
children: error
|
|
9543
9643
|
})
|
|
9544
9644
|
]
|
|
@@ -10079,7 +10179,23 @@ function renderTopLevel(elements, ctx) {
|
|
|
10079
10179
|
i++;
|
|
10080
10180
|
}
|
|
10081
10181
|
const forest = buildListForest(run);
|
|
10082
|
-
|
|
10182
|
+
const lists = forest.map((group) => renderListGroup(group, ctx, key++, 0));
|
|
10183
|
+
if (!forest.some((group) => group.border)) {
|
|
10184
|
+
nodes.push(...lists);
|
|
10185
|
+
continue;
|
|
10186
|
+
}
|
|
10187
|
+
const quoted = [];
|
|
10188
|
+
while (i < elements.length && elements[i]?.type === "rich_text_quote") {
|
|
10189
|
+
quoted.push(/* @__PURE__ */ jsx("div", {
|
|
10190
|
+
className: "sbk-rich-text__quote-text",
|
|
10191
|
+
children: renderSectionChildren(elements[i].elements, ctx)
|
|
10192
|
+
}, key++));
|
|
10193
|
+
i++;
|
|
10194
|
+
}
|
|
10195
|
+
nodes.push(/* @__PURE__ */ jsxs("blockquote", {
|
|
10196
|
+
className: "sbk-rich-text__quote sbk-rich-text__quote--group",
|
|
10197
|
+
children: [lists, quoted]
|
|
10198
|
+
}, key++));
|
|
10083
10199
|
continue;
|
|
10084
10200
|
}
|
|
10085
10201
|
switch (el.type) {
|