@relevaince/mentions 0.3.2 → 0.5.0
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/README.md +125 -83
- package/dist/index.d.mts +41 -8
- package/dist/index.d.ts +41 -8
- package/dist/index.js +542 -90
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +548 -96
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -11,12 +11,27 @@ This is **not** a simple `@mention` dropdown. It is a resource-addressing langua
|
|
|
11
11
|
- **Cross-group search** — `searchAll` finds items across every level with a single query
|
|
12
12
|
- **Nested search** — search input inside the dropdown to filter children in real time
|
|
13
13
|
- **Async providers** — fetch suggestions from any API
|
|
14
|
+
- **Debounced fetching** — per-provider `debounceMs` prevents API floods
|
|
14
15
|
- **Structured output** — returns `{ markdown, tokens, plainText }` on every change
|
|
15
16
|
- **Markdown serialization** — `@[label](id)` token syntax for storage and LLM context
|
|
16
17
|
- **Markdown parsing** — `extractFromMarkdown()` returns tokens + plain text from a markdown string
|
|
17
18
|
- **Headless styling** — zero bundled CSS, style via `data-*` attributes with Tailwind or plain CSS
|
|
18
19
|
- **Accessible** — full ARIA combobox pattern with keyboard navigation
|
|
19
20
|
- **SSR compatible** — safe for Next.js with `"use client"` directive
|
|
21
|
+
- **Grouped suggestions** — section items under headers (e.g. "Active", "Pending")
|
|
22
|
+
- **Recently used items** — show recent mentions when the query is empty
|
|
23
|
+
- **Empty state** — customizable "No results" message
|
|
24
|
+
- **Mention lifecycle** — `onMentionAdd` / `onMentionRemove` callbacks
|
|
25
|
+
- **Mention interaction** — `onMentionClick` / `onMentionHover` handlers on chips
|
|
26
|
+
- **Mention validation** — mark stale/invalid mentions with `validateMention`
|
|
27
|
+
- **Controlled value** — reactive `value` prop for external content updates
|
|
28
|
+
- **Auto-resize** — `minHeight` / `maxHeight` for growing editor
|
|
29
|
+
- **Submit key customization** — choose Enter, Cmd+Enter, or none
|
|
30
|
+
- **Edge-aware popover** — flips above when near viewport bottom
|
|
31
|
+
- **Portal support** — render the dropdown into a custom container
|
|
32
|
+
- **Tab to complete** — Tab selects the active suggestion
|
|
33
|
+
- **Trigger gating** — `allowTrigger` to conditionally suppress the dropdown
|
|
34
|
+
- **Multi-instance** — unique ARIA IDs per component instance
|
|
20
35
|
|
|
21
36
|
## Install
|
|
22
37
|
|
|
@@ -38,6 +53,7 @@ import { MentionsInput, type MentionProvider } from "@relevaince/mentions";
|
|
|
38
53
|
const workspaceProvider: MentionProvider = {
|
|
39
54
|
trigger: "@",
|
|
40
55
|
name: "Workspaces",
|
|
56
|
+
debounceMs: 200,
|
|
41
57
|
async getRootItems(query) {
|
|
42
58
|
const res = await fetch(`/api/workspaces?q=${query}`);
|
|
43
59
|
return res.json();
|
|
@@ -46,6 +62,10 @@ const workspaceProvider: MentionProvider = {
|
|
|
46
62
|
const res = await fetch(`/api/workspaces/${parent.id}/files?q=${query}`);
|
|
47
63
|
return res.json();
|
|
48
64
|
},
|
|
65
|
+
async getRecentItems() {
|
|
66
|
+
const res = await fetch("/api/workspaces/recent");
|
|
67
|
+
return res.json();
|
|
68
|
+
},
|
|
49
69
|
};
|
|
50
70
|
|
|
51
71
|
function Chat() {
|
|
@@ -58,7 +78,11 @@ function Chat() {
|
|
|
58
78
|
console.log(output.plainText); // "Summarize Marketing"
|
|
59
79
|
}}
|
|
60
80
|
onSubmit={(output) => sendMessage(output)}
|
|
81
|
+
onMentionAdd={(token) => console.log("Added:", token)}
|
|
82
|
+
onMentionRemove={(token) => console.log("Removed:", token)}
|
|
61
83
|
placeholder="Ask anything..."
|
|
84
|
+
minHeight={40}
|
|
85
|
+
maxHeight={200}
|
|
62
86
|
/>
|
|
63
87
|
);
|
|
64
88
|
}
|
|
@@ -81,19 +105,29 @@ type MentionToken = {
|
|
|
81
105
|
|
|
82
106
|
### MentionProvider
|
|
83
107
|
|
|
84
|
-
Register one provider per trigger character. Each provider fetches suggestions and optionally supports nested drill-down
|
|
108
|
+
Register one provider per trigger character. Each provider fetches suggestions and optionally supports nested drill-down, cross-group search, debouncing, and recent items:
|
|
85
109
|
|
|
86
110
|
```ts
|
|
87
111
|
type MentionProvider = {
|
|
88
|
-
trigger: string;
|
|
89
|
-
name: string;
|
|
112
|
+
trigger: string;
|
|
113
|
+
name: string;
|
|
90
114
|
getRootItems: (query: string) => Promise<MentionItem[]>;
|
|
91
115
|
getChildren?: (parent: MentionItem, query: string) => Promise<MentionItem[]>;
|
|
92
|
-
searchAll?: (query: string) => Promise<MentionItem[]>;
|
|
116
|
+
searchAll?: (query: string) => Promise<MentionItem[]>;
|
|
117
|
+
debounceMs?: number;
|
|
118
|
+
getRecentItems?: () => Promise<MentionItem[]>;
|
|
93
119
|
};
|
|
94
120
|
```
|
|
95
121
|
|
|
96
|
-
|
|
122
|
+
| Property | Type | Required | Description |
|
|
123
|
+
|----------|------|----------|-------------|
|
|
124
|
+
| `trigger` | `string` | Yes | Character(s) that activate this provider |
|
|
125
|
+
| `name` | `string` | Yes | Human-readable name for ARIA labels |
|
|
126
|
+
| `getRootItems` | `(query) => Promise<MentionItem[]>` | Yes | Top-level suggestions |
|
|
127
|
+
| `getChildren` | `(parent, query) => Promise<MentionItem[]>` | No | Child suggestions for drill-down |
|
|
128
|
+
| `searchAll` | `(query) => Promise<MentionItem[]>` | No | Flat search across all levels |
|
|
129
|
+
| `debounceMs` | `number` | No | Delay before fetching (prevents API floods) |
|
|
130
|
+
| `getRecentItems` | `() => Promise<MentionItem[]>` | No | Recently used items, shown on empty query |
|
|
97
131
|
|
|
98
132
|
### MentionItem
|
|
99
133
|
|
|
@@ -104,15 +138,16 @@ type MentionItem = {
|
|
|
104
138
|
id: string;
|
|
105
139
|
type: string;
|
|
106
140
|
label: string;
|
|
107
|
-
icon?: ReactNode;
|
|
108
|
-
description?: string;
|
|
109
|
-
hasChildren?: boolean;
|
|
141
|
+
icon?: ReactNode;
|
|
142
|
+
description?: string;
|
|
143
|
+
hasChildren?: boolean;
|
|
110
144
|
data?: unknown;
|
|
111
|
-
rootLabel?: string;
|
|
145
|
+
rootLabel?: string;
|
|
146
|
+
group?: string; // group items under section headers
|
|
112
147
|
};
|
|
113
148
|
```
|
|
114
149
|
|
|
115
|
-
Set `
|
|
150
|
+
Set `group` on items to render them under section headers in the dropdown (e.g. "Active", "Pending", "Recent").
|
|
116
151
|
|
|
117
152
|
### MentionsOutput
|
|
118
153
|
|
|
@@ -120,9 +155,9 @@ Structured output returned on every change and submit:
|
|
|
120
155
|
|
|
121
156
|
```ts
|
|
122
157
|
type MentionsOutput = {
|
|
123
|
-
markdown: string;
|
|
124
|
-
tokens: MentionToken[];
|
|
125
|
-
plainText: string;
|
|
158
|
+
markdown: string;
|
|
159
|
+
tokens: MentionToken[];
|
|
160
|
+
plainText: string;
|
|
126
161
|
};
|
|
127
162
|
```
|
|
128
163
|
|
|
@@ -130,22 +165,37 @@ type MentionsOutput = {
|
|
|
130
165
|
|
|
131
166
|
| Prop | Type | Default | Description |
|
|
132
167
|
|------|------|---------|-------------|
|
|
133
|
-
| `value` | `string` | — |
|
|
168
|
+
| `value` | `string` | — | Controlled markdown content (reactive — updates editor on change) |
|
|
134
169
|
| `providers` | `MentionProvider[]` | **required** | Suggestion providers, one per trigger |
|
|
135
170
|
| `onChange` | `(output: MentionsOutput) => void` | — | Called on every content change |
|
|
136
|
-
| `onSubmit` | `(output: MentionsOutput) => void` | — | Called on
|
|
171
|
+
| `onSubmit` | `(output: MentionsOutput) => void` | — | Called on submit shortcut |
|
|
137
172
|
| `placeholder` | `string` | `"Type a message..."` | Placeholder text |
|
|
138
173
|
| `autoFocus` | `boolean` | `false` | Focus editor on mount |
|
|
139
174
|
| `disabled` | `boolean` | `false` | Disable editing |
|
|
140
175
|
| `className` | `string` | — | CSS class on the wrapper |
|
|
141
176
|
| `maxLength` | `number` | — | Max plain text character count |
|
|
177
|
+
| `clearOnSubmit` | `boolean` | `true` | Auto-clear after `onSubmit` |
|
|
178
|
+
| `submitKey` | `"enter" \| "mod+enter" \| "none"` | `"enter"` | Which key combo triggers submit |
|
|
179
|
+
| `minHeight` | `number` | — | Minimum editor height in px |
|
|
180
|
+
| `maxHeight` | `number` | — | Maximum editor height in px (enables scroll) |
|
|
181
|
+
| `onFocus` | `() => void` | — | Called when editor gains focus |
|
|
182
|
+
| `onBlur` | `() => void` | — | Called when editor loses focus |
|
|
183
|
+
| `onMentionAdd` | `(token: MentionToken) => void` | — | Called when a mention is inserted |
|
|
184
|
+
| `onMentionRemove` | `(token: MentionToken) => void` | — | Called when a mention is deleted |
|
|
185
|
+
| `onMentionClick` | `(token: MentionToken, event: MouseEvent) => void` | — | Called when a mention chip is clicked |
|
|
186
|
+
| `onMentionHover` | `(token: MentionToken) => ReactNode` | — | Return content for a hover tooltip |
|
|
142
187
|
| `renderItem` | `(item, depth) => ReactNode` | — | Custom suggestion item renderer |
|
|
143
|
-
| `clearOnSubmit` | `boolean` | `true` | Auto-clear the editor after `onSubmit` fires |
|
|
144
188
|
| `renderChip` | `(token) => ReactNode` | — | Custom inline mention chip renderer |
|
|
189
|
+
| `renderEmpty` | `(query: string) => ReactNode` | — | Custom empty state (no results) |
|
|
190
|
+
| `renderLoading` | `() => ReactNode` | — | Custom loading indicator |
|
|
191
|
+
| `renderGroupHeader` | `(group: string) => ReactNode` | — | Custom section header renderer |
|
|
192
|
+
| `allowTrigger` | `(trigger, { textBefore }) => boolean` | — | Conditionally suppress the dropdown |
|
|
193
|
+
| `validateMention` | `(token) => boolean \| Promise<boolean>` | — | Validate mentions; invalid ones get `data-mention-invalid` |
|
|
194
|
+
| `portalContainer` | `HTMLElement` | — | Render dropdown into a custom DOM node |
|
|
145
195
|
|
|
146
196
|
## Imperative ref API
|
|
147
197
|
|
|
148
|
-
`MentionsInput` supports `forwardRef` for programmatic control
|
|
198
|
+
`MentionsInput` supports `forwardRef` for programmatic control:
|
|
149
199
|
|
|
150
200
|
```tsx
|
|
151
201
|
import { useRef } from "react";
|
|
@@ -158,16 +208,15 @@ function Chat() {
|
|
|
158
208
|
<>
|
|
159
209
|
<MentionsInput ref={ref} providers={providers} />
|
|
160
210
|
|
|
161
|
-
<button onClick={() => ref.current?.clear()}>
|
|
162
|
-
Clear
|
|
163
|
-
</button>
|
|
164
|
-
|
|
211
|
+
<button onClick={() => ref.current?.clear()}>Clear</button>
|
|
165
212
|
<button onClick={() => {
|
|
166
213
|
ref.current?.setContent("Summarize @[NDA](contract:c_1) risks");
|
|
167
214
|
ref.current?.focus();
|
|
168
|
-
}}>
|
|
169
|
-
|
|
170
|
-
|
|
215
|
+
}}>Use Prompt</button>
|
|
216
|
+
<button onClick={() => {
|
|
217
|
+
const output = ref.current?.getOutput();
|
|
218
|
+
console.log(output?.tokens);
|
|
219
|
+
}}>Read Output</button>
|
|
171
220
|
</>
|
|
172
221
|
);
|
|
173
222
|
}
|
|
@@ -180,42 +229,21 @@ function Chat() {
|
|
|
180
229
|
| `clear` | `() => void` | Clears all editor content |
|
|
181
230
|
| `setContent` | `(markdown: string) => void` | Replaces content with a markdown string (mention tokens are parsed) |
|
|
182
231
|
| `focus` | `() => void` | Focuses the editor and places the cursor at the end |
|
|
232
|
+
| `getOutput` | `() => MentionsOutput \| null` | Reads the current structured output without waiting for onChange |
|
|
183
233
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
By default, the editor clears itself after `onSubmit` fires. Disable with `clearOnSubmit={false}` if you want to manage clearing yourself:
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
<MentionsInput
|
|
190
|
-
onSubmit={(output) => sendMessage(output)}
|
|
191
|
-
clearOnSubmit={false} // don't auto-clear
|
|
192
|
-
/>
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## Nested mentions
|
|
196
|
-
|
|
197
|
-
The killer feature. When a `MentionItem` has `hasChildren: true`, selecting it drills into the next level using `provider.getChildren()`:
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
@workspace → choose workspace → choose file inside workspace
|
|
201
|
-
@contract → choose contract → choose clause
|
|
202
|
-
:web → choose provider → type query
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
When inside a nested level, a search input appears in the dropdown. Type to filter children in real time. Press `Backspace` on an empty search input to go back one level.
|
|
206
|
-
|
|
207
|
-
Keyboard navigation:
|
|
234
|
+
## Keyboard shortcuts
|
|
208
235
|
|
|
209
236
|
| Key | Context | Action |
|
|
210
237
|
|-----|---------|--------|
|
|
211
238
|
| `↑` `↓` | Suggestions open | Navigate suggestions |
|
|
212
239
|
| `Enter` | Suggestions open | Select / drill into children |
|
|
240
|
+
| `Tab` | Suggestions open | Select the active suggestion |
|
|
213
241
|
| `→` | Suggestions open | Drill into children (if item has children) |
|
|
214
242
|
| `←` | Nested level | Go back one level |
|
|
215
243
|
| `Backspace` | Nested level, empty search | Go back one level |
|
|
216
244
|
| `Escape` | Suggestions open | Close suggestions |
|
|
217
|
-
| `Enter` | Editor (
|
|
218
|
-
| `Shift+Enter` | Editor | New line |
|
|
245
|
+
| `Enter` | Editor (default `submitKey`) | Submit message |
|
|
246
|
+
| `Shift+Enter` | Editor (default `submitKey`) | New line |
|
|
219
247
|
| `Cmd/Ctrl+Enter` | Editor | Submit message |
|
|
220
248
|
|
|
221
249
|
## Markdown format
|
|
@@ -224,6 +252,7 @@ Mentions serialize to a compact token syntax:
|
|
|
224
252
|
|
|
225
253
|
```
|
|
226
254
|
@[Marketing Workspace](ws_123) summarize the latest files
|
|
255
|
+
@Marketing[Q4 Strategy.pdf](file:file_1) review this document
|
|
227
256
|
```
|
|
228
257
|
|
|
229
258
|
Use the standalone helpers for server-side processing:
|
|
@@ -235,38 +264,38 @@ import {
|
|
|
235
264
|
extractFromMarkdown,
|
|
236
265
|
} from "@relevaince/mentions";
|
|
237
266
|
|
|
238
|
-
// Parse markdown into a Tiptap-compatible JSON document
|
|
239
267
|
const doc = parseFromMarkdown("Check @[NDA](contract:c_44) for risks");
|
|
240
|
-
|
|
241
|
-
// Serialize a Tiptap JSON document back to markdown
|
|
242
268
|
const md = serializeToMarkdown(doc);
|
|
243
|
-
|
|
244
|
-
// Extract tokens and plain text directly from a markdown string
|
|
245
269
|
const { tokens, plainText } = extractFromMarkdown(
|
|
246
270
|
"Summarize @Marketing[Q4 Strategy.pdf](file:file_1) for the team"
|
|
247
271
|
);
|
|
248
|
-
// tokens → [{ id: "file_1", type: "file", label: "Q4 Strategy.pdf" }]
|
|
249
|
-
// plainText → "Summarize Q4 Strategy.pdf for the team"
|
|
250
272
|
```
|
|
251
273
|
|
|
252
274
|
## Styling
|
|
253
275
|
|
|
254
|
-
Zero bundled CSS. Every element exposes `data-*` attributes
|
|
276
|
+
Zero bundled CSS. Every element exposes `data-*` attributes:
|
|
255
277
|
|
|
256
278
|
```css
|
|
257
|
-
/* Mention chips
|
|
258
|
-
[data-mention]
|
|
259
|
-
[data-mention][data-type="workspace"]
|
|
260
|
-
[data-mention][data-type="contract"]
|
|
279
|
+
/* Mention chips */
|
|
280
|
+
[data-mention] { /* base chip */ }
|
|
281
|
+
[data-mention][data-type="workspace"] { /* workspace chip */ }
|
|
282
|
+
[data-mention][data-type="contract"] { /* contract chip */ }
|
|
283
|
+
[data-mention-clickable] { /* clickable chip (when onMentionClick set) */ }
|
|
284
|
+
[data-mention-invalid] { /* invalid/stale mention */ }
|
|
285
|
+
[data-mention-tooltip] { /* hover tooltip container */ }
|
|
261
286
|
|
|
262
287
|
/* Suggestion popover */
|
|
263
|
-
[data-suggestions]
|
|
264
|
-
[data-
|
|
265
|
-
[data-
|
|
266
|
-
[data-suggestion-
|
|
267
|
-
[data-suggestion-
|
|
268
|
-
[data-suggestion-
|
|
269
|
-
[data-suggestion-
|
|
288
|
+
[data-suggestions] { /* popover wrapper */ }
|
|
289
|
+
[data-suggestions-position="above"] { /* when popover flips above */ }
|
|
290
|
+
[data-suggestions-position="below"] { /* when popover is below */ }
|
|
291
|
+
[data-suggestion-item] { /* each item */ }
|
|
292
|
+
[data-suggestion-item-active] { /* highlighted item */ }
|
|
293
|
+
[data-suggestion-group-header] { /* group section header */ }
|
|
294
|
+
[data-suggestion-empty] { /* "no results" state */ }
|
|
295
|
+
[data-suggestion-loading] { /* loading indicator */ }
|
|
296
|
+
[data-suggestion-breadcrumb] { /* breadcrumb bar (nested) */ }
|
|
297
|
+
[data-suggestion-search] { /* search input wrapper */ }
|
|
298
|
+
[data-suggestion-search-input] { /* search input field */ }
|
|
270
299
|
```
|
|
271
300
|
|
|
272
301
|
Example with Tailwind:
|
|
@@ -288,6 +317,18 @@ Example with Tailwind:
|
|
|
288
317
|
[data-suggestion-item-active] {
|
|
289
318
|
@apply bg-neutral-100;
|
|
290
319
|
}
|
|
320
|
+
|
|
321
|
+
[data-suggestion-group-header] {
|
|
322
|
+
@apply px-3 py-1 text-[10px] font-semibold text-neutral-400 uppercase tracking-wider;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
[data-suggestion-empty] {
|
|
326
|
+
@apply px-3 py-2 text-xs text-neutral-400 italic;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
[data-mention-invalid] {
|
|
330
|
+
@apply opacity-50 line-through;
|
|
331
|
+
}
|
|
291
332
|
```
|
|
292
333
|
|
|
293
334
|
## Architecture
|
|
@@ -295,30 +336,31 @@ Example with Tailwind:
|
|
|
295
336
|
```
|
|
296
337
|
<MentionsInput>
|
|
297
338
|
├── Tiptap Editor (ProseMirror)
|
|
298
|
-
│ ├── MentionNode Extension — inline atom nodes with id/label/type
|
|
299
|
-
│ ├── Suggestion Plugin — multi-trigger detection +
|
|
300
|
-
│ ├── Enter/Submit Extension —
|
|
339
|
+
│ ├── MentionNode Extension — inline atom nodes with id/label/type + click/hover
|
|
340
|
+
│ ├── Suggestion Plugin — multi-trigger detection + allowTrigger gating
|
|
341
|
+
│ ├── Enter/Submit Extension — configurable submit key (Enter/Mod+Enter/none)
|
|
342
|
+
│ ├── Mention Remove Detector — transaction-based onMentionRemove
|
|
301
343
|
│ └── Markdown Parser/Serializer — doc ↔ @[label](id) + extractFromMarkdown
|
|
302
|
-
└── SuggestionList (React) — headless popover with ARIA
|
|
344
|
+
└── SuggestionList (React) — headless popover with ARIA, groups, edge-aware positioning
|
|
303
345
|
```
|
|
304
346
|
|
|
305
|
-
|
|
347
|
+
## Testing
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
npm test # run all tests
|
|
351
|
+
npm run test:watch # watch mode
|
|
352
|
+
```
|
|
306
353
|
|
|
307
354
|
## Development
|
|
308
355
|
|
|
309
356
|
```bash
|
|
310
|
-
#
|
|
311
|
-
npm
|
|
312
|
-
|
|
313
|
-
# Build the library
|
|
314
|
-
npm run build
|
|
357
|
+
npm install # install dependencies
|
|
358
|
+
npm run build # build the library
|
|
359
|
+
npm test # run tests
|
|
315
360
|
|
|
316
|
-
#
|
|
317
|
-
cd demo && npm install && npx vite
|
|
361
|
+
cd demo && npm install && npx vite # run the demo app
|
|
318
362
|
```
|
|
319
363
|
|
|
320
|
-
The demo app registers mock providers for workspaces, contracts, and web search. It displays live structured output below the editor.
|
|
321
|
-
|
|
322
364
|
## License
|
|
323
365
|
|
|
324
366
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -25,6 +25,8 @@ type MentionItem = {
|
|
|
25
25
|
* item originates from a nested level.
|
|
26
26
|
*/
|
|
27
27
|
rootLabel?: string;
|
|
28
|
+
/** Optional group name for sectioned suggestion lists. Items with the same group are grouped together. */
|
|
29
|
+
group?: string;
|
|
28
30
|
};
|
|
29
31
|
/**
|
|
30
32
|
* Hierarchical suggestion source registered per trigger character.
|
|
@@ -48,6 +50,10 @@ type MentionProvider = {
|
|
|
48
50
|
* results from `searchAll` are shown instead of `getRootItems`.
|
|
49
51
|
*/
|
|
50
52
|
searchAll?: (query: string) => Promise<MentionItem[]>;
|
|
53
|
+
/** Debounce delay in ms before fetching suggestions. `0` or `undefined` means no debounce. */
|
|
54
|
+
debounceMs?: number;
|
|
55
|
+
/** Fetch recently used items, shown when the query is empty. Items are auto-grouped under "Recent". */
|
|
56
|
+
getRecentItems?: () => Promise<MentionItem[]>;
|
|
51
57
|
};
|
|
52
58
|
|
|
53
59
|
/**
|
|
@@ -112,6 +118,38 @@ type MentionsInputProps = {
|
|
|
112
118
|
renderItem?: (item: MentionItem, depth: number) => ReactNode;
|
|
113
119
|
/** Custom renderer for inline mention chips. */
|
|
114
120
|
renderChip?: (token: MentionToken) => ReactNode;
|
|
121
|
+
/** Custom renderer for empty suggestion state. Receives the current query. */
|
|
122
|
+
renderEmpty?: (query: string) => ReactNode;
|
|
123
|
+
/** Custom renderer for the suggestion loading indicator. */
|
|
124
|
+
renderLoading?: () => ReactNode;
|
|
125
|
+
/** Custom renderer for suggestion group section headers. */
|
|
126
|
+
renderGroupHeader?: (group: string) => ReactNode;
|
|
127
|
+
/** Called when the editor gains focus. */
|
|
128
|
+
onFocus?: () => void;
|
|
129
|
+
/** Called when the editor loses focus. */
|
|
130
|
+
onBlur?: () => void;
|
|
131
|
+
/** Called when a mention token is inserted. */
|
|
132
|
+
onMentionAdd?: (token: MentionToken) => void;
|
|
133
|
+
/** Called when a mention token is removed. */
|
|
134
|
+
onMentionRemove?: (token: MentionToken) => void;
|
|
135
|
+
/** Called when a mention chip is clicked. */
|
|
136
|
+
onMentionClick?: (token: MentionToken, event: MouseEvent) => void;
|
|
137
|
+
/** Return a ReactNode to show as a tooltip when hovering over a mention chip. */
|
|
138
|
+
onMentionHover?: (token: MentionToken) => ReactNode;
|
|
139
|
+
/** Minimum height of the editor in pixels. */
|
|
140
|
+
minHeight?: number;
|
|
141
|
+
/** Maximum height of the editor in pixels. Enables scrolling. */
|
|
142
|
+
maxHeight?: number;
|
|
143
|
+
/** Which key combo triggers `onSubmit`. Defaults to `"enter"`. */
|
|
144
|
+
submitKey?: "enter" | "mod+enter" | "none";
|
|
145
|
+
/** Conditionally suppress the suggestion dropdown. Return `false` to block. */
|
|
146
|
+
allowTrigger?: (trigger: string, context: {
|
|
147
|
+
textBefore: string;
|
|
148
|
+
}) => boolean;
|
|
149
|
+
/** Validate existing mentions. Return `false` to mark as invalid. */
|
|
150
|
+
validateMention?: (token: MentionToken) => boolean | Promise<boolean>;
|
|
151
|
+
/** DOM element to portal the suggestion dropdown into. */
|
|
152
|
+
portalContainer?: HTMLElement;
|
|
115
153
|
};
|
|
116
154
|
/**
|
|
117
155
|
* Imperative handle exposed via `ref` on `<MentionsInput>`.
|
|
@@ -123,6 +161,7 @@ type MentionsInputProps = {
|
|
|
123
161
|
* ref.current.clear();
|
|
124
162
|
* ref.current.setContent("Hello @[Marketing](ws_123)");
|
|
125
163
|
* ref.current.focus();
|
|
164
|
+
* ref.current.getOutput(); // { markdown, tokens, plainText }
|
|
126
165
|
* ```
|
|
127
166
|
*/
|
|
128
167
|
type MentionsInputHandle = {
|
|
@@ -132,6 +171,8 @@ type MentionsInputHandle = {
|
|
|
132
171
|
setContent: (markdown: string) => void;
|
|
133
172
|
/** Focus the editor. */
|
|
134
173
|
focus: () => void;
|
|
174
|
+
/** Read the current structured output without waiting for onChange. */
|
|
175
|
+
getOutput: () => MentionsOutput | null;
|
|
135
176
|
};
|
|
136
177
|
|
|
137
178
|
/**
|
|
@@ -140,14 +181,6 @@ type MentionsInputHandle = {
|
|
|
140
181
|
* A structured text editor with typed entity tokens.
|
|
141
182
|
* Consumers register `providers` for each trigger character,
|
|
142
183
|
* and receive structured output via `onChange` and `onSubmit`.
|
|
143
|
-
*
|
|
144
|
-
* Supports an imperative ref handle for programmatic control:
|
|
145
|
-
* ```tsx
|
|
146
|
-
* const ref = useRef<MentionsInputHandle>(null);
|
|
147
|
-
* ref.current.clear();
|
|
148
|
-
* ref.current.setContent("@[Marketing](ws_123) summarize");
|
|
149
|
-
* ref.current.focus();
|
|
150
|
-
* ```
|
|
151
184
|
*/
|
|
152
185
|
declare const MentionsInput: React.ForwardRefExoticComponent<MentionsInputProps & React.RefAttributes<MentionsInputHandle>>;
|
|
153
186
|
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ type MentionItem = {
|
|
|
25
25
|
* item originates from a nested level.
|
|
26
26
|
*/
|
|
27
27
|
rootLabel?: string;
|
|
28
|
+
/** Optional group name for sectioned suggestion lists. Items with the same group are grouped together. */
|
|
29
|
+
group?: string;
|
|
28
30
|
};
|
|
29
31
|
/**
|
|
30
32
|
* Hierarchical suggestion source registered per trigger character.
|
|
@@ -48,6 +50,10 @@ type MentionProvider = {
|
|
|
48
50
|
* results from `searchAll` are shown instead of `getRootItems`.
|
|
49
51
|
*/
|
|
50
52
|
searchAll?: (query: string) => Promise<MentionItem[]>;
|
|
53
|
+
/** Debounce delay in ms before fetching suggestions. `0` or `undefined` means no debounce. */
|
|
54
|
+
debounceMs?: number;
|
|
55
|
+
/** Fetch recently used items, shown when the query is empty. Items are auto-grouped under "Recent". */
|
|
56
|
+
getRecentItems?: () => Promise<MentionItem[]>;
|
|
51
57
|
};
|
|
52
58
|
|
|
53
59
|
/**
|
|
@@ -112,6 +118,38 @@ type MentionsInputProps = {
|
|
|
112
118
|
renderItem?: (item: MentionItem, depth: number) => ReactNode;
|
|
113
119
|
/** Custom renderer for inline mention chips. */
|
|
114
120
|
renderChip?: (token: MentionToken) => ReactNode;
|
|
121
|
+
/** Custom renderer for empty suggestion state. Receives the current query. */
|
|
122
|
+
renderEmpty?: (query: string) => ReactNode;
|
|
123
|
+
/** Custom renderer for the suggestion loading indicator. */
|
|
124
|
+
renderLoading?: () => ReactNode;
|
|
125
|
+
/** Custom renderer for suggestion group section headers. */
|
|
126
|
+
renderGroupHeader?: (group: string) => ReactNode;
|
|
127
|
+
/** Called when the editor gains focus. */
|
|
128
|
+
onFocus?: () => void;
|
|
129
|
+
/** Called when the editor loses focus. */
|
|
130
|
+
onBlur?: () => void;
|
|
131
|
+
/** Called when a mention token is inserted. */
|
|
132
|
+
onMentionAdd?: (token: MentionToken) => void;
|
|
133
|
+
/** Called when a mention token is removed. */
|
|
134
|
+
onMentionRemove?: (token: MentionToken) => void;
|
|
135
|
+
/** Called when a mention chip is clicked. */
|
|
136
|
+
onMentionClick?: (token: MentionToken, event: MouseEvent) => void;
|
|
137
|
+
/** Return a ReactNode to show as a tooltip when hovering over a mention chip. */
|
|
138
|
+
onMentionHover?: (token: MentionToken) => ReactNode;
|
|
139
|
+
/** Minimum height of the editor in pixels. */
|
|
140
|
+
minHeight?: number;
|
|
141
|
+
/** Maximum height of the editor in pixels. Enables scrolling. */
|
|
142
|
+
maxHeight?: number;
|
|
143
|
+
/** Which key combo triggers `onSubmit`. Defaults to `"enter"`. */
|
|
144
|
+
submitKey?: "enter" | "mod+enter" | "none";
|
|
145
|
+
/** Conditionally suppress the suggestion dropdown. Return `false` to block. */
|
|
146
|
+
allowTrigger?: (trigger: string, context: {
|
|
147
|
+
textBefore: string;
|
|
148
|
+
}) => boolean;
|
|
149
|
+
/** Validate existing mentions. Return `false` to mark as invalid. */
|
|
150
|
+
validateMention?: (token: MentionToken) => boolean | Promise<boolean>;
|
|
151
|
+
/** DOM element to portal the suggestion dropdown into. */
|
|
152
|
+
portalContainer?: HTMLElement;
|
|
115
153
|
};
|
|
116
154
|
/**
|
|
117
155
|
* Imperative handle exposed via `ref` on `<MentionsInput>`.
|
|
@@ -123,6 +161,7 @@ type MentionsInputProps = {
|
|
|
123
161
|
* ref.current.clear();
|
|
124
162
|
* ref.current.setContent("Hello @[Marketing](ws_123)");
|
|
125
163
|
* ref.current.focus();
|
|
164
|
+
* ref.current.getOutput(); // { markdown, tokens, plainText }
|
|
126
165
|
* ```
|
|
127
166
|
*/
|
|
128
167
|
type MentionsInputHandle = {
|
|
@@ -132,6 +171,8 @@ type MentionsInputHandle = {
|
|
|
132
171
|
setContent: (markdown: string) => void;
|
|
133
172
|
/** Focus the editor. */
|
|
134
173
|
focus: () => void;
|
|
174
|
+
/** Read the current structured output without waiting for onChange. */
|
|
175
|
+
getOutput: () => MentionsOutput | null;
|
|
135
176
|
};
|
|
136
177
|
|
|
137
178
|
/**
|
|
@@ -140,14 +181,6 @@ type MentionsInputHandle = {
|
|
|
140
181
|
* A structured text editor with typed entity tokens.
|
|
141
182
|
* Consumers register `providers` for each trigger character,
|
|
142
183
|
* and receive structured output via `onChange` and `onSubmit`.
|
|
143
|
-
*
|
|
144
|
-
* Supports an imperative ref handle for programmatic control:
|
|
145
|
-
* ```tsx
|
|
146
|
-
* const ref = useRef<MentionsInputHandle>(null);
|
|
147
|
-
* ref.current.clear();
|
|
148
|
-
* ref.current.setContent("@[Marketing](ws_123) summarize");
|
|
149
|
-
* ref.current.focus();
|
|
150
|
-
* ```
|
|
151
184
|
*/
|
|
152
185
|
declare const MentionsInput: React.ForwardRefExoticComponent<MentionsInputProps & React.RefAttributes<MentionsInputHandle>>;
|
|
153
186
|
|