emoji-picker-react 4.0.0-rc.19 → 4.0.0-rc.21

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 (82) hide show
  1. package/README.md +44 -4
  2. package/dist/DomUtils/classNames.d.ts +6 -1
  3. package/dist/DomUtils/elementPositionInRow.d.ts +10 -0
  4. package/dist/DomUtils/emojiElementRef.d.ts +4 -3
  5. package/dist/DomUtils/focusElement.d.ts +5 -0
  6. package/dist/DomUtils/getActiveElement.d.ts +2 -0
  7. package/dist/DomUtils/keyboardNavigation.d.ts +8 -0
  8. package/dist/DomUtils/scrollCategoryIntoView.d.ts +2 -1
  9. package/dist/DomUtils/scrollTo.d.ts +3 -1
  10. package/dist/DomUtils/selectors.d.ts +23 -11
  11. package/dist/components/context/ElementRefContext.d.ts +9 -13
  12. package/dist/components/context/PickerContext.d.ts +1 -1
  13. package/dist/components/emoji/Emoji.d.ts +2 -2
  14. package/dist/components/footer/Preview.d.ts +1 -1
  15. package/dist/config/config.d.ts +12 -15
  16. package/dist/config/useConfig.d.ts +2 -1
  17. package/dist/dataUtils/emojiSelectors.d.ts +2 -2
  18. package/dist/emoji-picker-react.cjs.development.css +7 -7
  19. package/dist/emoji-picker-react.cjs.development.js +1119 -302
  20. package/dist/emoji-picker-react.cjs.development.js.map +1 -1
  21. package/dist/emoji-picker-react.cjs.production.min.js +1 -1
  22. package/dist/emoji-picker-react.cjs.production.min.js.map +1 -1
  23. package/dist/emoji-picker-react.esm.js +1129 -312
  24. package/dist/emoji-picker-react.esm.js.map +1 -1
  25. package/dist/hooks/useCloseAllOpenToggles.d.ts +1 -4
  26. package/dist/hooks/useDebouncedState.d.ts +1 -1
  27. package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -0
  28. package/dist/hooks/useFilter.d.ts +3 -3
  29. package/dist/hooks/useFocus.d.ts +3 -0
  30. package/dist/hooks/useInitialLoad.d.ts +1 -1
  31. package/dist/hooks/useKeyboardNavigation.d.ts +1 -0
  32. package/dist/hooks/useMouseDownHandlers.d.ts +3 -2
  33. package/package.json +7 -4
  34. package/src/DomUtils/classNames.ts +9 -1
  35. package/src/DomUtils/elementPositionInRow.ts +169 -0
  36. package/src/DomUtils/emojiElementRef.ts +4 -3
  37. package/src/DomUtils/focusElement.ts +35 -0
  38. package/src/DomUtils/getActiveElement.ts +5 -0
  39. package/src/DomUtils/keyboardNavigation.ts +159 -0
  40. package/src/DomUtils/scrollCategoryIntoView.ts +2 -1
  41. package/src/DomUtils/scrollTo.ts +22 -1
  42. package/src/DomUtils/selectors.ts +157 -32
  43. package/src/EmojiPickerReact.css +57 -29
  44. package/src/components/ErrorBoundary.tsx +1 -0
  45. package/src/components/body/EmojiCategory.css +14 -20
  46. package/src/components/body/EmojiCategory.tsx +3 -3
  47. package/src/components/body/EmojiList.tsx +8 -7
  48. package/src/components/body/EmojiVariationPicker.tsx +3 -3
  49. package/src/components/body/Suggested.tsx +3 -1
  50. package/src/components/context/ElementRefContext.tsx +46 -19
  51. package/src/components/context/PickerContext.tsx +16 -14
  52. package/src/components/emoji/Emoji.css +7 -2
  53. package/src/components/emoji/Emoji.tsx +10 -10
  54. package/src/components/footer/Preview.css +1 -1
  55. package/src/components/footer/Preview.tsx +13 -16
  56. package/src/components/header/Search.css +7 -7
  57. package/src/components/header/Search.tsx +6 -4
  58. package/src/components/header/SkinTonePicker.css +12 -5
  59. package/src/components/header/SkinTonePicker.tsx +11 -3
  60. package/src/components/main/PickerMain.tsx +6 -2
  61. package/src/components/navigation/CategoryNavigation.css +11 -0
  62. package/src/components/navigation/CategoryNavigation.tsx +5 -2
  63. package/src/config/config.ts +39 -10
  64. package/src/config/useConfig.ts +6 -5
  65. package/src/dataUtils/emojiSelectors.ts +3 -2
  66. package/src/dataUtils/suggested.ts +1 -0
  67. package/src/hooks/preloadEmoji.ts +1 -1
  68. package/src/hooks/useActiveCategoryScrollDetection.ts +7 -4
  69. package/src/hooks/useCloseAllOpenToggles.ts +7 -5
  70. package/src/hooks/useDebouncedState.ts +10 -7
  71. package/src/hooks/useEmojiPreviewEvents.ts +90 -0
  72. package/src/hooks/useFilter.ts +41 -21
  73. package/src/hooks/useFocus.ts +40 -0
  74. package/src/hooks/useInitialLoad.ts +1 -0
  75. package/src/hooks/useKeyboardNavigation.ts +389 -0
  76. package/src/hooks/useMouseDownHandlers.ts +100 -74
  77. package/src/hooks/useOnScroll.ts +2 -2
  78. package/src/index.tsx +3 -1
  79. package/dist/hooks/useEmojiElements.d.ts +0 -1
  80. package/dist/hooks/useEmojiMouseEnter.d.ts +0 -3
  81. package/src/hooks/useEmojiElements.ts +0 -7
  82. package/src/hooks/useEmojiMouseEnter.ts +0 -59
@@ -1,4 +1 @@
1
- export declare function useCloseAllOpenToggles(): {
2
- closeAllOpenToggles: () => void;
3
- dependencyArray: (boolean | import("../dataUtils/DataTypes").DataEmoji | null)[];
4
- };
1
+ export declare function useCloseAllOpenToggles(): () => void;
@@ -1 +1 @@
1
- export declare function useDebouncedState<T>(initialValue: T, delay?: number): [T, (value: T) => void];
1
+ export declare function useDebouncedState<T>(initialValue: T, delay?: number): [T, (value: T) => Promise<T>];
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ import { PreviewEmoji } from '../components/footer/Preview';
3
+ export declare function useEmojiPreviewEvents(allow: boolean, setPreviewEmoji: React.Dispatch<React.SetStateAction<PreviewEmoji>>): void;
@@ -1,10 +1,10 @@
1
- /// <reference types="react" />
2
1
  import { DataEmoji } from '../dataUtils/DataTypes';
2
+ export declare function useClearSearch(): () => void;
3
+ export declare function useAppendSearch(): (str: string) => void;
3
4
  export declare function useFilter(): {
4
5
  onChange: (nextValue: string) => void;
5
6
  searchTerm: string;
6
- clearSearch: () => void;
7
- SearchInputRef: import("react").RefObject<HTMLInputElement>;
7
+ SearchInputRef: import("../components/context/ElementRefContext").ElementRef<HTMLInputElement>;
8
8
  };
9
9
  export declare function useIsEmojiFiltered(): (unified: string) => boolean;
10
10
  export declare type FilterDict = Record<string, DataEmoji>;
@@ -0,0 +1,3 @@
1
+ export declare function useFocusSearchInput(): () => void;
2
+ export declare function useFocusSkinTonePicker(): () => void;
3
+ export declare function useFocusCategoryNavigation(): () => void;
@@ -1,2 +1,2 @@
1
- /// <reference types="react" />
1
+ import * as React from 'react';
2
2
  export declare function useMarkInitialLoad(dispatch: React.Dispatch<React.SetStateAction<boolean>>): void;
@@ -0,0 +1 @@
1
+ export declare function useKeyboardNavigation(): void;
@@ -1,2 +1,3 @@
1
- /// <reference types="react" />
2
- export declare function useMouseDownHandlers(BodyRef: React.MutableRefObject<HTMLElement | null>): void;
1
+ import * as React from 'react';
2
+ import { NullableElement } from '../DomUtils/selectors';
3
+ export declare function useMouseDownHandlers(BodyRef: React.MutableRefObject<NullableElement>): void;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.0.0-rc.19",
2
+ "version": "4.0.0-rc.21",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "start": "tsdx watch",
15
15
  "build": "node ./scripts/prepare.js && tsdx build",
16
16
  "test": "tsdx test --passWithNoTests",
17
- "lint": "tsdx lint",
17
+ "lint": "eslint ./src --ext .ts,.tsx",
18
18
  "prepare": "tsdx build",
19
19
  "size": "size-limit",
20
20
  "analyze": "size-limit --why",
@@ -58,6 +58,8 @@
58
58
  "@storybook/react": "^6.5.10",
59
59
  "@types/react": "^18.0.17",
60
60
  "@types/react-dom": "^18.0.6",
61
+ "@typescript-eslint/eslint-plugin": "^5.36.2",
62
+ "@typescript-eslint/parser": "^5.36.2",
61
63
  "autoprefixer": "^10.4.8",
62
64
  "babel-loader": "^8.2.5",
63
65
  "cssnano": "^5.1.13",
@@ -77,12 +79,13 @@
77
79
  "rollup-plugin-postcss": "^4.0.2",
78
80
  "size-limit": "^8.0.1",
79
81
  "tiny-invariant": "^1.2.0",
80
- "tinykeys": "^1.4.0",
81
82
  "tsdx": "^0.14.1",
82
83
  "tslib": "^2.4.0",
83
84
  "typescript": "^4.7.4"
84
85
  },
85
86
  "dependencies": {
86
- "clsx": "^1.2.1"
87
+ "clsx": "^1.2.1",
88
+ "eslint-plugin-react": "^7.31.8",
89
+ "eslint-plugin-react-hooks": "^4.6.0"
87
90
  }
88
91
  }
@@ -2,5 +2,13 @@ export enum ClassNames {
2
2
  hiddenOnSearch = 'epr-hidden-on-search',
3
3
  searchActive = 'epr-search-active',
4
4
  hidden = 'epr-hidden',
5
- visible = 'epr-visible'
5
+ visible = 'epr-visible',
6
+ active = 'epr-active',
7
+ emoji = 'epr-emoji',
8
+ category = 'epr-emoji-category',
9
+ label = 'epr-emoji-category-label',
10
+ }
11
+
12
+ export function asSelectors(...classNames: ClassNames[]): string {
13
+ return classNames.map(c => `.${c}`).join('');
6
14
  }
@@ -0,0 +1,169 @@
1
+ import { NullableElement } from './selectors';
2
+
3
+ export function elementCountInRow(
4
+ parent: NullableElement,
5
+ element: NullableElement
6
+ ): number {
7
+ if (!parent || !element) {
8
+ return 0;
9
+ }
10
+
11
+ const parentWidth = parent.getBoundingClientRect().width;
12
+ const elementWidth = element.getBoundingClientRect().width;
13
+
14
+ return Math.floor(parentWidth / elementWidth);
15
+ }
16
+
17
+ export function elementIndexInRow(
18
+ parent: NullableElement,
19
+ element: NullableElement
20
+ ): number {
21
+ if (!parent || !element) {
22
+ return 0;
23
+ }
24
+
25
+ const elementWidth = element.getBoundingClientRect().width;
26
+ const elementLeft = element.getBoundingClientRect().left;
27
+ const parentLeft = parent.getBoundingClientRect().left;
28
+
29
+ return Math.floor((elementLeft - parentLeft) / elementWidth);
30
+ }
31
+
32
+ export function rowNumber(
33
+ parent: NullableElement,
34
+ element: NullableElement
35
+ ): number {
36
+ if (!parent || !element) {
37
+ return 0;
38
+ }
39
+
40
+ const elementHeight = element.getBoundingClientRect().height;
41
+ const elementTop = element.getBoundingClientRect().top;
42
+ const parentTop = parent.getBoundingClientRect().top;
43
+
44
+ return Math.floor((elementTop - parentTop) / elementHeight) - 1;
45
+ }
46
+
47
+ export function hasNextRow(
48
+ parent: NullableElement,
49
+ element: NullableElement
50
+ ): boolean {
51
+ if (!parent || !element) {
52
+ return false;
53
+ }
54
+
55
+ const elementHeight = element.getBoundingClientRect().height;
56
+ const elementTop = element.getBoundingClientRect().top;
57
+ const parentTop = parent.getBoundingClientRect().top;
58
+ const parentHeight = parent.getBoundingClientRect().height;
59
+
60
+ return elementTop - parentTop + elementHeight < parentHeight;
61
+ }
62
+
63
+ function getRowElements(
64
+ elements: HTMLElement[],
65
+ row: number,
66
+ elementsInRow: number
67
+ ): HTMLElement[] {
68
+ if (row === -1) {
69
+ const lastRow = Math.floor(elements.length / elementsInRow);
70
+ const firstElementIndex = lastRow * elementsInRow;
71
+ const lastElementIndex = elements.length - 1;
72
+ return elements.slice(firstElementIndex, lastElementIndex + 1);
73
+ }
74
+
75
+ return elements.slice(row * elementsInRow, (row + 1) * elementsInRow);
76
+ }
77
+
78
+ function getNextRowElements(
79
+ allElements: HTMLElement[],
80
+ currentRow: number,
81
+ elementsInRow: number
82
+ ): HTMLElement[] {
83
+ const nextRow = currentRow + 1;
84
+
85
+ if (nextRow * elementsInRow > allElements.length) {
86
+ return [];
87
+ }
88
+
89
+ return getRowElements(allElements, nextRow, elementsInRow);
90
+ }
91
+
92
+ export function getElementInRow(
93
+ elements: HTMLElement[],
94
+ row: number,
95
+ elementsInRow: number,
96
+ indexInRow: number
97
+ ): NullableElement {
98
+ const rowElements = getRowElements(elements, row, elementsInRow);
99
+ // get element, default to last
100
+ return rowElements[indexInRow] || rowElements[rowElements.length - 1] || null;
101
+ }
102
+
103
+ export function getElementInNextRow(
104
+ allElements: HTMLElement[],
105
+ currentRow: number,
106
+ elementsInRow: number,
107
+ index: number
108
+ ): NullableElement {
109
+ const nextRowElements = getNextRowElements(
110
+ allElements,
111
+ currentRow,
112
+ elementsInRow
113
+ );
114
+
115
+ // return item in index, or last item in row
116
+ return (
117
+ nextRowElements[index] ||
118
+ nextRowElements[nextRowElements.length - 1] ||
119
+ null
120
+ );
121
+ }
122
+
123
+ export function getElementInPrevRow(
124
+ allElements: HTMLElement[],
125
+ currentRow: number,
126
+ elementsInRow: number,
127
+ index: number
128
+ ): NullableElement {
129
+ const prevRowElements = getRowElements(
130
+ allElements,
131
+ currentRow - 1,
132
+ elementsInRow
133
+ );
134
+
135
+ // default to last
136
+ return (
137
+ prevRowElements[index] ||
138
+ prevRowElements[prevRowElements.length - 1] ||
139
+ null
140
+ );
141
+ }
142
+
143
+ export function firstVisibleElementInContainer(
144
+ parent: NullableElement,
145
+ elements: HTMLElement[]
146
+ ): NullableElement {
147
+ if (!parent || !elements.length) {
148
+ return null;
149
+ }
150
+
151
+ const parentTop = parent.getBoundingClientRect().top;
152
+ const parentBottom = parent.getBoundingClientRect().bottom;
153
+
154
+ const visibleElements = elements.find(element => {
155
+ const elementTop = element.getBoundingClientRect().top;
156
+ const elementBottom = element.getBoundingClientRect().bottom;
157
+
158
+ return (
159
+ (elementTop >= parentTop && elementTop <= parentBottom) ||
160
+ (elementBottom >= parentTop && elementBottom <= parentBottom)
161
+ );
162
+ });
163
+
164
+ return visibleElements || null;
165
+ }
166
+
167
+ export function hasNextElementSibling(element: HTMLElement) {
168
+ return !!element.nextElementSibling;
169
+ }
@@ -1,6 +1,7 @@
1
1
  import { ClassNames } from './classNames';
2
+ import { NullableElement } from './selectors';
2
3
 
3
- export type EmojiRef = { current: null | HTMLElement };
4
+ export type EmojiRef = { current: NullableElement };
4
5
 
5
6
  export const emojiRef: Record<string, EmojiRef> = {};
6
7
 
@@ -29,13 +30,13 @@ export function showEmoji(unified: string): void {
29
30
  showElement(getEmojiRef(unified).current);
30
31
  }
31
32
 
32
- export function hideElement(element: HTMLElement | null): void {
33
+ export function hideElement(element: NullableElement): void {
33
34
  if (!element) return;
34
35
  element.classList.add(ClassNames.hidden);
35
36
  element.classList.remove(ClassNames.visible);
36
37
  }
37
38
 
38
- export function showElement(element: HTMLElement | null): void {
39
+ export function showElement(element: NullableElement): void {
39
40
  if (!element) return;
40
41
  element.classList.remove(ClassNames.hidden);
41
42
  element.classList.add(ClassNames.visible);
@@ -0,0 +1,35 @@
1
+ import { NullableElement } from './selectors';
2
+
3
+ export function focusElement(element: NullableElement) {
4
+ if (!element) {
5
+ return;
6
+ }
7
+
8
+ requestAnimationFrame(() => {
9
+ element.focus();
10
+ });
11
+ }
12
+
13
+ export function focusPrevElementSibling(element: NullableElement) {
14
+ if (!element) return;
15
+
16
+ const prev = element.previousElementSibling as HTMLElement;
17
+
18
+ focusElement(prev);
19
+ }
20
+
21
+ export function focusNextElementSibling(element: NullableElement) {
22
+ if (!element) return;
23
+
24
+ const next = element.nextElementSibling as HTMLElement;
25
+
26
+ focusElement(next);
27
+ }
28
+
29
+ export function focusFirstElementChild(element: NullableElement) {
30
+ if (!element) return;
31
+
32
+ const first = element.firstElementChild as HTMLElement;
33
+
34
+ focusElement(first);
35
+ }
@@ -0,0 +1,5 @@
1
+ import { NullableElement } from './selectors';
2
+
3
+ export function getActiveElement() {
4
+ return document.activeElement as NullableElement;
5
+ }
@@ -0,0 +1,159 @@
1
+ import {
2
+ elementCountInRow,
3
+ elementIndexInRow,
4
+ getElementInNextRow,
5
+ getElementInPrevRow,
6
+ getElementInRow,
7
+ hasNextRow,
8
+ rowNumber
9
+ } from './elementPositionInRow';
10
+ import { focusElement } from './focusElement';
11
+ import {
12
+ allVisibleEmojis,
13
+ closestCategory,
14
+ firstVisibleEmoji,
15
+ lastVisibleEmoji,
16
+ nextCategory,
17
+ nextVisibleEmoji,
18
+ NullableElement,
19
+ prevCategory,
20
+ prevVisibleEmoji
21
+ } from './selectors';
22
+
23
+ export function focusFirstVisibleEmoji(parent: NullableElement) {
24
+ focusElement(firstVisibleEmoji(parent));
25
+ }
26
+
27
+ export function focusAndClickFirstVisibleEmoji(parent: NullableElement) {
28
+ const firstEmoji = firstVisibleEmoji(parent);
29
+
30
+ focusElement(firstEmoji);
31
+ firstEmoji?.click();
32
+ }
33
+
34
+ export function focusLastVisibleEmoji(parent: NullableElement) {
35
+ focusElement(lastVisibleEmoji(parent));
36
+ }
37
+
38
+ export function focusNextVisibleEmoji(element: NullableElement) {
39
+ if (!element) {
40
+ return;
41
+ }
42
+
43
+ const next = nextVisibleEmoji(element);
44
+
45
+ if (!next) {
46
+ return focusFirstVisibleEmoji(nextCategory(element));
47
+ }
48
+
49
+ focusElement(next);
50
+ }
51
+
52
+ export function focusPrevVisibleEmoji(element: NullableElement) {
53
+ if (!element) {
54
+ return;
55
+ }
56
+
57
+ const prev = prevVisibleEmoji(element);
58
+
59
+ if (!prev) {
60
+ return focusLastVisibleEmoji(prevCategory(element));
61
+ }
62
+
63
+ focusElement(prev);
64
+ }
65
+
66
+ export function focusVisibleEmojiOneRowUp(
67
+ element: NullableElement,
68
+ exitUp: () => void
69
+ ) {
70
+ if (!element) {
71
+ return;
72
+ }
73
+
74
+ const prev = visibleEmojiOneRowUp(element);
75
+
76
+ if (!prev) {
77
+ return exitUp();
78
+ }
79
+
80
+ focusElement(prev);
81
+ }
82
+
83
+ export function focusVisibleEmojiOneRowDown(element: NullableElement) {
84
+ if (!element) {
85
+ return;
86
+ }
87
+
88
+ const next = visibleEmojiOneRowDown(element);
89
+
90
+ return focusElement(next);
91
+ }
92
+
93
+ function visibleEmojiOneRowUp(element: HTMLElement) {
94
+ if (!element) {
95
+ return null;
96
+ }
97
+
98
+ const category = closestCategory(element);
99
+ const indexInRow = elementIndexInRow(category, element);
100
+ const row = rowNumber(category, element);
101
+ const countInRow = elementCountInRow(category, element);
102
+
103
+ if (row === 0) {
104
+ const prevVisibleCategory = prevCategory(category);
105
+
106
+ if (!prevVisibleCategory) {
107
+ return null;
108
+ }
109
+
110
+ return getElementInRow(
111
+ allVisibleEmojis(prevVisibleCategory),
112
+ -1, // last row
113
+ countInRow,
114
+ indexInRow
115
+ );
116
+ }
117
+
118
+ return getElementInPrevRow(
119
+ allVisibleEmojis(category),
120
+ row,
121
+ countInRow,
122
+ indexInRow
123
+ );
124
+ }
125
+
126
+ function visibleEmojiOneRowDown(element: HTMLElement) {
127
+ if (!element) {
128
+ return null;
129
+ }
130
+
131
+ const category = closestCategory(element);
132
+ const indexInRow = elementIndexInRow(category, element);
133
+ const row = rowNumber(category, element);
134
+ const countInRow = elementCountInRow(category, element);
135
+
136
+ if (!hasNextRow(category, element)) {
137
+ const nextVisibleCategory = nextCategory(category);
138
+
139
+ if (!nextVisibleCategory) {
140
+ return null;
141
+ }
142
+
143
+ return getElementInRow(
144
+ allVisibleEmojis(nextVisibleCategory),
145
+ 0,
146
+ countInRow,
147
+ indexInRow
148
+ );
149
+ }
150
+
151
+ const itemInNextRow = getElementInNextRow(
152
+ allVisibleEmojis(category),
153
+ row,
154
+ countInRow,
155
+ indexInRow
156
+ );
157
+
158
+ return itemInNextRow;
159
+ }
@@ -1,7 +1,8 @@
1
1
  import { scrollTo } from './scrollTo';
2
+ import { NullableElement } from './selectors';
2
3
 
3
4
  export function scrollCategoryIntoView(
4
- root: HTMLElement | null,
5
+ root: NullableElement,
5
6
  category: string
6
7
  ) {
7
8
  const $category = root?.querySelector(
@@ -1,4 +1,10 @@
1
- export function scrollTo(root: HTMLElement | null, top: number = 0) {
1
+ import { useCallback } from 'react';
2
+
3
+ import { useBodyRef } from '../components/context/ElementRefContext';
4
+
5
+ import { NullableElement } from './selectors';
6
+
7
+ export function scrollTo(root: NullableElement, top: number = 0) {
2
8
  const $eprBody = root ? root.querySelector('.epr-body') : null;
3
9
 
4
10
  if (!root || !$eprBody) {
@@ -9,3 +15,18 @@ export function scrollTo(root: HTMLElement | null, top: number = 0) {
9
15
  $eprBody.scrollTop = top + 1;
10
16
  });
11
17
  }
18
+
19
+ export function useScrollTo() {
20
+ const BodyRef = useBodyRef();
21
+
22
+ return useCallback(
23
+ (top: number) => {
24
+ requestAnimationFrame(() => {
25
+ if (BodyRef.current) {
26
+ BodyRef.current.scrollTop = top;
27
+ }
28
+ });
29
+ },
30
+ [BodyRef]
31
+ );
32
+ }