@ray-js/t-agent-ui-ray 0.2.3-beta-1 → 0.2.3-beta-3
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/LazyScrollView/LazyItem/index.d.ts +0 -1
- package/dist/LazyScrollView/LazyItem/index.js +6 -12
- package/dist/LazyScrollView/LazyItem/index.tyml +7 -7
- package/dist/LazyScrollView/get-visible-items.sjs +53 -0
- package/dist/LazyScrollView/index.d.ts +1 -0
- package/dist/LazyScrollView/index.js +18 -1
- package/dist/LazyScrollView/index.tyml +2 -0
- package/dist/LazyScrollView/lazy-item-context.sjs +90 -0
- package/dist/LazyScrollView/lazy-scroll-view.sjs +20 -256
- package/dist/LazyScrollView/ordered-list.sjs +57 -0
- package/dist/LazyScrollView/scroll-view-context.sjs +237 -0
- package/dist/MessageActionBar/back.svg +1 -0
- package/dist/MessageActionBar/delete.svg +1 -0
- package/dist/MessageActionBar/index.d.ts +4 -0
- package/dist/MessageActionBar/index.js +79 -0
- package/dist/MessageActionBar/index.less +68 -0
- package/dist/MessageInput/MessageInputAIStream/index.js +4 -3
- package/dist/MessageInput/MessageInputAssistant/asr.d.ts +30 -0
- package/dist/MessageInput/MessageInputAssistant/asr.js +126 -0
- package/dist/MessageInput/MessageInputAssistant/index.js +5 -2
- package/dist/MessageInput/MessageInputAssistant/useAsrInput.js +1 -1
- package/dist/MessageList/PartRender.d.ts +15 -0
- package/dist/MessageList/PartRender.js +25 -0
- package/dist/MessageList/ScrollBottomView.d.ts +1 -0
- package/dist/MessageList/ScrollBottomView.js +4 -0
- package/dist/MessageList/index.d.ts +3 -5
- package/dist/MessageList/index.js +30 -47
- package/dist/MessageList/index.less +2 -22
- package/dist/MessageRender/index.d.ts +1 -2
- package/dist/MessageRender/index.js +0 -4
- package/dist/hooks/context.js +0 -1
- package/dist/hooks/useLongPress.js +1 -1
- package/dist/i18n/strings.d.ts +48 -16
- package/dist/i18n/strings.js +48 -16
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/utils/abort.d.ts +38 -0
- package/dist/utils/abort.js +177 -0
- package/dist/utils/ttt.d.ts +98 -0
- package/dist/utils/ttt.js +54 -0
- package/package.json +2 -2
- package/dist/LazyScrollView/weak-ref.sjs +0 -45
|
@@ -28,23 +28,17 @@ Component({
|
|
|
28
28
|
type: Number
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
-
data: {
|
|
32
|
-
|
|
31
|
+
data: {},
|
|
32
|
+
relations: {
|
|
33
|
+
'../index': {
|
|
34
|
+
type: 'ancestor',
|
|
35
|
+
unlinked() {}
|
|
36
|
+
}
|
|
33
37
|
},
|
|
34
38
|
lifetimes: {},
|
|
35
39
|
methods: {
|
|
36
40
|
click() {
|
|
37
41
|
this.triggerEvent('click');
|
|
38
|
-
},
|
|
39
|
-
setShow(show) {
|
|
40
|
-
if (show !== this.data.show) {
|
|
41
|
-
this.setData({
|
|
42
|
-
show
|
|
43
|
-
});
|
|
44
|
-
this.triggerEvent('show', {
|
|
45
|
-
show
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
42
|
}
|
|
49
43
|
},
|
|
50
44
|
observers: {}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<sjs src="../lazy-scroll-view.sjs" module="lazyScrollView"></sjs>
|
|
2
2
|
|
|
3
3
|
<view
|
|
4
|
-
change:update="{{lazyScrollView.itemUpdateObserver}}"
|
|
5
|
-
update="{{update}}"
|
|
6
|
-
change:ready="{{lazyScrollView.itemReadyObserver}}"
|
|
7
|
-
ready="{{ready}}"
|
|
8
|
-
change:index="{{lazyScrollView.itemIndexObserver}}"
|
|
9
|
-
index="{{index}}"
|
|
4
|
+
change:data-update="{{lazyScrollView.itemUpdateObserver}}"
|
|
5
|
+
data-update="{{update}}"
|
|
6
|
+
change:data-ready="{{lazyScrollView.itemReadyObserver}}"
|
|
7
|
+
data-ready="{{ready}}"
|
|
8
|
+
change:data-index="{{lazyScrollView.itemIndexObserver}}"
|
|
9
|
+
data-index="{{index}}"
|
|
10
10
|
bind:tap="click"
|
|
11
11
|
data-scroll-view-id="{{scrollViewId}}"
|
|
12
12
|
data-item-id="{{itemId}}"
|
|
13
13
|
class="{{className || ''}}"
|
|
14
14
|
>
|
|
15
|
-
<slot
|
|
15
|
+
<slot></slot>
|
|
16
16
|
</view>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export function getVisibleItems(input) {
|
|
2
|
+
const { sizes, containerSize, offset, overscanCount } = input;
|
|
3
|
+
|
|
4
|
+
if (offset == null || !containerSize || !sizes.length) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const total = sizes.length;
|
|
9
|
+
|
|
10
|
+
// Step 1: 构建 offsets(每个 item 的 top)
|
|
11
|
+
const offsets = [];
|
|
12
|
+
let currTop = 0;
|
|
13
|
+
for (let i = 0; i < total; i++) {
|
|
14
|
+
offsets.push(currTop);
|
|
15
|
+
currTop += sizes[i].height;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Step 2: 二分查找第一个 bottom > visibleTop 的 item
|
|
19
|
+
const visibleTop = offset;
|
|
20
|
+
const visibleBottom = offset + containerSize;
|
|
21
|
+
|
|
22
|
+
let start = 0;
|
|
23
|
+
let end = total - 1;
|
|
24
|
+
let startIndex = total;
|
|
25
|
+
while (start <= end) {
|
|
26
|
+
const mid = Math.floor((start + end) / 2);
|
|
27
|
+
const bottom = offsets[mid] + sizes[mid].height;
|
|
28
|
+
if (bottom >= visibleTop) {
|
|
29
|
+
startIndex = mid;
|
|
30
|
+
end = mid - 1;
|
|
31
|
+
} else {
|
|
32
|
+
start = mid + 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Step 3: 向后遍历直到 top > visibleBottom
|
|
37
|
+
let endIndex = startIndex;
|
|
38
|
+
while (endIndex < total && offsets[endIndex] < visibleBottom) {
|
|
39
|
+
endIndex++;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Step 4: 往前数五个,往后数五个
|
|
43
|
+
const finalStartIndex = Math.max(0, startIndex - overscanCount);
|
|
44
|
+
const finalEndIndex = Math.min(total, endIndex + overscanCount);
|
|
45
|
+
|
|
46
|
+
// Step 5: 构建结果
|
|
47
|
+
const map = Object.create(null);
|
|
48
|
+
for (let i = finalStartIndex; i < finalEndIndex; i++) {
|
|
49
|
+
map[sizes[i].itemId] = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return map;
|
|
53
|
+
}
|
|
@@ -19,6 +19,7 @@ interface Props {
|
|
|
19
19
|
scrollIntoView?: string;
|
|
20
20
|
bindscroll?: (event: { detail: ScrollEventDetail }) => void;
|
|
21
21
|
binddone?: (event: { detail: { scrollIntoView: string } }) => void;
|
|
22
|
+
bindchanged?: (event: { detail: { changes: Record<string, boolean> } }) => void;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
declare function LazyScrollView(props: Props): React.JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "core-js/modules/es.json.stringify.js";
|
|
1
2
|
// eslint-disable-next-line no-undef
|
|
2
3
|
Component({
|
|
3
4
|
options: {
|
|
@@ -20,7 +21,23 @@ Component({
|
|
|
20
21
|
value: ''
|
|
21
22
|
}
|
|
22
23
|
},
|
|
23
|
-
|
|
24
|
+
relations: {
|
|
25
|
+
'./LazyItem/index': {
|
|
26
|
+
type: 'descendant',
|
|
27
|
+
unlinked(target) {
|
|
28
|
+
this.setData({
|
|
29
|
+
op: JSON.stringify({
|
|
30
|
+
type: 'detachItem',
|
|
31
|
+
itemId: target.properties.itemId,
|
|
32
|
+
t: Math.random()
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
data: {
|
|
39
|
+
op: ''
|
|
40
|
+
},
|
|
24
41
|
lifetimes: {},
|
|
25
42
|
methods: {},
|
|
26
43
|
observers: {}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
change:scrollIntoView="{{lazyScrollView.scrollIntoViewObserver}}"
|
|
11
11
|
scrollIntoView="{{scrollIntoView}}"
|
|
12
12
|
bind:scroll="{{lazyScrollView.scroll}}"
|
|
13
|
+
change:data-op="{{lazyScrollView.opObserver}}"
|
|
14
|
+
data-op="{{op}}"
|
|
13
15
|
>
|
|
14
16
|
<slot></slot>
|
|
15
17
|
</scroll-view>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// 为了尽可能防止内存泄漏,LazyItemContext 不引用 ownerInstance、instance
|
|
2
|
+
export class LazyItemContext {
|
|
3
|
+
constructor(prefix) {
|
|
4
|
+
this.prefix = prefix;
|
|
5
|
+
this.show = null; // 是否显示, null 表示未初始化
|
|
6
|
+
this.ready = false;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
emitToScroll(ownerInstance, data) {
|
|
10
|
+
ownerInstance.eventChannel.emit(`${this.prefix}${this.scrollViewId}`, data);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
init(ownerInstance, instance) {
|
|
14
|
+
const { scrollViewId, itemId, index } = instance.getDataset();
|
|
15
|
+
const rect = instance.getBoundingClientRect();
|
|
16
|
+
this.scrollViewId = scrollViewId;
|
|
17
|
+
this.itemId = itemId;
|
|
18
|
+
this.index = index;
|
|
19
|
+
|
|
20
|
+
this.height = rect.height;
|
|
21
|
+
this.eventName = `${this.prefix}${scrollViewId}/${itemId}`;
|
|
22
|
+
|
|
23
|
+
ownerInstance.eventChannel.on(this.eventName, (event) => {
|
|
24
|
+
switch (event.type) {
|
|
25
|
+
case 'refreshSize':
|
|
26
|
+
this._refreshSize(ownerInstance, instance, false);
|
|
27
|
+
ownerInstance.eventChannel.emit(`${this.eventName}/${event.id}`, this.height);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
this.emitToScroll(ownerInstance, {
|
|
33
|
+
type: 'itemAdd',
|
|
34
|
+
itemContext: this,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
refreshSize(ownerInstance) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const id = Math.random()
|
|
41
|
+
ownerInstance.eventChannel.once(`${this.eventName}/${id}`, resolve)
|
|
42
|
+
ownerInstance.eventChannel.emit(this.eventName, { type: 'refreshSize', id });
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_refreshSize(ownerInstance, instance, byUpdate = false) {
|
|
47
|
+
if (this.show === false) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (byUpdate && this.ready && this.show === true) {
|
|
52
|
+
// 为了测量真实的高度,先清除 minHeight
|
|
53
|
+
instance.setStyle({
|
|
54
|
+
minHeight: '',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const rect = instance.getBoundingClientRect();
|
|
59
|
+
this.height = rect.height;
|
|
60
|
+
if (this.ready) {
|
|
61
|
+
instance.setStyle({
|
|
62
|
+
minHeight: `${rect.height}px`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
onUpdate(ownerInstance, instance) {
|
|
68
|
+
this._refreshSize(ownerInstance, instance, true);
|
|
69
|
+
|
|
70
|
+
if (this.ready) {
|
|
71
|
+
this.emitToScroll(ownerInstance,{
|
|
72
|
+
type: 'itemUpdate',
|
|
73
|
+
itemContext: this,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onReady(ownerInstance, instance) {
|
|
79
|
+
this.ready = true;
|
|
80
|
+
this._refreshSize(ownerInstance, instance, false);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
onIndex(ownerInstance, instance, index) {
|
|
84
|
+
this.index = index;
|
|
85
|
+
this.emitToScroll(ownerInstance, {
|
|
86
|
+
type: 'itemIndexChange',
|
|
87
|
+
itemContext: this,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -1,145 +1,18 @@
|
|
|
1
1
|
// ownerInstance:表示的是触发事件的组件所在组件的 ComponentDescriptor 实例。如果触发事件的组件是在页面内的,ownerInstance 表示的是页面实例。
|
|
2
2
|
// instance:表示触发事件的组件的 ComponentDescriptor 实例。
|
|
3
3
|
|
|
4
|
-
const {
|
|
4
|
+
const { ScrollViewContext } = require('./scroll-view-context.sjs');
|
|
5
|
+
const { LazyItemContext } = require('./lazy-item-context.sjs');
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
// scrollViewId -> WeakRef(ScrollViewContext)
|
|
9
|
-
const map = new Map();
|
|
10
|
-
|
|
11
|
-
function getVisibleItemIdsBinary(input) {
|
|
12
|
-
const {
|
|
13
|
-
sizes,
|
|
14
|
-
containerSize,
|
|
15
|
-
offset,
|
|
16
|
-
overscanCount,
|
|
17
|
-
} = input
|
|
18
|
-
|
|
19
|
-
if (!offset || !containerSize || !sizes.length) {
|
|
20
|
-
return null
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const total = sizes.length;
|
|
24
|
-
const avgHeight = sizes.reduce((sum, s) => sum + s.height, 0) / total || 0;
|
|
25
|
-
const overscanHeight = overscanCount * avgHeight;
|
|
26
|
-
|
|
27
|
-
const visibleTop = offset;
|
|
28
|
-
const visibleBottom = offset + containerSize;
|
|
29
|
-
const overscanTop = visibleTop - overscanHeight;
|
|
30
|
-
const overscanBottom = visibleBottom + overscanHeight;
|
|
31
|
-
|
|
32
|
-
// Step 1: 构建 offsets(每个 item 的 top)
|
|
33
|
-
const offsets = [];
|
|
34
|
-
let currTop = 0;
|
|
35
|
-
for (let i = 0; i < total; i++) {
|
|
36
|
-
offsets.push(currTop);
|
|
37
|
-
currTop += sizes[i].height;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Step 2: 二分查找第一个 bottom > overscanTop 的 item
|
|
41
|
-
let start = 0, end = total - 1, startIndex = total;
|
|
42
|
-
while (start <= end) {
|
|
43
|
-
const mid = Math.floor((start + end) / 2);
|
|
44
|
-
const bottom = offsets[mid] + sizes[mid].height;
|
|
45
|
-
if (bottom >= overscanTop) {
|
|
46
|
-
startIndex = mid;
|
|
47
|
-
end = mid - 1;
|
|
48
|
-
} else {
|
|
49
|
-
start = mid + 1;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Step 3: 向后遍历直到 top > overscanBottom
|
|
54
|
-
const visibleMap = Object.create(null);
|
|
55
|
-
|
|
56
|
-
let i = 0;
|
|
57
|
-
while (i < startIndex) {
|
|
58
|
-
i++
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
while (i < total && offsets[i] <= overscanBottom) {
|
|
62
|
-
visibleMap[sizes[i].itemId] = true;
|
|
63
|
-
i++;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return visibleMap
|
|
67
|
-
}
|
|
7
|
+
const prefix = `t-agent-lazy-scroll-view-${Math.random()}@`;
|
|
68
8
|
|
|
69
9
|
const getScrollViewContext = function (ownerInstance, instance) {
|
|
70
10
|
const state = ownerInstance.getState()
|
|
71
11
|
let context = state.scrollViewContext
|
|
72
12
|
if (!context) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
context = {
|
|
77
|
-
scrollViewId,
|
|
78
|
-
height: rect.height,
|
|
79
|
-
width: rect.width,
|
|
80
|
-
overscanCount: 5,
|
|
81
|
-
items: new Map(), // itemId -> WeakRef(ItemContext)
|
|
82
|
-
updateShowPending: false,
|
|
83
|
-
offset: 0,
|
|
84
|
-
lastScrollEventDetail: null,
|
|
85
|
-
lastScrollIntoView: null,
|
|
86
|
-
refresh() {
|
|
87
|
-
const rect = instance.getBoundingClientRect()
|
|
88
|
-
context.height = rect.height
|
|
89
|
-
context.width = rect.width
|
|
90
|
-
},
|
|
91
|
-
refreshSizes() {
|
|
92
|
-
const sizes = [];
|
|
93
|
-
context.items.forEach((ref) => {
|
|
94
|
-
const itemContext = ref.deref()
|
|
95
|
-
if (itemContext) {
|
|
96
|
-
sizes.push(itemContext.data)
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
sizes.sort((a, b) => a.index - b.index); // 按照 top 排序
|
|
100
|
-
context.sizes = sizes;
|
|
101
|
-
},
|
|
102
|
-
updateVisibleItems() {
|
|
103
|
-
if (context.updateShowPending) {
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
context.updateShowPending = true;
|
|
107
|
-
ownerInstance.requestAnimationFrame(() => {
|
|
108
|
-
const visibleMap = getVisibleItemIdsBinary({
|
|
109
|
-
sizes: context.sizes,
|
|
110
|
-
containerSize: context.height,
|
|
111
|
-
offset: context.offset,
|
|
112
|
-
overscanCount: context.overscanCount,
|
|
113
|
-
})
|
|
114
|
-
if (visibleMap) {
|
|
115
|
-
context.items.forEach((ref, itemId) => {
|
|
116
|
-
const itemContext = ref.deref()
|
|
117
|
-
if (itemContext) {
|
|
118
|
-
itemContext.setShow(!!visibleMap[itemId]);
|
|
119
|
-
}
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
context.updateShowPending = false;
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
}
|
|
13
|
+
context = new ScrollViewContext(prefix, 10);
|
|
14
|
+
context.init(ownerInstance, instance)
|
|
126
15
|
state.scrollViewContext = context;
|
|
127
|
-
// 使用 WeakRef 来避免内存泄漏
|
|
128
|
-
map.set(scrollViewId, new WeakRef(context));
|
|
129
|
-
}
|
|
130
|
-
return context;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const getScrollViewContextById = function (scrollViewId) {
|
|
134
|
-
const ref = map.get(scrollViewId);
|
|
135
|
-
if (!ref) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
const context = ref.deref();
|
|
139
|
-
if (!context) {
|
|
140
|
-
console.warn(`ScrollViewContext for scrollViewId ${scrollViewId} has been garbage collected.`);
|
|
141
|
-
map.delete(scrollViewId);
|
|
142
|
-
return null;
|
|
143
16
|
}
|
|
144
17
|
return context;
|
|
145
18
|
}
|
|
@@ -147,89 +20,28 @@ const getScrollViewContextById = function (scrollViewId) {
|
|
|
147
20
|
const scroll = function (event, ownerInstance) {
|
|
148
21
|
const instance = event.instance
|
|
149
22
|
const context = getScrollViewContext(ownerInstance, instance)
|
|
150
|
-
context.
|
|
151
|
-
// 触发父组件的滚动事件
|
|
152
|
-
ownerInstance.triggerEvent('scroll', {
|
|
153
|
-
...event.detail,
|
|
154
|
-
clientHeight: context.height,
|
|
155
|
-
clientWidth: context.width,
|
|
156
|
-
});
|
|
157
|
-
context.lastScrollEventDetail = event.detail;
|
|
158
|
-
context.updateVisibleItems();
|
|
23
|
+
context.onScroll(event, ownerInstance, instance)
|
|
159
24
|
}
|
|
160
25
|
|
|
161
26
|
const getItemContext = function (ownerInstance, instance) {
|
|
162
27
|
const state = ownerInstance.getState()
|
|
163
28
|
let itemContext = state.context
|
|
164
29
|
if (!itemContext) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
itemContext = {
|
|
168
|
-
show: true,
|
|
169
|
-
scrollViewId,
|
|
170
|
-
data: {
|
|
171
|
-
itemId,
|
|
172
|
-
index: null,
|
|
173
|
-
height: rect.height,
|
|
174
|
-
},
|
|
175
|
-
ready: false,
|
|
176
|
-
setShow(show) {
|
|
177
|
-
if (show === itemContext.show || !itemContext.ready) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
itemContext.show = show;
|
|
181
|
-
if (show) {
|
|
182
|
-
ownerInstance.requestAnimationFrame(() => {
|
|
183
|
-
itemContext.refreshSize();
|
|
184
|
-
})
|
|
185
|
-
}
|
|
186
|
-
ownerInstance.callMethod('setShow', show);
|
|
187
|
-
},
|
|
188
|
-
refreshSize(byUpdate = false) {
|
|
189
|
-
if (byUpdate && itemContext.ready && itemContext.show) {
|
|
190
|
-
// 为了测量真实的高度,先清除 minHeight
|
|
191
|
-
instance.setStyle({
|
|
192
|
-
minHeight: '',
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const rect = instance.getBoundingClientRect()
|
|
197
|
-
itemContext.data.height = rect.height;
|
|
198
|
-
if (itemContext.ready) {
|
|
199
|
-
instance.setStyle({
|
|
200
|
-
minHeight: `${rect.height}px`,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const context = getScrollViewContextById(scrollViewId)
|
|
206
|
-
if (context) {
|
|
207
|
-
context.items.set(itemId, new WeakRef(itemContext));
|
|
208
|
-
} else {
|
|
209
|
-
console.warn(`ScrollViewContext not found for scrollViewId: ${scrollViewId}`);
|
|
210
|
-
}
|
|
30
|
+
itemContext = new LazyItemContext(prefix);
|
|
31
|
+
itemContext.init(ownerInstance, instance);
|
|
211
32
|
state.context = itemContext;
|
|
212
33
|
}
|
|
213
34
|
return itemContext;
|
|
214
35
|
}
|
|
215
36
|
|
|
216
37
|
const itemUpdateObserver = function (newValue, oldValue, ownerInstance, instance) {
|
|
217
|
-
|
|
38
|
+
// 没有旧值就表示第一次挂载,跳过,以防止刚挂载上时,由于外部 show === false,导致高度计算为 0。
|
|
39
|
+
if (newValue === oldValue || !oldValue) {
|
|
218
40
|
return;
|
|
219
41
|
}
|
|
220
42
|
|
|
221
43
|
const itemContext = getItemContext(ownerInstance, instance)
|
|
222
|
-
itemContext.
|
|
223
|
-
if (itemContext.ready) {
|
|
224
|
-
ownerInstance.requestAnimationFrame(() => {
|
|
225
|
-
const context = getScrollViewContextById(itemContext.scrollViewId);
|
|
226
|
-
if (context) {
|
|
227
|
-
context.refreshSizes();
|
|
228
|
-
context.updateVisibleItems();
|
|
229
|
-
}
|
|
230
|
-
})
|
|
231
|
-
}
|
|
232
|
-
|
|
44
|
+
itemContext.onUpdate(ownerInstance, instance);
|
|
233
45
|
};
|
|
234
46
|
|
|
235
47
|
const itemReadyObserver = function (newValue, oldValue, ownerInstance, instance) {
|
|
@@ -238,13 +50,7 @@ const itemReadyObserver = function (newValue, oldValue, ownerInstance, instance)
|
|
|
238
50
|
}
|
|
239
51
|
|
|
240
52
|
const itemContext = getItemContext(ownerInstance, instance)
|
|
241
|
-
|
|
242
|
-
// ready 属性变化时,设置 ready 状态
|
|
243
|
-
if (newValue === true) {
|
|
244
|
-
itemContext.ready = true;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
itemContext.refreshSize();
|
|
53
|
+
itemContext.onReady(ownerInstance, instance);
|
|
248
54
|
};
|
|
249
55
|
|
|
250
56
|
const itemIndexObserver = function (newValue, oldValue, ownerInstance, instance) {
|
|
@@ -252,60 +58,17 @@ const itemIndexObserver = function (newValue, oldValue, ownerInstance, instance)
|
|
|
252
58
|
return;
|
|
253
59
|
}
|
|
254
60
|
const itemContext = getItemContext(ownerInstance, instance)
|
|
255
|
-
itemContext.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
61
|
+
itemContext.onIndex(ownerInstance, instance, newValue);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const opObserver = function (opJson, old, ownerInstance, instance) {
|
|
65
|
+
const context = getScrollViewContext(ownerInstance, instance);
|
|
66
|
+
context.doOp(opJson, old, ownerInstance, instance);
|
|
261
67
|
}
|
|
262
68
|
|
|
263
69
|
const scrollIntoViewObserver = function (newValue, oldValue, ownerInstance, instance) {
|
|
264
70
|
const context = getScrollViewContext(ownerInstance, instance)
|
|
265
|
-
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
ownerInstance.requestAnimationFrame(() => {
|
|
270
|
-
ownerInstance.triggerEvent('done', {
|
|
271
|
-
scrollIntoView: newValue,
|
|
272
|
-
})
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
if (!newValue) {
|
|
276
|
-
return
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const last = context.lastScrollIntoView;
|
|
280
|
-
|
|
281
|
-
if (last && last === newValue) {
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
context.lastScrollIntoView = newValue
|
|
286
|
-
|
|
287
|
-
const rect = instance.getBoundingClientRect()
|
|
288
|
-
if (context.lastScrollEventDetail == null) {
|
|
289
|
-
return
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// 这个就是用来滚动到底部的,所以直接计算底部 offset 就行
|
|
293
|
-
|
|
294
|
-
const lastOffset = context.offset
|
|
295
|
-
context.offset = context.lastScrollEventDetail.scrollHeight - rect.height;
|
|
296
|
-
context.refresh()
|
|
297
|
-
context.updateVisibleItems();
|
|
298
|
-
|
|
299
|
-
ownerInstance.triggerEvent('scroll', {
|
|
300
|
-
scrollTop: context.offset,
|
|
301
|
-
deltaX: 0,
|
|
302
|
-
deltaY: context.offset - lastOffset,
|
|
303
|
-
scrollLeft: context.lastScrollEventDetail.scrollLeft,
|
|
304
|
-
scrollHeight: context.lastScrollEventDetail.scrollHeight,
|
|
305
|
-
scrollWidth: context.lastScrollEventDetail.scrollWidth,
|
|
306
|
-
clientHeight: context.height,
|
|
307
|
-
clientWidth: context.width,
|
|
308
|
-
});
|
|
71
|
+
context.onScrollBottom(newValue, oldValue, ownerInstance, instance)
|
|
309
72
|
}
|
|
310
73
|
|
|
311
74
|
export default {
|
|
@@ -313,5 +76,6 @@ export default {
|
|
|
313
76
|
itemUpdateObserver,
|
|
314
77
|
itemReadyObserver,
|
|
315
78
|
scrollIntoViewObserver,
|
|
79
|
+
opObserver,
|
|
316
80
|
scroll
|
|
317
81
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class OrderedList {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.map = new Map(); // 用于快速通过 itemId 查找元素
|
|
4
|
+
this.list = []; // 存储有序元素
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// 添加或更新元素
|
|
8
|
+
upsert(item) {
|
|
9
|
+
const { itemId, index } = item;
|
|
10
|
+
if (!itemId || typeof index !== 'number') {
|
|
11
|
+
throw new Error('Item must have itemId and index');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const exists = this.map.has(itemId);
|
|
15
|
+
if (exists) {
|
|
16
|
+
// 更新旧元素
|
|
17
|
+
const i = this.list.findIndex(el => el.itemId === itemId);
|
|
18
|
+
if (i !== -1) {
|
|
19
|
+
this.list.splice(i, 1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.map.set(itemId, item);
|
|
24
|
+
// 插入并排序(插入后保持 list 有序)
|
|
25
|
+
this._insertSorted(item);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 删除元素
|
|
29
|
+
remove(itemId) {
|
|
30
|
+
if (this.map.has(itemId)) {
|
|
31
|
+
this.map.delete(itemId);
|
|
32
|
+
const i = this.list.findIndex(el => el.itemId === itemId);
|
|
33
|
+
if (i !== -1) {
|
|
34
|
+
this.list.splice(i, 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 获取有序元素
|
|
40
|
+
getAll() {
|
|
41
|
+
return this.list;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_insertSorted(item) {
|
|
45
|
+
// 二分插入,保持排序
|
|
46
|
+
let left = 0, right = this.list.length;
|
|
47
|
+
while (left < right) {
|
|
48
|
+
const mid = (left + right) >> 1;
|
|
49
|
+
if (this.list[mid].index < item.index) {
|
|
50
|
+
left = mid + 1;
|
|
51
|
+
} else {
|
|
52
|
+
right = mid;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.list.splice(left, 0, item);
|
|
56
|
+
}
|
|
57
|
+
}
|