@pallasoft/ps-core-components 2.42.0 → 2.44.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/components/ps-skeleton-table.d.ts +3 -1
- package/dist/components/ps-skeleton-table.js +15 -2
- package/dist/constants/ps-flag-param-value.d.ts +3 -0
- package/dist/constants/ps-flag-param-value.js +6 -0
- package/dist/constants/ps-query-param-config.d.ts +4 -0
- package/dist/constants/ps-query-param-config.js +6 -0
- package/dist/constants/ps-rerender-config.d.ts +3 -0
- package/dist/constants/ps-rerender-config.js +6 -0
- package/dist/constants/ps-scroll-restoration-config.d.ts +10 -0
- package/dist/constants/ps-scroll-restoration-config.js +13 -0
- package/dist/constants/ps-skeleton-config.d.ts +2 -0
- package/dist/constants/ps-skeleton-config.js +2 -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/components/ps-skeleton-table.js +15 -2
- package/dist/esm/constants/ps-flag-param-value.js +3 -0
- package/dist/esm/constants/ps-query-param-config.js +3 -0
- package/dist/esm/constants/ps-rerender-config.js +3 -0
- package/dist/esm/constants/ps-scroll-restoration-config.js +10 -0
- package/dist/esm/constants/ps-skeleton-config.js +2 -0
- package/dist/esm/hooks/use-ps-browser-layout-effect.js +2 -0
- package/dist/esm/hooks/use-ps-choice-param.js +6 -0
- package/dist/esm/hooks/use-ps-flag-param.js +6 -0
- package/dist/esm/hooks/use-ps-rerender-at.js +21 -0
- package/dist/esm/hooks/use-ps-scroll-restoration.js +103 -0
- package/dist/esm/index.js +25 -0
- package/dist/esm/models/ps-choice-param-data.js +1 -0
- package/dist/esm/models/ps-choice-param-options.js +1 -0
- package/dist/esm/models/ps-flag-param-data.js +1 -0
- package/dist/esm/models/ps-flag-param-options.js +1 -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/models/ps-search-params-set-options.js +1 -0
- package/dist/esm/models/ps-search-params-setter.js +1 -0
- package/dist/esm/models/ps-search-params-state.js +1 -0
- package/dist/esm/models/ps-skeleton-table-row.js +1 -0
- package/dist/esm/services/ps-query-param-service.js +32 -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-choice-param.d.ts +4 -0
- package/dist/hooks/use-ps-choice-param.js +10 -0
- package/dist/hooks/use-ps-flag-param.d.ts +4 -0
- package/dist/hooks/use-ps-flag-param.js +10 -0
- package/dist/hooks/use-ps-rerender-at.d.ts +1 -0
- package/dist/hooks/use-ps-rerender-at.js +25 -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 +25 -0
- package/dist/index.js +25 -0
- package/dist/models/ps-choice-param-data.d.ts +4 -0
- package/dist/models/ps-choice-param-data.js +2 -0
- package/dist/models/ps-choice-param-options.d.ts +6 -0
- package/dist/models/ps-choice-param-options.js +2 -0
- package/dist/models/ps-flag-param-data.d.ts +4 -0
- package/dist/models/ps-flag-param-data.js +2 -0
- package/dist/models/ps-flag-param-options.d.ts +4 -0
- package/dist/models/ps-flag-param-options.js +2 -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/models/ps-search-params-set-options.d.ts +4 -0
- package/dist/models/ps-search-params-set-options.js +2 -0
- package/dist/models/ps-search-params-setter.d.ts +2 -0
- package/dist/models/ps-search-params-setter.js +2 -0
- package/dist/models/ps-search-params-state.d.ts +2 -0
- package/dist/models/ps-search-params-state.js +2 -0
- package/dist/models/ps-skeleton-table-row.d.ts +4 -0
- package/dist/models/ps-skeleton-table-row.js +2 -0
- package/dist/services/ps-query-param-service.d.ts +7 -0
- package/dist/services/ps-query-param-service.js +36 -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;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PsSkeletonTableRow } from '../models/ps-skeleton-table-row';
|
|
1
2
|
export interface PsSkeletonTableProps {
|
|
2
3
|
alignments?: ('left' | 'right')[];
|
|
3
4
|
className?: string;
|
|
@@ -5,6 +6,7 @@ export interface PsSkeletonTableProps {
|
|
|
5
6
|
hasFooter?: boolean;
|
|
6
7
|
hasToolbar?: boolean;
|
|
7
8
|
isFramed?: boolean;
|
|
9
|
+
rowLayout?: PsSkeletonTableRow[];
|
|
8
10
|
rows?: number;
|
|
9
11
|
}
|
|
10
|
-
export declare const PsSkeletonTable: ({ alignments, className, columns, hasFooter, hasToolbar, isFramed, rows, }: PsSkeletonTableProps) => import("react").JSX.Element;
|
|
12
|
+
export declare const PsSkeletonTable: ({ alignments, className, columns, hasFooter, hasToolbar, isFramed, rowLayout, rows, }: PsSkeletonTableProps) => import("react").JSX.Element;
|
|
@@ -7,7 +7,8 @@ const ps_skeleton_config_1 = require("../constants/ps-skeleton-config");
|
|
|
7
7
|
const ps_skeleton_shape_1 = require("../constants/ps-skeleton-shape");
|
|
8
8
|
const ps_skeleton_1 = require("./ps-skeleton");
|
|
9
9
|
const ps_skeleton_group_1 = require("./ps-skeleton-group");
|
|
10
|
-
const PsSkeletonTable = ({ alignments, className, columns = 4, hasFooter = false, hasToolbar = false, isFramed = true, rows = 6, }) => {
|
|
10
|
+
const PsSkeletonTable = ({ alignments, className, columns = 4, hasFooter = false, hasToolbar = false, isFramed = true, rowLayout, rows = 6, }) => {
|
|
11
|
+
const layout = rowLayout === undefined ? Array.from({ length: rows }, () => ({})) : rowLayout;
|
|
11
12
|
const gridTemplateColumns = `minmax(0, 2fr) repeat(${Math.max(columns - 1, 0)}, minmax(0, 1fr))`;
|
|
12
13
|
const isRight = (columnIndex) => alignments !== undefined
|
|
13
14
|
? alignments[columnIndex] === 'right'
|
|
@@ -17,6 +18,18 @@ const PsSkeletonTable = ({ alignments, className, columns = 4, hasFooter = false
|
|
|
17
18
|
: ps_skeleton_config_1.PsSkeletonConfig.lineWidths[(rowIndex + columnIndex) % ps_skeleton_config_1.PsSkeletonConfig.lineWidths.length], isStrong ? 'h-3.5' : '') }, columnIndex));
|
|
18
19
|
return ((0, jsx_runtime_1.jsxs)(ps_skeleton_group_1.PsSkeletonGroup, { className: ps_design_system_1.PallasoftClassNameService.merge('flex flex-col gap-3', className), children: [hasToolbar ? ((0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-3', children: [(0, jsx_runtime_1.jsx)(ps_skeleton_1.PsSkeleton, { className: 'max-w-sm', shape: ps_skeleton_shape_1.PsSkeletonShape.Control }), (0, jsx_runtime_1.jsx)(ps_skeleton_1.PsSkeleton, { className: 'ml-auto w-24', shape: ps_skeleton_shape_1.PsSkeletonShape.Button })] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: isFramed
|
|
19
20
|
? ps_design_system_1.PallasoftClassNameService.merge('overflow-hidden border border-line bg-surface', ps_skeleton_config_1.PsSkeletonConfig.tableFrameClassName)
|
|
20
|
-
: 'overflow-hidden', children: [(0, jsx_runtime_1.jsx)("div", { className: 'grid gap-4 bg-mist/70 px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (_, index) => ((0, jsx_runtime_1.jsx)(ps_skeleton_1.PsSkeleton, { className: isRight(index) ? 'ml-auto h-2.5 w-12' : 'h-2.5 w-16' }, index))) }),
|
|
21
|
+
: 'overflow-hidden', children: [(0, jsx_runtime_1.jsx)("div", { className: 'grid gap-4 bg-mist/70 px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (_, index) => ((0, jsx_runtime_1.jsx)(ps_skeleton_1.PsSkeleton, { className: isRight(index) ? 'ml-auto h-2.5 w-12' : 'h-2.5 w-16' }, index))) }), layout.map((row, rowIndex) => {
|
|
22
|
+
const isSection = row.isSection === true;
|
|
23
|
+
const depth = row.depth === undefined ? 0 : row.depth;
|
|
24
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: 'grid items-center gap-4 border-t border-line px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (__, columnIndex) => {
|
|
25
|
+
if (isSection && columnIndex > 0 && !isRight(columnIndex))
|
|
26
|
+
return (0, jsx_runtime_1.jsx)("span", {}, columnIndex);
|
|
27
|
+
if (columnIndex > 0 || (depth === 0 && !isSection))
|
|
28
|
+
return cell(rowIndex, columnIndex, isSection);
|
|
29
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 items-center gap-3', style: {
|
|
30
|
+
paddingInlineStart: `${depth * ps_skeleton_config_1.PsSkeletonConfig.tableIndentRem}rem`,
|
|
31
|
+
}, children: [isSection ? ((0, jsx_runtime_1.jsx)(ps_skeleton_1.PsSkeleton, { className: ps_skeleton_config_1.PsSkeletonConfig.tableSectionMarkerClassName })) : null, cell(rowIndex, columnIndex, isSection)] }, columnIndex));
|
|
32
|
+
}) }, rowIndex));
|
|
33
|
+
}), hasFooter ? ((0, jsx_runtime_1.jsx)("div", { className: 'grid items-center gap-4 border-t-2 border-line px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (__, columnIndex) => cell(layout.length, columnIndex, true)) })) : null] })] }));
|
|
21
34
|
};
|
|
22
35
|
exports.PsSkeletonTable = PsSkeletonTable;
|
|
@@ -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
|
+
};
|
|
@@ -14,6 +14,8 @@ export declare const PsSkeletonConfig: {
|
|
|
14
14
|
readonly tabWidthClassName: "w-24";
|
|
15
15
|
readonly tabCount: 4;
|
|
16
16
|
readonly tableFrameClassName: "rounded-[0.875rem]";
|
|
17
|
+
readonly tableIndentRem: 1.25;
|
|
18
|
+
readonly tableSectionMarkerClassName: "h-5 w-5 shrink-0 rounded-md";
|
|
17
19
|
readonly shapeClassNames: Record<PsSkeletonShape, string>;
|
|
18
20
|
readonly shapeSizes: Record<PsSkeletonShape, PsSkeletonSize>;
|
|
19
21
|
};
|
|
@@ -20,6 +20,8 @@ exports.PsSkeletonConfig = {
|
|
|
20
20
|
tabWidthClassName: 'w-24',
|
|
21
21
|
tabCount: 4,
|
|
22
22
|
tableFrameClassName: 'rounded-[0.875rem]',
|
|
23
|
+
tableIndentRem: 1.25,
|
|
24
|
+
tableSectionMarkerClassName: 'h-5 w-5 shrink-0 rounded-md',
|
|
23
25
|
shapeClassNames: {
|
|
24
26
|
[ps_skeleton_shape_1.PsSkeletonShape.Badge]: controlRadius,
|
|
25
27
|
[ps_skeleton_shape_1.PsSkeletonShape.Block]: controlRadius,
|
|
@@ -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
|
};
|
|
@@ -4,7 +4,8 @@ import { PsSkeletonConfig } from '../constants/ps-skeleton-config.js';
|
|
|
4
4
|
import { PsSkeletonShape } from '../constants/ps-skeleton-shape.js';
|
|
5
5
|
import { PsSkeleton } from './ps-skeleton.js';
|
|
6
6
|
import { PsSkeletonGroup } from './ps-skeleton-group.js';
|
|
7
|
-
export const PsSkeletonTable = ({ alignments, className, columns = 4, hasFooter = false, hasToolbar = false, isFramed = true, rows = 6, }) => {
|
|
7
|
+
export const PsSkeletonTable = ({ alignments, className, columns = 4, hasFooter = false, hasToolbar = false, isFramed = true, rowLayout, rows = 6, }) => {
|
|
8
|
+
const layout = rowLayout === undefined ? Array.from({ length: rows }, () => ({})) : rowLayout;
|
|
8
9
|
const gridTemplateColumns = `minmax(0, 2fr) repeat(${Math.max(columns - 1, 0)}, minmax(0, 1fr))`;
|
|
9
10
|
const isRight = (columnIndex) => alignments !== undefined
|
|
10
11
|
? alignments[columnIndex] === 'right'
|
|
@@ -14,5 +15,17 @@ export const PsSkeletonTable = ({ alignments, className, columns = 4, hasFooter
|
|
|
14
15
|
: PsSkeletonConfig.lineWidths[(rowIndex + columnIndex) % PsSkeletonConfig.lineWidths.length], isStrong ? 'h-3.5' : '') }, columnIndex));
|
|
15
16
|
return (_jsxs(PsSkeletonGroup, { className: PallasoftClassNameService.merge('flex flex-col gap-3', className), children: [hasToolbar ? (_jsxs("div", { className: 'flex items-center gap-3', children: [_jsx(PsSkeleton, { className: 'max-w-sm', shape: PsSkeletonShape.Control }), _jsx(PsSkeleton, { className: 'ml-auto w-24', shape: PsSkeletonShape.Button })] })) : null, _jsxs("div", { className: isFramed
|
|
16
17
|
? PallasoftClassNameService.merge('overflow-hidden border border-line bg-surface', PsSkeletonConfig.tableFrameClassName)
|
|
17
|
-
: 'overflow-hidden', children: [_jsx("div", { className: 'grid gap-4 bg-mist/70 px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (_, index) => (_jsx(PsSkeleton, { className: isRight(index) ? 'ml-auto h-2.5 w-12' : 'h-2.5 w-16' }, index))) }),
|
|
18
|
+
: 'overflow-hidden', children: [_jsx("div", { className: 'grid gap-4 bg-mist/70 px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (_, index) => (_jsx(PsSkeleton, { className: isRight(index) ? 'ml-auto h-2.5 w-12' : 'h-2.5 w-16' }, index))) }), layout.map((row, rowIndex) => {
|
|
19
|
+
const isSection = row.isSection === true;
|
|
20
|
+
const depth = row.depth === undefined ? 0 : row.depth;
|
|
21
|
+
return (_jsx("div", { className: 'grid items-center gap-4 border-t border-line px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (__, columnIndex) => {
|
|
22
|
+
if (isSection && columnIndex > 0 && !isRight(columnIndex))
|
|
23
|
+
return _jsx("span", {}, columnIndex);
|
|
24
|
+
if (columnIndex > 0 || (depth === 0 && !isSection))
|
|
25
|
+
return cell(rowIndex, columnIndex, isSection);
|
|
26
|
+
return (_jsxs("div", { className: 'flex min-w-0 items-center gap-3', style: {
|
|
27
|
+
paddingInlineStart: `${depth * PsSkeletonConfig.tableIndentRem}rem`,
|
|
28
|
+
}, children: [isSection ? (_jsx(PsSkeleton, { className: PsSkeletonConfig.tableSectionMarkerClassName })) : null, cell(rowIndex, columnIndex, isSection)] }, columnIndex));
|
|
29
|
+
}) }, rowIndex));
|
|
30
|
+
}), hasFooter ? (_jsx("div", { className: 'grid items-center gap-4 border-t-2 border-line px-5 py-4', style: { gridTemplateColumns }, children: Array.from({ length: columns }, (__, columnIndex) => cell(layout.length, columnIndex, true)) })) : null] })] }));
|
|
18
31
|
};
|
|
@@ -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
|
+
};
|
|
@@ -17,6 +17,8 @@ export const PsSkeletonConfig = {
|
|
|
17
17
|
tabWidthClassName: 'w-24',
|
|
18
18
|
tabCount: 4,
|
|
19
19
|
tableFrameClassName: 'rounded-[0.875rem]',
|
|
20
|
+
tableIndentRem: 1.25,
|
|
21
|
+
tableSectionMarkerClassName: 'h-5 w-5 shrink-0 rounded-md',
|
|
20
22
|
shapeClassNames: {
|
|
21
23
|
[PsSkeletonShape.Badge]: controlRadius,
|
|
22
24
|
[PsSkeletonShape.Block]: controlRadius,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PsQueryParamConfig } from '../constants/ps-query-param-config.js';
|
|
2
|
+
import { PsQueryParamService } from '../services/ps-query-param-service.js';
|
|
3
|
+
export const usePsChoiceParam = ([searchParams, setSearchParams], { fallback, name, options, resetParams }) => ({
|
|
4
|
+
onChange: (value) => setSearchParams((current) => PsQueryParamService.withChoice(current, name, value, fallback, resetParams), PsQueryParamConfig.setOptions),
|
|
5
|
+
value: PsQueryParamService.choice(searchParams.get(name), options, fallback),
|
|
6
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PsQueryParamConfig } from '../constants/ps-query-param-config.js';
|
|
2
|
+
import { PsQueryParamService } from '../services/ps-query-param-service.js';
|
|
3
|
+
export const usePsFlagParam = ([searchParams, setSearchParams], { name, resetParams }) => ({
|
|
4
|
+
isOn: PsQueryParamService.flag(searchParams.get(name)),
|
|
5
|
+
onChange: (isOn) => setSearchParams((current) => PsQueryParamService.withFlag(current, name, isOn, resetParams), PsQueryParamConfig.setOptions),
|
|
6
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { PsRerenderConfig } from '../constants/ps-rerender-config.js';
|
|
3
|
+
export const usePsRerenderAt = (atMs) => {
|
|
4
|
+
const [, setTick] = useState(0);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (atMs === null || typeof window === 'undefined')
|
|
7
|
+
return;
|
|
8
|
+
let timer = 0;
|
|
9
|
+
const arm = () => {
|
|
10
|
+
const remaining = atMs - Date.now();
|
|
11
|
+
if (remaining <= 0) {
|
|
12
|
+
setTick((tick) => tick + 1);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
timer = window.setTimeout(arm, Math.min(remaining, PsRerenderConfig.maxTimerMs));
|
|
16
|
+
};
|
|
17
|
+
arm();
|
|
18
|
+
return () => window.clearTimeout(timer);
|
|
19
|
+
}, [atMs]);
|
|
20
|
+
return Date.now();
|
|
21
|
+
};
|
|
@@ -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
|
@@ -59,6 +59,7 @@ export * from './components/ps-skeleton.js';
|
|
|
59
59
|
export * from './services/ps-scroll-lock-service.js';
|
|
60
60
|
export * from './services/ps-skeleton-class-service.js';
|
|
61
61
|
export * from './models/ps-skeleton-size.js';
|
|
62
|
+
export * from './models/ps-skeleton-table-row.js';
|
|
62
63
|
export * from './constants/ps-skeleton-shape.js';
|
|
63
64
|
export * from './components/ps-skeleton-card.js';
|
|
64
65
|
export * from './components/ps-skeleton-group.js';
|
|
@@ -192,3 +193,27 @@ export * from './components/ps-divider.js';
|
|
|
192
193
|
export * from './models/ps-divider-orientation.js';
|
|
193
194
|
export * from './components/ps-rating-display.js';
|
|
194
195
|
export * from './constants/ps-rating-display-size.js';
|
|
196
|
+
export * from './constants/ps-scroll-restoration-config.js';
|
|
197
|
+
export * from './models/ps-scroll-offsets-data.js';
|
|
198
|
+
export * from './models/ps-scroll-restoration-options.js';
|
|
199
|
+
export * from './services/ps-scroll-storage-service.js';
|
|
200
|
+
export * from './services/ps-scroll-target-service.js';
|
|
201
|
+
export * from './services/ps-scroll-restore-service.js';
|
|
202
|
+
export * from './hooks/use-ps-scroll-restoration.js';
|
|
203
|
+
export * from './components/ps-scroll-restoration.js';
|
|
204
|
+
export * from './hooks/use-ps-browser-layout-effect.js';
|
|
205
|
+
export * from './models/ps-scroll-pending-save-data.js';
|
|
206
|
+
export * from './constants/ps-flag-param-value.js';
|
|
207
|
+
export * from './constants/ps-query-param-config.js';
|
|
208
|
+
export * from './models/ps-search-params-set-options.js';
|
|
209
|
+
export * from './models/ps-search-params-setter.js';
|
|
210
|
+
export * from './models/ps-search-params-state.js';
|
|
211
|
+
export * from './models/ps-choice-param-data.js';
|
|
212
|
+
export * from './models/ps-flag-param-data.js';
|
|
213
|
+
export * from './models/ps-choice-param-options.js';
|
|
214
|
+
export * from './models/ps-flag-param-options.js';
|
|
215
|
+
export * from './services/ps-query-param-service.js';
|
|
216
|
+
export * from './hooks/use-ps-choice-param.js';
|
|
217
|
+
export * from './hooks/use-ps-flag-param.js';
|
|
218
|
+
export * from './constants/ps-rerender-config.js';
|
|
219
|
+
export * from './hooks/use-ps-rerender-at.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PsFlagParamValue } from '../constants/ps-flag-param-value.js';
|
|
2
|
+
export class PsQueryParamService {
|
|
3
|
+
static choice(value, options, fallback) {
|
|
4
|
+
const match = options.find((option) => option === value);
|
|
5
|
+
return match === undefined ? fallback : match;
|
|
6
|
+
}
|
|
7
|
+
static withChoice(params, name, value, fallback, resetParams = []) {
|
|
8
|
+
const next = PsQueryParamService.cleared(params, resetParams);
|
|
9
|
+
if (value === fallback)
|
|
10
|
+
next.delete(name);
|
|
11
|
+
else
|
|
12
|
+
next.set(name, value);
|
|
13
|
+
return next;
|
|
14
|
+
}
|
|
15
|
+
static flag(value) {
|
|
16
|
+
return value === PsFlagParamValue.on;
|
|
17
|
+
}
|
|
18
|
+
static withFlag(params, name, isOn, resetParams = []) {
|
|
19
|
+
const next = PsQueryParamService.cleared(params, resetParams);
|
|
20
|
+
if (isOn)
|
|
21
|
+
next.set(name, PsFlagParamValue.on);
|
|
22
|
+
else
|
|
23
|
+
next.delete(name);
|
|
24
|
+
return next;
|
|
25
|
+
}
|
|
26
|
+
static cleared(params, resetParams) {
|
|
27
|
+
const next = new URLSearchParams(params);
|
|
28
|
+
for (const name of resetParams)
|
|
29
|
+
next.delete(name);
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
32
|
+
}
|