@pallasoft/ps-core-components 2.42.0 → 2.43.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/dist/components/ps-button.js +3 -1
- package/dist/components/ps-reflective-app-shell.d.ts +3 -1
- package/dist/components/ps-reflective-app-shell.js +10 -3
- package/dist/components/ps-scroll-restoration.d.ts +6 -0
- package/dist/components/ps-scroll-restoration.js +9 -0
- package/dist/constants/ps-scroll-restoration-config.d.ts +10 -0
- package/dist/constants/ps-scroll-restoration-config.js +13 -0
- package/dist/esm/components/ps-button.js +3 -1
- package/dist/esm/components/ps-reflective-app-shell.js +10 -3
- package/dist/esm/components/ps-scroll-restoration.js +5 -0
- package/dist/esm/constants/ps-scroll-restoration-config.js +10 -0
- package/dist/esm/hooks/use-ps-browser-layout-effect.js +2 -0
- package/dist/esm/hooks/use-ps-scroll-restoration.js +103 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/models/ps-scroll-offsets-data.js +1 -0
- package/dist/esm/models/ps-scroll-pending-save-data.js +1 -0
- package/dist/esm/models/ps-scroll-restoration-options.js +1 -0
- package/dist/esm/services/ps-scroll-restore-service.js +52 -0
- package/dist/esm/services/ps-scroll-storage-service.js +66 -0
- package/dist/esm/services/ps-scroll-target-service.js +29 -0
- package/dist/hooks/use-ps-browser-layout-effect.d.ts +2 -0
- package/dist/hooks/use-ps-browser-layout-effect.js +5 -0
- package/dist/hooks/use-ps-scroll-restoration.d.ts +2 -0
- package/dist/hooks/use-ps-scroll-restoration.js +107 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/models/ps-scroll-offsets-data.d.ts +1 -0
- package/dist/models/ps-scroll-offsets-data.js +2 -0
- package/dist/models/ps-scroll-pending-save-data.d.ts +4 -0
- package/dist/models/ps-scroll-pending-save-data.js +2 -0
- package/dist/models/ps-scroll-restoration-options.d.ts +7 -0
- package/dist/models/ps-scroll-restoration-options.js +2 -0
- package/dist/services/ps-scroll-restore-service.d.ts +3 -0
- package/dist/services/ps-scroll-restore-service.js +56 -0
- package/dist/services/ps-scroll-storage-service.d.ts +11 -0
- package/dist/services/ps-scroll-storage-service.js +70 -0
- package/dist/services/ps-scroll-target-service.d.ts +8 -0
- package/dist/services/ps-scroll-target-service.js +33 -0
- package/package.json +1 -1
|
@@ -5,5 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
6
|
const ps_button_classes_1 = require("../constants/ps-button-classes");
|
|
7
7
|
const ps_spinner_1 = require("./ps-spinner");
|
|
8
|
-
const PsButton = ({ children, className, disabled, isLabelHiddenOnMobile = false, isLoading = false, leadingIcon, loadingLabel, size = 'default', trailingIcon, type = 'button', variant = 'secondary', ...props }) => ((0, jsx_runtime_1.jsxs)("button", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_button_classes_1.PsButtonBaseClassName,
|
|
8
|
+
const PsButton = ({ children, className, disabled, isLabelHiddenOnMobile = false, isLoading = false, leadingIcon, loadingLabel, size = 'default', trailingIcon, type = 'button', variant = 'secondary', ...props }) => ((0, jsx_runtime_1.jsxs)("button", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_button_classes_1.PsButtonBaseClassName, isLoading && disabled !== true
|
|
9
|
+
? 'cursor-progress'
|
|
10
|
+
: 'disabled:cursor-not-allowed disabled:opacity-50', ps_button_classes_1.PsButtonSizeClassNames[size], isLabelHiddenOnMobile && leadingIcon ? 'min-w-11 max-sm:px-0' : '', ps_button_classes_1.PsButtonVariantClassNames[variant], className), "aria-busy": isLoading || undefined, disabled: disabled || isLoading, type: type, ...props, children: [isLoading ? ((0, jsx_runtime_1.jsx)(ps_spinner_1.PsSpinner, { className: 'absolute inset-0 m-auto', color: 'current', label: loadingLabel !== null && loadingLabel !== undefined ? loadingLabel : 'Loading', size: 'small' })) : null, (0, jsx_runtime_1.jsxs)("span", { "aria-hidden": isLoading || undefined, className: ps_design_system_1.PallasoftClassNameService.merge('inline-flex min-w-0 items-center gap-2', isLoading ? 'invisible' : ''), children: [leadingIcon ? (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: leadingIcon }) : null, (0, jsx_runtime_1.jsx)("span", { className: isLabelHiddenOnMobile && leadingIcon ? 'sr-only sm:not-sr-only sm:truncate' : 'truncate', children: children }), trailingIcon ? (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: trailingIcon }) : null] })] }));
|
|
9
11
|
exports.PsButton = PsButton;
|
|
@@ -4,9 +4,11 @@ export interface PsReflectiveAppShellProps {
|
|
|
4
4
|
className?: string;
|
|
5
5
|
contentClassName?: string;
|
|
6
6
|
frameClassName?: string;
|
|
7
|
+
scrollPreventReset?: boolean;
|
|
7
8
|
scrollResetKey?: string;
|
|
9
|
+
scrollRestoreKey?: string;
|
|
8
10
|
sidebar: ReactNode;
|
|
9
11
|
sidebarWidth?: string;
|
|
10
12
|
topbar: ReactNode;
|
|
11
13
|
}
|
|
12
|
-
export declare const PsReflectiveAppShell: ({ children, className, contentClassName, frameClassName, scrollResetKey, sidebar, sidebarWidth, topbar, }: PsReflectiveAppShellProps) => import("react").JSX.Element;
|
|
14
|
+
export declare const PsReflectiveAppShell: ({ children, className, contentClassName, frameClassName, scrollPreventReset, scrollResetKey, scrollRestoreKey, sidebar, sidebarWidth, topbar, }: PsReflectiveAppShellProps) => import("react").JSX.Element;
|
|
@@ -4,14 +4,21 @@ exports.PsReflectiveAppShell = void 0;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
|
-
const
|
|
7
|
+
const use_ps_scroll_restoration_1 = require("../hooks/use-ps-scroll-restoration");
|
|
8
|
+
const PsReflectiveAppShell = ({ children, className, contentClassName, frameClassName, scrollPreventReset, scrollResetKey, scrollRestoreKey, sidebar, sidebarWidth = '17rem', topbar, }) => {
|
|
8
9
|
const contentRef = (0, react_1.useRef)(null);
|
|
10
|
+
(0, use_ps_scroll_restoration_1.usePsScrollRestoration)({
|
|
11
|
+
elementRef: contentRef,
|
|
12
|
+
preventScrollReset: scrollPreventReset,
|
|
13
|
+
resetKey: scrollResetKey,
|
|
14
|
+
restoreKey: scrollRestoreKey,
|
|
15
|
+
});
|
|
9
16
|
(0, react_1.useLayoutEffect)(() => {
|
|
10
|
-
if (scrollResetKey === undefined)
|
|
17
|
+
if (scrollResetKey === undefined || scrollRestoreKey !== undefined)
|
|
11
18
|
return;
|
|
12
19
|
contentRef.current?.scrollTo({ top: 0, left: 0 });
|
|
13
20
|
window.scrollTo({ top: 0, left: 0 });
|
|
14
|
-
}, [scrollResetKey]);
|
|
21
|
+
}, [scrollResetKey, scrollRestoreKey]);
|
|
15
22
|
return ((0, jsx_runtime_1.jsx)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-reflective-frame h-[100dvh] overflow-hidden text-ink', className), children: (0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-liquid-strong grid h-full min-h-0 w-full overflow-hidden border-line/70 bg-white/70 backdrop-blur-2xl transition-[grid-template-columns] duration-200 dark:border-white/10 dark:bg-surface/70 md:grid-cols-[var(--ps-sidebar-width)_minmax(0,1fr)]', frameClassName), style: { ['--ps-sidebar-width']: sidebarWidth }, children: [sidebar, (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-h-0 min-w-0 flex-col bg-white/32 dark:bg-white/[0.02]', children: [topbar, (0, jsx_runtime_1.jsx)("main", { ref: contentRef, className: ps_design_system_1.PallasoftClassNameService.merge('ps-scrollbar min-h-0 flex-1 overflow-y-auto px-4 ps-page-enter sm:px-5 lg:px-6 xl:px-7', contentClassName), children: (0, jsx_runtime_1.jsx)("div", { className: 'py-5', children: children }) })] })] }) }));
|
|
16
23
|
};
|
|
17
24
|
exports.PsReflectiveAppShell = PsReflectiveAppShell;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScrollRestoration = void 0;
|
|
4
|
+
const use_ps_scroll_restoration_1 = require("../hooks/use-ps-scroll-restoration");
|
|
5
|
+
const PsScrollRestoration = ({ preventScrollReset, resetKey, restoreKey, }) => {
|
|
6
|
+
(0, use_ps_scroll_restoration_1.usePsScrollRestoration)({ preventScrollReset, resetKey, restoreKey });
|
|
7
|
+
return null;
|
|
8
|
+
};
|
|
9
|
+
exports.PsScrollRestoration = PsScrollRestoration;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const PsScrollRestorationConfig: {
|
|
2
|
+
readonly elementScope: "element:";
|
|
3
|
+
readonly inputEvents: readonly ["wheel", "touchstart", "keydown", "mousedown"];
|
|
4
|
+
readonly maxEntries: 100;
|
|
5
|
+
readonly persistThrottleMs: 150;
|
|
6
|
+
readonly restoreTimeoutMs: 4000;
|
|
7
|
+
readonly storageKey: "ps-scroll-restoration";
|
|
8
|
+
readonly tolerancePx: 2;
|
|
9
|
+
readonly windowScope: "window:";
|
|
10
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScrollRestorationConfig = void 0;
|
|
4
|
+
exports.PsScrollRestorationConfig = {
|
|
5
|
+
elementScope: 'element:',
|
|
6
|
+
inputEvents: ['wheel', 'touchstart', 'keydown', 'mousedown'],
|
|
7
|
+
maxEntries: 100,
|
|
8
|
+
persistThrottleMs: 150,
|
|
9
|
+
restoreTimeoutMs: 4000,
|
|
10
|
+
storageKey: 'ps-scroll-restoration',
|
|
11
|
+
tolerancePx: 2,
|
|
12
|
+
windowScope: 'window:',
|
|
13
|
+
};
|
|
@@ -2,4 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
3
|
import { PsButtonBaseClassName, PsButtonSizeClassNames, PsButtonVariantClassNames, } from '../constants/ps-button-classes.js';
|
|
4
4
|
import { PsSpinner } from './ps-spinner.js';
|
|
5
|
-
export const PsButton = ({ children, className, disabled, isLabelHiddenOnMobile = false, isLoading = false, leadingIcon, loadingLabel, size = 'default', trailingIcon, type = 'button', variant = 'secondary', ...props }) => (_jsxs("button", { className: PallasoftClassNameService.merge(PsButtonBaseClassName,
|
|
5
|
+
export const PsButton = ({ children, className, disabled, isLabelHiddenOnMobile = false, isLoading = false, leadingIcon, loadingLabel, size = 'default', trailingIcon, type = 'button', variant = 'secondary', ...props }) => (_jsxs("button", { className: PallasoftClassNameService.merge(PsButtonBaseClassName, isLoading && disabled !== true
|
|
6
|
+
? 'cursor-progress'
|
|
7
|
+
: 'disabled:cursor-not-allowed disabled:opacity-50', PsButtonSizeClassNames[size], isLabelHiddenOnMobile && leadingIcon ? 'min-w-11 max-sm:px-0' : '', PsButtonVariantClassNames[variant], className), "aria-busy": isLoading || undefined, disabled: disabled || isLoading, type: type, ...props, children: [isLoading ? (_jsx(PsSpinner, { className: 'absolute inset-0 m-auto', color: 'current', label: loadingLabel !== null && loadingLabel !== undefined ? loadingLabel : 'Loading', size: 'small' })) : null, _jsxs("span", { "aria-hidden": isLoading || undefined, className: PallasoftClassNameService.merge('inline-flex min-w-0 items-center gap-2', isLoading ? 'invisible' : ''), children: [leadingIcon ? _jsx("span", { className: 'shrink-0', children: leadingIcon }) : null, _jsx("span", { className: isLabelHiddenOnMobile && leadingIcon ? 'sr-only sm:not-sr-only sm:truncate' : 'truncate', children: children }), trailingIcon ? _jsx("span", { className: 'shrink-0', children: trailingIcon }) : null] })] }));
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useLayoutEffect, useRef } from 'react';
|
|
3
3
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
-
|
|
4
|
+
import { usePsScrollRestoration } from '../hooks/use-ps-scroll-restoration.js';
|
|
5
|
+
export const PsReflectiveAppShell = ({ children, className, contentClassName, frameClassName, scrollPreventReset, scrollResetKey, scrollRestoreKey, sidebar, sidebarWidth = '17rem', topbar, }) => {
|
|
5
6
|
const contentRef = useRef(null);
|
|
7
|
+
usePsScrollRestoration({
|
|
8
|
+
elementRef: contentRef,
|
|
9
|
+
preventScrollReset: scrollPreventReset,
|
|
10
|
+
resetKey: scrollResetKey,
|
|
11
|
+
restoreKey: scrollRestoreKey,
|
|
12
|
+
});
|
|
6
13
|
useLayoutEffect(() => {
|
|
7
|
-
if (scrollResetKey === undefined)
|
|
14
|
+
if (scrollResetKey === undefined || scrollRestoreKey !== undefined)
|
|
8
15
|
return;
|
|
9
16
|
contentRef.current?.scrollTo({ top: 0, left: 0 });
|
|
10
17
|
window.scrollTo({ top: 0, left: 0 });
|
|
11
|
-
}, [scrollResetKey]);
|
|
18
|
+
}, [scrollResetKey, scrollRestoreKey]);
|
|
12
19
|
return (_jsx("section", { className: PallasoftClassNameService.merge('ps-reflective-frame h-[100dvh] overflow-hidden text-ink', className), children: _jsxs("div", { className: PallasoftClassNameService.merge('ps-liquid-strong grid h-full min-h-0 w-full overflow-hidden border-line/70 bg-white/70 backdrop-blur-2xl transition-[grid-template-columns] duration-200 dark:border-white/10 dark:bg-surface/70 md:grid-cols-[var(--ps-sidebar-width)_minmax(0,1fr)]', frameClassName), style: { ['--ps-sidebar-width']: sidebarWidth }, children: [sidebar, _jsxs("div", { className: 'flex min-h-0 min-w-0 flex-col bg-white/32 dark:bg-white/[0.02]', children: [topbar, _jsx("main", { ref: contentRef, className: PallasoftClassNameService.merge('ps-scrollbar min-h-0 flex-1 overflow-y-auto px-4 ps-page-enter sm:px-5 lg:px-6 xl:px-7', contentClassName), children: _jsx("div", { className: 'py-5', children: children }) })] })] }) }));
|
|
13
20
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const PsScrollRestorationConfig = {
|
|
2
|
+
elementScope: 'element:',
|
|
3
|
+
inputEvents: ['wheel', 'touchstart', 'keydown', 'mousedown'],
|
|
4
|
+
maxEntries: 100,
|
|
5
|
+
persistThrottleMs: 150,
|
|
6
|
+
restoreTimeoutMs: 4000,
|
|
7
|
+
storageKey: 'ps-scroll-restoration',
|
|
8
|
+
tolerancePx: 2,
|
|
9
|
+
windowScope: 'window:',
|
|
10
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { PsScrollRestoreService } from '../services/ps-scroll-restore-service.js';
|
|
3
|
+
import { PsScrollStorageService } from '../services/ps-scroll-storage-service.js';
|
|
4
|
+
import { PsScrollTargetService } from '../services/ps-scroll-target-service.js';
|
|
5
|
+
import { usePsBrowserLayoutEffect } from './use-ps-browser-layout-effect.js';
|
|
6
|
+
export const usePsScrollRestoration = ({ elementRef, preventScrollReset, resetKey, restoreKey, }) => {
|
|
7
|
+
const keyRef = useRef(restoreKey);
|
|
8
|
+
const hasSavedRef = useRef(false);
|
|
9
|
+
const appliedKeyRef = useRef(undefined);
|
|
10
|
+
const appliedResetKeyRef = useRef(undefined);
|
|
11
|
+
const isRestoringRef = useRef(false);
|
|
12
|
+
const pendingRef = useRef(null);
|
|
13
|
+
const isEnabled = restoreKey !== undefined;
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!isEnabled ||
|
|
16
|
+
typeof window === 'undefined' ||
|
|
17
|
+
typeof history === 'undefined' ||
|
|
18
|
+
!('scrollRestoration' in history))
|
|
19
|
+
return;
|
|
20
|
+
const previous = history.scrollRestoration;
|
|
21
|
+
history.scrollRestoration = 'manual';
|
|
22
|
+
return () => {
|
|
23
|
+
history.scrollRestoration = previous;
|
|
24
|
+
};
|
|
25
|
+
}, [isEnabled]);
|
|
26
|
+
usePsBrowserLayoutEffect(() => {
|
|
27
|
+
keyRef.current = restoreKey;
|
|
28
|
+
if (typeof window === 'undefined')
|
|
29
|
+
return;
|
|
30
|
+
PsScrollStorageService.flush();
|
|
31
|
+
if (restoreKey === undefined)
|
|
32
|
+
return;
|
|
33
|
+
const element = elementRef === undefined ? null : elementRef.current;
|
|
34
|
+
const saved = PsScrollStorageService.read(PsScrollTargetService.storageKey(element, restoreKey));
|
|
35
|
+
hasSavedRef.current = saved !== undefined;
|
|
36
|
+
if (saved === undefined)
|
|
37
|
+
return;
|
|
38
|
+
isRestoringRef.current = true;
|
|
39
|
+
const stop = PsScrollRestoreService.start(element, saved, () => {
|
|
40
|
+
isRestoringRef.current = false;
|
|
41
|
+
});
|
|
42
|
+
return () => {
|
|
43
|
+
stop();
|
|
44
|
+
isRestoringRef.current = false;
|
|
45
|
+
};
|
|
46
|
+
}, [restoreKey, elementRef]);
|
|
47
|
+
usePsBrowserLayoutEffect(() => {
|
|
48
|
+
const previousKey = appliedKeyRef.current;
|
|
49
|
+
const previousResetKey = appliedResetKeyRef.current;
|
|
50
|
+
appliedKeyRef.current = restoreKey;
|
|
51
|
+
appliedResetKeyRef.current = resetKey;
|
|
52
|
+
if (restoreKey === undefined || typeof window === 'undefined')
|
|
53
|
+
return;
|
|
54
|
+
const hasChanged = previousResetKey !== undefined && previousResetKey !== resetKey;
|
|
55
|
+
const isRestored = previousKey !== restoreKey && hasSavedRef.current;
|
|
56
|
+
if (!hasChanged || isRestored || preventScrollReset === true)
|
|
57
|
+
return;
|
|
58
|
+
PsScrollTargetService.scrollTo(elementRef === undefined ? null : elementRef.current, 0);
|
|
59
|
+
}, [resetKey, restoreKey, elementRef, preventScrollReset]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!isEnabled || typeof window === 'undefined')
|
|
62
|
+
return;
|
|
63
|
+
const element = elementRef === undefined ? null : elementRef.current;
|
|
64
|
+
const target = PsScrollTargetService.eventTarget(element);
|
|
65
|
+
let frame = 0;
|
|
66
|
+
const flushPending = () => {
|
|
67
|
+
const pending = pendingRef.current;
|
|
68
|
+
pendingRef.current = null;
|
|
69
|
+
if (pending !== null)
|
|
70
|
+
PsScrollStorageService.write(pending.key, pending.top);
|
|
71
|
+
};
|
|
72
|
+
const onScroll = () => {
|
|
73
|
+
const key = keyRef.current;
|
|
74
|
+
if (key === undefined || isRestoringRef.current)
|
|
75
|
+
return;
|
|
76
|
+
pendingRef.current = {
|
|
77
|
+
key: PsScrollTargetService.storageKey(element, key),
|
|
78
|
+
top: PsScrollTargetService.top(element),
|
|
79
|
+
};
|
|
80
|
+
if (frame !== 0)
|
|
81
|
+
return;
|
|
82
|
+
frame = window.requestAnimationFrame(() => {
|
|
83
|
+
frame = 0;
|
|
84
|
+
flushPending();
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
const onPageHide = () => {
|
|
88
|
+
const key = keyRef.current;
|
|
89
|
+
pendingRef.current = null;
|
|
90
|
+
if (key !== undefined && !isRestoringRef.current)
|
|
91
|
+
PsScrollStorageService.write(PsScrollTargetService.storageKey(element, key), PsScrollTargetService.top(element));
|
|
92
|
+
PsScrollStorageService.flush();
|
|
93
|
+
};
|
|
94
|
+
target.addEventListener('scroll', onScroll, { passive: true });
|
|
95
|
+
window.addEventListener('pagehide', onPageHide);
|
|
96
|
+
return () => {
|
|
97
|
+
target.removeEventListener('scroll', onScroll);
|
|
98
|
+
window.removeEventListener('pagehide', onPageHide);
|
|
99
|
+
window.cancelAnimationFrame(frame);
|
|
100
|
+
flushPending();
|
|
101
|
+
};
|
|
102
|
+
}, [isEnabled, elementRef]);
|
|
103
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -192,3 +192,13 @@ export * from './components/ps-divider.js';
|
|
|
192
192
|
export * from './models/ps-divider-orientation.js';
|
|
193
193
|
export * from './components/ps-rating-display.js';
|
|
194
194
|
export * from './constants/ps-rating-display-size.js';
|
|
195
|
+
export * from './constants/ps-scroll-restoration-config.js';
|
|
196
|
+
export * from './models/ps-scroll-offsets-data.js';
|
|
197
|
+
export * from './models/ps-scroll-restoration-options.js';
|
|
198
|
+
export * from './services/ps-scroll-storage-service.js';
|
|
199
|
+
export * from './services/ps-scroll-target-service.js';
|
|
200
|
+
export * from './services/ps-scroll-restore-service.js';
|
|
201
|
+
export * from './hooks/use-ps-scroll-restoration.js';
|
|
202
|
+
export * from './components/ps-scroll-restoration.js';
|
|
203
|
+
export * from './hooks/use-ps-browser-layout-effect.js';
|
|
204
|
+
export * from './models/ps-scroll-pending-save-data.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { PsScrollRestorationConfig } from '../constants/ps-scroll-restoration-config.js';
|
|
2
|
+
import { PsScrollTargetService } from './ps-scroll-target-service.js';
|
|
3
|
+
export class PsScrollRestoreService {
|
|
4
|
+
static start(element, top, onDone) {
|
|
5
|
+
let isDone = false;
|
|
6
|
+
let observer = null;
|
|
7
|
+
let mutations = null;
|
|
8
|
+
let timer = 0;
|
|
9
|
+
const finish = () => {
|
|
10
|
+
if (isDone)
|
|
11
|
+
return;
|
|
12
|
+
isDone = true;
|
|
13
|
+
window.clearTimeout(timer);
|
|
14
|
+
observer?.disconnect();
|
|
15
|
+
mutations?.disconnect();
|
|
16
|
+
for (const name of PsScrollRestorationConfig.inputEvents)
|
|
17
|
+
window.removeEventListener(name, onInput, true);
|
|
18
|
+
onDone();
|
|
19
|
+
};
|
|
20
|
+
const onInput = () => finish();
|
|
21
|
+
const attempt = () => {
|
|
22
|
+
if (isDone)
|
|
23
|
+
return;
|
|
24
|
+
if (PsScrollTargetService.maxTop(element) >= top - PsScrollRestorationConfig.tolerancePx) {
|
|
25
|
+
PsScrollTargetService.scrollTo(element, top);
|
|
26
|
+
finish();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
for (const name of PsScrollRestorationConfig.inputEvents)
|
|
30
|
+
window.addEventListener(name, onInput, { capture: true, passive: true });
|
|
31
|
+
timer = window.setTimeout(() => {
|
|
32
|
+
if (isDone)
|
|
33
|
+
return;
|
|
34
|
+
PsScrollTargetService.scrollTo(element, top);
|
|
35
|
+
finish();
|
|
36
|
+
}, PsScrollRestorationConfig.restoreTimeoutMs);
|
|
37
|
+
attempt();
|
|
38
|
+
if (!isDone && typeof MutationObserver !== 'undefined') {
|
|
39
|
+
mutations = new MutationObserver(attempt);
|
|
40
|
+
mutations.observe(element === null ? document.body : element, {
|
|
41
|
+
childList: true,
|
|
42
|
+
subtree: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (!isDone && typeof ResizeObserver !== 'undefined') {
|
|
46
|
+
observer = new ResizeObserver(attempt);
|
|
47
|
+
for (const node of PsScrollTargetService.observed(element))
|
|
48
|
+
observer.observe(node);
|
|
49
|
+
}
|
|
50
|
+
return finish;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { PsScrollRestorationConfig } from '../constants/ps-scroll-restoration-config.js';
|
|
2
|
+
export class PsScrollStorageService {
|
|
3
|
+
static read(key) {
|
|
4
|
+
const offsets = PsScrollStorageService.memory();
|
|
5
|
+
return Object.prototype.hasOwnProperty.call(offsets, key) ? offsets[key] : undefined;
|
|
6
|
+
}
|
|
7
|
+
static write(key, top) {
|
|
8
|
+
const offsets = PsScrollStorageService.memory();
|
|
9
|
+
delete offsets[key];
|
|
10
|
+
offsets[key] = top;
|
|
11
|
+
const keys = Object.keys(offsets);
|
|
12
|
+
const overflow = keys.length - PsScrollRestorationConfig.maxEntries;
|
|
13
|
+
for (const oldest of keys.slice(0, overflow > 0 ? overflow : 0))
|
|
14
|
+
delete offsets[oldest];
|
|
15
|
+
if (PsScrollStorageService.timer !== 0 || typeof window === 'undefined')
|
|
16
|
+
return;
|
|
17
|
+
PsScrollStorageService.timer = window.setTimeout(PsScrollStorageService.flush, PsScrollRestorationConfig.persistThrottleMs);
|
|
18
|
+
}
|
|
19
|
+
static flush() {
|
|
20
|
+
if (typeof window === 'undefined')
|
|
21
|
+
return;
|
|
22
|
+
window.clearTimeout(PsScrollStorageService.timer);
|
|
23
|
+
PsScrollStorageService.timer = 0;
|
|
24
|
+
if (PsScrollStorageService.offsets === null)
|
|
25
|
+
return;
|
|
26
|
+
try {
|
|
27
|
+
window.sessionStorage.setItem(PsScrollRestorationConfig.storageKey, JSON.stringify(PsScrollStorageService.offsets));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
static reset() {
|
|
34
|
+
if (typeof window !== 'undefined')
|
|
35
|
+
window.clearTimeout(PsScrollStorageService.timer);
|
|
36
|
+
PsScrollStorageService.timer = 0;
|
|
37
|
+
PsScrollStorageService.offsets = null;
|
|
38
|
+
}
|
|
39
|
+
static memory() {
|
|
40
|
+
if (PsScrollStorageService.offsets === null)
|
|
41
|
+
PsScrollStorageService.offsets = PsScrollStorageService.load();
|
|
42
|
+
return PsScrollStorageService.offsets;
|
|
43
|
+
}
|
|
44
|
+
static load() {
|
|
45
|
+
if (typeof window === 'undefined')
|
|
46
|
+
return {};
|
|
47
|
+
try {
|
|
48
|
+
const raw = window.sessionStorage.getItem(PsScrollRestorationConfig.storageKey);
|
|
49
|
+
if (raw === null)
|
|
50
|
+
return {};
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
return PsScrollStorageService.isOffsets(parsed) ? parsed : {};
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
static isOffsets(value) {
|
|
59
|
+
return (typeof value === 'object' &&
|
|
60
|
+
value !== null &&
|
|
61
|
+
!Array.isArray(value) &&
|
|
62
|
+
Object.values(value).every((entry) => typeof entry === 'number' && Number.isFinite(entry)));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
PsScrollStorageService.offsets = null;
|
|
66
|
+
PsScrollStorageService.timer = 0;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PsScrollRestorationConfig } from '../constants/ps-scroll-restoration-config.js';
|
|
2
|
+
export class PsScrollTargetService {
|
|
3
|
+
static storageKey(element, restoreKey) {
|
|
4
|
+
return `${element === null ? PsScrollRestorationConfig.windowScope : PsScrollRestorationConfig.elementScope}${restoreKey}`;
|
|
5
|
+
}
|
|
6
|
+
static top(element) {
|
|
7
|
+
return element === null ? window.scrollY : element.scrollTop;
|
|
8
|
+
}
|
|
9
|
+
static maxTop(element) {
|
|
10
|
+
if (element !== null)
|
|
11
|
+
return element.scrollHeight - element.clientHeight;
|
|
12
|
+
return document.documentElement.scrollHeight - window.innerHeight;
|
|
13
|
+
}
|
|
14
|
+
static scrollTo(element, top) {
|
|
15
|
+
if (element === null) {
|
|
16
|
+
window.scrollTo({ behavior: 'instant', left: 0, top });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
element.scrollTo({ behavior: 'instant', left: 0, top });
|
|
20
|
+
}
|
|
21
|
+
static eventTarget(element) {
|
|
22
|
+
return element === null ? window : element;
|
|
23
|
+
}
|
|
24
|
+
static observed(element) {
|
|
25
|
+
if (element === null)
|
|
26
|
+
return [document.documentElement, document.body];
|
|
27
|
+
return [element, ...Array.from(element.children)];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsBrowserLayoutEffect = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
exports.usePsBrowserLayoutEffect = typeof window === 'undefined' ? react_1.useEffect : react_1.useLayoutEffect;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsScrollRestoration = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ps_scroll_restore_service_1 = require("../services/ps-scroll-restore-service");
|
|
6
|
+
const ps_scroll_storage_service_1 = require("../services/ps-scroll-storage-service");
|
|
7
|
+
const ps_scroll_target_service_1 = require("../services/ps-scroll-target-service");
|
|
8
|
+
const use_ps_browser_layout_effect_1 = require("./use-ps-browser-layout-effect");
|
|
9
|
+
const usePsScrollRestoration = ({ elementRef, preventScrollReset, resetKey, restoreKey, }) => {
|
|
10
|
+
const keyRef = (0, react_1.useRef)(restoreKey);
|
|
11
|
+
const hasSavedRef = (0, react_1.useRef)(false);
|
|
12
|
+
const appliedKeyRef = (0, react_1.useRef)(undefined);
|
|
13
|
+
const appliedResetKeyRef = (0, react_1.useRef)(undefined);
|
|
14
|
+
const isRestoringRef = (0, react_1.useRef)(false);
|
|
15
|
+
const pendingRef = (0, react_1.useRef)(null);
|
|
16
|
+
const isEnabled = restoreKey !== undefined;
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
18
|
+
if (!isEnabled ||
|
|
19
|
+
typeof window === 'undefined' ||
|
|
20
|
+
typeof history === 'undefined' ||
|
|
21
|
+
!('scrollRestoration' in history))
|
|
22
|
+
return;
|
|
23
|
+
const previous = history.scrollRestoration;
|
|
24
|
+
history.scrollRestoration = 'manual';
|
|
25
|
+
return () => {
|
|
26
|
+
history.scrollRestoration = previous;
|
|
27
|
+
};
|
|
28
|
+
}, [isEnabled]);
|
|
29
|
+
(0, use_ps_browser_layout_effect_1.usePsBrowserLayoutEffect)(() => {
|
|
30
|
+
keyRef.current = restoreKey;
|
|
31
|
+
if (typeof window === 'undefined')
|
|
32
|
+
return;
|
|
33
|
+
ps_scroll_storage_service_1.PsScrollStorageService.flush();
|
|
34
|
+
if (restoreKey === undefined)
|
|
35
|
+
return;
|
|
36
|
+
const element = elementRef === undefined ? null : elementRef.current;
|
|
37
|
+
const saved = ps_scroll_storage_service_1.PsScrollStorageService.read(ps_scroll_target_service_1.PsScrollTargetService.storageKey(element, restoreKey));
|
|
38
|
+
hasSavedRef.current = saved !== undefined;
|
|
39
|
+
if (saved === undefined)
|
|
40
|
+
return;
|
|
41
|
+
isRestoringRef.current = true;
|
|
42
|
+
const stop = ps_scroll_restore_service_1.PsScrollRestoreService.start(element, saved, () => {
|
|
43
|
+
isRestoringRef.current = false;
|
|
44
|
+
});
|
|
45
|
+
return () => {
|
|
46
|
+
stop();
|
|
47
|
+
isRestoringRef.current = false;
|
|
48
|
+
};
|
|
49
|
+
}, [restoreKey, elementRef]);
|
|
50
|
+
(0, use_ps_browser_layout_effect_1.usePsBrowserLayoutEffect)(() => {
|
|
51
|
+
const previousKey = appliedKeyRef.current;
|
|
52
|
+
const previousResetKey = appliedResetKeyRef.current;
|
|
53
|
+
appliedKeyRef.current = restoreKey;
|
|
54
|
+
appliedResetKeyRef.current = resetKey;
|
|
55
|
+
if (restoreKey === undefined || typeof window === 'undefined')
|
|
56
|
+
return;
|
|
57
|
+
const hasChanged = previousResetKey !== undefined && previousResetKey !== resetKey;
|
|
58
|
+
const isRestored = previousKey !== restoreKey && hasSavedRef.current;
|
|
59
|
+
if (!hasChanged || isRestored || preventScrollReset === true)
|
|
60
|
+
return;
|
|
61
|
+
ps_scroll_target_service_1.PsScrollTargetService.scrollTo(elementRef === undefined ? null : elementRef.current, 0);
|
|
62
|
+
}, [resetKey, restoreKey, elementRef, preventScrollReset]);
|
|
63
|
+
(0, react_1.useEffect)(() => {
|
|
64
|
+
if (!isEnabled || typeof window === 'undefined')
|
|
65
|
+
return;
|
|
66
|
+
const element = elementRef === undefined ? null : elementRef.current;
|
|
67
|
+
const target = ps_scroll_target_service_1.PsScrollTargetService.eventTarget(element);
|
|
68
|
+
let frame = 0;
|
|
69
|
+
const flushPending = () => {
|
|
70
|
+
const pending = pendingRef.current;
|
|
71
|
+
pendingRef.current = null;
|
|
72
|
+
if (pending !== null)
|
|
73
|
+
ps_scroll_storage_service_1.PsScrollStorageService.write(pending.key, pending.top);
|
|
74
|
+
};
|
|
75
|
+
const onScroll = () => {
|
|
76
|
+
const key = keyRef.current;
|
|
77
|
+
if (key === undefined || isRestoringRef.current)
|
|
78
|
+
return;
|
|
79
|
+
pendingRef.current = {
|
|
80
|
+
key: ps_scroll_target_service_1.PsScrollTargetService.storageKey(element, key),
|
|
81
|
+
top: ps_scroll_target_service_1.PsScrollTargetService.top(element),
|
|
82
|
+
};
|
|
83
|
+
if (frame !== 0)
|
|
84
|
+
return;
|
|
85
|
+
frame = window.requestAnimationFrame(() => {
|
|
86
|
+
frame = 0;
|
|
87
|
+
flushPending();
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const onPageHide = () => {
|
|
91
|
+
const key = keyRef.current;
|
|
92
|
+
pendingRef.current = null;
|
|
93
|
+
if (key !== undefined && !isRestoringRef.current)
|
|
94
|
+
ps_scroll_storage_service_1.PsScrollStorageService.write(ps_scroll_target_service_1.PsScrollTargetService.storageKey(element, key), ps_scroll_target_service_1.PsScrollTargetService.top(element));
|
|
95
|
+
ps_scroll_storage_service_1.PsScrollStorageService.flush();
|
|
96
|
+
};
|
|
97
|
+
target.addEventListener('scroll', onScroll, { passive: true });
|
|
98
|
+
window.addEventListener('pagehide', onPageHide);
|
|
99
|
+
return () => {
|
|
100
|
+
target.removeEventListener('scroll', onScroll);
|
|
101
|
+
window.removeEventListener('pagehide', onPageHide);
|
|
102
|
+
window.cancelAnimationFrame(frame);
|
|
103
|
+
flushPending();
|
|
104
|
+
};
|
|
105
|
+
}, [isEnabled, elementRef]);
|
|
106
|
+
};
|
|
107
|
+
exports.usePsScrollRestoration = usePsScrollRestoration;
|
package/dist/index.d.ts
CHANGED
|
@@ -192,3 +192,13 @@ export * from './components/ps-divider';
|
|
|
192
192
|
export * from './models/ps-divider-orientation';
|
|
193
193
|
export * from './components/ps-rating-display';
|
|
194
194
|
export * from './constants/ps-rating-display-size';
|
|
195
|
+
export * from './constants/ps-scroll-restoration-config';
|
|
196
|
+
export * from './models/ps-scroll-offsets-data';
|
|
197
|
+
export * from './models/ps-scroll-restoration-options';
|
|
198
|
+
export * from './services/ps-scroll-storage-service';
|
|
199
|
+
export * from './services/ps-scroll-target-service';
|
|
200
|
+
export * from './services/ps-scroll-restore-service';
|
|
201
|
+
export * from './hooks/use-ps-scroll-restoration';
|
|
202
|
+
export * from './components/ps-scroll-restoration';
|
|
203
|
+
export * from './hooks/use-ps-browser-layout-effect';
|
|
204
|
+
export * from './models/ps-scroll-pending-save-data';
|
package/dist/index.js
CHANGED
|
@@ -208,3 +208,13 @@ __exportStar(require("./components/ps-divider"), exports);
|
|
|
208
208
|
__exportStar(require("./models/ps-divider-orientation"), exports);
|
|
209
209
|
__exportStar(require("./components/ps-rating-display"), exports);
|
|
210
210
|
__exportStar(require("./constants/ps-rating-display-size"), exports);
|
|
211
|
+
__exportStar(require("./constants/ps-scroll-restoration-config"), exports);
|
|
212
|
+
__exportStar(require("./models/ps-scroll-offsets-data"), exports);
|
|
213
|
+
__exportStar(require("./models/ps-scroll-restoration-options"), exports);
|
|
214
|
+
__exportStar(require("./services/ps-scroll-storage-service"), exports);
|
|
215
|
+
__exportStar(require("./services/ps-scroll-target-service"), exports);
|
|
216
|
+
__exportStar(require("./services/ps-scroll-restore-service"), exports);
|
|
217
|
+
__exportStar(require("./hooks/use-ps-scroll-restoration"), exports);
|
|
218
|
+
__exportStar(require("./components/ps-scroll-restoration"), exports);
|
|
219
|
+
__exportStar(require("./hooks/use-ps-browser-layout-effect"), exports);
|
|
220
|
+
__exportStar(require("./models/ps-scroll-pending-save-data"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type PsScrollOffsetsData = Record<string, number>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScrollRestoreService = void 0;
|
|
4
|
+
const ps_scroll_restoration_config_1 = require("../constants/ps-scroll-restoration-config");
|
|
5
|
+
const ps_scroll_target_service_1 = require("./ps-scroll-target-service");
|
|
6
|
+
class PsScrollRestoreService {
|
|
7
|
+
static start(element, top, onDone) {
|
|
8
|
+
let isDone = false;
|
|
9
|
+
let observer = null;
|
|
10
|
+
let mutations = null;
|
|
11
|
+
let timer = 0;
|
|
12
|
+
const finish = () => {
|
|
13
|
+
if (isDone)
|
|
14
|
+
return;
|
|
15
|
+
isDone = true;
|
|
16
|
+
window.clearTimeout(timer);
|
|
17
|
+
observer?.disconnect();
|
|
18
|
+
mutations?.disconnect();
|
|
19
|
+
for (const name of ps_scroll_restoration_config_1.PsScrollRestorationConfig.inputEvents)
|
|
20
|
+
window.removeEventListener(name, onInput, true);
|
|
21
|
+
onDone();
|
|
22
|
+
};
|
|
23
|
+
const onInput = () => finish();
|
|
24
|
+
const attempt = () => {
|
|
25
|
+
if (isDone)
|
|
26
|
+
return;
|
|
27
|
+
if (ps_scroll_target_service_1.PsScrollTargetService.maxTop(element) >= top - ps_scroll_restoration_config_1.PsScrollRestorationConfig.tolerancePx) {
|
|
28
|
+
ps_scroll_target_service_1.PsScrollTargetService.scrollTo(element, top);
|
|
29
|
+
finish();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
for (const name of ps_scroll_restoration_config_1.PsScrollRestorationConfig.inputEvents)
|
|
33
|
+
window.addEventListener(name, onInput, { capture: true, passive: true });
|
|
34
|
+
timer = window.setTimeout(() => {
|
|
35
|
+
if (isDone)
|
|
36
|
+
return;
|
|
37
|
+
ps_scroll_target_service_1.PsScrollTargetService.scrollTo(element, top);
|
|
38
|
+
finish();
|
|
39
|
+
}, ps_scroll_restoration_config_1.PsScrollRestorationConfig.restoreTimeoutMs);
|
|
40
|
+
attempt();
|
|
41
|
+
if (!isDone && typeof MutationObserver !== 'undefined') {
|
|
42
|
+
mutations = new MutationObserver(attempt);
|
|
43
|
+
mutations.observe(element === null ? document.body : element, {
|
|
44
|
+
childList: true,
|
|
45
|
+
subtree: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (!isDone && typeof ResizeObserver !== 'undefined') {
|
|
49
|
+
observer = new ResizeObserver(attempt);
|
|
50
|
+
for (const node of ps_scroll_target_service_1.PsScrollTargetService.observed(element))
|
|
51
|
+
observer.observe(node);
|
|
52
|
+
}
|
|
53
|
+
return finish;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.PsScrollRestoreService = PsScrollRestoreService;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class PsScrollStorageService {
|
|
2
|
+
private static offsets;
|
|
3
|
+
private static timer;
|
|
4
|
+
static read(key: string): number | undefined;
|
|
5
|
+
static write(key: string, top: number): void;
|
|
6
|
+
static flush(): void;
|
|
7
|
+
static reset(): void;
|
|
8
|
+
private static memory;
|
|
9
|
+
private static load;
|
|
10
|
+
private static isOffsets;
|
|
11
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScrollStorageService = void 0;
|
|
4
|
+
const ps_scroll_restoration_config_1 = require("../constants/ps-scroll-restoration-config");
|
|
5
|
+
class PsScrollStorageService {
|
|
6
|
+
static read(key) {
|
|
7
|
+
const offsets = PsScrollStorageService.memory();
|
|
8
|
+
return Object.prototype.hasOwnProperty.call(offsets, key) ? offsets[key] : undefined;
|
|
9
|
+
}
|
|
10
|
+
static write(key, top) {
|
|
11
|
+
const offsets = PsScrollStorageService.memory();
|
|
12
|
+
delete offsets[key];
|
|
13
|
+
offsets[key] = top;
|
|
14
|
+
const keys = Object.keys(offsets);
|
|
15
|
+
const overflow = keys.length - ps_scroll_restoration_config_1.PsScrollRestorationConfig.maxEntries;
|
|
16
|
+
for (const oldest of keys.slice(0, overflow > 0 ? overflow : 0))
|
|
17
|
+
delete offsets[oldest];
|
|
18
|
+
if (PsScrollStorageService.timer !== 0 || typeof window === 'undefined')
|
|
19
|
+
return;
|
|
20
|
+
PsScrollStorageService.timer = window.setTimeout(PsScrollStorageService.flush, ps_scroll_restoration_config_1.PsScrollRestorationConfig.persistThrottleMs);
|
|
21
|
+
}
|
|
22
|
+
static flush() {
|
|
23
|
+
if (typeof window === 'undefined')
|
|
24
|
+
return;
|
|
25
|
+
window.clearTimeout(PsScrollStorageService.timer);
|
|
26
|
+
PsScrollStorageService.timer = 0;
|
|
27
|
+
if (PsScrollStorageService.offsets === null)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
window.sessionStorage.setItem(ps_scroll_restoration_config_1.PsScrollRestorationConfig.storageKey, JSON.stringify(PsScrollStorageService.offsets));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
static reset() {
|
|
37
|
+
if (typeof window !== 'undefined')
|
|
38
|
+
window.clearTimeout(PsScrollStorageService.timer);
|
|
39
|
+
PsScrollStorageService.timer = 0;
|
|
40
|
+
PsScrollStorageService.offsets = null;
|
|
41
|
+
}
|
|
42
|
+
static memory() {
|
|
43
|
+
if (PsScrollStorageService.offsets === null)
|
|
44
|
+
PsScrollStorageService.offsets = PsScrollStorageService.load();
|
|
45
|
+
return PsScrollStorageService.offsets;
|
|
46
|
+
}
|
|
47
|
+
static load() {
|
|
48
|
+
if (typeof window === 'undefined')
|
|
49
|
+
return {};
|
|
50
|
+
try {
|
|
51
|
+
const raw = window.sessionStorage.getItem(ps_scroll_restoration_config_1.PsScrollRestorationConfig.storageKey);
|
|
52
|
+
if (raw === null)
|
|
53
|
+
return {};
|
|
54
|
+
const parsed = JSON.parse(raw);
|
|
55
|
+
return PsScrollStorageService.isOffsets(parsed) ? parsed : {};
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
static isOffsets(value) {
|
|
62
|
+
return (typeof value === 'object' &&
|
|
63
|
+
value !== null &&
|
|
64
|
+
!Array.isArray(value) &&
|
|
65
|
+
Object.values(value).every((entry) => typeof entry === 'number' && Number.isFinite(entry)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.PsScrollStorageService = PsScrollStorageService;
|
|
69
|
+
PsScrollStorageService.offsets = null;
|
|
70
|
+
PsScrollStorageService.timer = 0;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class PsScrollTargetService {
|
|
2
|
+
static storageKey(element: HTMLElement | null, restoreKey: string): string;
|
|
3
|
+
static top(element: HTMLElement | null): number;
|
|
4
|
+
static maxTop(element: HTMLElement | null): number;
|
|
5
|
+
static scrollTo(element: HTMLElement | null, top: number): void;
|
|
6
|
+
static eventTarget(element: HTMLElement | null): HTMLElement | Window;
|
|
7
|
+
static observed(element: HTMLElement | null): Element[];
|
|
8
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScrollTargetService = void 0;
|
|
4
|
+
const ps_scroll_restoration_config_1 = require("../constants/ps-scroll-restoration-config");
|
|
5
|
+
class PsScrollTargetService {
|
|
6
|
+
static storageKey(element, restoreKey) {
|
|
7
|
+
return `${element === null ? ps_scroll_restoration_config_1.PsScrollRestorationConfig.windowScope : ps_scroll_restoration_config_1.PsScrollRestorationConfig.elementScope}${restoreKey}`;
|
|
8
|
+
}
|
|
9
|
+
static top(element) {
|
|
10
|
+
return element === null ? window.scrollY : element.scrollTop;
|
|
11
|
+
}
|
|
12
|
+
static maxTop(element) {
|
|
13
|
+
if (element !== null)
|
|
14
|
+
return element.scrollHeight - element.clientHeight;
|
|
15
|
+
return document.documentElement.scrollHeight - window.innerHeight;
|
|
16
|
+
}
|
|
17
|
+
static scrollTo(element, top) {
|
|
18
|
+
if (element === null) {
|
|
19
|
+
window.scrollTo({ behavior: 'instant', left: 0, top });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
element.scrollTo({ behavior: 'instant', left: 0, top });
|
|
23
|
+
}
|
|
24
|
+
static eventTarget(element) {
|
|
25
|
+
return element === null ? window : element;
|
|
26
|
+
}
|
|
27
|
+
static observed(element) {
|
|
28
|
+
if (element === null)
|
|
29
|
+
return [document.documentElement, document.body];
|
|
30
|
+
return [element, ...Array.from(element.children)];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.PsScrollTargetService = PsScrollTargetService;
|