@king-design/intact 3.5.0-beta.0 → 3.5.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.
Files changed (122) hide show
  1. package/components/affix/index.md +1 -0
  2. package/components/affix/index.ts +2 -0
  3. package/components/affix/useStyle.ts +38 -35
  4. package/components/datepicker/basepicker.ts +3 -3
  5. package/components/dialog/styles.ts +2 -2
  6. package/components/dropdown/useKeyboard.ts +3 -0
  7. package/components/layout/styles.ts +1 -0
  8. package/components/select/base.ts +2 -0
  9. package/components/select/base.vdt +0 -2
  10. package/components/select/demos/group.md +1 -1
  11. package/components/select/demos/virtual.md +47 -0
  12. package/components/select/group.vdt +3 -2
  13. package/components/select/index.md +1 -0
  14. package/components/select/menu.vdt +5 -4
  15. package/components/select/select.vdt +1 -1
  16. package/components/select/useFilterable.ts +2 -2
  17. package/components/select/useInput.ts +6 -1
  18. package/components/table/demos/fixHeader.md +25 -5
  19. package/components/table/demos/virtual.md +105 -0
  20. package/components/table/index.md +1 -0
  21. package/components/table/index.spec.ts +2 -1
  22. package/components/table/row.ts +2 -1
  23. package/components/table/styles.ts +4 -0
  24. package/components/table/table.ts +6 -4
  25. package/components/table/table.vdt +15 -11
  26. package/components/treeSelect/index.ts +1 -1
  27. package/components/virtualList/container.ts +36 -0
  28. package/components/virtualList/container.vdt +30 -0
  29. package/components/virtualList/demos/basic.md +67 -0
  30. package/components/virtualList/demos/combined.md +57 -0
  31. package/components/virtualList/demos/delete.md +70 -0
  32. package/components/virtualList/index.md +19 -0
  33. package/components/virtualList/index.spec.ts +263 -0
  34. package/components/virtualList/index.ts +5 -0
  35. package/components/virtualList/phantom.ts +18 -0
  36. package/components/virtualList/phantom.vdt +28 -0
  37. package/components/virtualList/rows.ts +13 -0
  38. package/components/virtualList/rows.vdt +20 -0
  39. package/components/virtualList/styles.ts +29 -0
  40. package/components/virtualList/useRows.ts +24 -0
  41. package/components/virtualList/useVirtualRows.ts +145 -0
  42. package/components/virtualList/virtual.ts +19 -0
  43. package/components/virtualList/virtual.vdt +17 -0
  44. package/components/virtualList/wrapper.ts +17 -0
  45. package/components/virtualList/wrapper.vdt +24 -0
  46. package/es/components/affix/index.d.ts +1 -0
  47. package/es/components/affix/index.js +2 -1
  48. package/es/components/affix/useStyle.js +50 -47
  49. package/es/components/datepicker/basepicker.js +3 -3
  50. package/es/components/dialog/styles.js +2 -2
  51. package/es/components/dropdown/useKeyboard.js +3 -0
  52. package/es/components/input/index.spec.js +4 -2
  53. package/es/components/layout/styles.js +1 -1
  54. package/es/components/select/base.d.ts +1 -0
  55. package/es/components/select/base.js +2 -1
  56. package/es/components/select/base.vdt.js +2 -4
  57. package/es/components/select/group.vdt.js +8 -3
  58. package/es/components/select/menu.vdt.js +12 -3
  59. package/es/components/select/select.vdt.js +2 -1
  60. package/es/components/select/useFilterable.js +7 -5
  61. package/es/components/select/useInput.js +6 -2
  62. package/es/components/table/index.spec.js +7 -6
  63. package/es/components/table/styles.js +1 -1
  64. package/es/components/table/table.d.ts +1 -0
  65. package/es/components/table/table.js +3 -2
  66. package/es/components/table/table.vdt.js +126 -114
  67. package/es/components/treeSelect/index.js +4 -3
  68. package/es/components/virtualList/container.d.ts +10 -0
  69. package/es/components/virtualList/container.js +26 -0
  70. package/es/components/virtualList/container.vdt.js +39 -0
  71. package/es/components/virtualList/index.d.ts +5 -0
  72. package/es/components/virtualList/index.js +5 -0
  73. package/es/components/virtualList/index.spec.d.ts +1 -0
  74. package/es/components/virtualList/index.spec.js +372 -0
  75. package/es/components/virtualList/phantom.d.ts +9 -0
  76. package/es/components/virtualList/phantom.js +24 -0
  77. package/es/components/virtualList/phantom.vdt.js +33 -0
  78. package/es/components/virtualList/rows.d.ts +8 -0
  79. package/es/components/virtualList/rows.js +20 -0
  80. package/es/components/virtualList/rows.vdt.js +32 -0
  81. package/es/components/virtualList/styles.d.ts +13 -0
  82. package/es/components/virtualList/styles.js +34 -0
  83. package/es/components/virtualList/useRows.d.ts +2 -0
  84. package/es/components/virtualList/useRows.js +19 -0
  85. package/es/components/virtualList/useVirtualRows.d.ts +20 -0
  86. package/es/components/virtualList/useVirtualRows.js +120 -0
  87. package/es/components/virtualList/virtual.d.ts +8 -0
  88. package/es/components/virtualList/virtual.js +15 -0
  89. package/es/components/virtualList/virtual.vdt.js +26 -0
  90. package/es/components/virtualList/wrapper.d.ts +9 -0
  91. package/es/components/virtualList/wrapper.js +24 -0
  92. package/es/components/virtualList/wrapper.vdt.js +34 -0
  93. package/es/index.d.ts +3 -2
  94. package/es/index.js +3 -2
  95. package/es/site/data/components/select/demos/virtual/index.d.ts +11 -0
  96. package/es/site/data/components/select/demos/virtual/index.js +32 -0
  97. package/es/site/data/components/select/demos/virtual/react.d.ts +11 -0
  98. package/es/site/data/components/select/demos/virtual/react.js +53 -0
  99. package/es/site/data/components/table/demos/fixHeader/index.d.ts +6 -0
  100. package/es/site/data/components/table/demos/fixHeader/index.js +14 -0
  101. package/es/site/data/components/table/demos/fixHeader/react.d.ts +6 -0
  102. package/es/site/data/components/table/demos/fixHeader/react.js +28 -11
  103. package/es/site/data/components/table/demos/virtual/index.d.ts +13 -0
  104. package/es/site/data/components/table/demos/virtual/index.js +76 -0
  105. package/es/site/data/components/table/demos/virtual/react.d.ts +14 -0
  106. package/es/site/data/components/table/demos/virtual/react.js +114 -0
  107. package/es/site/data/components/virtualList/demos/basic/index.d.ts +12 -0
  108. package/es/site/data/components/virtualList/demos/basic/index.js +42 -0
  109. package/es/site/data/components/virtualList/demos/basic/react.d.ts +12 -0
  110. package/es/site/data/components/virtualList/demos/basic/react.js +67 -0
  111. package/es/site/data/components/virtualList/demos/combined/index.d.ts +11 -0
  112. package/es/site/data/components/virtualList/demos/combined/index.js +32 -0
  113. package/es/site/data/components/virtualList/demos/combined/react.d.ts +11 -0
  114. package/es/site/data/components/virtualList/demos/combined/react.js +50 -0
  115. package/es/site/data/components/virtualList/demos/delete/index.d.ts +13 -0
  116. package/es/site/data/components/virtualList/demos/delete/index.js +51 -0
  117. package/es/site/data/components/virtualList/demos/delete/react.d.ts +13 -0
  118. package/es/site/data/components/virtualList/demos/delete/react.js +75 -0
  119. package/es/site/data/components/virtualList/index.d.ts +57 -0
  120. package/es/site/data/components/virtualList/index.js +32 -0
  121. package/index.ts +3 -2
  122. package/package.json +1 -1
@@ -17,6 +17,7 @@ import {AllCheckedStatus} from './useChecked';
17
17
  import {context as ResizableContext} from './useResizable';
18
18
  import {context as FixedColumnsContext} from './useFixedColumns';
19
19
  import {Pagination} from '../pagination';
20
+ import {VirtualListContainer, VirtualListWrapper, VirtualListRows, VirtualListPhantom} from '../virtualList';
20
21
 
21
22
  const {
22
23
  data, children, className, fixHeader,
@@ -25,7 +26,7 @@ const {
25
26
  merge, childrenKey, indent, tooltipPosition,
26
27
  tooltipContainer, showIndeterminate, resizable,
27
28
  draggable, animation: _animation, hideHeader,
28
- pagination, fixFooter, spreadArrowIndex
29
+ pagination, fixFooter, virtual, spreadArrowIndex
29
30
  } = this.get();
30
31
  const animation = !Array.isArray(_animation) ? [_animation, _animation] : _animation;
31
32
  const {columns, cols, maxRows, maxCols} = this.columns.getData();
@@ -121,7 +122,7 @@ const {isSelected} = this.selected;
121
122
  const {loopData, isSpreaded, toggleSpreadRow} = this.tree;
122
123
  const {onRowDragStart, onRowDragOver, onRowDragEnd, draggingKey} = this.draggable;
123
124
  const tbody = (
124
- <tbody>
125
+ <VirtualListWrapper tagName="tbody">
125
126
  {!hasData ?
126
127
  <tr key="table-empty">
127
128
  <td colspan={colCount} class={`${k}-table-empty`}>
@@ -138,6 +139,7 @@ const tbody = (
138
139
  const key = allKeys[index];
139
140
  const spreaded = isSpreaded(key);
140
141
  const hasChildren = !!childrenKey && Array.isArray(value[childrenKey]);
142
+
141
143
  const indentSize = indent ? indent * level : 0;
142
144
  let row = <TableRow
143
145
  key={key}
@@ -200,15 +202,15 @@ const tbody = (
200
202
  );
201
203
  }
202
204
 
203
- return hidden || !spreaded;
205
+ return hidden || !spreaded;
204
206
  });
205
207
 
206
- return animation[0] ?
207
- <TransitionGroup name="k-fade-in-left" move={!draggingKey.value}>{rows}</TransitionGroup> :
208
- rows;
208
+ return animation[0] && !virtual?
209
+ <TransitionGroup name="k-fade-in-left" move={!draggingKey.value}>{rows}</TransitionGroup> :
210
+ <VirtualListRows>{rows}</VirtualListRows>;
209
211
  })()
210
212
  }
211
- </tbody>
213
+ </VirtualListWrapper>
212
214
  );
213
215
 
214
216
  let tfooter = null;
@@ -239,11 +241,13 @@ const {
239
241
  } = this.pagination;
240
242
 
241
243
  <div class={classNameObj} ref={elementRef}>
242
- <div class={`${k}-table-wrapper`} style={style} ref={scrollRef}>
243
- <Affix v-if={!isNull(stickHeader.value)}
244
+ <VirtualListContainer class={`${k}-table-wrapper`} disabled={!virtual} style={style} ref={scrollRef}>
245
+ <VirtualListPhantom />
246
+ <Affix v-if={!isNull(stickHeader.value) || fixHeader}
244
247
  top={stickHeader.value}
245
248
  exclude={excludeStickHeader}
246
249
  class={`${k}-table-affix-header`}
250
+ disabled={!!fixHeader}
247
251
  >
248
252
  <table ref={headRef} style={{width: tableWidthPx}}>
249
253
  <template>{colgroup}</template>
@@ -252,11 +256,11 @@ const {
252
256
  </Affix>
253
257
  <table ref={tableRef} style={{width: tableWidthPx}}>
254
258
  <template>{colgroup}</template>
255
- <template v-if={isNull(stickHeader.value)}>{thead}</template>
259
+ <template v-if={isNull(stickHeader.value) && !fixHeader}>{thead}</template>
256
260
  <template>{tbody}</template>
257
261
  <template v-if={$blocks.footer}>{tfooter}</template>
258
262
  </table>
259
- </div>
263
+ </VirtualListContainer>
260
264
  <Pagination v-if={pagination}
261
265
  ref={paginationRef}
262
266
  total={data ? data.length : 0}
@@ -124,7 +124,7 @@ export class TreeSelect<
124
124
 
125
125
  @bind
126
126
  private filter(data: DataItem<K>) {
127
- let keywords = this.input.keywords.value;
127
+ let keywords = this.input.keywords.value.trim();
128
128
  if (!keywords) return true;
129
129
 
130
130
  const {filter} = this.get();
@@ -0,0 +1,36 @@
1
+ import { Component, TypeDefs, findDomFromVNode } from 'intact';
2
+ import template from './container.vdt';
3
+ import { useConfigContext } from '../config';
4
+ import { useVirtualRows } from './useVirtualRows';
5
+
6
+ export interface VirtualListContainerProps {
7
+ disabled?: boolean
8
+ }
9
+
10
+ const typeDefs: Required<TypeDefs<VirtualListContainerProps>> = {
11
+ disabled: Boolean,
12
+ };
13
+
14
+ export class VirtualListContainer extends Component<VirtualListContainerProps> {
15
+ static template = template;
16
+ static typeDefs = typeDefs;
17
+
18
+ private config = useConfigContext();
19
+ private virtualRows = useVirtualRows();
20
+
21
+ // TODO
22
+ // public scrollToIndex(index: number, behavior: ScrollBehavior = 'auto') {
23
+ // const { disabled } = this.get();
24
+ // if (disabled) return;
25
+
26
+ // let height = 0;
27
+ // for (let i = 0; i < index; i++) {
28
+ // height += this.virtualRows.getRowHeightByIndex(i);
29
+ // }
30
+ // const containerDom = findDomFromVNode(this.$lastInput!, true) as HTMLElement;
31
+ // containerDom.scrollTo({
32
+ // top: height,
33
+ // behavior,
34
+ // });
35
+ // }
36
+ }
@@ -0,0 +1,30 @@
1
+ import { addStyle, getRestProps } from '../utils';
2
+ import { makeContainerStyles } from './styles';
3
+ import { context as VirtualRowsContext } from './useVirtualRows';
4
+
5
+ const { children, className, disabled, ref } = this.get();
6
+ const { k } = this.config;
7
+
8
+ const { notifyRows, startIndex, length, getTotalHeight, translateY } = this.virtualRows;
9
+
10
+ const classNameObj = {
11
+ [`${k}-virtual`]: !disabled,
12
+ [`${k}-virtual-container`]: !disabled,
13
+ [makeContainerStyles(k)]: !disabled,
14
+ [className]: className,
15
+ }
16
+
17
+ <VirtualRowsContext.Provider
18
+ value={{
19
+ disabled,
20
+ notifyRows,
21
+ getTotalHeight,
22
+ startIndex: startIndex.value,
23
+ length: length.value,
24
+ translateY: translateY.value,
25
+ }}
26
+ >
27
+ <div {...getRestProps(this)} class={classNameObj} ref={ref}>
28
+ {children}
29
+ </div>
30
+ </VirtualRowsContext.Provider>
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: 基本用法
3
+ order: 0
4
+ ---
5
+
6
+ ```vdt
7
+ import { VirtualList } from 'kpc';
8
+
9
+ <div>
10
+ <h3>1. 定高元素</h3>
11
+ <VirtualList style="height: 450px">
12
+ <div v-for={this.get('data')} class="fixed-height-item">
13
+ {$value.label}
14
+ </div>
15
+ </VirtualList>
16
+
17
+ <h3>2. 不定高元素</h3>
18
+ <VirtualList style="width: 200px; height: 450px;">
19
+ <div v-for={this.get('variableHeightData')} class="variable-height-item">
20
+ {$value.label}
21
+ </div>
22
+ </VirtualList>
23
+ </div>
24
+ ```
25
+
26
+ ```styl
27
+ .fixed-height-item
28
+ height 30px
29
+ border-bottom 1px solid #eee
30
+ padding 5px
31
+ .variable-height-item
32
+ min-height 20px
33
+ border-bottom 1px solid #eee
34
+ padding 5px
35
+ ```
36
+
37
+ ```ts
38
+ interface Props {
39
+ data: any[]
40
+ variableHeightData: any[]
41
+ }
42
+
43
+ export default class extends Component {
44
+ static template = template;
45
+
46
+ static defaults() {
47
+ return {
48
+ data: [],
49
+ variableHeightData: [],
50
+ } as Props;
51
+ }
52
+
53
+ init() {
54
+ const arr = [];
55
+ const variableHeightData = [];
56
+ for (let index = 0; index < 10000; index++) {
57
+ arr.push({
58
+ value: index,
59
+ label: `测试${index}`
60
+ });
61
+ const repeatPart = '行内容'.repeat(Math.floor(Math.random() * 5) + 1);
62
+ variableHeightData.push({ value: index, label: `不定高度项 ${index}\n${repeatPart}` });
63
+ }
64
+ this.set({data: arr, variableHeightData});
65
+ }
66
+ }
67
+ ```
@@ -0,0 +1,57 @@
1
+ ---
2
+ title: 组合使用
3
+ order: 1
4
+ ---
5
+
6
+ 在一些复杂场景中,可以通过`VirtualListContainer`、`VirtualListWrapper`等子组件组合使用, 也可通过`tagName`属性指定自定义标签
7
+
8
+ ```vdt
9
+ import { VirtualListContainer, VirtualListWrapper, VirtualListPhantom, VirtualListRows } from 'kpc';
10
+
11
+ <div style="height: 460px">
12
+ <VirtualListContainer >
13
+ <VirtualListPhantom />
14
+ <VirtualListWrapper tagName="ul">
15
+ <VirtualListRows>
16
+ <li v-for={this.get('data')} class="fixed-height-item">
17
+ {$value.label}
18
+ </li>
19
+ </VirtualListRows>
20
+ </VirtualListWrapper>
21
+ </VirtualListContainer>
22
+ </div>
23
+ ```
24
+
25
+ ```styl
26
+ .fixed-height-item
27
+ height 30px
28
+ border-bottom 1px solid #eee
29
+ padding 5px
30
+ ```
31
+
32
+ ```ts
33
+ interface Props {
34
+ data: any[]
35
+ }
36
+
37
+ export default class extends Component {
38
+ static template = template;
39
+
40
+ static defaults() {
41
+ return {
42
+ data: [],
43
+ } as Props;
44
+ }
45
+
46
+ init() {
47
+ const arr = [];
48
+ for (let index = 0; index < 10000; index++) {
49
+ arr.push({
50
+ value: index,
51
+ label: `测试${index}`
52
+ });
53
+ }
54
+ this.set({data: arr});
55
+ }
56
+ }
57
+ ```
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: 动态删除元素
3
+ order: 2
4
+ ---
5
+
6
+ ```vdt
7
+ import { VirtualList, Button } from 'kpc';
8
+
9
+ <div>
10
+ <Button ev-click={this.removeItems}>删除前5项</Button>
11
+ <Button ev-click={this.removeLastItems}>删除后5项</Button>
12
+ <VirtualList style="height: 450px">
13
+ <div v-for={this.get('data')} class="fixed-height-item">
14
+ {$value.label}
15
+ </div>
16
+ </VirtualList>
17
+ </div>
18
+ ```
19
+
20
+ ```styl
21
+ .fixed-height-item
22
+ height 30px
23
+ border-bottom 1px solid #eee
24
+ padding 5px
25
+ .k-btn
26
+ margin-bottom 10px
27
+ ```
28
+
29
+ ```ts
30
+ import {bind} from 'kpc/components/utils';
31
+ interface Props {
32
+ data: any[]
33
+ }
34
+
35
+ export default class extends Component<Props> {
36
+ static template = template;
37
+
38
+ static defaults() {
39
+ return {
40
+ data: [],
41
+ } as Props
42
+ }
43
+
44
+ init() {
45
+ const newData = [];
46
+ const variableHeightData = [];
47
+ for (let index = 0; index < 10000; index++) {
48
+ newData.push({
49
+ value: index,
50
+ label: `测试${index}`
51
+ });
52
+ }
53
+ this.set({data: newData});
54
+ }
55
+
56
+ @bind
57
+ removeItems() {
58
+ const data = this.get('data').slice();
59
+ data.splice(0, 5);
60
+ this.set('data', data);
61
+ }
62
+
63
+ @bind
64
+ removeLastItems() {
65
+ const data = this.get('data').slice();
66
+ data.splice(data.length - 5);
67
+ this.set('data', data);
68
+ }
69
+ }
70
+ ```
@@ -0,0 +1,19 @@
1
+ ---
2
+ title: 虚拟列表
3
+ category: 组件
4
+ order: 99
5
+ sidebar: doc
6
+ ---
7
+
8
+ # 属性
9
+
10
+ | 属性 | 说明 | 类型 | 默认值 |
11
+ | --- | --- | --- | --- |
12
+ | disabled | 是否禁用虚拟化 | `boolean` | `false` |
13
+
14
+ <!-- # 方法 -->
15
+
16
+ <!-- | 方法名 | 说明 | 参数 | -->
17
+ <!-- | --- | --- | --- | -->
18
+ <!-- | scrollToIndex | 滚动到指定索引位置 | `(index: number, behavior?: 'auto' \| 'smooth') => void` | -->
19
+
@@ -0,0 +1,263 @@
1
+ import {mount, unmount, dispatchEvent, getElement, wait} from '../../test/utils';
2
+ import {VirtualList, VirtualListContainer, VirtualListWrapper, VirtualListPhantom} from './';
3
+ import {Component} from 'intact';
4
+ import BasicDemo from '~/components/virtualList/demos/basic';
5
+ import CombinedDemo from '~/components/virtualList/demos/combined';
6
+
7
+
8
+ describe('VirtualList', () => {
9
+ afterEach(() => unmount());
10
+
11
+ it('should render virtual list correctly', async () => {
12
+ const [instance, element] = mount(BasicDemo);
13
+
14
+ // check basic structure
15
+ const container = element.querySelector('.k-virtual-container')!;
16
+ expect(container.outerHTML).to.matchSnapshot();
17
+
18
+ const wrapper = element.querySelector('.k-virtual-wrapper')!;
19
+ expect(wrapper).to.exist;
20
+
21
+ // check render items is less than total
22
+ const items = wrapper.children;
23
+ expect(items.length).to.be.lessThan(100);
24
+ });
25
+
26
+ it('should handle scroll correctly', async () => {
27
+ const [instance, element] = mount(BasicDemo);
28
+
29
+ const container = element.querySelector('.k-virtual-container')!;
30
+ const wrapper = element.querySelector('.k-virtual-wrapper')!;
31
+ await wait(50);
32
+ container.scrollTop = 400;
33
+ await wait(50);
34
+
35
+ // check content is updated
36
+ const currentFirstItem = wrapper.firstElementChild;
37
+ expect(currentFirstItem!.textContent).to.not.equal('测试0');
38
+ });
39
+
40
+
41
+ it('should update total height when data changes', async () => {
42
+ const [instance, element] = mount(BasicDemo);
43
+ const [, container2] = element.querySelectorAll<HTMLElement>('.k-virtual-container');
44
+
45
+
46
+ const [, phantom2] = element.querySelectorAll<HTMLElement>('.k-virtual-phantom')!;
47
+ const initialHeight = phantom2.style.height;
48
+ await wait(50);
49
+ container2.scrollTop = 800;
50
+ await wait(50);
51
+
52
+ // check phantom height is updated
53
+ expect(phantom2.style.height).to.not.equal(initialHeight);
54
+ });
55
+
56
+ it('should work with custom container and wrapper', async () => {
57
+ const [instance, element] = mount(CombinedDemo);
58
+ await wait();
59
+
60
+ const container = element.querySelector('.k-virtual-container')!;
61
+ const wrapper = element.querySelector('.k-virtual-wrapper')!;
62
+
63
+ expect(container.outerHTML).to.matchSnapshot();
64
+ // check wrapper tag name
65
+ expect(wrapper.tagName.toLowerCase()).to.equal('ul');
66
+ });
67
+
68
+ it('should handle dynamic data changes correctly', async () => {
69
+ class Demo extends Component<{list: number[]}> {
70
+ static template = `
71
+ const VirtualList = this.VirtualList;
72
+ <VirtualList style="height: 300px">
73
+ <div v-for={this.get('list')} key={$value}>Item {$value}</div>
74
+ </VirtualList>
75
+ `;
76
+
77
+ static defaults() {
78
+ return {
79
+ list: Array.from({length: 100}, (_, i) => i)
80
+ }
81
+ }
82
+ private VirtualList = VirtualList;
83
+ }
84
+
85
+ const [instance] = mount(Demo);
86
+ await wait();
87
+
88
+ const container = getElement('.k-virtual-container')!;
89
+ const wrapper = getElement('.k-virtual-wrapper')!;
90
+
91
+ instance.set('list', instance.get('list')!.filter(i => i % 2 === 0));
92
+ await wait();
93
+
94
+ // check deleted render
95
+ expect(wrapper.children.length).to.be.lessThan(100);
96
+ expect(wrapper.firstElementChild!.textContent).to.equal('Item 0');
97
+
98
+ container.scrollTop = 300;
99
+ await wait(50);
100
+
101
+ // check scroll render
102
+ const middleContent = wrapper.firstElementChild!.textContent;
103
+ expect(middleContent).to.not.equal('Item 0');
104
+
105
+ const newList = [...instance.get('list'), ...Array.from({length: 20}, (_, i) => i + 200)];
106
+ instance.set('list', newList);
107
+ await wait(50);
108
+
109
+ // check add render position not change
110
+ expect(wrapper.firstElementChild!.textContent).to.equal(middleContent);
111
+
112
+ const prevScrollTop = container.scrollTop;
113
+ container.scrollTop = prevScrollTop + 200;
114
+ await wait(50);
115
+
116
+ // check scroll to new content
117
+ expect(wrapper.firstElementChild!.textContent).to.not.equal(middleContent);
118
+ });
119
+
120
+ it('should handle visible area data changes', async () => {
121
+ class Demo extends Component<{list: number[]}> {
122
+ static template = `
123
+ const VirtualList = this.VirtualList;
124
+ <VirtualList style="height: 300px">
125
+ <div v-for={this.get('list')} key={$value}>Item {$value}</div>
126
+ </VirtualList>
127
+ `;
128
+ static defaults() {
129
+ return {
130
+ list: Array.from({length: 100}, (_, i) => i)
131
+ }
132
+ }
133
+ private VirtualList = VirtualList;
134
+ }
135
+
136
+ const [instance, element] = mount(Demo);
137
+ await wait();
138
+
139
+ const container = getElement('.k-virtual-container')!;
140
+ const wrapper = getElement('.k-virtual-wrapper')!;
141
+
142
+ container.scrollTop = 300;
143
+ await wait(50);
144
+
145
+ const visibleFirstItem = wrapper.firstElementChild!.textContent;
146
+ const currentList = instance.get('list');
147
+
148
+ // delete visible items
149
+ const visibleIndex = parseInt(visibleFirstItem!.replace('Item ', ''));
150
+ const newList = currentList.filter(i => i !== visibleIndex && i !== visibleIndex + 1);
151
+ instance.set('list', newList);
152
+ await wait();
153
+
154
+ // check deleted render and position
155
+ expect(wrapper.firstElementChild!.textContent).to.not.equal(visibleFirstItem);
156
+ expect(wrapper.children.length).to.be.greaterThan(0);
157
+ });
158
+
159
+ it('should clean up height cache when items are deleted', async () => {
160
+ class Demo extends Component<{list: number[]}> {
161
+ static template = `
162
+ const VirtualList = this.VirtualList;
163
+ <VirtualList style="height: 300px">
164
+ <div v-for={this.get('list')} key={$value} style="height: 30px">Item {$value}</div>
165
+ </VirtualList>
166
+ `;
167
+ static defaults() {
168
+ return {
169
+ list: Array.from({length: 100}, (_, i) => i)
170
+ }
171
+ }
172
+ private VirtualList = VirtualList;
173
+ }
174
+
175
+ const [instance, element] = mount(Demo);
176
+ await wait();
177
+
178
+ const container = getElement('.k-virtual-container')!;
179
+
180
+ // first cache some height
181
+ container.scrollTop = 300;
182
+ await wait(50);
183
+
184
+ // get first visible item
185
+ const wrapper = getElement('.k-virtual-wrapper')!;
186
+ const firstVisibleItem = wrapper.firstElementChild!;
187
+ const firstVisibleIndex = parseInt(firstVisibleItem.textContent!.replace('Item ', ''));
188
+
189
+ // delete visible items
190
+ const currentList = instance.get('list');
191
+ const newList = currentList.filter(i => i > firstVisibleIndex + 5);
192
+ instance.set('list', newList);
193
+ await wait(50);
194
+
195
+ // check scroll position is adjusted
196
+ const newFirstItem = wrapper.firstElementChild!;
197
+ expect(parseInt(newFirstItem.textContent!.replace('Item ', ''))).to.be.greaterThan(firstVisibleIndex);
198
+
199
+ // scroll again to check render is normal
200
+ container.scrollTop += 100;
201
+ await wait(50);
202
+
203
+ // check new render position is correct
204
+ const afterScrollItem = wrapper.firstElementChild!;
205
+ expect(afterScrollItem.textContent).to.not.equal(newFirstItem.textContent);
206
+ });
207
+
208
+ it('should update total height when deleting last items', async () => {
209
+ class Demo extends Component<{list: number[]}> {
210
+ static template = `
211
+ const VirtualList = this.VirtualList;
212
+ <VirtualList style="height: 300px">
213
+ <div v-for={this.get('list')} key={$value} style="height: 30px">Item {$value}</div>
214
+ </VirtualList>
215
+ `;
216
+ static defaults() {
217
+ return {
218
+ list: Array.from({length: 100}, (_, i) => i)
219
+ }
220
+ }
221
+ private VirtualList = VirtualList;
222
+ }
223
+
224
+ const [instance, element] = mount(Demo);
225
+ await wait();
226
+
227
+ const container = getElement('.k-virtual-container')!;
228
+ const phantom = getElement('.k-virtual-phantom')!;
229
+
230
+ // record initial total height
231
+ const initialHeight = parseInt(phantom.style.height);
232
+
233
+ // scroll to cache height
234
+ container.scrollTop = initialHeight;
235
+ await wait(50);
236
+
237
+ const wrapper = getElement('.k-virtual-wrapper')!;
238
+ const lastVisibleItem = wrapper.lastElementChild!;
239
+ const lastVisibleIndex = parseInt(lastVisibleItem.textContent!.replace('Item ', ''));
240
+
241
+ // delete last 5 items
242
+ const currentList = instance.get('list');
243
+ const newList = currentList.slice(0, -5);
244
+ instance.set('list', newList);
245
+ await wait(50);
246
+
247
+ // check total height is updated
248
+ const finalHeight = parseInt(phantom.style.height);
249
+ expect(finalHeight).to.equal(initialHeight - 5 * 30);
250
+
251
+ // check new last item is at bottom
252
+ const newLastItem = wrapper.lastElementChild!;
253
+ const newLastIndex = parseInt(newLastItem.textContent!.replace('Item ', ''));
254
+ expect(newLastIndex).to.equal(94);
255
+
256
+ // check new last item is at bottom
257
+ const containerRect = container.getBoundingClientRect();
258
+ const lastItemRect = newLastItem.getBoundingClientRect();
259
+ const isAtBottom = Math.abs((containerRect.bottom - lastItemRect.bottom)) <= 1;
260
+ expect(isAtBottom).to.be.true;
261
+ });
262
+ });
263
+
@@ -0,0 +1,5 @@
1
+ export * from './container';
2
+ export * from './wrapper';
3
+ export * from './rows';
4
+ export * from './virtual';
5
+ export * from './phantom';
@@ -0,0 +1,18 @@
1
+ import { Component, TypeDefs } from 'intact';
2
+ import template from './phantom.vdt';
3
+ import { useConfigContext } from '../config';
4
+
5
+ export interface VirtualListPhantomProps {
6
+ tagName?: string;
7
+ }
8
+
9
+ const typeDefs: Required<TypeDefs<VirtualListPhantomProps>> = {
10
+ tagName: String,
11
+ };
12
+
13
+ export class VirtualListPhantom extends Component<VirtualListPhantomProps> {
14
+ static template = template;
15
+ static typeDefs = typeDefs;
16
+
17
+ private config = useConfigContext();
18
+ }