react-sticky-kit 0.1.6 โ†’ 0.2.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
@@ -31,6 +31,7 @@ A lightweight, flexible React sticky container and item component library. Easil
31
31
  - ๐Ÿ“ฆ Simple API: `<StickyContainer>` and `<StickyItem>`
32
32
  - ๐Ÿงฉ Supports `replace`, `stack`, and `none` sticky modes
33
33
  - ๐Ÿท๏ธ Customizable offset, z-index (baseZIndex), and sticky logic
34
+ - ๐Ÿ–ฅ๏ธ Flexible reference containers (window, DOM elements, refs)
34
35
  - ๐Ÿ”„ Supports SSR/SSG (Next.js, Gatsby, Astro, etc.)
35
36
  - ๐Ÿงช Handles edge cases: empty sections, dynamic heights, zero-height headers, long headers, etc.
36
37
  - โšก๏ธ Written in TypeScript, fully typed
@@ -76,13 +77,14 @@ export default function Example() {
76
77
  ## Props
77
78
 
78
79
  ### `<StickyContainer />`
79
- | Prop | Type | Default | Description |
80
- |-----------------------------|--------------------------------------|-------------|---------------------------------------------------------------------------------------------|
81
- | `offsetTop` | `number` | `0` | Offset from the top of the viewport |
82
- | `defaultMode` | `'replace' \| 'stack' \| 'none'` | `'replace'`| Default sticky mode for all items |
83
- | `baseZIndex` | `number` | `200` | Base z-index for sticky items. Should be greater than the number of sticky items. |
84
- | | | | In `replace` mode, z-index = baseZIndex - index; in `stack` mode, z-index = baseZIndex + index. |
85
- | `onStickyItemsHeightChange` | `(height: number) => void` | | Callback when total sticky height changes |
80
+ | Prop | Type | Default | Description |
81
+ |-----------------------------|------------------------------------------------------|-------------|---------------------------------------------------------------------------------------------|
82
+ | `offsetTop` | `number` | `0` | Offset from the top of the viewport |
83
+ | `defaultMode` | `'replace' \| 'stack' \| 'none'` | `'replace'` | Default sticky mode for all items |
84
+ | `baseZIndex` | `number` | `200` | Base z-index for sticky items. Should be greater than the number of sticky items. |
85
+ | | | | In `replace` mode, z-index = baseZIndex - index; in `stack` mode, z-index = baseZIndex + index. |
86
+ | `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) |
86
88
 
87
89
  ### `<StickyItem />`
88
90
  | Prop | Type | Default | Description |
@@ -94,6 +96,44 @@ export default function Example() {
94
96
  - **stack**: Sticky items stack on top of each other.
95
97
  - **none**: Sticky is disabled for this item.
96
98
 
99
+ ## Constraint Behavior
100
+
101
+ The `constraint` prop allows you to control when sticky items should stop being sticky:
102
+
103
+ ```tsx
104
+ import React from 'react';
105
+ import { StickyContainer, StickyItem } from 'react-sticky-kit';
106
+
107
+ function Example() {
108
+ return (
109
+ <div>
110
+ {/* 1. Default: Container-based constraint */}
111
+ <StickyContainer>
112
+ <StickyItem><div>Sticky stops when container leaves viewport</div></StickyItem>
113
+ <div>Content...</div>
114
+ </StickyContainer>
115
+
116
+ {/* 2. No constraints (like CSS position:sticky) */}
117
+ <StickyContainer constraint="none">
118
+ <StickyItem><div>Always sticky like CSS position:sticky</div></StickyItem>
119
+ <div>Content...</div>
120
+ </StickyContainer>
121
+ </div>
122
+ );
123
+ }
124
+ <div>Content...</div>
125
+ </StickyContainer>
126
+ </div>
127
+ );
128
+ }
129
+ ```
130
+
131
+ ### Special behavior of `constraint="none"`
132
+
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`.
134
+
135
+ In contrast, the default behavior (without specifying a constraint) only makes items sticky when their parent container is visible in the viewport.
136
+
97
137
  ## Demo
98
138
 
99
139
  Run the demo locally:
@@ -106,9 +146,10 @@ pnpm dev
106
146
  Open [http://localhost:5173](http://localhost:5173) and switch between demo pages to explore all features and edge cases:
107
147
  * [iOS Contact](http://localhost:5173/#ios-contact) a dead simple iOS contact clone
108
148
  * [Mixed mode](http://localhost:5173/#mixed-mode) mix replace/stack/none mode
109
- * [Nested](http://localhost:5173/#dynamic-height) nest sticky containers
110
- * [Dynamic sticky items](http://localhost:5173/#nested) dynamic sticky items(add/remove)
149
+ * [Nested](http://localhost:5173/#nested) nest sticky containers
150
+ * [Dynamic sticky items](http://localhost:5173/#dynamic) dynamic sticky items(add/remove)
111
151
  * [Dynamic offsetTop](http://localhost:5173/#dynamic-offset) dynamic offsetTop that adapts to header height changes
152
+ * [Constraint Demo](http://localhost:5173/#constraint) demo showing different constraint options
112
153
 
113
154
  ## SSR/SSG Support
114
155
 
@@ -155,6 +196,10 @@ import 'react-sticky-kit/dist/style.css'
155
196
  </html>
156
197
  ```
157
198
 
199
+ ## Publish steps
200
+
201
+ `pnpm pub`
202
+
158
203
  ## License
159
204
 
160
205
  MIT
package/dist/index.d.ts CHANGED
@@ -25,6 +25,12 @@ export declare interface IStickyContainerProps extends default_2.HTMLAttributes<
25
25
  * @param height The total height of all sticky items inside the container.
26
26
  */
27
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';
28
34
  }
29
35
 
30
36
  export declare interface IStickyItemProps extends default_2.HTMLAttributes<HTMLDivElement> {
@@ -43,7 +49,7 @@ export declare interface IStickyItemProps extends default_2.HTMLAttributes<HTMLD
43
49
  */
44
50
  export declare type IStickyMode = 'replace' | 'stack' | 'none';
45
51
 
46
- export declare function StickyContainer({ children, offsetTop, baseZIndex, onStickyItemsHeightChange, defaultMode, ...rest }: IStickyContainerProps): JSX.Element;
52
+ export declare function StickyContainer({ children, offsetTop, baseZIndex, onStickyItemsHeightChange, defaultMode, constraint, ...rest }: IStickyContainerProps): JSX.Element;
47
53
 
48
54
  export declare function StickyItem({ mode, children, className, ...rest }: IStickyItemProps): JSX.Element;
49
55
 
@@ -1,142 +1,157 @@
1
- import { jsx as R } from "react/jsx-runtime";
2
- import { createContext as b, useContext as Z, useRef as h, useState as L, useEffect as E, useCallback as w, useMemo as N } from "react";
3
- const x = 20, T = 200, $ = b(null);
4
- function _() {
5
- return Z($);
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($);
6
6
  }
7
- function F({ mode: u, children: f, className: g, ...k }) {
8
- const e = _(), S = h(null), I = h(null), [r, m] = L(!1), t = h({
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
9
  fixedOffsetTop: (e == null ? void 0 : e.fixedOffsetTop) || 0,
10
- isSticky: r,
11
- baseZIndex: (e == null ? void 0 : e.baseZIndex) || x
10
+ isSticky: m,
11
+ baseZIndex: (e == null ? void 0 : e.baseZIndex) || O
12
12
  });
13
- return t.current.baseZIndex = (e == null ? void 0 : e.baseZIndex) || x, t.current.fixedOffsetTop = (e == null ? void 0 : e.fixedOffsetTop) || 0, t.current.isSticky = r, E(() => {
14
- const n = I.current, a = S.current, l = u || (e == null ? void 0 : e.mode);
15
- if (!e || !a || !n || l === "none" || !l || !e.register) {
16
- m(!1);
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
17
  return;
18
18
  }
19
- const s = {
19
+ const x = {
20
20
  el: a,
21
- update: (d, c, i, o, C) => {
22
- if (!d || c > i)
23
- return t.current.isSticky && m(!1), 0;
24
- const p = n.getBoundingClientRect().height;
25
- let H = i;
26
- if (l === "replace" && typeof o < "u") {
27
- const y = o - (i + p);
28
- if (y < 0 && (H = i + y, y + p < 0))
29
- return t.current.isSticky && m(!1), 0;
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
30
  }
31
- return a.style.height = `${p}px`, t.current.isSticky || m(!0), n.style.top = `${H}px`, n.style.width = `${a.offsetWidth}px`, n.style.zIndex = `${t.current.baseZIndex + (l === "replace" ? -C : C)}`, l === "replace" ? 0 : p;
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
32
  }
33
33
  };
34
- return e.register(s);
35
- }, [e == null ? void 0 : e.mode, e == null ? void 0 : e.register, u]), E(() => {
36
- const n = S.current, a = I.current, l = e == null ? void 0 : e.updateStickyItemsHeight;
37
- if (!r || !n || !a || !l)
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
38
  return;
39
- const v = l(a.getBoundingClientRect().height);
39
+ const H = n(a.getBoundingClientRect().height);
40
40
  return () => {
41
- O(n, ["height"]), O(a, ["top", "z-index", "width"]), v();
41
+ N(t, ["height"]), N(a, ["top", "z-index", "width"]), H();
42
42
  };
43
- }, [r, e == null ? void 0 : e.updateStickyItemsHeight]), /* @__PURE__ */ R("div", { className: "oe-sticky-item " + (g || ""), ...k, ref: S, children: /* @__PURE__ */ R("div", { className: "oe-sticky-content " + (r ? "is-sticky" : ""), ref: I, children: f }) });
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 }) });
44
44
  }
45
- function O(u, f) {
46
- f.forEach((g) => {
47
- u.style.removeProperty(g);
45
+ function N(f, y) {
46
+ y.forEach((h) => {
47
+ f.style.removeProperty(h);
48
48
  });
49
49
  }
50
- function M({
51
- children: u,
52
- offsetTop: f = 0,
53
- baseZIndex: g,
54
- onStickyItemsHeightChange: k,
50
+ function P({
51
+ children: f,
52
+ offsetTop: y = 0,
53
+ baseZIndex: h,
54
+ onStickyItemsHeightChange: I,
55
55
  defaultMode: e = "replace",
56
+ constraint: g,
56
57
  ...S
57
58
  }) {
58
- const I = h(null), r = h([]), m = h(null), t = h({
59
- fixedOffsetTop: f,
59
+ const m = k(null), o = k([]), l = k(null), t = k({
60
+ fixedOffsetTop: y,
60
61
  defaultMode: e,
61
62
  stickyItemsHeight: 0,
62
- onStickyItemsHeightChange: k,
63
+ onStickyItemsHeightChange: I,
63
64
  lastCanSticky: !1
64
65
  });
65
- t.current.onStickyItemsHeightChange = k;
66
- const n = w(() => {
67
- const s = I.current;
68
- !s || m.current || (m.current = requestAnimationFrame(() => {
69
- m.current = null;
70
- const d = s.getBoundingClientRect(), { fixedOffsetTop: c, stickyItemsHeight: i } = t.current, o = !(d.top > c || d.bottom < c);
71
- if (!o) {
72
- t.current.lastCanSticky !== o && (s.classList.toggle("can-sticky", !1), r.current.forEach((y) => y.update(!1, 0, 0, 0, 0)), t.current.lastCanSticky = o);
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);
73
88
  return;
74
89
  }
75
- t.current.lastCanSticky = o, i > 0 && s.classList.toggle("can-sticky", !0);
76
- let C = c, p = d.bottom - (c + i);
77
- p > 0 && (p = 0);
78
- const H = r.current.map((y) => y.el.getBoundingClientRect().top);
79
- r.current.forEach((y, B) => {
80
- C += y.update(o, H[B], C + p, H[B + 1], B);
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);
81
96
  });
82
97
  }));
83
- }, []), a = w((s) => {
84
- const d = t.current.stickyItemsHeight + s;
85
- return t.current.stickyItemsHeight = d, setTimeout(() => {
86
- var c, i;
87
- (i = (c = t.current).onStickyItemsHeightChange) == null || i.call(c, d);
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);
88
103
  }, 0), () => {
89
- const c = t.current.stickyItemsHeight - s;
90
- t.current.stickyItemsHeight = c, setTimeout(() => {
91
- var i, o;
92
- (o = (i = t.current).onStickyItemsHeightChange) == null || o.call(i, c);
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);
93
108
  }, 0);
94
109
  };
95
110
  }, []);
96
111
  E(() => {
97
- t.current.fixedOffsetTop = f, t.current.defaultMode = e, setTimeout(() => {
112
+ t.current.fixedOffsetTop = y, t.current.defaultMode = e, setTimeout(() => {
98
113
  n();
99
114
  }, 10);
100
- }, [f, e, n]);
101
- const l = w((s) => (r.current.push(s), r.current = z(r.current), n(), () => {
102
- r.current = r.current.filter((d) => d !== s), n();
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();
103
118
  }), [n]);
104
119
  E(() => (window.addEventListener("scroll", n, { passive: !0 }), window.addEventListener("resize", n, { passive: !0 }), () => {
105
120
  window.removeEventListener("scroll", n), window.removeEventListener("resize", n);
106
121
  }), [n]);
107
- const v = N(() => A(g), [g]);
108
- return /* @__PURE__ */ R(
122
+ const B = _(() => D(h), [h]);
123
+ return /* @__PURE__ */ w(
109
124
  $.Provider,
110
125
  {
111
126
  value: {
112
- register: l,
113
- baseZIndex: v,
114
- updateStickyItemsHeight: a,
115
- fixedOffsetTop: f,
127
+ register: x,
128
+ baseZIndex: B,
129
+ updateStickyItemsHeight: H,
130
+ fixedOffsetTop: y,
116
131
  mode: e
117
132
  },
118
- children: /* @__PURE__ */ R(
133
+ children: /* @__PURE__ */ w(
119
134
  "div",
120
135
  {
121
136
  ...S,
122
- ref: I,
137
+ ref: m,
123
138
  className: "oe-sticky-container",
124
- children: u
139
+ children: f
125
140
  }
126
141
  )
127
142
  }
128
143
  );
129
144
  }
130
- function A(u) {
131
- return typeof u > "u" ? T : Math.max(Number(u) || 0, x);
145
+ function D(f) {
146
+ return typeof f > "u" ? A : Math.max(Number(f) || 0, O);
132
147
  }
133
- function z(u) {
134
- return u.sort((f, g) => {
135
- const k = f.el.getBoundingClientRect(), e = g.el.getBoundingClientRect();
136
- return k.top - e.top;
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;
137
152
  });
138
153
  }
139
154
  export {
140
- M as StickyContainer,
141
- F as StickyItem
155
+ P as StickyContainer,
156
+ M as StickyItem
142
157
  };
@@ -1 +1 @@
1
- (function(p,m){typeof exports=="object"&&typeof module<"u"?m(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],m):(p=typeof globalThis<"u"?globalThis:p||self,m(p.ReactSticky={},p.jsxRuntime,p.React))})(this,function(p,m,r){"use strict";const B=r.createContext(null);function x(){return r.useContext(B)}const $="";function Z({mode:o,children:l,className:h,...S}){const e=x(),C=r.useRef(null),E=r.useRef(null),[s,k]=r.useState(!1),t=r.useRef({fixedOffsetTop:(e==null?void 0:e.fixedOffsetTop)||0,isSticky:s,baseZIndex:(e==null?void 0:e.baseZIndex)||20});return t.current.baseZIndex=(e==null?void 0:e.baseZIndex)||20,t.current.fixedOffsetTop=(e==null?void 0:e.fixedOffsetTop)||0,t.current.isSticky=s,r.useEffect(()=>{const n=E.current,d=C.current,a=o||(e==null?void 0:e.mode);if(!e||!d||!n||a==="none"||!a||!e.register){k(!1);return}const c={el:d,update:(y,u,i,f,R)=>{if(!y||u>i)return t.current.isSticky&&k(!1),0;const I=n.getBoundingClientRect().height;let _=i;if(a==="replace"&&typeof f<"u"){const g=f-(i+I);if(g<0&&(_=i+g,g+I<0))return t.current.isSticky&&k(!1),0}return d.style.height=`${I}px`,t.current.isSticky||k(!0),n.style.top=`${_}px`,n.style.width=`${d.offsetWidth}px`,n.style.zIndex=`${t.current.baseZIndex+(a==="replace"?-R:R)}`,a==="replace"?0:I}};return e.register(c)},[e==null?void 0:e.mode,e==null?void 0:e.register,o]),r.useEffect(()=>{const n=C.current,d=E.current,a=e==null?void 0:e.updateStickyItemsHeight;if(!s||!n||!d||!a)return;const H=a(d.getBoundingClientRect().height);return()=>{N(n,["height"]),N(d,["top","z-index","width"]),H()}},[s,e==null?void 0:e.updateStickyItemsHeight]),m.jsx("div",{className:"oe-sticky-item "+(h||""),...S,ref:C,children:m.jsx("div",{className:"oe-sticky-content "+(s?"is-sticky":""),ref:E,children:l})})}function N(o,l){l.forEach(h=>{o.style.removeProperty(h)})}function b({children:o,offsetTop:l=0,baseZIndex:h,onStickyItemsHeightChange:S,defaultMode:e="replace",...C}){const E=r.useRef(null),s=r.useRef([]),k=r.useRef(null),t=r.useRef({fixedOffsetTop:l,defaultMode:e,stickyItemsHeight:0,onStickyItemsHeightChange:S,lastCanSticky:!1});t.current.onStickyItemsHeightChange=S;const n=r.useCallback(()=>{const c=E.current;!c||k.current||(k.current=requestAnimationFrame(()=>{k.current=null;const y=c.getBoundingClientRect(),{fixedOffsetTop:u,stickyItemsHeight:i}=t.current,f=!(y.top>u||y.bottom<u);if(!f){t.current.lastCanSticky!==f&&(c.classList.toggle("can-sticky",!1),s.current.forEach(g=>g.update(!1,0,0,0,0)),t.current.lastCanSticky=f);return}t.current.lastCanSticky=f,i>0&&c.classList.toggle("can-sticky",!0);let R=u,I=y.bottom-(u+i);I>0&&(I=0);const _=s.current.map(g=>g.el.getBoundingClientRect().top);s.current.forEach((g,v)=>{R+=g.update(f,_[v],R+I,_[v+1],v)})}))},[]),d=r.useCallback(c=>{const y=t.current.stickyItemsHeight+c;return t.current.stickyItemsHeight=y,setTimeout(()=>{var u,i;(i=(u=t.current).onStickyItemsHeightChange)==null||i.call(u,y)},0),()=>{const u=t.current.stickyItemsHeight-c;t.current.stickyItemsHeight=u,setTimeout(()=>{var i,f;(f=(i=t.current).onStickyItemsHeightChange)==null||f.call(i,u)},0)}},[]);r.useEffect(()=>{t.current.fixedOffsetTop=l,t.current.defaultMode=e,setTimeout(()=>{n()},10)},[l,e,n]);const a=r.useCallback(c=>(s.current.push(c),s.current=O(s.current),n(),()=>{s.current=s.current.filter(y=>y!==c),n()}),[n]);r.useEffect(()=>(window.addEventListener("scroll",n,{passive:!0}),window.addEventListener("resize",n,{passive:!0}),()=>{window.removeEventListener("scroll",n),window.removeEventListener("resize",n)}),[n]);const H=r.useMemo(()=>w(h),[h]);return m.jsx(B.Provider,{value:{register:a,baseZIndex:H,updateStickyItemsHeight:d,fixedOffsetTop:l,mode:e},children:m.jsx("div",{...C,ref:E,className:"oe-sticky-container",children:o})})}function w(o){return typeof o>"u"?200:Math.max(Number(o)||0,20)}function O(o){return o.sort((l,h)=>{const S=l.el.getBoundingClientRect(),e=h.el.getBoundingClientRect();return S.top-e.top})}p.StickyContainer=b,p.StickyItem=Z,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})});
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"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-sticky-kit",
3
- "version": "0.1.6",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "A lightweight, flexible React sticky container and item component library supporting multiple sticky modes, advanced layouts, and edge cases.",
6
6
  "keywords": [