@geneui/components 3.0.0-next-9bedd8e-12022025 → 3.0.0-next-5cecb1e-17022025

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/hooks/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as useDebounce } from "./useDebounceCallback";
2
2
  export { default as useEllipsisDetection } from "./useEllipsisDetection";
3
+ export { default as useScrollLock } from "./useScrollLock";
3
4
  export { default as useWindowSize } from "./useWindowSize";
@@ -0,0 +1 @@
1
+ export { default } from "./useScrollLock";
@@ -0,0 +1,7 @@
1
+ import { RefObject } from "react";
2
+ type TargetElement<T extends HTMLElement> = T | RefObject<T>;
3
+ declare const useScrollLock: <T extends HTMLElement>(target: TargetElement<T>) => {
4
+ lock: () => void;
5
+ unlock: () => void;
6
+ };
7
+ export default useScrollLock;
@@ -0,0 +1,342 @@
1
+ import React__default, { useRef, useCallback, useEffect, useState, useContext, useLayoutEffect } from 'react';
2
+ import { u as useFloating, p as platform, o as offset, f as flip, a as arrow, s as shift, b as autoUpdate, c as useDismiss, d as useClick, e as useRole, g as useInteractions, F as FloatingPortal } from './floating-ui.react-0485e4db.js';
3
+ import { S as SvgClose } from './Close-e8302008.js';
4
+ import { S as SvgInfoOutline } from './InfoOutline-dd2e89d9.js';
5
+ import { s as styleInject } from './style-inject.es-746bb8ed.js';
6
+ import { GeneUIDesignSystemContext } from './GeneUIProvider.js';
7
+ import Button from './Button.js';
8
+
9
+ var css_248z = ".popover{background-color:var(--guit-sem-color-background-neutral-1-nudge);border-top-left-radius:var(--guit-ref-radius-2xsmall);border-top-right-radius:var(--guit-ref-radius-2xsmall);filter:drop-shadow(var(--guit-sem-shadow-floating-2-position-x-1) var(--guit-sem-shadow-floating-2-position-y-1) var(--guit-sem-shadow-floating-2-blur-1) var(--guit-sem-color-floating-2-rgba-1)) drop-shadow(var(--guit-sem-shadow-floating-2-position-x-2) var(--guit-sem-shadow-floating-2-position-y-2) var(--guit-sem-shadow-floating-2-blur-2) var(--guit-sem-color-floating-2-rgba-2))}.popover:not(.popover_size_mobile){border-bottom-left-radius:var(--guit-ref-radius-2xsmall);border-bottom-right-radius:var(--guit-ref-radius-2xsmall)}.popover:not(.popover_size_mobile) .popover__arrow{position:absolute}.popover:not(.popover_size_mobile) .popover__arrowPath{fill:var(--guit-sem-color-background-neutral-1-nudge)}.popover_position_top .popover__arrow{transform:rotate(180deg)}.popover_position_bottom .popover__arrow{transform:rotate(0deg)}.popover_position_left .popover__arrow{transform:rotate(90deg)}.popover_position_right .popover__arrow{transform:rotate(-90deg)}.popover_size_xLarge{width:72rem}.popover_size_xLarge .popover__body{height:15.4rem}.popover_size_xLarge .popover__footer{height:6.4rem}.popover_size_large{width:48rem}.popover_size_medium{width:36rem}.popover_size_large .popover__body,.popover_size_medium .popover__body{height:17.2rem}.popover_size_large .popover__footer,.popover_size_medium .popover__footer{height:5.6rem}.popover_size_small{width:24rem}.popover_size_small .popover__body{height:8.8rem}.popover_size_small .popover__footer{height:5.6rem}.popover_size_mobile{min-width:32rem;width:100%}.popover_size_mobile .popover__body{height:17.2rem}.popover_size_mobile .popover__footer{height:6.4rem}.popover__header{border-bottom:var(--guit-ref-border-width-thin) var(--guit-ref-border-style-solid) var(--guit-sem-color-border-neutral-2);height:4.8rem;justify-content:space-between;padding-inline:var(--guit-ref-spacing-large)}.popover__header,.popover__title{align-items:center;column-gap:var(--guit-ref-spacing-xsmall);display:flex}.popover__title{color:var(--guit-sem-color-foreground-neutral-2);flex:1;overflow:hidden}.popover__title_icon{flex:0 0 auto}.popover__title_text{font-family:var(--guit-sem-font-label-medium-default-semibold-font-family);font-size:var(--guit-sem-font-label-medium-default-semibold-font-size);font-weight:var(--guit-sem-font-label-medium-default-semibold-font-weight);line-height:var(--guit-sem-font-label-medium-default-semibold-line-height)}.popover__close{flex:0 0 auto}.popover__body{padding-block:var(--guit-ref-spacing-large);padding-inline:var(--guit-ref-spacing-large)}.popover__content{height:100%;overflow-y:auto;width:100%}.popover__footer{border-top:var(--guit-ref-border-width-thin) var(--guit-ref-border-style-solid) var(--guit-sem-color-border-neutral-2);justify-content:space-between;padding-block:var(--guit-ref-spacing-large);padding-inline:var(--guit-ref-spacing-large)}.popover__footer,.popover__footer_buttons{align-items:center;column-gap:var(--guit-ref-spacing-xsmall);display:flex}.popover__footer_buttons{-webkit-margin-start:auto;margin-inline-start:auto}";
10
+ styleInject(css_248z);
11
+
12
+ const SCROLL_LOCK_CLASS = "scroll-lock";
13
+ const useScrollLock = (target) => {
14
+ const resolvedElement = target instanceof HTMLElement ? target : target.current;
15
+ const lockedRef = useRef(false);
16
+ const lock = useCallback(() => {
17
+ if (resolvedElement && !lockedRef.current) {
18
+ resolvedElement.classList.add(SCROLL_LOCK_CLASS);
19
+ lockedRef.current = true;
20
+ }
21
+ }, [resolvedElement]);
22
+ const unlock = useCallback(() => {
23
+ if (resolvedElement && lockedRef.current) {
24
+ resolvedElement.classList.remove(SCROLL_LOCK_CLASS);
25
+ lockedRef.current = false;
26
+ }
27
+ }, [resolvedElement]);
28
+ useEffect(() => {
29
+ return () => {
30
+ unlock();
31
+ };
32
+ }, [unlock]);
33
+ return { lock, unlock };
34
+ };
35
+
36
+ const getPositionRect = (currentPopoverRect, position) => {
37
+ const { width, height, top, right, bottom, left } = currentPopoverRect;
38
+ switch (position) {
39
+ case "top":
40
+ return {
41
+ top: top - width,
42
+ left,
43
+ bottom: top,
44
+ right: left + width
45
+ };
46
+ case "right":
47
+ return {
48
+ top,
49
+ left: right,
50
+ bottom: top + height,
51
+ right: right + width
52
+ };
53
+ case "bottom":
54
+ return {
55
+ top: bottom,
56
+ left,
57
+ bottom: bottom + height,
58
+ right: left + width
59
+ };
60
+ case "left":
61
+ return {
62
+ top,
63
+ left: left - width,
64
+ bottom: top + height,
65
+ right: left
66
+ };
67
+ case "top-start":
68
+ return {
69
+ top: top - height,
70
+ left: left - width / 2,
71
+ bottom: top,
72
+ right: left + width / 2
73
+ };
74
+ case "right-start":
75
+ return {
76
+ top: top - height / 2,
77
+ left: right,
78
+ bottom: top + height / 2,
79
+ right: right + width
80
+ };
81
+ case "bottom-start":
82
+ return {
83
+ top: bottom,
84
+ left: left - width / 2,
85
+ bottom: bottom + height,
86
+ right: left + width / 2
87
+ };
88
+ case "left-start":
89
+ return {
90
+ top: top - height / 2,
91
+ left: left - width,
92
+ bottom: top + height / 2,
93
+ right: left
94
+ };
95
+ case "top-end":
96
+ return {
97
+ top: top - height,
98
+ left: right - width,
99
+ bottom: top,
100
+ right
101
+ };
102
+ case "right-end":
103
+ return {
104
+ top: bottom - height,
105
+ left: right,
106
+ bottom,
107
+ right: right + width
108
+ };
109
+ case "bottom-end":
110
+ return {
111
+ top: bottom,
112
+ left: right - width,
113
+ bottom: bottom + height,
114
+ right
115
+ };
116
+ case "left-end":
117
+ return {
118
+ top: top + height / 2,
119
+ left: left - width,
120
+ bottom: bottom - height / 2,
121
+ right: left
122
+ };
123
+ default:
124
+ return currentPopoverRect;
125
+ }
126
+ };
127
+ const calculateOverlap = (rect1, rect2) => {
128
+ if (rect1.right <= rect2.left ||
129
+ rect1.left >= rect2.right ||
130
+ rect1.bottom <= rect2.top ||
131
+ rect1.top >= rect2.bottom) {
132
+ return 0;
133
+ }
134
+ const overlapWidth = Math.min(rect1.right, rect2.right) - Math.max(rect1.left, rect2.left);
135
+ const overlapHeight = Math.min(rect1.bottom, rect2.bottom) - Math.max(rect1.top, rect2.top);
136
+ return overlapWidth * overlapHeight;
137
+ };
138
+
139
+ const positions = [
140
+ "top",
141
+ "bottom",
142
+ "right",
143
+ "left",
144
+ "top-start",
145
+ "right-start",
146
+ "bottom-start",
147
+ "left-start",
148
+ "top-end",
149
+ "right-end",
150
+ "bottom-end",
151
+ "left-end"
152
+ ];
153
+ const correctPosition = {
154
+ "bottom-center": "bottom",
155
+ "bottom-left": "bottom-start",
156
+ "bottom-right": "bottom-end",
157
+ "left-bottom": "left-end",
158
+ "left-center": "left",
159
+ "left-top": "left-start",
160
+ "right-bottom": "right-end",
161
+ "right-center": "right",
162
+ "right-top": "right-start",
163
+ "top-center": "top",
164
+ "top-left": "top-start",
165
+ "top-right": "top-end",
166
+ auto: "auto"
167
+ };
168
+ const arrowPositions = {
169
+ "top-start": "left",
170
+ "top-end": "right",
171
+ "bottom-end": "right",
172
+ "bottom-start": "left"
173
+ };
174
+ const staticSides = {
175
+ top: "bottom",
176
+ right: "left",
177
+ bottom: "top",
178
+ left: "right"
179
+ };
180
+ /**
181
+ Popover displays additional content or information in an overlay box.
182
+ It appears on top of the main content when triggered by a user action,
183
+ such as a click or hover. Unlike tooltips, popovers can contain more
184
+ complex and interactive content, including text, images, and form elements.
185
+ */
186
+ const Popover = ({ size = "medium", position = "bottom-center", padding = 10, isOpen = false, alwaysShow, setProps, title, withArrow = true, children, disableReposition = true }) => {
187
+ const { lock: lockBodyScroll, unlock: unlockBodyScroll } = useScrollLock(document.body);
188
+ const [popoverOpened, setPopoverOpened] = useState(isOpen);
189
+ const { geneUIProviderRef } = useContext(GeneUIDesignSystemContext);
190
+ const [currentPosition, setCurrentPosition] = useState(correctPosition[position]);
191
+ const arrowRef = useRef(null);
192
+ const wosPosed = useRef(new Map());
193
+ const { refs, floatingStyles, context, middlewareData, placement } = useFloating({
194
+ open: popoverOpened,
195
+ onOpenChange: setPopoverOpened,
196
+ placement: currentPosition,
197
+ platform: Object.assign(Object.assign({}, platform), { isRTL: () => false }),
198
+ middleware: [
199
+ offset(padding),
200
+ flip({
201
+ mainAxis: position !== "auto" && !disableReposition,
202
+ fallbackAxisSideDirection: "none",
203
+ fallbackPlacements: position === "auto" ? [] : positions
204
+ }),
205
+ arrow({ element: arrowRef }),
206
+ shift({
207
+ mainAxis: false,
208
+ crossAxis: false,
209
+ limiter: {
210
+ fn: ({ x, y }) => ({
211
+ x: Math.max(0, x),
212
+ y: Math.max(0, y)
213
+ })
214
+ }
215
+ })
216
+ ],
217
+ whileElementsMounted: autoUpdate
218
+ });
219
+ useDismiss(context, {
220
+ outsidePressEvent: "click"
221
+ });
222
+ const click = useClick(context, {
223
+ event: "click"
224
+ });
225
+ const role = useRole(context);
226
+ const { getReferenceProps, getFloatingProps } = useInteractions([click, role]);
227
+ useEffect(() => {
228
+ setProps(Object.assign({ ref: refs.setReference }, getReferenceProps()));
229
+ }, [setProps, getReferenceProps, refs.setReference]);
230
+ const [currentDirection] = placement.split("-");
231
+ const offsetFromEdge = 8;
232
+ const middlewareArrowData = middlewareData.arrow;
233
+ const staticSide = staticSides[currentDirection];
234
+ const arrowPosition = arrowPositions[placement];
235
+ const getCorrectPosition = arrowPosition
236
+ ? { [arrowPosition]: offsetFromEdge }
237
+ : { insetInlineStart: middlewareArrowData === null || middlewareArrowData === void 0 ? void 0 : middlewareArrowData.x };
238
+ const styles = size === "mobile"
239
+ ? {
240
+ position: "fixed",
241
+ bottom: "0"
242
+ }
243
+ : floatingStyles;
244
+ const isShowPopover = alwaysShow || popoverOpened;
245
+ useEffect(() => {
246
+ if (size === "mobile" && isShowPopover) {
247
+ lockBodyScroll();
248
+ }
249
+ else {
250
+ unlockBodyScroll();
251
+ }
252
+ }, [size, isShowPopover]);
253
+ useEffect(() => {
254
+ return () => {
255
+ unlockBodyScroll();
256
+ };
257
+ }, []);
258
+ useLayoutEffect(() => {
259
+ if (position === "auto") {
260
+ setCurrentPosition(size === "small" ? "auto" : "bottom");
261
+ return;
262
+ }
263
+ setCurrentPosition(correctPosition[position]);
264
+ return () => {
265
+ wosPosed.current.clear();
266
+ };
267
+ }, [position, size]);
268
+ /* eslint consistent-return: off */
269
+ useEffect(() => {
270
+ if (!refs.floating.current || position !== "auto")
271
+ return;
272
+ const currentPopoverRect = refs.floating.current.getBoundingClientRect();
273
+ const otherPopovers = document.querySelectorAll(".popover");
274
+ let bestPosition = correctPosition[position];
275
+ let leastOverlap = Infinity;
276
+ let hasOverlap = false;
277
+ const preventPosition = correctPosition[currentPosition];
278
+ const updatePopoverPosition = () => {
279
+ positions.forEach((possiblePositions) => {
280
+ const rect = getPositionRect(currentPopoverRect, possiblePositions);
281
+ let overlap = 0;
282
+ otherPopovers.forEach((otherPopover) => {
283
+ if (otherPopover === refs.floating.current)
284
+ return;
285
+ const otherRect = otherPopover.getBoundingClientRect();
286
+ overlap += calculateOverlap(rect, otherRect);
287
+ });
288
+ if (overlap < leastOverlap) {
289
+ leastOverlap = overlap;
290
+ bestPosition = possiblePositions;
291
+ }
292
+ });
293
+ hasOverlap = leastOverlap > 0;
294
+ if (preventPosition !== bestPosition && !hasOverlap && !wosPosed.current.has(bestPosition)) {
295
+ wosPosed.current.set(bestPosition, true);
296
+ setCurrentPosition(bestPosition);
297
+ }
298
+ };
299
+ const checkInterval = setInterval(() => {
300
+ updatePopoverPosition();
301
+ if (!hasOverlap) {
302
+ clearInterval(checkInterval);
303
+ }
304
+ });
305
+ return () => {
306
+ clearInterval(checkInterval);
307
+ leastOverlap = Infinity;
308
+ };
309
+ }, [popoverOpened, refs.floating.current, placement, alwaysShow, position, currentPosition]);
310
+ const arrowOffsetFromEdge = staticSide === "left" || staticSide === "right" ? 7 : 11;
311
+ return (React__default.createElement(React__default.Fragment, null, isShowPopover && (React__default.createElement(FloatingPortal, { root: geneUIProviderRef.current },
312
+ React__default.createElement("div", Object.assign({ style: styles, className: `popover popover_size_${size} popover_position_${currentDirection}`, ref: refs.setFloating }, getFloatingProps()),
313
+ size !== "mobile" && (React__default.createElement("div", { ref: arrowRef, className: "popover__arrow", style: Object.assign(Object.assign({}, getCorrectPosition), { top: middlewareArrowData === null || middlewareArrowData === void 0 ? void 0 : middlewareArrowData.y, [staticSide]: arrowRef.current
314
+ ? `${-arrowRef.current.offsetWidth + arrowOffsetFromEdge}px`
315
+ : 0 }) },
316
+ React__default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "8", viewBox: "0 0 20 8", fill: "none" }, withArrow && (React__default.createElement("path", { d: "M8.75061 0.999513C9.48105 0.415163 10.519 0.415162 11.2494 0.999512L20 8H0L8.75061 0.999513Z", className: "popover__arrowPath" }))))),
317
+ React__default.createElement("div", { className: "popover__container" },
318
+ title && (React__default.createElement("div", { className: "popover__header" },
319
+ React__default.createElement("p", { className: "popover__title" },
320
+ React__default.createElement(SvgInfoOutline, { className: "popover__title_icon", size: 20 }),
321
+ React__default.createElement("span", { className: "popover__title_text ellipsis-text" }, title)),
322
+ React__default.createElement(Button, { Icon: SvgClose, size: "small", appearance: "secondary", displayType: "text", className: "popover__close", onClick: () => setPopoverOpened(false) }))),
323
+ children))))));
324
+ };
325
+
326
+ const PopoverBody = ({ children }) => {
327
+ return (React__default.createElement("div", { className: "popover__body" },
328
+ React__default.createElement("div", { className: "popover__content" },
329
+ children,
330
+ " ")));
331
+ };
332
+
333
+ const PopoverFooter = ({ children }) => {
334
+ return React__default.createElement("div", { className: "popover__footer" }, children);
335
+ };
336
+
337
+ const PopoverFooterActions = ({ children }) => {
338
+ return (React__default.createElement("div", { className: "popover__footer_buttons" },
339
+ React__default.createElement("div", { className: "popover__footer_buttons" }, children)));
340
+ };
341
+
342
+ export { Popover as P, PopoverBody as a, PopoverFooter as b, PopoverFooterActions as c, useScrollLock as u };
package/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { default as Pill, IPillProps } from "./components/atoms/Pill";
6
6
  export { default as Divider, IDividerProps } from "./components/atoms/Divider";
7
7
  export { default as Info, IInfoProps } from "./components/atoms/Info";
8
8
  export { default as Button, IButtonProps } from "./components/atoms/Button";
9
+ export { Popover, PopoverBody, PopoverFooter, PopoverFooterActions, IPopoverProps } from "./components/atoms/Popover";
9
10
  export { default as Badge, IBadgeProps } from "./components/atoms/Badge";
10
11
  export { default as Scrollbar, IScrollbarProps } from "./components/atoms/Scrollbar";
11
12
  export { Grid, Col, Row, IColProps, IRowProps } from "./components/atoms/Grid";
@@ -16,4 +17,5 @@ export { default as Tag, ITagProps } from "./components/molecules/Tag";
16
17
  export { default as GeneUIProvider, GeneUIDesignSystemContext, IGeneUIDesignSystemContext, IGeneUIProviderProps } from "./components/providers/GeneUIProvider";
17
18
  export { default as useDebounce } from "./hooks/useDebounceCallback";
18
19
  export { default as useEllipsisDetection } from "./hooks/useEllipsisDetection";
20
+ export { default as useScrollLock } from "./hooks/useScrollLock";
19
21
  export { default as useWindowSize } from "./hooks/useWindowSize";
package/index.js CHANGED
@@ -6,6 +6,7 @@ export { default as Pill } from './Pill.js';
6
6
  export { default as Divider } from './Divider.js';
7
7
  export { default as Info } from './Info.js';
8
8
  export { default as Button } from './Button.js';
9
+ export { P as Popover, a as PopoverBody, b as PopoverFooter, c as PopoverFooterActions, u as useScrollLock } from './index-18e397a9.js';
9
10
  export { default as Badge } from './Badge.js';
10
11
  export { default as Scrollbar } from './Scrollbar.js';
11
12
  export { Col, Grid, Row } from './Grid.js';
@@ -22,8 +23,11 @@ import './ArrowLeft-b88e2ba8.js';
22
23
  import './style-inject.es-746bb8ed.js';
23
24
  import './ErrorAlertFill-a0afebbf.js';
24
25
  import './WarningFill-143d0870.js';
25
- import 'prop-types';
26
+ import './InfoOutline-dd2e89d9.js';
27
+ import './floating-ui.react-0485e4db.js';
26
28
  import 'react-dom';
29
+ import './Close-e8302008.js';
30
+ import 'prop-types';
27
31
 
28
32
  const useWindowSize = () => {
29
33
  const [windowSize, setWindowSize] = useState({ width: 0, height: 0 });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@geneui/components",
3
3
  "description": "The Gene UI components library designed for BI tools",
4
- "version": "3.0.0-next-9bedd8e-12022025",
4
+ "version": "3.0.0-next-5cecb1e-17022025",
5
5
  "author": "SoftConstruct",
6
6
  "homepage": "https://github.com/softconstruct/gene-ui-components#readme",
7
7
  "repository": {