@king-design/intact 3.5.1-beta.0 → 3.5.2
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/components/breadcrumb/demos/separator.md +4 -4
- package/components/breadcrumb/index.vdt +1 -1
- package/components/breadcrumb/styles.ts +3 -2
- package/components/select/index.spec.ts +34 -0
- package/components/table/table.vdt +3 -3
- package/components/tree/useChecked.ts +11 -4
- package/components/treeSelect/index.spec.ts +60 -1
- package/components/treeSelect/useValue.ts +7 -4
- package/components/virtualList/index.spec.ts +161 -0
- package/components/virtualList/rows.vdt +2 -1
- package/components/virtualList/useRows.ts +16 -2
- package/components/virtualList/useVirtualRows.ts +29 -25
- package/es/components/breadcrumb/index.vdt.js +2 -1
- package/es/components/breadcrumb/styles.js +5 -3
- package/es/components/select/index.spec.js +54 -0
- package/es/components/table/table.vdt.js +4 -2
- package/es/components/tree/useChecked.js +10 -4
- package/es/components/treeSelect/index.spec.js +82 -5
- package/es/components/treeSelect/useValue.d.ts +1 -1
- package/es/components/treeSelect/useValue.js +6 -3
- package/es/components/virtualList/index.spec.js +200 -1
- package/es/components/virtualList/rows.vdt.js +2 -1
- package/es/components/virtualList/useRows.js +15 -2
- package/es/components/virtualList/useVirtualRows.js +25 -22
- package/es/hooks/useDocumentClick.js +3 -3
- package/es/index.d.ts +2 -2
- package/es/index.js +2 -2
- package/es/site/data/components/breadcrumb/demos/separator/react.js +7 -5
- package/es/site/data/components/select/demos/virtual/index.js +1 -1
- package/es/site/data/components/select/demos/virtual/react.js +2 -3
- package/es/site/src/pages/resource/index.js +1 -1
- package/es/styles/fonts/iconfont.js +2 -1
- package/es/styles/global.js +2 -1
- package/hooks/useDocumentClick.ts +3 -3
- package/index.ts +2 -2
- package/package.json +1 -1
- package/styles/fonts/iconfont.ts +2 -1
- package/styles/global.ts +2 -1
|
@@ -10,16 +10,16 @@ import {Breadcrumb, BreadcrumbItem, Icon} from 'kpc';
|
|
|
10
10
|
|
|
11
11
|
<Breadcrumb>
|
|
12
12
|
<b:separator>
|
|
13
|
-
<Icon class="ion-ios-arrow-thin-right separator"
|
|
13
|
+
<Icon class="ion-ios-arrow-thin-right separator" />
|
|
14
14
|
</b:separator>
|
|
15
15
|
<BreadcrumbItem to="/">
|
|
16
|
-
<Icon class="ion-home" /> item 1
|
|
16
|
+
<Icon class="ion-home" size="small"/> item 1
|
|
17
17
|
</BreadcrumbItem>
|
|
18
18
|
<BreadcrumbItem to="/components/breadcrumb/">
|
|
19
|
-
<Icon class="ion-earth"
|
|
19
|
+
<Icon class="ion-earth" size="small"/> item 2
|
|
20
20
|
</BreadcrumbItem>
|
|
21
21
|
<BreadcrumbItem>
|
|
22
|
-
<Icon class="ion-planet" /> item 3
|
|
22
|
+
<Icon class="ion-planet" size="small"/> item 3
|
|
23
23
|
</BreadcrumbItem>
|
|
24
24
|
</Breadcrumb>
|
|
25
25
|
```
|
|
@@ -15,7 +15,7 @@ const classNameObj = {
|
|
|
15
15
|
const separatorVNode = (
|
|
16
16
|
<span class={`${k}-breadcrumb-separator`}>
|
|
17
17
|
<template v-if={!$blocks.separator && !separator}>
|
|
18
|
-
<Icon class="k-icon-right" />
|
|
18
|
+
<Icon class="k-icon-right" size="small"/>
|
|
19
19
|
</template>
|
|
20
20
|
<b:separator v-else>{separator}</b:separator>
|
|
21
21
|
</span>
|
|
@@ -5,12 +5,12 @@ import '../../styles/global';
|
|
|
5
5
|
import { cache } from '../utils';
|
|
6
6
|
|
|
7
7
|
const defaults = {
|
|
8
|
-
fontSize: '
|
|
8
|
+
fontSize: '12px',
|
|
9
9
|
get color() { return theme.color.lightBlack },
|
|
10
10
|
get hoverColor() {return theme.color.primary },
|
|
11
11
|
gap: '6px',
|
|
12
12
|
activeFontWeight: 'normal',
|
|
13
|
-
activeColor
|
|
13
|
+
get activeColor() { return theme.color.title },
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
let breadcrumb: typeof defaults;
|
|
@@ -22,6 +22,7 @@ setDefault(() => {
|
|
|
22
22
|
export const makeStyles = cache(function makeStyles(k: string) {
|
|
23
23
|
return css`
|
|
24
24
|
font-size: ${breadcrumb.fontSize};
|
|
25
|
+
line-height: 12px;
|
|
25
26
|
display: flex;
|
|
26
27
|
align-items: center;
|
|
27
28
|
.${k}-breadcrumb-item {
|
|
@@ -328,6 +328,40 @@ describe('Select', () => {
|
|
|
328
328
|
// expect(clear).to.be.null;
|
|
329
329
|
});
|
|
330
330
|
|
|
331
|
+
it('should handle async data correctly', async () => {
|
|
332
|
+
class Demo extends Component<{list: number[]}> {
|
|
333
|
+
static template = `
|
|
334
|
+
const {Select, Option} = this;
|
|
335
|
+
<Select v-model="day" virtual>
|
|
336
|
+
<Option v-for={this.get('list')} value={$value}>
|
|
337
|
+
{$value}
|
|
338
|
+
</Option>
|
|
339
|
+
</Select>
|
|
340
|
+
`;
|
|
341
|
+
static defaults() {
|
|
342
|
+
return {
|
|
343
|
+
list: []
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
private Select = Select;
|
|
347
|
+
private Option = Option;
|
|
348
|
+
}
|
|
349
|
+
const [instance, element] = mount(Demo);
|
|
350
|
+
await wait();
|
|
351
|
+
|
|
352
|
+
// simulate async data loading
|
|
353
|
+
instance.set('list', Array.from({length: 100}, (_, i) => i));
|
|
354
|
+
await wait(100);
|
|
355
|
+
|
|
356
|
+
element.click();
|
|
357
|
+
await wait();
|
|
358
|
+
const wrapper = getElement('.k-virtual-wrapper');
|
|
359
|
+
const phantom = getElement('.k-virtual-phantom');
|
|
360
|
+
|
|
361
|
+
expect(wrapper!.children.length).to.be.equal(10);
|
|
362
|
+
expect(phantom!.style.height).to.be.equal('3000px');
|
|
363
|
+
});
|
|
364
|
+
|
|
331
365
|
// it('should trigger change event correctly', async () => {
|
|
332
366
|
// const spy = sinon.spy();
|
|
333
367
|
|
|
@@ -71,7 +71,8 @@ const hasData = data && data.length;
|
|
|
71
71
|
|
|
72
72
|
const hasFixedLeft = getHasFixedLeft();
|
|
73
73
|
const {getAllCheckedStatus, toggleCheckedAll, getAllStatus, onChangeChecked} = this.checked;
|
|
74
|
-
const allCheckedStatus = getAllCheckedStatus();
|
|
74
|
+
const allCheckedStatus = getAllCheckedStatus();
|
|
75
|
+
const allStatus = getAllStatus();
|
|
75
76
|
const thead = hideHeader ? null : (
|
|
76
77
|
<TableContext.Provider value={{checkType, lastCellKey, lastCellStyle}}>
|
|
77
78
|
<GroupContext.Provider value={{group, onChange: this.onChangeGroup}}>
|
|
@@ -93,7 +94,7 @@ const thead = hideHeader ? null : (
|
|
|
93
94
|
value={allCheckedStatus === AllCheckedStatus.All}
|
|
94
95
|
indeterminate={showIndeterminate && allCheckedStatus == AllCheckedStatus.Indeterminate}
|
|
95
96
|
ev-$change:value={toggleCheckedAll}
|
|
96
|
-
disabled={!hasData}
|
|
97
|
+
disabled={!hasData || allStatus.every(_it => _it.disabled)}
|
|
97
98
|
/>
|
|
98
99
|
</th>
|
|
99
100
|
{$value}
|
|
@@ -114,7 +115,6 @@ const thead = hideHeader ? null : (
|
|
|
114
115
|
|
|
115
116
|
const {getAllKeys} = this.disableRow;
|
|
116
117
|
const {getGrid} = this.merge;
|
|
117
|
-
const allStatus = getAllStatus();
|
|
118
118
|
const allKeys = getAllKeys();
|
|
119
119
|
const colCount = maxCols + (checkType !== 'none' ? 1 : 0);
|
|
120
120
|
const {isExpanded} = this.expandable;
|
|
@@ -3,6 +3,7 @@ import {useState} from '../../hooks/useState';
|
|
|
3
3
|
import {useReceive} from '../../hooks/useReceive';
|
|
4
4
|
import type {Tree} from './';
|
|
5
5
|
import type {Node, DataItem} from './useNodes';
|
|
6
|
+
import { isEqualArray } from '../utils';
|
|
6
7
|
|
|
7
8
|
export function useChecked(getNodes: () => Node<Key>[]) {
|
|
8
9
|
const instance = useInstance() as Tree;
|
|
@@ -38,9 +39,7 @@ export function useChecked(getNodes: () => Node<Key>[]) {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
node.checked = checked;
|
|
41
|
-
|
|
42
|
-
node.indeterminate = false;
|
|
43
|
-
}
|
|
42
|
+
node.indeterminate = false;
|
|
44
43
|
|
|
45
44
|
if (shouldUpdateCheckedKeys) {
|
|
46
45
|
updateCheckedKeys(node);
|
|
@@ -56,6 +55,14 @@ export function useChecked(getNodes: () => Node<Key>[]) {
|
|
|
56
55
|
needRecheckNodes.forEach(node => {
|
|
57
56
|
updateUpward(node);
|
|
58
57
|
});
|
|
58
|
+
|
|
59
|
+
if (instance.get('checkbox')) {
|
|
60
|
+
const oldCheckedKeys = instance.get('checkedKeys');
|
|
61
|
+
const newCheckedKeys = Array.from(checkedKeys);
|
|
62
|
+
if (!isEqualArray(oldCheckedKeys, newCheckedKeys)) {
|
|
63
|
+
instance.set('checkedKeys', newCheckedKeys);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
function updateCheckedKeys(node: Node<Key>) {
|
|
@@ -67,7 +74,7 @@ export function useChecked(getNodes: () => Node<Key>[]) {
|
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
function toggle(node: Node<Key>) {
|
|
70
|
-
const uncorrelated = instance.get('uncorrelated');
|
|
77
|
+
// const uncorrelated = instance.get('uncorrelated');
|
|
71
78
|
updateDownward(node, !node.checked);
|
|
72
79
|
updateUpward(node.parent);
|
|
73
80
|
|
|
@@ -18,10 +18,22 @@ describe('TreeSelect', () => {
|
|
|
18
18
|
const dropdown = getElement('.k-tree-select-menu')!;
|
|
19
19
|
expect(dropdown.innerHTML).to.matchSnapshot();
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
const texts = dropdown.querySelectorAll('.k-tree-text');
|
|
22
|
+
const text1 = texts[0] as HTMLElement;
|
|
23
|
+
const text111 = texts[2] as HTMLElement;
|
|
24
|
+
text1.click();
|
|
25
|
+
await wait();
|
|
22
26
|
expect(instance.get('value')).to.eql('1');
|
|
23
27
|
|
|
24
28
|
await wait(500);
|
|
29
|
+
|
|
30
|
+
element.click();
|
|
31
|
+
text111.click();
|
|
32
|
+
await wait();
|
|
33
|
+
expect(instance.get('value')).to.eql('1.1.1');
|
|
34
|
+
|
|
35
|
+
await wait(500);
|
|
36
|
+
|
|
25
37
|
expect(getElement('.k-tree-select-menu')).to.be.undefined;
|
|
26
38
|
});
|
|
27
39
|
|
|
@@ -110,4 +122,51 @@ describe('TreeSelect', () => {
|
|
|
110
122
|
await wait(500);
|
|
111
123
|
expect(element.querySelector('.k-form-error')).not.to.be.null;
|
|
112
124
|
});
|
|
125
|
+
|
|
126
|
+
it('should handle parent node correctly when all children are checked initially', async () => {
|
|
127
|
+
const [instance, element] = mount(CheckboxDemo);
|
|
128
|
+
|
|
129
|
+
// init values
|
|
130
|
+
instance.set('values', ['2.1.1', '2.1.2']);
|
|
131
|
+
await wait();
|
|
132
|
+
|
|
133
|
+
// because 2.2 is disabled, so it will select the outermost parent node 2
|
|
134
|
+
expect(instance.get('values')).to.eql(['2']);
|
|
135
|
+
|
|
136
|
+
element.click();
|
|
137
|
+
await wait();
|
|
138
|
+
const dropdown = getElement('.k-tree-select-menu')!;
|
|
139
|
+
// verify parent node is selected
|
|
140
|
+
const parentCheckboxes = dropdown.querySelectorAll('.k-checkbox');
|
|
141
|
+
expect(parentCheckboxes[3].classList.contains('k-checked')).to.be.true; // First floor-2
|
|
142
|
+
expect(parentCheckboxes[4].classList.contains('k-checked')).to.be.true; // Second floor-2.1
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should clear parent node state when setting values to empty array', async () => {
|
|
146
|
+
const [instance, element] = mount(CheckboxDemo);
|
|
147
|
+
|
|
148
|
+
// select a parent node first
|
|
149
|
+
instance.set('values', ['2.1.1']);
|
|
150
|
+
await wait();
|
|
151
|
+
|
|
152
|
+
element.click();
|
|
153
|
+
await wait();
|
|
154
|
+
let dropdown = getElement('.k-tree-select-menu')!;
|
|
155
|
+
// verify related nodes are selected
|
|
156
|
+
const checkboxes = dropdown.querySelectorAll('.k-checkbox');
|
|
157
|
+
expect(checkboxes[3].classList.contains('k-indeterminate')).to.be.true; // First floor-2
|
|
158
|
+
expect(checkboxes[4].classList.contains('k-indeterminate')).to.be.true; // Second floor-2.1
|
|
159
|
+
expect(checkboxes[5].classList.contains('k-checkbox')).to.be.true; // Third floor-2.1.1
|
|
160
|
+
// expect(checkboxes[6].classList.contains('k-checked')).to.be.true; // Third floor-2.1.2
|
|
161
|
+
|
|
162
|
+
// set to empty array
|
|
163
|
+
instance.set('values', []);
|
|
164
|
+
await wait();
|
|
165
|
+
|
|
166
|
+
// verify all nodes are cleared selected state
|
|
167
|
+
checkboxes.forEach(checkbox => {
|
|
168
|
+
expect(checkbox.classList.contains('k-checked')).to.be.false;
|
|
169
|
+
expect(checkbox.classList.contains('k-indeterminate')).to.be.false;
|
|
170
|
+
});
|
|
171
|
+
});
|
|
113
172
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {useInstance, Key, createRef} from 'intact';
|
|
1
|
+
import {useInstance, Key, createRef, nextTick} from 'intact';
|
|
2
2
|
import type {TreeSelect} from './';
|
|
3
3
|
import {useState} from '../../hooks/useState';
|
|
4
4
|
import {isNullOrUndefined} from 'intact-shared';
|
|
5
5
|
import type {Tree} from '../tree';
|
|
6
6
|
import type {Node} from '../tree/useNodes';
|
|
7
|
+
import { isEqualArray } from '../utils';
|
|
7
8
|
|
|
8
9
|
export function useValue() {
|
|
9
10
|
const instance = useInstance() as TreeSelect<Key, boolean, boolean>;
|
|
@@ -23,10 +24,12 @@ export function useValue() {
|
|
|
23
24
|
checkedKeys.set(v as Key[]);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function onChangeCheckedKeys() {
|
|
27
|
+
function onChangeCheckedKeys(allKeys: Key[]) {
|
|
28
|
+
checkedKeys.set(allKeys);
|
|
27
29
|
const keys = getAllCheckedKeys();
|
|
28
|
-
instance.
|
|
29
|
-
|
|
30
|
+
if (!isEqualArray(keys, instance.get('value'))) {
|
|
31
|
+
instance.set('value', keys);
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
function getAllCheckedKeys() {
|
|
@@ -13,6 +13,7 @@ describe('VirtualList', () => {
|
|
|
13
13
|
|
|
14
14
|
// check basic structure
|
|
15
15
|
const container = element.querySelector('.k-virtual-container')!;
|
|
16
|
+
await wait();
|
|
16
17
|
expect(container.outerHTML).to.matchSnapshot();
|
|
17
18
|
|
|
18
19
|
const wrapper = element.querySelector('.k-virtual-wrapper')!;
|
|
@@ -259,5 +260,165 @@ describe('VirtualList', () => {
|
|
|
259
260
|
const isAtBottom = Math.abs((containerRect.bottom - lastItemRect.bottom)) <= 1;
|
|
260
261
|
expect(isAtBottom).to.be.true;
|
|
261
262
|
});
|
|
263
|
+
|
|
264
|
+
it('should handle async data correctly', async () => {
|
|
265
|
+
class AsyncDemo extends Component<{list: number[]}> {
|
|
266
|
+
static template = `
|
|
267
|
+
const VirtualList = this.VirtualList;
|
|
268
|
+
<VirtualList style="height: 300px">
|
|
269
|
+
<div v-for={this.get('list')} key={$value}>Item {$value}</div>
|
|
270
|
+
</VirtualList>
|
|
271
|
+
`;
|
|
272
|
+
static defaults() {
|
|
273
|
+
return {
|
|
274
|
+
list: []
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
private VirtualList = VirtualList;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const [instance] = mount(AsyncDemo);
|
|
281
|
+
await wait();
|
|
282
|
+
|
|
283
|
+
const container = getElement('.k-virtual-container')!;
|
|
284
|
+
const wrapper = getElement('.k-virtual-wrapper')!;
|
|
285
|
+
const phantom = getElement('.k-virtual-phantom')!;
|
|
286
|
+
|
|
287
|
+
// check initial state
|
|
288
|
+
expect(wrapper.children.length).to.equal(0);
|
|
289
|
+
expect(phantom.style.height).to.equal('0px');
|
|
290
|
+
|
|
291
|
+
// simulate async data loading
|
|
292
|
+
instance.set('list', Array.from({length: 100}, (_, i) => i));
|
|
293
|
+
await wait(50);
|
|
294
|
+
|
|
295
|
+
expect(wrapper.children.length).to.be.equal(23);
|
|
296
|
+
expect(phantom.style.height).to.be.equal('1800px');
|
|
297
|
+
|
|
298
|
+
// check render content
|
|
299
|
+
expect(wrapper.firstElementChild!.textContent).to.equal('Item 0');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should handle empty data and re-adding data correctly', async () => {
|
|
303
|
+
class EmptyDemo extends Component<{list: number[]}> {
|
|
304
|
+
static template = `
|
|
305
|
+
const VirtualList = this.VirtualList;
|
|
306
|
+
<VirtualList style="height: 300px">
|
|
307
|
+
<div v-for={this.get('list')} key={$value}>Item {$value}</div>
|
|
308
|
+
</VirtualList>
|
|
309
|
+
`;
|
|
310
|
+
static defaults() {
|
|
311
|
+
return {
|
|
312
|
+
list: Array.from({length: 100}, (_, i) => i)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
private VirtualList = VirtualList;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const [instance] = mount(EmptyDemo);
|
|
319
|
+
await wait();
|
|
320
|
+
|
|
321
|
+
const container = getElement('.k-virtual-container')!;
|
|
322
|
+
const wrapper = getElement('.k-virtual-wrapper')!;
|
|
323
|
+
const phantom = getElement('.k-virtual-phantom')!;
|
|
324
|
+
|
|
325
|
+
// record initial state
|
|
326
|
+
const initialHeight = phantom.style.height;
|
|
327
|
+
const initialChildrenCount = wrapper.children.length;
|
|
328
|
+
|
|
329
|
+
// clear data
|
|
330
|
+
instance.set('list', []);
|
|
331
|
+
await wait(50);
|
|
332
|
+
|
|
333
|
+
// check empty state
|
|
334
|
+
expect(wrapper.children.length).to.equal(0);
|
|
335
|
+
expect(phantom.style.height).to.equal('0px');
|
|
336
|
+
|
|
337
|
+
// re-add data
|
|
338
|
+
instance.set('list', Array.from({length: 50}, (_, i) => i));
|
|
339
|
+
await wait(50);
|
|
340
|
+
|
|
341
|
+
// check re-add data state
|
|
342
|
+
expect(wrapper.children.length).to.be.equal(23);
|
|
343
|
+
expect(parseInt(phantom.style.height)).to.be.equal(900);
|
|
344
|
+
expect(wrapper.firstElementChild!.textContent).to.equal('Item 0');
|
|
345
|
+
|
|
346
|
+
// 滚动测试
|
|
347
|
+
// container.scrollTop = 100;
|
|
348
|
+
// await wait(50);
|
|
349
|
+
|
|
350
|
+
// // 检查滚动后的渲染是否正确
|
|
351
|
+
// expect(wrapper.firstElementChild!.textContent).to.not.equal('Item 0');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should handle extreme height differences correctly', async () => {
|
|
355
|
+
class ExtremeHeightDemo extends Component<{list: number[]}> {
|
|
356
|
+
static template = `
|
|
357
|
+
const VirtualList = this.VirtualList;
|
|
358
|
+
<VirtualList style="height: 300px">
|
|
359
|
+
<div v-for={this.get('list')}
|
|
360
|
+
key={$value}
|
|
361
|
+
style={$value < 5 ? 'height: 100px' : 'height: 30px'}
|
|
362
|
+
>
|
|
363
|
+
Item {$value}
|
|
364
|
+
</div>
|
|
365
|
+
</VirtualList>
|
|
366
|
+
`;
|
|
367
|
+
static defaults() {
|
|
368
|
+
return {
|
|
369
|
+
list: Array.from({length: 100}, (_, i) => i)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
private VirtualList = VirtualList;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const [instance] = mount(ExtremeHeightDemo);
|
|
376
|
+
await wait();
|
|
377
|
+
|
|
378
|
+
const container = getElement('.k-virtual-container')!;
|
|
379
|
+
const wrapper = getElement('.k-virtual-wrapper')!;
|
|
380
|
+
const phantom = getElement('.k-virtual-phantom')!;
|
|
381
|
+
|
|
382
|
+
const initialItems = wrapper.children;
|
|
383
|
+
const initialLength = initialItems.length;
|
|
384
|
+
|
|
385
|
+
// check first 5 elements height
|
|
386
|
+
const firstItem = initialItems[0] as HTMLElement;
|
|
387
|
+
expect(firstItem.offsetHeight).to.equal(100);
|
|
388
|
+
|
|
389
|
+
// record initial average height (calculate by total height and render count)
|
|
390
|
+
const initialTotalHeight = parseInt(phantom.style.height);
|
|
391
|
+
const initialAvgHeight = initialTotalHeight / 100;
|
|
392
|
+
expect(initialAvgHeight).to.be.greaterThan(30);
|
|
393
|
+
|
|
394
|
+
// scroll to small height area
|
|
395
|
+
container.scrollTop = 800;
|
|
396
|
+
await wait(50);
|
|
397
|
+
|
|
398
|
+
// check new render elements
|
|
399
|
+
const newItems = wrapper.children;
|
|
400
|
+
const newLength = newItems.length;
|
|
401
|
+
const firstNewItem = newItems[0] as HTMLElement;
|
|
402
|
+
|
|
403
|
+
// check
|
|
404
|
+
// new render elements should be 30px height
|
|
405
|
+
expect(firstNewItem.offsetHeight).to.equal(30);
|
|
406
|
+
|
|
407
|
+
// because average height is smaller, render elements count should be more
|
|
408
|
+
expect(newLength).to.be.greaterThan(initialLength);
|
|
409
|
+
|
|
410
|
+
// check scroll position is correct
|
|
411
|
+
const firstVisibleIndex = parseInt(firstNewItem.textContent!.replace('Item ', ''));
|
|
412
|
+
expect(firstVisibleIndex).to.be.greaterThan(5);
|
|
413
|
+
|
|
414
|
+
container.scrollTop = 1200;
|
|
415
|
+
await wait();
|
|
416
|
+
|
|
417
|
+
const finalItems = wrapper.children;
|
|
418
|
+
const finalFirstItem = finalItems[0] as HTMLElement;
|
|
419
|
+
expect(finalFirstItem.offsetHeight).to.equal(30);
|
|
420
|
+
expect(finalItems.length).to.equal(newLength); // render count should be stable
|
|
421
|
+
});
|
|
262
422
|
});
|
|
263
423
|
|
|
424
|
+
|
|
@@ -8,11 +8,12 @@ const rows = this.rows;
|
|
|
8
8
|
<VirtualRowsContext.Consumer>
|
|
9
9
|
{({ notifyRows, startIndex, length, disabled }) => {
|
|
10
10
|
if (!disabled) {
|
|
11
|
-
children = rows.value.slice(startIndex, startIndex + length);
|
|
12
11
|
notifyRows(rows.value);
|
|
12
|
+
children = rows.value.slice(startIndex, startIndex + length);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
if (isNullOrUndefined(tagName)) {
|
|
16
|
+
if (!children.length) return;
|
|
16
17
|
return createFragment(children, 8 /* ChildrenTypes.HasKeyedChildren */);
|
|
17
18
|
}
|
|
18
19
|
return createVNode(tagName, null, children);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useInstance, VNode, createRef, createFragment } from 'intact';
|
|
1
|
+
import { useInstance, VNode, createRef, createFragment, Children, isText } from 'intact';
|
|
2
2
|
import { VirtualListRows } from './rows';
|
|
3
3
|
|
|
4
4
|
export function useRows() {
|
|
@@ -12,11 +12,25 @@ export function useRows() {
|
|
|
12
12
|
// convert to array if it has only one child
|
|
13
13
|
const childrenType = vNode.childrenType;
|
|
14
14
|
if (childrenType & 2 /* ChildrenTypes.HasVNodeChildren */) {
|
|
15
|
-
|
|
15
|
+
const children = vNode.children as unknown as VNode;
|
|
16
|
+
if (isText(children)) {
|
|
17
|
+
// ignore void and text vnode
|
|
18
|
+
rows.value = [];
|
|
19
|
+
} else {
|
|
20
|
+
rows.value = [children];
|
|
21
|
+
}
|
|
16
22
|
} else if (childrenType & 1 /* ChildrenTypes.HasInvalidChildren */) {
|
|
17
23
|
rows.value = [];
|
|
18
24
|
} else {
|
|
19
25
|
rows.value = vNode.children as VNode[];
|
|
26
|
+
|
|
27
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
28
|
+
rows.value.forEach(row => {
|
|
29
|
+
if (isText(row)) {
|
|
30
|
+
console.warn('VirtualList: Text node can not been used as children.');
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
20
34
|
}
|
|
21
35
|
});
|
|
22
36
|
|
|
@@ -22,46 +22,51 @@ export function useVirtualRows() {
|
|
|
22
22
|
const length = useState(MIN_LENGTH);
|
|
23
23
|
|
|
24
24
|
let calculatedHeight = 0;
|
|
25
|
-
let rowAvgHeight =
|
|
25
|
+
let rowAvgHeight = 30;
|
|
26
26
|
let rows: VNode[] = [];
|
|
27
27
|
function notifyRows(_rows: VNode[]) {
|
|
28
|
+
if (_rows === rows) return;
|
|
29
|
+
|
|
28
30
|
const oldRows = rows;
|
|
29
31
|
const oldLength = rows.length;
|
|
30
32
|
rows = _rows;
|
|
33
|
+
|
|
31
34
|
// diff oldRows, newRows
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
if (!_rows.length) {
|
|
36
|
+
calculatedHeight = 0;
|
|
37
|
+
rowsHeightMap.clear();
|
|
38
|
+
} else {
|
|
39
|
+
const newKeys = new Set(_rows.map(row => row.key));
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < oldLength; i++) {
|
|
42
|
+
const oldKey = oldRows[i].key!;
|
|
43
|
+
if (!newKeys.has(oldKey)) {
|
|
44
|
+
const height = rowsHeightMap.get(oldKey);
|
|
45
|
+
if (!isNullOrUndefined(height)) {
|
|
46
|
+
calculatedHeight -= height;
|
|
47
|
+
rowsHeightMap.delete(oldKey);
|
|
48
|
+
}
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
|
-
|
|
45
|
-
// update rowAvgHeight
|
|
46
|
-
if (rowsHeightMap.size === 0) {
|
|
47
|
-
rowAvgHeight = calculatedHeight = 0;
|
|
48
|
-
} else {
|
|
49
|
-
rowAvgHeight = calculatedHeight / rowsHeightMap.size;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
if (_rows.length < oldLength) {
|
|
53
54
|
const maxStartIndex = Math.max(0, _rows.length - length.value);
|
|
54
55
|
if (startIndex.value > maxStartIndex) {
|
|
55
56
|
startIndex.set(maxStartIndex);
|
|
56
|
-
// 重新计算位置
|
|
57
57
|
nextTick(() => {
|
|
58
58
|
handleScroll();
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
instance.forceUpdate(() => {
|
|
64
|
+
calculateRowsHeight();
|
|
65
|
+
});
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
function calculateRowsHeight() {
|
|
69
|
+
if (!rows.length) return;
|
|
65
70
|
for (
|
|
66
71
|
let i = startIndex.value;
|
|
67
72
|
i < startIndex.value + length.value && i < rows.length;
|
|
@@ -71,7 +76,7 @@ export function useVirtualRows() {
|
|
|
71
76
|
const key = row.key!;
|
|
72
77
|
if (!rowsHeightMap.has(key)) {
|
|
73
78
|
const rowDom = findDomFromVNode(row, true) as HTMLElement;
|
|
74
|
-
const height = rowDom.offsetHeight;
|
|
79
|
+
const height = rowDom.offsetHeight || rowAvgHeight;
|
|
75
80
|
rowsHeightMap.set(key, height);
|
|
76
81
|
calculatedHeight += height;
|
|
77
82
|
}
|
|
@@ -79,6 +84,10 @@ export function useVirtualRows() {
|
|
|
79
84
|
|
|
80
85
|
// use the average height to estimate the row height
|
|
81
86
|
rowAvgHeight = calculatedHeight / rowsHeightMap.size;
|
|
87
|
+
|
|
88
|
+
const containerHeight = containerDom.offsetHeight;
|
|
89
|
+
// calculate the length of rows we should render
|
|
90
|
+
length.set(Math.max(Math.ceil(containerHeight / rowAvgHeight) + BUFFER_SIZE * 2, MIN_LENGTH));
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
watchState(startIndex, () => {
|
|
@@ -89,13 +98,8 @@ export function useVirtualRows() {
|
|
|
89
98
|
onMounted(() => {
|
|
90
99
|
// get contains height
|
|
91
100
|
containerDom = findDomFromVNode(instance.$lastInput!, true) as HTMLElement;
|
|
92
|
-
const containerHeight = containerDom.offsetHeight;
|
|
93
101
|
|
|
94
102
|
calculateRowsHeight();
|
|
95
|
-
|
|
96
|
-
// calculate the length of rows we should render
|
|
97
|
-
length.set(Math.max(Math.ceil(containerHeight / rowAvgHeight) + BUFFER_SIZE * 2, MIN_LENGTH));
|
|
98
|
-
|
|
99
103
|
containerDom.addEventListener('scroll', handleScroll);
|
|
100
104
|
});
|
|
101
105
|
|
|
@@ -6,7 +6,8 @@ import { BreadcrumbItem } from './item';
|
|
|
6
6
|
import { makeStyles } from './styles';
|
|
7
7
|
import { Icon } from '../icon';
|
|
8
8
|
var _$tmp0 = {
|
|
9
|
-
'className': 'k-icon-right'
|
|
9
|
+
'className': 'k-icon-right',
|
|
10
|
+
'size': 'small'
|
|
10
11
|
};
|
|
11
12
|
export default function ($props, $blocks, $__proto__) {
|
|
12
13
|
var _classNameObj;
|
|
@@ -4,7 +4,7 @@ import { theme, setDefault } from '../../styles/theme';
|
|
|
4
4
|
import '../../styles/global';
|
|
5
5
|
import { cache } from '../utils';
|
|
6
6
|
var defaults = {
|
|
7
|
-
fontSize: '
|
|
7
|
+
fontSize: '12px',
|
|
8
8
|
get color() {
|
|
9
9
|
return theme.color.lightBlack;
|
|
10
10
|
},
|
|
@@ -13,7 +13,9 @@ var defaults = {
|
|
|
13
13
|
},
|
|
14
14
|
gap: '6px',
|
|
15
15
|
activeFontWeight: 'normal',
|
|
16
|
-
activeColor
|
|
16
|
+
get activeColor() {
|
|
17
|
+
return theme.color.title;
|
|
18
|
+
}
|
|
17
19
|
};
|
|
18
20
|
var breadcrumb;
|
|
19
21
|
setDefault(function () {
|
|
@@ -23,5 +25,5 @@ setDefault(function () {
|
|
|
23
25
|
makeStyles == null || makeStyles.clearCache();
|
|
24
26
|
});
|
|
25
27
|
export var makeStyles = cache(function makeStyles(k) {
|
|
26
|
-
return /*#__PURE__*/css("font-size:", breadcrumb.fontSize, ";display:flex;align-items:center;.", k, "-breadcrumb-item{&:last-of-type .", k, "-breadcrumb-link{font-weight:", breadcrumb.activeFontWeight, ";color:", breadcrumb.activeColor, ";}}.", k, "-breadcrumb-link,.", k, "-breadcrumb-link a{color:", breadcrumb.color, ";}a.", k, "-breadcrumb-link,.", k, "-breadcrumb-link a{cursor:pointer;&:hover{color:", breadcrumb.hoverColor, ";}}.", k, "-breadcrumb-separator{margin:0 ", breadcrumb.gap, ";}");
|
|
28
|
+
return /*#__PURE__*/css("font-size:", breadcrumb.fontSize, ";line-height:12px;display:flex;align-items:center;.", k, "-breadcrumb-item{&:last-of-type .", k, "-breadcrumb-link{font-weight:", breadcrumb.activeFontWeight, ";color:", breadcrumb.activeColor, ";}}.", k, "-breadcrumb-link,.", k, "-breadcrumb-link a{color:", breadcrumb.color, ";}a.", k, "-breadcrumb-link,.", k, "-breadcrumb-link a{cursor:pointer;&:hover{color:", breadcrumb.hoverColor, ";}}.", k, "-breadcrumb-separator{margin:0 ", breadcrumb.gap, ";}");
|
|
27
29
|
});
|