@koslibs/components 1.14.2 → 1.15.0
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
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @koslibs/components
|
|
2
2
|
|
|
3
|
+
## 1.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#18](https://github.com/koslibs/components/pull/18) [`d845497`](https://github.com/koslibs/components/commit/d845497056e4d88881bb25814102fe9fb4bb9e07) Thanks [@holypower777](https://github.com/holypower777)! - Добавлен Sidepanel.onExited?: VoidFunction для уведомления после анимации ухода и фактического закрытия dialog. При reduced motion callback вызывается сразу после закрытия. Повторное открытие отменяет прежнее завершение; первоначальное open=false, ререндер и принудительное размонтирование не вызывают callback. onClose остаётся запросом закрытия.
|
|
8
|
+
|
|
9
|
+
Добавлены тесты закрытия, отмены, reduced motion и StrictMode, а также документация и пример удаления экземпляра панели менеджером приложения через onExited.
|
|
10
|
+
|
|
3
11
|
## 1.14.2
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -18,6 +18,7 @@ function useDialogTransition(dialogRef, open, onExited) {
|
|
|
18
18
|
open,
|
|
19
19
|
onExited
|
|
20
20
|
});
|
|
21
|
+
const phase = useRef('closed');
|
|
21
22
|
useLayoutEffect(()=>{
|
|
22
23
|
latest.current = {
|
|
23
24
|
open,
|
|
@@ -29,7 +30,8 @@ function useDialogTransition(dialogRef, open, onExited) {
|
|
|
29
30
|
]);
|
|
30
31
|
const finishClosing = useCallback(()=>{
|
|
31
32
|
const dialog = dialogRef.current;
|
|
32
|
-
if (!dialog || latest.current.open) return;
|
|
33
|
+
if (!dialog || latest.current.open || 'closing' !== phase.current) return;
|
|
34
|
+
phase.current = 'closed';
|
|
33
35
|
if (dialog.open) dialog.close();
|
|
34
36
|
setActive(false);
|
|
35
37
|
latest.current.onExited();
|
|
@@ -42,7 +44,9 @@ function useDialogTransition(dialogRef, open, onExited) {
|
|
|
42
44
|
if (!dialog || !view) return;
|
|
43
45
|
let frame;
|
|
44
46
|
let timeout;
|
|
47
|
+
let cancelled = false;
|
|
45
48
|
if (open) {
|
|
49
|
+
phase.current = 'open';
|
|
46
50
|
if (!dialog.open) {
|
|
47
51
|
dialog.dataset.state = 'closed';
|
|
48
52
|
dialog.showModal();
|
|
@@ -52,13 +56,19 @@ function useDialogTransition(dialogRef, open, onExited) {
|
|
|
52
56
|
frame = view.requestAnimationFrame(()=>{
|
|
53
57
|
dialog.dataset.state = 'open';
|
|
54
58
|
});
|
|
55
|
-
} else if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
} else if ('closed' !== phase.current) {
|
|
60
|
+
phase.current = 'closing';
|
|
61
|
+
if (dialog.open && 'open' === dialog.dataset.state) {
|
|
62
|
+
dialog.dataset.state = 'closed';
|
|
63
|
+
const duration = transitionTime(dialog);
|
|
64
|
+
if (0 === duration) finishClosing();
|
|
65
|
+
else timeout = view.setTimeout(()=>{
|
|
66
|
+
if (!cancelled) finishClosing();
|
|
67
|
+
}, duration + 50);
|
|
68
|
+
} else finishClosing();
|
|
69
|
+
}
|
|
61
70
|
return ()=>{
|
|
71
|
+
cancelled = true;
|
|
62
72
|
if (void 0 !== frame) view.cancelAnimationFrame(frame);
|
|
63
73
|
if (void 0 !== timeout) view.clearTimeout(timeout);
|
|
64
74
|
};
|
|
@@ -70,6 +80,7 @@ function useDialogTransition(dialogRef, open, onExited) {
|
|
|
70
80
|
useLayoutEffect(()=>{
|
|
71
81
|
const dialog = dialogRef.current;
|
|
72
82
|
return ()=>{
|
|
83
|
+
phase.current = 'closed';
|
|
73
84
|
if (dialog?.open) dialog.close();
|
|
74
85
|
};
|
|
75
86
|
}, [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const Sidepanel: import("react").ForwardRefExoticComponent<Omit<import("react").DialogHTMLAttributes<HTMLDialogElement>, "open" | "onClose" | "onCancel"> & {
|
|
2
2
|
open: boolean;
|
|
3
3
|
onClose?: VoidFunction;
|
|
4
|
+
onExited?: VoidFunction;
|
|
4
5
|
escapeKeyDown?: boolean;
|
|
5
6
|
backdropClick?: boolean;
|
|
6
7
|
lockScroll?: boolean;
|
|
@@ -2,7 +2,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef, useCallback, useLayoutEffect, useState } from "react";
|
|
3
3
|
import { Portal } from "../Portal/index.js";
|
|
4
4
|
import { SidepanelSurface } from "./components/sidepanel-surface/index.js";
|
|
5
|
-
const Sidepanel = /*#__PURE__*/ forwardRef(({ open, getPortalContainer, ...restProps }, ref)=>{
|
|
5
|
+
const Sidepanel = /*#__PURE__*/ forwardRef(({ open, onExited, getPortalContainer, ...restProps }, ref)=>{
|
|
6
6
|
const [mounted, setMounted] = useState(open);
|
|
7
7
|
useLayoutEffect(()=>{
|
|
8
8
|
if (open) setMounted(true);
|
|
@@ -10,9 +10,10 @@ const Sidepanel = /*#__PURE__*/ forwardRef(({ open, getPortalContainer, ...restP
|
|
|
10
10
|
open
|
|
11
11
|
]);
|
|
12
12
|
const handleExited = useCallback(()=>{
|
|
13
|
-
|
|
13
|
+
setMounted(false);
|
|
14
|
+
onExited?.();
|
|
14
15
|
}, [
|
|
15
|
-
|
|
16
|
+
onExited
|
|
16
17
|
]);
|
|
17
18
|
return mounted ? /*#__PURE__*/ jsx(Portal, {
|
|
18
19
|
getPortalContainer: getPortalContainer,
|
|
@@ -10,6 +10,10 @@ export type SidepanelProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, 'open
|
|
|
10
10
|
* Запрос закрытия по крестику, Escape или бекдропу.
|
|
11
11
|
*/
|
|
12
12
|
onClose?: VoidFunction;
|
|
13
|
+
/**
|
|
14
|
+
* Вызывается после завершения закрытия панели.
|
|
15
|
+
*/
|
|
16
|
+
onExited?: VoidFunction;
|
|
13
17
|
/**
|
|
14
18
|
* Разрешает запрос закрытия по Escape.
|
|
15
19
|
* @default true
|