@jump-section/core 1.0.18 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,23 +1,76 @@
1
1
  interface ScrollOptions {
2
2
  offset?: number;
3
3
  behavior?: ScrollBehavior;
4
+ /** URL hash와 활성 섹션을 자동으로 동기화합니다 */
5
+ hash?: boolean;
6
+ /** window 대신 커스텀 스크롤 컨테이너를 사용합니다 */
7
+ root?: HTMLElement | null;
8
+ /** Alt+ArrowDown/Up 키보드 네비게이션을 활성화합니다 */
9
+ keyboard?: boolean;
4
10
  }
5
- type ActiveChangeCallback = (id: string | null) => void;
11
+ interface ActiveChangeMeta {
12
+ /** 이전에 활성화된 섹션 ID */
13
+ previous: string | null;
14
+ /** 스크롤 방향 */
15
+ direction: 'up' | 'down' | null;
16
+ }
17
+ type ActiveChangeCallback = (id: string | null, meta: ActiveChangeMeta) => void;
18
+ type ProgressCallback = (progress: number) => void;
6
19
  declare class ScrollManager {
7
20
  private sections;
21
+ private disabledSections;
8
22
  private observer;
23
+ private resizeObserver;
9
24
  private activeId;
25
+ private previousId;
26
+ private lastScrollY;
27
+ private scrollDirection;
10
28
  private listeners;
29
+ private progressListeners;
11
30
  private options;
31
+ private keyboardHandler;
32
+ private scrollHandler;
33
+ private popstateHandler;
34
+ private notifyScheduled;
12
35
  constructor(options?: ScrollOptions);
36
+ private get scrollRoot();
37
+ private get currentScrollTop();
13
38
  private initObserver;
39
+ private initScrollListener;
40
+ private initHashSync;
41
+ private initKeyboard;
14
42
  private handleIntersection;
43
+ private scheduleNotify;
44
+ private updateProgress;
45
+ /** 섹션을 등록하고 IntersectionObserver로 감시합니다 */
15
46
  registerSection(id: string, element: HTMLElement): void;
47
+ /** 섹션 등록을 해제하고 감시를 중지합니다 */
16
48
  unregisterSection(id: string): void;
17
- scrollTo(id: string): void;
18
- onActiveChange(callback: ActiveChangeCallback): () => boolean;
49
+ /** 특정 섹션을 활성 감지에서 일시적으로 제외합니다 */
50
+ disableSection(id: string): void;
51
+ /** 비활성화된 섹션을 다시 활성 감지에 포함시킵니다 */
52
+ enableSection(id: string): void;
53
+ /** 실제 DOM 위치 기준으로 정렬된 섹션 ID 목록을 반환합니다 */
54
+ getSections(): string[];
55
+ /** 현재 활성 섹션 ID를 반환합니다 */
56
+ getActiveId(): string | null;
57
+ /** 지정한 섹션으로 스크롤합니다. 스크롤 완료 시 resolve되는 Promise를 반환합니다 */
58
+ scrollTo(id: string): Promise<void>;
59
+ /** 다음 섹션으로 스크롤합니다 */
60
+ scrollToNext(): Promise<void>;
61
+ /** 이전 섹션으로 스크롤합니다 */
62
+ scrollToPrev(): Promise<void>;
63
+ /** 첫 번째 섹션으로 스크롤합니다 */
64
+ scrollToFirst(): Promise<void>;
65
+ /** 마지막 섹션으로 스크롤합니다 */
66
+ scrollToLast(): Promise<void>;
67
+ /** 활성 섹션 변경 시 호출될 콜백을 등록합니다. 구독 해제 함수를 반환합니다 */
68
+ onActiveChange(callback: ActiveChangeCallback): () => void;
69
+ /** 특정 섹션의 스크롤 진행률(0~1) 변경 시 호출될 콜백을 등록합니다 */
70
+ onProgressChange(id: string, callback: ProgressCallback): () => void;
19
71
  private notifyListeners;
72
+ /** 모든 리소스를 정리합니다 */
20
73
  destroy(): void;
21
74
  }
22
75
 
23
- export { type ActiveChangeCallback, ScrollManager, type ScrollOptions };
76
+ export { type ActiveChangeCallback, type ActiveChangeMeta, type ProgressCallback, ScrollManager, type ScrollOptions };
package/dist/index.d.ts CHANGED
@@ -1,23 +1,76 @@
1
1
  interface ScrollOptions {
2
2
  offset?: number;
3
3
  behavior?: ScrollBehavior;
4
+ /** URL hash와 활성 섹션을 자동으로 동기화합니다 */
5
+ hash?: boolean;
6
+ /** window 대신 커스텀 스크롤 컨테이너를 사용합니다 */
7
+ root?: HTMLElement | null;
8
+ /** Alt+ArrowDown/Up 키보드 네비게이션을 활성화합니다 */
9
+ keyboard?: boolean;
4
10
  }
5
- type ActiveChangeCallback = (id: string | null) => void;
11
+ interface ActiveChangeMeta {
12
+ /** 이전에 활성화된 섹션 ID */
13
+ previous: string | null;
14
+ /** 스크롤 방향 */
15
+ direction: 'up' | 'down' | null;
16
+ }
17
+ type ActiveChangeCallback = (id: string | null, meta: ActiveChangeMeta) => void;
18
+ type ProgressCallback = (progress: number) => void;
6
19
  declare class ScrollManager {
7
20
  private sections;
21
+ private disabledSections;
8
22
  private observer;
23
+ private resizeObserver;
9
24
  private activeId;
25
+ private previousId;
26
+ private lastScrollY;
27
+ private scrollDirection;
10
28
  private listeners;
29
+ private progressListeners;
11
30
  private options;
31
+ private keyboardHandler;
32
+ private scrollHandler;
33
+ private popstateHandler;
34
+ private notifyScheduled;
12
35
  constructor(options?: ScrollOptions);
36
+ private get scrollRoot();
37
+ private get currentScrollTop();
13
38
  private initObserver;
39
+ private initScrollListener;
40
+ private initHashSync;
41
+ private initKeyboard;
14
42
  private handleIntersection;
43
+ private scheduleNotify;
44
+ private updateProgress;
45
+ /** 섹션을 등록하고 IntersectionObserver로 감시합니다 */
15
46
  registerSection(id: string, element: HTMLElement): void;
47
+ /** 섹션 등록을 해제하고 감시를 중지합니다 */
16
48
  unregisterSection(id: string): void;
17
- scrollTo(id: string): void;
18
- onActiveChange(callback: ActiveChangeCallback): () => boolean;
49
+ /** 특정 섹션을 활성 감지에서 일시적으로 제외합니다 */
50
+ disableSection(id: string): void;
51
+ /** 비활성화된 섹션을 다시 활성 감지에 포함시킵니다 */
52
+ enableSection(id: string): void;
53
+ /** 실제 DOM 위치 기준으로 정렬된 섹션 ID 목록을 반환합니다 */
54
+ getSections(): string[];
55
+ /** 현재 활성 섹션 ID를 반환합니다 */
56
+ getActiveId(): string | null;
57
+ /** 지정한 섹션으로 스크롤합니다. 스크롤 완료 시 resolve되는 Promise를 반환합니다 */
58
+ scrollTo(id: string): Promise<void>;
59
+ /** 다음 섹션으로 스크롤합니다 */
60
+ scrollToNext(): Promise<void>;
61
+ /** 이전 섹션으로 스크롤합니다 */
62
+ scrollToPrev(): Promise<void>;
63
+ /** 첫 번째 섹션으로 스크롤합니다 */
64
+ scrollToFirst(): Promise<void>;
65
+ /** 마지막 섹션으로 스크롤합니다 */
66
+ scrollToLast(): Promise<void>;
67
+ /** 활성 섹션 변경 시 호출될 콜백을 등록합니다. 구독 해제 함수를 반환합니다 */
68
+ onActiveChange(callback: ActiveChangeCallback): () => void;
69
+ /** 특정 섹션의 스크롤 진행률(0~1) 변경 시 호출될 콜백을 등록합니다 */
70
+ onProgressChange(id: string, callback: ProgressCallback): () => void;
19
71
  private notifyListeners;
72
+ /** 모든 리소스를 정리합니다 */
20
73
  destroy(): void;
21
74
  }
22
75
 
23
- export { type ActiveChangeCallback, ScrollManager, type ScrollOptions };
76
+ export { type ActiveChangeCallback, type ActiveChangeMeta, type ProgressCallback, ScrollManager, type ScrollOptions };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var d=Object.defineProperty;var h=Object.getOwnPropertySymbols;var p=Object.prototype.hasOwnProperty,b=Object.prototype.propertyIsEnumerable;var c=(i,e,t)=>e in i?d(i,e,{enumerable:true,configurable:true,writable:true,value:t}):i[e]=t,a=(i,e)=>{for(var t in e||(e={}))p.call(e,t)&&c(i,t,e[t]);if(h)for(var t of h(e))b.call(e,t)&&c(i,t,e[t]);return i};var s=(i,e,t)=>c(i,typeof e!="symbol"?e+"":e,t);var v=class{constructor(e={}){s(this,"sections",new Map);s(this,"observer",null);s(this,"activeId",null);s(this,"listeners",new Set);s(this,"options");s(this,"handleIntersection",e=>{let t=e.filter(r=>r.isIntersecting);if(t.length>0){let n=t.reduce((o,l)=>o.intersectionRatio>l.intersectionRatio?o:l).target.id;n&&n!==this.activeId&&(this.activeId=n,this.notifyListeners());}});this.options=a({offset:0,behavior:"smooth"},e),this.initObserver();}initObserver(){typeof window!="undefined"&&(this.observer=new IntersectionObserver(this.handleIntersection,{rootMargin:"-20% 0px -60% 0px",threshold:[0,.1,.5,1]}));}registerSection(e,t){typeof window!="undefined"&&t&&(this.sections.set(e,t),this.observer||(this.observer=new IntersectionObserver(this.handleIntersection,{rootMargin:"-10% 0px -80% 0px",threshold:0})),t.id||(t.id=e),this.observer.observe(t));}unregisterSection(e){let t=this.sections.get(e);t&&this.observer&&this.observer.unobserve(t),this.sections.delete(e);}scrollTo(e){if(typeof window=="undefined")return;let t=this.sections.get(e);if(!t){console.warn(`[ScrollManager] Section with id "${e}" not found. Available sections: ${Array.from(this.sections.keys()).join(", ")}`);return}let{offset:r,behavior:n}=this.options,o=t.getBoundingClientRect().top+window.scrollY;window.scrollTo({top:o+(r||0),behavior:n||"smooth"});}onActiveChange(e){return this.listeners.add(e),e(this.activeId),()=>this.listeners.delete(e)}notifyListeners(){this.listeners.forEach(e=>e(this.activeId));}destroy(){this.observer&&this.observer.disconnect(),this.sections.clear(),this.listeners.clear();}};exports.ScrollManager=v;
1
+ 'use strict';var b=Object.defineProperty;var p=Object.getOwnPropertySymbols;var w=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable;var d=(l,e,t)=>e in l?b(l,e,{enumerable:true,configurable:true,writable:true,value:t}):l[e]=t,u=(l,e)=>{for(var t in e||(e={}))w.call(e,t)&&d(l,t,e[t]);if(p)for(var t of p(e))S.call(e,t)&&d(l,t,e[t]);return l};var o=(l,e,t)=>d(l,typeof e!="symbol"?e+"":e,t);var v=class{constructor(e={}){o(this,"sections",new Map);o(this,"disabledSections",new Set);o(this,"observer",null);o(this,"resizeObserver",null);o(this,"activeId",null);o(this,"previousId",null);o(this,"lastScrollY",0);o(this,"scrollDirection",null);o(this,"listeners",new Set);o(this,"progressListeners",new Map);o(this,"options");o(this,"keyboardHandler",null);o(this,"scrollHandler",null);o(this,"popstateHandler",null);o(this,"notifyScheduled",false);o(this,"handleIntersection",e=>{var c;let t=e.filter(n=>n.isIntersecting);if(t.length===0)return;let i=t.reduce((n,a)=>n.intersectionRatio>a.intersectionRatio?n:a),s=[...this.sections.entries()].find(([,n])=>n===i.target),r=(c=s==null?void 0:s[0])!=null?c:i.target.id;!r||r===this.activeId||this.disabledSections.has(r)||(this.previousId=this.activeId,this.activeId=r,this.scheduleNotify(),this.options.hash&&history.replaceState(null,"",`#${r}`));});this.options=u({offset:0,behavior:"smooth",hash:false,root:null,keyboard:false},e),this.initObserver(),this.initScrollListener(),this.options.hash&&this.initHashSync(),this.options.keyboard&&this.initKeyboard();}get scrollRoot(){var e;return (e=this.options.root)!=null?e:window}get currentScrollTop(){return this.options.root?this.options.root.scrollTop:window.scrollY}initObserver(){var e;typeof window!="undefined"&&(this.observer=new IntersectionObserver(this.handleIntersection,{root:(e=this.options.root)!=null?e:null,rootMargin:"-20% 0px -60% 0px",threshold:[0,.1,.5,1]}),typeof ResizeObserver!="undefined"&&(this.resizeObserver=new ResizeObserver(()=>{this.sections.forEach(t=>{var i,s;(i=this.observer)==null||i.unobserve(t),(s=this.observer)==null||s.observe(t);});})));}initScrollListener(){typeof window!="undefined"&&(this.scrollHandler=()=>{let e=this.currentScrollTop;e!==this.lastScrollY&&(this.scrollDirection=e>this.lastScrollY?"down":"up",this.lastScrollY=e),this.updateProgress();},this.scrollRoot.addEventListener("scroll",this.scrollHandler,{passive:true}));}initHashSync(){typeof window!="undefined"&&(this.popstateHandler=()=>{let e=window.location.hash.slice(1);e&&this.sections.has(e)&&this.scrollTo(e);},window.addEventListener("popstate",this.popstateHandler));}initKeyboard(){typeof window!="undefined"&&(this.keyboardHandler=e=>{e.altKey&&(e.key==="ArrowDown"?(e.preventDefault(),this.scrollToNext()):e.key==="ArrowUp"&&(e.preventDefault(),this.scrollToPrev()));},document.addEventListener("keydown",this.keyboardHandler));}scheduleNotify(){this.notifyScheduled||(this.notifyScheduled=true,queueMicrotask(()=>{this.notifyScheduled=false,this.notifyListeners();}));}updateProgress(){if(this.progressListeners.size===0)return;let e=this.currentScrollTop,t=this.options.root?this.options.root.clientHeight:window.innerHeight;this.progressListeners.forEach((i,s)=>{if(i.size===0)return;let r=this.sections.get(s);if(!r)return;let c=r.getBoundingClientRect(),n=this.options.root?c.top-this.options.root.getBoundingClientRect().top+this.options.root.scrollTop:c.top+e,a=r.offsetHeight||c.height;if(a===0)return;let h=(e-n+t*.2)/a,f=Math.max(0,Math.min(1,h));i.forEach(g=>g(f));});}registerSection(e,t){var i,s;typeof window!="undefined"&&t&&(this.sections.set(e,t),t.id||(t.id=e),(i=this.observer)==null||i.observe(t),(s=this.resizeObserver)==null||s.observe(t),this.options.hash&&window.location.hash===`#${e}`&&setTimeout(()=>this.scrollTo(e),0));}unregisterSection(e){var i,s;let t=this.sections.get(e);t&&((i=this.observer)==null||i.unobserve(t),(s=this.resizeObserver)==null||s.unobserve(t)),this.sections.delete(e),this.disabledSections.delete(e),this.progressListeners.delete(e),this.activeId===e&&(this.previousId=this.activeId,this.activeId=null,this.scheduleNotify());}disableSection(e){this.disabledSections.add(e),this.activeId===e&&(this.previousId=this.activeId,this.activeId=null,this.scheduleNotify());}enableSection(e){this.disabledSections.delete(e);}getSections(){return [...this.sections.entries()].filter(([e])=>!this.disabledSections.has(e)).sort(([,e],[,t])=>{let i=e.getBoundingClientRect().top,s=t.getBoundingClientRect().top;return i-s}).map(([e])=>e)}getActiveId(){return this.activeId}scrollTo(e){if(typeof window=="undefined")return Promise.resolve();let t=this.sections.get(e);if(!t)return console.warn(`[ScrollManager] Section with id "${e}" not found. Available sections: ${Array.from(this.sections.keys()).join(", ")}`),Promise.resolve();let{offset:i,behavior:s}=this.options;if(this.options.root){let r=t.offsetTop;this.options.root.scrollTo({top:r+i,behavior:s});}else {let r=t.getBoundingClientRect().top+window.scrollY;window.scrollTo({top:r+i,behavior:s});}return new Promise(r=>{if(s==="auto"||s==="instant"){r();return}let c=this.currentScrollTop,n,a=()=>{let h=this.currentScrollTop;if(h===c){cancelAnimationFrame(n),r();return}c=h,n=requestAnimationFrame(a);};n=requestAnimationFrame(a),setTimeout(()=>{cancelAnimationFrame(n),r();},1e3);})}scrollToNext(){let e=this.getSections(),t=this.activeId?e.indexOf(this.activeId):-1,i=e[t+1];return i?this.scrollTo(i):Promise.resolve()}scrollToPrev(){let e=this.getSections(),t=this.activeId?e.indexOf(this.activeId):e.length,i=e[t-1];return i?this.scrollTo(i):Promise.resolve()}scrollToFirst(){let e=this.getSections();return e.length>0?this.scrollTo(e[0]):Promise.resolve()}scrollToLast(){let e=this.getSections();return e.length>0?this.scrollTo(e[e.length-1]):Promise.resolve()}onActiveChange(e){return this.listeners.add(e),e(this.activeId,{previous:this.previousId,direction:this.scrollDirection}),()=>this.listeners.delete(e)}onProgressChange(e,t){return this.progressListeners.has(e)||this.progressListeners.set(e,new Set),this.progressListeners.get(e).add(t),()=>{let i=this.progressListeners.get(e);i&&(i.delete(t),i.size===0&&this.progressListeners.delete(e));}}notifyListeners(){let e={previous:this.previousId,direction:this.scrollDirection};this.listeners.forEach(t=>t(this.activeId,e));}destroy(){var e,t;(e=this.observer)==null||e.disconnect(),(t=this.resizeObserver)==null||t.disconnect(),this.scrollHandler&&(this.scrollRoot.removeEventListener("scroll",this.scrollHandler),this.scrollHandler=null),this.keyboardHandler&&(document.removeEventListener("keydown",this.keyboardHandler),this.keyboardHandler=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.sections.clear(),this.listeners.clear(),this.progressListeners.clear(),this.disabledSections.clear();}};exports.ScrollManager=v;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var d=Object.defineProperty;var h=Object.getOwnPropertySymbols;var p=Object.prototype.hasOwnProperty,b=Object.prototype.propertyIsEnumerable;var c=(i,e,t)=>e in i?d(i,e,{enumerable:true,configurable:true,writable:true,value:t}):i[e]=t,a=(i,e)=>{for(var t in e||(e={}))p.call(e,t)&&c(i,t,e[t]);if(h)for(var t of h(e))b.call(e,t)&&c(i,t,e[t]);return i};var s=(i,e,t)=>c(i,typeof e!="symbol"?e+"":e,t);var v=class{constructor(e={}){s(this,"sections",new Map);s(this,"observer",null);s(this,"activeId",null);s(this,"listeners",new Set);s(this,"options");s(this,"handleIntersection",e=>{let t=e.filter(r=>r.isIntersecting);if(t.length>0){let n=t.reduce((o,l)=>o.intersectionRatio>l.intersectionRatio?o:l).target.id;n&&n!==this.activeId&&(this.activeId=n,this.notifyListeners());}});this.options=a({offset:0,behavior:"smooth"},e),this.initObserver();}initObserver(){typeof window!="undefined"&&(this.observer=new IntersectionObserver(this.handleIntersection,{rootMargin:"-20% 0px -60% 0px",threshold:[0,.1,.5,1]}));}registerSection(e,t){typeof window!="undefined"&&t&&(this.sections.set(e,t),this.observer||(this.observer=new IntersectionObserver(this.handleIntersection,{rootMargin:"-10% 0px -80% 0px",threshold:0})),t.id||(t.id=e),this.observer.observe(t));}unregisterSection(e){let t=this.sections.get(e);t&&this.observer&&this.observer.unobserve(t),this.sections.delete(e);}scrollTo(e){if(typeof window=="undefined")return;let t=this.sections.get(e);if(!t){console.warn(`[ScrollManager] Section with id "${e}" not found. Available sections: ${Array.from(this.sections.keys()).join(", ")}`);return}let{offset:r,behavior:n}=this.options,o=t.getBoundingClientRect().top+window.scrollY;window.scrollTo({top:o+(r||0),behavior:n||"smooth"});}onActiveChange(e){return this.listeners.add(e),e(this.activeId),()=>this.listeners.delete(e)}notifyListeners(){this.listeners.forEach(e=>e(this.activeId));}destroy(){this.observer&&this.observer.disconnect(),this.sections.clear(),this.listeners.clear();}};export{v as ScrollManager};
1
+ var b=Object.defineProperty;var p=Object.getOwnPropertySymbols;var w=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable;var d=(l,e,t)=>e in l?b(l,e,{enumerable:true,configurable:true,writable:true,value:t}):l[e]=t,u=(l,e)=>{for(var t in e||(e={}))w.call(e,t)&&d(l,t,e[t]);if(p)for(var t of p(e))S.call(e,t)&&d(l,t,e[t]);return l};var o=(l,e,t)=>d(l,typeof e!="symbol"?e+"":e,t);var v=class{constructor(e={}){o(this,"sections",new Map);o(this,"disabledSections",new Set);o(this,"observer",null);o(this,"resizeObserver",null);o(this,"activeId",null);o(this,"previousId",null);o(this,"lastScrollY",0);o(this,"scrollDirection",null);o(this,"listeners",new Set);o(this,"progressListeners",new Map);o(this,"options");o(this,"keyboardHandler",null);o(this,"scrollHandler",null);o(this,"popstateHandler",null);o(this,"notifyScheduled",false);o(this,"handleIntersection",e=>{var c;let t=e.filter(n=>n.isIntersecting);if(t.length===0)return;let i=t.reduce((n,a)=>n.intersectionRatio>a.intersectionRatio?n:a),s=[...this.sections.entries()].find(([,n])=>n===i.target),r=(c=s==null?void 0:s[0])!=null?c:i.target.id;!r||r===this.activeId||this.disabledSections.has(r)||(this.previousId=this.activeId,this.activeId=r,this.scheduleNotify(),this.options.hash&&history.replaceState(null,"",`#${r}`));});this.options=u({offset:0,behavior:"smooth",hash:false,root:null,keyboard:false},e),this.initObserver(),this.initScrollListener(),this.options.hash&&this.initHashSync(),this.options.keyboard&&this.initKeyboard();}get scrollRoot(){var e;return (e=this.options.root)!=null?e:window}get currentScrollTop(){return this.options.root?this.options.root.scrollTop:window.scrollY}initObserver(){var e;typeof window!="undefined"&&(this.observer=new IntersectionObserver(this.handleIntersection,{root:(e=this.options.root)!=null?e:null,rootMargin:"-20% 0px -60% 0px",threshold:[0,.1,.5,1]}),typeof ResizeObserver!="undefined"&&(this.resizeObserver=new ResizeObserver(()=>{this.sections.forEach(t=>{var i,s;(i=this.observer)==null||i.unobserve(t),(s=this.observer)==null||s.observe(t);});})));}initScrollListener(){typeof window!="undefined"&&(this.scrollHandler=()=>{let e=this.currentScrollTop;e!==this.lastScrollY&&(this.scrollDirection=e>this.lastScrollY?"down":"up",this.lastScrollY=e),this.updateProgress();},this.scrollRoot.addEventListener("scroll",this.scrollHandler,{passive:true}));}initHashSync(){typeof window!="undefined"&&(this.popstateHandler=()=>{let e=window.location.hash.slice(1);e&&this.sections.has(e)&&this.scrollTo(e);},window.addEventListener("popstate",this.popstateHandler));}initKeyboard(){typeof window!="undefined"&&(this.keyboardHandler=e=>{e.altKey&&(e.key==="ArrowDown"?(e.preventDefault(),this.scrollToNext()):e.key==="ArrowUp"&&(e.preventDefault(),this.scrollToPrev()));},document.addEventListener("keydown",this.keyboardHandler));}scheduleNotify(){this.notifyScheduled||(this.notifyScheduled=true,queueMicrotask(()=>{this.notifyScheduled=false,this.notifyListeners();}));}updateProgress(){if(this.progressListeners.size===0)return;let e=this.currentScrollTop,t=this.options.root?this.options.root.clientHeight:window.innerHeight;this.progressListeners.forEach((i,s)=>{if(i.size===0)return;let r=this.sections.get(s);if(!r)return;let c=r.getBoundingClientRect(),n=this.options.root?c.top-this.options.root.getBoundingClientRect().top+this.options.root.scrollTop:c.top+e,a=r.offsetHeight||c.height;if(a===0)return;let h=(e-n+t*.2)/a,f=Math.max(0,Math.min(1,h));i.forEach(g=>g(f));});}registerSection(e,t){var i,s;typeof window!="undefined"&&t&&(this.sections.set(e,t),t.id||(t.id=e),(i=this.observer)==null||i.observe(t),(s=this.resizeObserver)==null||s.observe(t),this.options.hash&&window.location.hash===`#${e}`&&setTimeout(()=>this.scrollTo(e),0));}unregisterSection(e){var i,s;let t=this.sections.get(e);t&&((i=this.observer)==null||i.unobserve(t),(s=this.resizeObserver)==null||s.unobserve(t)),this.sections.delete(e),this.disabledSections.delete(e),this.progressListeners.delete(e),this.activeId===e&&(this.previousId=this.activeId,this.activeId=null,this.scheduleNotify());}disableSection(e){this.disabledSections.add(e),this.activeId===e&&(this.previousId=this.activeId,this.activeId=null,this.scheduleNotify());}enableSection(e){this.disabledSections.delete(e);}getSections(){return [...this.sections.entries()].filter(([e])=>!this.disabledSections.has(e)).sort(([,e],[,t])=>{let i=e.getBoundingClientRect().top,s=t.getBoundingClientRect().top;return i-s}).map(([e])=>e)}getActiveId(){return this.activeId}scrollTo(e){if(typeof window=="undefined")return Promise.resolve();let t=this.sections.get(e);if(!t)return console.warn(`[ScrollManager] Section with id "${e}" not found. Available sections: ${Array.from(this.sections.keys()).join(", ")}`),Promise.resolve();let{offset:i,behavior:s}=this.options;if(this.options.root){let r=t.offsetTop;this.options.root.scrollTo({top:r+i,behavior:s});}else {let r=t.getBoundingClientRect().top+window.scrollY;window.scrollTo({top:r+i,behavior:s});}return new Promise(r=>{if(s==="auto"||s==="instant"){r();return}let c=this.currentScrollTop,n,a=()=>{let h=this.currentScrollTop;if(h===c){cancelAnimationFrame(n),r();return}c=h,n=requestAnimationFrame(a);};n=requestAnimationFrame(a),setTimeout(()=>{cancelAnimationFrame(n),r();},1e3);})}scrollToNext(){let e=this.getSections(),t=this.activeId?e.indexOf(this.activeId):-1,i=e[t+1];return i?this.scrollTo(i):Promise.resolve()}scrollToPrev(){let e=this.getSections(),t=this.activeId?e.indexOf(this.activeId):e.length,i=e[t-1];return i?this.scrollTo(i):Promise.resolve()}scrollToFirst(){let e=this.getSections();return e.length>0?this.scrollTo(e[0]):Promise.resolve()}scrollToLast(){let e=this.getSections();return e.length>0?this.scrollTo(e[e.length-1]):Promise.resolve()}onActiveChange(e){return this.listeners.add(e),e(this.activeId,{previous:this.previousId,direction:this.scrollDirection}),()=>this.listeners.delete(e)}onProgressChange(e,t){return this.progressListeners.has(e)||this.progressListeners.set(e,new Set),this.progressListeners.get(e).add(t),()=>{let i=this.progressListeners.get(e);i&&(i.delete(t),i.size===0&&this.progressListeners.delete(e));}}notifyListeners(){let e={previous:this.previousId,direction:this.scrollDirection};this.listeners.forEach(t=>t(this.activeId,e));}destroy(){var e,t;(e=this.observer)==null||e.disconnect(),(t=this.resizeObserver)==null||t.disconnect(),this.scrollHandler&&(this.scrollRoot.removeEventListener("scroll",this.scrollHandler),this.scrollHandler=null),this.keyboardHandler&&(document.removeEventListener("keydown",this.keyboardHandler),this.keyboardHandler=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.sections.clear(),this.listeners.clear(),this.progressListeners.clear(),this.disabledSections.clear();}};export{v as ScrollManager};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jump-section/core",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Core scroll management library for jump-section",
5
5
  "keywords": [
6
6
  "scroll",