@portabletext/editor 1.55.6 → 1.55.7

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.
Files changed (63) hide show
  1. package/lib/_chunks-cjs/selector.get-text-before.cjs +3 -3
  2. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  3. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +37 -37
  4. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
  5. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs +9 -9
  6. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs.map +1 -1
  7. package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs +10 -10
  8. package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs.map +1 -1
  9. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs +5 -5
  10. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs.map +1 -1
  11. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +3 -3
  12. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  13. package/lib/_chunks-cjs/{selection-point.cjs → util.slice-blocks.cjs} +14 -14
  14. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -0
  15. package/lib/_chunks-cjs/util.slice-text-block.cjs +11 -11
  16. package/lib/_chunks-cjs/util.slice-text-block.cjs.map +1 -1
  17. package/lib/_chunks-es/selector.get-text-before.js +1 -1
  18. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +1 -1
  19. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
  20. package/lib/_chunks-es/selector.is-selection-expanded.js +1 -1
  21. package/lib/_chunks-es/util.child-selection-point-to-block-offset.js +1 -1
  22. package/lib/_chunks-es/util.is-equal-selection-points.js +1 -1
  23. package/lib/_chunks-es/util.merge-text-blocks.js +1 -1
  24. package/lib/_chunks-es/{selection-point.js → util.slice-blocks.js} +14 -14
  25. package/lib/_chunks-es/util.slice-blocks.js.map +1 -0
  26. package/lib/_chunks-es/util.slice-text-block.js +1 -1
  27. package/lib/index.cjs +417 -194
  28. package/lib/index.cjs.map +1 -1
  29. package/lib/index.d.cts +6 -6
  30. package/lib/index.d.ts +6 -6
  31. package/lib/index.js +350 -127
  32. package/lib/index.js.map +1 -1
  33. package/lib/plugins/index.cjs +11 -11
  34. package/lib/plugins/index.cjs.map +1 -1
  35. package/lib/plugins/index.d.cts +2 -2
  36. package/lib/plugins/index.d.ts +2 -2
  37. package/lib/plugins/index.js +2 -2
  38. package/lib/selectors/index.cjs +7 -7
  39. package/lib/selectors/index.cjs.map +1 -1
  40. package/lib/selectors/index.js +1 -1
  41. package/lib/utils/index.cjs +11 -11
  42. package/lib/utils/index.cjs.map +1 -1
  43. package/lib/utils/index.d.cts +1 -1
  44. package/lib/utils/index.d.ts +1 -1
  45. package/lib/utils/index.js +2 -2
  46. package/package.json +13 -13
  47. package/src/behaviors/behavior.abstract.keyboard.ts +28 -3
  48. package/src/behaviors/behavior.core.block-objects.ts +5 -3
  49. package/src/behaviors/behavior.core.decorators.ts +5 -5
  50. package/src/behaviors/behavior.core.lists.ts +5 -3
  51. package/src/editor/relay-machine.ts +2 -2
  52. package/src/keyboard-shortcuts/default-keyboard-shortcuts.ts +207 -0
  53. package/src/keyboard-shortcuts/is-keyboard-shortcut.test.ts +93 -0
  54. package/src/keyboard-shortcuts/is-keyboard-shortcut.ts +28 -0
  55. package/src/keyboard-shortcuts/keyboard-shortcuts.ts +120 -0
  56. package/src/selection/selection-point.ts +1 -1
  57. package/src/selectors/selector.get-caret-word-selection.ts +3 -3
  58. package/src/types/editor.ts +1 -1
  59. package/src/utils/util.slice-blocks.ts +2 -1
  60. package/lib/_chunks-cjs/selection-point.cjs.map +0 -1
  61. package/lib/_chunks-es/selection-point.js.map +0 -1
  62. package/src/behaviors/behavior.emoji-picker.ts +0 -402
  63. package/src/internal-utils/key-is.ts +0 -11
@@ -1,402 +0,0 @@
1
- import {assertEvent, assign, createActor, setup} from 'xstate'
2
- import {isHotkey} from '../internal-utils/is-hotkey'
3
- import * as selectors from '../selectors'
4
- import {effect, execute, forward} from './behavior.types.action'
5
- import {defineBehavior} from './behavior.types.behavior'
6
-
7
- const emojiCharRegEx = /^[a-zA-Z-_0-9]{1}$/
8
- const incompleteEmojiRegEx = /:([a-zA-Z-_0-9]+)$/
9
- const emojiRegEx = /:([a-zA-Z-_0-9]+):$/
10
-
11
- export type EmojiPickerBehaviorsConfig<TEmojiMatch> = {
12
- /**
13
- * Match emojis by keyword.
14
- */
15
- matchEmojis: ({keyword}: {keyword: string}) => Array<TEmojiMatch>
16
- onMatchesChanged: ({matches}: {matches: Array<TEmojiMatch>}) => void
17
- onSelectedIndexChanged: ({selectedIndex}: {selectedIndex: number}) => void
18
- /**
19
- * Parse an emoji match to a string that will be inserted into the editor.
20
- */
21
- parseMatch: ({match}: {match: TEmojiMatch}) => string | undefined
22
- }
23
-
24
- export function createEmojiPickerBehaviors<TEmojiMatch>(
25
- config: EmojiPickerBehaviorsConfig<TEmojiMatch>,
26
- ) {
27
- const emojiPickerActor = createActor(createEmojiPickerMachine<TEmojiMatch>())
28
- emojiPickerActor.start()
29
- emojiPickerActor.subscribe((state) => {
30
- config.onMatchesChanged({matches: state.context.matches})
31
- config.onSelectedIndexChanged({selectedIndex: state.context.selectedIndex})
32
- })
33
-
34
- return [
35
- defineBehavior({
36
- on: 'insert.text',
37
- guard: ({snapshot, event}) => {
38
- if (event.text === ':') {
39
- return false
40
- }
41
-
42
- const isEmojiChar = emojiCharRegEx.test(event.text)
43
-
44
- if (!isEmojiChar) {
45
- return {emojis: []}
46
- }
47
-
48
- const focusBlock = selectors.getFocusTextBlock(snapshot)
49
- const textBefore = selectors.getBlockTextBefore(snapshot)
50
- const emojiKeyword = `${textBefore}${event.text}`.match(
51
- incompleteEmojiRegEx,
52
- )?.[1]
53
-
54
- if (!focusBlock || emojiKeyword === undefined) {
55
- return {emojis: []}
56
- }
57
-
58
- const emojis = config.matchEmojis({keyword: emojiKeyword})
59
-
60
- return {emojis}
61
- },
62
- actions: [
63
- ({event}, params) => [
64
- {
65
- type: 'effect',
66
- effect: () => {
67
- emojiPickerActor.send({
68
- type: 'emojis found',
69
- matches: params.emojis,
70
- })
71
- },
72
- },
73
- forward(event),
74
- ],
75
- ],
76
- }),
77
- defineBehavior({
78
- on: 'insert.text',
79
- guard: ({snapshot, event}) => {
80
- const isColon = event.text === ':'
81
-
82
- if (!isColon) {
83
- return false
84
- }
85
-
86
- const matches = emojiPickerActor.getSnapshot().context.matches
87
- const selectedIndex =
88
- emojiPickerActor.getSnapshot().context.selectedIndex
89
- const emoji = matches[selectedIndex]
90
- ? config.parseMatch({match: matches[selectedIndex]})
91
- : undefined
92
-
93
- const focusBlock = selectors.getFocusTextBlock(snapshot)
94
- const textBefore = selectors.getBlockTextBefore(snapshot)
95
- const emojiKeyword = `${textBefore}:`.match(emojiRegEx)?.[1]
96
-
97
- if (!focusBlock || emojiKeyword === undefined) {
98
- return false
99
- }
100
-
101
- const emojiStringLength = emojiKeyword.length + 2
102
-
103
- if (emoji) {
104
- return {
105
- focusBlock,
106
- emoji,
107
- emojiStringLength,
108
- textBeforeLength: textBefore.length + 1,
109
- }
110
- }
111
-
112
- return false
113
- },
114
- actions: [
115
- () => [
116
- execute({
117
- type: 'insert.text',
118
- text: ':',
119
- }),
120
- ],
121
- (_, params) => [
122
- effect(() => {
123
- emojiPickerActor.send({type: 'select'})
124
- }),
125
- execute({
126
- type: 'delete.text',
127
- at: {
128
- anchor: {
129
- path: params.focusBlock.path,
130
- offset: params.textBeforeLength - params.emojiStringLength,
131
- },
132
- focus: {
133
- path: params.focusBlock.path,
134
- offset: params.textBeforeLength,
135
- },
136
- },
137
- }),
138
- execute({
139
- type: 'insert.text',
140
- text: params.emoji,
141
- }),
142
- ],
143
- ],
144
- }),
145
- defineBehavior({
146
- on: 'keyboard.keydown',
147
- guard: ({snapshot, event}) => {
148
- const matches = emojiPickerActor.getSnapshot().context.matches
149
-
150
- if (matches.length === 0) {
151
- return false
152
- }
153
-
154
- const isEscape = isHotkey('Escape', event.originEvent)
155
-
156
- if (isEscape) {
157
- return {action: 'reset' as const}
158
- }
159
-
160
- const isEnter = isHotkey('Enter', event.originEvent)
161
- const isTab = isHotkey('Tab', event.originEvent)
162
-
163
- if (isEnter || isTab) {
164
- const selectedIndex =
165
- emojiPickerActor.getSnapshot().context.selectedIndex
166
-
167
- const emoji = matches[selectedIndex]
168
- ? config.parseMatch({match: matches[selectedIndex]})
169
- : undefined
170
-
171
- if (!emoji) {
172
- return false
173
- }
174
-
175
- const focusBlock = selectors.getFocusTextBlock(snapshot)
176
- const textBefore = selectors.getBlockTextBefore(snapshot)
177
- const emojiKeyword = textBefore.match(incompleteEmojiRegEx)?.[1]
178
-
179
- if (!focusBlock || emojiKeyword === undefined) {
180
- return false
181
- }
182
-
183
- const emojiStringLength = emojiKeyword.length + 1
184
-
185
- if (emoji) {
186
- return {
187
- action: 'select' as const,
188
- focusBlock,
189
- emoji,
190
- emojiStringLength,
191
- textBeforeLength: textBefore.length,
192
- }
193
- }
194
-
195
- return false
196
- }
197
-
198
- const isArrowDown = isHotkey('ArrowDown', event.originEvent)
199
- const isArrowUp = isHotkey('ArrowUp', event.originEvent)
200
-
201
- if (isArrowDown && matches.length > 0) {
202
- return {action: 'navigate down' as const}
203
- }
204
-
205
- if (isArrowUp && matches.length > 0) {
206
- return {action: 'navigate up' as const}
207
- }
208
-
209
- return false
210
- },
211
- actions: [
212
- (_, params) => {
213
- if (params.action === 'select') {
214
- return [
215
- effect(() => {
216
- emojiPickerActor.send({type: 'select'})
217
- }),
218
- execute({
219
- type: 'delete.text',
220
- at: {
221
- anchor: {
222
- path: params.focusBlock.path,
223
- offset: params.textBeforeLength - params.emojiStringLength,
224
- },
225
- focus: {
226
- path: params.focusBlock.path,
227
- offset: params.textBeforeLength,
228
- },
229
- },
230
- }),
231
- execute({
232
- type: 'insert.text',
233
- text: params.emoji,
234
- }),
235
- ]
236
- }
237
-
238
- if (params.action === 'navigate up') {
239
- return [
240
- // If we are navigating then we want to hijack the key event
241
- effect(() => {
242
- emojiPickerActor.send({type: 'navigate up'})
243
- }),
244
- ]
245
- }
246
-
247
- if (params.action === 'navigate down') {
248
- return [
249
- // If we are navigating then we want to hijack the key event
250
- effect(() => {
251
- emojiPickerActor.send({type: 'navigate down'})
252
- }),
253
- ]
254
- }
255
-
256
- return [
257
- effect(() => {
258
- emojiPickerActor.send({type: 'reset'})
259
- }),
260
- ]
261
- },
262
- ],
263
- }),
264
- defineBehavior({
265
- on: 'delete.backward',
266
- guard: ({snapshot, event}) => {
267
- if (event.unit !== 'character') {
268
- return false
269
- }
270
-
271
- const matches = emojiPickerActor.getSnapshot().context.matches
272
-
273
- if (matches.length === 0) {
274
- return false
275
- }
276
-
277
- const focusBlock = selectors.getFocusTextBlock(snapshot)
278
- const textBefore = selectors.getBlockTextBefore(snapshot)
279
- const emojiKeyword = textBefore
280
- .slice(0, textBefore.length - 1)
281
- .match(incompleteEmojiRegEx)?.[1]
282
-
283
- if (!focusBlock || emojiKeyword === undefined) {
284
- return {emojis: []}
285
- }
286
-
287
- const emojis = config.matchEmojis({keyword: emojiKeyword})
288
-
289
- return {emojis}
290
- },
291
- actions: [
292
- ({event}, params) => [
293
- {
294
- type: 'effect',
295
- effect: () => {
296
- emojiPickerActor.send({
297
- type: 'emojis found',
298
- matches: params.emojis,
299
- })
300
- },
301
- },
302
- forward(event),
303
- ],
304
- ],
305
- }),
306
- ]
307
- }
308
-
309
- function createEmojiPickerMachine<TEmojiSearchResult>() {
310
- return setup({
311
- types: {
312
- context: {} as {
313
- matches: Array<TEmojiSearchResult>
314
- selectedIndex: number
315
- },
316
- events: {} as
317
- | {
318
- type: 'emojis found'
319
- matches: Array<TEmojiSearchResult>
320
- }
321
- | {
322
- type: 'navigate down' | 'navigate up' | 'select' | 'reset'
323
- },
324
- },
325
- actions: {
326
- 'assign matches': assign({
327
- matches: ({event}) => {
328
- assertEvent(event, 'emojis found')
329
- return event.matches
330
- },
331
- }),
332
- 'reset matches': assign({
333
- matches: [],
334
- }),
335
- 'reset selected index': assign({
336
- selectedIndex: 0,
337
- }),
338
- 'increment selected index': assign({
339
- selectedIndex: ({context}) => {
340
- if (context.selectedIndex === context.matches.length - 1) {
341
- return 0
342
- }
343
- return context.selectedIndex + 1
344
- },
345
- }),
346
- 'decrement selected index': assign({
347
- selectedIndex: ({context}) => {
348
- if (context.selectedIndex === 0) {
349
- return context.matches.length - 1
350
- }
351
- return context.selectedIndex - 1
352
- },
353
- }),
354
- },
355
- guards: {
356
- 'no matches': ({context}) => context.matches.length === 0,
357
- },
358
- }).createMachine({
359
- id: 'emoji picker',
360
- context: {
361
- matches: [],
362
- selectedIndex: 0,
363
- },
364
- initial: 'idle',
365
- states: {
366
- 'idle': {
367
- on: {
368
- 'emojis found': {
369
- actions: 'assign matches',
370
- target: 'showing matches',
371
- },
372
- },
373
- },
374
- 'showing matches': {
375
- always: {
376
- guard: 'no matches',
377
- target: 'idle',
378
- },
379
- exit: ['reset selected index'],
380
- on: {
381
- 'emojis found': {
382
- actions: 'assign matches',
383
- },
384
- 'navigate down': {
385
- actions: 'increment selected index',
386
- },
387
- 'navigate up': {
388
- actions: 'decrement selected index',
389
- },
390
- 'reset': {
391
- target: 'idle',
392
- actions: ['reset selected index', 'reset matches'],
393
- },
394
- 'select': {
395
- target: 'idle',
396
- actions: ['reset selected index', 'reset matches'],
397
- },
398
- },
399
- },
400
- },
401
- })
402
- }
@@ -1,11 +0,0 @@
1
- export const keyIs = {
2
- break: (event) => event.key === 'Enter' && !event.shiftKey,
3
- lineBreak: (event) => event.key === 'Enter' && event.shiftKey,
4
- } satisfies Record<string, KeyboardEventPredicate>
5
-
6
- type KeyboardEventPredicate = (
7
- event: Pick<
8
- KeyboardEvent,
9
- 'key' | 'code' | 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey'
10
- >,
11
- ) => boolean