blixify-ui-web-mcp 0.1.23 → 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/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
|
@@ -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
|
};
|