laif-ds 0.2.83 → 0.2.85

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 CHANGED
@@ -5,6 +5,36 @@ 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.85]
9
+
10
+ ### 🐛 Fixed
11
+
12
+ - **AppEditor**: The `@mention` typeahead menu now renders above modals. The menu is portaled to `<body>`, so inside a laif-ds `Dialog` it was hidden behind the overlay (z-index had no effect on the statically-positioned menu) and, once visible, its options were unclickable (Radix sets `pointer-events:none` on `<body>` while a modal is open). The menu is now positioned with a higher stacking order and re-enables pointer events, so it is both visible and selectable inside dialogs (see issue ISS-235)
13
+
14
+ ## [laif-ds@0.2.84]
15
+
16
+ ### 🚀 Added
17
+
18
+ - **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
19
+
20
+ ### 🔧 Changed
21
+
22
+ - **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
23
+
24
+ ### 🐛 Fixed
25
+
26
+ - **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
27
+
28
+ ## [laif-ds@0.2.83]
29
+
30
+ ### 🚀 Added
31
+
32
+ - **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
33
+
34
+ ### 🔧 Changed
35
+
36
+ - **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)
37
+
8
38
  ## [laif-ds@0.2.82]
9
39
 
10
40
  ### 🚀 Added
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-06-09T07:56:53.942Z",
3
+ "generatedAt": "2026-06-30T13:45:32.734Z",
4
4
  "package": {
5
5
  "name": "laif-ds",
6
- "version": "0.2.83"
6
+ "version": "0.2.85"
7
7
  },
8
8
  "summary": {
9
- "scannedFiles": 394,
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": 73,
18
- "averageAiReadinessScore": 54.73
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
- | `onMarkdownEdit` | `(markdown: string) => void` | `undefined` | Emits current content as Markdown on change. |
15
- | `plugins` | `("clear" | "counter")[]` | `[]` | Enable optional plugins: clear buttons, character counter. |
16
- | `toolbars` | `("block-format" | "font-format" | "history")[]` | `[]` | Enable toolbar groups. |
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 `onMarkdownEdit`. Keyboard navigation and selection per Lexical defaults.
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
- onMarkdownEdit={setMarkdown}
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
- onMarkdownEdit={setMd}
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
- onMarkdownEdit={setMd}
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 `"@"`.
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "schemaVersion": "1.1.0",
3
- "generatedAt": "2026-06-09T07:56:53.318Z",
3
+ "generatedAt": "2026-06-30T13:45:32.075Z",
4
4
  "package": {
5
5
  "name": "laif-ds",
6
- "version": "0.2.83"
6
+ "version": "0.2.85"
7
7
  },
8
8
  "stats": {
9
- "documentedComponentCount": 92,
9
+ "documentedComponentCount": 93,
10
10
  "catalogedComponentCount": 92,
11
11
  "missingFromDocsCount": 1,
12
12
  "deprecatedComponentCount": 2,
13
- "averageAiReadinessScore": 54.73,
14
- "highAiReadinessCount": 36,
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": 7,
548
+ "totalProps": 10,
543
549
  "requiredPropsCount": 0,
544
- "typedPropsCount": 7,
545
- "describedPropsCount": 7
550
+ "typedPropsCount": 10,
551
+ "describedPropsCount": 10
546
552
  },
547
553
  "accessibility": {
548
554
  "hasCoverage": true
549
555
  },
550
556
  "states": {
551
557
  "coveredStates": [
552
- "empty"
558
+ "disabled",
559
+ "loading",
560
+ "empty",
561
+ "selected"
553
562
  ],
554
- "coveredStateCount": 1
563
+ "coveredStateCount": 4
555
564
  }
556
565
  },
557
566
  "aiReadiness": {
558
- "score": 85,
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"
@@ -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": "911fd5d24f66568dc9007439161320c179ac69eaed81a631fe5b38ffc901e9c7"
6170
+ "checksum": "7f642d50b2acfeced3ccdf69ffdba0041f4c65b70e3b54ae8d47ba311c8637a9"
6065
6171
  }
@@ -0,0 +1,113 @@
1
+ "use client";
2
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
3
+ import { useLexicalComposerContext as m } from "../../../node_modules/@lexical/react/LexicalComposerContext.prod.js";
4
+ import { DecoratorNode as u, $getNodeByKey as c } from "../../../node_modules/lexical/Lexical.prod.js";
5
+ import { Icon as _ } from "../../ui/icon.js";
6
+ const p = "bg-d-primary/10 text-d-primary inline-flex items-center gap-0.5 rounded-md py-0.5 pr-0.5 pl-1.5 text-sm font-medium align-baseline", d = "ml-0.5 inline-flex size-5 items-center justify-center rounded-sm hover:bg-d-primary/20 cursor-pointer";
7
+ function b({ nodeKey: n, data: e }) {
8
+ const [i] = m(), a = () => {
9
+ i.update(() => {
10
+ const r = c(n);
11
+ f(r) && r.remove();
12
+ });
13
+ }, l = e.value ? `Mention: ${e.label}, ${e.value}` : `Mention: ${e.label}`;
14
+ return /* @__PURE__ */ s(
15
+ "span",
16
+ {
17
+ className: p,
18
+ "data-mention-id": e.id,
19
+ "aria-roledescription": "mention",
20
+ "aria-label": l,
21
+ children: [
22
+ "@",
23
+ e.label,
24
+ /* @__PURE__ */ o(
25
+ "button",
26
+ {
27
+ type: "button",
28
+ className: d,
29
+ "aria-label": `Remove mention ${e.label}`,
30
+ onClick: a,
31
+ children: /* @__PURE__ */ o(_, { name: "X", size: "xs", "aria-hidden": !0 })
32
+ }
33
+ )
34
+ ]
35
+ }
36
+ );
37
+ }
38
+ class t extends u {
39
+ __mentionId;
40
+ __mentionLabel;
41
+ __mentionValue;
42
+ static getType() {
43
+ return "mention";
44
+ }
45
+ static clone(e) {
46
+ return new t(
47
+ {
48
+ id: e.__mentionId,
49
+ label: e.__mentionLabel,
50
+ value: e.__mentionValue
51
+ },
52
+ e.__key
53
+ );
54
+ }
55
+ constructor(e, i) {
56
+ super(i), this.__mentionId = e.id, this.__mentionLabel = e.label, this.__mentionValue = e.value;
57
+ }
58
+ static importJSON(e) {
59
+ return h({
60
+ id: e.mentionId,
61
+ label: e.mentionLabel,
62
+ value: e.mentionValue
63
+ });
64
+ }
65
+ exportJSON() {
66
+ return {
67
+ ...super.exportJSON(),
68
+ type: "mention",
69
+ version: 1,
70
+ mentionId: this.__mentionId,
71
+ mentionLabel: this.__mentionLabel,
72
+ mentionValue: this.__mentionValue
73
+ };
74
+ }
75
+ static importDOM() {
76
+ return null;
77
+ }
78
+ getMentionData() {
79
+ return {
80
+ id: this.__mentionId,
81
+ label: this.__mentionLabel,
82
+ value: this.__mentionValue
83
+ };
84
+ }
85
+ // Plain-text representation (used by $rootTextContent, copy/paste, counters).
86
+ // Intentionally returns the human-readable `@label`, NOT the markdown token —
87
+ // the token is the markdown-serialization concern (see MENTION_TRANSFORMER),
88
+ // while plain-text consumers want readable text.
89
+ getTextContent() {
90
+ return `@${this.__mentionLabel}`;
91
+ }
92
+ createDOM() {
93
+ const e = document.createElement("span");
94
+ return e.setAttribute("data-slot", "mention-node"), e;
95
+ }
96
+ updateDOM() {
97
+ return !1;
98
+ }
99
+ decorate() {
100
+ return /* @__PURE__ */ o(b, { nodeKey: this.getKey(), data: this.getMentionData() });
101
+ }
102
+ }
103
+ function h(n) {
104
+ return new t(n);
105
+ }
106
+ function f(n) {
107
+ return n instanceof t;
108
+ }
109
+ export {
110
+ h as $createMentionNode,
111
+ f as $isMentionNode,
112
+ t as MentionNode
113
+ };