react-panel-layout 0.4.2 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FloatingPanelFrame-6W5OexYe.js +98 -0
- package/dist/FloatingPanelFrame-6W5OexYe.js.map +1 -0
- package/dist/FloatingPanelFrame-D9Cp2al1.cjs +2 -0
- package/dist/FloatingPanelFrame-D9Cp2al1.cjs.map +1 -0
- package/dist/GridLayout-BzrIDrC9.js +1465 -0
- package/dist/GridLayout-BzrIDrC9.js.map +1 -0
- package/dist/GridLayout-ZrOhoLLB.cjs +2 -0
- package/dist/GridLayout-ZrOhoLLB.cjs.map +1 -0
- package/dist/PanelSystemContext.d.ts +9 -0
- package/dist/components/grid/GridLayerList.d.ts +3 -0
- package/dist/components/grid/GridLayout.d.ts +5 -0
- package/dist/components/paneling/FloatingPanelFrame.d.ts +4 -0
- package/dist/components/window/FloatingWindow.d.ts +15 -0
- package/dist/config/panelRouter.d.ts +2 -2
- package/dist/config.cjs +1 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constants/styles.d.ts +3 -1
- package/dist/floating.cjs +1 -1
- package/dist/floating.js +1 -1
- package/dist/hooks/ContentCacheContext.d.ts +59 -0
- package/dist/hooks/useContainerScroll.d.ts +23 -0
- package/dist/hooks/useContentCache.d.ts +67 -0
- package/dist/hooks/useDocumentScroll.d.ts +13 -0
- package/dist/hooks/useScrollContainer.d.ts +21 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +950 -878
- package/dist/index.js.map +1 -1
- package/dist/modules/grid/GridLayoutContext.d.ts +5 -0
- package/dist/modules/grid/resizeHandles.d.ts +14 -0
- package/dist/modules/grid/trackUtils.d.ts +29 -0
- package/dist/modules/grid/useGridTracks.d.ts +6 -15
- package/dist/modules/grid/useLayerInteractions.d.ts +2 -1
- package/dist/modules/window/useFloatingState.d.ts +9 -0
- package/dist/pivot.cjs +1 -1
- package/dist/pivot.js +1 -1
- package/dist/sticky-header/StickyHeader.d.ts +53 -0
- package/dist/sticky-header/index.d.ts +7 -0
- package/dist/sticky-header/types.d.ts +50 -0
- package/dist/sticky-header.cjs +2 -0
- package/dist/sticky-header.cjs.map +1 -0
- package/dist/sticky-header.js +198 -0
- package/dist/sticky-header.js.map +1 -0
- package/dist/styles-CA2_zLZt.js +52 -0
- package/dist/{styles-DcG3aIFx.cjs.map → styles-CA2_zLZt.js.map} +1 -1
- package/dist/styles-PsqGOEJP.cjs +2 -0
- package/dist/styles-PsqGOEJP.cjs.map +1 -0
- package/dist/types.d.ts +79 -4
- package/dist/useIsomorphicLayoutEffect-DGRNF4Lf.cjs +2 -0
- package/dist/useIsomorphicLayoutEffect-DGRNF4Lf.cjs.map +1 -0
- package/dist/useIsomorphicLayoutEffect-DhmEnmZ_.js +6 -0
- package/dist/useIsomorphicLayoutEffect-DhmEnmZ_.js.map +1 -0
- package/dist/usePivot-BS-DGfwd.cjs +2 -0
- package/dist/usePivot-BS-DGfwd.cjs.map +1 -0
- package/dist/usePivot-BvOGxLQQ.js +124 -0
- package/dist/usePivot-BvOGxLQQ.js.map +1 -0
- package/dist/utils/css.d.ts +19 -0
- package/package.json +6 -1
- package/dist/FloatingPanelFrame-SOrLGjZd.js +0 -67
- package/dist/FloatingPanelFrame-SOrLGjZd.js.map +0 -1
- package/dist/FloatingPanelFrame-XtBcHANI.cjs +0 -2
- package/dist/FloatingPanelFrame-XtBcHANI.cjs.map +0 -1
- package/dist/GridLayout-CLvW8jID.js +0 -1352
- package/dist/GridLayout-CLvW8jID.js.map +0 -1
- package/dist/GridLayout-qufTyOQM.cjs +0 -2
- package/dist/GridLayout-qufTyOQM.cjs.map +0 -1
- package/dist/styles-DcG3aIFx.cjs +0 -2
- package/dist/styles-w0ZixggV.js +0 -51
- package/dist/styles-w0ZixggV.js.map +0 -1
- package/dist/usePivot-C8q0pMgW.cjs +0 -2
- package/dist/usePivot-C8q0pMgW.cjs.map +0 -1
- package/dist/usePivot-z9gumDf-.js +0 -97
- package/dist/usePivot-z9gumDf-.js.map +0 -1
|
@@ -27,6 +27,11 @@ export type GridLayoutContextValue = {
|
|
|
27
27
|
handleLayerPointerDown: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
28
28
|
getLayerRenderState: (layer: LayerDefinition) => GridLayerRenderState;
|
|
29
29
|
getLayerHandleProps: (layerId: string) => GridLayerHandleProps;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the GridLayout is mounted at root level.
|
|
32
|
+
* When true, scrollable layers should delegate to browser's native scroll.
|
|
33
|
+
*/
|
|
34
|
+
isRootLevel: boolean;
|
|
30
35
|
};
|
|
31
36
|
export declare const GridLayoutProvider: React.FC<React.PropsWithChildren<{
|
|
32
37
|
value: GridLayoutContextValue;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Resize handle configuration computation for grid tracks.
|
|
3
|
+
*/
|
|
4
|
+
import type { GridTrack } from "../../types";
|
|
5
|
+
export type TrackHandleConfig = {
|
|
6
|
+
trackIndex: number;
|
|
7
|
+
align: "start" | "end";
|
|
8
|
+
span: {
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export declare const computeColumnResizeHandles: (tracks: GridTrack[], areas: string[][]) => TrackHandleConfig[];
|
|
14
|
+
export declare const computeRowResizeHandles: (tracks: GridTrack[], areas: string[][]) => TrackHandleConfig[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Grid track sizing utilities.
|
|
3
|
+
*/
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
import type { GridTrack } from "../../types";
|
|
6
|
+
import { type GridDirection, type ParsedGap } from "../../utils/css";
|
|
7
|
+
export type TrackDirection = GridDirection;
|
|
8
|
+
export declare const createTrackKey: (direction: TrackDirection, index: number) => string;
|
|
9
|
+
export declare const getTrackSize: (track: GridTrack, trackSizes: Record<string, number>, direction: TrackDirection, index: number) => string;
|
|
10
|
+
export declare const buildTrackTemplateString: (tracks: GridTrack[], trackSizes: Record<string, number>, direction: TrackDirection) => string;
|
|
11
|
+
export declare const extractInitialTrackSizes: (tracks: GridTrack[], direction: TrackDirection) => Record<string, number>;
|
|
12
|
+
export type ResolveTrackSizeParams = {
|
|
13
|
+
trackSizes: Record<string, number>;
|
|
14
|
+
track: GridTrack;
|
|
15
|
+
direction: TrackDirection;
|
|
16
|
+
trackIndex: number;
|
|
17
|
+
containerRef: React.RefObject<HTMLElement | null> | undefined;
|
|
18
|
+
};
|
|
19
|
+
export declare const resolveCurrentTrackSize: ({ trackSizes, track, direction, trackIndex, containerRef, }: ResolveTrackSizeParams) => number;
|
|
20
|
+
export type EffectiveMaxSizeParams = {
|
|
21
|
+
track: GridTrack;
|
|
22
|
+
tracks: GridTrack[];
|
|
23
|
+
trackIndex: number;
|
|
24
|
+
direction: TrackDirection;
|
|
25
|
+
containerRef: React.RefObject<HTMLElement | null> | undefined;
|
|
26
|
+
gapSizes: ParsedGap;
|
|
27
|
+
};
|
|
28
|
+
export declare const calculateEffectiveMaxSize: ({ track, tracks, trackIndex, direction, containerRef, gapSizes, }: EffectiveMaxSizeParams) => number | undefined;
|
|
29
|
+
export declare const applyTrackConstraints: (size: number, minSize?: number, maxSize?: number) => number;
|
|
@@ -3,24 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import type { PanelLayoutConfig } from "../../types";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
type ParsedGap = {
|
|
15
|
-
rowGap: number;
|
|
16
|
-
columnGap: number;
|
|
17
|
-
};
|
|
18
|
-
type TrackDirection = "row" | "col";
|
|
19
|
-
export declare const useGridTracks: (config: PanelLayoutConfig, styleProp?: React.CSSProperties) => {
|
|
6
|
+
import { type ParsedGap } from "../../utils/css";
|
|
7
|
+
import { type TrackDirection } from "./trackUtils";
|
|
8
|
+
import { type TrackHandleConfig } from "./resizeHandles";
|
|
9
|
+
export type { TrackHandleConfig } from "./resizeHandles";
|
|
10
|
+
export type { TrackDirection } from "./trackUtils";
|
|
11
|
+
export declare const useGridTracks: (config: PanelLayoutConfig, styleProp?: React.CSSProperties, containerRef?: React.RefObject<HTMLElement | null>) => {
|
|
20
12
|
columnHandles: TrackHandleConfig[];
|
|
21
13
|
rowHandles: TrackHandleConfig[];
|
|
22
14
|
gapSizes: ParsedGap;
|
|
23
15
|
gridStyle: React.CSSProperties;
|
|
24
16
|
handleResize: (direction: TrackDirection, trackIndex: number, delta: number) => void;
|
|
25
17
|
};
|
|
26
|
-
export {};
|
|
@@ -3,8 +3,9 @@ import type { GridLayoutContextValue } from "./GridLayoutContext";
|
|
|
3
3
|
type UseLayerInteractionsArgs = {
|
|
4
4
|
layers: LayerDefinition[];
|
|
5
5
|
layerById: Map<string, LayerDefinition>;
|
|
6
|
+
isRootLevel: boolean;
|
|
6
7
|
};
|
|
7
|
-
export declare const useLayerInteractions: ({ layers, layerById, }: UseLayerInteractionsArgs) => {
|
|
8
|
+
export declare const useLayerInteractions: ({ layers, layerById, isRootLevel, }: UseLayerInteractionsArgs) => {
|
|
8
9
|
providerValue: GridLayoutContextValue;
|
|
9
10
|
draggingLayerId: string | null;
|
|
10
11
|
resizingLayerId: string | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LayerDefinition, WindowPosition, WindowSize } from "../../types";
|
|
2
|
+
export declare const useFloatingState: (layers: LayerDefinition[]) => {
|
|
3
|
+
getPosition: (layerId: string) => WindowPosition;
|
|
4
|
+
getSize: (layerId: string) => WindowSize;
|
|
5
|
+
getZIndex: (layerId: string) => number | undefined;
|
|
6
|
+
updatePosition: (layerId: string, position: WindowPosition) => void;
|
|
7
|
+
updateSize: (layerId: string, size: WindowSize) => void;
|
|
8
|
+
close: (layerId: string) => void;
|
|
9
|
+
};
|
package/dist/pivot.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./usePivot-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./usePivot-BS-DGfwd.cjs");exports.usePivot=e.usePivot;
|
|
2
2
|
//# sourceMappingURL=pivot.cjs.map
|
package/dist/pivot.js
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file StickyHeader component for native app-like overscroll experience.
|
|
3
|
+
*
|
|
4
|
+
* Displays cover content that expands during overscroll/bounce,
|
|
5
|
+
* providing a native app-like pull effect commonly seen in iOS apps.
|
|
6
|
+
*
|
|
7
|
+
* This component is designed for SPAs, PWAs, and hybrid apps where
|
|
8
|
+
* the browser's pull-to-refresh is disabled and bounce effects
|
|
9
|
+
* should display content rather than empty space.
|
|
10
|
+
*
|
|
11
|
+
* Supports both document-level scroll and nested scroll containers.
|
|
12
|
+
*/
|
|
13
|
+
import * as React from "react";
|
|
14
|
+
import type { StickyHeaderProps } from "./types";
|
|
15
|
+
/**
|
|
16
|
+
* StickyHeader provides a native app-like overscroll experience.
|
|
17
|
+
*
|
|
18
|
+
* When the user pulls down beyond the top of the page (overscroll/bounce),
|
|
19
|
+
* the cover content expands to fill the visible area, similar to
|
|
20
|
+
* native iOS/Android app behavior.
|
|
21
|
+
*
|
|
22
|
+
* Also supports nested scroll containers with overflow: scroll/auto.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* // Basic usage
|
|
27
|
+
* <StickyHeader cover={<img src="/hero.jpg" alt="Hero" />}>
|
|
28
|
+
* <header>
|
|
29
|
+
* <h1>My App</h1>
|
|
30
|
+
* </header>
|
|
31
|
+
* </StickyHeader>
|
|
32
|
+
*
|
|
33
|
+
* // With state callback
|
|
34
|
+
* <StickyHeader
|
|
35
|
+
* cover={<img src="/hero.jpg" alt="Hero" />}
|
|
36
|
+
* onStateChange={({ isStuck }) => console.log('Stuck:', isStuck)}
|
|
37
|
+
* >
|
|
38
|
+
* <header>
|
|
39
|
+
* <h1>My App</h1>
|
|
40
|
+
* </header>
|
|
41
|
+
* </StickyHeader>
|
|
42
|
+
*
|
|
43
|
+
* // With render function
|
|
44
|
+
* <StickyHeader cover={<img src="/hero.jpg" alt="Hero" />}>
|
|
45
|
+
* {({ isStuck }) => (
|
|
46
|
+
* <header style={{ background: isStuck ? 'white' : 'transparent' }}>
|
|
47
|
+
* <h1>My App</h1>
|
|
48
|
+
* </header>
|
|
49
|
+
* )}
|
|
50
|
+
* </StickyHeader>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare const StickyHeader: React.FC<StickyHeaderProps>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file StickyHeader entry point - Native app-like overscroll experience
|
|
3
|
+
*
|
|
4
|
+
* Import via: import { StickyHeader } from "react-panel-layout/sticky-header";
|
|
5
|
+
*/
|
|
6
|
+
export { StickyHeader } from "./StickyHeader";
|
|
7
|
+
export type { StickyHeaderProps, StickyHeaderState } from "./types";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Type definitions for the StickyHeader component.
|
|
3
|
+
*
|
|
4
|
+
* StickyHeader provides a native app-like experience for SPAs/PWAs
|
|
5
|
+
* by displaying cover content during overscroll/bounce.
|
|
6
|
+
*/
|
|
7
|
+
import type * as React from "react";
|
|
8
|
+
/**
|
|
9
|
+
* State information exposed by StickyHeader.
|
|
10
|
+
*/
|
|
11
|
+
export type StickyHeaderState = {
|
|
12
|
+
/**
|
|
13
|
+
* Whether the header is stuck at the top of the scroll container.
|
|
14
|
+
* True when the header has scrolled to (or past) the top edge.
|
|
15
|
+
*/
|
|
16
|
+
isStuck: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The current scroll offset relative to the header.
|
|
19
|
+
* - Positive: Header has scrolled up past the viewport
|
|
20
|
+
* - Zero: Header is at the top
|
|
21
|
+
* - Negative: Overscroll/bounce (pulling down beyond top)
|
|
22
|
+
*/
|
|
23
|
+
scrollOffset: number;
|
|
24
|
+
/**
|
|
25
|
+
* The detected scroll container type.
|
|
26
|
+
* - "document": Using window/document scroll
|
|
27
|
+
* - "container": Using a nested overflow:scroll/auto element
|
|
28
|
+
*/
|
|
29
|
+
containerType: "document" | "container";
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Props for the StickyHeader component.
|
|
33
|
+
*/
|
|
34
|
+
export type StickyHeaderProps = {
|
|
35
|
+
/**
|
|
36
|
+
* Cover content displayed behind the header.
|
|
37
|
+
* This content expands during overscroll/bounce to create
|
|
38
|
+
* a native app-like pull effect.
|
|
39
|
+
*/
|
|
40
|
+
cover: React.ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* Main header content that scrolls over the cover.
|
|
43
|
+
* Can be a ReactNode or a render function receiving state.
|
|
44
|
+
*/
|
|
45
|
+
children: React.ReactNode | ((state: StickyHeaderState) => React.ReactNode);
|
|
46
|
+
/**
|
|
47
|
+
* Callback fired when sticky state changes.
|
|
48
|
+
*/
|
|
49
|
+
onStateChange?: (state: StickyHeaderState) => void;
|
|
50
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("react/jsx-runtime"),D=require("react"),P=require("./useIsomorphicLayoutEffect-DGRNF4Lf.cjs");function X(e){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const t in e)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:()=>e[t]})}}return r.default=e,Object.freeze(r)}const s=X(D);function Y(e){const[r,t]=s.useState(()=>typeof window>"u"?{scrollTop:0,scrollLeft:0}:e?{scrollTop:e.scrollTop,scrollLeft:e.scrollLeft}:{scrollTop:window.scrollY,scrollLeft:window.scrollX});return s.useEffect(()=>{const n=()=>{t(e?{scrollTop:e.scrollTop,scrollLeft:e.scrollLeft}:{scrollTop:window.scrollY,scrollLeft:window.scrollX})};n();const l=e??window;return l.addEventListener("scroll",n,{passive:!0}),()=>{l.removeEventListener("scroll",n)}},[e]),r}const j=new Map,F=e=>{const{box:r="content-box"}=e,t=`resize-box:${r}`,n=j.get(t);if(n)return n;const l=new class{#e=new Map;#t=new ResizeObserver((i,u)=>{i.forEach(f=>{const y=this.#e.get(f.target);y&&y(f,u)})});observe(i,u){return this.#e.set(i,u),this.#t.observe(i,e),()=>{this.#e.delete(i),this.#t.unobserve(i)}}};return j.set(t,l),l};function _(e,{box:r}){const[t,n]=s.useState(null),l=e.current;s.useEffect(()=>l?F({box:r}).observe(l,f=>{n(f)}):void 0,[r,l]);const i=s.useMemo(()=>{if(!t)return null;if(t.borderBoxSize?.length>0){const u=t.borderBoxSize[0];return new DOMRect(0,0,u.inlineSize,u.blockSize)}return t.contentRect},[t]);return{entry:t,rect:i}}function K(e){const r=getComputedStyle(e),t=r.overflowY,n=r.overflowX;return t==="scroll"||t==="auto"||n==="scroll"||n==="auto"}function W(e){if(!e)return null;let r=e.parentElement;for(;r;){if(K(r))return r;r=r.parentElement}return null}function G(e){const[r,t]=s.useState(null);return s.useEffect(()=>{const n=e.current;if(!n){t(null);return}const l=W(n);t(l)},[e]),r}const L={position:"relative"},J={position:"relative",paddingTop:"env(safe-area-inset-top)",boxSizing:"border-box"},Q={zIndex:1};function U(e){return e?{position:"fixed",top:0,left:0,right:0,opacity:0,zIndex:0,userSelect:"none",pointerEvents:"none"}:{position:"absolute",top:0,left:0,right:0,opacity:0,zIndex:0,userSelect:"none",pointerEvents:"none"}}function V(e){return e?L:{...L,overflow:"hidden"}}const B=({cover:e,children:r,onStateChange:t})=>{const n=s.useRef(null),l=s.useRef(null),i=s.useRef(null),u=G(i),f=u===null,{scrollTop:y}=Y(u),O=s.useRef(y);O.current=y;const w=s.useRef(null),{rect:k}=_(n,{});Object.is(w.current,k)||(w.current=k);const M=s.useRef(null),[z,$]=s.useState({isStuck:!1,scrollOffset:0,containerType:"document"}),C=s.useRef(z),x=s.useCallback(a=>{const c=C.current;(c.isStuck!==a.isStuck||c.scrollOffset!==a.scrollOffset||c.containerType!==a.containerType)&&(C.current=a,$(a),t?.(a))},[t]);P.useIsomorphicLayoutEffect(()=>{const a=n.current,c=l.current,H=i.current;if(!c||!a||!H)return;let v=Number.NaN,b=null,S=!1;const I=()=>{const o=w.current;if(!o)return;const h=O.current;if(f){const p=o.height-h;p!==v&&(c.style.opacity="1",c.style.height=`${p}px`,v=p),(o.x>=0||o.y>=0||o.width>0)&&b!==o&&(c.style.left=`${o.x}px`,c.style.top=`${o.y}px`,c.style.width=`${o.width}px`,b=o);const d=h>0;d!==S&&(S=d,x({isStuck:d,scrollOffset:h,containerType:"document"}))}else{const p=u;if(!p)return;const d=p.getBoundingClientRect();M.current=d;const q=a.getBoundingClientRect().top-d.top+h,g=h-q,R=Math.max(0,o.height-g);R!==v&&(c.style.opacity="1",c.style.height=`${R}px`,v=R),b!==o&&(c.style.left="0",c.style.width=`${o.width}px`,b=o);const N=Math.max(0,g);c.style.top=`${N}px`;const T=g>0;T!==S&&(S=T,x({isStuck:T,scrollOffset:g,containerType:"container"}))}};let E=requestAnimationFrame(function o(){I(),E=requestAnimationFrame(o)});return()=>{cancelAnimationFrame(E)}},[f,u,x]);const A=typeof r=="function"?r(z):r;return m.jsxs("div",{ref:i,style:V(f),children:[m.jsx("div",{ref:l,style:U(f),children:e}),m.jsx("div",{ref:n,style:J,children:m.jsx("div",{style:Q,children:A})})]})};B.displayName="StickyHeader";exports.StickyHeader=B;
|
|
2
|
+
//# sourceMappingURL=sticky-header.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sticky-header.cjs","sources":["../src/hooks/useContainerScroll.ts","../src/hooks/useResizeObserver.tsx","../src/hooks/useScrollContainer.ts","../src/sticky-header/StickyHeader.tsx"],"sourcesContent":["/**\n * @file useContainerScroll - Track scroll position of a container or document\n *\n * Tracks scroll position for either a specific scroll container element\n * or the document (when container is null).\n */\nimport * as React from \"react\";\n\nexport type ScrollPosition = {\n /** Vertical scroll position */\n scrollTop: number;\n /** Horizontal scroll position */\n scrollLeft: number;\n};\n\n/**\n * Track scroll position of a container element or the document.\n *\n * @param container - Scroll container element, or null for document scroll\n * @returns Current scroll position\n *\n * @example\n * ```tsx\n * // For document scroll\n * const scroll = useContainerScroll(null);\n *\n * // For container scroll\n * const containerRef = useRef<HTMLDivElement>(null);\n * const scroll = useContainerScroll(containerRef.current);\n * ```\n */\nexport function useContainerScroll(container: HTMLElement | null): ScrollPosition {\n const [position, setPosition] = React.useState<ScrollPosition>(() => {\n if (typeof window === \"undefined\") {\n return { scrollTop: 0, scrollLeft: 0 };\n }\n\n if (container) {\n return {\n scrollTop: container.scrollTop,\n scrollLeft: container.scrollLeft,\n };\n }\n\n return {\n scrollTop: window.scrollY,\n scrollLeft: window.scrollX,\n };\n });\n\n React.useEffect(() => {\n const handleScroll = () => {\n if (container) {\n setPosition({\n scrollTop: container.scrollTop,\n scrollLeft: container.scrollLeft,\n });\n } else {\n setPosition({\n scrollTop: window.scrollY,\n scrollLeft: window.scrollX,\n });\n }\n };\n\n // Initialize position\n handleScroll();\n\n const target = container ?? window;\n target.addEventListener(\"scroll\", handleScroll, { passive: true });\n\n return () => {\n target.removeEventListener(\"scroll\", handleScroll);\n };\n }, [container]);\n\n return position;\n}\n","/**\n * @file Shared useResizeObserver hook with cached observer instances.\n */\nimport * as React from \"react\";\n\ntype Unobserve = () => void;\ntype Callback = (entry: ResizeObserverEntry, observer: ResizeObserver) => void;\ntype SharedObserver = {\n observe: (target: Element, callback: Callback) => Unobserve;\n};\nconst observerCache = new Map<string, SharedObserver>();\nconst getSharedObserver = (options: ResizeObserverOptions) => {\n const { box = \"content-box\" } = options;\n const observerKey = `resize-box:${box}`;\n const cached = observerCache.get(observerKey);\n if (cached) {\n return cached;\n }\n const observer = new (class {\n #callbackMap = new Map<Element, Callback>();\n #resizeObserver = new ResizeObserver((entries, observer) => {\n entries.forEach((entry) => {\n const callback = this.#callbackMap.get(entry.target);\n if (callback) {\n callback(entry, observer);\n }\n });\n });\n observe(target: Element, callback: Callback) {\n this.#callbackMap.set(target, callback);\n this.#resizeObserver.observe(target, options);\n return () => {\n this.#callbackMap.delete(target);\n this.#resizeObserver.unobserve(target);\n };\n }\n })();\n observerCache.set(observerKey, observer);\n\n return observer;\n};\n/**\n * Observe size changes for a given element reference using shared resize observers.\n *\n * @param ref - Ref holding the element whose size to monitor.\n * @param options - Resize observer configuration.\n * @returns Latest resize entry and a derived DOMRect snapshot.\n */\nexport function useResizeObserver<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n { box }: ResizeObserverOptions,\n) {\n const [entry, setEntry] = React.useState<ResizeObserverEntry | null>(null);\n const target = ref.current;\n\n React.useEffect(() => {\n if (!target) {\n return;\n }\n\n const observer = getSharedObserver({ box });\n return observer.observe(target, (nextEntry) => {\n setEntry(nextEntry);\n });\n }, [box, target]);\n\n const rect = React.useMemo(() => {\n if (!entry) {\n return null;\n }\n\n if (entry.borderBoxSize?.length > 0) {\n const size = entry.borderBoxSize[0];\n return new DOMRect(0, 0, size.inlineSize, size.blockSize);\n }\n\n return entry.contentRect;\n }, [entry]);\n\n return { entry, rect };\n}\n","/**\n * @file useScrollContainer - Detect the nearest scrollable ancestor\n *\n * Traverses the DOM tree to find the nearest ancestor with overflow: scroll/auto.\n * Returns null if the document is the scroll container.\n */\nimport * as React from \"react\";\n\n/**\n * Check if an element is a scroll container.\n */\nfunction isScrollContainer(element: Element): boolean {\n const style = getComputedStyle(element);\n const overflowY = style.overflowY;\n const overflowX = style.overflowX;\n\n return (\n overflowY === \"scroll\" ||\n overflowY === \"auto\" ||\n overflowX === \"scroll\" ||\n overflowX === \"auto\"\n );\n}\n\n/**\n * Find the nearest scrollable ancestor of an element.\n *\n * @param element - Starting element to search from\n * @returns The nearest scrollable ancestor, or null if document is the container\n */\nfunction findScrollContainer(element: Element | null): HTMLElement | null {\n if (!element) {\n return null;\n }\n\n // eslint-disable-next-line no-restricted-syntax -- DOM traversal requires let\n let current = element.parentElement;\n\n while (current) {\n if (isScrollContainer(current)) {\n return current;\n }\n current = current.parentElement;\n }\n\n return null;\n}\n\n/**\n * Hook to detect the nearest scrollable ancestor of a ref element.\n *\n * @param ref - Ref to the element to find scroll container for\n * @returns The nearest scrollable ancestor element, or null if document is the container\n *\n * @example\n * ```tsx\n * const elementRef = useRef<HTMLDivElement>(null);\n * const scrollContainer = useScrollContainer(elementRef);\n * // scrollContainer is HTMLElement if in nested scroll, null if document scroll\n * ```\n */\nexport function useScrollContainer<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n): HTMLElement | null {\n const [container, setContainer] = React.useState<HTMLElement | null>(null);\n\n React.useEffect(() => {\n const element = ref.current;\n if (!element) {\n setContainer(null);\n return;\n }\n\n const scrollContainer = findScrollContainer(element);\n setContainer(scrollContainer);\n }, [ref]);\n\n return container;\n}\n","/**\n * @file StickyHeader component for native app-like overscroll experience.\n *\n * Displays cover content that expands during overscroll/bounce,\n * providing a native app-like pull effect commonly seen in iOS apps.\n *\n * This component is designed for SPAs, PWAs, and hybrid apps where\n * the browser's pull-to-refresh is disabled and bounce effects\n * should display content rather than empty space.\n *\n * Supports both document-level scroll and nested scroll containers.\n */\nimport * as React from \"react\";\nimport { useContainerScroll } from \"../hooks/useContainerScroll\";\nimport { useIsomorphicLayoutEffect } from \"../hooks/useIsomorphicLayoutEffect\";\nimport { useResizeObserver } from \"../hooks/useResizeObserver\";\nimport { useScrollContainer } from \"../hooks/useScrollContainer\";\nimport type { StickyHeaderProps, StickyHeaderState } from \"./types\";\n\nconst baseStyle: React.CSSProperties = {\n position: \"relative\",\n};\n\nconst headerStyle: React.CSSProperties = {\n position: \"relative\",\n paddingTop: \"env(safe-area-inset-top)\",\n boxSizing: \"border-box\",\n};\n\nconst bodyStyle: React.CSSProperties = {\n zIndex: 1,\n};\n\n/**\n * Get cover styles based on container type.\n */\nfunction getCoverStyle(isDocumentScroll: boolean): React.CSSProperties {\n if (isDocumentScroll) {\n return {\n position: \"fixed\",\n top: 0,\n left: 0,\n right: 0,\n opacity: 0,\n zIndex: 0,\n userSelect: \"none\",\n pointerEvents: \"none\",\n };\n }\n\n // For nested scroll containers, use absolute positioning\n return {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n opacity: 0,\n zIndex: 0,\n userSelect: \"none\",\n pointerEvents: \"none\",\n };\n}\n\n/**\n * Get wrapper styles for nested scroll containers.\n */\nfunction getWrapperStyle(isDocumentScroll: boolean): React.CSSProperties {\n if (isDocumentScroll) {\n return baseStyle;\n }\n\n return {\n ...baseStyle,\n overflow: \"hidden\",\n };\n}\n\n/**\n * StickyHeader provides a native app-like overscroll experience.\n *\n * When the user pulls down beyond the top of the page (overscroll/bounce),\n * the cover content expands to fill the visible area, similar to\n * native iOS/Android app behavior.\n *\n * Also supports nested scroll containers with overflow: scroll/auto.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <StickyHeader cover={<img src=\"/hero.jpg\" alt=\"Hero\" />}>\n * <header>\n * <h1>My App</h1>\n * </header>\n * </StickyHeader>\n *\n * // With state callback\n * <StickyHeader\n * cover={<img src=\"/hero.jpg\" alt=\"Hero\" />}\n * onStateChange={({ isStuck }) => console.log('Stuck:', isStuck)}\n * >\n * <header>\n * <h1>My App</h1>\n * </header>\n * </StickyHeader>\n *\n * // With render function\n * <StickyHeader cover={<img src=\"/hero.jpg\" alt=\"Hero\" />}>\n * {({ isStuck }) => (\n * <header style={{ background: isStuck ? 'white' : 'transparent' }}>\n * <h1>My App</h1>\n * </header>\n * )}\n * </StickyHeader>\n * ```\n */\nexport const StickyHeader: React.FC<StickyHeaderProps> = ({ cover, children, onStateChange }) => {\n const headerRef = React.useRef<HTMLDivElement>(null);\n const coverAreaRef = React.useRef<HTMLDivElement>(null);\n const wrapperRef = React.useRef<HTMLDivElement>(null);\n\n // Detect scroll container\n const scrollContainer = useScrollContainer(wrapperRef);\n const isDocumentScroll = scrollContainer === null;\n\n // Track scroll position\n const { scrollTop } = useContainerScroll(scrollContainer);\n const scrollTopRef = React.useRef(scrollTop);\n scrollTopRef.current = scrollTop;\n\n // Track header bounds\n const headerBoundRef = React.useRef<DOMRectReadOnly | null>(null);\n const { rect: headerRect } = useResizeObserver(headerRef, {});\n if (!Object.is(headerBoundRef.current, headerRect)) {\n headerBoundRef.current = headerRect;\n }\n\n // Track container bounds for nested scroll\n const containerBoundRef = React.useRef<DOMRect | null>(null);\n\n // State for render function and callback\n const [state, setState] = React.useState<StickyHeaderState>({\n isStuck: false,\n scrollOffset: 0,\n containerType: \"document\",\n });\n\n // Update state when values change\n const stateRef = React.useRef(state);\n const updateState = React.useCallback(\n (newState: StickyHeaderState) => {\n const prev = stateRef.current;\n if (prev.isStuck !== newState.isStuck || prev.scrollOffset !== newState.scrollOffset || prev.containerType !== newState.containerType) {\n stateRef.current = newState;\n setState(newState);\n onStateChange?.(newState);\n }\n },\n [onStateChange],\n );\n\n useIsomorphicLayoutEffect(() => {\n const header = headerRef.current;\n const coverArea = coverAreaRef.current;\n const wrapper = wrapperRef.current;\n if (!coverArea || !header || !wrapper) {\n return;\n }\n\n // eslint-disable-next-line no-restricted-syntax -- Performance: mutable state for RAF loop\n let prevHeight = Number.NaN;\n // eslint-disable-next-line no-restricted-syntax -- Performance: mutable state for RAF loop\n let prevHeaderBound: DOMRectReadOnly | null = null;\n // eslint-disable-next-line no-restricted-syntax -- Performance: mutable state for RAF loop\n let prevIsStuck = false;\n\n const loop = () => {\n const headerBound = headerBoundRef.current;\n if (!headerBound) {\n return;\n }\n\n const currentScrollTop = scrollTopRef.current;\n\n if (isDocumentScroll) {\n // Document scroll mode (original behavior)\n const coverAreaHeight = headerBound.height - currentScrollTop;\n if (coverAreaHeight !== prevHeight) {\n coverArea.style.opacity = \"1\";\n coverArea.style.height = `${coverAreaHeight}px`;\n prevHeight = coverAreaHeight;\n }\n\n if ((headerBound.x >= 0 || headerBound.y >= 0 || headerBound.width > 0) && prevHeaderBound !== headerBound) {\n coverArea.style.left = `${headerBound.x}px`;\n coverArea.style.top = `${headerBound.y}px`;\n coverArea.style.width = `${headerBound.width}px`;\n prevHeaderBound = headerBound;\n }\n\n // Calculate stuck state\n const isStuck = currentScrollTop > 0;\n if (isStuck !== prevIsStuck) {\n prevIsStuck = isStuck;\n updateState({\n isStuck,\n scrollOffset: currentScrollTop,\n containerType: \"document\",\n });\n }\n } else {\n // Nested container scroll mode\n const container = scrollContainer;\n if (!container) {\n return;\n }\n\n // Get container bounds\n const containerBound = container.getBoundingClientRect();\n containerBoundRef.current = containerBound;\n\n // Calculate header position relative to container\n const headerTop = header.getBoundingClientRect().top - containerBound.top + currentScrollTop;\n\n // Calculate cover height based on scroll position\n const scrollOffset = currentScrollTop - headerTop;\n const coverAreaHeight = Math.max(0, headerBound.height - scrollOffset);\n\n if (coverAreaHeight !== prevHeight) {\n coverArea.style.opacity = \"1\";\n coverArea.style.height = `${coverAreaHeight}px`;\n prevHeight = coverAreaHeight;\n }\n\n // Position cover relative to scroll\n if (prevHeaderBound !== headerBound) {\n coverArea.style.left = \"0\";\n coverArea.style.width = `${headerBound.width}px`;\n prevHeaderBound = headerBound;\n }\n\n // Calculate top position to follow scroll\n const coverTop = Math.max(0, scrollOffset);\n coverArea.style.top = `${coverTop}px`;\n\n // Calculate stuck state\n const isStuck = scrollOffset > 0;\n if (isStuck !== prevIsStuck) {\n prevIsStuck = isStuck;\n updateState({\n isStuck,\n scrollOffset,\n containerType: \"container\",\n });\n }\n }\n };\n\n // eslint-disable-next-line no-restricted-syntax -- Performance: RAF id needs reassignment\n let id = requestAnimationFrame(function animate() {\n loop();\n id = requestAnimationFrame(animate);\n });\n\n return () => {\n cancelAnimationFrame(id);\n };\n }, [isDocumentScroll, scrollContainer, updateState]);\n\n // Render children\n const renderedChildren = typeof children === \"function\" ? children(state) : children;\n\n return (\n <div ref={wrapperRef} style={getWrapperStyle(isDocumentScroll)}>\n <div ref={coverAreaRef} style={getCoverStyle(isDocumentScroll)}>\n {cover}\n </div>\n <div ref={headerRef} style={headerStyle}>\n <div style={bodyStyle}>{renderedChildren}</div>\n </div>\n </div>\n );\n};\n\nStickyHeader.displayName = \"StickyHeader\";\n"],"names":["useContainerScroll","container","position","setPosition","React","handleScroll","target","observerCache","getSharedObserver","options","box","observerKey","cached","observer","#callbackMap","#resizeObserver","entries","entry","callback","useResizeObserver","ref","setEntry","nextEntry","rect","size","isScrollContainer","element","style","overflowY","overflowX","findScrollContainer","current","useScrollContainer","setContainer","scrollContainer","baseStyle","headerStyle","bodyStyle","getCoverStyle","isDocumentScroll","getWrapperStyle","StickyHeader","cover","children","onStateChange","headerRef","coverAreaRef","wrapperRef","scrollTop","scrollTopRef","headerBoundRef","headerRect","containerBoundRef","state","setState","stateRef","updateState","newState","prev","useIsomorphicLayoutEffect","header","coverArea","wrapper","prevHeight","prevHeaderBound","prevIsStuck","loop","headerBound","currentScrollTop","coverAreaHeight","isStuck","containerBound","headerTop","scrollOffset","coverTop","id","animate","renderedChildren","jsx"],"mappings":"wdA+BO,SAASA,EAAmBC,EAA+C,CAChF,KAAM,CAACC,EAAUC,CAAW,EAAIC,EAAM,SAAyB,IACzD,OAAO,OAAW,IACb,CAAE,UAAW,EAAG,WAAY,CAAA,EAGjCH,EACK,CACL,UAAWA,EAAU,UACrB,WAAYA,EAAU,UAAA,EAInB,CACL,UAAW,OAAO,QAClB,WAAY,OAAO,OAAA,CAEtB,EAEDG,OAAAA,EAAM,UAAU,IAAM,CACpB,MAAMC,EAAe,IAAM,CAEvBF,EADEF,EACU,CACV,UAAWA,EAAU,UACrB,WAAYA,EAAU,UAAA,EAGZ,CACV,UAAW,OAAO,QAClB,WAAY,OAAO,OAAA,CAJpB,CAOL,EAGAI,EAAA,EAEA,MAAMC,EAASL,GAAa,OAC5B,OAAAK,EAAO,iBAAiB,SAAUD,EAAc,CAAE,QAAS,GAAM,EAE1D,IAAM,CACXC,EAAO,oBAAoB,SAAUD,CAAY,CACnD,CACF,EAAG,CAACJ,CAAS,CAAC,EAEPC,CACT,CCnEA,MAAMK,MAAoB,IACpBC,EAAqBC,GAAmC,CAC5D,KAAM,CAAE,IAAAC,EAAM,aAAA,EAAkBD,EAC1BE,EAAc,cAAcD,CAAG,GAC/BE,EAASL,EAAc,IAAII,CAAW,EAC5C,GAAIC,EACF,OAAOA,EAET,MAAMC,EAAW,IAAK,KAAM,CAC1BC,OAAmB,IACnBC,GAAkB,IAAI,eAAe,CAACC,EAASH,IAAa,CAC1DG,EAAQ,QAASC,GAAU,CACzB,MAAMC,EAAW,KAAKJ,GAAa,IAAIG,EAAM,MAAM,EAC/CC,GACFA,EAASD,EAAOJ,CAAQ,CAE5B,CAAC,CACH,CAAC,EACD,QAAQP,EAAiBY,EAAoB,CAC3C,YAAKJ,GAAa,IAAIR,EAAQY,CAAQ,EACtC,KAAKH,GAAgB,QAAQT,EAAQG,CAAO,EACrC,IAAM,CACX,KAAKK,GAAa,OAAOR,CAAM,EAC/B,KAAKS,GAAgB,UAAUT,CAAM,CACvC,CACF,CAAA,EAEF,OAAAC,EAAc,IAAII,EAAaE,CAAQ,EAEhCA,CACT,EAQO,SAASM,EACdC,EACA,CAAE,IAAAV,GACF,CACA,KAAM,CAACO,EAAOI,CAAQ,EAAIjB,EAAM,SAAqC,IAAI,EACnEE,EAASc,EAAI,QAEnBhB,EAAM,UAAU,IACTE,EAIYE,EAAkB,CAAE,IAAAE,EAAK,EAC1B,QAAQJ,EAASgB,GAAc,CAC7CD,EAASC,CAAS,CACpB,CAAC,EANC,OAOD,CAACZ,EAAKJ,CAAM,CAAC,EAEhB,MAAMiB,EAAOnB,EAAM,QAAQ,IAAM,CAC/B,GAAI,CAACa,EACH,OAAO,KAGT,GAAIA,EAAM,eAAe,OAAS,EAAG,CACnC,MAAMO,EAAOP,EAAM,cAAc,CAAC,EAClC,OAAO,IAAI,QAAQ,EAAG,EAAGO,EAAK,WAAYA,EAAK,SAAS,CAC1D,CAEA,OAAOP,EAAM,WACf,EAAG,CAACA,CAAK,CAAC,EAEV,MAAO,CAAE,MAAAA,EAAO,KAAAM,CAAA,CAClB,CCrEA,SAASE,EAAkBC,EAA2B,CACpD,MAAMC,EAAQ,iBAAiBD,CAAO,EAChCE,EAAYD,EAAM,UAClBE,EAAYF,EAAM,UAExB,OACEC,IAAc,UACdA,IAAc,QACdC,IAAc,UACdA,IAAc,MAElB,CAQA,SAASC,EAAoBJ,EAA6C,CACxE,GAAI,CAACA,EACH,OAAO,KAIT,IAAIK,EAAUL,EAAQ,cAEtB,KAAOK,GAAS,CACd,GAAIN,EAAkBM,CAAO,EAC3B,OAAOA,EAETA,EAAUA,EAAQ,aACpB,CAEA,OAAO,IACT,CAeO,SAASC,EACdZ,EACoB,CACpB,KAAM,CAACnB,EAAWgC,CAAY,EAAI7B,EAAM,SAA6B,IAAI,EAEzEA,OAAAA,EAAM,UAAU,IAAM,CACpB,MAAMsB,EAAUN,EAAI,QACpB,GAAI,CAACM,EAAS,CACZO,EAAa,IAAI,EACjB,MACF,CAEA,MAAMC,EAAkBJ,EAAoBJ,CAAO,EACnDO,EAAaC,CAAe,CAC9B,EAAG,CAACd,CAAG,CAAC,EAEDnB,CACT,CC3DA,MAAMkC,EAAiC,CACrC,SAAU,UACZ,EAEMC,EAAmC,CACvC,SAAU,WACV,WAAY,2BACZ,UAAW,YACb,EAEMC,EAAiC,CACrC,OAAQ,CACV,EAKA,SAASC,EAAcC,EAAgD,CACrE,OAAIA,EACK,CACL,SAAU,QACV,IAAK,EACL,KAAM,EACN,MAAO,EACP,QAAS,EACT,OAAQ,EACR,WAAY,OACZ,cAAe,MAAA,EAKZ,CACL,SAAU,WACV,IAAK,EACL,KAAM,EACN,MAAO,EACP,QAAS,EACT,OAAQ,EACR,WAAY,OACZ,cAAe,MAAA,CAEnB,CAKA,SAASC,EAAgBD,EAAgD,CACvE,OAAIA,EACKJ,EAGF,CACL,GAAGA,EACH,SAAU,QAAA,CAEd,CAwCO,MAAMM,EAA4C,CAAC,CAAE,MAAAC,EAAO,SAAAC,EAAU,cAAAC,KAAoB,CAC/F,MAAMC,EAAYzC,EAAM,OAAuB,IAAI,EAC7C0C,EAAe1C,EAAM,OAAuB,IAAI,EAChD2C,EAAa3C,EAAM,OAAuB,IAAI,EAG9C8B,EAAkBF,EAAmBe,CAAU,EAC/CR,EAAmBL,IAAoB,KAGvC,CAAE,UAAAc,CAAA,EAAchD,EAAmBkC,CAAe,EAClDe,EAAe7C,EAAM,OAAO4C,CAAS,EAC3CC,EAAa,QAAUD,EAGvB,MAAME,EAAiB9C,EAAM,OAA+B,IAAI,EAC1D,CAAE,KAAM+C,CAAA,EAAehC,EAAkB0B,EAAW,CAAA,CAAE,EACvD,OAAO,GAAGK,EAAe,QAASC,CAAU,IAC/CD,EAAe,QAAUC,GAI3B,MAAMC,EAAoBhD,EAAM,OAAuB,IAAI,EAGrD,CAACiD,EAAOC,CAAQ,EAAIlD,EAAM,SAA4B,CAC1D,QAAS,GACT,aAAc,EACd,cAAe,UAAA,CAChB,EAGKmD,EAAWnD,EAAM,OAAOiD,CAAK,EAC7BG,EAAcpD,EAAM,YACvBqD,GAAgC,CAC/B,MAAMC,EAAOH,EAAS,SAClBG,EAAK,UAAYD,EAAS,SAAWC,EAAK,eAAiBD,EAAS,cAAgBC,EAAK,gBAAkBD,EAAS,iBACtHF,EAAS,QAAUE,EACnBH,EAASG,CAAQ,EACjBb,IAAgBa,CAAQ,EAE5B,EACA,CAACb,CAAa,CAAA,EAGhBe,EAAAA,0BAA0B,IAAM,CAC9B,MAAMC,EAASf,EAAU,QACnBgB,EAAYf,EAAa,QACzBgB,EAAUf,EAAW,QAC3B,GAAI,CAACc,GAAa,CAACD,GAAU,CAACE,EAC5B,OAIF,IAAIC,EAAa,OAAO,IAEpBC,EAA0C,KAE1CC,EAAc,GAElB,MAAMC,EAAO,IAAM,CACjB,MAAMC,EAAcjB,EAAe,QACnC,GAAI,CAACiB,EACH,OAGF,MAAMC,EAAmBnB,EAAa,QAEtC,GAAIV,EAAkB,CAEpB,MAAM8B,EAAkBF,EAAY,OAASC,EACzCC,IAAoBN,IACtBF,EAAU,MAAM,QAAU,IAC1BA,EAAU,MAAM,OAAS,GAAGQ,CAAe,KAC3CN,EAAaM,IAGVF,EAAY,GAAK,GAAKA,EAAY,GAAK,GAAKA,EAAY,MAAQ,IAAMH,IAAoBG,IAC7FN,EAAU,MAAM,KAAO,GAAGM,EAAY,CAAC,KACvCN,EAAU,MAAM,IAAM,GAAGM,EAAY,CAAC,KACtCN,EAAU,MAAM,MAAQ,GAAGM,EAAY,KAAK,KAC5CH,EAAkBG,GAIpB,MAAMG,EAAUF,EAAmB,EAC/BE,IAAYL,IACdA,EAAcK,EACdd,EAAY,CACV,QAAAc,EACA,aAAcF,EACd,cAAe,UAAA,CAChB,EAEL,KAAO,CAEL,MAAMnE,EAAYiC,EAClB,GAAI,CAACjC,EACH,OAIF,MAAMsE,EAAiBtE,EAAU,sBAAA,EACjCmD,EAAkB,QAAUmB,EAG5B,MAAMC,EAAYZ,EAAO,sBAAA,EAAwB,IAAMW,EAAe,IAAMH,EAGtEK,EAAeL,EAAmBI,EAClCH,EAAkB,KAAK,IAAI,EAAGF,EAAY,OAASM,CAAY,EAEjEJ,IAAoBN,IACtBF,EAAU,MAAM,QAAU,IAC1BA,EAAU,MAAM,OAAS,GAAGQ,CAAe,KAC3CN,EAAaM,GAIXL,IAAoBG,IACtBN,EAAU,MAAM,KAAO,IACvBA,EAAU,MAAM,MAAQ,GAAGM,EAAY,KAAK,KAC5CH,EAAkBG,GAIpB,MAAMO,EAAW,KAAK,IAAI,EAAGD,CAAY,EACzCZ,EAAU,MAAM,IAAM,GAAGa,CAAQ,KAGjC,MAAMJ,EAAUG,EAAe,EAC3BH,IAAYL,IACdA,EAAcK,EACdd,EAAY,CACV,QAAAc,EACA,aAAAG,EACA,cAAe,WAAA,CAChB,EAEL,CACF,EAGA,IAAIE,EAAK,sBAAsB,SAASC,GAAU,CAChDV,EAAA,EACAS,EAAK,sBAAsBC,CAAO,CACpC,CAAC,EAED,MAAO,IAAM,CACX,qBAAqBD,CAAE,CACzB,CACF,EAAG,CAACpC,EAAkBL,EAAiBsB,CAAW,CAAC,EAGnD,MAAMqB,EAAmB,OAAOlC,GAAa,WAAaA,EAASU,CAAK,EAAIV,EAE5E,cACG,MAAA,CAAI,IAAKI,EAAY,MAAOP,EAAgBD,CAAgB,EAC3D,SAAA,CAAAuC,EAAAA,IAAC,OAAI,IAAKhC,EAAc,MAAOR,EAAcC,CAAgB,EAC1D,SAAAG,EACH,EACAoC,EAAAA,IAAC,MAAA,CAAI,IAAKjC,EAAW,MAAOT,EAC1B,SAAA0C,EAAAA,IAAC,MAAA,CAAI,MAAOzC,EAAY,SAAAwC,CAAA,CAAiB,CAAA,CAC3C,CAAA,EACF,CAEJ,EAEApC,EAAa,YAAc"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { jsxs as Y, jsx as T } from "react/jsx-runtime";
|
|
2
|
+
import * as s from "react";
|
|
3
|
+
import { u as j } from "./useIsomorphicLayoutEffect-DhmEnmZ_.js";
|
|
4
|
+
function F(e) {
|
|
5
|
+
const [t, r] = s.useState(() => typeof window > "u" ? { scrollTop: 0, scrollLeft: 0 } : e ? {
|
|
6
|
+
scrollTop: e.scrollTop,
|
|
7
|
+
scrollLeft: e.scrollLeft
|
|
8
|
+
} : {
|
|
9
|
+
scrollTop: window.scrollY,
|
|
10
|
+
scrollLeft: window.scrollX
|
|
11
|
+
});
|
|
12
|
+
return s.useEffect(() => {
|
|
13
|
+
const o = () => {
|
|
14
|
+
r(e ? {
|
|
15
|
+
scrollTop: e.scrollTop,
|
|
16
|
+
scrollLeft: e.scrollLeft
|
|
17
|
+
} : {
|
|
18
|
+
scrollTop: window.scrollY,
|
|
19
|
+
scrollLeft: window.scrollX
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
o();
|
|
23
|
+
const l = e ?? window;
|
|
24
|
+
return l.addEventListener("scroll", o, { passive: !0 }), () => {
|
|
25
|
+
l.removeEventListener("scroll", o);
|
|
26
|
+
};
|
|
27
|
+
}, [e]), t;
|
|
28
|
+
}
|
|
29
|
+
const L = /* @__PURE__ */ new Map(), q = (e) => {
|
|
30
|
+
const { box: t = "content-box" } = e, r = `resize-box:${t}`, o = L.get(r);
|
|
31
|
+
if (o)
|
|
32
|
+
return o;
|
|
33
|
+
const l = new class {
|
|
34
|
+
#e = /* @__PURE__ */ new Map();
|
|
35
|
+
#t = new ResizeObserver((i, u) => {
|
|
36
|
+
i.forEach((f) => {
|
|
37
|
+
const h = this.#e.get(f.target);
|
|
38
|
+
h && h(f, u);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
observe(i, u) {
|
|
42
|
+
return this.#e.set(i, u), this.#t.observe(i, e), () => {
|
|
43
|
+
this.#e.delete(i), this.#t.unobserve(i);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}();
|
|
47
|
+
return L.set(r, l), l;
|
|
48
|
+
};
|
|
49
|
+
function D(e, { box: t }) {
|
|
50
|
+
const [r, o] = s.useState(null), l = e.current;
|
|
51
|
+
s.useEffect(() => l ? q({ box: t }).observe(l, (f) => {
|
|
52
|
+
o(f);
|
|
53
|
+
}) : void 0, [t, l]);
|
|
54
|
+
const i = s.useMemo(() => {
|
|
55
|
+
if (!r)
|
|
56
|
+
return null;
|
|
57
|
+
if (r.borderBoxSize?.length > 0) {
|
|
58
|
+
const u = r.borderBoxSize[0];
|
|
59
|
+
return new DOMRect(0, 0, u.inlineSize, u.blockSize);
|
|
60
|
+
}
|
|
61
|
+
return r.contentRect;
|
|
62
|
+
}, [r]);
|
|
63
|
+
return { entry: r, rect: i };
|
|
64
|
+
}
|
|
65
|
+
function K(e) {
|
|
66
|
+
const t = getComputedStyle(e), r = t.overflowY, o = t.overflowX;
|
|
67
|
+
return r === "scroll" || r === "auto" || o === "scroll" || o === "auto";
|
|
68
|
+
}
|
|
69
|
+
function P(e) {
|
|
70
|
+
if (!e)
|
|
71
|
+
return null;
|
|
72
|
+
let t = e.parentElement;
|
|
73
|
+
for (; t; ) {
|
|
74
|
+
if (K(t))
|
|
75
|
+
return t;
|
|
76
|
+
t = t.parentElement;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
function W(e) {
|
|
81
|
+
const [t, r] = s.useState(null);
|
|
82
|
+
return s.useEffect(() => {
|
|
83
|
+
const o = e.current;
|
|
84
|
+
if (!o) {
|
|
85
|
+
r(null);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const l = P(o);
|
|
89
|
+
r(l);
|
|
90
|
+
}, [e]), t;
|
|
91
|
+
}
|
|
92
|
+
const B = {
|
|
93
|
+
position: "relative"
|
|
94
|
+
}, G = {
|
|
95
|
+
position: "relative",
|
|
96
|
+
paddingTop: "env(safe-area-inset-top)",
|
|
97
|
+
boxSizing: "border-box"
|
|
98
|
+
}, J = {
|
|
99
|
+
zIndex: 1
|
|
100
|
+
};
|
|
101
|
+
function Q(e) {
|
|
102
|
+
return e ? {
|
|
103
|
+
position: "fixed",
|
|
104
|
+
top: 0,
|
|
105
|
+
left: 0,
|
|
106
|
+
right: 0,
|
|
107
|
+
opacity: 0,
|
|
108
|
+
zIndex: 0,
|
|
109
|
+
userSelect: "none",
|
|
110
|
+
pointerEvents: "none"
|
|
111
|
+
} : {
|
|
112
|
+
position: "absolute",
|
|
113
|
+
top: 0,
|
|
114
|
+
left: 0,
|
|
115
|
+
right: 0,
|
|
116
|
+
opacity: 0,
|
|
117
|
+
zIndex: 0,
|
|
118
|
+
userSelect: "none",
|
|
119
|
+
pointerEvents: "none"
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function U(e) {
|
|
123
|
+
return e ? B : {
|
|
124
|
+
...B,
|
|
125
|
+
overflow: "hidden"
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const V = ({ cover: e, children: t, onStateChange: r }) => {
|
|
129
|
+
const o = s.useRef(null), l = s.useRef(null), i = s.useRef(null), u = W(i), f = u === null, { scrollTop: h } = F(u), z = s.useRef(h);
|
|
130
|
+
z.current = h;
|
|
131
|
+
const w = s.useRef(null), { rect: C } = D(o, {});
|
|
132
|
+
Object.is(w.current, C) || (w.current = C);
|
|
133
|
+
const $ = s.useRef(null), [k, A] = s.useState({
|
|
134
|
+
isStuck: !1,
|
|
135
|
+
scrollOffset: 0,
|
|
136
|
+
containerType: "document"
|
|
137
|
+
}), E = s.useRef(k), x = s.useCallback(
|
|
138
|
+
(a) => {
|
|
139
|
+
const c = E.current;
|
|
140
|
+
(c.isStuck !== a.isStuck || c.scrollOffset !== a.scrollOffset || c.containerType !== a.containerType) && (E.current = a, A(a), r?.(a));
|
|
141
|
+
},
|
|
142
|
+
[r]
|
|
143
|
+
);
|
|
144
|
+
j(() => {
|
|
145
|
+
const a = o.current, c = l.current, H = i.current;
|
|
146
|
+
if (!c || !a || !H)
|
|
147
|
+
return;
|
|
148
|
+
let y = Number.NaN, b = null, S = !1;
|
|
149
|
+
const I = () => {
|
|
150
|
+
const n = w.current;
|
|
151
|
+
if (!n)
|
|
152
|
+
return;
|
|
153
|
+
const v = z.current;
|
|
154
|
+
if (f) {
|
|
155
|
+
const p = n.height - v;
|
|
156
|
+
p !== y && (c.style.opacity = "1", c.style.height = `${p}px`, y = p), (n.x >= 0 || n.y >= 0 || n.width > 0) && b !== n && (c.style.left = `${n.x}px`, c.style.top = `${n.y}px`, c.style.width = `${n.width}px`, b = n);
|
|
157
|
+
const d = v > 0;
|
|
158
|
+
d !== S && (S = d, x({
|
|
159
|
+
isStuck: d,
|
|
160
|
+
scrollOffset: v,
|
|
161
|
+
containerType: "document"
|
|
162
|
+
}));
|
|
163
|
+
} else {
|
|
164
|
+
const p = u;
|
|
165
|
+
if (!p)
|
|
166
|
+
return;
|
|
167
|
+
const d = p.getBoundingClientRect();
|
|
168
|
+
$.current = d;
|
|
169
|
+
const N = a.getBoundingClientRect().top - d.top + v, m = v - N, R = Math.max(0, n.height - m);
|
|
170
|
+
R !== y && (c.style.opacity = "1", c.style.height = `${R}px`, y = R), b !== n && (c.style.left = "0", c.style.width = `${n.width}px`, b = n);
|
|
171
|
+
const X = Math.max(0, m);
|
|
172
|
+
c.style.top = `${X}px`;
|
|
173
|
+
const g = m > 0;
|
|
174
|
+
g !== S && (S = g, x({
|
|
175
|
+
isStuck: g,
|
|
176
|
+
scrollOffset: m,
|
|
177
|
+
containerType: "container"
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
let O = requestAnimationFrame(function n() {
|
|
182
|
+
I(), O = requestAnimationFrame(n);
|
|
183
|
+
});
|
|
184
|
+
return () => {
|
|
185
|
+
cancelAnimationFrame(O);
|
|
186
|
+
};
|
|
187
|
+
}, [f, u, x]);
|
|
188
|
+
const M = typeof t == "function" ? t(k) : t;
|
|
189
|
+
return /* @__PURE__ */ Y("div", { ref: i, style: U(f), children: [
|
|
190
|
+
/* @__PURE__ */ T("div", { ref: l, style: Q(f), children: e }),
|
|
191
|
+
/* @__PURE__ */ T("div", { ref: o, style: G, children: /* @__PURE__ */ T("div", { style: J, children: M }) })
|
|
192
|
+
] });
|
|
193
|
+
};
|
|
194
|
+
V.displayName = "StickyHeader";
|
|
195
|
+
export {
|
|
196
|
+
V as StickyHeader
|
|
197
|
+
};
|
|
198
|
+
//# sourceMappingURL=sticky-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sticky-header.js","sources":["../src/hooks/useContainerScroll.ts","../src/hooks/useResizeObserver.tsx","../src/hooks/useScrollContainer.ts","../src/sticky-header/StickyHeader.tsx"],"sourcesContent":["/**\n * @file useContainerScroll - Track scroll position of a container or document\n *\n * Tracks scroll position for either a specific scroll container element\n * or the document (when container is null).\n */\nimport * as React from \"react\";\n\nexport type ScrollPosition = {\n /** Vertical scroll position */\n scrollTop: number;\n /** Horizontal scroll position */\n scrollLeft: number;\n};\n\n/**\n * Track scroll position of a container element or the document.\n *\n * @param container - Scroll container element, or null for document scroll\n * @returns Current scroll position\n *\n * @example\n * ```tsx\n * // For document scroll\n * const scroll = useContainerScroll(null);\n *\n * // For container scroll\n * const containerRef = useRef<HTMLDivElement>(null);\n * const scroll = useContainerScroll(containerRef.current);\n * ```\n */\nexport function useContainerScroll(container: HTMLElement | null): ScrollPosition {\n const [position, setPosition] = React.useState<ScrollPosition>(() => {\n if (typeof window === \"undefined\") {\n return { scrollTop: 0, scrollLeft: 0 };\n }\n\n if (container) {\n return {\n scrollTop: container.scrollTop,\n scrollLeft: container.scrollLeft,\n };\n }\n\n return {\n scrollTop: window.scrollY,\n scrollLeft: window.scrollX,\n };\n });\n\n React.useEffect(() => {\n const handleScroll = () => {\n if (container) {\n setPosition({\n scrollTop: container.scrollTop,\n scrollLeft: container.scrollLeft,\n });\n } else {\n setPosition({\n scrollTop: window.scrollY,\n scrollLeft: window.scrollX,\n });\n }\n };\n\n // Initialize position\n handleScroll();\n\n const target = container ?? window;\n target.addEventListener(\"scroll\", handleScroll, { passive: true });\n\n return () => {\n target.removeEventListener(\"scroll\", handleScroll);\n };\n }, [container]);\n\n return position;\n}\n","/**\n * @file Shared useResizeObserver hook with cached observer instances.\n */\nimport * as React from \"react\";\n\ntype Unobserve = () => void;\ntype Callback = (entry: ResizeObserverEntry, observer: ResizeObserver) => void;\ntype SharedObserver = {\n observe: (target: Element, callback: Callback) => Unobserve;\n};\nconst observerCache = new Map<string, SharedObserver>();\nconst getSharedObserver = (options: ResizeObserverOptions) => {\n const { box = \"content-box\" } = options;\n const observerKey = `resize-box:${box}`;\n const cached = observerCache.get(observerKey);\n if (cached) {\n return cached;\n }\n const observer = new (class {\n #callbackMap = new Map<Element, Callback>();\n #resizeObserver = new ResizeObserver((entries, observer) => {\n entries.forEach((entry) => {\n const callback = this.#callbackMap.get(entry.target);\n if (callback) {\n callback(entry, observer);\n }\n });\n });\n observe(target: Element, callback: Callback) {\n this.#callbackMap.set(target, callback);\n this.#resizeObserver.observe(target, options);\n return () => {\n this.#callbackMap.delete(target);\n this.#resizeObserver.unobserve(target);\n };\n }\n })();\n observerCache.set(observerKey, observer);\n\n return observer;\n};\n/**\n * Observe size changes for a given element reference using shared resize observers.\n *\n * @param ref - Ref holding the element whose size to monitor.\n * @param options - Resize observer configuration.\n * @returns Latest resize entry and a derived DOMRect snapshot.\n */\nexport function useResizeObserver<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n { box }: ResizeObserverOptions,\n) {\n const [entry, setEntry] = React.useState<ResizeObserverEntry | null>(null);\n const target = ref.current;\n\n React.useEffect(() => {\n if (!target) {\n return;\n }\n\n const observer = getSharedObserver({ box });\n return observer.observe(target, (nextEntry) => {\n setEntry(nextEntry);\n });\n }, [box, target]);\n\n const rect = React.useMemo(() => {\n if (!entry) {\n return null;\n }\n\n if (entry.borderBoxSize?.length > 0) {\n const size = entry.borderBoxSize[0];\n return new DOMRect(0, 0, size.inlineSize, size.blockSize);\n }\n\n return entry.contentRect;\n }, [entry]);\n\n return { entry, rect };\n}\n","/**\n * @file useScrollContainer - Detect the nearest scrollable ancestor\n *\n * Traverses the DOM tree to find the nearest ancestor with overflow: scroll/auto.\n * Returns null if the document is the scroll container.\n */\nimport * as React from \"react\";\n\n/**\n * Check if an element is a scroll container.\n */\nfunction isScrollContainer(element: Element): boolean {\n const style = getComputedStyle(element);\n const overflowY = style.overflowY;\n const overflowX = style.overflowX;\n\n return (\n overflowY === \"scroll\" ||\n overflowY === \"auto\" ||\n overflowX === \"scroll\" ||\n overflowX === \"auto\"\n );\n}\n\n/**\n * Find the nearest scrollable ancestor of an element.\n *\n * @param element - Starting element to search from\n * @returns The nearest scrollable ancestor, or null if document is the container\n */\nfunction findScrollContainer(element: Element | null): HTMLElement | null {\n if (!element) {\n return null;\n }\n\n // eslint-disable-next-line no-restricted-syntax -- DOM traversal requires let\n let current = element.parentElement;\n\n while (current) {\n if (isScrollContainer(current)) {\n return current;\n }\n current = current.parentElement;\n }\n\n return null;\n}\n\n/**\n * Hook to detect the nearest scrollable ancestor of a ref element.\n *\n * @param ref - Ref to the element to find scroll container for\n * @returns The nearest scrollable ancestor element, or null if document is the container\n *\n * @example\n * ```tsx\n * const elementRef = useRef<HTMLDivElement>(null);\n * const scrollContainer = useScrollContainer(elementRef);\n * // scrollContainer is HTMLElement if in nested scroll, null if document scroll\n * ```\n */\nexport function useScrollContainer<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n): HTMLElement | null {\n const [container, setContainer] = React.useState<HTMLElement | null>(null);\n\n React.useEffect(() => {\n const element = ref.current;\n if (!element) {\n setContainer(null);\n return;\n }\n\n const scrollContainer = findScrollContainer(element);\n setContainer(scrollContainer);\n }, [ref]);\n\n return container;\n}\n","/**\n * @file StickyHeader component for native app-like overscroll experience.\n *\n * Displays cover content that expands during overscroll/bounce,\n * providing a native app-like pull effect commonly seen in iOS apps.\n *\n * This component is designed for SPAs, PWAs, and hybrid apps where\n * the browser's pull-to-refresh is disabled and bounce effects\n * should display content rather than empty space.\n *\n * Supports both document-level scroll and nested scroll containers.\n */\nimport * as React from \"react\";\nimport { useContainerScroll } from \"../hooks/useContainerScroll\";\nimport { useIsomorphicLayoutEffect } from \"../hooks/useIsomorphicLayoutEffect\";\nimport { useResizeObserver } from \"../hooks/useResizeObserver\";\nimport { useScrollContainer } from \"../hooks/useScrollContainer\";\nimport type { StickyHeaderProps, StickyHeaderState } from \"./types\";\n\nconst baseStyle: React.CSSProperties = {\n position: \"relative\",\n};\n\nconst headerStyle: React.CSSProperties = {\n position: \"relative\",\n paddingTop: \"env(safe-area-inset-top)\",\n boxSizing: \"border-box\",\n};\n\nconst bodyStyle: React.CSSProperties = {\n zIndex: 1,\n};\n\n/**\n * Get cover styles based on container type.\n */\nfunction getCoverStyle(isDocumentScroll: boolean): React.CSSProperties {\n if (isDocumentScroll) {\n return {\n position: \"fixed\",\n top: 0,\n left: 0,\n right: 0,\n opacity: 0,\n zIndex: 0,\n userSelect: \"none\",\n pointerEvents: \"none\",\n };\n }\n\n // For nested scroll containers, use absolute positioning\n return {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n opacity: 0,\n zIndex: 0,\n userSelect: \"none\",\n pointerEvents: \"none\",\n };\n}\n\n/**\n * Get wrapper styles for nested scroll containers.\n */\nfunction getWrapperStyle(isDocumentScroll: boolean): React.CSSProperties {\n if (isDocumentScroll) {\n return baseStyle;\n }\n\n return {\n ...baseStyle,\n overflow: \"hidden\",\n };\n}\n\n/**\n * StickyHeader provides a native app-like overscroll experience.\n *\n * When the user pulls down beyond the top of the page (overscroll/bounce),\n * the cover content expands to fill the visible area, similar to\n * native iOS/Android app behavior.\n *\n * Also supports nested scroll containers with overflow: scroll/auto.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <StickyHeader cover={<img src=\"/hero.jpg\" alt=\"Hero\" />}>\n * <header>\n * <h1>My App</h1>\n * </header>\n * </StickyHeader>\n *\n * // With state callback\n * <StickyHeader\n * cover={<img src=\"/hero.jpg\" alt=\"Hero\" />}\n * onStateChange={({ isStuck }) => console.log('Stuck:', isStuck)}\n * >\n * <header>\n * <h1>My App</h1>\n * </header>\n * </StickyHeader>\n *\n * // With render function\n * <StickyHeader cover={<img src=\"/hero.jpg\" alt=\"Hero\" />}>\n * {({ isStuck }) => (\n * <header style={{ background: isStuck ? 'white' : 'transparent' }}>\n * <h1>My App</h1>\n * </header>\n * )}\n * </StickyHeader>\n * ```\n */\nexport const StickyHeader: React.FC<StickyHeaderProps> = ({ cover, children, onStateChange }) => {\n const headerRef = React.useRef<HTMLDivElement>(null);\n const coverAreaRef = React.useRef<HTMLDivElement>(null);\n const wrapperRef = React.useRef<HTMLDivElement>(null);\n\n // Detect scroll container\n const scrollContainer = useScrollContainer(wrapperRef);\n const isDocumentScroll = scrollContainer === null;\n\n // Track scroll position\n const { scrollTop } = useContainerScroll(scrollContainer);\n const scrollTopRef = React.useRef(scrollTop);\n scrollTopRef.current = scrollTop;\n\n // Track header bounds\n const headerBoundRef = React.useRef<DOMRectReadOnly | null>(null);\n const { rect: headerRect } = useResizeObserver(headerRef, {});\n if (!Object.is(headerBoundRef.current, headerRect)) {\n headerBoundRef.current = headerRect;\n }\n\n // Track container bounds for nested scroll\n const containerBoundRef = React.useRef<DOMRect | null>(null);\n\n // State for render function and callback\n const [state, setState] = React.useState<StickyHeaderState>({\n isStuck: false,\n scrollOffset: 0,\n containerType: \"document\",\n });\n\n // Update state when values change\n const stateRef = React.useRef(state);\n const updateState = React.useCallback(\n (newState: StickyHeaderState) => {\n const prev = stateRef.current;\n if (prev.isStuck !== newState.isStuck || prev.scrollOffset !== newState.scrollOffset || prev.containerType !== newState.containerType) {\n stateRef.current = newState;\n setState(newState);\n onStateChange?.(newState);\n }\n },\n [onStateChange],\n );\n\n useIsomorphicLayoutEffect(() => {\n const header = headerRef.current;\n const coverArea = coverAreaRef.current;\n const wrapper = wrapperRef.current;\n if (!coverArea || !header || !wrapper) {\n return;\n }\n\n // eslint-disable-next-line no-restricted-syntax -- Performance: mutable state for RAF loop\n let prevHeight = Number.NaN;\n // eslint-disable-next-line no-restricted-syntax -- Performance: mutable state for RAF loop\n let prevHeaderBound: DOMRectReadOnly | null = null;\n // eslint-disable-next-line no-restricted-syntax -- Performance: mutable state for RAF loop\n let prevIsStuck = false;\n\n const loop = () => {\n const headerBound = headerBoundRef.current;\n if (!headerBound) {\n return;\n }\n\n const currentScrollTop = scrollTopRef.current;\n\n if (isDocumentScroll) {\n // Document scroll mode (original behavior)\n const coverAreaHeight = headerBound.height - currentScrollTop;\n if (coverAreaHeight !== prevHeight) {\n coverArea.style.opacity = \"1\";\n coverArea.style.height = `${coverAreaHeight}px`;\n prevHeight = coverAreaHeight;\n }\n\n if ((headerBound.x >= 0 || headerBound.y >= 0 || headerBound.width > 0) && prevHeaderBound !== headerBound) {\n coverArea.style.left = `${headerBound.x}px`;\n coverArea.style.top = `${headerBound.y}px`;\n coverArea.style.width = `${headerBound.width}px`;\n prevHeaderBound = headerBound;\n }\n\n // Calculate stuck state\n const isStuck = currentScrollTop > 0;\n if (isStuck !== prevIsStuck) {\n prevIsStuck = isStuck;\n updateState({\n isStuck,\n scrollOffset: currentScrollTop,\n containerType: \"document\",\n });\n }\n } else {\n // Nested container scroll mode\n const container = scrollContainer;\n if (!container) {\n return;\n }\n\n // Get container bounds\n const containerBound = container.getBoundingClientRect();\n containerBoundRef.current = containerBound;\n\n // Calculate header position relative to container\n const headerTop = header.getBoundingClientRect().top - containerBound.top + currentScrollTop;\n\n // Calculate cover height based on scroll position\n const scrollOffset = currentScrollTop - headerTop;\n const coverAreaHeight = Math.max(0, headerBound.height - scrollOffset);\n\n if (coverAreaHeight !== prevHeight) {\n coverArea.style.opacity = \"1\";\n coverArea.style.height = `${coverAreaHeight}px`;\n prevHeight = coverAreaHeight;\n }\n\n // Position cover relative to scroll\n if (prevHeaderBound !== headerBound) {\n coverArea.style.left = \"0\";\n coverArea.style.width = `${headerBound.width}px`;\n prevHeaderBound = headerBound;\n }\n\n // Calculate top position to follow scroll\n const coverTop = Math.max(0, scrollOffset);\n coverArea.style.top = `${coverTop}px`;\n\n // Calculate stuck state\n const isStuck = scrollOffset > 0;\n if (isStuck !== prevIsStuck) {\n prevIsStuck = isStuck;\n updateState({\n isStuck,\n scrollOffset,\n containerType: \"container\",\n });\n }\n }\n };\n\n // eslint-disable-next-line no-restricted-syntax -- Performance: RAF id needs reassignment\n let id = requestAnimationFrame(function animate() {\n loop();\n id = requestAnimationFrame(animate);\n });\n\n return () => {\n cancelAnimationFrame(id);\n };\n }, [isDocumentScroll, scrollContainer, updateState]);\n\n // Render children\n const renderedChildren = typeof children === \"function\" ? children(state) : children;\n\n return (\n <div ref={wrapperRef} style={getWrapperStyle(isDocumentScroll)}>\n <div ref={coverAreaRef} style={getCoverStyle(isDocumentScroll)}>\n {cover}\n </div>\n <div ref={headerRef} style={headerStyle}>\n <div style={bodyStyle}>{renderedChildren}</div>\n </div>\n </div>\n );\n};\n\nStickyHeader.displayName = \"StickyHeader\";\n"],"names":["useContainerScroll","container","position","setPosition","React","handleScroll","target","observerCache","getSharedObserver","options","box","observerKey","cached","observer","#callbackMap","#resizeObserver","entries","entry","callback","useResizeObserver","ref","setEntry","nextEntry","rect","size","isScrollContainer","element","style","overflowY","overflowX","findScrollContainer","current","useScrollContainer","setContainer","scrollContainer","baseStyle","headerStyle","bodyStyle","getCoverStyle","isDocumentScroll","getWrapperStyle","StickyHeader","cover","children","onStateChange","headerRef","coverAreaRef","wrapperRef","scrollTop","scrollTopRef","headerBoundRef","headerRect","containerBoundRef","state","setState","stateRef","updateState","newState","prev","useIsomorphicLayoutEffect","header","coverArea","wrapper","prevHeight","prevHeaderBound","prevIsStuck","loop","headerBound","currentScrollTop","coverAreaHeight","isStuck","containerBound","headerTop","scrollOffset","coverTop","id","animate","renderedChildren","jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const I = "rpl", l = "var(--rpl-color-resize-handle-idle, rgba(255, 255, 255, 0.0))", N = "var(--rpl-color-resize-handle-hover, rgba(33, 150, 243, 0.35))", T = "var(--rpl-color-resize-handle-active, rgba(33, 150, 243, 0.55))", a = "var(--rpl-color-drop-suggest-border, rgba(90, 150, 255, 0.9))", _ = "var(--rpl-color-drop-suggest-bg, rgba(90, 150, 255, 0.15))", r = "var(--rpl-color-insert-guide, rgba(120, 160, 255, 0.95))", s = "var(--rpl-color-insert-guide-shadow, 0 0 0 2px rgba(120, 160, 255, 0.2))", E = "var(--rpl-color-surface, #fff)", o = "var(--rpl-color-surface-2, #fafafa)", n = "var(--rpl-color-border, #e5e7eb)", R = "var(--rpl-color-muted-fg, #6b7280)", e = "var(--rpl-shadow-card, 0 2px 10px rgba(0, 0, 0, 0.08))", S = "var(--rpl-color-drawer-backdrop, rgba(0, 0, 0, 0.5))", i = "var(--rpl-drawer-transition-duration, 220ms)", L = "var(--rpl-drawer-transition-easing, cubic-bezier(0.22, 1, 0.36, 1))", G = "var(--rpl-pivot-animation-enter, none)", d = "var(--rpl-pivot-animation-leave, none)", t = "var(--rpl-radius-suggest, 6px)", O = "var(--rpl-size-suggest-border, 2px)";
|
|
2
|
+
const p = "var(--rpl-size-resize-handle-thickness, 4px)", A = "var(--rpl-size-split-handle-thickness, 6px)", D = "var(--rpl-z-overlay, 9998)", c = "var(--rpl-z-tabdrag-overlay, 9999)", v = p, g = "var(--rpl-z-resize-handle, 1000)", C = 4, P = "var(--rpl-size-grid-layer-corner-hit, 14px)", H = "var(--rpl-size-grid-layer-edge-hit-thickness, 12px)", x = D, U = O, F = t, b = a, f = _, h = 6, B = c, z = "var(--rpl-space-tab-drag-preview-offset-x, 12px)", Z = "var(--rpl-space-tab-drag-preview-offset-y, 12px)", u = "var(--rpl-size-tab-drag-insert-guide-width, 2px)", W = "var(--rpl-radius-tab-drag-insert-guide, 1px)", w = r, V = s, K = "var(--rpl-radius-floating-panel, 8px)", m = "var(--rpl-space-floating-panel-gap, 8px)", y = "var(--rpl-space-floating-panel-header-padding-y, 8px)", X = "var(--rpl-space-floating-panel-header-padding-x, 12px)", Y = "var(--rpl-space-floating-panel-content-padding, 12px)", M = "var(--rpl-size-floating-panel-meta-font, 12px)", k = "var(--rpl-space-floating-panel-controls-gap, 6px)", j = "var(--rpl-size-floating-panel-close-button-font, 1.25rem)", q = "var(--rpl-space-floating-panel-close-button-padding, 0.25rem 0.5rem)", J = E, Q = o, $ = n, aa = R, _a = e, ra = "var(--rpl-space-drawer-header-padding-y, 10px)", sa = "var(--rpl-space-drawer-header-padding-x, 12px)", Ea = "var(--rpl-space-drawer-header-gap, 8px)", oa = "var(--rpl-space-drawer-content-padding, 12px)", na = A, Ra = "var(--rpl-size-horizontal-divider-width, 4px)";
|
|
3
|
+
export {
|
|
4
|
+
F as A,
|
|
5
|
+
f as B,
|
|
6
|
+
S as C,
|
|
7
|
+
i as D,
|
|
8
|
+
U as E,
|
|
9
|
+
_a as F,
|
|
10
|
+
H as G,
|
|
11
|
+
Ra as H,
|
|
12
|
+
b as I,
|
|
13
|
+
h as J,
|
|
14
|
+
Z as K,
|
|
15
|
+
V as L,
|
|
16
|
+
w as M,
|
|
17
|
+
W as N,
|
|
18
|
+
u as O,
|
|
19
|
+
G as P,
|
|
20
|
+
B as Q,
|
|
21
|
+
v as R,
|
|
22
|
+
na as S,
|
|
23
|
+
z as T,
|
|
24
|
+
I as U,
|
|
25
|
+
K as a,
|
|
26
|
+
J as b,
|
|
27
|
+
$ as c,
|
|
28
|
+
Q as d,
|
|
29
|
+
y as e,
|
|
30
|
+
X as f,
|
|
31
|
+
m as g,
|
|
32
|
+
M as h,
|
|
33
|
+
aa as i,
|
|
34
|
+
k as j,
|
|
35
|
+
Y as k,
|
|
36
|
+
q as l,
|
|
37
|
+
j as m,
|
|
38
|
+
d as n,
|
|
39
|
+
L as o,
|
|
40
|
+
oa as p,
|
|
41
|
+
Ea as q,
|
|
42
|
+
ra as r,
|
|
43
|
+
sa as s,
|
|
44
|
+
P as t,
|
|
45
|
+
T as u,
|
|
46
|
+
N as v,
|
|
47
|
+
l as w,
|
|
48
|
+
g as x,
|
|
49
|
+
C as y,
|
|
50
|
+
x as z
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=styles-CA2_zLZt.js.map
|