@via-profit/ui-kit 0.11.0 → 0.12.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/CHANGELOG.md +17 -0
- package/Popper/Popper.d.ts +1 -1
- package/Popper/Popper.js +37 -14
- package/Popper/usePopper.js +1 -1
- package/Tooltip/index.d.ts +6 -0
- package/Tooltip/index.js +9 -6
- package/esm/Popper/Popper.js +37 -14
- package/esm/Popper/usePopper.js +1 -1
- package/esm/Tooltip/index.js +9 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
|
|
5
5
|
## Не выпущено
|
|
6
6
|
|
|
7
|
+
## 0.12.1 — 2026-11-06
|
|
8
|
+
|
|
9
|
+
### Исправлено
|
|
10
|
+
|
|
11
|
+
- **Popper**: автоматически закрывается при удалении элемента привязки из DOM или при передаче `anchorElement={null}`.
|
|
12
|
+
|
|
13
|
+
## 0.12.0 — 2026-11-05
|
|
14
|
+
|
|
15
|
+
### Добавлено
|
|
16
|
+
|
|
17
|
+
- **Tooltip**: свойство `interactive` — указатель можно перевести на подсказку, чтобы выделить текст или нажать ссылку внутри. Без него подсказка не перехватывает указатель.
|
|
18
|
+
|
|
19
|
+
### Изменено
|
|
20
|
+
|
|
21
|
+
- **Tooltip**: по умолчанию подсказка не получает события указателя. Раньше указатель, попавший на подсказку, удерживал её открытой: в плотном списке подсказка нижней кнопки перекрывала кнопку выше, и навести на неё было нельзя. Если подсказку нужно выделять или нажимать в ней, передайте `interactive`;
|
|
22
|
+
- **Popper**: явный `pointerEvents` в `style` больше не затирается — скрытый поппер по-прежнему не перехватывает указатель.
|
|
23
|
+
|
|
7
24
|
## 0.11.0 — 2026-11-05
|
|
8
25
|
|
|
9
26
|
### Добавлено
|
package/Popper/Popper.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface PopperProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
16
16
|
readonly isOpen: boolean;
|
|
17
17
|
/**
|
|
18
18
|
* The HTML element to which the popper will be attached and positioned relative to.
|
|
19
|
-
*
|
|
19
|
+
* The popper closes if the element is removed from the DOM or becomes `null`.
|
|
20
20
|
*
|
|
21
21
|
* @required
|
|
22
22
|
* @example
|
package/Popper/Popper.js
CHANGED
|
@@ -33,11 +33,29 @@ const react_1 = __importDefault(require("react"));
|
|
|
33
33
|
const react_dom_1 = __importDefault(require("react-dom"));
|
|
34
34
|
const PopperContainer_1 = __importDefault(require("./PopperContainer"));
|
|
35
35
|
const usePopper_1 = require("./usePopper");
|
|
36
|
+
const getServerAnchorConnected = () => false;
|
|
36
37
|
__exportStar(require("./usePopper"), exports);
|
|
37
38
|
exports.PORTAL_ID = 'ui-kit-portal';
|
|
38
39
|
const Popper = (props, ref) => {
|
|
39
40
|
const { isOpen, children, anchorElement, zIndex, overrides, anchorPos = 'auto', positionStrategy = 'fixed', viewportMargin = 30, autoFlip = false, alternativePlacements, offset = 0, onAnchorPosChanged } = props, nativeProps = __rest(props, ["isOpen", "children", "anchorElement", "zIndex", "overrides", "anchorPos", "positionStrategy", "viewportMargin", "autoFlip", "alternativePlacements", "offset", "onAnchorPosChanged"]);
|
|
40
41
|
const [domLoaded, setDomLoaded] = react_1.default.useState(false);
|
|
42
|
+
const subscribeToAnchor = react_1.default.useCallback((onChange) => {
|
|
43
|
+
if (!isOpen || !anchorElement) {
|
|
44
|
+
return () => { };
|
|
45
|
+
}
|
|
46
|
+
const observer = new MutationObserver(onChange);
|
|
47
|
+
const options = { childList: true, subtree: true };
|
|
48
|
+
observer.observe(anchorElement.ownerDocument, options);
|
|
49
|
+
// Mutations inside a shadow root are not reported by the document observer.
|
|
50
|
+
let root = anchorElement.getRootNode();
|
|
51
|
+
while (root instanceof ShadowRoot) {
|
|
52
|
+
observer.observe(root, options);
|
|
53
|
+
root = root.host.getRootNode();
|
|
54
|
+
}
|
|
55
|
+
return () => observer.disconnect();
|
|
56
|
+
}, [isOpen, anchorElement]);
|
|
57
|
+
const getAnchorConnected = react_1.default.useCallback(() => isOpen && Boolean(anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.isConnected), [isOpen, anchorElement]);
|
|
58
|
+
const canOpen = react_1.default.useSyncExternalStore(subscribeToAnchor, getAnchorConnected, getServerAnchorConnected);
|
|
41
59
|
const { actualPlacement, style, popperRef, isVisible } = (0, usePopper_1.usePopper)({
|
|
42
60
|
anchorElement,
|
|
43
61
|
anchorPos,
|
|
@@ -46,7 +64,7 @@ const Popper = (props, ref) => {
|
|
|
46
64
|
offset,
|
|
47
65
|
viewportMargin,
|
|
48
66
|
alternativePlacements,
|
|
49
|
-
isOpen,
|
|
67
|
+
isOpen: canOpen,
|
|
50
68
|
});
|
|
51
69
|
const overridesMap = react_1.default.useMemo(() => ({
|
|
52
70
|
Container: (overrides === null || overrides === void 0 ? void 0 : overrides.Container) || PopperContainer_1.default,
|
|
@@ -81,19 +99,24 @@ const Popper = (props, ref) => {
|
|
|
81
99
|
}
|
|
82
100
|
}, [actualPlacement]);
|
|
83
101
|
const childrenMemo = react_1.default.useMemo(() => children, [children]);
|
|
84
|
-
const renderNode = react_1.default.useCallback(() =>
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
ref
|
|
102
|
+
const renderNode = react_1.default.useCallback(() => {
|
|
103
|
+
var _a, _b;
|
|
104
|
+
return (react_1.default.createElement(overridesMap.Container, Object.assign({}, nativeProps, { style: Object.assign(Object.assign(Object.assign(Object.assign({}, style), nativeProps.style), { opacity: isVisible ? 1 : 0,
|
|
105
|
+
// A hidden popper never intercepts the pointer; a visible one lets the caller opt out
|
|
106
|
+
pointerEvents: !isVisible ? 'none' : ((_b = (_a = nativeProps.style) === null || _a === void 0 ? void 0 : _a.pointerEvents) !== null && _b !== void 0 ? _b : 'auto') }), (positionStrategy === 'fixed' && {
|
|
107
|
+
position: 'fixed',
|
|
108
|
+
})), zIndex: zIndex, positionStrategy: positionStrategy, "data-popper-placement": actualPlacement, "data-popper-strategy": positionStrategy, ref: el => {
|
|
109
|
+
popperRef.current = el;
|
|
110
|
+
if (ref) {
|
|
111
|
+
if (typeof ref === 'function') {
|
|
112
|
+
ref(el);
|
|
113
|
+
}
|
|
114
|
+
if (typeof ref === 'object') {
|
|
115
|
+
ref.current = el;
|
|
116
|
+
}
|
|
94
117
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
118
|
+
} }), childrenMemo));
|
|
119
|
+
}, [
|
|
97
120
|
overridesMap,
|
|
98
121
|
nativeProps,
|
|
99
122
|
style,
|
|
@@ -106,7 +129,7 @@ const Popper = (props, ref) => {
|
|
|
106
129
|
popperRef,
|
|
107
130
|
ref,
|
|
108
131
|
]);
|
|
109
|
-
if (!
|
|
132
|
+
if (!canOpen) {
|
|
110
133
|
return null;
|
|
111
134
|
}
|
|
112
135
|
if (positionStrategy === 'fixed') {
|
package/Popper/usePopper.js
CHANGED
|
@@ -380,7 +380,7 @@ const usePopper = (props) => {
|
|
|
380
380
|
]);
|
|
381
381
|
// #region Calculate Position
|
|
382
382
|
const calculatePosition = react_1.default.useCallback(() => {
|
|
383
|
-
if (!anchorElement || !popperRef.current) {
|
|
383
|
+
if (!(anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.isConnected) || !popperRef.current) {
|
|
384
384
|
return;
|
|
385
385
|
}
|
|
386
386
|
const anchorRect = anchorElement.getBoundingClientRect();
|
package/Tooltip/index.d.ts
CHANGED
|
@@ -49,6 +49,12 @@ export type TooltipProps = {
|
|
|
49
49
|
* Default: false
|
|
50
50
|
*/
|
|
51
51
|
readonly describeChild?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Lets the pointer move onto the tooltip, e.g. to select its text or click a link inside.
|
|
54
|
+
* When `false`, the tooltip does not receive pointer events and cannot block the elements under it
|
|
55
|
+
* Default: false
|
|
56
|
+
*/
|
|
57
|
+
readonly interactive?: boolean;
|
|
52
58
|
/**
|
|
53
59
|
* Default: 8
|
|
54
60
|
*/
|
package/Tooltip/index.js
CHANGED
|
@@ -45,7 +45,7 @@ const setRef = (ref, value) => {
|
|
|
45
45
|
const isEmptyTitle = (title) => title === null || typeof title === 'undefined' || title === false || title === '';
|
|
46
46
|
const Tooltip = props => {
|
|
47
47
|
var _a;
|
|
48
|
-
const { title, children, placement = 'top', arrow = false, enterDelay = 400, leaveDelay = 100, isOpen, onOpenChange, describeChild = false, offset = 8, zIndex, id, overrides, } = props;
|
|
48
|
+
const { title, children, placement = 'top', arrow = false, enterDelay = 400, leaveDelay = 100, isOpen, onOpenChange, describeChild = false, interactive = false, offset = 8, zIndex, id, overrides, } = props;
|
|
49
49
|
const theme = (0, react_2.useTheme)();
|
|
50
50
|
const generatedId = `tooltip-${react_1.default.useId().replace(/:/g, '')}`;
|
|
51
51
|
const tooltipId = id || generatedId;
|
|
@@ -182,12 +182,15 @@ const Tooltip = props => {
|
|
|
182
182
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
183
183
|
anchor,
|
|
184
184
|
react_1.default.createElement(Popper_1.default, { isOpen: open, anchorElement: anchorElement, anchorPos: placement, autoFlip: true, offset: offset, viewportMargin: 8, zIndex: zIndex !== null && zIndex !== void 0 ? zIndex : theme.zIndex.modal + 1, onAnchorPosChanged: setActualPlacement, id: tooltipId, role: "tooltip",
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
// Without `interactive` the tooltip must not intercept the pointer: otherwise it
|
|
186
|
+
// blocks the elements under it and sticks under the cursor that moves across it
|
|
187
|
+
style: interactive ? undefined : { pointerEvents: 'none' }, onPointerEnter: interactive ? clearTimer : undefined, onPointerLeave: interactive
|
|
188
|
+
? event => {
|
|
189
|
+
if (isHoverPointer(event)) {
|
|
190
|
+
hide(leaveDelay);
|
|
191
|
+
}
|
|
189
192
|
}
|
|
190
|
-
|
|
193
|
+
: undefined },
|
|
191
194
|
react_1.default.createElement(overridesMap.Container, null,
|
|
192
195
|
title,
|
|
193
196
|
arrow && react_1.default.createElement(overridesMap.Arrow, { placement: actualPlacement })))));
|
package/esm/Popper/Popper.js
CHANGED
|
@@ -13,11 +13,29 @@ import React from 'react';
|
|
|
13
13
|
import ReactDOM from 'react-dom';
|
|
14
14
|
import Container from './PopperContainer';
|
|
15
15
|
import { usePopper } from './usePopper';
|
|
16
|
+
const getServerAnchorConnected = () => false;
|
|
16
17
|
export * from './usePopper';
|
|
17
18
|
export const PORTAL_ID = 'ui-kit-portal';
|
|
18
19
|
const Popper = (props, ref) => {
|
|
19
20
|
const { isOpen, children, anchorElement, zIndex, overrides, anchorPos = 'auto', positionStrategy = 'fixed', viewportMargin = 30, autoFlip = false, alternativePlacements, offset = 0, onAnchorPosChanged } = props, nativeProps = __rest(props, ["isOpen", "children", "anchorElement", "zIndex", "overrides", "anchorPos", "positionStrategy", "viewportMargin", "autoFlip", "alternativePlacements", "offset", "onAnchorPosChanged"]);
|
|
20
21
|
const [domLoaded, setDomLoaded] = React.useState(false);
|
|
22
|
+
const subscribeToAnchor = React.useCallback((onChange) => {
|
|
23
|
+
if (!isOpen || !anchorElement) {
|
|
24
|
+
return () => { };
|
|
25
|
+
}
|
|
26
|
+
const observer = new MutationObserver(onChange);
|
|
27
|
+
const options = { childList: true, subtree: true };
|
|
28
|
+
observer.observe(anchorElement.ownerDocument, options);
|
|
29
|
+
// Mutations inside a shadow root are not reported by the document observer.
|
|
30
|
+
let root = anchorElement.getRootNode();
|
|
31
|
+
while (root instanceof ShadowRoot) {
|
|
32
|
+
observer.observe(root, options);
|
|
33
|
+
root = root.host.getRootNode();
|
|
34
|
+
}
|
|
35
|
+
return () => observer.disconnect();
|
|
36
|
+
}, [isOpen, anchorElement]);
|
|
37
|
+
const getAnchorConnected = React.useCallback(() => isOpen && Boolean(anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.isConnected), [isOpen, anchorElement]);
|
|
38
|
+
const canOpen = React.useSyncExternalStore(subscribeToAnchor, getAnchorConnected, getServerAnchorConnected);
|
|
21
39
|
const { actualPlacement, style, popperRef, isVisible } = usePopper({
|
|
22
40
|
anchorElement,
|
|
23
41
|
anchorPos,
|
|
@@ -26,7 +44,7 @@ const Popper = (props, ref) => {
|
|
|
26
44
|
offset,
|
|
27
45
|
viewportMargin,
|
|
28
46
|
alternativePlacements,
|
|
29
|
-
isOpen,
|
|
47
|
+
isOpen: canOpen,
|
|
30
48
|
});
|
|
31
49
|
const overridesMap = React.useMemo(() => ({
|
|
32
50
|
Container: (overrides === null || overrides === void 0 ? void 0 : overrides.Container) || Container,
|
|
@@ -61,19 +79,24 @@ const Popper = (props, ref) => {
|
|
|
61
79
|
}
|
|
62
80
|
}, [actualPlacement]);
|
|
63
81
|
const childrenMemo = React.useMemo(() => children, [children]);
|
|
64
|
-
const renderNode = React.useCallback(() =>
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
ref
|
|
82
|
+
const renderNode = React.useCallback(() => {
|
|
83
|
+
var _a, _b;
|
|
84
|
+
return (React.createElement(overridesMap.Container, Object.assign({}, nativeProps, { style: Object.assign(Object.assign(Object.assign(Object.assign({}, style), nativeProps.style), { opacity: isVisible ? 1 : 0,
|
|
85
|
+
// A hidden popper never intercepts the pointer; a visible one lets the caller opt out
|
|
86
|
+
pointerEvents: !isVisible ? 'none' : ((_b = (_a = nativeProps.style) === null || _a === void 0 ? void 0 : _a.pointerEvents) !== null && _b !== void 0 ? _b : 'auto') }), (positionStrategy === 'fixed' && {
|
|
87
|
+
position: 'fixed',
|
|
88
|
+
})), zIndex: zIndex, positionStrategy: positionStrategy, "data-popper-placement": actualPlacement, "data-popper-strategy": positionStrategy, ref: el => {
|
|
89
|
+
popperRef.current = el;
|
|
90
|
+
if (ref) {
|
|
91
|
+
if (typeof ref === 'function') {
|
|
92
|
+
ref(el);
|
|
93
|
+
}
|
|
94
|
+
if (typeof ref === 'object') {
|
|
95
|
+
ref.current = el;
|
|
96
|
+
}
|
|
74
97
|
}
|
|
75
|
-
}
|
|
76
|
-
|
|
98
|
+
} }), childrenMemo));
|
|
99
|
+
}, [
|
|
77
100
|
overridesMap,
|
|
78
101
|
nativeProps,
|
|
79
102
|
style,
|
|
@@ -86,7 +109,7 @@ const Popper = (props, ref) => {
|
|
|
86
109
|
popperRef,
|
|
87
110
|
ref,
|
|
88
111
|
]);
|
|
89
|
-
if (!
|
|
112
|
+
if (!canOpen) {
|
|
90
113
|
return null;
|
|
91
114
|
}
|
|
92
115
|
if (positionStrategy === 'fixed') {
|
package/esm/Popper/usePopper.js
CHANGED
|
@@ -373,7 +373,7 @@ export const usePopper = (props) => {
|
|
|
373
373
|
]);
|
|
374
374
|
// #region Calculate Position
|
|
375
375
|
const calculatePosition = React.useCallback(() => {
|
|
376
|
-
if (!anchorElement || !popperRef.current) {
|
|
376
|
+
if (!(anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.isConnected) || !popperRef.current) {
|
|
377
377
|
return;
|
|
378
378
|
}
|
|
379
379
|
const anchorRect = anchorElement.getBoundingClientRect();
|
package/esm/Tooltip/index.js
CHANGED
|
@@ -40,7 +40,7 @@ const setRef = (ref, value) => {
|
|
|
40
40
|
const isEmptyTitle = (title) => title === null || typeof title === 'undefined' || title === false || title === '';
|
|
41
41
|
const Tooltip = props => {
|
|
42
42
|
var _a;
|
|
43
|
-
const { title, children, placement = 'top', arrow = false, enterDelay = 400, leaveDelay = 100, isOpen, onOpenChange, describeChild = false, offset = 8, zIndex, id, overrides, } = props;
|
|
43
|
+
const { title, children, placement = 'top', arrow = false, enterDelay = 400, leaveDelay = 100, isOpen, onOpenChange, describeChild = false, interactive = false, offset = 8, zIndex, id, overrides, } = props;
|
|
44
44
|
const theme = useTheme();
|
|
45
45
|
const generatedId = `tooltip-${React.useId().replace(/:/g, '')}`;
|
|
46
46
|
const tooltipId = id || generatedId;
|
|
@@ -177,12 +177,15 @@ const Tooltip = props => {
|
|
|
177
177
|
return (React.createElement(React.Fragment, null,
|
|
178
178
|
anchor,
|
|
179
179
|
React.createElement(Popper, { isOpen: open, anchorElement: anchorElement, anchorPos: placement, autoFlip: true, offset: offset, viewportMargin: 8, zIndex: zIndex !== null && zIndex !== void 0 ? zIndex : theme.zIndex.modal + 1, onAnchorPosChanged: setActualPlacement, id: tooltipId, role: "tooltip",
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
// Without `interactive` the tooltip must not intercept the pointer: otherwise it
|
|
181
|
+
// blocks the elements under it and sticks under the cursor that moves across it
|
|
182
|
+
style: interactive ? undefined : { pointerEvents: 'none' }, onPointerEnter: interactive ? clearTimer : undefined, onPointerLeave: interactive
|
|
183
|
+
? event => {
|
|
184
|
+
if (isHoverPointer(event)) {
|
|
185
|
+
hide(leaveDelay);
|
|
186
|
+
}
|
|
184
187
|
}
|
|
185
|
-
|
|
188
|
+
: undefined },
|
|
186
189
|
React.createElement(overridesMap.Container, null,
|
|
187
190
|
title,
|
|
188
191
|
arrow && React.createElement(overridesMap.Arrow, { placement: actualPlacement })))));
|