@wc-lib/infinite-scroll-list 1.0.0 → 1.1.0

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.
@@ -10,14 +10,13 @@
10
10
  * 6. 自适应父元素大小变化
11
11
  * 7. 组件只在has-next-page为true时触发end-reached事件
12
12
  */
13
- declare const style = "\n:host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.relative {\n position: relative;\n}\n\n.absolute {\n position: absolute;\n}\n\n.bottom-0 {\n bottom: 0px;\n}\n\n.z-\\[-1\\] {\n z-index: -1;\n}\n\n.w-full {\n width: 100%;\n}\n\n.hidden {\n display: none;\n}\n\n.contents {\n display: contents;\n}\n";
13
+ declare const style = "\n:host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.bottom-ref {\n width: 0;\n height: 0;\n pointer-events: none;\n}\n\n.w-full {\n width: 100%;\n}\n\n.hidden {\n display: none;\n}\n\n.contents {\n display: contents;\n}\n";
14
14
  declare class InfiniteScrollList extends HTMLElement {
15
15
  private _observerRef;
16
16
  private _resizeObserverRef;
17
17
  private _bottomRef;
18
18
  private _onEndReachedThreshold;
19
19
  private _hasNextPage;
20
- private _container;
21
20
  constructor();
22
21
  static get observedAttributes(): string[];
23
22
  attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
@@ -1,2 +1,2 @@
1
- !function(){"use strict";class e extends HTMLElement{constructor(){super(),this._observerRef=null,this._resizeObserverRef=null,this._bottomRef=null,this._onEndReachedThreshold=0,this._hasNextPage=!1,this._container=null,this.attachShadow({mode:"open"}),this._intersectionCallback=this._intersectionCallback.bind(this),this._resizeCallback=this._resizeCallback.bind(this)}static get observedAttributes(){return["on-end-reached-threshold","has-next-page"]}attributeChangedCallback(e,t,s){t!==s&&("on-end-reached-threshold"===e?(this._onEndReachedThreshold=Number(s)||0,this._bottomRef&&(this._bottomRef.style.height=`${this._onEndReachedThreshold}px`)):"has-next-page"===e&&(this._hasNextPage=null!==s&&"false"!==s,this._updateSlotVisibility()))}connectedCallback(){this._onEndReachedThreshold=Number(this.getAttribute("on-end-reached-threshold"))||0,this._hasNextPage="false"!==this.getAttribute("has-next-page"),this._render(),this._observerRef=new IntersectionObserver(this._intersectionCallback,{threshold:0,root:this._findScrollContainer()}),this._bottomRef&&this._observerRef.observe(this._bottomRef),this._resizeObserverRef=new ResizeObserver(this._resizeCallback),this.parentElement&&this._resizeObserverRef.observe(this.parentElement)}disconnectedCallback(){this._observerRef&&(this._observerRef.disconnect(),this._observerRef=null),this._resizeObserverRef&&(this._resizeObserverRef.disconnect(),this._resizeObserverRef=null)}_findScrollContainer(){let e=this.parentElement;for(;e;){const t=window.getComputedStyle(e).overflowY;if("auto"===t||"scroll"===t)return e;e=e.parentElement}return null}_intersectionCallback(e){const[t]=e;t.isIntersecting&&this._hasNextPage&&this.dispatchEvent(new CustomEvent("end-reached",{bubbles:!0,composed:!0}))}_resizeCallback(e){this._observerRef&&(this._observerRef.disconnect(),this._observerRef=new IntersectionObserver(this._intersectionCallback,{threshold:0,root:this._findScrollContainer()}),this._bottomRef&&this._observerRef.observe(this._bottomRef))}_updateSlotVisibility(){const e=this.shadowRoot?.querySelector(".loading-slot"),t=this.shadowRoot?.querySelector(".no-data-slot");e&&t&&(e.className=this._hasNextPage?"loading-slot contents":"loading-slot hidden",t.className=this._hasNextPage?"no-data-slot hidden":"no-data-slot contents")}_render(){if(!this.shadowRoot)return;const e=document.createElement("style");e.textContent="\n:host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.relative {\n position: relative;\n}\n\n.absolute {\n position: absolute;\n}\n\n.bottom-0 {\n bottom: 0px;\n}\n\n.z-\\[-1\\] {\n z-index: -1;\n}\n\n.w-full {\n width: 100%;\n}\n\n.hidden {\n display: none;\n}\n\n.contents {\n display: contents;\n}\n",this.shadowRoot.appendChild(e),this._container=document.createElement("div"),this._container.setAttribute("part","container"),this._container.className="relative",this._bottomRef=document.createElement("div"),this._bottomRef.className="absolute bottom-0 z-[-1]",this._bottomRef.style.height=`${this._onEndReachedThreshold}px`;const t=document.createElement("slot"),s=document.createElement("div");s.className=this._hasNextPage?"loading-slot contents":"loading-slot hidden";const n=document.createElement("slot");n.setAttribute("name","loading"),s.appendChild(n);const i=document.createElement("div");i.className=this._hasNextPage?"no-data-slot hidden":"no-data-slot contents";const o=document.createElement("slot");o.setAttribute("name","no-data"),i.appendChild(o),this._container.appendChild(this._bottomRef),this._container.appendChild(t),this._container.appendChild(s),this._container.appendChild(i),this.shadowRoot.appendChild(this._container)}}customElements.define("infinite-scroll-list",e)}();
1
+ !function(){"use strict";class e extends HTMLElement{constructor(){super(),this._observerRef=null,this._resizeObserverRef=null,this._bottomRef=null,this._onEndReachedThreshold=0,this._hasNextPage=!1,this.attachShadow({mode:"open"}),this._intersectionCallback=this._intersectionCallback.bind(this),this._resizeCallback=this._resizeCallback.bind(this)}static get observedAttributes(){return["on-end-reached-threshold","has-next-page"]}attributeChangedCallback(e,t,s){t!==s&&("on-end-reached-threshold"===e?(this._onEndReachedThreshold=Number(s)||0,this._bottomRef&&(this._bottomRef.style.marginTop=-this._onEndReachedThreshold+"px")):"has-next-page"===e&&(this._hasNextPage=null!==s&&"false"!==s,this._updateSlotVisibility()))}connectedCallback(){this._onEndReachedThreshold=Number(this.getAttribute("on-end-reached-threshold"))||0,this._hasNextPage="false"!==this.getAttribute("has-next-page"),this._render(),this._observerRef=new IntersectionObserver(this._intersectionCallback,{threshold:0,root:this._findScrollContainer()}),this._bottomRef&&this._observerRef.observe(this._bottomRef),this._resizeObserverRef=new ResizeObserver(this._resizeCallback),this.parentElement&&this._resizeObserverRef.observe(this.parentElement)}disconnectedCallback(){this._observerRef&&(this._observerRef.disconnect(),this._observerRef=null),this._resizeObserverRef&&(this._resizeObserverRef.disconnect(),this._resizeObserverRef=null)}_findScrollContainer(){let e=this.parentElement;for(;e;){const t=window.getComputedStyle(e).overflowY;if("auto"===t||"scroll"===t)return e;e=e.parentElement}return null}_intersectionCallback(e){const[t]=e;t.isIntersecting&&this._hasNextPage&&this.dispatchEvent(new CustomEvent("end-reached",{bubbles:!0,composed:!0}))}_resizeCallback(e){this._observerRef&&(this._observerRef.disconnect(),this._observerRef=new IntersectionObserver(this._intersectionCallback,{threshold:0,root:this._findScrollContainer()}),this._bottomRef&&this._observerRef.observe(this._bottomRef))}_updateSlotVisibility(){const e=this.shadowRoot?.querySelector(".loading-slot"),t=this.shadowRoot?.querySelector(".no-data-slot");e&&t&&(e.className=this._hasNextPage?"loading-slot contents":"loading-slot hidden",t.className=this._hasNextPage?"no-data-slot hidden":"no-data-slot contents")}_render(){if(!this.shadowRoot)return;const e=document.createElement("style");e.textContent="\n:host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.bottom-ref {\n width: 0;\n height: 0;\n pointer-events: none;\n}\n\n.w-full {\n width: 100%;\n}\n\n.hidden {\n display: none;\n}\n\n.contents {\n display: contents;\n}\n",this.shadowRoot.appendChild(e);const t=document.createElement("slot");this._bottomRef=document.createElement("div"),this._bottomRef.className="bottom-ref",this._bottomRef.style.marginTop=-this._onEndReachedThreshold+"px";const s=document.createElement("div");s.className=this._hasNextPage?"loading-slot contents":"loading-slot hidden";const n=document.createElement("slot");n.setAttribute("name","loading"),s.appendChild(n);const o=document.createElement("div");o.className=this._hasNextPage?"no-data-slot hidden":"no-data-slot contents";const i=document.createElement("slot");i.setAttribute("name","no-data"),o.appendChild(i),this.shadowRoot.appendChild(t),this.shadowRoot.appendChild(this._bottomRef),this.shadowRoot.appendChild(s),this.shadowRoot.appendChild(o)}}customElements.define("infinite-scroll-list",e)}();
2
2
  //# sourceMappingURL=infinite-scroll-list.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"infinite-scroll-list.min.js","sources":["../src/infinite-scroll-list.ts"],"sourcesContent":["/**\n * 无限滚动加载组件\n * \n * 功能:\n * 1. 支持传入距离底部的距离参数(on-end-reached-threshold)\n * 2. 支持外部传入是否还有下一页(has-next-page)\n * 3. 支持两个slot:loading和no-data\n * 4. 根据是否有下一页显示对应的slot内容\n * 5. 支持在body中使用,也支持在父或祖先元素overflow-y: auto的元素中使用\n * 6. 自适应父元素大小变化\n * 7. 组件只在has-next-page为true时触发end-reached事件\n */\n\n// 定义组件的样式\nconst style = `\n:host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.relative {\n position: relative;\n}\n\n.absolute {\n position: absolute;\n}\n\n.bottom-0 {\n bottom: 0px;\n}\n\n.z-\\\\[-1\\\\] {\n z-index: -1;\n}\n\n.w-full {\n width: 100%;\n}\n\n.hidden {\n display: none;\n}\n\n.contents {\n display: contents;\n}\n`;\n\nclass InfiniteScrollList extends HTMLElement {\n // 私有属性\n private _observerRef: IntersectionObserver | null = null;\n private _resizeObserverRef: ResizeObserver | null = null;\n private _bottomRef: HTMLDivElement | null = null;\n private _onEndReachedThreshold: number = 0;\n private _hasNextPage: boolean = false;\n private _container: HTMLDivElement | null = null;\n\n constructor() {\n super();\n \n // 创建 Shadow DOM\n this.attachShadow({ mode: 'open' });\n \n // 绑定回调函数,避免this指向问题\n this._intersectionCallback = this._intersectionCallback.bind(this);\n this._resizeCallback = this._resizeCallback.bind(this);\n }\n\n // 定义观察的属性\n static get observedAttributes(): string[] {\n return ['on-end-reached-threshold', 'has-next-page'];\n }\n\n // 属性变化时的回调\n attributeChangedCallback(name: string, oldValue: string, newValue: string): void {\n if (oldValue === newValue) return;\n \n if (name === 'on-end-reached-threshold') {\n this._onEndReachedThreshold = Number(newValue) || 0;\n if (this._bottomRef) {\n this._bottomRef.style.height = `${this._onEndReachedThreshold}px`;\n }\n } else if (name === 'has-next-page') {\n this._hasNextPage = newValue !== null && newValue !== 'false';\n this._updateSlotVisibility();\n }\n }\n\n // 组件连接到 DOM 时\n connectedCallback(): void {\n // 初始化属性默认值\n this._onEndReachedThreshold = Number(this.getAttribute('on-end-reached-threshold')) || 0;\n this._hasNextPage = this.getAttribute('has-next-page') !== 'false';\n \n // 渲染组件\n this._render();\n \n // 初始化 IntersectionObserver\n this._observerRef = new IntersectionObserver(this._intersectionCallback, {\n threshold: 0.0,\n root: this._findScrollContainer()\n });\n \n if (this._bottomRef) {\n this._observerRef.observe(this._bottomRef);\n }\n\n // 初始化 ResizeObserver 监听父元素大小变化\n this._resizeObserverRef = new ResizeObserver(this._resizeCallback);\n if (this.parentElement) {\n this._resizeObserverRef.observe(this.parentElement);\n }\n }\n\n // 组件从 DOM 断开连接时\n disconnectedCallback(): void {\n // 清理 IntersectionObserver\n if (this._observerRef) {\n this._observerRef.disconnect();\n this._observerRef = null;\n }\n\n // 清理 ResizeObserver\n if (this._resizeObserverRef) {\n this._resizeObserverRef.disconnect();\n this._resizeObserverRef = null;\n }\n }\n\n // 查找滚动容器\n private _findScrollContainer(): Element | null {\n let parent = this.parentElement;\n \n while (parent) {\n const overflowY = window.getComputedStyle(parent).overflowY;\n if (overflowY === 'auto' || overflowY === 'scroll') {\n return parent;\n }\n parent = parent.parentElement;\n }\n \n return null; // 如果没有找到滚动容器,则返回null,使用默认的viewport\n }\n\n // IntersectionObserver 回调\n private _intersectionCallback(entries: IntersectionObserverEntry[]): void {\n const [target] = entries;\n if (!(target.isIntersecting)) return;\n \n // 只在有下一页时触发事件\n if (this._hasNextPage) {\n // 触发自定义事件\n this.dispatchEvent(new CustomEvent('end-reached', {\n bubbles: true,\n composed: true\n }));\n }\n }\n\n // ResizeObserver 回调\n private _resizeCallback(entries: ResizeObserverEntry[]): void {\n // 当父元素大小变化时,重新初始化观察器\n if (this._observerRef) {\n this._observerRef.disconnect();\n \n this._observerRef = new IntersectionObserver(this._intersectionCallback, {\n threshold: 0.0,\n root: this._findScrollContainer()\n });\n \n if (this._bottomRef) {\n this._observerRef.observe(this._bottomRef);\n }\n }\n }\n\n // 更新插槽可见性\n private _updateSlotVisibility(): void {\n const loadingSlot = this.shadowRoot?.querySelector('.loading-slot');\n const noDataSlot = this.shadowRoot?.querySelector('.no-data-slot');\n \n if (loadingSlot && noDataSlot) {\n loadingSlot.className = this._hasNextPage ? 'loading-slot contents' : 'loading-slot hidden';\n noDataSlot.className = this._hasNextPage ? 'no-data-slot hidden' : 'no-data-slot contents';\n }\n }\n\n // 渲染组件\n private _render(): void {\n if (!this.shadowRoot) return;\n \n // 添加样式\n const styleElement = document.createElement('style');\n styleElement.textContent = style;\n this.shadowRoot.appendChild(styleElement);\n \n // 创建容器\n this._container = document.createElement('div');\n this._container.setAttribute('part', 'container');\n this._container.className = 'relative';\n \n // 创建底部观察元素\n this._bottomRef = document.createElement('div');\n this._bottomRef.className = 'absolute bottom-0 z-[-1]';\n this._bottomRef.style.height = `${this._onEndReachedThreshold}px`;\n \n // 创建内容插槽\n const defaultSlot = document.createElement('slot');\n \n // 创建加载中插槽\n const loadingSlot = document.createElement('div');\n loadingSlot.className = this._hasNextPage ? 'loading-slot contents' : 'loading-slot hidden';\n \n const loadingNamedSlot = document.createElement('slot');\n loadingNamedSlot.setAttribute('name', 'loading');\n \n loadingSlot.appendChild(loadingNamedSlot);\n \n // 创建无数据插槽\n const noDataSlot = document.createElement('div');\n noDataSlot.className = this._hasNextPage ? 'no-data-slot hidden' : 'no-data-slot contents';\n \n const noDataNamedSlot = document.createElement('slot');\n noDataNamedSlot.setAttribute('name', 'no-data');\n \n noDataSlot.appendChild(noDataNamedSlot);\n \n // 组装组件\n this._container.appendChild(this._bottomRef);\n this._container.appendChild(defaultSlot);\n this._container.appendChild(loadingSlot);\n this._container.appendChild(noDataSlot);\n \n this.shadowRoot.appendChild(this._container);\n }\n}\n\n// 注册自定义元素\ncustomElements.define('infinite-scroll-list', InfiniteScrollList);\n"],"names":["InfiniteScrollList","HTMLElement","constructor","super","this","_observerRef","_resizeObserverRef","_bottomRef","_onEndReachedThreshold","_hasNextPage","_container","attachShadow","mode","_intersectionCallback","bind","_resizeCallback","observedAttributes","attributeChangedCallback","name","oldValue","newValue","Number","style","height","_updateSlotVisibility","connectedCallback","getAttribute","_render","IntersectionObserver","threshold","root","_findScrollContainer","observe","ResizeObserver","parentElement","disconnectedCallback","disconnect","parent","overflowY","window","getComputedStyle","entries","target","dispatchEvent","CustomEvent","bubbles","composed","loadingSlot","shadowRoot","querySelector","noDataSlot","className","styleElement","document","createElement","textContent","appendChild","setAttribute","defaultSlot","loadingNamedSlot","noDataNamedSlot","customElements","define"],"mappings":"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"}
1
+ {"version":3,"file":"infinite-scroll-list.min.js","sources":["../src/infinite-scroll-list.ts"],"sourcesContent":["/**\n * 无限滚动加载组件\n * \n * 功能:\n * 1. 支持传入距离底部的距离参数(on-end-reached-threshold)\n * 2. 支持外部传入是否还有下一页(has-next-page)\n * 3. 支持两个slot:loading和no-data\n * 4. 根据是否有下一页显示对应的slot内容\n * 5. 支持在body中使用,也支持在父或祖先元素overflow-y: auto的元素中使用\n * 6. 自适应父元素大小变化\n * 7. 组件只在has-next-page为true时触发end-reached事件\n */\n\n// 定义组件的样式\nconst style = `\n:host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.bottom-ref {\n width: 0;\n height: 0;\n pointer-events: none;\n}\n\n.w-full {\n width: 100%;\n}\n\n.hidden {\n display: none;\n}\n\n.contents {\n display: contents;\n}\n`;\n\nclass InfiniteScrollList extends HTMLElement {\n // 私有属性\n private _observerRef: IntersectionObserver | null = null;\n private _resizeObserverRef: ResizeObserver | null = null;\n private _bottomRef: HTMLDivElement | null = null;\n private _onEndReachedThreshold: number = 0;\n private _hasNextPage: boolean = false;\n\n constructor() {\n super();\n \n // 创建 Shadow DOM\n this.attachShadow({ mode: 'open' });\n \n // 绑定回调函数,避免this指向问题\n this._intersectionCallback = this._intersectionCallback.bind(this);\n this._resizeCallback = this._resizeCallback.bind(this);\n }\n\n // 定义观察的属性\n static get observedAttributes(): string[] {\n return ['on-end-reached-threshold', 'has-next-page'];\n }\n\n // 属性变化时的回调\n attributeChangedCallback(name: string, oldValue: string, newValue: string): void {\n if (oldValue === newValue) return;\n \n if (name === 'on-end-reached-threshold') {\n this._onEndReachedThreshold = Number(newValue) || 0;\n if (this._bottomRef) {\n // 使用 margin-top 负值来实现距离阈值\n // 当滚动到距离底部还有 threshold 像素时,_bottomRef 会进入视口\n this._bottomRef.style.marginTop = `${-this._onEndReachedThreshold}px`;\n }\n } else if (name === 'has-next-page') {\n this._hasNextPage = newValue !== null && newValue !== 'false';\n this._updateSlotVisibility();\n }\n }\n\n // 组件连接到 DOM 时\n connectedCallback(): void {\n // 初始化属性默认值\n this._onEndReachedThreshold = Number(this.getAttribute('on-end-reached-threshold')) || 0;\n this._hasNextPage = this.getAttribute('has-next-page') !== 'false';\n \n // 渲染组件\n this._render();\n \n // 初始化 IntersectionObserver\n this._observerRef = new IntersectionObserver(this._intersectionCallback, {\n threshold: 0.0,\n root: this._findScrollContainer()\n });\n \n if (this._bottomRef) {\n this._observerRef.observe(this._bottomRef);\n }\n\n // 初始化 ResizeObserver 监听父元素大小变化\n this._resizeObserverRef = new ResizeObserver(this._resizeCallback);\n if (this.parentElement) {\n this._resizeObserverRef.observe(this.parentElement);\n }\n }\n\n // 组件从 DOM 断开连接时\n disconnectedCallback(): void {\n // 清理 IntersectionObserver\n if (this._observerRef) {\n this._observerRef.disconnect();\n this._observerRef = null;\n }\n\n // 清理 ResizeObserver\n if (this._resizeObserverRef) {\n this._resizeObserverRef.disconnect();\n this._resizeObserverRef = null;\n }\n }\n\n // 查找滚动容器\n private _findScrollContainer(): Element | null {\n let parent = this.parentElement;\n \n while (parent) {\n const overflowY = window.getComputedStyle(parent).overflowY;\n if (overflowY === 'auto' || overflowY === 'scroll') {\n return parent;\n }\n parent = parent.parentElement;\n }\n \n return null; // 如果没有找到滚动容器,则返回null,使用默认的viewport\n }\n\n // IntersectionObserver 回调\n private _intersectionCallback(entries: IntersectionObserverEntry[]): void {\n const [target] = entries;\n if (!(target.isIntersecting)) return;\n \n // 只在有下一页时触发事件\n if (this._hasNextPage) {\n // 触发自定义事件\n this.dispatchEvent(new CustomEvent('end-reached', {\n bubbles: true,\n composed: true\n }));\n }\n }\n\n // ResizeObserver 回调\n private _resizeCallback(entries: ResizeObserverEntry[]): void {\n // 当父元素大小变化时,重新初始化观察器\n if (this._observerRef) {\n this._observerRef.disconnect();\n \n this._observerRef = new IntersectionObserver(this._intersectionCallback, {\n threshold: 0.0,\n root: this._findScrollContainer()\n });\n \n if (this._bottomRef) {\n this._observerRef.observe(this._bottomRef);\n }\n }\n }\n\n // 更新插槽可见性\n private _updateSlotVisibility(): void {\n const loadingSlot = this.shadowRoot?.querySelector('.loading-slot');\n const noDataSlot = this.shadowRoot?.querySelector('.no-data-slot');\n \n if (loadingSlot && noDataSlot) {\n loadingSlot.className = this._hasNextPage ? 'loading-slot contents' : 'loading-slot hidden';\n noDataSlot.className = this._hasNextPage ? 'no-data-slot hidden' : 'no-data-slot contents';\n }\n }\n\n // 渲染组件\n private _render(): void {\n if (!this.shadowRoot) return;\n \n // 添加样式\n const styleElement = document.createElement('style');\n styleElement.textContent = style;\n this.shadowRoot.appendChild(styleElement);\n \n // 创建内容插槽\n const defaultSlot = document.createElement('slot');\n \n // 创建底部观察元素(宽高为0,跟在内容之后)\n this._bottomRef = document.createElement('div');\n this._bottomRef.className = 'bottom-ref';\n this._bottomRef.style.marginTop = `${-this._onEndReachedThreshold}px`;\n \n // 创建加载中插槽\n const loadingSlot = document.createElement('div');\n loadingSlot.className = this._hasNextPage ? 'loading-slot contents' : 'loading-slot hidden';\n \n const loadingNamedSlot = document.createElement('slot');\n loadingNamedSlot.setAttribute('name', 'loading');\n \n loadingSlot.appendChild(loadingNamedSlot);\n \n // 创建无数据插槽\n const noDataSlot = document.createElement('div');\n noDataSlot.className = this._hasNextPage ? 'no-data-slot hidden' : 'no-data-slot contents';\n \n const noDataNamedSlot = document.createElement('slot');\n noDataNamedSlot.setAttribute('name', 'no-data');\n \n noDataSlot.appendChild(noDataNamedSlot);\n \n // 组装组件(调整顺序:defaultSlot -> _bottomRef -> loadingSlot -> noDataSlot)\n this.shadowRoot.appendChild(defaultSlot);\n this.shadowRoot.appendChild(this._bottomRef);\n this.shadowRoot.appendChild(loadingSlot);\n this.shadowRoot.appendChild(noDataSlot);\n }\n}\n\n// 注册自定义元素\ncustomElements.define('infinite-scroll-list', InfiniteScrollList);\n"],"names":["InfiniteScrollList","HTMLElement","constructor","super","this","_observerRef","_resizeObserverRef","_bottomRef","_onEndReachedThreshold","_hasNextPage","attachShadow","mode","_intersectionCallback","bind","_resizeCallback","observedAttributes","attributeChangedCallback","name","oldValue","newValue","Number","style","marginTop","_updateSlotVisibility","connectedCallback","getAttribute","_render","IntersectionObserver","threshold","root","_findScrollContainer","observe","ResizeObserver","parentElement","disconnectedCallback","disconnect","parent","overflowY","window","getComputedStyle","entries","target","dispatchEvent","CustomEvent","bubbles","composed","loadingSlot","shadowRoot","querySelector","noDataSlot","className","styleElement","document","createElement","textContent","appendChild","defaultSlot","loadingNamedSlot","setAttribute","noDataNamedSlot","customElements","define"],"mappings":"yBAwCA,MAAMA,UAA2BC,YAQ/B,WAAAC,GACEC,QAPMC,KAAYC,aAAgC,KAC5CD,KAAkBE,mBAA0B,KAC5CF,KAAUG,WAA0B,KACpCH,KAAsBI,uBAAW,EACjCJ,KAAYK,cAAY,EAM9BL,KAAKM,aAAa,CAAEC,KAAM,SAG1BP,KAAKQ,sBAAwBR,KAAKQ,sBAAsBC,KAAKT,MAC7DA,KAAKU,gBAAkBV,KAAKU,gBAAgBD,KAAKT,KAClD,CAGD,6BAAWW,GACT,MAAO,CAAC,2BAA4B,gBACrC,CAGD,wBAAAC,CAAyBC,EAAcC,EAAkBC,GACnDD,IAAaC,IAEJ,6BAATF,GACFb,KAAKI,uBAAyBY,OAAOD,IAAa,EAC9Cf,KAAKG,aAGPH,KAAKG,WAAWc,MAAMC,WAAgBlB,KAAKI,uBAAT,OAElB,kBAATS,IACTb,KAAKK,aAA4B,OAAbU,GAAkC,UAAbA,EACzCf,KAAKmB,yBAER,CAGD,iBAAAC,GAEEpB,KAAKI,uBAAyBY,OAAOhB,KAAKqB,aAAa,8BAAgC,EACvFrB,KAAKK,aAAsD,UAAvCL,KAAKqB,aAAa,iBAGtCrB,KAAKsB,UAGLtB,KAAKC,aAAe,IAAIsB,qBAAqBvB,KAAKQ,sBAAuB,CACvEgB,UAAW,EACXC,KAAMzB,KAAK0B,yBAGT1B,KAAKG,YACPH,KAAKC,aAAa0B,QAAQ3B,KAAKG,YAIjCH,KAAKE,mBAAqB,IAAI0B,eAAe5B,KAAKU,iBAC9CV,KAAK6B,eACP7B,KAAKE,mBAAmByB,QAAQ3B,KAAK6B,cAExC,CAGD,oBAAAC,GAEM9B,KAAKC,eACPD,KAAKC,aAAa8B,aAClB/B,KAAKC,aAAe,MAIlBD,KAAKE,qBACPF,KAAKE,mBAAmB6B,aACxB/B,KAAKE,mBAAqB,KAE7B,CAGO,oBAAAwB,GACN,IAAIM,EAAShC,KAAK6B,cAElB,KAAOG,GAAQ,CACb,MAAMC,EAAYC,OAAOC,iBAAiBH,GAAQC,UAClD,GAAkB,SAAdA,GAAsC,WAAdA,EAC1B,OAAOD,EAETA,EAASA,EAAOH,aACjB,CAED,OAAO,IACR,CAGO,qBAAArB,CAAsB4B,GAC5B,MAAOC,GAAUD,EACXC,EAAqB,gBAGvBrC,KAAKK,cAEPL,KAAKsC,cAAc,IAAIC,YAAY,cAAe,CAChDC,SAAS,EACTC,UAAU,IAGf,CAGO,eAAA/B,CAAgB0B,GAElBpC,KAAKC,eACPD,KAAKC,aAAa8B,aAElB/B,KAAKC,aAAe,IAAIsB,qBAAqBvB,KAAKQ,sBAAuB,CACvEgB,UAAW,EACXC,KAAMzB,KAAK0B,yBAGT1B,KAAKG,YACPH,KAAKC,aAAa0B,QAAQ3B,KAAKG,YAGpC,CAGO,qBAAAgB,GACN,MAAMuB,EAAc1C,KAAK2C,YAAYC,cAAc,iBAC7CC,EAAa7C,KAAK2C,YAAYC,cAAc,iBAE9CF,GAAeG,IACjBH,EAAYI,UAAY9C,KAAKK,aAAe,wBAA0B,sBACtEwC,EAAWC,UAAY9C,KAAKK,aAAe,sBAAwB,wBAEtE,CAGO,OAAAiB,GACN,IAAKtB,KAAK2C,WAAY,OAGtB,MAAMI,EAAeC,SAASC,cAAc,SAC5CF,EAAaG,YA5KH,uPA6KVlD,KAAK2C,WAAWQ,YAAYJ,GAG5B,MAAMK,EAAcJ,SAASC,cAAc,QAG3CjD,KAAKG,WAAa6C,SAASC,cAAc,OACzCjD,KAAKG,WAAW2C,UAAY,aAC5B9C,KAAKG,WAAWc,MAAMC,WAAgBlB,KAAKI,uBAAT,KAGlC,MAAMsC,EAAcM,SAASC,cAAc,OAC3CP,EAAYI,UAAY9C,KAAKK,aAAe,wBAA0B,sBAEtE,MAAMgD,EAAmBL,SAASC,cAAc,QAChDI,EAAiBC,aAAa,OAAQ,WAEtCZ,EAAYS,YAAYE,GAGxB,MAAMR,EAAaG,SAASC,cAAc,OAC1CJ,EAAWC,UAAY9C,KAAKK,aAAe,sBAAwB,wBAEnE,MAAMkD,EAAkBP,SAASC,cAAc,QAC/CM,EAAgBD,aAAa,OAAQ,WAErCT,EAAWM,YAAYI,GAGvBvD,KAAK2C,WAAWQ,YAAYC,GAC5BpD,KAAK2C,WAAWQ,YAAYnD,KAAKG,YACjCH,KAAK2C,WAAWQ,YAAYT,GAC5B1C,KAAK2C,WAAWQ,YAAYN,EAC7B,EAIHW,eAAeC,OAAO,uBAAwB7D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wc-lib/infinite-scroll-list",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Web Component for infinite scroll loading",
5
5
  "type": "module",
6
6
  "main": "dist/infinite-scroll-list.min.js",
@@ -33,5 +33,6 @@
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public"
36
- }
36
+ },
37
+ "packageManager": "pnpm@10.20.0+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd"
37
38
  }