laif-ds 0.2.80 → 0.2.84
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/CHANGELOG.md +40 -0
- package/dist/_virtual/index3.js +5 -5
- package/dist/_virtual/index4.js +5 -5
- package/dist/_virtual/index5.js +4 -4
- package/dist/_virtual/index7.js +5 -2
- package/dist/_virtual/index8.js +2 -5
- package/dist/agent-docs/adoption-report.json +17 -13
- package/dist/agent-docs/components/AppEditor.md +117 -7
- package/dist/agent-docs/components/Chat.md +1 -0
- package/dist/agent-docs/components/FileUploader.md +8 -3
- package/dist/agent-docs/manifest.json +125 -19
- package/dist/components/editor/nodes/mention-node.js +113 -0
- package/dist/components/editor/plugins/mention-plugin.js +215 -0
- package/dist/components/editor/plugins/toolbar/mention-insert-toolbar-plugin.js +41 -0
- package/dist/components/ui/app-editor.js +150 -112
- package/dist/components/ui/chat.js +35 -33
- package/dist/components/ui/file-previewer.js +66 -61
- package/dist/components/ui/file-uploader.js +116 -112
- package/dist/components/ui/gantt/components/Chart/Chart.js +1 -1
- package/dist/index.d.ts +44 -5
- package/dist/node_modules/@lexical/markdown/LexicalMarkdown.prod.js +5 -5
- package/dist/node_modules/@lexical/react/LexicalTypeaheadMenuPlugin.prod.js +220 -0
- package/dist/node_modules/eventemitter3/index.js +1 -1
- package/dist/node_modules/eventemitter3/index2.js +1 -1
- package/dist/node_modules/hast-util-to-jsx-runtime/lib/index.js +1 -1
- package/dist/node_modules/lexical/Lexical.prod.js +314 -303
- package/dist/node_modules/recharts/es6/util/Events.js +1 -1
- package/dist/node_modules/unified/lib/index.js +1 -1
- package/dist/styles.v3.css +1 -1
- package/package.json +1 -1
- /package/dist/agent-docs/{truncated-cell.md → components/truncated-cell.md} +0 -0
package/dist/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,46 @@ All notable technical changes to the laif-ds core will be documented in this fil
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [laif-ds@0.2.84]
|
|
9
|
+
|
|
10
|
+
### 🚀 Added
|
|
11
|
+
|
|
12
|
+
- **AppEditor**: Added opt-in `@mention` support — pass a `mentions` prop (a `MentionItem[]` or an async loader) to register the mention node, plugin, transformer and a toolbar insert button. Typing the trigger (configurable via `mentionTrigger`, default `@`) opens a searchable people menu; selected mentions render as atomic, removable chips. `onValueChange` now reports `meta.mentions` — the deduped (by `id`) list of mentions present in the text. Mentions serialize to a round-trippable markdown token (`@[label](mention:id?v=value)`). When `mentions` is omitted, the node/plugin/transformer are not registered and output is byte-identical to a mention-less editor
|
|
13
|
+
|
|
14
|
+
### 🔧 Changed
|
|
15
|
+
|
|
16
|
+
- **AppEditor**: Mention chips are now primary-tinted (`text-d-primary` on a de-opacified `bg-d-primary/10` background) so they stand out from surrounding text without the solid fill of a primary CTA
|
|
17
|
+
|
|
18
|
+
### 🐛 Fixed
|
|
19
|
+
|
|
20
|
+
- **AppEditor**: Fixed the caret resetting to the start of the editor on every keystroke in the `Default` story by seeding `defaultValue` from a stable constant instead of feeding live state back into the uncontrolled prop
|
|
21
|
+
|
|
22
|
+
## [laif-ds@0.2.83]
|
|
23
|
+
|
|
24
|
+
### 🚀 Added
|
|
25
|
+
|
|
26
|
+
- **FileUploader**: Added an `onRemove(removed: File[], remaining: File[])` callback fired when files are removed (single file or remove-all), reporting the removed files and the remaining list
|
|
27
|
+
|
|
28
|
+
### 🔧 Changed
|
|
29
|
+
|
|
30
|
+
- **FileUploader**: Removals now notify via `onRemove` instead of `onUpload` — `onUpload` fires only on add. Consumers mirroring the file list in their own state must wire both callbacks (e.g. `onRemove={(_, remaining) => setFiles(remaining)}`); the previous behavior, where removing the last file silently failed to propagate, is fixed (see issue #84)
|
|
31
|
+
|
|
32
|
+
## [laif-ds@0.2.82]
|
|
33
|
+
|
|
34
|
+
### 🚀 Added
|
|
35
|
+
|
|
36
|
+
- **Chat**: Added a `placeholder` prop forwarded to the message input, allowing a custom composer placeholder (defaults to `"Ask AI..."`)
|
|
37
|
+
|
|
38
|
+
## [laif-ds@0.2.81]
|
|
39
|
+
|
|
40
|
+
### 🚀 Added
|
|
41
|
+
|
|
42
|
+
- **FilePreviewer**: Added a `size` option to `PreviewOptions` (`sm` | `default` | `lg` | `xl` | `full`, defaults to `xl`) to control the preview dialog size per call
|
|
43
|
+
|
|
44
|
+
### 🔧 Changed
|
|
45
|
+
|
|
46
|
+
- **FilePreviewer**: Migrated from the low-level `Dialog` to `AppDialog`, simplifying the markup and relying on its built-in header and configurable sizing
|
|
47
|
+
|
|
8
48
|
## [laif-ds@0.2.80]
|
|
9
49
|
|
|
10
50
|
### 🚀 Added
|
package/dist/_virtual/index3.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { getDefaultExportFromCjs as
|
|
3
|
-
import { __require as
|
|
4
|
-
var
|
|
5
|
-
const
|
|
2
|
+
import { getDefaultExportFromCjs as r } from "./_commonjsHelpers.js";
|
|
3
|
+
import { __require as e } from "../node_modules/style-to-js/cjs/index.js";
|
|
4
|
+
var t = e();
|
|
5
|
+
const a = /* @__PURE__ */ r(t);
|
|
6
6
|
export {
|
|
7
|
-
|
|
7
|
+
a as default
|
|
8
8
|
};
|
package/dist/_virtual/index4.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { getDefaultExportFromCjs as
|
|
3
|
-
import { __require as
|
|
4
|
-
var t =
|
|
5
|
-
const
|
|
2
|
+
import { getDefaultExportFromCjs as e } from "./_commonjsHelpers.js";
|
|
3
|
+
import { __require as r } from "../node_modules/extend/index.js";
|
|
4
|
+
var t = r();
|
|
5
|
+
const x = /* @__PURE__ */ e(t);
|
|
6
6
|
export {
|
|
7
|
-
|
|
7
|
+
x as default
|
|
8
8
|
};
|
package/dist/_virtual/index5.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { getDefaultExportFromCjs as e } from "./_commonjsHelpers.js";
|
|
3
|
-
import { __require as
|
|
4
|
-
var
|
|
5
|
-
const
|
|
3
|
+
import { __require as t } from "../node_modules/eventemitter3/index2.js";
|
|
4
|
+
var r = t();
|
|
5
|
+
const m = /* @__PURE__ */ e(r);
|
|
6
6
|
export {
|
|
7
|
-
|
|
7
|
+
m as default
|
|
8
8
|
};
|
package/dist/_virtual/index7.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
2
|
+
import { getDefaultExportFromCjs as r } from "./_commonjsHelpers.js";
|
|
3
|
+
import { __require as e } from "../node_modules/classnames/index.js";
|
|
4
|
+
var s = e();
|
|
5
|
+
const o = /* @__PURE__ */ r(s);
|
|
3
6
|
export {
|
|
4
|
-
|
|
7
|
+
o as default
|
|
5
8
|
};
|
package/dist/_virtual/index8.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
3
|
-
import { __require as e } from "../node_modules/classnames/index.js";
|
|
4
|
-
var s = e();
|
|
5
|
-
const o = /* @__PURE__ */ r(s);
|
|
2
|
+
var e = { exports: {} };
|
|
6
3
|
export {
|
|
7
|
-
|
|
4
|
+
e as __module
|
|
8
5
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-
|
|
3
|
+
"generatedAt": "2026-06-30T08:03:50.466Z",
|
|
4
4
|
"package": {
|
|
5
5
|
"name": "laif-ds",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.84"
|
|
7
7
|
},
|
|
8
8
|
"summary": {
|
|
9
|
-
"scannedFiles":
|
|
9
|
+
"scannedFiles": 397,
|
|
10
10
|
"deprecatedComponentsCount": 2,
|
|
11
11
|
"deprecatedComponentsUsedCount": 0,
|
|
12
12
|
"componentsWithoutExamplesCount": 5,
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"repeatedClassPatternsCount": 25,
|
|
15
15
|
"lowAiReadinessComponentsCount": 42,
|
|
16
16
|
"componentsWithoutAccessibilityCoverageCount": 46,
|
|
17
|
-
"componentsWithWeakStateCoverageCount":
|
|
18
|
-
"averageAiReadinessScore":
|
|
17
|
+
"componentsWithWeakStateCoverageCount": 72,
|
|
18
|
+
"averageAiReadinessScore": 55.38
|
|
19
19
|
},
|
|
20
20
|
"deprecatedUsage": [],
|
|
21
21
|
"documentationGaps": {
|
|
@@ -111,7 +111,6 @@
|
|
|
111
111
|
"Alert",
|
|
112
112
|
"AlertDialog",
|
|
113
113
|
"AppDialog",
|
|
114
|
-
"AppEditor",
|
|
115
114
|
"AppMultipleSelectDropdown",
|
|
116
115
|
"AppSidebar",
|
|
117
116
|
"AspectRatio",
|
|
@@ -551,6 +550,18 @@
|
|
|
551
550
|
"src/components/stories/spinner.stories.tsx"
|
|
552
551
|
]
|
|
553
552
|
},
|
|
553
|
+
{
|
|
554
|
+
"className": "text-d-muted-foreground text-xs",
|
|
555
|
+
"occurrences": 6,
|
|
556
|
+
"files": [
|
|
557
|
+
"src/components/editor/plugins/mention-plugin.tsx",
|
|
558
|
+
"src/components/stories/app-card.stories.tsx",
|
|
559
|
+
"src/components/stories/input.stories.tsx",
|
|
560
|
+
"src/components/ui/tables/data-cross-table/cell-editor-dialog.tsx",
|
|
561
|
+
"src/components/ui/tables/data-cross-table/data-cross-table.tsx",
|
|
562
|
+
"src/components/ui/tables/data-cross-table/editable-input.tsx"
|
|
563
|
+
]
|
|
564
|
+
},
|
|
554
565
|
{
|
|
555
566
|
"className": "flex items-center gap-1 font-normal",
|
|
556
567
|
"occurrences": 6,
|
|
@@ -605,13 +616,6 @@
|
|
|
605
616
|
"src/components/stories/data-table-no-utils-example.stories.tsx",
|
|
606
617
|
"src/components/stories/data-table-utils-example.stories.tsx"
|
|
607
618
|
]
|
|
608
|
-
},
|
|
609
|
-
{
|
|
610
|
-
"className": "mx-1 rounded bg-gray-200 px-1",
|
|
611
|
-
"occurrences": 6,
|
|
612
|
-
"files": [
|
|
613
|
-
"src/components/stories/data-table-utils-example.stories.tsx"
|
|
614
|
-
]
|
|
615
619
|
}
|
|
616
620
|
]
|
|
617
621
|
}
|
|
@@ -11,12 +11,26 @@ Rich text editor powered by Lexical with optional Markdown sync. Supports config
|
|
|
11
11
|
| Prop | Type | Default | Description |
|
|
12
12
|
| ---------------- | ---------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------- |
|
|
13
13
|
| `defaultValue` | `string` | `undefined` | Initial content. Interpreted as Markdown when `onlyMarkdown=true`, plain text otherwise. |
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
14
|
+
| `onValueChange` | `(markdown: string, meta: { mentions: MentionItem[] }) => void` | `undefined` | Fires on every edit with the current Markdown and a metadata object. `meta.mentions` is the deduped (by `id`) list of mentions in the text (empty when mentions are disabled). Takes precedence over `onMarkdownEdit`. |
|
|
15
|
+
| `onMarkdownEdit` | `(markdown: string) => void` | `undefined` | **Deprecated** — use `onValueChange`. Still honored as a fallback when `onValueChange` is not provided, but never receives mention metadata. |
|
|
16
|
+
| `plugins` | `("clear" \| "counter")[]` | `[]` | Enable optional plugins: clear buttons, character counter. |
|
|
17
|
+
| `toolbars` | `("block-format" \| "font-format" \| "history")[]` | `[]` | Enable toolbar groups. |
|
|
17
18
|
| `placeholder` | `string` | `"Start typing..."` | Placeholder for empty editor. |
|
|
18
19
|
| `onlyMarkdown` | `boolean` | `true` | Treat `defaultValue` as Markdown and sync editor to Markdown. If `false`, `defaultValue` is plain text. |
|
|
19
20
|
| `className` | `string` | `"overflow-hidden rounded-lg border border-d-border"` | Extra classes for the content editable. |
|
|
21
|
+
| `mentions` | `MentionItem[] \| (() => MentionItem[] \| Promise<MentionItem[]>)` | `undefined` | Enables @mention support when provided. Either a ready array (filtered client-side) or a **loader** called once on first menu open (no preload on mount). When omitted, no mention node/plugin/transformer is registered and output is identical to a mention-less editor. |
|
|
22
|
+
| `mentionTrigger` | `string` | `"@"` | Single character that opens the mention typeahead menu. |
|
|
23
|
+
|
|
24
|
+
### MentionItem
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
type MentionItem = {
|
|
28
|
+
id: string | number; // stable identifier (mandatory join key)
|
|
29
|
+
label: string; // shown text + rendered in the chip
|
|
30
|
+
value?: string; // free-form metadata (email, phone, URL, …)
|
|
31
|
+
sublabel?: string; // optional secondary line in the menu
|
|
32
|
+
};
|
|
33
|
+
```
|
|
20
34
|
|
|
21
35
|
---
|
|
22
36
|
|
|
@@ -31,8 +45,45 @@ Rich text editor powered by Lexical with optional Markdown sync. Supports config
|
|
|
31
45
|
- **Plugins**:
|
|
32
46
|
- `clear`: clear actions in the footer.
|
|
33
47
|
- `counter`: UTF-16 character counter in the footer.
|
|
34
|
-
- **Editing**: Autosave via `
|
|
48
|
+
- **Editing**: Autosave via `onValueChange`. Keyboard navigation and selection per Lexical defaults.
|
|
35
49
|
- **Markdown Features**: Full support for headings, lists, quotes, links, inline code, bold, italic, strikethrough, and custom underline syntax.
|
|
50
|
+
- **Mentions** (opt-in via `mentions`):
|
|
51
|
+
- `mentions` is either a ready `MentionItem[]` or a **loader function** `() => MentionItem[] | Promise<MentionItem[]>`. The loader is called **once, the first time the menu opens**, so a large list is never preloaded on mount; while it resolves the menu shows a `Loading…` row, and on failure a `Couldn't load mentions` row (re-opening the menu retries). The resolved list is then filtered client-side like the array form.
|
|
52
|
+
- With the **loader form**, `meta.mentions` cannot reconcile against a synchronous source list, so its emitted items carry `id`/`label`/`value` from the chip but **not `sublabel`** (which is menu-only metadata). The array form preserves `sublabel`.
|
|
53
|
+
- Typing `mentionTrigger` (default `@`) opens a typeahead menu filtering `mentions` by `label` (case-insensitive, word-prefix match).
|
|
54
|
+
- Selecting an item inserts a single **non-editable chip token** (not free text), styled with the secondary token family (`bg-d-secondary text-d-secondary-foreground`, the same as `<Badge variant="secondary">`) so it does not compete with primary-CTA buttons.
|
|
55
|
+
- Each chip carries a built-in **remove control** (a small `X` button with a ≥44pt hit area, `aria-label="Remove mention <label>"`) that deletes the mention. Removal fires `onValueChange` through the normal change path.
|
|
56
|
+
- When `mentions` is set **and** the `font-format` toolbar is enabled, an `@` insert button (`aria-label="Insert mention"`) appears immediately after the strikethrough button; clicking it inserts the trigger character at the caret and opens the typeahead menu. The button is hidden when `mentions` is not provided (or the `font-format` toolbar is not enabled).
|
|
57
|
+
- The menu shows `label` and `sublabel` (when present) and is fully keyboard-driven (↑/↓ navigate, Enter selects, Esc closes). When a non-empty query matches nothing, the menu shows a non-interactive `No matches for "<query>"` row.
|
|
58
|
+
- **Source of truth for tags is `onValueChange`'s `meta.mentions`, not the body.** Consumers should persist the deduped `MentionItem[]` it emits rather than parse the markdown body. The body markdown is for rendering/round-trip only.
|
|
59
|
+
|
|
60
|
+
### Mention markdown serialization
|
|
61
|
+
|
|
62
|
+
When mentions are enabled, each chip serializes to a stable, round-trip-safe token:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
@[<encodedLabel>](mention:<encodedId>) # when no value
|
|
66
|
+
@[<encodedLabel>](mention:<encodedId>?v=<encodedValue>) # when a value is present
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`<encodedLabel>`, `<encodedId>`, and `<encodedValue>` are all `encodeURIComponent`-encoded, so a value containing `]`, `)`, `?`, newlines, or `++` cannot break the parser, corrupt the round-trip, or be mangled by the `++`→`<u>` underline substitution. On import the segments are decoded with a safe fallback: a malformed `%`-sequence falls back to the raw segment instead of throwing, so one bad token never aborts the whole markdown import. The chip and menu always render the human-readable label from the node fields, never from the token text.
|
|
70
|
+
|
|
71
|
+
This token is parsed back into a chip on import (reconstructing `label`, `id`, and `value`), so mentions survive the markdown round-trip. The mention transformer is appended to the editor transformers **only when `mentions` is provided** — without it, markdown output is byte-identical to a mention-less editor.
|
|
72
|
+
|
|
73
|
+
### Dedup, reconciliation, and id types
|
|
74
|
+
|
|
75
|
+
`meta.mentions` keys on the **mandatory `id`** (not `value`). For each mention currently in the text:
|
|
76
|
+
|
|
77
|
+
- If the mention is still in the `mentions` prop, the original source `MentionItem` is returned **verbatim** — its original `id` type (e.g. `number`), `value`, and `sublabel` are preserved.
|
|
78
|
+
- If it is an **orphan** (e.g. imported from a saved token, no longer in `mentions`), the node's own data is returned. In this case the emitted `id` may be a **string**, since the token carries the id as text.
|
|
79
|
+
|
|
80
|
+
The emitted list is deduped by `id`, so mentioning the same person multiple times yields a single entry.
|
|
81
|
+
|
|
82
|
+
### Mention accessibility
|
|
83
|
+
|
|
84
|
+
- The menu is a `role="listbox"` with a stable `id` and `aria-label="People you can mention"`; each option is a `role="option"` with a stable `id` and `aria-selected`.
|
|
85
|
+
- While the menu is open, the contenteditable's `aria-activedescendant` points at the highlighted option's id (cleared on close).
|
|
86
|
+
- The chip exposes `aria-roledescription="mention"` and `aria-label="Mention: <label>[, <value>]"`; its remove button has `aria-label="Remove mention <label>"`.
|
|
36
87
|
|
|
37
88
|
---
|
|
38
89
|
|
|
@@ -50,7 +101,7 @@ export function EditorFull() {
|
|
|
50
101
|
<div className="space-y-4">
|
|
51
102
|
<AppEditor
|
|
52
103
|
defaultValue={markdown}
|
|
53
|
-
|
|
104
|
+
onValueChange={setMarkdown}
|
|
54
105
|
toolbars={["block-format", "font-format", "history"]}
|
|
55
106
|
plugins={["clear", "counter"]}
|
|
56
107
|
className="h-96"
|
|
@@ -76,7 +127,7 @@ export function MarkdownDefault() {
|
|
|
76
127
|
return (
|
|
77
128
|
<AppEditor
|
|
78
129
|
defaultValue={initial}
|
|
79
|
-
|
|
130
|
+
onValueChange={setMd}
|
|
80
131
|
toolbars={["block-format", "font-format", "history"]}
|
|
81
132
|
/>
|
|
82
133
|
);
|
|
@@ -95,7 +146,64 @@ export function UnderlineExample() {
|
|
|
95
146
|
return (
|
|
96
147
|
<AppEditor
|
|
97
148
|
defaultValue={initial}
|
|
98
|
-
|
|
149
|
+
onValueChange={setMd}
|
|
150
|
+
toolbars={["font-format"]}
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Mentions
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import * as React from "react";
|
|
160
|
+
import { AppEditor, type MentionItem } from "laif-ds";
|
|
161
|
+
|
|
162
|
+
const people: MentionItem[] = [
|
|
163
|
+
{ id: 1, label: "Alice Rossi", value: "alice@laif.it", sublabel: "Product" },
|
|
164
|
+
{ id: 2, label: "Bob Bianchi", value: "bob@laif.it", sublabel: "Engineering" },
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
export function EditorWithMentions() {
|
|
168
|
+
const [markdown, setMarkdown] = React.useState("");
|
|
169
|
+
// Persist this list — it is the source of truth for tags, not the body.
|
|
170
|
+
const [mentions, setMentions] = React.useState<MentionItem[]>([]);
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<AppEditor
|
|
174
|
+
mentions={people}
|
|
175
|
+
onValueChange={(md, { mentions }) => {
|
|
176
|
+
setMarkdown(md);
|
|
177
|
+
setMentions(mentions);
|
|
178
|
+
}}
|
|
179
|
+
mentionTrigger="@"
|
|
180
|
+
toolbars={["font-format"]}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The emitted markdown contains tokens like `Ping @[Alice%20Rossi](mention:1?v=alice%40laif.it) about this`, while `meta.mentions` yields `[{ id: 1, label: "Alice Rossi", value: "alice@laif.it", sublabel: "Product" }]` (the source item, returned verbatim).
|
|
187
|
+
|
|
188
|
+
### Async mentions (lazy loader)
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import * as React from "react";
|
|
192
|
+
import { AppEditor, type MentionItem } from "laif-ds";
|
|
193
|
+
|
|
194
|
+
export function EditorWithAsyncMentions() {
|
|
195
|
+
const [mentions, setMentions] = React.useState<MentionItem[]>([]);
|
|
196
|
+
|
|
197
|
+
// Called ONCE the first time the menu opens — never preloaded on mount.
|
|
198
|
+
const loadPeople = async (): Promise<MentionItem[]> => {
|
|
199
|
+
const users = await fetchApi("/users");
|
|
200
|
+
return users.map((u) => ({ id: u.id, label: u.name, value: u.email }));
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<AppEditor
|
|
205
|
+
mentions={loadPeople}
|
|
206
|
+
onValueChange={(_md, meta) => setMentions(meta.mentions)}
|
|
99
207
|
toolbars={["font-format"]}
|
|
100
208
|
/>
|
|
101
209
|
);
|
|
@@ -111,3 +219,5 @@ export function UnderlineExample() {
|
|
|
111
219
|
- **Accessibility**: Toolbar buttons and editor area follow Lexical best practices.
|
|
112
220
|
- **Underline Syntax**: Uses `<u>text</u>` HTML tags for underline, which is standard HTML and natively supported by both this component and `MarkdownRenderer` via `rehype-raw`.
|
|
113
221
|
- **Theme**: Uses design system tokens for consistent styling with `border-d-border`, `bg-d-background`, etc.
|
|
222
|
+
- **Mention remove control sizing**: The inline mention remove `X` button is intentionally sized for desktop pointers (20px), not the 44pt touch-target minimum — `AppEditor` is a desktop authoring surface. Mentions are also removable via Backspace (the chip is an atomic token), so the small control is not the only removal path.
|
|
223
|
+
- **Mention trigger**: `mentionTrigger` must be a single character; any multi-character or empty value falls back to `"@"`.
|
|
@@ -25,6 +25,7 @@ Complete chat interface: message list with markdown rendering, auto-scroll and t
|
|
|
25
25
|
| `suggestions` | `string[]` | `undefined` | Suggestions displayed when empty. Requires `append`. |
|
|
26
26
|
| `className` | `string` | `undefined` | Wrapper classes. |
|
|
27
27
|
| `welcomeTitle` | `string` | `"Da dove iniziamo?"` | Title shown in empty state. |
|
|
28
|
+
| `placeholder` | `string` | `"Ask AI..."` | Placeholder text for the composer input. |
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
@@ -12,7 +12,8 @@ Drag-and-drop uploader with keyboard activation, file type filtering, limits (co
|
|
|
12
12
|
| ------------------- | ------------------------- | -------------------------------------------------- | --------------------------------------------------------- |
|
|
13
13
|
| `extensions` | `AcceptItem[]` | `["pdf","image","video","audio"]` | Allowed extensions/categories (maps to accept attribute). |
|
|
14
14
|
| `multiple` | `boolean` | `false` | Enables multiple file selection. |
|
|
15
|
-
| `onUpload` | `(files: File[]) => void` | `undefined` | Called
|
|
15
|
+
| `onUpload` | `(files: File[]) => void` | `undefined` | Called when files are **added**, with the resulting list. |
|
|
16
|
+
| `onRemove` | `(removed: File[], remaining: File[]) => void` | `undefined` | Called when files are **removed** (single or remove-all), with the removed files and the list that remains. |
|
|
16
17
|
| `description` | `string` | `"Trascina un file o clicca per selezionare"` | Dropzone hint. |
|
|
17
18
|
| `formatDescription` | `string` | `"Formato accettato: PDF, Immagini, Video, Audio"` | Accepted formats hint. |
|
|
18
19
|
| `selectedLabel` | `string` | `"File selezionati"` | Title shown above selected files list. |
|
|
@@ -30,7 +31,7 @@ Drag-and-drop uploader with keyboard activation, file type filtering, limits (co
|
|
|
30
31
|
- **Filtering**: `extensions` map to accept string; only matching files are added.
|
|
31
32
|
- **Limits**: Enforces `maxFiles` and `maxTotalSize` (with compact error caption).
|
|
32
33
|
- **Previews**: Renders selected files with `FilePreview`; supports remove per file and remove-all.
|
|
33
|
-
- **Integration**: `onUpload`
|
|
34
|
+
- **Integration**: `onUpload` fires on **add** with the resulting `File[]`; `onRemove` fires on **removal** (single file or remove-all) with the removed files and the remaining list. Removals do **not** trigger `onUpload` — a consumer mirroring the list in its own state must handle both callbacks (see example below).
|
|
34
35
|
|
|
35
36
|
---
|
|
36
37
|
|
|
@@ -127,7 +128,11 @@ export function ExternalUploadExample() {
|
|
|
127
128
|
|
|
128
129
|
return (
|
|
129
130
|
<div className="space-y-4">
|
|
130
|
-
<FileUploader
|
|
131
|
+
<FileUploader
|
|
132
|
+
multiple
|
|
133
|
+
onUpload={setFiles}
|
|
134
|
+
onRemove={(_removed, remaining) => setFiles(remaining)}
|
|
135
|
+
/>
|
|
131
136
|
{files.length > 0 && (
|
|
132
137
|
<div className="flex justify-end">
|
|
133
138
|
<Button onClick={handleSubmit} isLoading={loading} iconLeft="Upload">
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.1.0",
|
|
3
|
-
"generatedAt": "2026-
|
|
3
|
+
"generatedAt": "2026-06-30T08:03:49.821Z",
|
|
4
4
|
"package": {
|
|
5
5
|
"name": "laif-ds",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.84"
|
|
7
7
|
},
|
|
8
8
|
"stats": {
|
|
9
|
-
"documentedComponentCount":
|
|
9
|
+
"documentedComponentCount": 93,
|
|
10
10
|
"catalogedComponentCount": 92,
|
|
11
11
|
"missingFromDocsCount": 1,
|
|
12
12
|
"deprecatedComponentCount": 2,
|
|
13
|
-
"averageAiReadinessScore":
|
|
14
|
-
"highAiReadinessCount":
|
|
13
|
+
"averageAiReadinessScore": 55.38,
|
|
14
|
+
"highAiReadinessCount": 37,
|
|
15
15
|
"lowAiReadinessCount": 42
|
|
16
16
|
},
|
|
17
17
|
"missingFromDocs": [
|
|
@@ -530,35 +530,45 @@
|
|
|
530
530
|
"hasPropsTable": true,
|
|
531
531
|
"hasExamples": true,
|
|
532
532
|
"exampleTitles": [
|
|
533
|
+
"MentionItem",
|
|
534
|
+
"Mention markdown serialization",
|
|
535
|
+
"Dedup, reconciliation, and id types",
|
|
536
|
+
"Mention accessibility",
|
|
533
537
|
"Full Toolbar + Plugins",
|
|
534
538
|
"Markdown Default Value",
|
|
535
|
-
"Underline Support"
|
|
539
|
+
"Underline Support",
|
|
540
|
+
"Mentions",
|
|
541
|
+
"Async mentions (lazy loader)"
|
|
536
542
|
],
|
|
537
543
|
"controlledPattern": "likely-controlled",
|
|
538
544
|
"requiredProps": []
|
|
539
545
|
},
|
|
540
546
|
"metadata": {
|
|
541
547
|
"props": {
|
|
542
|
-
"totalProps":
|
|
548
|
+
"totalProps": 10,
|
|
543
549
|
"requiredPropsCount": 0,
|
|
544
|
-
"typedPropsCount":
|
|
545
|
-
"describedPropsCount":
|
|
550
|
+
"typedPropsCount": 10,
|
|
551
|
+
"describedPropsCount": 10
|
|
546
552
|
},
|
|
547
553
|
"accessibility": {
|
|
548
554
|
"hasCoverage": true
|
|
549
555
|
},
|
|
550
556
|
"states": {
|
|
551
557
|
"coveredStates": [
|
|
552
|
-
"
|
|
558
|
+
"disabled",
|
|
559
|
+
"loading",
|
|
560
|
+
"empty",
|
|
561
|
+
"selected"
|
|
553
562
|
],
|
|
554
|
-
"coveredStateCount":
|
|
563
|
+
"coveredStateCount": 4
|
|
555
564
|
}
|
|
556
565
|
},
|
|
557
566
|
"aiReadiness": {
|
|
558
|
-
"score":
|
|
567
|
+
"score": 100,
|
|
559
568
|
"tier": "high"
|
|
560
569
|
},
|
|
561
570
|
"typeReferences": [
|
|
571
|
+
"interface AppEditorChangeMeta {\r",
|
|
562
572
|
"interface AppEditorProps {\r",
|
|
563
573
|
"type AppEditorPlugin = \"clear\" | \"counter\";\r",
|
|
564
574
|
"type AppEditorToolbar = \"block-format\" | \"font-format\" | \"history\";\r"
|
|
@@ -1934,10 +1944,10 @@
|
|
|
1934
1944
|
},
|
|
1935
1945
|
"metadata": {
|
|
1936
1946
|
"props": {
|
|
1937
|
-
"totalProps":
|
|
1947
|
+
"totalProps": 16,
|
|
1938
1948
|
"requiredPropsCount": 4,
|
|
1939
|
-
"typedPropsCount":
|
|
1940
|
-
"describedPropsCount":
|
|
1949
|
+
"typedPropsCount": 16,
|
|
1950
|
+
"describedPropsCount": 16
|
|
1941
1951
|
},
|
|
1942
1952
|
"accessibility": {
|
|
1943
1953
|
"hasCoverage": false
|
|
@@ -3169,10 +3179,10 @@
|
|
|
3169
3179
|
},
|
|
3170
3180
|
"metadata": {
|
|
3171
3181
|
"props": {
|
|
3172
|
-
"totalProps":
|
|
3182
|
+
"totalProps": 10,
|
|
3173
3183
|
"requiredPropsCount": 0,
|
|
3174
|
-
"typedPropsCount":
|
|
3175
|
-
"describedPropsCount":
|
|
3184
|
+
"typedPropsCount": 10,
|
|
3185
|
+
"describedPropsCount": 10
|
|
3176
3186
|
},
|
|
3177
3187
|
"accessibility": {
|
|
3178
3188
|
"hasCoverage": true
|
|
@@ -5887,6 +5897,102 @@
|
|
|
5887
5897
|
"interface TooltipContentProps extends React_2.ComponentProps<typeof TooltipPrimitive.Content>, VariantProps<typeof tooltipVariants> {\r"
|
|
5888
5898
|
]
|
|
5889
5899
|
},
|
|
5900
|
+
{
|
|
5901
|
+
"name": "truncated-cell",
|
|
5902
|
+
"markdownPath": "components/truncated-cell.md",
|
|
5903
|
+
"importPath": "laif-ds",
|
|
5904
|
+
"deprecated": false,
|
|
5905
|
+
"replacement": null,
|
|
5906
|
+
"tags": {
|
|
5907
|
+
"cataloged": false,
|
|
5908
|
+
"aiReady": true
|
|
5909
|
+
},
|
|
5910
|
+
"docs": {
|
|
5911
|
+
"sections": [
|
|
5912
|
+
{
|
|
5913
|
+
"title": "Features",
|
|
5914
|
+
"slug": "features"
|
|
5915
|
+
},
|
|
5916
|
+
{
|
|
5917
|
+
"title": "Usage",
|
|
5918
|
+
"slug": "usage"
|
|
5919
|
+
},
|
|
5920
|
+
{
|
|
5921
|
+
"title": "Props",
|
|
5922
|
+
"slug": "props"
|
|
5923
|
+
},
|
|
5924
|
+
{
|
|
5925
|
+
"title": "Behavior",
|
|
5926
|
+
"slug": "behavior"
|
|
5927
|
+
},
|
|
5928
|
+
{
|
|
5929
|
+
"title": "Technical Details",
|
|
5930
|
+
"slug": "technical-details"
|
|
5931
|
+
},
|
|
5932
|
+
{
|
|
5933
|
+
"title": "Accessibility",
|
|
5934
|
+
"slug": "accessibility"
|
|
5935
|
+
},
|
|
5936
|
+
{
|
|
5937
|
+
"title": "Examples",
|
|
5938
|
+
"slug": "examples"
|
|
5939
|
+
},
|
|
5940
|
+
{
|
|
5941
|
+
"title": "Comparison with Alternatives",
|
|
5942
|
+
"slug": "comparison-with-alternatives"
|
|
5943
|
+
}
|
|
5944
|
+
],
|
|
5945
|
+
"hasPropsTable": true,
|
|
5946
|
+
"hasExamples": true,
|
|
5947
|
+
"exampleTitles": [
|
|
5948
|
+
"Truncation Detection",
|
|
5949
|
+
"Visual States",
|
|
5950
|
+
"Styling",
|
|
5951
|
+
"Content Types",
|
|
5952
|
+
"Choosing Between `children` and `text`",
|
|
5953
|
+
"Custom Popover Content",
|
|
5954
|
+
"Button Visibility Control",
|
|
5955
|
+
"Empty Fallback",
|
|
5956
|
+
"Popover Styling",
|
|
5957
|
+
"Dependencies",
|
|
5958
|
+
"Performance Optimizations",
|
|
5959
|
+
"Event Handling",
|
|
5960
|
+
"Accessibility Features",
|
|
5961
|
+
"In a Table Cell",
|
|
5962
|
+
"With Maximum Width Constraint",
|
|
5963
|
+
"Multiline Text Support",
|
|
5964
|
+
"vs Tooltip",
|
|
5965
|
+
"vs CSS text-overflow alone",
|
|
5966
|
+
"vs Expandable Text"
|
|
5967
|
+
],
|
|
5968
|
+
"controlledPattern": "likely-controlled",
|
|
5969
|
+
"requiredProps": []
|
|
5970
|
+
},
|
|
5971
|
+
"metadata": {
|
|
5972
|
+
"props": {
|
|
5973
|
+
"totalProps": 9,
|
|
5974
|
+
"requiredPropsCount": 0,
|
|
5975
|
+
"typedPropsCount": 9,
|
|
5976
|
+
"describedPropsCount": 9
|
|
5977
|
+
},
|
|
5978
|
+
"accessibility": {
|
|
5979
|
+
"hasCoverage": true
|
|
5980
|
+
},
|
|
5981
|
+
"states": {
|
|
5982
|
+
"coveredStates": [
|
|
5983
|
+
"hover",
|
|
5984
|
+
"focus",
|
|
5985
|
+
"empty"
|
|
5986
|
+
],
|
|
5987
|
+
"coveredStateCount": 3
|
|
5988
|
+
}
|
|
5989
|
+
},
|
|
5990
|
+
"aiReadiness": {
|
|
5991
|
+
"score": 100,
|
|
5992
|
+
"tier": "high"
|
|
5993
|
+
},
|
|
5994
|
+
"typeReferences": []
|
|
5995
|
+
},
|
|
5890
5996
|
{
|
|
5891
5997
|
"name": "TypingIndicator",
|
|
5892
5998
|
"markdownPath": "components/TypingIndicator.md",
|
|
@@ -6061,5 +6167,5 @@
|
|
|
6061
6167
|
]
|
|
6062
6168
|
}
|
|
6063
6169
|
],
|
|
6064
|
-
"checksum": "
|
|
6170
|
+
"checksum": "b5e0252676d93c4e43b15be47dda3d1fae971b84bc0f2907ec12db211e686d5d"
|
|
6065
6171
|
}
|