react-sticky-kit 0.2.1 โ†’ 0.3.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/README.md CHANGED
@@ -24,56 +24,90 @@
24
24
  </a>
25
25
  </p>
26
26
 
27
- A lightweight, flexible React sticky container and item component library. Easily create sticky headers, sections, and advanced sticky layouts with support for multiple modes and edge cases.
28
-
29
- ## Features
30
-
31
- - ๐Ÿ“ฆ Simple API: `<StickyContainer>` and `<StickyItem>`
32
- - ๐Ÿงฉ Supports `replace`, `stack`, and `none` sticky modes
33
- - ๐Ÿท๏ธ Customizable offset, z-index (baseZIndex), and sticky logic
34
- - ๐Ÿ–ฅ๏ธ Flexible reference containers (window, DOM elements, refs)
35
- - ๐Ÿ”„ Supports SSR/SSG (Next.js, Gatsby, Astro, etc.)
36
- - ๐Ÿงช Handles edge cases: empty sections, dynamic heights, zero-height headers, long headers, etc.
37
- - โšก๏ธ Written in TypeScript, fully typed
38
- - ๐Ÿงช Includes demo pages for real-world scenarios
27
+ Coordinate multiple sticky section headers in React. Stack headers using their actual
28
+ heights, replace a section header as the next one arrives, or mix both behaviors
29
+ under a shared viewport offset.
30
+
31
+ **Use native CSS `position: sticky` for a single header or a straightforward grouped
32
+ list.** Choose React Sticky Kit when you need dynamic-height stacking, mixed
33
+ stack/replace behavior, or the total sticky height without maintaining your own
34
+ measurement and scroll logic.
35
+
36
+ ## See the behavior
37
+
38
+ | Replace section headings | Stack reached headings |
39
+ | --- | --- |
40
+ | ![Replacing section headers while scrolling](docs/assets/replace.gif) | ![Stacking section headers while scrolling](docs/assets/stack.gif) |
41
+
42
+ - **Replace:** an alphabetical contact list updates its section heading as you scroll.
43
+ - **Stack:** previously reached headings accumulate below the shared offset; their
44
+ heights are measured automatically, including when content changes.
45
+ - **Mixed:** keep a page title stacked while section headings replace below it.
46
+
47
+ [Open the live demo](https://app.evecalm.com/react-sticky-kit/) โ€” no installation required.
48
+ Try [replace](https://app.evecalm.com/react-sticky-kit/#replace),
49
+ [stack](https://app.evecalm.com/react-sticky-kit/#stack), or
50
+ [mixed modes](https://app.evecalm.com/react-sticky-kit/#mixed-mode).
51
+ The demo also includes dynamic heights, nested groups and container boundaries.
52
+ To edit an example, use the [contact-list sandbox](https://codesandbox.io/p/sandbox/dreamy-hofstadter-v9dzfz);
53
+ see [running the demo](#run-the-demo) for local development.
39
54
 
40
55
  ## Installation
41
56
 
42
57
  ```bash
43
58
  npm install react-sticky-kit
44
- # or
45
- yarn add react-sticky-kit
46
- # or
47
- pnpm add react-sticky-kit
59
+ # or: pnpm add react-sticky-kit
48
60
  ```
49
61
 
50
- ## Demo
51
- - [Apple iOS Contact App](https://codesandbox.io/p/sandbox/dreamy-hofstadter-v9dzfz)
62
+ ## Quick start: replacing section headers
52
63
 
53
- ## Usage
64
+ Import the stylesheet once. Give sections enough content to scroll; headers replace
65
+ one another within the container's boundary.
54
66
 
55
67
  ```tsx
56
68
  import { StickyContainer, StickyItem } from 'react-sticky-kit';
57
- // !! Import styles for sticky components
58
- import 'react-sticky-kit/dist/style.css';
59
- // or `import 'react-sticky-kit/style';` for more clean style path(require modern bundler tools)
69
+ import 'react-sticky-kit/style';
60
70
 
61
- export default function Example() {
71
+ export default function Sections() {
62
72
  return (
63
- <StickyContainer offsetTop={48} defaultMode="stack" baseZIndex={300}>
64
- <StickyItem>
65
- <div>Sticky Header</div>
66
- </StickyItem>
67
- <div>Content...</div>
68
- <StickyItem mode="replace">
69
- <div>Another Sticky Header (replace mode)</div>
70
- </StickyItem>
71
- <div>More Content...</div>
73
+ <StickyContainer defaultMode="replace" offsetTop={48}>
74
+ <StickyItem><h2 style={{ margin: 0, padding: 12, background: '#fff' }}>Overview</h2></StickyItem>
75
+ <section style={{ minHeight: 600 }}>Overview content</section>
76
+ <StickyItem><h2 style={{ margin: 0, padding: 12, background: '#fff' }}>Details</h2></StickyItem>
77
+ <section style={{ minHeight: 600 }}>Details content</section>
72
78
  </StickyContainer>
73
79
  );
74
80
  }
75
81
  ```
76
82
 
83
+ Change `defaultMode` to `"stack"` to retain previously reached headings. To keep a
84
+ page title above replacing section headings, add a first `<StickyItem mode="stack">`.
85
+ Offsets are relative to the **viewport**, including when scrolling an inner element.
86
+ For offsets relative to a scrollable panel, prefer native CSS sticky positioning.
87
+
88
+ ## Which approach should I choose?
89
+
90
+ | Need | Start with |
91
+ | --- | --- |
92
+ | A single sticky navigation bar or simple grouped list | Native CSS `position: sticky` |
93
+ | A sidebar taller than the viewport | A sidebar-focused solution such as `react-sticky-box` |
94
+ | Dynamic-height headers that stack automatically | React Sticky Kit, `stack` mode |
95
+ | A persistent title above replacing section headings | React Sticky Kit, mixed modes |
96
+ | The current total sticky height | `onStickyItemsHeightChange` |
97
+ | Table columns, built-in virtualization, or hide-on-scroll navigation | A solution designed for that specific interaction |
98
+
99
+ React Sticky Kit uses fixed positioning while active. It is not a drop-in
100
+ replacement for scroll-container-relative sticky behavior. Ancestor transforms
101
+ and overflow clipping can affect fixed positioning.
102
+
103
+ React 17/18/19, SSR and TypeScript 5 NodeNext consumers are covered by package
104
+ checks. The ESM artifact is approximately **3.48 kB gzip**, excluding React and CSS;
105
+ there are no additional runtime dependencies. See the
106
+ [performance audit](docs/performance.md) for methods and tradeoffs.
107
+
108
+ For practical examples and existing-library migration, see the
109
+ [patterns and migration guide](docs/patterns-and-migration.md).
110
+
77
111
  ## Props
78
112
 
79
113
  ### `<StickyContainer />`
@@ -84,7 +118,7 @@ export default function Example() {
84
118
  | `baseZIndex` | `number` | `200` | Base z-index for sticky items. Should be greater than the number of sticky items. |
85
119
  | | | | In `replace` mode, z-index = baseZIndex - index; in `stack` mode, z-index = baseZIndex + index. |
86
120
  | `onStickyItemsHeightChange` | `(height: number) => void` | | Callback when total sticky height changes |
87
- | `constraint` | `'none'` | | Define the constraint for sticky behavior. 'none' = no constraints (CSS-like behavior) |
121
+ | `constraint` | `'none'` | | Define the constraint for sticky behavior. 'none' = no container boundary |
88
122
 
89
123
  ### `<StickyItem />`
90
124
  | Prop | Type | Default | Description |
@@ -113,14 +147,9 @@ function Example() {
113
147
  <div>Content...</div>
114
148
  </StickyContainer>
115
149
 
116
- {/* 2. No constraints (like CSS position:sticky) */}
150
+ {/* 2. No container boundary */}
117
151
  <StickyContainer constraint="none">
118
- <StickyItem><div>Always sticky like CSS position:sticky</div></StickyItem>
119
- <div>Content...</div>
120
- </StickyContainer>
121
- </div>
122
- );
123
- }
152
+ <StickyItem><div>Sticky even after the container leaves</div></StickyItem>
124
153
  <div>Content...</div>
125
154
  </StickyContainer>
126
155
  </div>
@@ -130,11 +159,15 @@ function Example() {
130
159
 
131
160
  ### Special behavior of `constraint="none"`
132
161
 
133
- When you set `constraint="none"`, the sticky items will always stick when they reach the top of the viewport, regardless of their parent container's visibility. This exactly matches the behavior of native CSS `position: sticky`.
162
+ When you set `constraint="none"`, the sticky items will always stick when they reach the top of the viewport, regardless of their parent container's visibility. Offsets remain relative to the viewport; this differs from native CSS `position: sticky`, which is constrained by its scrolling ancestor.
134
163
 
135
164
  In contrast, the default behavior (without specifying a constraint) only makes items sticky when their parent container is visible in the viewport.
136
165
 
137
- ## Demo
166
+ ## Run the demo
167
+
168
+ The demo builds as a standalone static site with `pnpm build:demo`. Its relative
169
+ asset paths work when hosted under a repository subdirectory.
170
+ See [deployment instructions](docs/demo-deployment.md) for GitHub Pages setup.
138
171
 
139
172
  Run the demo locally:
140
173
 
@@ -158,7 +191,7 @@ React Sticky Kit supports Server-Side Rendering (SSR) and Static Site Generation
158
191
  ### Next.js Example
159
192
 
160
193
  ```tsx
161
- // pages/index.tsx
194
+ // app/page.tsx (Next.js App Router)
162
195
  import { StickyContainer, StickyItem } from 'react-sticky-kit'
163
196
  import 'react-sticky-kit/dist/style.css'
164
197
 
@@ -174,28 +207,85 @@ export default function Home() {
174
207
  }
175
208
  ```
176
209
 
210
+ For Pages Router, import the global stylesheet from `pages/_app.tsx`.
211
+
177
212
  ### Astro Example
178
213
 
214
+ Create a React component containing both `StickyContainer` and `StickyItem`, then
215
+ hydrate it from Astro:
216
+
179
217
  ```astro
180
218
  ---
181
- // src/pages/index.astro
182
- import { StickyContainer, StickyItem } from 'react-sticky-kit'
183
- import 'react-sticky-kit/dist/style.css'
219
+ import StickyLayout from '../components/StickyLayout.tsx'
220
+ import 'react-sticky-kit/style'
184
221
  ---
222
+ <StickyLayout client:load />
223
+ ```
185
224
 
186
- <html>
187
- <head>...</head>
188
- <body>
189
- <StickyContainer client:load>
190
- <StickyItem>
191
- <header>Sticky Header</header>
192
- </StickyItem>
193
- <div>Content...</div>
194
- </StickyContainer>
195
- </body>
196
- </html>
225
+ ## Compatibility and updates
226
+
227
+ React 17 and later are supported. Import the stylesheet explicitly, using either
228
+ `react-sticky-kit/style` or `react-sticky-kit/dist/style.css`. ESM, CommonJS and the
229
+ UMD browser build are available; UMD requires a global `React`.
230
+
231
+ The built entry points include `"use client"` for Next.js App Router. Pages Router
232
+ and server rendering remain supported; sticky layout is applied after mounting.
233
+ For Astro, wrap the sticky layout in a React component and hydrate that component
234
+ as a single island.
235
+
236
+ Sticky offsets use the viewport, even when an event comes from a nested scrolling
237
+ element. Ancestor transforms and overflow clipping can affect fixed positioning;
238
+ use native CSS `position: sticky` if you need offsets relative to a scrolling ancestor.
239
+
240
+ When available, `ResizeObserver` updates dynamic content heights and widths, and
241
+ tracks ancestor and preceding sibling sizes that can move the container. Shared,
242
+ scoped `MutationObserver` subscriptions refresh these observations when the surrounding
243
+ DOM structure or relevant classes/styles change. Observers and global scroll/resize
244
+ listeners are shared across containers. No continuous polling is used.
245
+ Without `ResizeObserver`, measurements update on scroll, window resize and component
246
+ commits. Native browser scroll anchoring can move the viewport when content above
247
+ it changes; sticky positions follow the resulting viewport.
248
+
249
+ Wrapper padding, borders and `box-sizing` are accounted for when preserving the
250
+ placeholder and content width. Definite inline heights are retained. Fixed headers
251
+ also follow horizontal scrolling. Per-item layout metrics are cached and invalidated
252
+ by observed dimension changes, window resize, relevant DOM changes and React
253
+ commits. Inactive or fully replaced headers do not have their content heights measured during scrolling.
254
+ `onStickyItemsHeightChange` reports the final total once per animation frame, after
255
+ all scheduled containers have applied their styles, when it changes, including zero when no items remain sticky. It does not emit transient
256
+ per-item totals or callbacks after the container unmounts.
257
+
258
+ ## Performance
259
+
260
+ Scheduled containers share one animation frame. Their geometry reads finish before
261
+ any sticky styles are written, and unchanged layouts skip repeated style handling.
262
+ Inactive content heights are not measured.
263
+ Prefer grouping related sections in one container rather than mounting a container
264
+ for every row. Large numbers of simultaneously stacked headers still require
265
+ per-frame geometry reads. See the [performance audit](docs/performance.md) for
266
+ stress-test findings and limitations.
267
+
268
+ ## Development
269
+
270
+ Use Node.js 22.22.2+ or 24.15.0+ and the pnpm version pinned in `package.json`.
271
+ The development tools require these versions; the published library keeps its
272
+ React 17+ peer dependency range and targets ES2018.
273
+
274
+ ```bash
275
+ pnpm install --frozen-lockfile
276
+ pnpm type-check
277
+ pnpm lint
278
+ pnpm test
279
+ pnpm build
280
+ pnpm check:package
281
+ pnpm check:compatibility # installs the tarball with React 17, 18 and 19
282
+ pnpm exec playwright install --with-deps chromium firefox webkit
283
+ pnpm test:browser
197
284
  ```
198
285
 
286
+ TypeScript 6 is used for development while the ESLint TypeScript plugin supports
287
+ versions below 6.1. Generated declarations use the existing public component API.
288
+
199
289
  ## Publish steps
200
290
 
201
291
  `pnpm pub`
@@ -0,0 +1,29 @@
1
+ export declare const MIN_BASE_Z_INDEX = 20;
2
+ export declare const DEFAULT_BASE_Z_INDEX = 200;
3
+ /** Replace the previous item, stack below it, or disable sticky behavior. */
4
+ export type IStickyMode = 'replace' | 'stack' | 'none';
5
+ export interface IStickyItemMeasurement {
6
+ height: number;
7
+ wrapperHeight: number;
8
+ width: number;
9
+ left: number;
10
+ }
11
+ export interface IStickyItemHandle {
12
+ el: HTMLElement;
13
+ content: HTMLElement;
14
+ mode: Exclude<IStickyMode, 'none'>;
15
+ measure: (rect: DOMRect) => IStickyItemMeasurement;
16
+ invalidate: () => void;
17
+ apply: (layout: (IStickyItemMeasurement & {
18
+ top: number;
19
+ index: number;
20
+ }) | null) => void;
21
+ }
22
+ export interface IStickyGroupContextValue {
23
+ baseZIndex: number;
24
+ register: (handle: IStickyItemHandle) => () => void;
25
+ scheduleUpdate: () => void;
26
+ mode: IStickyMode;
27
+ }
28
+ export declare const StickyGroupContext: import("react").Context<IStickyGroupContextValue | null>;
29
+ export declare function useStickyContext(): IStickyGroupContextValue | null;
@@ -0,0 +1,29 @@
1
+ export declare const MIN_BASE_Z_INDEX = 20;
2
+ export declare const DEFAULT_BASE_Z_INDEX = 200;
3
+ /** Replace the previous item, stack below it, or disable sticky behavior. */
4
+ export type IStickyMode = 'replace' | 'stack' | 'none';
5
+ export interface IStickyItemMeasurement {
6
+ height: number;
7
+ wrapperHeight: number;
8
+ width: number;
9
+ left: number;
10
+ }
11
+ export interface IStickyItemHandle {
12
+ el: HTMLElement;
13
+ content: HTMLElement;
14
+ mode: Exclude<IStickyMode, 'none'>;
15
+ measure: (rect: DOMRect) => IStickyItemMeasurement;
16
+ invalidate: () => void;
17
+ apply: (layout: (IStickyItemMeasurement & {
18
+ top: number;
19
+ index: number;
20
+ }) | null) => void;
21
+ }
22
+ export interface IStickyGroupContextValue {
23
+ baseZIndex: number;
24
+ register: (handle: IStickyItemHandle) => () => void;
25
+ scheduleUpdate: () => void;
26
+ mode: IStickyMode;
27
+ }
28
+ export declare const StickyGroupContext: import("react").Context<IStickyGroupContextValue | null>;
29
+ export declare function useStickyContext(): IStickyGroupContextValue | null;
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ export type { IStickyMode } from './context.cjs';
3
+ export * from './sticky-item.cjs';
4
+ export interface IStickyContainerProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ children: React.ReactNode;
6
+ /**
7
+ * The offset from the top of the viewport for sticky elements. Default is 0.
8
+ */
9
+ offsetTop?: number;
10
+ /**
11
+ * base z-index for sticky items. Default is 200. minimum z-index is 20.
12
+ * * - When using the `replace` mode, the z-index of a `StickyItem` is calculated as `baseZIndex` minus its index within the container.
13
+ * * - When using the `stack` mode, the z-index of a `StickyItem` is calculated as `baseZIndex` plus its index.
14
+ * * should be greater than number of sticky items in the container.
15
+ * * use it when you need nest StickyContainer or need to change z-index of sticky items.
16
+ */
17
+ baseZIndex?: number;
18
+ /**
19
+ * Default sticky mode for the group. 'none' disables sticky behavior.
20
+ */
21
+ defaultMode?: 'replace' | 'stack' | 'none';
22
+ /**
23
+ * Callback triggered when the total height of all currently sticky items changes.
24
+ * @param height The total height of all sticky items inside the container.
25
+ */
26
+ onStickyItemsHeightChange?: (height: number) => void;
27
+ /**
28
+ * Define the constraint for sticky behavior
29
+ * - undefined (default): Sticky items will stop being sticky when StickyContainer leaves viewport
30
+ * - 'none': No container boundary; offsets remain viewport-relative
31
+ */
32
+ constraint?: 'none';
33
+ }
34
+ export declare function StickyContainer({ children, offsetTop, baseZIndex, onStickyItemsHeightChange, defaultMode, constraint, className, ...rest }: IStickyContainerProps): React.ReactElement<any, any>;
package/dist/index.d.ts CHANGED
@@ -1,56 +1,34 @@
1
- import { default as default_2 } from 'react';
2
- import { JSX } from 'react/jsx-runtime';
3
-
4
- export declare interface IStickyContainerProps extends default_2.HTMLAttributes<HTMLDivElement> {
5
- children: default_2.ReactNode;
6
- /**
7
- * The offset from the top of the viewport for sticky elements. Default is 0.
8
- */
9
- offsetTop?: number;
10
- /**
11
- * base z-index for sticky items. Default is 200. minimum z-index is 20.
12
- * * - When using the `replace` mode, the z-index of a `StickyItem` is calculated as `baseZIndex` minus its index within the container.
13
- * * - When using the `stack` mode, the z-index of a `StickyItem` is calculated as `baseZIndex` plus its index.
14
- * * should be greater than number of sticky items in the container.
15
- * * for performance reasons, when baseZIndex is changed, the component will not re-render.
16
- * * use it when you need nest StickyContainer or need to change z-index of sticky items.
17
- */
18
- baseZIndex?: number;
19
- /**
20
- * Default sticky mode for the group. 'none' disables sticky behavior.
21
- */
22
- defaultMode?: 'replace' | 'stack' | 'none';
23
- /**
24
- * Callback triggered when the total height of all currently sticky items changes.
25
- * @param height The total height of all sticky items inside the container.
26
- */
27
- onStickyItemsHeightChange?: (height: number) => void;
28
- /**
29
- * Define the constraint for sticky behavior
30
- * - undefined (default): Sticky items will stop being sticky when StickyContainer leaves viewport
31
- * - 'none': No constraints, similar to CSS position:sticky behavior
32
- */
33
- constraint?: 'none';
34
- }
35
-
36
- export declare interface IStickyItemProps extends default_2.HTMLAttributes<HTMLDivElement> {
37
- children: default_2.ReactNode;
38
- /**
39
- * Sticky mode for this item. Defaults to the StickyContainer's mode if not specified.
40
- */
41
- mode?: IStickyMode;
42
- }
43
-
44
- /**
45
- * Sticky mode:
46
- * - 'replace': This item replaces the previous sticky item; its height is set to the previous sticky item's height.
47
- * - 'stack': This item stacks on top of previous sticky items; its height is set to 0.
48
- * - 'none': This item is not sticky.
49
- */
50
- export declare type IStickyMode = 'replace' | 'stack' | 'none';
51
-
52
- export declare function StickyContainer({ children, offsetTop, baseZIndex, onStickyItemsHeightChange, defaultMode, constraint, ...rest }: IStickyContainerProps): JSX.Element;
53
-
54
- export declare function StickyItem({ mode, children, className, ...rest }: IStickyItemProps): JSX.Element;
55
-
56
- export { }
1
+ import React from 'react';
2
+ export type { IStickyMode } from './context.js';
3
+ export * from './sticky-item.js';
4
+ export interface IStickyContainerProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ children: React.ReactNode;
6
+ /**
7
+ * The offset from the top of the viewport for sticky elements. Default is 0.
8
+ */
9
+ offsetTop?: number;
10
+ /**
11
+ * base z-index for sticky items. Default is 200. minimum z-index is 20.
12
+ * * - When using the `replace` mode, the z-index of a `StickyItem` is calculated as `baseZIndex` minus its index within the container.
13
+ * * - When using the `stack` mode, the z-index of a `StickyItem` is calculated as `baseZIndex` plus its index.
14
+ * * should be greater than number of sticky items in the container.
15
+ * * use it when you need nest StickyContainer or need to change z-index of sticky items.
16
+ */
17
+ baseZIndex?: number;
18
+ /**
19
+ * Default sticky mode for the group. 'none' disables sticky behavior.
20
+ */
21
+ defaultMode?: 'replace' | 'stack' | 'none';
22
+ /**
23
+ * Callback triggered when the total height of all currently sticky items changes.
24
+ * @param height The total height of all sticky items inside the container.
25
+ */
26
+ onStickyItemsHeightChange?: (height: number) => void;
27
+ /**
28
+ * Define the constraint for sticky behavior
29
+ * - undefined (default): Sticky items will stop being sticky when StickyContainer leaves viewport
30
+ * - 'none': No container boundary; offsets remain viewport-relative
31
+ */
32
+ constraint?: 'none';
33
+ }
34
+ export declare function StickyContainer({ children, offsetTop, baseZIndex, onStickyItemsHeightChange, defaultMode, constraint, className, ...rest }: IStickyContainerProps): React.ReactElement<any, any>;
@@ -0,0 +1,2 @@
1
+ /** Share observations of ancestors and preceding siblings across all containers. */
2
+ export declare function observeLayoutChanges(container: HTMLElement, callback: () => void): () => void;
@@ -0,0 +1,2 @@
1
+ /** Share observations of ancestors and preceding siblings across all containers. */
2
+ export declare function observeLayoutChanges(container: HTMLElement, callback: () => void): () => void;
@@ -0,0 +1 @@
1
+ "use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));let c=require("react");c=s(c,1);var l=(0,c.createContext)(null);function u(){return(0,c.useContext)(l)}var d=new Map,f=null;function p(e,t){if(typeof ResizeObserver>`u`)return()=>{};f!=null||(f=new ResizeObserver(e=>{let t=new Map;for(let r of e){var n;for(let e of(n=d.get(r.target))==null?[]:n){let n=t.get(e);n?n.push(r):t.set(e,[r])}}for(let[e,n]of t)e(n)}));let n=d.get(e);return n||(n=new Set,d.set(e,n),f.observe(e)),n.add(t),()=>{n.delete(t),n.size||(d.delete(e),f==null||f.unobserve(e)),d.size||(f==null||f.disconnect(),f=null)}}var m=new Map,h=new Map,g=null,_=!1;function v(){for(let e of m.values())e()}function y(){_||(_=!0,Promise.resolve().then(()=>{_=!1,m.size&&b()}))}function b(){let e=new Set,t=new Set;for(let n of m.keys())for(let r=n;r!=null&&r.parentElement;r=r.parentElement){let n=r.parentElement;e.add(n);for(let t=r.previousElementSibling;t&&!e.has(t);t=t.previousElementSibling)e.add(t);if(t.has(n))break;t.add(n)}let n=[...h.keys()].filter(t=>!e.has(t));n.length&&(g==null||g.disconnect());for(let e of n)h.get(e)(),h.delete(e);for(let t of e){let e=!h.has(t);e&&h.set(t,p(t,v)),(e||n.length)&&(g==null||g.observe(t,{childList:!0,attributes:!0,attributeFilter:[`class`,`style`]}))}}function x(e,t){return m.set(e,t),!g&&typeof MutationObserver<`u`&&(g=new MutationObserver(e=>{e.some(e=>e.type===`childList`)&&y(),v()})),y(),()=>{if(m.delete(e),m.size)y();else{g==null||g.disconnect(),g=null;for(let e of h.values())e();h.clear()}}}var S=new Set,C=new Set,w=null;function T(e){S.has(e)&&(C.add(e),w!=null||(w=requestAnimationFrame(O)))}function E(){for(let e of S)T(e)}function D(){for(let e of S)e.resize(),T(e)}function O(){w=null;let e=[...C];C.clear();let t=[],n=[],r=[];for(let n of e)if(S.has(n))try{let e=n.read();e&&t.push({client:n,write:e})}catch(e){r.push(e)}for(let{client:e,write:i}of t)if(S.has(e))try{let t=i();t&&n.push({client:e,notify:t})}catch(e){r.push(e)}for(let{client:e,notify:t}of n)if(S.has(e))try{t()}catch(e){r.push(e)}if(r.length)throw r[0]}function k(e,t){let n={read:e,resize:t};return S.size||(window.addEventListener(`scroll`,E,{passive:!0,capture:!0}),window.addEventListener(`resize`,D,{passive:!0})),S.add(n),T(n),{schedule:()=>T(n),stop:()=>{S.delete(n),C.delete(n),!C.size&&w!==null&&(cancelAnimationFrame(w),w=null),S.size||(window.removeEventListener(`scroll`,E,!0),window.removeEventListener(`resize`,D))}}}function A({mode:e,children:t,className:n,...r}){var i;let a=u(),o=(0,c.useRef)(null),s=(0,c.useRef)(null),l=a==null?void 0:a.register,d=e==null?a==null?void 0:a.mode:e,f=a==null?void 0:a.baseZIndex,p=(i=r.style)==null?void 0:i.height,m=(0,c.useRef)(null),h=a==null?void 0:a.scheduleUpdate;return(0,c.useEffect)(()=>{let e=o.current,t=s.current;if(!e||!t)return;let n=typeof p==`number`?`${p}px`:p==null?``:p;if(j(e,`height`,n),!l||!d||d===`none`)return;let r=p===void 0||[`auto`,`initial`,`unset`,`revert`,`revert-layer`,`fit-content`,`min-content`,`max-content`].includes(String(p)),i=!1,a=null,c=()=>{if(i){i=!1,a=null,t.classList.remove(`is-sticky`),e.style.height=n;for(let e of[`top`,`left`,`width`,`z-index`])t.style.removeProperty(e)}},u=null,h={el:e,content:t,mode:d,apply:n=>{if(n===null){c();return}let{top:o,wrapperHeight:s,width:l,left:u,index:p}=n;a&&a.top===o&&a.wrapperHeight===s&&a.width===l&&a.left===u&&a.index===p||(a=n,r&&j(e,`height`,`${s}px`),j(t,`top`,`${o}px`),j(t,`left`,`${u}px`),j(t,`width`,`${l}px`),j(t,`zIndex`,`${(f==null?200:f)+(d===`replace`?-p:p)}`),i||(i=!0,t.classList.add(`is-sticky`)))},measure:n=>{u!=null||(u=M(e,t));let r=t.getBoundingClientRect().height;return{height:r,wrapperHeight:r+u.heightInset,width:Math.max(0,n.width-u.widthInset),left:n.left+u.leftInset}},invalidate:()=>{u=null,a=null}};m.current=h;let g=l(h);return()=>{m.current=null,g(),c()}},[l,d,f,p]),(0,c.useEffect)(()=>{var e;(e=m.current)==null||e.invalidate(),h==null||h()}),c.default.createElement(`div`,{...r,className:[`oe-sticky-item`,n].filter(Boolean).join(` `),ref:o},c.default.createElement(`div`,{className:`oe-sticky-content`,ref:s},t))}function j(e,t,n){e.style[t]!==n&&(e.style[t]=n)}function M(e,t){let n=getComputedStyle(e),r=getComputedStyle(t),i=e=>Number.parseFloat(e)||0,a=e=>i(e.paddingLeft)+i(e.paddingRight)+i(e.borderLeftWidth)+i(e.borderRightWidth);return{heightInset:n.boxSizing===`border-box`?i(n.paddingTop)+i(n.paddingBottom)+i(n.borderTopWidth)+i(n.borderBottomWidth):0,widthInset:a(n)+(r.boxSizing===`border-box`?0:a(r)),leftInset:i(n.paddingLeft)+i(n.borderLeftWidth)}}function N({children:e,offsetTop:t=0,baseZIndex:n,onStickyItemsHeightChange:r,defaultMode:i=`replace`,constraint:a,className:o,...s}){let u=(0,c.useRef)(null),d=(0,c.useRef)(new Set),f=(0,c.useRef)(null),m=(0,c.useRef)(new Map),h=(0,c.useRef)(!1),g=(0,c.useRef)(null),_=(0,c.useRef)(new Map),v=(0,c.useRef)(0),y=(0,c.useRef)({offsetTop:t,constraint:a,onStickyItemsHeightChange:r}),b=(0,c.useCallback)(()=>{var e;return(e=g.current)==null?void 0:e.schedule()},[]),S=(0,c.useCallback)(()=>{var e;let t=u.current;if(!t)return;let n=y.current,r=t.getBoundingClientRect();if(!(n.constraint===`none`||r.top<=n.offsetTop&&r.bottom>=n.offsetTop))return()=>{if(h.current){t.classList.remove(`can-sticky`);for(let e of d.current)e.apply(null);h.current=!1}if(v.current!==0)return v.current=0,()=>{var e;return(e=n.onStickyItemsHeightChange)==null?void 0:e.call(n,0)}};let i=((e=f.current)==null?f.current=[...d.current]:e).map(e=>({item:e,rect:e.el.getBoundingClientRect()})).sort((e,t)=>{let n=e.rect.top-t.rect.top;return n===0?e.item.el.compareDocumentPosition(t.item.el)&Node.DOCUMENT_POSITION_FOLLOWING?-1:1:n}),a=n.offsetTop,o=0,s=i.map(({item:e,rect:t},n)=>{var r;if(t.top>a)return null;let s=(r=i[n+1])==null?void 0:r.rect.top;if(e.mode===`replace`&&s!==void 0&&s<a)return null;let c=e.measure(t),l=e.mode===`replace`&&s!==void 0?Math.min(a,s-c.height):a;return o+=c.height,e.mode===`stack`&&(a+=c.height),{...c,top:l,index:n}}),c=n.constraint===`none`?0:Math.min(0,r.bottom-n.offsetTop-o);return()=>{if(h.current=s.some(e=>e!==null),t.classList.toggle(`can-sticky`,h.current),i.forEach(({item:e},t)=>{let n=s[t];n&&(n.top+=c),e.apply(n)}),v.current!==o)return v.current=o,()=>{var e;return(e=n.onStickyItemsHeightChange)==null?void 0:e.call(n,o)}}},[]),C=(0,c.useCallback)(e=>{var t;for(let n of e)(t=m.current.get(n.target))==null||t.invalidate();b()},[b]),w=(0,c.useCallback)(()=>{for(let e of d.current)e.invalidate()},[]),T=(0,c.useCallback)(e=>(d.current.add(e),f.current=null,m.current.set(e.el,e),m.current.set(e.content,e),g.current&&(_.current.set(e.el,p(e.el,C)),_.current.set(e.content,p(e.content,C))),b(),()=>{var t,n;(t=_.current.get(e.el))==null||t(),(n=_.current.get(e.content))==null||n(),_.current.delete(e.el),_.current.delete(e.content),m.current.delete(e.el),m.current.delete(e.content),d.current.delete(e),f.current=null,b()}),[b,C]);(0,c.useEffect)(()=>{g.current=k(S,w);let e;if(typeof ResizeObserver<`u`){u.current&&(_.current.set(u.current,p(u.current,C)),e=x(u.current,()=>{w(),b()}));for(let e of d.current)_.current.set(e.el,p(e.el,C)),_.current.set(e.content,p(e.content,C))}let t=_.current;return()=>{var n;(n=g.current)==null||n.stop(),g.current=null,e==null||e();for(let e of t.values())e();t.clear()}},[S,w,b,C]),(0,c.useEffect)(()=>{y.current={offsetTop:t,constraint:a,onStickyItemsHeightChange:r};for(let e of d.current)e.invalidate();b()});let E=n===void 0?200:Math.max(Number(n)||0,20),D=(0,c.useMemo)(()=>({register:T,scheduleUpdate:b,baseZIndex:E,mode:i}),[T,b,E,i]);return c.default.createElement(l.Provider,{value:D},c.default.createElement(`div`,{...s,ref:u,className:[`oe-sticky-container`,o].filter(Boolean).join(` `)},e))}exports.StickyContainer=N,exports.StickyItem=A;
@@ -1,157 +1,329 @@
1
- import { jsx as w } from "react/jsx-runtime";
2
- import { createContext as Z, useContext as L, useRef as k, useState as T, useEffect as E, useCallback as v, useMemo as _ } from "react";
3
- const O = 20, A = 200, $ = Z(null);
4
- function z() {
5
- return L($);
1
+ "use client";
2
+ import e, { createContext as t, useCallback as n, useContext as r, useEffect as i, useMemo as a, useRef as o } from "react";
3
+ //#region src/context.ts
4
+ var s = t(null);
5
+ function c() {
6
+ return r(s);
6
7
  }
7
- function M({ mode: f, children: y, className: h, ...I }) {
8
- const e = z(), g = k(null), S = k(null), [m, o] = T(!1), l = k({
9
- fixedOffsetTop: (e == null ? void 0 : e.fixedOffsetTop) || 0,
10
- isSticky: m,
11
- baseZIndex: (e == null ? void 0 : e.baseZIndex) || O
12
- });
13
- return l.current.baseZIndex = (e == null ? void 0 : e.baseZIndex) || O, l.current.fixedOffsetTop = (e == null ? void 0 : e.fixedOffsetTop) || 0, l.current.isSticky = m, E(() => {
14
- const t = S.current, a = g.current, n = f || (e == null ? void 0 : e.mode);
15
- if (!e || !a || !t || n === "none" || !n || !e.register) {
16
- o(!1);
17
- return;
18
- }
19
- const x = {
20
- el: a,
21
- update: (B, c, s, r, u) => {
22
- if (!B || c > s)
23
- return l.current.isSticky && o(!1), 0;
24
- const i = t.getBoundingClientRect().height;
25
- let p = s;
26
- if (n === "replace" && typeof r < "u") {
27
- const d = r - (s + i);
28
- if (d < 0 && (p = s + d, d + i < 0))
29
- return l.current.isSticky && o(!1), 0;
30
- }
31
- return a.style.height = `${i}px`, l.current.isSticky || o(!0), t.style.top = `${p}px`, t.style.width = `${a.offsetWidth}px`, t.style.zIndex = `${l.current.baseZIndex + (n === "replace" ? -u : u)}`, n === "replace" ? 0 : i;
32
- }
33
- };
34
- return e.register(x);
35
- }, [e == null ? void 0 : e.mode, e == null ? void 0 : e.register, f]), E(() => {
36
- const t = g.current, a = S.current, n = e == null ? void 0 : e.updateStickyItemsHeight;
37
- if (!m || !t || !a || !n)
38
- return;
39
- const H = n(a.getBoundingClientRect().height);
40
- return () => {
41
- N(t, ["height"]), N(a, ["top", "z-index", "width"]), H();
42
- };
43
- }, [m, e == null ? void 0 : e.updateStickyItemsHeight]), /* @__PURE__ */ w("div", { className: "oe-sticky-item " + (h || ""), ...I, ref: g, children: /* @__PURE__ */ w("div", { className: "oe-sticky-content " + (m ? "is-sticky" : ""), ref: S, children: y }) });
8
+ //#endregion
9
+ //#region src/resize-observer.ts
10
+ var l = /* @__PURE__ */ new Map(), u = null;
11
+ function d(e, t) {
12
+ if (typeof ResizeObserver > "u") return () => {};
13
+ u != null || (u = new ResizeObserver((e) => {
14
+ let t = /* @__PURE__ */ new Map();
15
+ for (let r of e) {
16
+ var n;
17
+ for (let e of (n = l.get(r.target)) == null ? [] : n) {
18
+ let n = t.get(e);
19
+ n ? n.push(r) : t.set(e, [r]);
20
+ }
21
+ }
22
+ for (let [e, n] of t) e(n);
23
+ }));
24
+ let n = l.get(e);
25
+ return n || (n = /* @__PURE__ */ new Set(), l.set(e, n), u.observe(e)), n.add(t), () => {
26
+ n.delete(t), n.size || (l.delete(e), u == null || u.unobserve(e)), l.size || (u == null || u.disconnect(), u = null);
27
+ };
44
28
  }
45
- function N(f, y) {
46
- y.forEach((h) => {
47
- f.style.removeProperty(h);
48
- });
29
+ //#endregion
30
+ //#region src/layout-observer.ts
31
+ var f = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map(), m = null, h = !1;
32
+ function g() {
33
+ for (let e of f.values()) e();
49
34
  }
50
- function P({
51
- children: f,
52
- offsetTop: y = 0,
53
- baseZIndex: h,
54
- onStickyItemsHeightChange: I,
55
- defaultMode: e = "replace",
56
- constraint: g,
57
- ...S
58
- }) {
59
- const m = k(null), o = k([]), l = k(null), t = k({
60
- fixedOffsetTop: y,
61
- defaultMode: e,
62
- stickyItemsHeight: 0,
63
- onStickyItemsHeightChange: I,
64
- lastCanSticky: !1
65
- });
66
- t.current.onStickyItemsHeightChange = I;
67
- const a = v(() => {
68
- var c;
69
- return g === "none" ? {
70
- top: -1 / 0,
71
- bottom: 1 / 0,
72
- left: -1 / 0,
73
- right: 1 / 0,
74
- width: 1 / 0,
75
- height: 1 / 0,
76
- x: 0,
77
- y: 0,
78
- toJSON: () => ({})
79
- } : ((c = m.current) == null ? void 0 : c.getBoundingClientRect()) || { top: 0, bottom: 0, left: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => ({}) };
80
- }, [g]), n = v(() => {
81
- const c = m.current;
82
- !c || l.current || (l.current = requestAnimationFrame(() => {
83
- l.current = null;
84
- const s = a(), { fixedOffsetTop: r, stickyItemsHeight: u } = t.current;
85
- let i;
86
- if (g === "none" ? i = !0 : i = !(s.top > r || s.bottom < r), !i) {
87
- t.current.lastCanSticky !== i && (c.classList.toggle("can-sticky", !1), o.current.forEach((C) => C.update(!1, 0, 0, 0, 0)), t.current.lastCanSticky = i);
88
- return;
89
- }
90
- t.current.lastCanSticky = i, u > 0 && c.classList.toggle("can-sticky", !0);
91
- let p, d = 0;
92
- p = r, d = s.bottom - (r + u), d > 0 && (d = 0);
93
- const b = o.current.map((C) => C.el.getBoundingClientRect().top);
94
- o.current.forEach((C, R) => {
95
- p += C.update(i, b[R], p + d, b[R + 1], R);
96
- });
97
- }));
98
- }, [a, g]), H = v((c) => {
99
- const s = t.current.stickyItemsHeight + c;
100
- return t.current.stickyItemsHeight = s, setTimeout(() => {
101
- var r, u;
102
- (u = (r = t.current).onStickyItemsHeightChange) == null || u.call(r, s);
103
- }, 0), () => {
104
- const r = t.current.stickyItemsHeight - c;
105
- t.current.stickyItemsHeight = r, setTimeout(() => {
106
- var u, i;
107
- (i = (u = t.current).onStickyItemsHeightChange) == null || i.call(u, r);
108
- }, 0);
109
- };
110
- }, []);
111
- E(() => {
112
- t.current.fixedOffsetTop = y, t.current.defaultMode = e, setTimeout(() => {
113
- n();
114
- }, 10);
115
- }, [y, e, n]);
116
- const x = v((c) => (o.current.push(c), o.current = W(o.current), n(), () => {
117
- o.current = o.current.filter((s) => s !== c), n();
118
- }), [n]);
119
- E(() => (window.addEventListener("scroll", n, { passive: !0 }), window.addEventListener("resize", n, { passive: !0 }), () => {
120
- window.removeEventListener("scroll", n), window.removeEventListener("resize", n);
121
- }), [n]);
122
- const B = _(() => D(h), [h]);
123
- return /* @__PURE__ */ w(
124
- $.Provider,
125
- {
126
- value: {
127
- register: x,
128
- baseZIndex: B,
129
- updateStickyItemsHeight: H,
130
- fixedOffsetTop: y,
131
- mode: e
132
- },
133
- children: /* @__PURE__ */ w(
134
- "div",
135
- {
136
- ...S,
137
- ref: m,
138
- className: "oe-sticky-container",
139
- children: f
140
- }
141
- )
142
- }
143
- );
35
+ function _() {
36
+ h || (h = !0, Promise.resolve().then(() => {
37
+ h = !1, f.size && v();
38
+ }));
144
39
  }
145
- function D(f) {
146
- return typeof f > "u" ? A : Math.max(Number(f) || 0, O);
40
+ function v() {
41
+ let e = /* @__PURE__ */ new Set(), t = /* @__PURE__ */ new Set();
42
+ for (let n of f.keys()) for (let r = n; r != null && r.parentElement; r = r.parentElement) {
43
+ let n = r.parentElement;
44
+ e.add(n);
45
+ for (let t = r.previousElementSibling; t && !e.has(t); t = t.previousElementSibling) e.add(t);
46
+ if (t.has(n)) break;
47
+ t.add(n);
48
+ }
49
+ let n = [...p.keys()].filter((t) => !e.has(t));
50
+ n.length && (m == null || m.disconnect());
51
+ for (let e of n) p.get(e)(), p.delete(e);
52
+ for (let t of e) {
53
+ let e = !p.has(t);
54
+ e && p.set(t, d(t, g)), (e || n.length) && (m == null || m.observe(t, {
55
+ childList: !0,
56
+ attributes: !0,
57
+ attributeFilter: ["class", "style"]
58
+ }));
59
+ }
147
60
  }
148
- function W(f) {
149
- return f.sort((y, h) => {
150
- const I = y.el.getBoundingClientRect(), e = h.el.getBoundingClientRect();
151
- return I.top - e.top;
152
- });
61
+ function y(e, t) {
62
+ return f.set(e, t), !m && typeof MutationObserver < "u" && (m = new MutationObserver((e) => {
63
+ e.some((e) => e.type === "childList") && _(), g();
64
+ })), _(), () => {
65
+ if (f.delete(e), f.size) _();
66
+ else {
67
+ m == null || m.disconnect(), m = null;
68
+ for (let e of p.values()) e();
69
+ p.clear();
70
+ }
71
+ };
153
72
  }
154
- export {
155
- P as StickyContainer,
156
- M as StickyItem
157
- };
73
+ //#endregion
74
+ //#region src/scheduler.ts
75
+ var b = /* @__PURE__ */ new Set(), x = /* @__PURE__ */ new Set(), S = null;
76
+ function C(e) {
77
+ b.has(e) && (x.add(e), S != null || (S = requestAnimationFrame(E)));
78
+ }
79
+ function w() {
80
+ for (let e of b) C(e);
81
+ }
82
+ function T() {
83
+ for (let e of b) e.resize(), C(e);
84
+ }
85
+ function E() {
86
+ S = null;
87
+ let e = [...x];
88
+ x.clear();
89
+ let t = [], n = [], r = [];
90
+ for (let n of e) if (b.has(n)) try {
91
+ let e = n.read();
92
+ e && t.push({
93
+ client: n,
94
+ write: e
95
+ });
96
+ } catch (e) {
97
+ r.push(e);
98
+ }
99
+ for (let { client: e, write: i } of t) if (b.has(e)) try {
100
+ let t = i();
101
+ t && n.push({
102
+ client: e,
103
+ notify: t
104
+ });
105
+ } catch (e) {
106
+ r.push(e);
107
+ }
108
+ for (let { client: e, notify: t } of n) if (b.has(e)) try {
109
+ t();
110
+ } catch (e) {
111
+ r.push(e);
112
+ }
113
+ if (r.length) throw r[0];
114
+ }
115
+ function D(e, t) {
116
+ let n = {
117
+ read: e,
118
+ resize: t
119
+ };
120
+ return b.size || (window.addEventListener("scroll", w, {
121
+ passive: !0,
122
+ capture: !0
123
+ }), window.addEventListener("resize", T, { passive: !0 })), b.add(n), C(n), {
124
+ schedule: () => C(n),
125
+ stop: () => {
126
+ b.delete(n), x.delete(n), !x.size && S !== null && (cancelAnimationFrame(S), S = null), b.size || (window.removeEventListener("scroll", w, !0), window.removeEventListener("resize", T));
127
+ }
128
+ };
129
+ }
130
+ //#endregion
131
+ //#region src/sticky-item.tsx
132
+ function O({ mode: t, children: n, className: r, ...a }) {
133
+ var s;
134
+ let l = c(), u = o(null), d = o(null), f = l == null ? void 0 : l.register, p = t == null ? l == null ? void 0 : l.mode : t, m = l == null ? void 0 : l.baseZIndex, h = (s = a.style) == null ? void 0 : s.height, g = o(null), _ = l == null ? void 0 : l.scheduleUpdate;
135
+ return i(() => {
136
+ let e = u.current, t = d.current;
137
+ if (!e || !t) return;
138
+ let n = typeof h == "number" ? `${h}px` : h == null ? "" : h;
139
+ if (k(e, "height", n), !f || !p || p === "none") return;
140
+ let r = h === void 0 || [
141
+ "auto",
142
+ "initial",
143
+ "unset",
144
+ "revert",
145
+ "revert-layer",
146
+ "fit-content",
147
+ "min-content",
148
+ "max-content"
149
+ ].includes(String(h)), i = !1, a = null, o = () => {
150
+ if (i) {
151
+ i = !1, a = null, t.classList.remove("is-sticky"), e.style.height = n;
152
+ for (let e of [
153
+ "top",
154
+ "left",
155
+ "width",
156
+ "z-index"
157
+ ]) t.style.removeProperty(e);
158
+ }
159
+ }, s = null, c = {
160
+ el: e,
161
+ content: t,
162
+ mode: p,
163
+ apply: (n) => {
164
+ if (n === null) {
165
+ o();
166
+ return;
167
+ }
168
+ let { top: s, wrapperHeight: c, width: l, left: u, index: d } = n;
169
+ a && a.top === s && a.wrapperHeight === c && a.width === l && a.left === u && a.index === d || (a = n, r && k(e, "height", `${c}px`), k(t, "top", `${s}px`), k(t, "left", `${u}px`), k(t, "width", `${l}px`), k(t, "zIndex", `${(m == null ? 200 : m) + (p === "replace" ? -d : d)}`), i || (i = !0, t.classList.add("is-sticky")));
170
+ },
171
+ measure: (n) => {
172
+ s != null || (s = A(e, t));
173
+ let r = t.getBoundingClientRect().height;
174
+ return {
175
+ height: r,
176
+ wrapperHeight: r + s.heightInset,
177
+ width: Math.max(0, n.width - s.widthInset),
178
+ left: n.left + s.leftInset
179
+ };
180
+ },
181
+ invalidate: () => {
182
+ s = null, a = null;
183
+ }
184
+ };
185
+ g.current = c;
186
+ let l = f(c);
187
+ return () => {
188
+ g.current = null, l(), o();
189
+ };
190
+ }, [
191
+ f,
192
+ p,
193
+ m,
194
+ h
195
+ ]), i(() => {
196
+ var e;
197
+ (e = g.current) == null || e.invalidate(), _ == null || _();
198
+ }), /* @__PURE__ */ e.createElement("div", {
199
+ ...a,
200
+ className: ["oe-sticky-item", r].filter(Boolean).join(" "),
201
+ ref: u
202
+ }, /* @__PURE__ */ e.createElement("div", {
203
+ className: "oe-sticky-content",
204
+ ref: d
205
+ }, n));
206
+ }
207
+ function k(e, t, n) {
208
+ e.style[t] !== n && (e.style[t] = n);
209
+ }
210
+ function A(e, t) {
211
+ let n = getComputedStyle(e), r = getComputedStyle(t), i = (e) => Number.parseFloat(e) || 0, a = (e) => i(e.paddingLeft) + i(e.paddingRight) + i(e.borderLeftWidth) + i(e.borderRightWidth);
212
+ return {
213
+ heightInset: n.boxSizing === "border-box" ? i(n.paddingTop) + i(n.paddingBottom) + i(n.borderTopWidth) + i(n.borderBottomWidth) : 0,
214
+ widthInset: a(n) + (r.boxSizing === "border-box" ? 0 : a(r)),
215
+ leftInset: i(n.paddingLeft) + i(n.borderLeftWidth)
216
+ };
217
+ }
218
+ //#endregion
219
+ //#region src/index.tsx
220
+ function j({ children: t, offsetTop: r = 0, baseZIndex: c, onStickyItemsHeightChange: l, defaultMode: u = "replace", constraint: f, className: p, ...m }) {
221
+ let h = o(null), g = o(/* @__PURE__ */ new Set()), _ = o(null), v = o(/* @__PURE__ */ new Map()), b = o(!1), x = o(null), S = o(/* @__PURE__ */ new Map()), C = o(0), w = o({
222
+ offsetTop: r,
223
+ constraint: f,
224
+ onStickyItemsHeightChange: l
225
+ }), T = n(() => {
226
+ var e;
227
+ return (e = x.current) == null ? void 0 : e.schedule();
228
+ }, []), E = n(() => {
229
+ var e;
230
+ let t = h.current;
231
+ if (!t) return;
232
+ let n = w.current, r = t.getBoundingClientRect();
233
+ if (!(n.constraint === "none" || r.top <= n.offsetTop && r.bottom >= n.offsetTop)) return () => {
234
+ if (b.current) {
235
+ t.classList.remove("can-sticky");
236
+ for (let e of g.current) e.apply(null);
237
+ b.current = !1;
238
+ }
239
+ if (C.current !== 0) return C.current = 0, () => {
240
+ var e;
241
+ return (e = n.onStickyItemsHeightChange) == null ? void 0 : e.call(n, 0);
242
+ };
243
+ };
244
+ let i = ((e = _.current) == null ? _.current = [...g.current] : e).map((e) => ({
245
+ item: e,
246
+ rect: e.el.getBoundingClientRect()
247
+ })).sort((e, t) => {
248
+ let n = e.rect.top - t.rect.top;
249
+ return n === 0 ? e.item.el.compareDocumentPosition(t.item.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1 : n;
250
+ }), a = n.offsetTop, o = 0, s = i.map(({ item: e, rect: t }, n) => {
251
+ var r;
252
+ if (t.top > a) return null;
253
+ let s = (r = i[n + 1]) == null ? void 0 : r.rect.top;
254
+ if (e.mode === "replace" && s !== void 0 && s < a) return null;
255
+ let c = e.measure(t), l = e.mode === "replace" && s !== void 0 ? Math.min(a, s - c.height) : a;
256
+ return o += c.height, e.mode === "stack" && (a += c.height), {
257
+ ...c,
258
+ top: l,
259
+ index: n
260
+ };
261
+ }), c = n.constraint === "none" ? 0 : Math.min(0, r.bottom - n.offsetTop - o);
262
+ return () => {
263
+ if (b.current = s.some((e) => e !== null), t.classList.toggle("can-sticky", b.current), i.forEach(({ item: e }, t) => {
264
+ let n = s[t];
265
+ n && (n.top += c), e.apply(n);
266
+ }), C.current !== o) return C.current = o, () => {
267
+ var e;
268
+ return (e = n.onStickyItemsHeightChange) == null ? void 0 : e.call(n, o);
269
+ };
270
+ };
271
+ }, []), O = n((e) => {
272
+ var t;
273
+ for (let n of e) (t = v.current.get(n.target)) == null || t.invalidate();
274
+ T();
275
+ }, [T]), k = n(() => {
276
+ for (let e of g.current) e.invalidate();
277
+ }, []), A = n((e) => (g.current.add(e), _.current = null, v.current.set(e.el, e), v.current.set(e.content, e), x.current && (S.current.set(e.el, d(e.el, O)), S.current.set(e.content, d(e.content, O))), T(), () => {
278
+ var t, n;
279
+ (t = S.current.get(e.el)) == null || t(), (n = S.current.get(e.content)) == null || n(), S.current.delete(e.el), S.current.delete(e.content), v.current.delete(e.el), v.current.delete(e.content), g.current.delete(e), _.current = null, T();
280
+ }), [T, O]);
281
+ i(() => {
282
+ x.current = D(E, k);
283
+ let e;
284
+ if (typeof ResizeObserver < "u") {
285
+ h.current && (S.current.set(h.current, d(h.current, O)), e = y(h.current, () => {
286
+ k(), T();
287
+ }));
288
+ for (let e of g.current) S.current.set(e.el, d(e.el, O)), S.current.set(e.content, d(e.content, O));
289
+ }
290
+ let t = S.current;
291
+ return () => {
292
+ var n;
293
+ (n = x.current) == null || n.stop(), x.current = null, e == null || e();
294
+ for (let e of t.values()) e();
295
+ t.clear();
296
+ };
297
+ }, [
298
+ E,
299
+ k,
300
+ T,
301
+ O
302
+ ]), i(() => {
303
+ w.current = {
304
+ offsetTop: r,
305
+ constraint: f,
306
+ onStickyItemsHeightChange: l
307
+ };
308
+ for (let e of g.current) e.invalidate();
309
+ T();
310
+ });
311
+ let j = c === void 0 ? 200 : Math.max(Number(c) || 0, 20), M = a(() => ({
312
+ register: A,
313
+ scheduleUpdate: T,
314
+ baseZIndex: j,
315
+ mode: u
316
+ }), [
317
+ A,
318
+ T,
319
+ j,
320
+ u
321
+ ]);
322
+ return /* @__PURE__ */ e.createElement(s.Provider, { value: M }, /* @__PURE__ */ e.createElement("div", {
323
+ ...m,
324
+ ref: h,
325
+ className: ["oe-sticky-container", p].filter(Boolean).join(" ")
326
+ }, t));
327
+ }
328
+ //#endregion
329
+ export { j as StickyContainer, O as StickyItem };
@@ -1 +1 @@
1
- (function(g,I){typeof exports=="object"&&typeof module<"u"?I(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],I):(g=typeof globalThis<"u"?globalThis:g||self,I(g.ReactSticky={},g.jsxRuntime,g.React))})(this,function(g,I,n){"use strict";const N=n.createContext(null);function w(){return n.useContext(N)}const L="";function O({mode:l,children:h,className:k,...E}){const e=w(),m=n.useRef(null),C=n.useRef(null),[p,o]=n.useState(!1),d=n.useRef({fixedOffsetTop:(e==null?void 0:e.fixedOffsetTop)||0,isSticky:p,baseZIndex:(e==null?void 0:e.baseZIndex)||20});return d.current.baseZIndex=(e==null?void 0:e.baseZIndex)||20,d.current.fixedOffsetTop=(e==null?void 0:e.fixedOffsetTop)||0,d.current.isSticky=p,n.useEffect(()=>{const t=C.current,a=m.current,i=l||(e==null?void 0:e.mode);if(!e||!a||!t||i==="none"||!i||!e.register){o(!1);return}const v={el:a,update:(B,c,u,r,f)=>{if(!B||c>u)return d.current.isSticky&&o(!1),0;const s=t.getBoundingClientRect().height;let S=u;if(i==="replace"&&typeof r<"u"){const y=r-(u+s);if(y<0&&(S=u+y,y+s<0))return d.current.isSticky&&o(!1),0}return a.style.height=`${s}px`,d.current.isSticky||o(!0),t.style.top=`${S}px`,t.style.width=`${a.offsetWidth}px`,t.style.zIndex=`${d.current.baseZIndex+(i==="replace"?-f:f)}`,i==="replace"?0:s}};return e.register(v)},[e==null?void 0:e.mode,e==null?void 0:e.register,l]),n.useEffect(()=>{const t=m.current,a=C.current,i=e==null?void 0:e.updateStickyItemsHeight;if(!p||!t||!a||!i)return;const _=i(a.getBoundingClientRect().height);return()=>{b(t,["height"]),b(a,["top","z-index","width"]),_()}},[p,e==null?void 0:e.updateStickyItemsHeight]),I.jsx("div",{className:"oe-sticky-item "+(k||""),...E,ref:m,children:I.jsx("div",{className:"oe-sticky-content "+(p?"is-sticky":""),ref:C,children:h})})}function b(l,h){h.forEach(k=>{l.style.removeProperty(k)})}function Z({children:l,offsetTop:h=0,baseZIndex:k,onStickyItemsHeightChange:E,defaultMode:e="replace",constraint:m,...C}){const p=n.useRef(null),o=n.useRef([]),d=n.useRef(null),t=n.useRef({fixedOffsetTop:h,defaultMode:e,stickyItemsHeight:0,onStickyItemsHeightChange:E,lastCanSticky:!1});t.current.onStickyItemsHeightChange=E;const a=n.useCallback(()=>{var c;return m==="none"?{top:-1/0,bottom:1/0,left:-1/0,right:1/0,width:1/0,height:1/0,x:0,y:0,toJSON:()=>({})}:((c=p.current)==null?void 0:c.getBoundingClientRect())||{top:0,bottom:0,left:0,right:0,width:0,height:0,x:0,y:0,toJSON:()=>({})}},[m]),i=n.useCallback(()=>{const c=p.current;!c||d.current||(d.current=requestAnimationFrame(()=>{d.current=null;const u=a(),{fixedOffsetTop:r,stickyItemsHeight:f}=t.current;let s;if(m==="none"?s=!0:s=!(u.top>r||u.bottom<r),!s){t.current.lastCanSticky!==s&&(c.classList.toggle("can-sticky",!1),o.current.forEach(R=>R.update(!1,0,0,0,0)),t.current.lastCanSticky=s);return}t.current.lastCanSticky=s,f>0&&c.classList.toggle("can-sticky",!0);let S,y=0;S=r,y=u.bottom-(r+f),y>0&&(y=0);const x=o.current.map(R=>R.el.getBoundingClientRect().top);o.current.forEach((R,H)=>{S+=R.update(s,x[H],S+y,x[H+1],H)})}))},[a,m]),_=n.useCallback(c=>{const u=t.current.stickyItemsHeight+c;return t.current.stickyItemsHeight=u,setTimeout(()=>{var r,f;(f=(r=t.current).onStickyItemsHeightChange)==null||f.call(r,u)},0),()=>{const r=t.current.stickyItemsHeight-c;t.current.stickyItemsHeight=r,setTimeout(()=>{var f,s;(s=(f=t.current).onStickyItemsHeightChange)==null||s.call(f,r)},0)}},[]);n.useEffect(()=>{t.current.fixedOffsetTop=h,t.current.defaultMode=e,setTimeout(()=>{i()},10)},[h,e,i]);const v=n.useCallback(c=>(o.current.push(c),o.current=A(o.current),i(),()=>{o.current=o.current.filter(u=>u!==c),i()}),[i]);n.useEffect(()=>(window.addEventListener("scroll",i,{passive:!0}),window.addEventListener("resize",i,{passive:!0}),()=>{window.removeEventListener("scroll",i),window.removeEventListener("resize",i)}),[i]);const B=n.useMemo(()=>T(k),[k]);return I.jsx(N.Provider,{value:{register:v,baseZIndex:B,updateStickyItemsHeight:_,fixedOffsetTop:h,mode:e},children:I.jsx("div",{...C,ref:p,className:"oe-sticky-container",children:l})})}function T(l){return typeof l>"u"?200:Math.max(Number(l)||0,20)}function A(l){return l.sort((h,k)=>{const E=h.el.getBoundingClientRect(),e=k.el.getBoundingClientRect();return E.top-e.top})}g.StickyContainer=Z,g.StickyItem=O,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})});
1
+ "use client";(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("react")):typeof define==`function`&&define.amd?define([`exports`,`react`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.ReactSticky={},e.React))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,c=(e,t,n,o)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=a(t),l=0,u=c.length,d;l<u;l++)d=c[l],!s.call(e,d)&&d!==n&&r(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(o=i(t,d))||o.enumerable});return e};t=((e,t,i)=>(i=e==null?{}:n(o(e)),c(t||!e||!e.__esModule||!s.call(e,`default`)?r(i,`default`,{value:e,enumerable:!0}):i,e)))(t,1);var l=(0,t.createContext)(null);function u(){return(0,t.useContext)(l)}var d=new Map,f=null;function p(e,t){if(typeof ResizeObserver>`u`)return()=>{};f!=null||(f=new ResizeObserver(e=>{let t=new Map;for(let r of e){var n;for(let e of(n=d.get(r.target))==null?[]:n){let n=t.get(e);n?n.push(r):t.set(e,[r])}}for(let[e,n]of t)e(n)}));let n=d.get(e);return n||(n=new Set,d.set(e,n),f.observe(e)),n.add(t),()=>{n.delete(t),n.size||(d.delete(e),f==null||f.unobserve(e)),d.size||(f==null||f.disconnect(),f=null)}}var m=new Map,h=new Map,g=null,_=!1;function v(){for(let e of m.values())e()}function y(){_||(_=!0,Promise.resolve().then(()=>{_=!1,m.size&&b()}))}function b(){let e=new Set,t=new Set;for(let n of m.keys())for(let r=n;r!=null&&r.parentElement;r=r.parentElement){let n=r.parentElement;e.add(n);for(let t=r.previousElementSibling;t&&!e.has(t);t=t.previousElementSibling)e.add(t);if(t.has(n))break;t.add(n)}let n=[...h.keys()].filter(t=>!e.has(t));n.length&&(g==null||g.disconnect());for(let e of n)h.get(e)(),h.delete(e);for(let t of e){let e=!h.has(t);e&&h.set(t,p(t,v)),(e||n.length)&&(g==null||g.observe(t,{childList:!0,attributes:!0,attributeFilter:[`class`,`style`]}))}}function x(e,t){return m.set(e,t),!g&&typeof MutationObserver<`u`&&(g=new MutationObserver(e=>{e.some(e=>e.type===`childList`)&&y(),v()})),y(),()=>{if(m.delete(e),m.size)y();else{g==null||g.disconnect(),g=null;for(let e of h.values())e();h.clear()}}}var S=new Set,C=new Set,w=null;function T(e){S.has(e)&&(C.add(e),w!=null||(w=requestAnimationFrame(O)))}function E(){for(let e of S)T(e)}function D(){for(let e of S)e.resize(),T(e)}function O(){w=null;let e=[...C];C.clear();let t=[],n=[],r=[];for(let n of e)if(S.has(n))try{let e=n.read();e&&t.push({client:n,write:e})}catch(e){r.push(e)}for(let{client:e,write:i}of t)if(S.has(e))try{let t=i();t&&n.push({client:e,notify:t})}catch(e){r.push(e)}for(let{client:e,notify:t}of n)if(S.has(e))try{t()}catch(e){r.push(e)}if(r.length)throw r[0]}function k(e,t){let n={read:e,resize:t};return S.size||(window.addEventListener(`scroll`,E,{passive:!0,capture:!0}),window.addEventListener(`resize`,D,{passive:!0})),S.add(n),T(n),{schedule:()=>T(n),stop:()=>{S.delete(n),C.delete(n),!C.size&&w!==null&&(cancelAnimationFrame(w),w=null),S.size||(window.removeEventListener(`scroll`,E,!0),window.removeEventListener(`resize`,D))}}}function A({mode:e,children:n,className:r,...i}){var a;let o=u(),s=(0,t.useRef)(null),c=(0,t.useRef)(null),l=o==null?void 0:o.register,d=e==null?o==null?void 0:o.mode:e,f=o==null?void 0:o.baseZIndex,p=(a=i.style)==null?void 0:a.height,m=(0,t.useRef)(null),h=o==null?void 0:o.scheduleUpdate;return(0,t.useEffect)(()=>{let e=s.current,t=c.current;if(!e||!t)return;let n=typeof p==`number`?`${p}px`:p==null?``:p;if(j(e,`height`,n),!l||!d||d===`none`)return;let r=p===void 0||[`auto`,`initial`,`unset`,`revert`,`revert-layer`,`fit-content`,`min-content`,`max-content`].includes(String(p)),i=!1,a=null,o=()=>{if(i){i=!1,a=null,t.classList.remove(`is-sticky`),e.style.height=n;for(let e of[`top`,`left`,`width`,`z-index`])t.style.removeProperty(e)}},u=null,h={el:e,content:t,mode:d,apply:n=>{if(n===null){o();return}let{top:s,wrapperHeight:c,width:l,left:u,index:p}=n;a&&a.top===s&&a.wrapperHeight===c&&a.width===l&&a.left===u&&a.index===p||(a=n,r&&j(e,`height`,`${c}px`),j(t,`top`,`${s}px`),j(t,`left`,`${u}px`),j(t,`width`,`${l}px`),j(t,`zIndex`,`${(f==null?200:f)+(d===`replace`?-p:p)}`),i||(i=!0,t.classList.add(`is-sticky`)))},measure:n=>{u!=null||(u=M(e,t));let r=t.getBoundingClientRect().height;return{height:r,wrapperHeight:r+u.heightInset,width:Math.max(0,n.width-u.widthInset),left:n.left+u.leftInset}},invalidate:()=>{u=null,a=null}};m.current=h;let g=l(h);return()=>{m.current=null,g(),o()}},[l,d,f,p]),(0,t.useEffect)(()=>{var e;(e=m.current)==null||e.invalidate(),h==null||h()}),t.default.createElement(`div`,{...i,className:[`oe-sticky-item`,r].filter(Boolean).join(` `),ref:s},t.default.createElement(`div`,{className:`oe-sticky-content`,ref:c},n))}function j(e,t,n){e.style[t]!==n&&(e.style[t]=n)}function M(e,t){let n=getComputedStyle(e),r=getComputedStyle(t),i=e=>Number.parseFloat(e)||0,a=e=>i(e.paddingLeft)+i(e.paddingRight)+i(e.borderLeftWidth)+i(e.borderRightWidth);return{heightInset:n.boxSizing===`border-box`?i(n.paddingTop)+i(n.paddingBottom)+i(n.borderTopWidth)+i(n.borderBottomWidth):0,widthInset:a(n)+(r.boxSizing===`border-box`?0:a(r)),leftInset:i(n.paddingLeft)+i(n.borderLeftWidth)}}function N({children:e,offsetTop:n=0,baseZIndex:r,onStickyItemsHeightChange:i,defaultMode:a=`replace`,constraint:o,className:s,...c}){let u=(0,t.useRef)(null),d=(0,t.useRef)(new Set),f=(0,t.useRef)(null),m=(0,t.useRef)(new Map),h=(0,t.useRef)(!1),g=(0,t.useRef)(null),_=(0,t.useRef)(new Map),v=(0,t.useRef)(0),y=(0,t.useRef)({offsetTop:n,constraint:o,onStickyItemsHeightChange:i}),b=(0,t.useCallback)(()=>{var e;return(e=g.current)==null?void 0:e.schedule()},[]),S=(0,t.useCallback)(()=>{var e;let t=u.current;if(!t)return;let n=y.current,r=t.getBoundingClientRect();if(!(n.constraint===`none`||r.top<=n.offsetTop&&r.bottom>=n.offsetTop))return()=>{if(h.current){t.classList.remove(`can-sticky`);for(let e of d.current)e.apply(null);h.current=!1}if(v.current!==0)return v.current=0,()=>{var e;return(e=n.onStickyItemsHeightChange)==null?void 0:e.call(n,0)}};let i=((e=f.current)==null?f.current=[...d.current]:e).map(e=>({item:e,rect:e.el.getBoundingClientRect()})).sort((e,t)=>{let n=e.rect.top-t.rect.top;return n===0?e.item.el.compareDocumentPosition(t.item.el)&Node.DOCUMENT_POSITION_FOLLOWING?-1:1:n}),a=n.offsetTop,o=0,s=i.map(({item:e,rect:t},n)=>{var r;if(t.top>a)return null;let s=(r=i[n+1])==null?void 0:r.rect.top;if(e.mode===`replace`&&s!==void 0&&s<a)return null;let c=e.measure(t),l=e.mode===`replace`&&s!==void 0?Math.min(a,s-c.height):a;return o+=c.height,e.mode===`stack`&&(a+=c.height),{...c,top:l,index:n}}),c=n.constraint===`none`?0:Math.min(0,r.bottom-n.offsetTop-o);return()=>{if(h.current=s.some(e=>e!==null),t.classList.toggle(`can-sticky`,h.current),i.forEach(({item:e},t)=>{let n=s[t];n&&(n.top+=c),e.apply(n)}),v.current!==o)return v.current=o,()=>{var e;return(e=n.onStickyItemsHeightChange)==null?void 0:e.call(n,o)}}},[]),C=(0,t.useCallback)(e=>{var t;for(let n of e)(t=m.current.get(n.target))==null||t.invalidate();b()},[b]),w=(0,t.useCallback)(()=>{for(let e of d.current)e.invalidate()},[]),T=(0,t.useCallback)(e=>(d.current.add(e),f.current=null,m.current.set(e.el,e),m.current.set(e.content,e),g.current&&(_.current.set(e.el,p(e.el,C)),_.current.set(e.content,p(e.content,C))),b(),()=>{var t,n;(t=_.current.get(e.el))==null||t(),(n=_.current.get(e.content))==null||n(),_.current.delete(e.el),_.current.delete(e.content),m.current.delete(e.el),m.current.delete(e.content),d.current.delete(e),f.current=null,b()}),[b,C]);(0,t.useEffect)(()=>{g.current=k(S,w);let e;if(typeof ResizeObserver<`u`){u.current&&(_.current.set(u.current,p(u.current,C)),e=x(u.current,()=>{w(),b()}));for(let e of d.current)_.current.set(e.el,p(e.el,C)),_.current.set(e.content,p(e.content,C))}let t=_.current;return()=>{var n;(n=g.current)==null||n.stop(),g.current=null,e==null||e();for(let e of t.values())e();t.clear()}},[S,w,b,C]),(0,t.useEffect)(()=>{y.current={offsetTop:n,constraint:o,onStickyItemsHeightChange:i};for(let e of d.current)e.invalidate();b()});let E=r===void 0?200:Math.max(Number(r)||0,20),D=(0,t.useMemo)(()=>({register:T,scheduleUpdate:b,baseZIndex:E,mode:a}),[T,b,E,a]);return t.default.createElement(l.Provider,{value:D},t.default.createElement(`div`,{...c,ref:u,className:[`oe-sticky-container`,s].filter(Boolean).join(` `)},e))}e.StickyContainer=N,e.StickyItem=A});
@@ -0,0 +1,3 @@
1
+ type Listener = (entries: ResizeObserverEntry[]) => void;
2
+ export declare function observeResize(target: Element, listener: Listener): () => void;
3
+ export {};
@@ -0,0 +1,3 @@
1
+ type Listener = (entries: ResizeObserverEntry[]) => void;
2
+ export declare function observeResize(target: Element, listener: Listener): () => void;
3
+ export {};
@@ -0,0 +1,10 @@
1
+ type Write = () => (() => void) | void;
2
+ type Client = {
3
+ read: () => Write | undefined;
4
+ resize: () => void;
5
+ };
6
+ export declare function subscribeUpdates(read: Client['read'], invalidate: () => void): {
7
+ schedule: () => void;
8
+ stop: () => void;
9
+ };
10
+ export {};
@@ -0,0 +1,10 @@
1
+ type Write = () => (() => void) | void;
2
+ type Client = {
3
+ read: () => Write | undefined;
4
+ resize: () => void;
5
+ };
6
+ export declare function subscribeUpdates(read: Client['read'], invalidate: () => void): {
7
+ schedule: () => void;
8
+ stop: () => void;
9
+ };
10
+ export {};
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { type IStickyMode } from './context.cjs';
3
+ export interface IStickyItemProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ children: React.ReactNode;
5
+ /** Sticky mode for this item. Defaults to the StickyContainer's mode. */
6
+ mode?: IStickyMode;
7
+ }
8
+ export declare function StickyItem({ mode, children, className, ...rest }: IStickyItemProps): React.ReactElement<any, any>;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { type IStickyMode } from './context.js';
3
+ export interface IStickyItemProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ children: React.ReactNode;
5
+ /** Sticky mode for this item. Defaults to the StickyContainer's mode. */
6
+ mode?: IStickyMode;
7
+ }
8
+ export declare function StickyItem({ mode, children, className, ...rest }: IStickyItemProps): React.ReactElement<any, any>;
package/dist/style.css CHANGED
@@ -1 +1,2 @@
1
- .oe-sticky-container{position:relative}.oe-sticky-container.can-sticky .oe-sticky-content.is-sticky{position:fixed}.oe-sticky-item{position:relative}.oe-sticky-content{will-change:top,position}
1
+ .oe-sticky-container,.oe-sticky-item{position:relative}.oe-sticky-content.is-sticky{position:fixed}
2
+ /*$vite$:1*/
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "react-sticky-kit",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
- "description": "A lightweight, flexible React sticky container and item component library supporting multiple sticky modes, advanced layouts, and edge cases.",
5
+ "description": "React components for stacked and replacing sticky section headers, with automatic height coordination and viewport offsets.",
6
6
  "keywords": [
7
7
  "react",
8
8
  "sticky",
9
+ "sticky-header",
10
+ "sticky-headers",
11
+ "sticky-section",
12
+ "stacked-headers",
9
13
  "react-sticky",
10
14
  "sticky-container",
11
- "sticky-item",
12
- "z-index"
15
+ "typescript",
16
+ "nextjs"
13
17
  ],
14
18
  "repository": {
15
19
  "type": "git",
@@ -18,30 +22,50 @@
18
22
  "bugs": {
19
23
  "url": "https://github.com/oe/react-sticky-kit/issues"
20
24
  },
21
- "homepage": "https://github.com/oe/react-sticky-kit#readme",
25
+ "homepage": "https://app.evecalm.com/react-sticky-kit/",
22
26
  "license": "MIT",
23
27
  "scripts": {
24
28
  "dev": "vite",
25
- "build": "vite build",
29
+ "build": "vite build && tsc -p tsconfig.build.json && node scripts/build-types.mjs",
26
30
  "preview": "vite preview",
27
31
  "type-check": "tsc --noEmit",
28
- "lint": "eslint src demo --ext .ts,.tsx,.js,.jsx",
32
+ "lint": "eslint src demo test scripts vite.config.ts vite.demo.config.ts vitest.config.ts playwright.config.ts",
29
33
  "test": "vitest run",
30
34
  "test:watch": "vitest",
31
35
  "coverage": "vitest run --coverage",
32
- "pub": "tsx ./scripts/publish.ts"
36
+ "pub": "tsx ./scripts/publish.ts",
37
+ "check:package": "node scripts/check-package.mjs",
38
+ "test:browser": "playwright test",
39
+ "prepublishOnly": "pnpm type-check && pnpm lint && pnpm test && pnpm build && pnpm check:package",
40
+ "check:compatibility": "node scripts/check-compatibility.mjs",
41
+ "build:demo": "vite build --config vite.demo.config.ts"
33
42
  },
34
43
  "files": [
35
44
  "dist"
36
45
  ],
37
- "main": "dist/react-sticky.umd.js",
46
+ "main": "dist/react-sticky.cjs",
38
47
  "module": "dist/react-sticky.es.js",
39
48
  "types": "dist/index.d.ts",
40
49
  "exports": {
41
50
  ".": {
42
- "types": "./dist/index.d.ts",
43
- "import": "./dist/react-sticky.es.js",
44
- "require": "./dist/react-sticky.umd.js"
51
+ "node": {
52
+ "import": {
53
+ "types": "./dist/index.d.cts",
54
+ "default": "./dist/react-sticky.cjs"
55
+ },
56
+ "require": {
57
+ "types": "./dist/index.d.cts",
58
+ "default": "./dist/react-sticky.cjs"
59
+ }
60
+ },
61
+ "import": {
62
+ "types": "./dist/index.d.ts",
63
+ "default": "./dist/react-sticky.es.js"
64
+ },
65
+ "require": {
66
+ "types": "./dist/index.d.cts",
67
+ "default": "./dist/react-sticky.cjs"
68
+ }
45
69
  },
46
70
  "./style": "./dist/style.css",
47
71
  "./dist/style.css": "./dist/style.css"
@@ -52,33 +76,34 @@
52
76
  },
53
77
  "devDependencies": {
54
78
  "@eslint/js": "^9.25.0",
55
- "@testing-library/jest-dom": "^6.1.6",
56
- "@testing-library/react": "^14.1.2",
57
- "@testing-library/user-event": "^14.4.3",
58
- "@types/node": "^22.15.18",
59
- "@types/react": "^19.1.2",
60
- "@types/react-dom": "^19.1.2",
61
- "@typescript-eslint/eslint-plugin": "^8.32.1",
62
- "@typescript-eslint/parser": "^8.32.1",
63
- "@vitejs/plugin-react": "^4.0.0",
64
- "@vitest/coverage-v8": "^3.1.3",
65
- "eslint": "^9.26.0",
79
+ "@playwright/test": "^1.63.0",
80
+ "@testing-library/jest-dom": "^7.0.1",
81
+ "@testing-library/react": "^16.3.3",
82
+ "@types/node": "^24.19.1",
83
+ "@types/react": "^19.3.0",
84
+ "@types/react-dom": "^19.3.0",
85
+ "@typescript-eslint/eslint-plugin": "^8.71.0",
86
+ "@typescript-eslint/parser": "^8.71.0",
87
+ "@vitejs/plugin-react": "^6.1.1",
88
+ "@vitest/coverage-v8": "^5.0.3",
89
+ "eslint": "^9.39.4",
66
90
  "eslint-config-prettier": "^10.1.5",
67
91
  "eslint-plugin-jsx-a11y": "^6.8.0",
68
92
  "eslint-plugin-react": "^7.33.2",
69
- "eslint-plugin-react-hooks": "^5.2.0",
93
+ "eslint-plugin-react-hooks": "^7.1.1",
70
94
  "globals": "^16.0.0",
71
- "identity-obj-proxy": "^3.0.0",
72
- "jsdom": "^26.1.0",
73
- "react": "^19.1.0",
74
- "react-dom": "^19.1.0",
75
- "sass": "^1.88.0",
76
- "ts-node": "^10.9.2",
77
- "tsx": "^4.19.4",
78
- "typescript": "^5.0.0",
79
- "typescript-eslint": "^8.30.1",
80
- "vite": "^4.0.0",
81
- "vite-plugin-dts": "^4.5.4",
82
- "vitest": "^3.1.3"
83
- }
95
+ "jsdom": "^30.1.1",
96
+ "react": "^19.3.0",
97
+ "react-dom": "^19.3.0",
98
+ "sass": "^1.105.1",
99
+ "tsx": "^4.23.15",
100
+ "typescript": "^6.0.3",
101
+ "vite": "^8.3.2",
102
+ "vitest": "^5.0.3"
103
+ },
104
+ "packageManager": "pnpm@12.8.1",
105
+ "sideEffects": [
106
+ "**/*.css",
107
+ "**/*.scss"
108
+ ]
84
109
  }