@portabletext/plugin-emoji-picker 1.0.0 → 1.0.1
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.cjs +21 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -10
- package/dist/index.d.ts +29 -10
- package/dist/index.js +21 -21
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/create-match-emojis.ts +113 -0
- package/src/emoji-picker-machine.tsx +4 -4
- package/src/emoji-picker.test.tsx +1 -1
- package/src/emojis.ts +0 -3
- package/src/index.ts +1 -0
- package/src/match-emojis.ts +4 -103
- package/src/use-emoji-picker.ts +9 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/plugin-emoji-picker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Easily configure an Emoji Picker for the Portable Text Editor",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"portabletext",
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
"typescript": "5.9.3",
|
|
57
57
|
"typescript-eslint": "^8.46.1",
|
|
58
58
|
"vitest": "^3.2.4",
|
|
59
|
-
"
|
|
59
|
+
"racejar": "1.3.2",
|
|
60
60
|
"@portabletext/schema": "1.2.0",
|
|
61
|
-
"
|
|
61
|
+
"@portabletext/editor": "2.15.5"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"@portabletext/editor": "^2.15.5",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {emojis} from './emojis'
|
|
2
|
+
import type {MatchEmojis} from './match-emojis'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Proposed, but not required type, to represent an emoji match.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* {
|
|
10
|
+
* type: 'exact',
|
|
11
|
+
* key: '😂-joy',
|
|
12
|
+
* emoji: '😂',
|
|
13
|
+
* keyword: 'joy',
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* {
|
|
19
|
+
* type: 'partial',
|
|
20
|
+
* key: '😹-joy-_cat',
|
|
21
|
+
* emoji: '😹',
|
|
22
|
+
* keyword: 'joy',
|
|
23
|
+
* startSlice: '',
|
|
24
|
+
* endSlice: '_cat',
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @beta
|
|
29
|
+
*/
|
|
30
|
+
export type EmojiMatch =
|
|
31
|
+
| {
|
|
32
|
+
type: 'exact'
|
|
33
|
+
key: string
|
|
34
|
+
emoji: string
|
|
35
|
+
keyword: string
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
type: 'partial'
|
|
39
|
+
key: string
|
|
40
|
+
emoji: string
|
|
41
|
+
keyword: string
|
|
42
|
+
startSlice: string
|
|
43
|
+
endSlice: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Proposed, but not required, default implementation of `MatchEmojis`.
|
|
48
|
+
*
|
|
49
|
+
* @beta
|
|
50
|
+
*/
|
|
51
|
+
export const matchEmojis = createMatchEmojis({emojis})
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Proposed, but not required, function to create a `MatchEmojis` function.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const matchEmojis = createMatchEmojis({
|
|
59
|
+
* emojis: {
|
|
60
|
+
* '😂': ['joy'],
|
|
61
|
+
* '😹': ['joy_cat'],
|
|
62
|
+
* },
|
|
63
|
+
* })
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @beta
|
|
67
|
+
*/
|
|
68
|
+
export function createMatchEmojis(config: {
|
|
69
|
+
emojis: Record<string, ReadonlyArray<string>>
|
|
70
|
+
}): MatchEmojis<EmojiMatch> {
|
|
71
|
+
return ({keyword}: {keyword: string}) => {
|
|
72
|
+
const foundEmojis: Array<EmojiMatch> = []
|
|
73
|
+
|
|
74
|
+
if (keyword.length < 1) {
|
|
75
|
+
return foundEmojis
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const emoji in config.emojis) {
|
|
79
|
+
const emojiKeywords = config.emojis[emoji] ?? []
|
|
80
|
+
|
|
81
|
+
for (const emojiKeyword of emojiKeywords) {
|
|
82
|
+
const keywordIndex = emojiKeyword.indexOf(keyword)
|
|
83
|
+
|
|
84
|
+
if (keywordIndex === -1) {
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (emojiKeyword === keyword) {
|
|
89
|
+
foundEmojis.push({
|
|
90
|
+
type: 'exact',
|
|
91
|
+
key: `${emoji}-${keyword}`,
|
|
92
|
+
emoji,
|
|
93
|
+
keyword,
|
|
94
|
+
})
|
|
95
|
+
} else {
|
|
96
|
+
const start = emojiKeyword.slice(0, keywordIndex)
|
|
97
|
+
const end = emojiKeyword.slice(keywordIndex + keyword.length)
|
|
98
|
+
|
|
99
|
+
foundEmojis.push({
|
|
100
|
+
type: 'partial',
|
|
101
|
+
key: `${emoji}-${start}${keyword}${end}`,
|
|
102
|
+
emoji,
|
|
103
|
+
keyword,
|
|
104
|
+
startSlice: start,
|
|
105
|
+
endSlice: end,
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return foundEmojis
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
type AnyEventObject,
|
|
29
29
|
type CallbackLogicFunction,
|
|
30
30
|
} from 'xstate'
|
|
31
|
-
import type {
|
|
31
|
+
import type {BaseEmojiMatch, MatchEmojis} from './match-emojis'
|
|
32
32
|
|
|
33
33
|
/*******************
|
|
34
34
|
* Keyboard shortcuts
|
|
@@ -175,10 +175,10 @@ function createKeywordFoundEvent(payload: {
|
|
|
175
175
|
} as const
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
type EmojiPickerContext
|
|
178
|
+
type EmojiPickerContext = {
|
|
179
179
|
editor: Editor
|
|
180
|
-
matches: ReadonlyArray<
|
|
181
|
-
matchEmojis: MatchEmojis<
|
|
180
|
+
matches: ReadonlyArray<BaseEmojiMatch>
|
|
181
|
+
matchEmojis: MatchEmojis<BaseEmojiMatch>
|
|
182
182
|
selectedIndex: number
|
|
183
183
|
keywordAnchor:
|
|
184
184
|
| {
|
|
@@ -9,8 +9,8 @@ import {page, type Locator} from '@vitest/browser/context'
|
|
|
9
9
|
import {Before, Then} from 'racejar'
|
|
10
10
|
import {Feature} from 'racejar/vitest'
|
|
11
11
|
import {expect, vi} from 'vitest'
|
|
12
|
+
import {createMatchEmojis} from './create-match-emojis'
|
|
12
13
|
import emojiPickerFeature from './emoji-picker.feature?raw'
|
|
13
|
-
import {createMatchEmojis} from './match-emojis'
|
|
14
14
|
import {useEmojiPicker} from './use-emoji-picker'
|
|
15
15
|
|
|
16
16
|
type EmojiPickerTestContext = Context & {
|
package/src/emojis.ts
CHANGED
package/src/index.ts
CHANGED
package/src/match-emojis.ts
CHANGED
|
@@ -1,45 +1,16 @@
|
|
|
1
|
-
import {emojis} from './emojis'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```tsx
|
|
8
|
-
* {
|
|
9
|
-
* type: 'exact',
|
|
10
|
-
* key: '😂-joy',
|
|
11
|
-
* emoji: '😂',
|
|
12
|
-
* keyword: 'joy',
|
|
13
|
-
* }
|
|
14
|
-
* ```
|
|
15
|
-
* @example
|
|
16
|
-
* ```tsx
|
|
17
|
-
* {
|
|
18
|
-
* type: 'partial',
|
|
19
|
-
* key: '😹-joy-_cat',
|
|
20
|
-
* emoji: '😹',
|
|
21
|
-
* keyword: 'joy',
|
|
22
|
-
* startSlice: '',
|
|
23
|
-
* endSlice: '_cat',
|
|
24
|
-
* }
|
|
25
|
-
* ```
|
|
2
|
+
* The base type representing an emoji match.
|
|
26
3
|
*
|
|
27
4
|
* @beta
|
|
28
5
|
*/
|
|
29
|
-
export type
|
|
6
|
+
export type BaseEmojiMatch =
|
|
30
7
|
| {
|
|
31
8
|
type: 'exact'
|
|
32
|
-
key: string
|
|
33
9
|
emoji: string
|
|
34
|
-
keyword: string
|
|
35
10
|
}
|
|
36
11
|
| {
|
|
37
12
|
type: 'partial'
|
|
38
|
-
key: string
|
|
39
13
|
emoji: string
|
|
40
|
-
keyword: string
|
|
41
|
-
startSlice: string
|
|
42
|
-
endSlice: string
|
|
43
14
|
}
|
|
44
15
|
|
|
45
16
|
/**
|
|
@@ -47,75 +18,5 @@ export type EmojiMatch =
|
|
|
47
18
|
*
|
|
48
19
|
* @beta
|
|
49
20
|
*/
|
|
50
|
-
export type MatchEmojis<TEmojiMatch =
|
|
51
|
-
keyword: string
|
|
52
|
-
}) => ReadonlyArray<TEmojiMatch>
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Proposed, but not required, default implementation of `MatchEmojis`.
|
|
56
|
-
*
|
|
57
|
-
* @beta
|
|
58
|
-
*/
|
|
59
|
-
export const matchEmojis: MatchEmojis = createMatchEmojis({emojis})
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Proposed, but not required, function to create a `MatchEmojis` function.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```ts
|
|
66
|
-
* const matchEmojis = createMatchEmojis({
|
|
67
|
-
* emojis: {
|
|
68
|
-
* '😂': ['joy'],
|
|
69
|
-
* '😹': ['joy_cat'],
|
|
70
|
-
* },
|
|
71
|
-
* })
|
|
72
|
-
* ```
|
|
73
|
-
*
|
|
74
|
-
* @beta
|
|
75
|
-
*/
|
|
76
|
-
export function createMatchEmojis(config: {
|
|
77
|
-
emojis: Record<string, ReadonlyArray<string>>
|
|
78
|
-
}): MatchEmojis {
|
|
79
|
-
return ({keyword}: {keyword: string}) => {
|
|
80
|
-
const foundEmojis: Array<EmojiMatch> = []
|
|
81
|
-
|
|
82
|
-
if (keyword.length < 1) {
|
|
83
|
-
return foundEmojis
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const emoji in config.emojis) {
|
|
87
|
-
const emojiKeywords = config.emojis[emoji] ?? []
|
|
88
|
-
|
|
89
|
-
for (const emojiKeyword of emojiKeywords) {
|
|
90
|
-
const keywordIndex = emojiKeyword.indexOf(keyword)
|
|
91
|
-
|
|
92
|
-
if (keywordIndex === -1) {
|
|
93
|
-
continue
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (emojiKeyword === keyword) {
|
|
97
|
-
foundEmojis.push({
|
|
98
|
-
type: 'exact',
|
|
99
|
-
key: `${emoji}-${keyword}`,
|
|
100
|
-
emoji,
|
|
101
|
-
keyword,
|
|
102
|
-
})
|
|
103
|
-
} else {
|
|
104
|
-
const start = emojiKeyword.slice(0, keywordIndex)
|
|
105
|
-
const end = emojiKeyword.slice(keywordIndex + keyword.length)
|
|
106
|
-
|
|
107
|
-
foundEmojis.push({
|
|
108
|
-
type: 'partial',
|
|
109
|
-
key: `${emoji}-${start}${keyword}${end}`,
|
|
110
|
-
emoji,
|
|
111
|
-
keyword,
|
|
112
|
-
startSlice: start,
|
|
113
|
-
endSlice: end,
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return foundEmojis
|
|
120
|
-
}
|
|
121
|
-
}
|
|
21
|
+
export type MatchEmojis<TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch> =
|
|
22
|
+
(query: {keyword: string}) => ReadonlyArray<TEmojiMatch>
|
package/src/use-emoji-picker.ts
CHANGED
|
@@ -2,12 +2,12 @@ import {useEditor} from '@portabletext/editor'
|
|
|
2
2
|
import {useActorRef, useSelector} from '@xstate/react'
|
|
3
3
|
import {useCallback} from 'react'
|
|
4
4
|
import {emojiPickerMachine} from './emoji-picker-machine'
|
|
5
|
-
import type {
|
|
5
|
+
import type {BaseEmojiMatch, MatchEmojis} from './match-emojis'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @beta
|
|
9
9
|
*/
|
|
10
|
-
export type EmojiPicker<TEmojiMatch =
|
|
10
|
+
export type EmojiPicker<TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch> = {
|
|
11
11
|
/**
|
|
12
12
|
* The matched keyword, including colons.
|
|
13
13
|
*
|
|
@@ -105,7 +105,9 @@ export type EmojiPicker<TEmojiMatch = EmojiMatch> = {
|
|
|
105
105
|
/**
|
|
106
106
|
* @beta
|
|
107
107
|
*/
|
|
108
|
-
export type EmojiPickerProps<
|
|
108
|
+
export type EmojiPickerProps<
|
|
109
|
+
TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,
|
|
110
|
+
> = {
|
|
109
111
|
matchEmojis: MatchEmojis<TEmojiMatch>
|
|
110
112
|
}
|
|
111
113
|
|
|
@@ -136,12 +138,12 @@ export type EmojiPickerProps<TEmojiMatch = EmojiMatch> = {
|
|
|
136
138
|
*
|
|
137
139
|
* @beta
|
|
138
140
|
*/
|
|
139
|
-
export function useEmojiPicker<
|
|
140
|
-
|
|
141
|
-
): EmojiPicker<TEmojiMatch> {
|
|
141
|
+
export function useEmojiPicker<
|
|
142
|
+
TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,
|
|
143
|
+
>(props: EmojiPickerProps<TEmojiMatch>): EmojiPicker<TEmojiMatch> {
|
|
142
144
|
const editor = useEditor()
|
|
143
145
|
const emojiPickerActor = useActorRef(emojiPickerMachine, {
|
|
144
|
-
input: {editor, matchEmojis: props.matchEmojis
|
|
146
|
+
input: {editor, matchEmojis: props.matchEmojis},
|
|
145
147
|
})
|
|
146
148
|
const keyword = useSelector(
|
|
147
149
|
emojiPickerActor,
|