@pallasoft/ps-table-components 1.28.0 → 1.28.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.
@@ -396,7 +396,7 @@ const PsDataTable = ({ caption, className, clearSelectionKey, columns, cursorPag
396
396
  updateQuery({ search: next });
397
397
  resetPage();
398
398
  }, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, banner] }));
399
- return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: ps_design_system_1.PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [stickyTop === undefined ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [stickyLeading, topContent] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: '-mb-3 h-0', ref: stickySentinelRef }), (0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('sticky z-sticky flex flex-col gap-3', stickyClassName), "data-stuck": isStuck, ref: stickyRegionRef, style: { top: stickyTop }, children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('ps-glass-strong pointer-events-none absolute -inset-x-3 -bottom-3 rounded-b-ps-card border-t-0 transition-opacity duration-150', isStuck ? 'opacity-100' : 'opacity-0'), ref: stickyBackdropRef, style: { top: `calc(-1 * ${stickyTop})` } }), (0, jsx_runtime_1.jsxs)("div", { className: 'relative flex flex-col gap-3', children: [stickyLeading, topContent] })] })] })), (0, jsx_runtime_1.jsxs)("div", { "aria-busy": isRefreshing, className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [showsPagination &&
399
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: ps_design_system_1.PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [stickyTop === undefined ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [stickyLeading, topContent] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: '-mb-3 h-0', ref: stickySentinelRef }), (0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('sticky z-sticky flex flex-col gap-3', stickyClassName), "data-stuck": isStuck, ref: stickyRegionRef, style: { top: stickyTop }, children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: 'ps-glass-strong pointer-events-none absolute -inset-x-3 -bottom-3 rounded-b-ps-card border-t-0 opacity-0', ref: stickyBackdropRef, style: { top: `calc(-1 * ${stickyTop})` } }), (0, jsx_runtime_1.jsxs)("div", { className: 'relative flex flex-col gap-3', children: [stickyLeading, topContent] })] })] })), (0, jsx_runtime_1.jsxs)("div", { "aria-busy": isRefreshing, className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [showsPagination &&
400
400
  paginationPosition !== ps_table_pagination_position_1.PsTablePaginationPosition.bottom &&
401
401
  rowCount > ps_table_pagination_config_1.PsTablePaginationConfig.topMinRows
402
402
  ? renderPagination('border-b border-t-0')
@@ -0,0 +1,3 @@
1
+ export declare const PsTableStickyConfig: {
2
+ readonly backdropFadePx: 16;
3
+ };
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsTableStickyConfig = void 0;
4
+ exports.PsTableStickyConfig = {
5
+ backdropFadePx: 16,
6
+ };
@@ -360,7 +360,7 @@ export const PsDataTable = ({ caption, className, clearSelectionKey, columns, cu
360
360
  updateQuery({ search: next });
361
361
  resetPage();
362
362
  }, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, banner] }));
363
- return (_jsxs("div", { ref: containerRef, className: PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [stickyTop === undefined ? (_jsxs(_Fragment, { children: [stickyLeading, topContent] })) : (_jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": 'true', className: '-mb-3 h-0', ref: stickySentinelRef }), _jsxs("div", { className: PallasoftClassNameService.merge('sticky z-sticky flex flex-col gap-3', stickyClassName), "data-stuck": isStuck, ref: stickyRegionRef, style: { top: stickyTop }, children: [_jsx("div", { "aria-hidden": 'true', className: PallasoftClassNameService.merge('ps-glass-strong pointer-events-none absolute -inset-x-3 -bottom-3 rounded-b-ps-card border-t-0 transition-opacity duration-150', isStuck ? 'opacity-100' : 'opacity-0'), ref: stickyBackdropRef, style: { top: `calc(-1 * ${stickyTop})` } }), _jsxs("div", { className: 'relative flex flex-col gap-3', children: [stickyLeading, topContent] })] })] })), _jsxs("div", { "aria-busy": isRefreshing, className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [showsPagination &&
363
+ return (_jsxs("div", { ref: containerRef, className: PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [stickyTop === undefined ? (_jsxs(_Fragment, { children: [stickyLeading, topContent] })) : (_jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": 'true', className: '-mb-3 h-0', ref: stickySentinelRef }), _jsxs("div", { className: PallasoftClassNameService.merge('sticky z-sticky flex flex-col gap-3', stickyClassName), "data-stuck": isStuck, ref: stickyRegionRef, style: { top: stickyTop }, children: [_jsx("div", { "aria-hidden": 'true', className: 'ps-glass-strong pointer-events-none absolute -inset-x-3 -bottom-3 rounded-b-ps-card border-t-0 opacity-0', ref: stickyBackdropRef, style: { top: `calc(-1 * ${stickyTop})` } }), _jsxs("div", { className: 'relative flex flex-col gap-3', children: [stickyLeading, topContent] })] })] })), _jsxs("div", { "aria-busy": isRefreshing, className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [showsPagination &&
364
364
  paginationPosition !== PsTablePaginationPosition.bottom &&
365
365
  rowCount > PsTablePaginationConfig.topMinRows
366
366
  ? renderPagination('border-b border-t-0')
@@ -0,0 +1,3 @@
1
+ export const PsTableStickyConfig = {
2
+ backdropFadePx: 16,
3
+ };
@@ -1,4 +1,5 @@
1
1
  import { useLayoutEffect, useState } from 'react';
2
+ import { PsTableStickyConfig } from '../constants/ps-table-sticky-config';
2
3
  import { PsTableScrollService } from '../services/ps-table-scroll-service';
3
4
  import { PsTableStickyHeadService } from '../services/ps-table-sticky-head-service';
4
5
  export const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, sentinelRef, stickyTop, }) => {
@@ -19,7 +20,7 @@ export const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, se
19
20
  const scroller = PsTableScrollService.parent(region);
20
21
  const Timeline = window.ScrollTimeline;
21
22
  const isDriven = scroller !== null && Timeline !== undefined;
22
- let animation = null;
23
+ let animations = [];
23
24
  let frame = 0;
24
25
  const maxShift = () => Math.max(table.offsetHeight - head.offsetHeight, 0);
25
26
  const drive = () => {
@@ -32,23 +33,38 @@ export const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, se
32
33
  backdrop.getBoundingClientRect().bottom -
33
34
  regionTop;
34
35
  const pinScroll = table.getBoundingClientRect().top + scroller.scrollTop - pinnedBottom;
35
- const keyframes = PsTableStickyHeadService.keyframes(pinScroll, maxShift(), scroller.scrollHeight - scroller.clientHeight);
36
- if (animation !== null)
37
- animation.cancel();
38
- animation =
39
- keyframes.length === 0
40
- ? null
41
- : head.animate(keyframes.map((keyframe) => ({
42
- offset: keyframe.offset,
43
- transform: `translateY(${keyframe.shift}px)`,
44
- })), { fill: 'both', timeline: new Timeline({ axis: 'block', source: scroller }) });
36
+ const barPinScroll = sentinel.getBoundingClientRect().top -
37
+ scrollerTop +
38
+ scroller.scrollTop -
39
+ Number.parseFloat(getComputedStyle(region).top);
40
+ const scrollRange = scroller.scrollHeight - scroller.clientHeight;
41
+ const timeline = new Timeline({ axis: 'block', source: scroller });
42
+ const headFrames = PsTableStickyHeadService.keyframes(pinScroll, maxShift(), scrollRange);
43
+ const backdropFrames = PsTableStickyHeadService.backdropKeyframes(barPinScroll, PsTableStickyConfig.backdropFadePx, scrollRange);
44
+ animations.forEach((animation) => animation.cancel());
45
+ animations =
46
+ headFrames.length === 0
47
+ ? []
48
+ : [
49
+ head.animate(headFrames.map((keyframe) => ({
50
+ offset: keyframe.offset,
51
+ transform: `translateY(${keyframe.shift}px)`,
52
+ })), { fill: 'both', timeline }),
53
+ backdrop.animate(backdropFrames.map((keyframe) => ({
54
+ offset: keyframe.offset,
55
+ opacity: keyframe.opacity,
56
+ })), { fill: 'both', timeline }),
57
+ ];
45
58
  };
46
59
  const follow = () => (head.style.transform = `translateY(${PsTableStickyHeadService.offset(backdrop.getBoundingClientRect().bottom, table.getBoundingClientRect().top, table.offsetHeight, head.offsetHeight)}px)`);
47
60
  const check = () => {
61
+ const stuck = sentinel.getBoundingClientRect().top < region.getBoundingClientRect().top - 1;
48
62
  frame = 0;
49
- setIsStuck(sentinel.getBoundingClientRect().top < region.getBoundingClientRect().top - 1);
50
- if (!isDriven)
51
- follow();
63
+ setIsStuck(stuck);
64
+ if (isDriven)
65
+ return;
66
+ backdrop.style.opacity = stuck ? '1' : '0';
67
+ follow();
52
68
  };
53
69
  const schedule = () => {
54
70
  if (frame === 0)
@@ -69,9 +85,9 @@ export const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, se
69
85
  window.addEventListener('resize', measure);
70
86
  return () => {
71
87
  observer.disconnect();
72
- if (animation !== null)
73
- animation.cancel();
88
+ animations.forEach((animation) => animation.cancel());
74
89
  head.style.transform = '';
90
+ backdrop.style.opacity = '';
75
91
  window.removeEventListener('scroll', schedule, { capture: true });
76
92
  window.removeEventListener('resize', measure);
77
93
  if (frame !== 0)
@@ -6,12 +6,19 @@ export class PsTableStickyHeadService {
6
6
  if (scrollRange <= 0)
7
7
  return [];
8
8
  const shiftAt = (scroll) => Math.min(Math.max(scroll - pinScroll, 0), maxShift);
9
- const stops = [0, pinScroll, pinScroll + maxShift, scrollRange].filter((scroll) => scroll >= 0 && scroll <= scrollRange);
10
- return stops
11
- .filter((scroll, index) => stops.indexOf(scroll) === index)
12
- .map((scroll) => ({
9
+ return PsTableStickyHeadService.stops([0, pinScroll, pinScroll + maxShift, scrollRange], scrollRange).map((scroll) => ({
13
10
  offset: scroll / scrollRange,
14
11
  shift: shiftAt(scroll),
15
12
  }));
16
13
  }
14
+ static backdropKeyframes(pinScroll, fade, scrollRange) {
15
+ if (scrollRange <= 0)
16
+ return [];
17
+ const opacityAt = (scroll) => Math.min(Math.max((scroll - pinScroll + fade) / fade, 0), 1);
18
+ return PsTableStickyHeadService.stops([0, pinScroll - fade, pinScroll, scrollRange], scrollRange).map((scroll) => ({ offset: scroll / scrollRange, opacity: opacityAt(scroll) }));
19
+ }
20
+ static stops(scrolls, scrollRange) {
21
+ const inside = scrolls.filter((scroll) => scroll >= 0 && scroll <= scrollRange);
22
+ return inside.filter((scroll, index) => inside.indexOf(scroll) === index);
23
+ }
17
24
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.usePsStickyHead = void 0;
4
4
  const react_1 = require("react");
5
+ const ps_table_sticky_config_1 = require("../constants/ps-table-sticky-config");
5
6
  const ps_table_scroll_service_1 = require("../services/ps-table-scroll-service");
6
7
  const ps_table_sticky_head_service_1 = require("../services/ps-table-sticky-head-service");
7
8
  const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, sentinelRef, stickyTop, }) => {
@@ -22,7 +23,7 @@ const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, sentinelR
22
23
  const scroller = ps_table_scroll_service_1.PsTableScrollService.parent(region);
23
24
  const Timeline = window.ScrollTimeline;
24
25
  const isDriven = scroller !== null && Timeline !== undefined;
25
- let animation = null;
26
+ let animations = [];
26
27
  let frame = 0;
27
28
  const maxShift = () => Math.max(table.offsetHeight - head.offsetHeight, 0);
28
29
  const drive = () => {
@@ -35,23 +36,38 @@ const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, sentinelR
35
36
  backdrop.getBoundingClientRect().bottom -
36
37
  regionTop;
37
38
  const pinScroll = table.getBoundingClientRect().top + scroller.scrollTop - pinnedBottom;
38
- const keyframes = ps_table_sticky_head_service_1.PsTableStickyHeadService.keyframes(pinScroll, maxShift(), scroller.scrollHeight - scroller.clientHeight);
39
- if (animation !== null)
40
- animation.cancel();
41
- animation =
42
- keyframes.length === 0
43
- ? null
44
- : head.animate(keyframes.map((keyframe) => ({
45
- offset: keyframe.offset,
46
- transform: `translateY(${keyframe.shift}px)`,
47
- })), { fill: 'both', timeline: new Timeline({ axis: 'block', source: scroller }) });
39
+ const barPinScroll = sentinel.getBoundingClientRect().top -
40
+ scrollerTop +
41
+ scroller.scrollTop -
42
+ Number.parseFloat(getComputedStyle(region).top);
43
+ const scrollRange = scroller.scrollHeight - scroller.clientHeight;
44
+ const timeline = new Timeline({ axis: 'block', source: scroller });
45
+ const headFrames = ps_table_sticky_head_service_1.PsTableStickyHeadService.keyframes(pinScroll, maxShift(), scrollRange);
46
+ const backdropFrames = ps_table_sticky_head_service_1.PsTableStickyHeadService.backdropKeyframes(barPinScroll, ps_table_sticky_config_1.PsTableStickyConfig.backdropFadePx, scrollRange);
47
+ animations.forEach((animation) => animation.cancel());
48
+ animations =
49
+ headFrames.length === 0
50
+ ? []
51
+ : [
52
+ head.animate(headFrames.map((keyframe) => ({
53
+ offset: keyframe.offset,
54
+ transform: `translateY(${keyframe.shift}px)`,
55
+ })), { fill: 'both', timeline }),
56
+ backdrop.animate(backdropFrames.map((keyframe) => ({
57
+ offset: keyframe.offset,
58
+ opacity: keyframe.opacity,
59
+ })), { fill: 'both', timeline }),
60
+ ];
48
61
  };
49
62
  const follow = () => (head.style.transform = `translateY(${ps_table_sticky_head_service_1.PsTableStickyHeadService.offset(backdrop.getBoundingClientRect().bottom, table.getBoundingClientRect().top, table.offsetHeight, head.offsetHeight)}px)`);
50
63
  const check = () => {
64
+ const stuck = sentinel.getBoundingClientRect().top < region.getBoundingClientRect().top - 1;
51
65
  frame = 0;
52
- setIsStuck(sentinel.getBoundingClientRect().top < region.getBoundingClientRect().top - 1);
53
- if (!isDriven)
54
- follow();
66
+ setIsStuck(stuck);
67
+ if (isDriven)
68
+ return;
69
+ backdrop.style.opacity = stuck ? '1' : '0';
70
+ follow();
55
71
  };
56
72
  const schedule = () => {
57
73
  if (frame === 0)
@@ -72,9 +88,9 @@ const usePsStickyHead = ({ backdropRef, headRef, layoutKey, regionRef, sentinelR
72
88
  window.addEventListener('resize', measure);
73
89
  return () => {
74
90
  observer.disconnect();
75
- if (animation !== null)
76
- animation.cancel();
91
+ animations.forEach((animation) => animation.cancel());
77
92
  head.style.transform = '';
93
+ backdrop.style.opacity = '';
78
94
  window.removeEventListener('scroll', schedule, { capture: true });
79
95
  window.removeEventListener('resize', measure);
80
96
  if (frame !== 0)
@@ -0,0 +1,4 @@
1
+ export interface PsTableStickyBackdropKeyframe {
2
+ offset: number;
3
+ opacity: number;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,8 @@
1
+ import type { PsTableStickyBackdropKeyframe } from '../interfaces/ps-table-sticky-backdrop-keyframe';
1
2
  import type { PsTableStickyHeadKeyframe } from '../interfaces/ps-table-sticky-head-keyframe';
2
3
  export declare class PsTableStickyHeadService {
3
4
  static offset(pinnedBottom: number, tableTop: number, tableHeight: number, headHeight: number): number;
4
5
  static keyframes(pinScroll: number, maxShift: number, scrollRange: number): PsTableStickyHeadKeyframe[];
6
+ static backdropKeyframes(pinScroll: number, fade: number, scrollRange: number): PsTableStickyBackdropKeyframe[];
7
+ private static stops;
5
8
  }
@@ -9,13 +9,20 @@ class PsTableStickyHeadService {
9
9
  if (scrollRange <= 0)
10
10
  return [];
11
11
  const shiftAt = (scroll) => Math.min(Math.max(scroll - pinScroll, 0), maxShift);
12
- const stops = [0, pinScroll, pinScroll + maxShift, scrollRange].filter((scroll) => scroll >= 0 && scroll <= scrollRange);
13
- return stops
14
- .filter((scroll, index) => stops.indexOf(scroll) === index)
15
- .map((scroll) => ({
12
+ return PsTableStickyHeadService.stops([0, pinScroll, pinScroll + maxShift, scrollRange], scrollRange).map((scroll) => ({
16
13
  offset: scroll / scrollRange,
17
14
  shift: shiftAt(scroll),
18
15
  }));
19
16
  }
17
+ static backdropKeyframes(pinScroll, fade, scrollRange) {
18
+ if (scrollRange <= 0)
19
+ return [];
20
+ const opacityAt = (scroll) => Math.min(Math.max((scroll - pinScroll + fade) / fade, 0), 1);
21
+ return PsTableStickyHeadService.stops([0, pinScroll - fade, pinScroll, scrollRange], scrollRange).map((scroll) => ({ offset: scroll / scrollRange, opacity: opacityAt(scroll) }));
22
+ }
23
+ static stops(scrolls, scrollRange) {
24
+ const inside = scrolls.filter((scroll) => scroll >= 0 && scroll <= scrollRange);
25
+ return inside.filter((scroll, index) => inside.indexOf(scroll) === index);
26
+ }
20
27
  }
21
28
  exports.PsTableStickyHeadService = PsTableStickyHeadService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-table-components",
3
- "version": "1.28.0",
3
+ "version": "1.28.1",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",