@eventuras/scribo 0.4.2

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.
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ */
9
+ .PlaygroundEditorTheme__ltr {
10
+ text-align: left;
11
+ }
12
+ .PlaygroundEditorTheme__rtl {
13
+ text-align: right;
14
+ }
15
+ .PlaygroundEditorTheme__paragraph {
16
+ margin: 0;
17
+ position: relative;
18
+ }
19
+ .PlaygroundEditorTheme__quote {
20
+ margin: 0;
21
+ margin-left: 20px;
22
+ margin-bottom: 10px;
23
+ font-size: 15px;
24
+ color: rgb(101, 103, 107);
25
+ border-left-color: rgb(206, 208, 212);
26
+ border-left-width: 4px;
27
+ border-left-style: solid;
28
+ padding-left: 16px;
29
+ }
30
+ .PlaygroundEditorTheme__h1 {
31
+ font-size: 32px;
32
+ line-height: 1.3;
33
+ padding-top: 2rem;
34
+ padding-bottom: 0.5rem;
35
+ color: #406280;
36
+ font-weight: 700;
37
+ margin: 0;
38
+ }
39
+ .PlaygroundEditorTheme__h2 {
40
+ font-size: 32px;
41
+ color: #406280;
42
+ padding-top: 2rem;
43
+ padding-bottom: 0.5rem;
44
+ font-weight: 700;
45
+ margin: 0;
46
+ }
47
+ .PlaygroundEditorTheme__h3 {
48
+ font-size: 24px;
49
+ color: #406280;
50
+ padding-top: 2rem;
51
+ padding-bottom: 0.5rem;
52
+ font-weight: 700;
53
+ margin: 0;
54
+ }
55
+ .PlaygroundEditorTheme__h4 {
56
+ font-size: 16px;
57
+ color: #406280;
58
+ padding-top: 1.5rem;
59
+ padding-bottom: 0rem;
60
+ font-weight: 700;
61
+ margin: 0;
62
+ }
63
+ .PlaygroundEditorTheme__indent {
64
+ --lexical-indent-base-value: 40px;
65
+ }
66
+ .PlaygroundEditorTheme__textBold {
67
+ font-weight: bold;
68
+ }
69
+ .PlaygroundEditorTheme__textItalic {
70
+ font-style: italic;
71
+ }
72
+ .PlaygroundEditorTheme__textUnderline {
73
+ text-decoration: underline;
74
+ }
75
+ .PlaygroundEditorTheme__textStrikethrough {
76
+ text-decoration: line-through;
77
+ }
78
+ .PlaygroundEditorTheme__textUnderlineStrikethrough {
79
+ text-decoration: underline line-through;
80
+ }
81
+ .PlaygroundEditorTheme__textSubscript {
82
+ font-size: 0.8em;
83
+ vertical-align: sub !important;
84
+ }
85
+ .PlaygroundEditorTheme__textSuperscript {
86
+ font-size: 0.8em;
87
+ vertical-align: super;
88
+ }
89
+ .PlaygroundEditorTheme__textCode {
90
+ background-color: rgb(240, 242, 245);
91
+ padding: 1px 0.25rem;
92
+ font-family: Menlo, Consolas, Monaco, monospace;
93
+ font-size: 94%;
94
+ }
95
+ .PlaygroundEditorTheme__hashtag {
96
+ background-color: rgba(88, 144, 255, 0.15);
97
+ border-bottom: 1px solid rgba(88, 144, 255, 0.3);
98
+ }
99
+ .PlaygroundEditorTheme__link {
100
+ color: rgb(33, 111, 219);
101
+ text-decoration: none;
102
+ }
103
+ .PlaygroundEditorTheme__link:hover {
104
+ text-decoration: underline;
105
+ cursor: pointer;
106
+ }
107
+ .PlaygroundEditorTheme__code {
108
+ background-color: rgb(240, 242, 245);
109
+ font-family: Menlo, Consolas, Monaco, monospace;
110
+ display: block;
111
+ padding: 8px 8px 8px 52px;
112
+ line-height: 1.53;
113
+ font-size: 13px;
114
+ margin: 0;
115
+ margin-top: 8px;
116
+ margin-bottom: 8px;
117
+ overflow-x: auto;
118
+ position: relative;
119
+ tab-size: 2;
120
+ }
121
+ .PlaygroundEditorTheme__code:before {
122
+ content: attr(data-gutter);
123
+ position: absolute;
124
+ background-color: #eee;
125
+ left: 0;
126
+ top: 0;
127
+ border-right: 1px solid #ccc;
128
+ padding: 8px;
129
+ color: #777;
130
+ white-space: pre-wrap;
131
+ text-align: right;
132
+ min-width: 25px;
133
+ }
134
+ .PlaygroundEditorTheme__characterLimit {
135
+ display: inline;
136
+ background-color: #ffbbbb !important;
137
+ }
138
+ .PlaygroundEditorTheme__ol1 {
139
+ padding: 0;
140
+ margin: 0;
141
+ list-style-position: inside;
142
+ }
143
+ .PlaygroundEditorTheme__ol2 {
144
+ padding: 0;
145
+ margin: 0;
146
+ list-style-type: upper-alpha;
147
+ list-style-position: inside;
148
+ }
149
+ .PlaygroundEditorTheme__ol3 {
150
+ padding: 0;
151
+ margin: 0;
152
+ list-style-type: lower-alpha;
153
+ list-style-position: inside;
154
+ }
155
+ .PlaygroundEditorTheme__ol4 {
156
+ padding: 0;
157
+ margin: 0;
158
+ list-style-type: upper-roman;
159
+ list-style-position: inside;
160
+ }
161
+ .PlaygroundEditorTheme__ol5 {
162
+ padding: 0;
163
+ margin: 0;
164
+ list-style-type: lower-roman;
165
+ list-style-position: inside;
166
+ }
167
+ .PlaygroundEditorTheme__ul {
168
+ padding: 0;
169
+ margin: 0;
170
+ list-style-position: inside;
171
+ }
172
+ .PlaygroundEditorTheme__listItem {
173
+ margin: 0 32px;
174
+ }
175
+ .PlaygroundEditorTheme__listItemChecked,
176
+ .PlaygroundEditorTheme__listItemUnchecked {
177
+ position: relative;
178
+ margin-left: 8px;
179
+ margin-right: 8px;
180
+ padding-left: 24px;
181
+ padding-right: 24px;
182
+ list-style-type: none;
183
+ outline: none;
184
+ }
185
+ .PlaygroundEditorTheme__listItemChecked {
186
+ text-decoration: line-through;
187
+ }
188
+ .PlaygroundEditorTheme__listItemUnchecked:before,
189
+ .PlaygroundEditorTheme__listItemChecked:before {
190
+ content: "";
191
+ width: 16px;
192
+ height: 16px;
193
+ top: 2px;
194
+ left: 0;
195
+ cursor: pointer;
196
+ display: block;
197
+ background-size: cover;
198
+ position: absolute;
199
+ }
200
+ .PlaygroundEditorTheme__listItemUnchecked[dir="rtl"]:before,
201
+ .PlaygroundEditorTheme__listItemChecked[dir="rtl"]:before {
202
+ left: auto;
203
+ right: 0;
204
+ }
205
+ .PlaygroundEditorTheme__listItemUnchecked:focus:before,
206
+ .PlaygroundEditorTheme__listItemChecked:focus:before {
207
+ box-shadow: 0 0 0 2px #a6cdfe;
208
+ border-radius: 2px;
209
+ }
210
+ .PlaygroundEditorTheme__listItemUnchecked:before {
211
+ border: 1px solid #999;
212
+ border-radius: 2px;
213
+ }
214
+ .PlaygroundEditorTheme__listItemChecked:before {
215
+ border: 1px solid rgb(61, 135, 245);
216
+ border-radius: 2px;
217
+ background-color: #3d87f5;
218
+ background-repeat: no-repeat;
219
+ }
220
+ .PlaygroundEditorTheme__listItemChecked:after {
221
+ content: "";
222
+ cursor: pointer;
223
+ border-color: #fff;
224
+ border-style: solid;
225
+ position: absolute;
226
+ display: block;
227
+ top: 6px;
228
+ width: 3px;
229
+ left: 7px;
230
+ right: 7px;
231
+ height: 6px;
232
+ transform: rotate(45deg);
233
+ border-width: 0 2px 2px 0;
234
+ }
235
+ .PlaygroundEditorTheme__nestedListItem {
236
+ list-style-type: none;
237
+ }
238
+ .PlaygroundEditorTheme__nestedListItem:before,
239
+ .PlaygroundEditorTheme__nestedListItem:after {
240
+ display: none;
241
+ }
242
+ .PlaygroundEditorTheme__tokenComment {
243
+ color: slategray;
244
+ }
245
+ .PlaygroundEditorTheme__tokenPunctuation {
246
+ color: #999;
247
+ }
248
+ .PlaygroundEditorTheme__tokenProperty {
249
+ color: #905;
250
+ }
251
+ .PlaygroundEditorTheme__tokenSelector {
252
+ color: #690;
253
+ }
254
+ .PlaygroundEditorTheme__tokenOperator {
255
+ color: #9a6e3a;
256
+ }
257
+ .PlaygroundEditorTheme__tokenAttr {
258
+ color: #07a;
259
+ }
260
+ .PlaygroundEditorTheme__tokenVariable {
261
+ color: #e90;
262
+ }
263
+ .PlaygroundEditorTheme__tokenFunction {
264
+ color: #dd4a68;
265
+ }
266
+ .PlaygroundEditorTheme__mark {
267
+ background: rgba(255, 212, 0, 0.14);
268
+ border-bottom: 2px solid rgba(255, 212, 0, 0.3);
269
+ padding-bottom: 2px;
270
+ }
271
+ .PlaygroundEditorTheme__markOverlap {
272
+ background: rgba(255, 212, 0, 0.3);
273
+ border-bottom: 2px solid rgba(255, 212, 0, 0.7);
274
+ }
275
+ .PlaygroundEditorTheme__mark.selected {
276
+ background: rgba(255, 212, 0, 0.5);
277
+ border-bottom: 2px solid rgba(255, 212, 0, 1);
278
+ }
279
+ .PlaygroundEditorTheme__markOverlap.selected {
280
+ background: rgba(255, 212, 0, 0.7);
281
+ border-bottom: 2px solid rgba(255, 212, 0, 0.7);
282
+ }
283
+ .PlaygroundEditorTheme__embedBlock {
284
+ user-select: none;
285
+ }
286
+ .PlaygroundEditorTheme__embedBlockFocus {
287
+ outline: 2px solid rgb(60, 132, 244);
288
+ }
289
+ .PlaygroundEditorTheme__layoutContaner {
290
+ display: grid;
291
+ gap: 10px;
292
+ margin: 10px 0;
293
+ }
294
+ .PlaygroundEditorTheme__layoutItem {
295
+ border: 1px dashed #ddd;
296
+ padding: 8px 16px;
297
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import "./EditorTheme.css";
10
+
11
+ import type { EditorThemeClasses } from "lexical";
12
+
13
+ const theme: EditorThemeClasses = {
14
+ blockCursor: "PlaygroundEditorTheme__blockCursor",
15
+ characterLimit: "PlaygroundEditorTheme__characterLimit",
16
+ code: "PlaygroundEditorTheme__code",
17
+ codeHighlight: {
18
+ atrule: "PlaygroundEditorTheme__tokenAttr",
19
+ attr: "PlaygroundEditorTheme__tokenAttr",
20
+ boolean: "PlaygroundEditorTheme__tokenProperty",
21
+ builtin: "PlaygroundEditorTheme__tokenSelector",
22
+ cdata: "PlaygroundEditorTheme__tokenComment",
23
+ char: "PlaygroundEditorTheme__tokenSelector",
24
+ class: "PlaygroundEditorTheme__tokenFunction",
25
+ "class-name": "PlaygroundEditorTheme__tokenFunction",
26
+ comment: "PlaygroundEditorTheme__tokenComment",
27
+ constant: "PlaygroundEditorTheme__tokenProperty",
28
+ deleted: "PlaygroundEditorTheme__tokenProperty",
29
+ doctype: "PlaygroundEditorTheme__tokenComment",
30
+ entity: "PlaygroundEditorTheme__tokenOperator",
31
+ function: "PlaygroundEditorTheme__tokenFunction",
32
+ important: "PlaygroundEditorTheme__tokenVariable",
33
+ inserted: "PlaygroundEditorTheme__tokenSelector",
34
+ keyword: "PlaygroundEditorTheme__tokenAttr",
35
+ namespace: "PlaygroundEditorTheme__tokenVariable",
36
+ number: "PlaygroundEditorTheme__tokenProperty",
37
+ operator: "PlaygroundEditorTheme__tokenOperator",
38
+ prolog: "PlaygroundEditorTheme__tokenComment",
39
+ property: "PlaygroundEditorTheme__tokenProperty",
40
+ punctuation: "PlaygroundEditorTheme__tokenPunctuation",
41
+ regex: "PlaygroundEditorTheme__tokenVariable",
42
+ selector: "PlaygroundEditorTheme__tokenSelector",
43
+ string: "PlaygroundEditorTheme__tokenSelector",
44
+ symbol: "PlaygroundEditorTheme__tokenProperty",
45
+ tag: "PlaygroundEditorTheme__tokenProperty",
46
+ url: "PlaygroundEditorTheme__tokenOperator",
47
+ variable: "PlaygroundEditorTheme__tokenVariable",
48
+ },
49
+ embedBlock: {
50
+ base: "PlaygroundEditorTheme__embedBlock",
51
+ focus: "PlaygroundEditorTheme__embedBlockFocus",
52
+ },
53
+ hashtag: "PlaygroundEditorTheme__hashtag",
54
+ heading: {
55
+ h1: "PlaygroundEditorTheme__h1",
56
+ h2: "PlaygroundEditorTheme__h2",
57
+ h3: "PlaygroundEditorTheme__h3",
58
+ h4: "PlaygroundEditorTheme__h4",
59
+ h5: "PlaygroundEditorTheme__h5",
60
+ h6: "PlaygroundEditorTheme__h6",
61
+ },
62
+ image: "editor-image",
63
+ indent: "PlaygroundEditorTheme__indent",
64
+ inlineImage: "inline-editor-image",
65
+ layoutContainer: "PlaygroundEditorTheme__layoutContaner",
66
+ layoutItem: "PlaygroundEditorTheme__layoutItem",
67
+ link: "PlaygroundEditorTheme__link",
68
+ list: {
69
+ listitem: "PlaygroundEditorTheme__listItem",
70
+ listitemChecked: "PlaygroundEditorTheme__listItemChecked",
71
+ listitemUnchecked: "PlaygroundEditorTheme__listItemUnchecked",
72
+ nested: {
73
+ listitem: "PlaygroundEditorTheme__nestedListItem",
74
+ },
75
+ olDepth: [
76
+ "PlaygroundEditorTheme__ol1",
77
+ "PlaygroundEditorTheme__ol2",
78
+ "PlaygroundEditorTheme__ol3",
79
+ "PlaygroundEditorTheme__ol4",
80
+ "PlaygroundEditorTheme__ol5",
81
+ ],
82
+ ul: "PlaygroundEditorTheme__ul",
83
+ },
84
+ ltr: "PlaygroundEditorTheme__ltr",
85
+ mark: "PlaygroundEditorTheme__mark",
86
+ markOverlap: "PlaygroundEditorTheme__markOverlap",
87
+ paragraph: "PlaygroundEditorTheme__paragraph",
88
+ quote: "PlaygroundEditorTheme__quote",
89
+ rtl: "PlaygroundEditorTheme__rtl",
90
+ table: "PlaygroundEditorTheme__table",
91
+ tableAddColumns: "PlaygroundEditorTheme__tableAddColumns",
92
+ tableAddRows: "PlaygroundEditorTheme__tableAddRows",
93
+ tableCell: "PlaygroundEditorTheme__tableCell",
94
+ tableCellActionButton: "PlaygroundEditorTheme__tableCellActionButton",
95
+ tableCellActionButtonContainer:
96
+ "PlaygroundEditorTheme__tableCellActionButtonContainer",
97
+ tableCellEditing: "PlaygroundEditorTheme__tableCellEditing",
98
+ tableCellHeader: "PlaygroundEditorTheme__tableCellHeader",
99
+ tableCellPrimarySelected: "PlaygroundEditorTheme__tableCellPrimarySelected",
100
+ tableCellResizer: "PlaygroundEditorTheme__tableCellResizer",
101
+ tableCellSelected: "PlaygroundEditorTheme__tableCellSelected",
102
+ tableCellSortedIndicator: "PlaygroundEditorTheme__tableCellSortedIndicator",
103
+ tableResizeRuler: "PlaygroundEditorTheme__tableCellResizeRuler",
104
+ tableSelected: "PlaygroundEditorTheme__tableSelected",
105
+ tableSelection: "PlaygroundEditorTheme__tableSelection",
106
+ text: {
107
+ bold: "PlaygroundEditorTheme__textBold",
108
+ code: "PlaygroundEditorTheme__textCode",
109
+ italic: "PlaygroundEditorTheme__textItalic",
110
+ strikethrough: "PlaygroundEditorTheme__textStrikethrough",
111
+ subscript: "PlaygroundEditorTheme__textSubscript",
112
+ superscript: "PlaygroundEditorTheme__textSuperscript",
113
+ underline: "PlaygroundEditorTheme__textUnderline",
114
+ underlineStrikethrough: "PlaygroundEditorTheme__textUnderlineStrikethrough",
115
+ },
116
+ };
117
+
118
+ export default theme;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ */
9
+ .ContentEditable__root {
10
+ border: 0;
11
+ font-size: 15px;
12
+ display: block;
13
+ position: relative;
14
+ outline: 0;
15
+ padding: 8px 28px 40px;
16
+ min-height: 150px;
17
+ }
18
+ @media (max-width: 1025px) {
19
+ .ContentEditable__root {
20
+ padding-left: 8px;
21
+ padding-right: 8px;
22
+ }
23
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type {JSX} from 'react';
9
+ import "./ContentEditable.css";
10
+
11
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
12
+
13
+ export default function LexicalContentEditable({
14
+ className,
15
+ }: {
16
+ className?: string;
17
+ }): JSX.Element {
18
+ return <ContentEditable className={className || "ContentEditable__root"} />;
19
+ }
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import type {JSX} from 'react';
10
+
11
+ import {isDOMNode} from 'lexical';
12
+ import * as React from 'react';
13
+ import {
14
+ ReactNode,
15
+ useCallback,
16
+ useEffect,
17
+ useMemo,
18
+ useRef,
19
+ useState,
20
+ } from 'react';
21
+ import {createPortal} from 'react-dom';
22
+
23
+ type DropDownContextType = {
24
+ registerItem: (ref: React.RefObject<HTMLButtonElement>) => void;
25
+ };
26
+
27
+ const DropDownContext = React.createContext<DropDownContextType | null>(null);
28
+
29
+ const dropDownPadding = 4;
30
+
31
+ export function DropDownItem({
32
+ children,
33
+ className,
34
+ onClick,
35
+ title,
36
+ }: Readonly<{
37
+ children: React.ReactNode;
38
+ className: string;
39
+ onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
40
+ title?: string;
41
+ }>) {
42
+ const ref = useRef<HTMLButtonElement>(null);
43
+
44
+ const dropDownContext = React.useContext(DropDownContext);
45
+
46
+ if (dropDownContext === null) {
47
+ throw new Error('DropDownItem must be used within a DropDown');
48
+ }
49
+
50
+ const {registerItem} = dropDownContext;
51
+
52
+ useEffect(() => {
53
+ if (ref && ref.current) {
54
+ registerItem(ref as React.RefObject<HTMLButtonElement>);
55
+ }
56
+ }, [ref, registerItem]);
57
+
58
+ return (
59
+ <button
60
+ className={className}
61
+ onClick={onClick}
62
+ ref={ref}
63
+ title={title}
64
+ type="button">
65
+ {children}
66
+ </button>
67
+ );
68
+ }
69
+
70
+ function DropDownItems({
71
+ children,
72
+ dropDownRef,
73
+ onClose,
74
+ }: {
75
+ children: React.ReactNode;
76
+ dropDownRef: React.Ref<HTMLDivElement>;
77
+ onClose: () => void;
78
+ }) {
79
+ const [items, setItems] = useState<React.RefObject<HTMLButtonElement>[]>();
80
+ const [highlightedItem, setHighlightedItem] =
81
+ useState<React.RefObject<HTMLButtonElement>>();
82
+
83
+ const registerItem = useCallback(
84
+ (itemRef: React.RefObject<HTMLButtonElement>) => {
85
+ setItems((prev) => (prev ? [...prev, itemRef] : [itemRef]));
86
+ },
87
+ [setItems],
88
+ );
89
+
90
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
91
+ if (!items) {
92
+ return;
93
+ }
94
+
95
+ const key = event.key;
96
+
97
+ if (['Escape', 'ArrowUp', 'ArrowDown', 'Tab'].includes(key)) {
98
+ event.preventDefault();
99
+ }
100
+
101
+ if (key === 'Escape' || key === 'Tab') {
102
+ onClose();
103
+ } else if (key === 'ArrowUp') {
104
+ setHighlightedItem((prev) => {
105
+ if (!prev) {
106
+ return items[0];
107
+ }
108
+ const index = items.indexOf(prev) - 1;
109
+ return items[index === -1 ? items.length - 1 : index];
110
+ });
111
+ } else if (key === 'ArrowDown') {
112
+ setHighlightedItem((prev) => {
113
+ if (!prev) {
114
+ return items[0];
115
+ }
116
+ return items[items.indexOf(prev) + 1];
117
+ });
118
+ }
119
+ };
120
+
121
+ const contextValue = useMemo(
122
+ () => ({
123
+ registerItem,
124
+ }),
125
+ [registerItem],
126
+ );
127
+
128
+ useEffect(() => {
129
+ if (items && !highlightedItem) {
130
+ setHighlightedItem(items[0]);
131
+ }
132
+
133
+ if (highlightedItem && highlightedItem.current) {
134
+ highlightedItem.current.focus();
135
+ }
136
+ }, [items, highlightedItem]);
137
+
138
+ return (
139
+ <DropDownContext.Provider value={contextValue}>
140
+ <div className="dropdown" ref={dropDownRef} onKeyDown={handleKeyDown}>
141
+ {children}
142
+ </div>
143
+ </DropDownContext.Provider>
144
+ );
145
+ }
146
+
147
+ export default function DropDown({
148
+ disabled = false,
149
+ buttonLabel,
150
+ buttonAriaLabel,
151
+ buttonClassName,
152
+ buttonIconClassName,
153
+ children,
154
+ stopCloseOnClickSelf,
155
+ }: Readonly<{
156
+ disabled?: boolean;
157
+ buttonAriaLabel?: string;
158
+ buttonClassName: string;
159
+ buttonIconClassName?: string;
160
+ buttonLabel?: string;
161
+ children: ReactNode;
162
+ stopCloseOnClickSelf?: boolean;
163
+ }>): JSX.Element {
164
+ const dropDownRef = useRef<HTMLDivElement>(null);
165
+ const buttonRef = useRef<HTMLButtonElement>(null);
166
+ const [showDropDown, setShowDropDown] = useState(false);
167
+
168
+ const handleClose = () => {
169
+ setShowDropDown(false);
170
+ if (buttonRef && buttonRef.current) {
171
+ buttonRef.current.focus();
172
+ }
173
+ };
174
+
175
+ useEffect(() => {
176
+ const button = buttonRef.current;
177
+ const dropDown = dropDownRef.current;
178
+
179
+ if (showDropDown && button !== null && dropDown !== null) {
180
+ const {top, left} = button.getBoundingClientRect();
181
+ dropDown.style.top = `${top + button.offsetHeight + dropDownPadding}px`;
182
+ dropDown.style.left = `${Math.min(
183
+ left,
184
+ window.innerWidth - dropDown.offsetWidth - 20,
185
+ )}px`;
186
+ }
187
+ }, [dropDownRef, buttonRef, showDropDown]);
188
+
189
+ useEffect(() => {
190
+ const button = buttonRef.current;
191
+
192
+ if (button !== null && showDropDown) {
193
+ const handle = (event: MouseEvent) => {
194
+ const target = event.target;
195
+ if (!isDOMNode(target)) {
196
+ return;
197
+ }
198
+ if (stopCloseOnClickSelf) {
199
+ if (dropDownRef.current && dropDownRef.current.contains(target)) {
200
+ return;
201
+ }
202
+ }
203
+ if (!button.contains(target)) {
204
+ setShowDropDown(false);
205
+ }
206
+ };
207
+ document.addEventListener('click', handle);
208
+
209
+ return () => {
210
+ document.removeEventListener('click', handle);
211
+ };
212
+ }
213
+ }, [dropDownRef, buttonRef, showDropDown, stopCloseOnClickSelf]);
214
+
215
+ useEffect(() => {
216
+ const handleButtonPositionUpdate = () => {
217
+ if (showDropDown) {
218
+ const button = buttonRef.current;
219
+ const dropDown = dropDownRef.current;
220
+ if (button !== null && dropDown !== null) {
221
+ const {top} = button.getBoundingClientRect();
222
+ const newPosition = top + button.offsetHeight + dropDownPadding;
223
+ if (newPosition !== dropDown.getBoundingClientRect().top) {
224
+ dropDown.style.top = `${newPosition}px`;
225
+ }
226
+ }
227
+ }
228
+ };
229
+
230
+ document.addEventListener('scroll', handleButtonPositionUpdate);
231
+
232
+ return () => {
233
+ document.removeEventListener('scroll', handleButtonPositionUpdate);
234
+ };
235
+ }, [buttonRef, dropDownRef, showDropDown]);
236
+
237
+ return (
238
+ <>
239
+ <button
240
+ type="button"
241
+ disabled={disabled}
242
+ aria-label={buttonAriaLabel || buttonLabel}
243
+ className={buttonClassName}
244
+ onClick={() => setShowDropDown(!showDropDown)}
245
+ ref={buttonRef}>
246
+ {buttonIconClassName && <span className={buttonIconClassName} />}
247
+ {buttonLabel && (
248
+ <span className="text dropdown-button-text">{buttonLabel}</span>
249
+ )}
250
+ <i className="chevron-down" />
251
+ </button>
252
+
253
+ {showDropDown &&
254
+ createPortal(
255
+ <DropDownItems dropDownRef={dropDownRef} onClose={handleClose}>
256
+ {children}
257
+ </DropDownItems>,
258
+ document.body,
259
+ )}
260
+ </>
261
+ );
262
+ }