funda-ui 4.7.625 → 4.7.711
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/CascadingSelect/index.js +2 -2
- package/CascadingSelectE2E/index.js +2 -2
- package/Chatbox/index.js +3 -17
- package/Checkbox/index.js +3 -3
- package/ColorPicker/index.js +3 -18
- package/Date/index.js +3 -18
- package/EventCalendarTimeline/index.d.ts +2 -2
- package/EventCalendarTimeline/index.js +24 -6
- package/File/index.d.ts +9 -0
- package/File/index.js +245 -93
- package/Input/index.js +3 -18
- package/LiveSearch/index.js +3 -18
- package/NativeSelect/index.js +3 -3
- package/NumberInput/index.js +3 -18
- package/Popover/index.css +198 -0
- package/Popover/index.d.ts +4 -0
- package/Popover/index.js +1808 -0
- package/README.md +1 -0
- package/Radio/index.js +3 -3
- package/RangeSlider/index.js +3 -18
- package/SearchBar/index.js +3 -18
- package/Select/index.js +3 -2
- package/Switch/index.js +3 -3
- package/TagInput/index.css +31 -31
- package/TagInput/index.js +12 -23
- package/Textarea/index.js +3 -17
- package/Utils/usePageVisibility.d.ts +5 -0
- package/Utils/usePageVisibility.js +166 -0
- package/Utils/useSSE.d.ts +50 -0
- package/Utils/useSSE.js +235 -0
- package/all.d.ts +1 -0
- package/all.js +1 -0
- package/lib/cjs/CascadingSelect/index.js +2 -2
- package/lib/cjs/CascadingSelectE2E/index.js +2 -2
- package/lib/cjs/Chatbox/index.js +3 -17
- package/lib/cjs/Checkbox/index.js +3 -3
- package/lib/cjs/ColorPicker/index.js +3 -18
- package/lib/cjs/Date/index.js +3 -18
- package/lib/cjs/EventCalendarTimeline/index.d.ts +2 -2
- package/lib/cjs/EventCalendarTimeline/index.js +24 -6
- package/lib/cjs/File/index.d.ts +9 -0
- package/lib/cjs/File/index.js +245 -93
- package/lib/cjs/Input/index.js +3 -18
- package/lib/cjs/LiveSearch/index.js +3 -18
- package/lib/cjs/NativeSelect/index.js +3 -3
- package/lib/cjs/NumberInput/index.js +3 -18
- package/lib/cjs/Popover/index.d.ts +4 -0
- package/lib/cjs/Popover/index.js +1808 -0
- package/lib/cjs/Radio/index.js +3 -3
- package/lib/cjs/RangeSlider/index.js +3 -18
- package/lib/cjs/SearchBar/index.js +3 -18
- package/lib/cjs/Select/index.js +3 -2
- package/lib/cjs/Switch/index.js +3 -3
- package/lib/cjs/TagInput/index.js +12 -23
- package/lib/cjs/Textarea/index.js +3 -17
- package/lib/cjs/Utils/usePageVisibility.d.ts +5 -0
- package/lib/cjs/Utils/usePageVisibility.js +166 -0
- package/lib/cjs/Utils/useSSE.d.ts +50 -0
- package/lib/cjs/Utils/useSSE.js +235 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/css/Popover/index.css +198 -0
- package/lib/css/TagInput/index.css +31 -31
- package/lib/esm/CascadingSelect/index.tsx +2 -2
- package/lib/esm/CascadingSelectE2E/index.tsx +2 -2
- package/lib/esm/Checkbox/index.tsx +3 -3
- package/lib/esm/ColorPicker/index.tsx +4 -15
- package/lib/esm/EventCalendarTimeline/index.tsx +30 -13
- package/lib/esm/File/index.tsx +148 -23
- package/lib/esm/Input/index.tsx +6 -17
- package/lib/esm/ModalDialog/index.tsx +0 -1
- package/lib/esm/NativeSelect/index.tsx +3 -3
- package/lib/esm/NumberInput/index.tsx +7 -15
- package/lib/esm/Popover/Popover.tsx +251 -0
- package/lib/esm/Popover/PopoverClose.tsx +51 -0
- package/lib/esm/Popover/PopoverContent.tsx +72 -0
- package/lib/esm/Popover/PopoverTrigger.tsx +62 -0
- package/lib/esm/Popover/index.scss +272 -0
- package/lib/esm/Popover/index.tsx +4 -0
- package/lib/esm/Radio/index.tsx +3 -3
- package/lib/esm/SearchBar/index.tsx +8 -12
- package/lib/esm/Select/index.tsx +2 -2
- package/lib/esm/Switch/index.tsx +3 -3
- package/lib/esm/TagInput/index.scss +24 -24
- package/lib/esm/TagInput/index.tsx +13 -20
- package/lib/esm/Textarea/index.tsx +6 -14
- package/lib/esm/Utils/hooks/usePageVisibility.tsx +84 -0
- package/lib/esm/Utils/hooks/useSSE.tsx +134 -0
- package/lib/esm/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect, useImperativeHandle, forwardRef, useMemo, createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import useComId from 'funda-utils/dist/cjs/useComId';
|
|
4
|
+
import useClickOutside from 'funda-utils/dist/cjs/useClickOutside';
|
|
5
|
+
import {
|
|
6
|
+
getAbsolutePositionOfStage
|
|
7
|
+
} from 'funda-utils/dist/cjs/getElementProperty';
|
|
8
|
+
import { getElCSS } from 'funda-utils/dist/cjs/inputsCalculation';
|
|
9
|
+
|
|
10
|
+
// Context for Popover state management
|
|
11
|
+
export interface PopoverContextValue {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
setIsOpen: (open: boolean) => void;
|
|
14
|
+
triggerRef: React.RefObject<HTMLElement>;
|
|
15
|
+
contentRef: React.RefObject<HTMLDivElement>;
|
|
16
|
+
id: string;
|
|
17
|
+
direction?: string;
|
|
18
|
+
offset?: number;
|
|
19
|
+
exceededSidePosOffset?: number;
|
|
20
|
+
size?: string;
|
|
21
|
+
popupArrowColor?: number[];
|
|
22
|
+
popupContentStyle?: React.CSSProperties;
|
|
23
|
+
wrapperClassName?: string;
|
|
24
|
+
position: { x: string; y: string };
|
|
25
|
+
setPosition: (pos: { x: string; y: string }) => void;
|
|
26
|
+
popupArrowStyle?: React.CSSProperties;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const PopoverContext = createContext<PopoverContextValue | null>(null);
|
|
30
|
+
|
|
31
|
+
export const usePopoverContext = () => {
|
|
32
|
+
const context = useContext(PopoverContext);
|
|
33
|
+
if (!context) {
|
|
34
|
+
throw new Error('Popover components must be used within a Popover');
|
|
35
|
+
}
|
|
36
|
+
return context;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Main Popover component (container)
|
|
40
|
+
export type PopoverProps = {
|
|
41
|
+
/** The direction of the tip. Defaults to `top`. Possible values are: `top`, `top-right`, `top-left`, `bottom`, `bottom-right`, `bottom-left` */
|
|
42
|
+
direction?: string;
|
|
43
|
+
/** Position offset */
|
|
44
|
+
offset?: number;
|
|
45
|
+
/** Offset px that exceeds the far right or left side of the screen */
|
|
46
|
+
exceededSidePosOffset?: number;
|
|
47
|
+
/** The size of the content area. Defaults to `auto`. Possible values are: `auto`, `large`, `medium`, `small` */
|
|
48
|
+
size?: string;
|
|
49
|
+
/** Custom color for the popup arrow */
|
|
50
|
+
popupArrowColor?: number[];
|
|
51
|
+
/** Custom style for the popup content */
|
|
52
|
+
popupContentStyle?: React.CSSProperties;
|
|
53
|
+
/** The class name of the control wrapper. */
|
|
54
|
+
wrapperClassName?: string;
|
|
55
|
+
/** -- */
|
|
56
|
+
id?: string;
|
|
57
|
+
/** Controls whether the Popover is open (controlled mode) */
|
|
58
|
+
open?: boolean;
|
|
59
|
+
/** Callback fired when the open state changes (controlled mode) */
|
|
60
|
+
onOpenChange?: (open: boolean) => void;
|
|
61
|
+
children: React.ReactNode;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const Popover = forwardRef<any, PopoverProps>((props, ref) => {
|
|
65
|
+
const {
|
|
66
|
+
direction,
|
|
67
|
+
offset,
|
|
68
|
+
exceededSidePosOffset,
|
|
69
|
+
size,
|
|
70
|
+
popupArrowColor,
|
|
71
|
+
popupContentStyle,
|
|
72
|
+
wrapperClassName,
|
|
73
|
+
id,
|
|
74
|
+
open,
|
|
75
|
+
onOpenChange,
|
|
76
|
+
children
|
|
77
|
+
} = props;
|
|
78
|
+
|
|
79
|
+
const POS_OFFSET = Number(offset) || 4;
|
|
80
|
+
const EXCEEDED_SIDE_POS_OFFSET = Number(exceededSidePosOffset) || 15;
|
|
81
|
+
const uniqueID = useComId();
|
|
82
|
+
const idRes = id || uniqueID;
|
|
83
|
+
const triggerRef = useRef<HTMLElement>(null);
|
|
84
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
85
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState<boolean>(false);
|
|
86
|
+
const isControlled = typeof open === 'boolean';
|
|
87
|
+
const isOpen = isControlled ? !!open : uncontrolledOpen;
|
|
88
|
+
const [position, setPosition] = useState<{ x: string; y: string }>({
|
|
89
|
+
x: '0',
|
|
90
|
+
y: '0'
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const setIsOpen = (nextOpen: boolean) => {
|
|
94
|
+
if (!isControlled) {
|
|
95
|
+
setUncontrolledOpen(nextOpen);
|
|
96
|
+
}
|
|
97
|
+
if (typeof onOpenChange === 'function') {
|
|
98
|
+
onOpenChange(nextOpen);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const popupArrowStyle = useMemo(() => {
|
|
103
|
+
if (
|
|
104
|
+
typeof popupArrowColor !== 'undefined' &&
|
|
105
|
+
Array.isArray(popupArrowColor) &&
|
|
106
|
+
popupArrowColor.length === 4
|
|
107
|
+
) {
|
|
108
|
+
return {
|
|
109
|
+
'--cus-popover-arrow-bg-top': `url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2236px%22%20height%3D%2212px%22%3E%3Cpath%20fill%3D%22rgba%28${popupArrowColor[0]},%20${popupArrowColor[1]},%20${popupArrowColor[2]},%20${popupArrowColor[3]}%29%22%20transform%3D%22rotate%280%29%22%20d%3D%22M2.658,0.000%20C-13.615,0.000%2050.938,0.000%2034.662,0.000%20C28.662,0.000%2023.035,12.002%2018.660,12.002%20C14.285,12.002%208.594,0.000%202.658,0.000%20Z%22/%3E%3C/svg%3E") no-repeat`,
|
|
110
|
+
'--cus-popover-arrow-bg-bottom': `url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2236px%22%20height%3D%2212px%22%3E%3Cpath%20fill%3D%22rgba%28${popupArrowColor[0]},%20${popupArrowColor[1]},%20${popupArrowColor[2]},%20${popupArrowColor[3]}%29%22%20transform%3D%22rotate%28180%2018%206%29%22%20d%3D%22M2.658,0.000%20C-13.615,0.000%2050.938,0.000%2034.662,0.000%20C28.662,0.000%2023.035,12.002%2018.660,12.002%20C14.285,12.002%208.594,0.000%202.658,0.000%20Z%22/%3E%3C/svg%3E") no-repeat`,
|
|
111
|
+
'--cus-popover-arrow-bg-left': `url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2212px%22%20height%3D%2236px%22%3E%3Cpath%20fill%3D%22rgba%28${popupArrowColor[0]},%20${popupArrowColor[1]},%20${popupArrowColor[2]},%20${popupArrowColor[3]}%29%22%20transform%3D%22rotate%28-90%2018%2018%29%22%20d%3D%22M2.658,0.000%20C-13.615,0.000%2050.938,0.000%2034.662,0.000%20C28.662,0.000%2023.035,12.002%2018.660,12.002%20C14.285,12.002%208.594,0.000%202.658,0.000%20Z%22/%3E%3C/svg%3E") no-repeat`,
|
|
112
|
+
'--cus-popover-arrow-bg-right': `url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2212px%22%20height%3D%2236px%22%3E%3Cpath%20fill%3D%22rgba%28${popupArrowColor[0]},%20${popupArrowColor[1]},%20${popupArrowColor[2]},%20${popupArrowColor[3]}%29%22%20transform%3D%22rotate%2890%206%206%29%22%20d%3D%22M2.658,0.000%20C-13.615,0.000%2050.938,0.000%2034.662,0.000%20C28.662,0.000%2023.035,12.002%2018.660,12.002%20C14.285,12.002%208.594,0.000%202.658,0.000%20Z%22/%3E%3C/svg%3E") no-repeat`,
|
|
113
|
+
} as React.CSSProperties;
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}, [popupArrowColor]);
|
|
117
|
+
|
|
118
|
+
// Calculate position based on trigger element
|
|
119
|
+
const calculatePosition = () => {
|
|
120
|
+
const triggerEl = triggerRef.current;
|
|
121
|
+
if (!triggerEl) return;
|
|
122
|
+
|
|
123
|
+
const { x, y, width, height } = getAbsolutePositionOfStage(triggerEl);
|
|
124
|
+
const pos = direction || 'top';
|
|
125
|
+
|
|
126
|
+
if (pos.indexOf('top') >= 0) {
|
|
127
|
+
setPosition({
|
|
128
|
+
x: x + (width / 2) + 'px',
|
|
129
|
+
y: y - height - POS_OFFSET + 'px'
|
|
130
|
+
});
|
|
131
|
+
} else if (pos.indexOf('bottom') >= 0) {
|
|
132
|
+
setPosition({
|
|
133
|
+
x: x + (width / 2) + 'px',
|
|
134
|
+
y: y + height + POS_OFFSET + 'px'
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Expose show/hide methods to parent via ref
|
|
140
|
+
useImperativeHandle(ref, () => ({
|
|
141
|
+
show: () => {
|
|
142
|
+
calculatePosition();
|
|
143
|
+
setIsOpen(true);
|
|
144
|
+
},
|
|
145
|
+
hide: () => {
|
|
146
|
+
setIsOpen(false);
|
|
147
|
+
}
|
|
148
|
+
}), [direction, POS_OFFSET]);
|
|
149
|
+
|
|
150
|
+
// Click outside to close
|
|
151
|
+
useClickOutside({
|
|
152
|
+
enabled: isOpen && triggerRef.current !== null,
|
|
153
|
+
isOutside: (event: any) => {
|
|
154
|
+
// Prevent closing when clicking inside the popover wrapper
|
|
155
|
+
if (contentRef.current && contentRef.current.contains(event.target)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return (
|
|
159
|
+
triggerRef.current !== event.target &&
|
|
160
|
+
!triggerRef.current?.contains(event.target as HTMLElement)
|
|
161
|
+
);
|
|
162
|
+
},
|
|
163
|
+
handle: () => {
|
|
164
|
+
setIsOpen(false);
|
|
165
|
+
}
|
|
166
|
+
}, [isOpen]);
|
|
167
|
+
|
|
168
|
+
const exceededOffsetInit = () => {
|
|
169
|
+
// Determine whether it exceeds the far right or left side of the screen
|
|
170
|
+
//------------------
|
|
171
|
+
const _modalRef: any = contentRef.current;
|
|
172
|
+
if (_modalRef === null) return;
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
const _modalContent = _modalRef.querySelector('.cus-popover__content');
|
|
176
|
+
if (!_modalContent) return;
|
|
177
|
+
|
|
178
|
+
const _modalBox = _modalContent.getBoundingClientRect();
|
|
179
|
+
if (typeof _modalContent.dataset.offset === 'undefined' && _modalBox.left > 0) {
|
|
180
|
+
|
|
181
|
+
// Adjust the coordinates due to height
|
|
182
|
+
//------------------
|
|
183
|
+
const triggerEl: any = document.querySelector(`[data-overlay-id="${_modalRef.id}"]`);
|
|
184
|
+
if (triggerEl !== null) {
|
|
185
|
+
let pos = triggerEl.dataset.microtipPosition;
|
|
186
|
+
if (typeof pos === 'undefined') pos = 'top';
|
|
187
|
+
|
|
188
|
+
const _offsetY = _modalBox.height - getElCSS(_modalContent, 'font-size', true) - getElCSS(_modalContent, 'padding-top', true) - getElCSS(_modalContent, 'padding-bottom', true);
|
|
189
|
+
|
|
190
|
+
if (pos.indexOf('top') >= 0) {
|
|
191
|
+
_modalRef.style.transform = `translateY(-${_offsetY}px)`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 10 pixels is used to account for some bias in mobile devices
|
|
196
|
+
if ((_modalBox.right + 10) > window.innerWidth) {
|
|
197
|
+
const _modalOffsetPosition = _modalBox.right - window.innerWidth + EXCEEDED_SIDE_POS_OFFSET;
|
|
198
|
+
_modalContent.dataset.offset = _modalOffsetPosition;
|
|
199
|
+
_modalContent.style.marginLeft = `-${_modalOffsetPosition}px`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if ((_modalBox.left - 10) < 0) {
|
|
203
|
+
const _modalOffsetPosition = Math.abs(_modalBox.left) + EXCEEDED_SIDE_POS_OFFSET;
|
|
204
|
+
_modalContent.dataset.offset = _modalOffsetPosition;
|
|
205
|
+
_modalContent.style.marginLeft = `${_modalOffsetPosition}px`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Update position when opening
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (isOpen) {
|
|
213
|
+
calculatePosition();
|
|
214
|
+
}
|
|
215
|
+
}, [isOpen, direction, POS_OFFSET]);
|
|
216
|
+
|
|
217
|
+
// Prevent element data from being unable to be obtained when ref is null
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
exceededOffsetInit();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
const contextValue: PopoverContextValue = {
|
|
225
|
+
isOpen,
|
|
226
|
+
setIsOpen,
|
|
227
|
+
triggerRef,
|
|
228
|
+
contentRef,
|
|
229
|
+
id: idRes,
|
|
230
|
+
direction,
|
|
231
|
+
offset,
|
|
232
|
+
exceededSidePosOffset,
|
|
233
|
+
size,
|
|
234
|
+
popupArrowColor,
|
|
235
|
+
popupContentStyle,
|
|
236
|
+
wrapperClassName,
|
|
237
|
+
position,
|
|
238
|
+
setPosition,
|
|
239
|
+
popupArrowStyle
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<PopoverContext.Provider value={contextValue}>
|
|
244
|
+
{children}
|
|
245
|
+
</PopoverContext.Provider>
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
Popover.displayName = 'Popover';
|
|
250
|
+
|
|
251
|
+
export default Popover;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { combinedCls } from 'funda-utils/dist/cjs/cls';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import { usePopoverContext } from './Popover';
|
|
6
|
+
|
|
7
|
+
export type PopoverCloseProps = {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
} & React.HTMLAttributes<HTMLElement>;
|
|
12
|
+
|
|
13
|
+
const PopoverClose = forwardRef<HTMLElement, PopoverCloseProps>((props, ref) => {
|
|
14
|
+
const { asChild, className, children, onClick, ...rest } = props;
|
|
15
|
+
const { setIsOpen } = usePopoverContext();
|
|
16
|
+
|
|
17
|
+
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
|
18
|
+
if (typeof onClick === 'function') {
|
|
19
|
+
onClick(event);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!event.defaultPrevented) {
|
|
23
|
+
setIsOpen(false);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const closeProps: any = {
|
|
28
|
+
ref,
|
|
29
|
+
className: combinedCls('cus-popover__close', className),
|
|
30
|
+
onClick: handleClick,
|
|
31
|
+
...rest
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (asChild && React.isValidElement(children)) {
|
|
35
|
+
return React.cloneElement(children as React.ReactElement<any>, {
|
|
36
|
+
...closeProps,
|
|
37
|
+
...(children.props || {})
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<button type="button" {...closeProps}>
|
|
43
|
+
{children || 'Close'}
|
|
44
|
+
</button>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
PopoverClose.displayName = 'PopoverClose';
|
|
49
|
+
|
|
50
|
+
export default PopoverClose;
|
|
51
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import RootPortal from 'funda-root-portal';
|
|
3
|
+
import { combinedCls } from 'funda-utils/dist/cjs/cls';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import { usePopoverContext } from './Popover';
|
|
7
|
+
|
|
8
|
+
// PopoverContent component
|
|
9
|
+
export type PopoverContentProps = {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const PopoverContent = forwardRef<HTMLDivElement, PopoverContentProps>((props, ref) => {
|
|
15
|
+
const { children, className } = props;
|
|
16
|
+
const {
|
|
17
|
+
isOpen,
|
|
18
|
+
contentRef,
|
|
19
|
+
id,
|
|
20
|
+
direction,
|
|
21
|
+
size,
|
|
22
|
+
popupArrowStyle,
|
|
23
|
+
popupContentStyle,
|
|
24
|
+
wrapperClassName,
|
|
25
|
+
position
|
|
26
|
+
} = usePopoverContext();
|
|
27
|
+
|
|
28
|
+
const wrapperRef = (node: HTMLDivElement | null) => {
|
|
29
|
+
if (node) {
|
|
30
|
+
(contentRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
31
|
+
}
|
|
32
|
+
if (typeof ref === 'function') {
|
|
33
|
+
ref(node);
|
|
34
|
+
} else if (ref && node) {
|
|
35
|
+
(ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<RootPortal show={isOpen} containerClassName="Popover">
|
|
41
|
+
<div
|
|
42
|
+
ref={wrapperRef}
|
|
43
|
+
id={`cus-popover__wrapper-${id}`}
|
|
44
|
+
className={combinedCls(
|
|
45
|
+
'cus-popover__wrapper',
|
|
46
|
+
wrapperClassName,
|
|
47
|
+
'active'
|
|
48
|
+
)}
|
|
49
|
+
role="popover"
|
|
50
|
+
data-microtip-position={direction || 'top'}
|
|
51
|
+
data-microtip-size={size || 'auto'}
|
|
52
|
+
style={{
|
|
53
|
+
left: position.x,
|
|
54
|
+
top: position.y,
|
|
55
|
+
display: 'none',
|
|
56
|
+
...popupArrowStyle
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<div
|
|
60
|
+
className={combinedCls('cus-popover__content', className)}
|
|
61
|
+
style={popupContentStyle}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</RootPortal>
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
PopoverContent.displayName = 'PopoverContent';
|
|
71
|
+
|
|
72
|
+
export default PopoverContent;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import { usePopoverContext } from './Popover';
|
|
6
|
+
|
|
7
|
+
// PopoverTrigger component
|
|
8
|
+
export type PopoverTriggerProps = {
|
|
9
|
+
asChild?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const PopoverTrigger = forwardRef<HTMLElement, PopoverTriggerProps>((props, ref) => {
|
|
15
|
+
const { asChild, className, children } = props;
|
|
16
|
+
const { triggerRef, setIsOpen, isOpen, id, direction, size } = usePopoverContext();
|
|
17
|
+
|
|
18
|
+
const handleClick = (e: React.MouseEvent) => {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
setIsOpen(!isOpen);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const setRefs = (node: HTMLElement | null) => {
|
|
24
|
+
if (node) {
|
|
25
|
+
(triggerRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
26
|
+
}
|
|
27
|
+
if (typeof ref === 'function') {
|
|
28
|
+
ref(node);
|
|
29
|
+
} else if (ref && node) {
|
|
30
|
+
(ref as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const triggerProps: any = {
|
|
35
|
+
ref: setRefs,
|
|
36
|
+
'data-overlay-id': `cus-popover__wrapper-${id}`,
|
|
37
|
+
className: combinedCls(
|
|
38
|
+
'cus-popover__trigger',
|
|
39
|
+
clsWrite(className, 'd-inline-block')
|
|
40
|
+
),
|
|
41
|
+
'data-microtip-position': direction || 'top',
|
|
42
|
+
'data-microtip-size': size || 'auto',
|
|
43
|
+
onClick: handleClick
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (asChild && React.isValidElement(children)) {
|
|
47
|
+
return React.cloneElement(children as React.ReactElement<any>, {
|
|
48
|
+
...triggerProps,
|
|
49
|
+
...(children.props || {})
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div {...triggerProps}>
|
|
55
|
+
{children}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
PopoverTrigger.displayName = 'PopoverTrigger';
|
|
61
|
+
|
|
62
|
+
export default PopoverTrigger;
|