@portabletext/plugin-typeahead-picker 6.0.44 → 6.0.46

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/index.d.ts CHANGED
@@ -1,45 +1,5 @@
1
- import type {BehaviorActionSet} from '@portabletext/editor/behaviors'
2
- import type {BehaviorGuard} from '@portabletext/editor/behaviors'
3
- import type {EditorSelection} from '@portabletext/editor'
4
-
5
- declare type AsyncConfigWithDelimiter<TMatch extends AutoCompleteMatch> =
6
- BaseConfigWithDelimiter<TMatch> & {
7
- /**
8
- * Set to `'async'` when `getMatches` returns a Promise.
9
- */
10
- mode: 'async'
11
- /**
12
- * Debounce delay in milliseconds before calling `getMatches`.
13
- * Recommended for API calls to reduce request frequency.
14
- * @defaultValue `0` (no debounce)
15
- */
16
- debounceMs?: number
17
- /**
18
- * Async function that returns matches for the current keyword.
19
- * Called whenever the keyword changes (after debounce if configured).
20
- */
21
- getMatches: (context: {keyword: string}) => Promise<ReadonlyArray<TMatch>>
22
- }
23
-
24
- declare type AsyncConfigWithoutDelimiter<TMatch extends object> =
25
- BaseConfigWithoutDelimiter<TMatch> & {
26
- /**
27
- * Set to `'async'` when `getMatches` returns a Promise.
28
- */
29
- mode: 'async'
30
- /**
31
- * Debounce delay in milliseconds before calling `getMatches`.
32
- * Recommended for API calls to reduce request frequency.
33
- * @defaultValue `0` (no debounce)
34
- */
35
- debounceMs?: number
36
- /**
37
- * Async function that returns matches for the current keyword.
38
- * Called whenever the keyword changes (after debounce if configured).
39
- */
40
- getMatches: (context: {keyword: string}) => Promise<ReadonlyArray<TMatch>>
41
- }
42
-
1
+ import { EditorSelection } from "@portabletext/editor";
2
+ import { BehaviorActionSet, BehaviorGuard } from "@portabletext/editor/behaviors";
43
3
  /**
44
4
  * Match type for pickers with auto-completion support.
45
5
  * Use this when `delimiter` is configured.
@@ -63,160 +23,9 @@ declare type AsyncConfigWithoutDelimiter<TMatch extends object> =
63
23
  *
64
24
  * @public
65
25
  */
66
- export declare type AutoCompleteMatch = {
67
- type: 'exact' | 'partial'
68
- }
69
-
70
- declare type BaseConfigWithDelimiter<TMatch extends AutoCompleteMatch> = {
71
- /**
72
- * Pattern that activates the picker. Must be a single character.
73
- * Can include `^` for start-of-block triggers.
74
- *
75
- * @example `/:/` - activates on colon
76
- * @example `/@/` - activates on at-sign
77
- * @example `/^\//` - activates on slash at start of block
78
- */
79
- trigger: RegExp
80
- /**
81
- * Pattern matching the keyword portion after the trigger.
82
- * The entire match becomes the keyword passed to `getMatches`.
83
- * Common patterns: non-whitespace (`\S*`) or word characters (`\w*`).
84
- */
85
- keyword: RegExp
86
- /**
87
- * Character that triggers auto-completion.
88
- * Typing this after a keyword with an exact match auto-inserts it.
89
- *
90
- * @example `':'` - typing `:joy:` auto-inserts the joy emoji
91
- */
92
- delimiter: string
93
- /**
94
- * Guard function that runs at trigger time to conditionally prevent activation.
95
- * Return `false` to block activation, or `true` to allow it.
96
- */
97
- guard?: TypeaheadTriggerGuard
98
- /**
99
- * Called when a match is selected.
100
- * Returns behavior actions to execute (e.g., delete trigger text, insert content).
101
- */
102
- onSelect: TypeaheadSelectActionSet<TMatch>[]
103
- /**
104
- * Called when the picker is dismissed.
105
- * Returns behavior actions to execute (optional cleanup).
106
- */
107
- onDismiss?: TypeaheadDismissActionSet[]
108
- }
109
-
110
- declare type BaseConfigWithoutDelimiter<TMatch extends object> = {
111
- /**
112
- * Pattern that activates the picker. Must be a single character.
113
- * Can include `^` for start-of-block triggers.
114
- *
115
- * @example `/:/` - activates on colon
116
- * @example `/@/` - activates on at-sign
117
- * @example `/^\//` - activates on slash at start of block
118
- */
119
- trigger: RegExp
120
- /**
121
- * Pattern matching the keyword portion after the trigger.
122
- * The entire match becomes the keyword passed to `getMatches`.
123
- * Common patterns: non-whitespace (`\S*`) or word characters (`\w*`).
124
- */
125
- keyword: RegExp
126
- delimiter?: undefined
127
- /**
128
- * Guard function that runs at trigger time to conditionally prevent activation.
129
- * Return `false` to block activation, or `true` to allow it.
130
- */
131
- guard?: TypeaheadTriggerGuard
132
- /**
133
- * Called when a match is selected.
134
- * Returns behavior actions to execute (e.g., delete trigger text, insert content).
135
- */
136
- onSelect: TypeaheadSelectActionSet<TMatch>[]
137
- /**
138
- * Called when the picker is dismissed.
139
- * Returns behavior actions to execute (optional cleanup).
140
- */
141
- onDismiss?: TypeaheadDismissActionSet[]
142
- }
143
-
144
- /**
145
- * Creates a typeahead picker definition to use with {@link useTypeaheadPicker}.
146
- *
147
- * @example Emoji picker with auto-complete
148
- * ```ts
149
- * const emojiPicker = defineTypeaheadPicker({
150
- * trigger: /:/,
151
- * keyword: /[\S]+/,
152
- * delimiter: ':',
153
- * getMatches: ({keyword}) => searchEmojis(keyword),
154
- * onSelect: [
155
- * ({event}) => [
156
- * raise({type: 'delete', at: event.patternSelection}),
157
- * raise({type: 'insert.text', text: event.match.emoji}),
158
- * ],
159
- * ],
160
- * })
161
- * ```
162
- *
163
- * @example Async mention picker
164
- * ```ts
165
- * const mentionPicker = defineTypeaheadPicker({
166
- * mode: 'async',
167
- * trigger: /@/,
168
- * keyword: /[\w]+/,
169
- * debounceMs: 200,
170
- * getMatches: async ({keyword}) => api.searchUsers(keyword),
171
- * onSelect: [
172
- * ({event}) => [
173
- * raise({type: 'delete', at: event.patternSelection}),
174
- * raise({type: 'insert.inline object', inlineObject: {_type: 'mention', userId: event.match.id}}),
175
- * ],
176
- * ],
177
- * })
178
- * ```
179
- *
180
- * @example Picker with guard (runs at trigger time)
181
- * ```ts
182
- * const emojiPicker = defineTypeaheadPicker({
183
- * trigger: /:/,
184
- * keyword: /[\S]+/,
185
- * getMatches: ({keyword}) => searchEmojis(keyword),
186
- * guard: ({snapshot, event, dom}) => {
187
- * if (anotherPickerIsOpen()) return false
188
- * return true
189
- * },
190
- * onSelect: [
191
- * ({event}) => [
192
- * raise({type: 'delete', at: event.patternSelection}),
193
- * raise({type: 'insert.text', text: event.match.emoji}),
194
- * ],
195
- * ],
196
- * })
197
- * ```
198
- *
199
- * @public
200
- */
201
- export declare function defineTypeaheadPicker<TMatch extends object>(
202
- config: SyncConfigWithoutDelimiter<TMatch>,
203
- ): TypeaheadPickerDefinition<TMatch>
204
-
205
- /** @public */
206
- export declare function defineTypeaheadPicker<TMatch extends AutoCompleteMatch>(
207
- config: SyncConfigWithDelimiter<TMatch>,
208
- ): TypeaheadPickerDefinition<TMatch>
209
-
210
- /** @public */
211
- export declare function defineTypeaheadPicker<TMatch extends object>(
212
- config: AsyncConfigWithoutDelimiter<TMatch>,
213
- ): TypeaheadPickerDefinition<TMatch>
214
-
215
- /** @public */
216
- export declare function defineTypeaheadPicker<TMatch extends AutoCompleteMatch>(
217
- config: AsyncConfigWithDelimiter<TMatch>,
218
- ): TypeaheadPickerDefinition<TMatch>
219
-
26
+ type AutoCompleteMatch = {
27
+ type: 'exact' | 'partial';
28
+ };
220
29
  /**
221
30
  * Function that retrieves matches for a given keyword.
222
31
  *
@@ -240,192 +49,76 @@ export declare function defineTypeaheadPicker<TMatch extends AutoCompleteMatch>(
240
49
  *
241
50
  * @public
242
51
  */
243
- export declare type GetMatches<TMatch extends object> = (context: {
244
- keyword: string
245
- }) => ReadonlyArray<TMatch> | Promise<ReadonlyArray<TMatch>>
246
-
247
- declare type SyncConfigWithDelimiter<TMatch extends AutoCompleteMatch> =
248
- BaseConfigWithDelimiter<TMatch> & {
249
- /**
250
- * Whether `getMatches` returns synchronously or asynchronously.
251
- * @defaultValue `'sync'`
252
- */
253
- mode?: 'sync'
254
- /**
255
- * Debounce delay in milliseconds before calling `getMatches`.
256
- * Useful for expensive local searches.
257
- * @defaultValue `0` (no debounce)
258
- */
259
- debounceMs?: number
260
- /**
261
- * Function that returns matches for the current keyword.
262
- * Called whenever the keyword changes (after debounce if configured).
263
- */
264
- getMatches: (context: {keyword: string}) => ReadonlyArray<TMatch>
265
- }
266
-
267
- declare type SyncConfigWithoutDelimiter<TMatch extends object> =
268
- BaseConfigWithoutDelimiter<TMatch> & {
269
- /**
270
- * Whether `getMatches` returns synchronously or asynchronously.
271
- * @defaultValue `'sync'`
272
- */
273
- mode?: 'sync'
274
- /**
275
- * Debounce delay in milliseconds before calling `getMatches`.
276
- * Useful for expensive local searches.
277
- * @defaultValue `0` (no debounce)
278
- */
279
- debounceMs?: number
280
- /**
281
- * Function that returns matches for the current keyword.
282
- * Called whenever the keyword changes (after debounce if configured).
283
- */
284
- getMatches: (context: {keyword: string}) => ReadonlyArray<TMatch>
285
- }
286
-
52
+ type GetMatches<TMatch extends object> = (context: {
53
+ keyword: string;
54
+ }) => ReadonlyArray<TMatch> | Promise<ReadonlyArray<TMatch>>;
287
55
  /**
288
- * Action set that runs when the picker is dismissed.
289
- * Returns an array of behavior actions to execute (optional cleanup).
56
+ * Event passed to `onSelect` when a match is selected.
290
57
  *
291
58
  * @public
292
59
  */
293
- export declare type TypeaheadDismissActionSet = BehaviorActionSet<
294
- TypeaheadDismissEvent,
295
- true
296
- >
297
-
60
+ type TypeaheadSelectEvent<TMatch> = {
61
+ type: 'custom.typeahead select';
62
+ /** The match that was selected */
63
+ match: TMatch;
64
+ /** The extracted keyword (e.g., `joy` from `:joy`) */
65
+ keyword: string;
66
+ /** Selection range covering the full pattern match (e.g., `:joy`) for replacement */
67
+ patternSelection: NonNullable<EditorSelection>;
68
+ };
298
69
  /**
299
70
  * Event passed to `onDismiss` when the picker is dismissed.
300
71
  *
301
72
  * @public
302
73
  */
303
- export declare type TypeaheadDismissEvent = {
304
- type: 'custom.typeahead dismiss'
74
+ type TypeaheadDismissEvent = {
75
+ type: 'custom.typeahead dismiss';
305
76
  /** Selection range covering the full pattern match (e.g., `@john`) for cleanup */
306
- patternSelection: NonNullable<EditorSelection>
307
- }
308
-
77
+ patternSelection: NonNullable<EditorSelection>;
78
+ };
309
79
  /**
310
- * The picker instance returned by {@link useTypeaheadPicker}.
311
- *
312
- * Provides a state machine-like interface for building picker UI:
313
- * - Use `snapshot.matches()` to check the current state and render accordingly
314
- * - Use `snapshot.context` to access the keyword, matches, and selected index
315
- * - Use `send()` to dispatch events like select, dismiss, or navigate
316
- *
317
- * @example
318
- * ```tsx
319
- * function EmojiPicker() {
320
- * const picker = useTypeaheadPicker(emojiPickerDefinition)
321
- *
322
- * if (picker.snapshot.matches('idle')) return null
323
- * if (picker.snapshot.matches({active: 'loading'})) return <Spinner />
324
- * if (picker.snapshot.matches({active: 'no matches'})) return <NoResults />
325
- *
326
- * const {matches, selectedIndex} = picker.snapshot.context
80
+ * Action set that runs when a match is selected.
81
+ * Returns an array of behavior actions to execute (e.g., delete trigger text, insert content).
327
82
  *
328
- * return (
329
- * <ul>
330
- * {matches.map((match, i) => (
331
- * <li
332
- * key={match.key}
333
- * aria-selected={i === selectedIndex}
334
- * onMouseEnter={() => picker.send({type: 'navigate to', index: i})}
335
- * onClick={() => picker.send({type: 'select'})}
336
- * >
337
- * {match.emoji} {match.shortcode}
338
- * </li>
339
- * ))}
340
- * </ul>
341
- * )
342
- * }
343
- * ```
83
+ * @public
84
+ */
85
+ type TypeaheadSelectActionSet<TMatch> = BehaviorActionSet<TypeaheadSelectEvent<TMatch>, true>;
86
+ /**
87
+ * Action set that runs when the picker is dismissed.
88
+ * Returns an array of behavior actions to execute (optional cleanup).
344
89
  *
345
90
  * @public
346
91
  */
347
- export declare type TypeaheadPicker<TMatch extends object> = {
348
- snapshot: {
349
- /**
350
- * Check if the picker is in a specific state.
351
- * @see {@link TypeaheadPickerState} for available states
352
- */
353
- matches: (state: TypeaheadPickerState) => boolean
354
- /**
355
- * Current picker data including keyword, matches, and selection.
356
- */
357
- context: TypeaheadPickerContext<TMatch>
358
- }
359
- /**
360
- * Dispatch an event to the picker.
361
- * @see {@link TypeaheadPickerEvent} for available events
362
- */
363
- send: (event: TypeaheadPickerEvent) => void
364
- }
365
-
92
+ type TypeaheadDismissActionSet = BehaviorActionSet<TypeaheadDismissEvent, true>;
366
93
  /**
367
- * Current picker data, accessible via `snapshot.context`.
94
+ * Event passed to the trigger guard when the picker is about to activate.
368
95
  *
369
96
  * @public
370
97
  */
371
- export declare type TypeaheadPickerContext<TMatch> = {
372
- /** The extracted keyword from the trigger pattern (e.g., `joy` from `:joy`) */
373
- keyword: string
374
- /** The current list of matches returned by `getMatches` */
375
- matches: ReadonlyArray<TMatch>
376
- /** Index of the currently selected match (for keyboard navigation and highlighting) */
377
- selectedIndex: number
378
- /** Error from `getMatches` if it threw, otherwise `undefined` */
379
- error: Error | undefined
380
- }
381
-
98
+ type TypeaheadTriggerEvent = {
99
+ type: 'custom.typeahead trigger found';
100
+ };
382
101
  /**
383
- * Configuration object that defines a typeahead picker's behavior.
102
+ * Guard function that runs at trigger time to conditionally prevent the picker
103
+ * from activating. Has the same signature as a behavior guard.
384
104
  *
385
- * Create using {@link (defineTypeaheadPicker:1)} and pass to {@link useTypeaheadPicker}.
105
+ * Return `false` to block activation, or `true` to allow it.
386
106
  *
387
107
  * @example
388
108
  * ```ts
389
- * const emojiPicker = defineTypeaheadPicker({
390
- * trigger: /:/,
391
- * keyword: /[\S]+/,
392
- * delimiter: ':',
393
- * getMatches: ({keyword}) => searchEmojis(keyword),
394
- * onSelect: [
395
- * ({event}) => [
396
- * raise({type: 'delete', at: event.patternSelection}),
397
- * raise({type: 'insert.text', text: event.match.emoji}),
398
- * ],
399
- * ],
400
- * })
109
+ * guard: ({snapshot, event, dom}) => {
110
+ * // Block activation if another picker is open
111
+ * if (anotherPickerIsOpen()) return false
112
+ *
113
+ * // Allow activation
114
+ * return true
115
+ * }
401
116
  * ```
402
117
  *
403
118
  * @public
404
119
  */
405
- export declare type TypeaheadPickerDefinition<TMatch extends object = object> =
406
- TypeaheadPickerDefinitionBase<TMatch> & {
407
- /** @internal Unique identifier for this picker definition */
408
- readonly _id: string
409
- /**
410
- * Whether `getMatches` returns synchronously or asynchronously.
411
- * @defaultValue `'sync'`
412
- */
413
- mode?: 'sync' | 'async'
414
- /**
415
- * Debounce delay in milliseconds before calling `getMatches`.
416
- * Useful for both async (API calls) and sync (expensive local search) modes.
417
- * @defaultValue `0` (no debounce)
418
- * @example `debounceMs: 200` - wait 200ms after last keystroke
419
- */
420
- debounceMs?: number
421
- /**
422
- * Function that retrieves matches for the current keyword.
423
- * Use the `debounceMs` option to reduce calls during rapid typing.
424
- */
425
- getMatches: GetMatches<TMatch>
426
- }
427
-
428
- declare type TypeaheadPickerDefinitionBase<TMatch extends object> = {
120
+ type TypeaheadTriggerGuard = BehaviorGuard<TypeaheadTriggerEvent, true>;
121
+ type TypeaheadPickerDefinitionBase<TMatch extends object> = {
429
122
  /**
430
123
  * Pattern that activates the picker.
431
124
  * Can include positional anchors like `^` for start-of-block triggers.
@@ -434,7 +127,7 @@ declare type TypeaheadPickerDefinitionBase<TMatch extends object> = {
434
127
  * @example `/@/` for mentions
435
128
  * @example `/^\//` for slash commands (start of block only)
436
129
  */
437
- trigger: RegExp
130
+ trigger: RegExp;
438
131
  /**
439
132
  * Pattern matching the keyword portion (after trigger, before delimiter).
440
133
  * The entire match is used as the keyword.
@@ -442,14 +135,14 @@ declare type TypeaheadPickerDefinitionBase<TMatch extends object> = {
442
135
  * @example `/[\S]+/` for emoji (any non-whitespace)
443
136
  * @example `/[\w]+/` for mentions (word characters only)
444
137
  */
445
- keyword: RegExp
138
+ keyword: RegExp;
446
139
  /**
447
140
  * Character that triggers auto-completion.
448
141
  * Typing this after a keyword with exactly one exact match auto-inserts it.
449
142
  *
450
143
  * @example `':'` - typing `:joy:` auto-inserts the joy emoji
451
144
  */
452
- delimiter?: string
145
+ delimiter?: string;
453
146
  /**
454
147
  * Guard function that runs at trigger time to conditionally prevent the picker
455
148
  * from activating.
@@ -457,7 +150,7 @@ declare type TypeaheadPickerDefinitionBase<TMatch extends object> = {
457
150
  *
458
151
  * @see {@link TypeaheadTriggerGuard}
459
152
  */
460
- guard?: TypeaheadTriggerGuard
153
+ guard?: TypeaheadTriggerGuard;
461
154
  /**
462
155
  * Called when a match is selected.
463
156
  * Returns behavior actions to execute (e.g., delete trigger text, insert content).
@@ -472,14 +165,57 @@ declare type TypeaheadPickerDefinitionBase<TMatch extends object> = {
472
165
  * ]
473
166
  * ```
474
167
  */
475
- onSelect: TypeaheadSelectActionSet<TMatch>[]
168
+ onSelect: TypeaheadSelectActionSet<TMatch>[];
476
169
  /**
477
170
  * Called when the picker is dismissed (Escape, cursor movement, etc.).
478
171
  * Returns behavior actions to execute (optional cleanup).
479
172
  */
480
- onDismiss?: TypeaheadDismissActionSet[]
481
- }
482
-
173
+ onDismiss?: TypeaheadDismissActionSet[];
174
+ };
175
+ /**
176
+ * Configuration object that defines a typeahead picker's behavior.
177
+ *
178
+ * Create using {@link (defineTypeaheadPicker:1)} and pass to {@link useTypeaheadPicker}.
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * const emojiPicker = defineTypeaheadPicker({
183
+ * trigger: /:/,
184
+ * keyword: /[\S]+/,
185
+ * delimiter: ':',
186
+ * getMatches: ({keyword}) => searchEmojis(keyword),
187
+ * onSelect: [
188
+ * ({event}) => [
189
+ * raise({type: 'delete', at: event.patternSelection}),
190
+ * raise({type: 'insert.text', text: event.match.emoji}),
191
+ * ],
192
+ * ],
193
+ * })
194
+ * ```
195
+ *
196
+ * @public
197
+ */
198
+ type TypeaheadPickerDefinition<TMatch extends object = object> = TypeaheadPickerDefinitionBase<TMatch> & {
199
+ /** @internal Unique identifier for this picker definition */
200
+ readonly _id: string;
201
+ /**
202
+ * Whether `getMatches` returns synchronously or asynchronously.
203
+ * @defaultValue `'sync'`
204
+ */
205
+ mode?: 'sync' | 'async';
206
+ /**
207
+ * Debounce delay in milliseconds before calling `getMatches`.
208
+ * Useful for both async (API calls) and sync (expensive local search) modes.
209
+ * @defaultValue `0` (no debounce)
210
+ * @example `debounceMs: 200` - wait 200ms after last keystroke
211
+ */
212
+ debounceMs?: number;
213
+ /**
214
+ * Function that retrieves matches for the current keyword.
215
+ * Use the `debounceMs` option to reduce calls during rapid typing.
216
+ */
217
+ getMatches: GetMatches<TMatch>;
218
+ };
483
219
  /**
484
220
  * Events that can be sent to the picker via `send()`.
485
221
  *
@@ -489,18 +225,14 @@ declare type TypeaheadPickerDefinitionBase<TMatch extends object> = {
489
225
  *
490
226
  * @public
491
227
  */
492
- export declare type TypeaheadPickerEvent =
493
- | {
494
- type: 'select'
495
- }
496
- | {
497
- type: 'dismiss'
498
- }
499
- | {
500
- type: 'navigate to'
501
- index: number
502
- }
503
-
228
+ type TypeaheadPickerEvent = {
229
+ type: 'select';
230
+ } | {
231
+ type: 'dismiss';
232
+ } | {
233
+ type: 'navigate to';
234
+ index: number;
235
+ };
504
236
  /**
505
237
  * Possible states for the picker, used with `snapshot.matches()`.
506
238
  *
@@ -529,88 +261,306 @@ export declare type TypeaheadPickerEvent =
529
261
  *
530
262
  * @public
531
263
  */
532
- export declare type TypeaheadPickerState =
533
- | 'idle'
534
- | 'active'
535
- | {
536
- active: 'loading'
537
- }
538
- | {
539
- active: 'no matches'
540
- }
541
- | {
542
- active: {
543
- 'no matches': 'loading'
544
- }
545
- }
546
- | {
547
- active: 'showing matches'
548
- }
549
- | {
550
- active: {
551
- 'showing matches': 'loading'
552
- }
553
- }
554
-
264
+ type TypeaheadPickerState = 'idle' | 'active' | {
265
+ active: 'loading';
266
+ } | {
267
+ active: 'no matches';
268
+ } | {
269
+ active: {
270
+ 'no matches': 'loading';
271
+ };
272
+ } | {
273
+ active: 'showing matches';
274
+ } | {
275
+ active: {
276
+ 'showing matches': 'loading';
277
+ };
278
+ };
555
279
  /**
556
- * Action set that runs when a match is selected.
557
- * Returns an array of behavior actions to execute (e.g., delete trigger text, insert content).
280
+ * Current picker data, accessible via `snapshot.context`.
558
281
  *
559
282
  * @public
560
283
  */
561
- export declare type TypeaheadSelectActionSet<TMatch> = BehaviorActionSet<
562
- TypeaheadSelectEvent<TMatch>,
563
- true
564
- >
565
-
284
+ type TypeaheadPickerContext<TMatch> = {
285
+ /** The extracted keyword from the trigger pattern (e.g., `joy` from `:joy`) */
286
+ keyword: string;
287
+ /** The current list of matches returned by `getMatches` */
288
+ matches: ReadonlyArray<TMatch>;
289
+ /** Index of the currently selected match (for keyboard navigation and highlighting) */
290
+ selectedIndex: number;
291
+ /** Error from `getMatches` if it threw, otherwise `undefined` */
292
+ error: Error | undefined;
293
+ };
566
294
  /**
567
- * Event passed to `onSelect` when a match is selected.
295
+ * The picker instance returned by {@link useTypeaheadPicker}.
568
296
  *
569
- * @public
570
- */
571
- export declare type TypeaheadSelectEvent<TMatch> = {
572
- type: 'custom.typeahead select'
573
- /** The match that was selected */
574
- match: TMatch
575
- /** The extracted keyword (e.g., `joy` from `:joy`) */
576
- keyword: string
577
- /** Selection range covering the full pattern match (e.g., `:joy`) for replacement */
578
- patternSelection: NonNullable<EditorSelection>
579
- }
580
-
581
- /**
582
- * Event passed to the trigger guard when the picker is about to activate.
297
+ * Provides a state machine-like interface for building picker UI:
298
+ * - Use `snapshot.matches()` to check the current state and render accordingly
299
+ * - Use `snapshot.context` to access the keyword, matches, and selected index
300
+ * - Use `send()` to dispatch events like select, dismiss, or navigate
301
+ *
302
+ * @example
303
+ * ```tsx
304
+ * function EmojiPicker() {
305
+ * const picker = useTypeaheadPicker(emojiPickerDefinition)
306
+ *
307
+ * if (picker.snapshot.matches('idle')) return null
308
+ * if (picker.snapshot.matches({active: 'loading'})) return <Spinner />
309
+ * if (picker.snapshot.matches({active: 'no matches'})) return <NoResults />
310
+ *
311
+ * const {matches, selectedIndex} = picker.snapshot.context
312
+ *
313
+ * return (
314
+ * <ul>
315
+ * {matches.map((match, i) => (
316
+ * <li
317
+ * key={match.key}
318
+ * aria-selected={i === selectedIndex}
319
+ * onMouseEnter={() => picker.send({type: 'navigate to', index: i})}
320
+ * onClick={() => picker.send({type: 'select'})}
321
+ * >
322
+ * {match.emoji} {match.shortcode}
323
+ * </li>
324
+ * ))}
325
+ * </ul>
326
+ * )
327
+ * }
328
+ * ```
583
329
  *
584
330
  * @public
585
331
  */
586
- export declare type TypeaheadTriggerEvent = {
587
- type: 'custom.typeahead trigger found'
588
- }
589
-
332
+ type TypeaheadPicker<TMatch extends object> = {
333
+ snapshot: {
334
+ /**
335
+ * Check if the picker is in a specific state.
336
+ * @see {@link TypeaheadPickerState} for available states
337
+ */
338
+ matches: (state: TypeaheadPickerState) => boolean;
339
+ /**
340
+ * Current picker data including keyword, matches, and selection.
341
+ */
342
+ context: TypeaheadPickerContext<TMatch>;
343
+ };
344
+ /**
345
+ * Dispatch an event to the picker.
346
+ * @see {@link TypeaheadPickerEvent} for available events
347
+ */
348
+ send: (event: TypeaheadPickerEvent) => void;
349
+ };
350
+ type BaseConfigWithoutDelimiter<TMatch extends object> = {
351
+ /**
352
+ * Pattern that activates the picker. Must be a single character.
353
+ * Can include `^` for start-of-block triggers.
354
+ *
355
+ * @example `/:/` - activates on colon
356
+ * @example `/@/` - activates on at-sign
357
+ * @example `/^\//` - activates on slash at start of block
358
+ */
359
+ trigger: RegExp;
360
+ /**
361
+ * Pattern matching the keyword portion after the trigger.
362
+ * The entire match becomes the keyword passed to `getMatches`.
363
+ * Common patterns: non-whitespace (`\S*`) or word characters (`\w*`).
364
+ */
365
+ keyword: RegExp;
366
+ delimiter?: undefined;
367
+ /**
368
+ * Guard function that runs at trigger time to conditionally prevent activation.
369
+ * Return `false` to block activation, or `true` to allow it.
370
+ */
371
+ guard?: TypeaheadTriggerGuard;
372
+ /**
373
+ * Called when a match is selected.
374
+ * Returns behavior actions to execute (e.g., delete trigger text, insert content).
375
+ */
376
+ onSelect: TypeaheadSelectActionSet<TMatch>[];
377
+ /**
378
+ * Called when the picker is dismissed.
379
+ * Returns behavior actions to execute (optional cleanup).
380
+ */
381
+ onDismiss?: TypeaheadDismissActionSet[];
382
+ };
383
+ type BaseConfigWithDelimiter<TMatch extends AutoCompleteMatch> = {
384
+ /**
385
+ * Pattern that activates the picker. Must be a single character.
386
+ * Can include `^` for start-of-block triggers.
387
+ *
388
+ * @example `/:/` - activates on colon
389
+ * @example `/@/` - activates on at-sign
390
+ * @example `/^\//` - activates on slash at start of block
391
+ */
392
+ trigger: RegExp;
393
+ /**
394
+ * Pattern matching the keyword portion after the trigger.
395
+ * The entire match becomes the keyword passed to `getMatches`.
396
+ * Common patterns: non-whitespace (`\S*`) or word characters (`\w*`).
397
+ */
398
+ keyword: RegExp;
399
+ /**
400
+ * Character that triggers auto-completion.
401
+ * Typing this after a keyword with an exact match auto-inserts it.
402
+ *
403
+ * @example `':'` - typing `:joy:` auto-inserts the joy emoji
404
+ */
405
+ delimiter: string;
406
+ /**
407
+ * Guard function that runs at trigger time to conditionally prevent activation.
408
+ * Return `false` to block activation, or `true` to allow it.
409
+ */
410
+ guard?: TypeaheadTriggerGuard;
411
+ /**
412
+ * Called when a match is selected.
413
+ * Returns behavior actions to execute (e.g., delete trigger text, insert content).
414
+ */
415
+ onSelect: TypeaheadSelectActionSet<TMatch>[];
416
+ /**
417
+ * Called when the picker is dismissed.
418
+ * Returns behavior actions to execute (optional cleanup).
419
+ */
420
+ onDismiss?: TypeaheadDismissActionSet[];
421
+ };
422
+ type SyncConfigWithoutDelimiter<TMatch extends object> = BaseConfigWithoutDelimiter<TMatch> & {
423
+ /**
424
+ * Whether `getMatches` returns synchronously or asynchronously.
425
+ * @defaultValue `'sync'`
426
+ */
427
+ mode?: 'sync';
428
+ /**
429
+ * Debounce delay in milliseconds before calling `getMatches`.
430
+ * Useful for expensive local searches.
431
+ * @defaultValue `0` (no debounce)
432
+ */
433
+ debounceMs?: number;
434
+ /**
435
+ * Function that returns matches for the current keyword.
436
+ * Called whenever the keyword changes (after debounce if configured).
437
+ */
438
+ getMatches: (context: {
439
+ keyword: string;
440
+ }) => ReadonlyArray<TMatch>;
441
+ };
442
+ type SyncConfigWithDelimiter<TMatch extends AutoCompleteMatch> = BaseConfigWithDelimiter<TMatch> & {
443
+ /**
444
+ * Whether `getMatches` returns synchronously or asynchronously.
445
+ * @defaultValue `'sync'`
446
+ */
447
+ mode?: 'sync';
448
+ /**
449
+ * Debounce delay in milliseconds before calling `getMatches`.
450
+ * Useful for expensive local searches.
451
+ * @defaultValue `0` (no debounce)
452
+ */
453
+ debounceMs?: number;
454
+ /**
455
+ * Function that returns matches for the current keyword.
456
+ * Called whenever the keyword changes (after debounce if configured).
457
+ */
458
+ getMatches: (context: {
459
+ keyword: string;
460
+ }) => ReadonlyArray<TMatch>;
461
+ };
462
+ type AsyncConfigWithoutDelimiter<TMatch extends object> = BaseConfigWithoutDelimiter<TMatch> & {
463
+ /**
464
+ * Set to `'async'` when `getMatches` returns a Promise.
465
+ */
466
+ mode: 'async';
467
+ /**
468
+ * Debounce delay in milliseconds before calling `getMatches`.
469
+ * Recommended for API calls to reduce request frequency.
470
+ * @defaultValue `0` (no debounce)
471
+ */
472
+ debounceMs?: number;
473
+ /**
474
+ * Async function that returns matches for the current keyword.
475
+ * Called whenever the keyword changes (after debounce if configured).
476
+ */
477
+ getMatches: (context: {
478
+ keyword: string;
479
+ }) => Promise<ReadonlyArray<TMatch>>;
480
+ };
481
+ type AsyncConfigWithDelimiter<TMatch extends AutoCompleteMatch> = BaseConfigWithDelimiter<TMatch> & {
482
+ /**
483
+ * Set to `'async'` when `getMatches` returns a Promise.
484
+ */
485
+ mode: 'async';
486
+ /**
487
+ * Debounce delay in milliseconds before calling `getMatches`.
488
+ * Recommended for API calls to reduce request frequency.
489
+ * @defaultValue `0` (no debounce)
490
+ */
491
+ debounceMs?: number;
492
+ /**
493
+ * Async function that returns matches for the current keyword.
494
+ * Called whenever the keyword changes (after debounce if configured).
495
+ */
496
+ getMatches: (context: {
497
+ keyword: string;
498
+ }) => Promise<ReadonlyArray<TMatch>>;
499
+ };
590
500
  /**
591
- * Guard function that runs at trigger time to conditionally prevent the picker
592
- * from activating. Has the same signature as a behavior guard.
501
+ * Creates a typeahead picker definition to use with {@link useTypeaheadPicker}.
593
502
  *
594
- * Return `false` to block activation, or `true` to allow it.
503
+ * @example Emoji picker with auto-complete
504
+ * ```ts
505
+ * const emojiPicker = defineTypeaheadPicker({
506
+ * trigger: /:/,
507
+ * keyword: /[\S]+/,
508
+ * delimiter: ':',
509
+ * getMatches: ({keyword}) => searchEmojis(keyword),
510
+ * onSelect: [
511
+ * ({event}) => [
512
+ * raise({type: 'delete', at: event.patternSelection}),
513
+ * raise({type: 'insert.text', text: event.match.emoji}),
514
+ * ],
515
+ * ],
516
+ * })
517
+ * ```
595
518
  *
596
- * @example
519
+ * @example Async mention picker
597
520
  * ```ts
598
- * guard: ({snapshot, event, dom}) => {
599
- * // Block activation if another picker is open
600
- * if (anotherPickerIsOpen()) return false
521
+ * const mentionPicker = defineTypeaheadPicker({
522
+ * mode: 'async',
523
+ * trigger: /@/,
524
+ * keyword: /[\w]+/,
525
+ * debounceMs: 200,
526
+ * getMatches: async ({keyword}) => api.searchUsers(keyword),
527
+ * onSelect: [
528
+ * ({event}) => [
529
+ * raise({type: 'delete', at: event.patternSelection}),
530
+ * raise({type: 'insert.inline object', inlineObject: {_type: 'mention', userId: event.match.id}}),
531
+ * ],
532
+ * ],
533
+ * })
534
+ * ```
601
535
  *
602
- * // Allow activation
603
- * return true
604
- * }
536
+ * @example Picker with guard (runs at trigger time)
537
+ * ```ts
538
+ * const emojiPicker = defineTypeaheadPicker({
539
+ * trigger: /:/,
540
+ * keyword: /[\S]+/,
541
+ * getMatches: ({keyword}) => searchEmojis(keyword),
542
+ * guard: ({snapshot, event, dom}) => {
543
+ * if (anotherPickerIsOpen()) return false
544
+ * return true
545
+ * },
546
+ * onSelect: [
547
+ * ({event}) => [
548
+ * raise({type: 'delete', at: event.patternSelection}),
549
+ * raise({type: 'insert.text', text: event.match.emoji}),
550
+ * ],
551
+ * ],
552
+ * })
605
553
  * ```
606
554
  *
607
555
  * @public
608
556
  */
609
- export declare type TypeaheadTriggerGuard = BehaviorGuard<
610
- TypeaheadTriggerEvent,
611
- true
612
- >
613
-
557
+ declare function defineTypeaheadPicker<TMatch extends object>(config: SyncConfigWithoutDelimiter<TMatch>): TypeaheadPickerDefinition<TMatch>;
558
+ /** @public */
559
+ declare function defineTypeaheadPicker<TMatch extends AutoCompleteMatch>(config: SyncConfigWithDelimiter<TMatch>): TypeaheadPickerDefinition<TMatch>;
560
+ /** @public */
561
+ declare function defineTypeaheadPicker<TMatch extends object>(config: AsyncConfigWithoutDelimiter<TMatch>): TypeaheadPickerDefinition<TMatch>;
562
+ /** @public */
563
+ declare function defineTypeaheadPicker<TMatch extends AutoCompleteMatch>(config: AsyncConfigWithDelimiter<TMatch>): TypeaheadPickerDefinition<TMatch>;
614
564
  /**
615
565
  * React hook that activates a typeahead picker and returns its current state.
616
566
  *
@@ -647,8 +597,6 @@ export declare type TypeaheadTriggerGuard = BehaviorGuard<
647
597
  *
648
598
  * @public
649
599
  */
650
- export declare function useTypeaheadPicker<TMatch extends object>(
651
- definition: TypeaheadPickerDefinition<TMatch>,
652
- ): TypeaheadPicker<TMatch>
653
-
654
- export {}
600
+ declare function useTypeaheadPicker<TMatch extends object>(definition: TypeaheadPickerDefinition<TMatch>): TypeaheadPicker<TMatch>;
601
+ export { type AutoCompleteMatch, type GetMatches, type TypeaheadDismissActionSet, type TypeaheadDismissEvent, type TypeaheadPicker, type TypeaheadPickerContext, type TypeaheadPickerDefinition, type TypeaheadPickerEvent, type TypeaheadPickerState, type TypeaheadSelectActionSet, type TypeaheadSelectEvent, type TypeaheadTriggerEvent, type TypeaheadTriggerGuard, defineTypeaheadPicker, useTypeaheadPicker };
602
+ //# sourceMappingURL=index.d.ts.map