diff --git a/packages/duoyun-ui/docs/en/02-elements/list.md b/packages/duoyun-ui/docs/en/02-elements/list.md index 63d007e0..e995124b 100644 --- a/packages/duoyun-ui/docs/en/02-elements/list.md +++ b/packages/duoyun-ui/docs/en/02-elements/list.md @@ -28,6 +28,40 @@ +## Infinite Scroll Example + + + +```json +{ + "style": "overflow:auto;height:300px;width:100%;overscroll-behavior:contain;text-align:center;", + "items": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + "infinite": true, + "getKey": "(item) => item", + "@backward": "({target})=>target.items=[...target.items,...Array.from({length:150},(_,i)=>target.items.length+i)]", + "innerHTML": "" +} +``` + + + + + +```json +{ + "style": "overflow:auto;height:300px;width:100%;overscroll-behavior:contain;", + "items": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + "infinite": true, + "debug": true, + "getKey": "(item) => item", + "renderItem": "(item) => item", + "@backward": "({target})=>target.items=[...target.items,...Array.from({length:150},(_,i)=>target.items.length+i)]", + "innerHTML": "" +} +``` + + + ## API diff --git a/packages/duoyun-ui/docs/zh/02-elements/list.md b/packages/duoyun-ui/docs/zh/02-elements/list.md index 8db4ec2c..e995124b 100644 --- a/packages/duoyun-ui/docs/zh/02-elements/list.md +++ b/packages/duoyun-ui/docs/zh/02-elements/list.md @@ -34,11 +34,12 @@ ```json { - "style": "overflow:auto;height:300px;width:100%;overscroll-behavior:contain;", + "style": "overflow:auto;height:300px;width:100%;overscroll-behavior:contain;text-align:center;", "items": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "infinite": true, "getKey": "(item) => item", - "@backward": "({target})=>target.items=[...target.items,...Array.from({length:150},(_,i)=>target.items.length+i)]" + "@backward": "({target})=>target.items=[...target.items,...Array.from({length:150},(_,i)=>target.items.length+i)]", + "innerHTML": "" } ``` @@ -48,14 +49,14 @@ ```json { - "style": "overflow:auto;height:300px;width:100%;overscroll-behavior:contain;display:grid;grid-template-columns:repeat(auto-fill,minmax(12em,1fr));", + "style": "overflow:auto;height:300px;width:100%;overscroll-behavior:contain;", "items": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "infinite": true, "debug": true, "getKey": "(item) => item", "renderItem": "(item) => item", "@backward": "({target})=>target.items=[...target.items,...Array.from({length:150},(_,i)=>target.items.length+i)]", - "innerHTML": "" + "innerHTML": "" } ``` diff --git a/packages/duoyun-ui/src/elements/list.ts b/packages/duoyun-ui/src/elements/list.ts index 3e955cc3..ac29cf62 100644 --- a/packages/duoyun-ui/src/elements/list.ts +++ b/packages/duoyun-ui/src/elements/list.ts @@ -45,21 +45,22 @@ export type PersistentState = State & { scrollTop: number; }; -const outsideStyle = createCSSSheet(css` - :host { - grid-column: 1/-1; - } -`); - @customElement('dy-list-outside') @adoptedStyle(blockContainer) -@adoptedStyle(outsideStyle) export class DuoyunOutsideElement extends DuoyunVisibleBaseElement {} const style = createCSSSheet(css` :host([infinite]) { overflow-anchor: none; } + .list { + display: contents; + } + dy-list-outside, + .placeholder, + ::slotted(*) { + grid-column: 1/-1; + } `); /** @@ -69,6 +70,7 @@ const style = createCSSSheet(css` @adoptedStyle(style) @adoptedStyle(blockContainer) export class DuoyunListElement extends GemElement { + @part static list: string; @part static item: string; @part static beforeOutside: string; @part static afterOutside: string; @@ -91,6 +93,7 @@ export class DuoyunListElement extends GemElement { @emitter itemshow: Emitter; @refobject beforeItemRef: RefObject; @refobject afterItemRef: RefObject; + @refobject listRef: RefObject; get #items() { return this.items || this.data; @@ -131,6 +134,8 @@ export class DuoyunListElement extends GemElement { return (item === undefined && key === undefined) || key === this.getKey!(item); }; + #getElementRowHeight = (ele: DuoyunListItemElement) => ele.borderBoxSize.blockSize + this.#rowGap; + #setState = (state: Partial) => { this.#log(state); this.setState(state); @@ -161,19 +166,25 @@ export class DuoyunListElement extends GemElement { let afterHeightSum = 0; let node = this.#itemLinked.first; let count = 0; + let pushed = false; while (node) { const ele = this.#getElement(node.value); + const isLeft = this.#isLeftItem(count); + const currentItemHeight = this.#getElementRowHeight(ele); - const blockSize = this.#isLeftItem(count) ? ele.borderBoxSize.blockSize : 0; const y = firstElementY + beforeHeightSum + renderHeightSum; - if (y > containerRect.bottom + safeHeight) { - afterHeightSum += blockSize; - } else if (y + blockSize > containerRect.top - safeHeight) { + const realY = isLeft ? y + currentItemHeight : y; + + const appendHeight = isLeft ? currentItemHeight : 0; + if (pushed || realY > containerRect.bottom + safeHeight) { + pushed = true; + afterHeightSum += appendHeight; + } else if (realY > containerRect.top - safeHeight) { list.push(node.value); - renderHeightSum += blockSize; + renderHeightSum += appendHeight; } else { - beforeHeightSum += blockSize; + beforeHeightSum += appendHeight; } count++; node = node.next; @@ -212,7 +223,7 @@ export class DuoyunListElement extends GemElement { for (let i = len - 1; i >= 0; i--) { const ele = this.#getElement(this.state.renderList[i]); if (!ele.visible) { - if (this.#isLeftItem(count)) afterHeight += ele.borderBoxSize.blockSize; + if (this.#isLeftItem(count)) afterHeight += this.#getElementRowHeight(ele); len--; } count++; @@ -239,7 +250,7 @@ export class DuoyunListElement extends GemElement { let beforeHeight = 0; for (let i = 0; i < this.#appendCount; i++) { if (!node) break; - if (this.#isLeftItem(i)) beforeHeight += this.#getElement(node.value).borderBoxSize.blockSize; + if (this.#isLeftItem(i)) beforeHeight += this.#getElementRowHeight(this.#getElement(node.value)); appendList.unshift(node.value); node = node.prev; } @@ -267,7 +278,7 @@ export class DuoyunListElement extends GemElement { const ele = this.#getElement(key); if (!ele.visible) { len++; - if (this.#isLeftItem(count)) beforeHeight += ele.borderBoxSize.blockSize; + if (this.#isLeftItem(count)) beforeHeight += this.#getElementRowHeight(ele); } count++; } @@ -295,7 +306,7 @@ export class DuoyunListElement extends GemElement { for (let i = 0; i < this.#appendCount; i++) { if (!node) break; appendList.push(node.value); - if (this.#isLeftItem(i)) afterHeight += this.#getElement(node.value).borderBoxSize.blockSize; + if (this.#isLeftItem(i)) afterHeight += this.#getElementRowHeight(this.#getElement(node.value)); node = node.next; } this.#setState({ @@ -311,6 +322,8 @@ export class DuoyunListElement extends GemElement { #itemHeight = 0; #itemColumnCount = 1; + #rowGap = 0; + #columnGap = 0; // 跟用户初始 Items 长度相同会触发两次 backward 事件,用户配置? #itemCountPerScreen = 19; #onItemResize = throttle( @@ -319,10 +332,17 @@ export class DuoyunListElement extends GemElement { if (ele?.borderBoxSize.blockSize) { this.#initCheckOnce(this.items!.length > this.#itemCountPerScreen); - this.#itemColumnCount = Math.floor(this.scrollContainer.clientWidth / ele.borderBoxSize.inlineSize); + const style = getComputedStyle(this.listRef.element!); + const thisGrid = getComputedStyle(this); + this.#rowGap = parseFloat(style.rowGap) || parseFloat(thisGrid.rowGap) || 0; + this.#columnGap = parseFloat(style.columnGap) || parseFloat(thisGrid.columnGap) || 0; + + this.#itemColumnCount = Math.round( + this.scrollContainer.clientWidth / (ele.borderBoxSize.inlineSize + this.#columnGap), + ); this.#itemHeight = ele.borderBoxSize.blockSize; this.#itemCountPerScreen = - Math.ceil(this.scrollContainer.clientHeight / this.#itemHeight) * this.#itemColumnCount; + Math.ceil(this.scrollContainer.clientHeight / this.#getElementRowHeight(ele)) * this.#itemColumnCount; } }, 1000, @@ -382,7 +402,7 @@ export class DuoyunListElement extends GemElement { let beforeHeight = 0; for (let i = 0; i < items.length; i++) { if (this.getKey!(items[i]) === this.getKey!(oldDeps[0][0])) break; - if (this.#isLeftItem(i)) beforeHeight += this.#itemHeight; + if (this.#isLeftItem(i)) beforeHeight += this.#itemHeight + this.#rowGap; } if (beforeHeight) { this.#setState({ beforeHeight: this.state.beforeHeight + beforeHeight }); @@ -439,7 +459,9 @@ export class DuoyunListElement extends GemElement { @show=${this.#onBeforeItemVisible} style=${styleMap({ height: `${beforeHeight}px` })} > - ${renderList.map((key) => this.#getElement(key))} + + ${renderList.map((key) => this.#getElement(key))} + { )} - + `; };