blixify-ui-web-mcp 0.1.22 → 0.1.24
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/lib/components/chat/chatAnchor/README.md +13 -3
- package/lib/components/chat/chatAnchor/chatAnchor.stories.tsx +50 -0
- package/lib/components/display/detailList/README.md +17 -11
- package/lib/components/display/detailList/detailList.stories.tsx +22 -0
- package/lib/components/input/form/README.md +14 -0
- package/lib/components/input/form/form.stories.tsx +15 -3
- package/lib/components/input/markdown/README.md +52 -1
- package/lib/components/input/markdown/markdown.stories.tsx +39 -1
- package/package.json +1 -1
|
@@ -8,10 +8,20 @@ A floating chat button fixed to the viewport. Clicking it toggles the `ChatBase`
|
|
|
8
8
|
|
|
9
9
|
The button can be dragged out of the way on desktop and touch, and where it is dropped is kept in `localStorage` so it stays there on the next visit. A short drag is still treated as a click, so tapping always opens the chat. A saved position is clamped back into view when it is restored on a smaller screen or the window is resized under it, and dragging is disabled while the panel is full screen on mobile.
|
|
10
10
|
|
|
11
|
+
With `resizableWidth`, an open panel gains a grab handle on its left edge — dragging it left or right resizes the panel to any width. The only bounds are physical: it cannot be dragged wider than the viewport, nor narrower than 280px, below which the header, messages and input stop being usable. The chosen width is kept in `localStorage`, re-clamped to the viewport on window resize, and the handle is hidden while the panel is full screen on mobile.
|
|
12
|
+
|
|
13
|
+
`resizableHeight` works the same way on the vertical axis: an open panel gains a grab handle on its top edge, and dragging it up or down resizes the panel to any height. Because the panel grows upward from the bottom-right anchor, the cap is the distance from its bottom edge to the top of the viewport, and the floor keeps the header and input row usable. The default height is three quarters of the viewport (matching the panel's `sm:h-[75vh]`). Width and height are independent — turn on either or both. The chosen height is kept in `localStorage` and re-clamped on window resize.
|
|
14
|
+
|
|
11
15
|
| Prop | Type | Default | Description |
|
|
12
16
|
| -------------------- | --------- | -------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
13
17
|
| `draggable` | `boolean` | `true` | Set `false` to pin the button to `positionOffset` and ignore any saved position |
|
|
14
18
|
| `positionStorageKey` | `string` | `"blixify-chat-anchor-position"` | `localStorage` key holding `{ right, bottom }` in px, measured from the bottom-right corner |
|
|
19
|
+
| `resizableWidth` | `boolean` | `false` | Set `true` to show a left-edge handle that drags the open panel to any width |
|
|
20
|
+
| `defaultWidth` | `number` | `384` | Width (px) the panel opens at before the user has dragged (matches `sm:w-96`) |
|
|
21
|
+
| `widthStorageKey` | `string` | `"blixify-chat-anchor-width"` | `localStorage` key holding the panel width in px |
|
|
22
|
+
| `resizableHeight` | `boolean` | `false` | Set `true` to show a top-edge handle that drags the open panel to any height |
|
|
23
|
+
| `defaultHeight` | `number` | `75% of viewport` | Height (px) the panel opens at before the user has dragged (matches `sm:h-[75vh]`) |
|
|
24
|
+
| `heightStorageKey` | `string` | `"blixify-chat-anchor-height"` | `localStorage` key holding the panel height in px |
|
|
15
25
|
|
|
16
26
|
##### Import
|
|
17
27
|
|
|
@@ -70,11 +80,11 @@ return (
|
|
|
70
80
|
|
|
71
81
|
| Name | Type | Default | Description |
|
|
72
82
|
| ---------------- | ----------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
73
|
-
| `positionOffset` | `string` | `"bottom-
|
|
83
|
+
| `positionOffset` | `string` | `"bottom-6 right-6"` | Tailwind classes that control the fixed position of the anchor. Ignored on mobile when open. |
|
|
74
84
|
| `greetingCard` | `boolean` | `false` | When `true`, shows a greeting bubble next to the anchor button while the chat is closed. |
|
|
75
85
|
| `greetingText` | `string` | `"Welcome! How can I help you today?"` | Text displayed inside the greeting card bubble. |
|
|
76
|
-
| `openIcon` | `React.ReactNode` \| `string` | `ChatBubbleOvalLeftIcon` | Icon shown when the chat is closed. A string is treated as an image URL (
|
|
77
|
-
| `closeIcon` | `React.ReactNode` \| `string` | `XMarkIcon` | Icon shown when the chat is open. A string is treated as an image URL (
|
|
86
|
+
| `openIcon` | `React.ReactNode` \| `string` | `ChatBubbleOvalLeftIcon` | Icon shown when the chat is closed. A string is treated as an image URL (56×56 rounded circle). |
|
|
87
|
+
| `closeIcon` | `React.ReactNode` \| `string` | `XMarkIcon` | Icon shown when the chat is open. A string is treated as an image URL (56×56 rounded circle). |
|
|
78
88
|
|
|
79
89
|
##### Optional props — inherited from ChatBase
|
|
80
90
|
|
|
@@ -148,3 +148,53 @@ export const ChatAnchor = () => {
|
|
|
148
148
|
</div>
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
|
+
|
|
152
|
+
export const Resizable = () => {
|
|
153
|
+
const botUser = { id: "1", name: "Bot" };
|
|
154
|
+
const currentUser = { id: "2", name: "Guest" };
|
|
155
|
+
|
|
156
|
+
const [openChatBase, setOpenChatBase] = useState<boolean>(true);
|
|
157
|
+
const [message, setMessage] = useState("");
|
|
158
|
+
|
|
159
|
+
const [conversation, setConversation] = useState<Conversation>({
|
|
160
|
+
id: "1",
|
|
161
|
+
members: [currentUser, botUser],
|
|
162
|
+
messageList: [
|
|
163
|
+
{
|
|
164
|
+
sender: botUser,
|
|
165
|
+
messageBody:
|
|
166
|
+
"Drag my left edge to resize the width and my top edge to resize the height. Both are remembered independently.",
|
|
167
|
+
createdAt: dayjs(
|
|
168
|
+
"Fri Nov 03 2023 12:21:14 GMT+0800 (Malaysia Time)"
|
|
169
|
+
).toDate(),
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const handleOnClick = () => setOpenChatBase(!openChatBase);
|
|
175
|
+
|
|
176
|
+
const handleOnSendMessage = (conversation: Conversation) => {
|
|
177
|
+
setConversation(conversation);
|
|
178
|
+
setMessage("");
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<div className="h-96 w-full relative">
|
|
183
|
+
<ChatAnchorImpl
|
|
184
|
+
open={openChatBase}
|
|
185
|
+
value={message}
|
|
186
|
+
currentUser={currentUser}
|
|
187
|
+
conversation={conversation}
|
|
188
|
+
onSendMessage={handleOnSendMessage}
|
|
189
|
+
onChangeMessage={(e) => setMessage(e.target.value)}
|
|
190
|
+
onClose={handleOnClick}
|
|
191
|
+
onClick={handleOnClick}
|
|
192
|
+
className="shadow-lg border border-gray-200"
|
|
193
|
+
resizableWidth
|
|
194
|
+
defaultWidth={384}
|
|
195
|
+
resizableHeight
|
|
196
|
+
defaultHeight={384}
|
|
197
|
+
/>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
};
|
|
@@ -42,17 +42,23 @@ import { DetailList } from "blixify-ui-web/components/display/detailList";
|
|
|
42
42
|
|
|
43
43
|
##### Optional props
|
|
44
44
|
|
|
45
|
-
| Name | Type | Description
|
|
46
|
-
| --------------------- | ------------------------------------------------ |
|
|
47
|
-
| `addTitleElement` | `ReactElement` | Additional react elemnt to be displayed next to title
|
|
48
|
-
| `headerRightElement` | `ReactElement` | Element anchored to the top-right of the header, opposite the title/description
|
|
49
|
-
| `description` | `string` |
|
|
50
|
-
| `className` | `tailwind class` |
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
45
|
+
| Name | Type | Description |
|
|
46
|
+
| --------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
47
|
+
| `addTitleElement` | `ReactElement` | Additional react elemnt to be displayed next to title |
|
|
48
|
+
| `headerRightElement` | `ReactElement` | Element anchored to the top-right of the header, opposite the title/description |
|
|
49
|
+
| `description` | `string` | |
|
|
50
|
+
| `className` | `tailwind class` | |
|
|
51
|
+
| `textClassName` | `tailwind class` | Applied to both the label (`dt`) and value (`dd`) text; overrides the default `text-sm` |
|
|
52
|
+
| `darkMode` | `boolean` | Renders with dark-mode colors |
|
|
53
|
+
| `darkModeBg` | `tailwind class` | Overrides the dark-mode background (defaults to `bg-gray-900`); only used when `darkMode` is set |
|
|
54
|
+
| `model` | `Model Data Structure` | Only used in Data Template |
|
|
55
|
+
| `hideModal` | `boolean` | Only used in Data Template |
|
|
56
|
+
| `referenceModel` | `Model Data Structure` | Only used in Data Template |
|
|
57
|
+
| `splitView` | `boolean` | Able to split into two |
|
|
58
|
+
| `size` | `"default" \| "compact"` | `compact` renders label:value on the same row, much denser than the default stacked layout |
|
|
59
|
+
| `valueTextAlign` | `"left" \| "right"` | Compact only. Forces the value alignment at all widths. When omitted, compact keeps its responsive default (right on mobile, left from `sm` up) |
|
|
60
|
+
| `maxHeight` | `string` | When set, the body scrolls with this max height (e.g. `"60vh"`, `"500px"`) |
|
|
61
|
+
| `handleReferenceLink` | `(dataId: string, collectionId: string) => void` | Only used in Data Template, when reference being clicked |
|
|
56
62
|
|
|
57
63
|
##### Detail List class
|
|
58
64
|
|
|
@@ -107,6 +107,28 @@ export const DetailList = () => (
|
|
|
107
107
|
/>
|
|
108
108
|
</StorySection>
|
|
109
109
|
|
|
110
|
+
<StorySection
|
|
111
|
+
title='Order Summary — valueTextAlign="right"'
|
|
112
|
+
description="Compact list with values pinned to the right at all widths. The final row uses a styled node as its text to render the total bigger and bold."
|
|
113
|
+
>
|
|
114
|
+
<DetailListImpl
|
|
115
|
+
title="Order Summary"
|
|
116
|
+
size="compact"
|
|
117
|
+
valueTextAlign="right"
|
|
118
|
+
list={[
|
|
119
|
+
{ title: "Subtotal", text: "RM 1,200.00" },
|
|
120
|
+
{ title: "Shipping", text: "RM 15.00" },
|
|
121
|
+
{ title: "Tax (6%)", text: "RM 72.90" },
|
|
122
|
+
{ title: "Discount", text: "- RM 27.90" },
|
|
123
|
+
{
|
|
124
|
+
title: "Total",
|
|
125
|
+
customTitle: <span className="text-lg font-bold">Total</span>,
|
|
126
|
+
text: <span className="text-lg font-bold">RM 1,260.00</span>,
|
|
127
|
+
},
|
|
128
|
+
]}
|
|
129
|
+
/>
|
|
130
|
+
</StorySection>
|
|
131
|
+
|
|
110
132
|
<StorySection
|
|
111
133
|
title="splitView (legacy) — Two-Column Grid"
|
|
112
134
|
description='Two-up grid layout. Kept for backward compat; new code should prefer size="compact" or groups instead.'
|
|
@@ -53,3 +53,17 @@ const passwordRef = useRef(null);
|
|
|
53
53
|
| `handleComplete` | `(isDraft?: boolean) => void` | — | Called when all inputs pass validation. `isDraft=true` when triggered via `handleSave()` |
|
|
54
54
|
| `handleOnFailValidation` | `(ids: string[]) => void` | — | Called when one or more inputs fail. Receives an array of the failed input `id` values |
|
|
55
55
|
| `disabledOnEnter` | `boolean` | `false` | When `true`, pressing Enter does not trigger form submission |
|
|
56
|
+
|
|
57
|
+
### Enter to submit
|
|
58
|
+
|
|
59
|
+
Pressing Enter calls `handleSubmit()` when the key event comes from inside this `<form>` — including inputs a child renders through a portal. Enter pressed anywhere else on the page, or inside another `Form`, does not submit this one.
|
|
60
|
+
|
|
61
|
+
Enter does not submit when:
|
|
62
|
+
|
|
63
|
+
| Condition | |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `disabledOnEnter` is `true` | set it for forms with a textarea or rich text field that needs Enter for a newline |
|
|
66
|
+
| Shift is held | |
|
|
67
|
+
| an IME composition is in progress (`isComposing`, or `keyCode` 229) | |
|
|
68
|
+
| the key is auto-repeating (`repeat`) | |
|
|
69
|
+
| the focused element is a `button` | |
|
|
@@ -6,19 +6,21 @@ import { RadioList } from "../../action/radioList";
|
|
|
6
6
|
import { StorySection } from "../../storyUtils";
|
|
7
7
|
import { TextInput } from "../textInput";
|
|
8
8
|
import { UploadInput } from "../uploadInput";
|
|
9
|
+
import { TextArea } from "../textArea";
|
|
9
10
|
|
|
10
11
|
const meta = { title: "Input/Form" };
|
|
11
12
|
export default meta;
|
|
12
13
|
|
|
13
|
-
|
|
14
14
|
export const Form = () => {
|
|
15
15
|
const formRef = useRef<any>(null);
|
|
16
16
|
const input1 = useRef<any>(null);
|
|
17
17
|
const input2 = useRef<any>(null);
|
|
18
|
+
const input3 = useRef<any>(null);
|
|
18
19
|
const uploadRef = useRef<any>(null);
|
|
19
20
|
|
|
20
21
|
const [radioValue, setRadioValue] = useState("");
|
|
21
22
|
const [textValue, setTextValue] = useState("");
|
|
23
|
+
const [textAreaValue, setTextAreaValue] = useState("");
|
|
22
24
|
const [file, setFile] = useState<any>(undefined);
|
|
23
25
|
const [result, setResult] = useState<string | null>(null);
|
|
24
26
|
|
|
@@ -38,7 +40,7 @@ export const Form = () => {
|
|
|
38
40
|
<div className="w-full max-w-sm">
|
|
39
41
|
<FormImpl
|
|
40
42
|
ref={formRef}
|
|
41
|
-
refList={[input1, input2, uploadRef]}
|
|
43
|
+
refList={[input1, input2, input3, uploadRef]}
|
|
42
44
|
handleComplete={() => setResult("All valid — form submitted!")}
|
|
43
45
|
>
|
|
44
46
|
<RadioList
|
|
@@ -61,6 +63,16 @@ export const Form = () => {
|
|
|
61
63
|
containerClassName="mt-3"
|
|
62
64
|
onChange={(e) => setTextValue(e.target.value)}
|
|
63
65
|
/>
|
|
66
|
+
<TextArea
|
|
67
|
+
ref={input3}
|
|
68
|
+
id="remarks"
|
|
69
|
+
value={textAreaValue}
|
|
70
|
+
rows={3}
|
|
71
|
+
label="Remarks"
|
|
72
|
+
placeholder="Remarks"
|
|
73
|
+
containerClassName="mt-3"
|
|
74
|
+
onChange={(e) => setTextAreaValue(e.target.value)}
|
|
75
|
+
/>
|
|
64
76
|
<UploadInput
|
|
65
77
|
ref={uploadRef}
|
|
66
78
|
id="file-upload"
|
|
@@ -134,4 +146,4 @@ export const Form = () => {
|
|
|
134
146
|
</StorySection>
|
|
135
147
|
</>
|
|
136
148
|
);
|
|
137
|
-
}
|
|
149
|
+
};
|
|
@@ -160,7 +160,7 @@ On error the editor draws a red outline (`border border-red-500 rounded`) — th
|
|
|
160
160
|
| `enableImageUpload` | `boolean` | `false` | Enable image upload functionality (drag-and-drop, paste, click to upload) |
|
|
161
161
|
| `enableVideoUpload` | `boolean` | `false` | Enable video upload functionality (supports MP4, MOV, WebM, AVI, MKV, etc.) |
|
|
162
162
|
| `compress` | `{ maxFileSize?: number, fileQuality?: number}` | `null` | Image compression settings. `maxFileSize` in MB (default: 30), `fileQuality` 0.0-1.0 (default: 0.8) |
|
|
163
|
-
| `lib` | `{ axios
|
|
163
|
+
| `lib` | `{ axios?: any, mermaid?: () => Promise<any> }` | `null` | External libraries — axios for API calls; `mermaid` loader (e.g. `() => import("mermaid")`) to draw diagrams |
|
|
164
164
|
| `uploadEndpoint` | `string` | `null` | API endpoint for uploading media files |
|
|
165
165
|
|
|
166
166
|
##### Ref Methods
|
|
@@ -209,6 +209,57 @@ commands. `onSelect` receives `insertEmbed`, and may await first — the host
|
|
|
209
209
|
usually has to create the space before it has an id. Embeds survive the round
|
|
210
210
|
trip back to markdown as the same `div`, the way an inline `<video>` does.
|
|
211
211
|
|
|
212
|
+
##### Embed view — grid or list
|
|
213
|
+
|
|
214
|
+
Each embed carries a `view` of `grid` (default) or `list`. It is stored on the
|
|
215
|
+
marker only when it is `list`:
|
|
216
|
+
|
|
217
|
+
```html
|
|
218
|
+
<div data-space-id="guides"></div> <!-- grid -->
|
|
219
|
+
<div data-space-id="guides" data-view="list"></div> <!-- list -->
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Grid writes no attribute at all, so documents saved before views existed
|
|
223
|
+
serialise byte-for-byte as they did. Anything other than exactly `list` reads as
|
|
224
|
+
grid. A host that parses these markers itself must accept the optional
|
|
225
|
+
`data-view`.
|
|
226
|
+
|
|
227
|
+
`renderEmbed` receives `view` and `onChangeView(view)` alongside `onRemove`, so
|
|
228
|
+
the host decides what a list row looks like and where its toggle sits. The node
|
|
229
|
+
view wrapper also carries `data-view` for styling. A new embed copies the view
|
|
230
|
+
of the embed directly above it, so a list stays a list as items are added.
|
|
231
|
+
|
|
232
|
+
##### Mermaid diagrams
|
|
233
|
+
|
|
234
|
+
Pass a loader and fenced ```` ```mermaid ```` blocks draw as diagrams — in the
|
|
235
|
+
editor as a live preview under the source, and in `MarkdownStatic` in place of
|
|
236
|
+
the block. The `/` menu gains **Diagram**, which inserts a starter flowchart.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
<MarkdownInput lib={{ axios, mermaid: () => import("mermaid") }} />
|
|
240
|
+
<MarkdownStatic label="" value={body} lib={{ mermaid: () => import("mermaid") }} />
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
- **The host supplies mermaid.** This library compiles to CommonJS, so importing
|
|
244
|
+
mermaid here would bundle it into every app with a markdown field. Passed in,
|
|
245
|
+
the host's bundler splits it into a chunk fetched only by pages with a diagram.
|
|
246
|
+
Without a loader the block shows as ordinary code and Diagram is not offered.
|
|
247
|
+
- **`securityLevel: "strict"` is always set.** Documents can be published to
|
|
248
|
+
anyone with a link; strict keeps mermaid sanitising labels and refusing click
|
|
249
|
+
handlers.
|
|
250
|
+
- **Html labels keep inline formatting; `<img>` is stripped.** `<b>`, `<i>`,
|
|
251
|
+
`<u>`, `<sup>` and `<br>` draw as formatting. Strict still keeps sanitised
|
|
252
|
+
tags, so `dompurifyConfig.FORBID_TAGS: ["img"]` removes images, which would
|
|
253
|
+
otherwise load a remote file for every reader.
|
|
254
|
+
- **Entity codes such as `#quot;` render garbled** in mermaid 11.17.2 with
|
|
255
|
+
either api (`render` or `run`). Write the character directly instead.
|
|
256
|
+
- **`suppressErrorRendering: true`.** Without it mermaid appends its own error
|
|
257
|
+
graphic to `document.body` on a syntax error, left behind below the page.
|
|
258
|
+
- **A broken diagram never breaks the page.** It shows a message; in
|
|
259
|
+
`MarkdownStatic` the source stays visible under it.
|
|
260
|
+
- **The source is what is stored**, as a normal fenced block, so search and
|
|
261
|
+
anything reading the raw markdown still see the diagram's text.
|
|
262
|
+
|
|
212
263
|
##### The slash menu
|
|
213
264
|
|
|
214
265
|
Only a real edit opens it. The detection also runs on selection changes so that
|
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { MarkdownInput as MarkdownInputImpl } from ".";
|
|
2
|
+
import { MarkdownInput as MarkdownInputImpl, MarkdownStatic } from ".";
|
|
3
3
|
import { StorySection } from "../../storyUtils";
|
|
4
4
|
|
|
5
5
|
const meta = { title: "Input/Markdown Input" };
|
|
6
6
|
export default meta;
|
|
7
7
|
|
|
8
|
+
// INFO: the host passes mermaid in; the library never imports it
|
|
9
|
+
const handleLoadMermaid = () => import("mermaid");
|
|
10
|
+
|
|
11
|
+
const DIAGRAM_SAMPLE = [
|
|
12
|
+
"Pick Diagram from the / menu to add another.",
|
|
13
|
+
"",
|
|
14
|
+
"```mermaid",
|
|
15
|
+
"flowchart LR",
|
|
16
|
+
" A[<b>Draft</b>] --> B[Review<br/>by <i>owner</i>]",
|
|
17
|
+
" B --> C[Published]",
|
|
18
|
+
"```",
|
|
19
|
+
].join("\n");
|
|
20
|
+
|
|
8
21
|
export const MarkdownInput = () => {
|
|
9
22
|
const [value, setValue] = useState<string | undefined>("");
|
|
10
23
|
const [noteValue, setNoteValue] = useState<string | undefined>(
|
|
11
24
|
'A page can hold a live database, not just a link to one.\n\n<div data-space-id="orders"></div>\n\nType / on any line and pick Database to add another.',
|
|
12
25
|
);
|
|
26
|
+
const [diagramValue, setDiagramValue] = useState<string | undefined>(
|
|
27
|
+
DIAGRAM_SAMPLE,
|
|
28
|
+
);
|
|
13
29
|
const handleOnChange = (id: string, value?: string) => setValue(value);
|
|
30
|
+
const handleOnDiagramChange = (id: string, value?: string) =>
|
|
31
|
+
setDiagramValue(value);
|
|
14
32
|
const handleOnNoteChange = (id: string, value?: string) =>
|
|
15
33
|
setNoteValue(value);
|
|
16
34
|
|
|
@@ -88,6 +106,26 @@ export const MarkdownInput = () => {
|
|
|
88
106
|
{noteValue}
|
|
89
107
|
</pre>
|
|
90
108
|
</StorySection>
|
|
109
|
+
|
|
110
|
+
<StorySection
|
|
111
|
+
title="Mermaid diagrams"
|
|
112
|
+
description="lib.mermaid given, so ```mermaid blocks draw as diagrams — under the source while editing, in place of it in MarkdownStatic. Inline tags such as <b>, <i> and <br> format the label; an <img> is stripped."
|
|
113
|
+
>
|
|
114
|
+
<div className="grid w-full gap-6 md:grid-cols-2">
|
|
115
|
+
<MarkdownInputImpl
|
|
116
|
+
id="diagram"
|
|
117
|
+
value={diagramValue ?? ""}
|
|
118
|
+
onChange={handleOnDiagramChange}
|
|
119
|
+
borderless={true}
|
|
120
|
+
lib={{ mermaid: handleLoadMermaid }}
|
|
121
|
+
/>
|
|
122
|
+
<MarkdownStatic
|
|
123
|
+
label="MarkdownStatic"
|
|
124
|
+
value={diagramValue ?? ""}
|
|
125
|
+
lib={{ mermaid: handleLoadMermaid }}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
</StorySection>
|
|
91
129
|
</>
|
|
92
130
|
);
|
|
93
131
|
};
|