lecodes-viewer 0.19.1 → 0.20.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.
- package/dist-embed/VERSION.json +3 -3
- package/dist-embed/assets/{createViewer2d-D12Wy3VF.js → createViewer2d-Dx-COLjO.js} +1 -1
- package/dist-embed/assets/createViewerLite-DkM-O9BQ.js +852 -0
- package/dist-embed/assets/host-Czj6Nu-U.js +168 -0
- package/dist-embed/assets/{index-CIK8PnUA.js → index-mhLEVWyK.js} +1 -1
- package/dist-embed/assets/{loadCreatorUiNode-UAdhjRTd.js → loadCreatorUiNode-D87bZHrH.js} +1 -1
- package/dist-embed/assets/mapViewImpl-B2JcES8l.js +810 -0
- package/dist-embed/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
- package/dist-embed/embed.js +8 -8
- package/package.json +7 -3
- package/src/host.ts +3 -0
- package/src/ui/creator-ui.wasm +0 -0
- package/src/ui/nativeViewSystem.ts +2 -91
- package/src/ui/presentPlayer.ts +2 -156
- package/src/ui/vListSystem.ts +211 -211
- package/src/viewer/creator-gl.wasm +0 -0
- package/src/viewer2d/creator2d.wasm +0 -0
- package/dist-embed/assets/createViewerLite-CP-WHGLg.js +0 -707
- package/dist-embed/assets/host-CWKB1WDd.js +0 -162
- package/src/assets/banner.png +0 -0
- package/src/assets/image2.png +0 -0
- package/src/assets/landing.mp4 +0 -0
- package/src/assets/neutral_ibl256.ktx +0 -0
- package/src/assets/output.mp4 +0 -0
- package/src/assets/output2.mp4 +0 -0
- package/src/assets/texture.png +0 -0
- package/src/assets/vue.svg +0 -1
package/src/ui/vListSystem.ts
CHANGED
|
@@ -1,212 +1,212 @@
|
|
|
1
|
-
import type { UIScreen } from './UIScreen'
|
|
2
|
-
|
|
3
|
-
type MountedItem = {
|
|
4
|
-
rootPtr: number // detached yoga root айтема
|
|
5
|
-
el: HTMLElement // DOM-корень поддерева
|
|
6
|
-
jsSubtree: any // JS-объект поддерева (для null _id при unmount)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type VListInstance = {
|
|
10
|
-
nodeId: number
|
|
11
|
-
el: HTMLElement
|
|
12
|
-
jsNode: any // VListElement
|
|
13
|
-
screen: UIScreen
|
|
14
|
-
pendingScroll: number | null
|
|
15
|
-
items: Map<string, MountedItem>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const createVListSystem = (module: any) => {
|
|
19
|
-
|
|
20
|
-
const instances = new Map<number, VListInstance>() // yogaId vlist-ноды → instance
|
|
21
|
-
|
|
22
|
-
// ---------- marshaling ----------
|
|
23
|
-
|
|
24
|
-
const withKey = <R>(key: string, fn: (ptr: number) => R): R => {
|
|
25
|
-
const len = module.lengthBytesUTF8(key) + 1
|
|
26
|
-
const p = module._malloc(len)
|
|
27
|
-
module.stringToUTF8(key, p, len)
|
|
28
|
-
try { return fn(p) } finally { module._free(p) }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const withStringArray = <R>(arr: string[], fn: (arrPtr: number) => R): R => {
|
|
32
|
-
const ptrs = arr.map(s => {
|
|
33
|
-
const len = module.lengthBytesUTF8(s) + 1
|
|
34
|
-
const p = module._malloc(len)
|
|
35
|
-
module.stringToUTF8(s, p, len)
|
|
36
|
-
return p
|
|
37
|
-
})
|
|
38
|
-
const arrPtr = module._malloc(Math.max(4 * ptrs.length, 4))
|
|
39
|
-
ptrs.forEach((p, i) => { module.HEAPU32[(arrPtr >> 2) + i] = p })
|
|
40
|
-
try { return fn(arrPtr) } finally {
|
|
41
|
-
ptrs.forEach(p => module._free(p))
|
|
42
|
-
module._free(arrPtr)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const withFloatArray = <R>(arr: number[], fn: (ptr: number) => R): R => {
|
|
47
|
-
const p = module._malloc(Math.max(4 * arr.length, 4))
|
|
48
|
-
arr.forEach((v, i) => { module.HEAPF32[(p >> 2) + i] = v })
|
|
49
|
-
try { return fn(p) } finally { module._free(p) }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const readStringArray = (arrPtr: number, count: number): string[] => {
|
|
53
|
-
const out: string[] = []
|
|
54
|
-
for (let i = 0; i < count; i++) {
|
|
55
|
-
out.push(module.UTF8ToString(module.HEAPU32[(arrPtr >> 2) + i]))
|
|
56
|
-
}
|
|
57
|
-
return out
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ---------- layout-проход по айтемам ----------
|
|
61
|
-
|
|
62
|
-
const refresh = (inst: VListInstance) => {
|
|
63
|
-
for (const [key, item] of inst.items) {
|
|
64
|
-
inst.screen.updateSizesFor(item.el)
|
|
65
|
-
item.el.style.top = withKey(key, p => module._vlistOffsetOf(inst.nodeId, p)) + "px"
|
|
66
|
-
item.el.style.left = "0px"
|
|
67
|
-
}
|
|
68
|
-
const h = module._getContentHeight(inst.nodeId)
|
|
69
|
-
inst.el.style.setProperty("--scroll-height", h + "px")
|
|
70
|
-
inst.el.style.setProperty("--scroll-width", "0px")
|
|
71
|
-
|
|
72
|
-
if (inst.pendingScroll !== null) {
|
|
73
|
-
if (Math.abs(inst.el.scrollTop - inst.pendingScroll) >= 1) {
|
|
74
|
-
inst.el.scrollTop = inst.pendingScroll // высота уже выставлена — кламп не сработает
|
|
75
|
-
}
|
|
76
|
-
inst.pendingScroll = null
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ---------- колбэки из C++ (регистрируются один раз) ----------
|
|
81
|
-
|
|
82
|
-
// g_vlSync
|
|
83
|
-
const syncFunc = module.addFunction(
|
|
84
|
-
(_screenId: number, nodePtr: number, unmountArr: number, unmountCount: number, mountArr: number, mountCount: number) => {
|
|
85
|
-
const inst = instances.get(nodePtr)
|
|
86
|
-
if (!inst) return
|
|
87
|
-
const unmountKeys = readStringArray(unmountArr, unmountCount)
|
|
88
|
-
const mountKeys = readStringArray(mountArr, mountCount)
|
|
89
|
-
|
|
90
|
-
// Контракт: интеграционный слой сносит поддеревья ДО вызова JS _syncWindow
|
|
91
|
-
for (const key of unmountKeys) {
|
|
92
|
-
const item = inst.items.get(key)
|
|
93
|
-
if (!item) continue
|
|
94
|
-
inst.items.delete(key)
|
|
95
|
-
item.el.remove()
|
|
96
|
-
inst.screen.releaseSubtree(item.jsSubtree) // nodeMap-чистка + _id = 0
|
|
97
|
-
withKey(key, p => module._vlistItemUnmounted(nodePtr, p)) // free yoga root
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
inst.jsNode._syncWindow(unmountKeys, mountKeys) // mount: render() → vlistMount()
|
|
101
|
-
refresh(inst)
|
|
102
|
-
}, "viiiiii")
|
|
103
|
-
|
|
104
|
-
// g_vlAdjust — якорение / кламп
|
|
105
|
-
const adjustFunc = module.addFunction((_screenId: number, nodePtr: number, scrollY: number) => {
|
|
106
|
-
const inst = instances.get(nodePtr)
|
|
107
|
-
if (!inst) return
|
|
108
|
-
inst.pendingScroll = scrollY // применим в refresh, после высоты
|
|
109
|
-
}, "viif")
|
|
110
|
-
|
|
111
|
-
// g_vlEdge
|
|
112
|
-
const edgeFunc = module.addFunction((_screenId: number, nodePtr: number, endEdge: number) => {
|
|
113
|
-
const inst = instances.get(nodePtr)
|
|
114
|
-
if (!inst) return
|
|
115
|
-
const listeners = endEdge ? (inst.jsNode.erl ?? []) : (inst.jsNode.strl ?? [])
|
|
116
|
-
for (const cb of listeners) cb()
|
|
117
|
-
}, "viii")
|
|
118
|
-
|
|
119
|
-
module._setVListHandlers(syncFunc, adjustFunc, edgeFunc)
|
|
120
|
-
|
|
121
|
-
// ---------- создание (из UIScreen.addNodeRecursive для type === "vlist") ----------
|
|
122
|
-
|
|
123
|
-
const register = (screen: UIScreen, jsNode: any, el: HTMLElement, yogaId: number) => {
|
|
124
|
-
const inst: VListInstance = { nodeId: yogaId, el, jsNode, screen, items: new Map(), pendingScroll: null }
|
|
125
|
-
instances.set(yogaId, inst)
|
|
126
|
-
|
|
127
|
-
module._vlistInit(yogaId, jsNode._overscan, jsNode._inverted ? 1 : 0,
|
|
128
|
-
jsNode._startThreshold, jsNode._endThreshold)
|
|
129
|
-
|
|
130
|
-
if (jsNode._keys.length > 0) {
|
|
131
|
-
setKeysInternal(yogaId, jsNode._keys, jsNode._estimates)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
el.addEventListener("scroll", () => {
|
|
135
|
-
module._vlistOnScroll(yogaId, el.scrollTop)
|
|
136
|
-
refresh(inst)
|
|
137
|
-
for (const cb of jsNode.sl ?? []) cb(el.scrollTop)
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Вызывается из UIScreen.updateSizes для vlist-элемента (после выставления его размеров)
|
|
142
|
-
const updateLayout = (yogaId: number) => {
|
|
143
|
-
const inst = instances.get(yogaId)
|
|
144
|
-
if (!inst) return
|
|
145
|
-
module._vlistUpdateLayout(yogaId) // внутри может дернуть sync → mount
|
|
146
|
-
refresh(inst)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const unregister = (yogaId: number) => {
|
|
150
|
-
const inst = instances.get(yogaId)
|
|
151
|
-
if (!inst) return
|
|
152
|
-
module._vlistFree(yogaId) // освобождает detached-корни
|
|
153
|
-
instances.delete(yogaId)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const setKeysInternal = (yogaId: number, keys: string[], estimates: number[]) => {
|
|
157
|
-
withStringArray(keys, kp =>
|
|
158
|
-
withFloatArray(estimates, ep =>
|
|
159
|
-
module._vlistSetKeys(yogaId, kp, ep, keys.length)))
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// ---------- API для _creatorUI (зовётся из VListElement) ----------
|
|
163
|
-
|
|
164
|
-
const api = {
|
|
165
|
-
vlistSetKeys: (jsNode: any, keys: string[], estimates: number[]) => {
|
|
166
|
-
setKeysInternal(jsNode._id, keys, estimates)
|
|
167
|
-
},
|
|
168
|
-
vlistInsertKeys: (jsNode: any, index: number, keys: string[], estimates: number[]) => {
|
|
169
|
-
withStringArray(keys, kp =>
|
|
170
|
-
withFloatArray(estimates, ep =>
|
|
171
|
-
module._vlistInsertKeys(jsNode._id, index, kp, ep, keys.length)))
|
|
172
|
-
},
|
|
173
|
-
vlistRemoveKeys: (jsNode: any, keys: string[]) => {
|
|
174
|
-
withStringArray(keys, kp => module._vlistRemoveKeys(jsNode._id, kp, keys.length))
|
|
175
|
-
},
|
|
176
|
-
vlistInvalidate: (jsNode: any, key: string) => {
|
|
177
|
-
withKey(key, p => module._vlistInvalidate(jsNode._id, p))
|
|
178
|
-
},
|
|
179
|
-
// Вызывается из _syncWindow на каждый mount-ключ
|
|
180
|
-
vlistMount: (jsNode: any, key: string, subtree: any) => {
|
|
181
|
-
const inst = instances.get(jsNode._id)
|
|
182
|
-
if (!inst) return
|
|
183
|
-
const rootPtr = withKey(key, p => module._vlistCreateItemRoot(jsNode._id, p))
|
|
184
|
-
const el = inst.screen.buildSubtree(subtree, rootPtr) // addNodeRecursive в detached root
|
|
185
|
-
inst.el.appendChild(el)
|
|
186
|
-
inst.items.set(key, { rootPtr, el, jsSubtree: subtree })
|
|
187
|
-
withKey(key, p => module._vlistItemMounted(jsNode._id, p)) // измерение + кэш + якорение
|
|
188
|
-
},
|
|
189
|
-
command: (jsNode: any, name: string, ...args: any[]) => {
|
|
190
|
-
const inst = instances.get(jsNode._id)
|
|
191
|
-
if (!inst) return
|
|
192
|
-
const behavior = (animated: boolean) => animated ? "smooth" as const : "auto" as const
|
|
193
|
-
switch (name) {
|
|
194
|
-
case "scrollTo":
|
|
195
|
-
inst.el.scrollTo({ top: args[0], behavior: behavior(args[1] ?? true) })
|
|
196
|
-
break
|
|
197
|
-
case "scrollToKey": {
|
|
198
|
-
const off = withKey(args[0], p => module._vlistOffsetOf(inst.nodeId, p))
|
|
199
|
-
if (!isNaN(off)) inst.el.scrollTo({ top: off, behavior: behavior(args[1] ?? true) })
|
|
200
|
-
break
|
|
201
|
-
}
|
|
202
|
-
case "scrollToEnd": {
|
|
203
|
-
const h = module._getContentHeight(inst.nodeId)
|
|
204
|
-
inst.el.scrollTo({ top: h - inst.el.clientHeight, behavior: behavior(args[0] ?? true) })
|
|
205
|
-
break
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return { register, updateLayout, unregister, refresh, api, instances }
|
|
1
|
+
import type { UIScreen } from './UIScreen'
|
|
2
|
+
|
|
3
|
+
type MountedItem = {
|
|
4
|
+
rootPtr: number // detached yoga root айтема
|
|
5
|
+
el: HTMLElement // DOM-корень поддерева
|
|
6
|
+
jsSubtree: any // JS-объект поддерева (для null _id при unmount)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type VListInstance = {
|
|
10
|
+
nodeId: number
|
|
11
|
+
el: HTMLElement
|
|
12
|
+
jsNode: any // VListElement
|
|
13
|
+
screen: UIScreen
|
|
14
|
+
pendingScroll: number | null
|
|
15
|
+
items: Map<string, MountedItem>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const createVListSystem = (module: any) => {
|
|
19
|
+
|
|
20
|
+
const instances = new Map<number, VListInstance>() // yogaId vlist-ноды → instance
|
|
21
|
+
|
|
22
|
+
// ---------- marshaling ----------
|
|
23
|
+
|
|
24
|
+
const withKey = <R>(key: string, fn: (ptr: number) => R): R => {
|
|
25
|
+
const len = module.lengthBytesUTF8(key) + 1
|
|
26
|
+
const p = module._malloc(len)
|
|
27
|
+
module.stringToUTF8(key, p, len)
|
|
28
|
+
try { return fn(p) } finally { module._free(p) }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const withStringArray = <R>(arr: string[], fn: (arrPtr: number) => R): R => {
|
|
32
|
+
const ptrs = arr.map(s => {
|
|
33
|
+
const len = module.lengthBytesUTF8(s) + 1
|
|
34
|
+
const p = module._malloc(len)
|
|
35
|
+
module.stringToUTF8(s, p, len)
|
|
36
|
+
return p
|
|
37
|
+
})
|
|
38
|
+
const arrPtr = module._malloc(Math.max(4 * ptrs.length, 4))
|
|
39
|
+
ptrs.forEach((p, i) => { module.HEAPU32[(arrPtr >> 2) + i] = p })
|
|
40
|
+
try { return fn(arrPtr) } finally {
|
|
41
|
+
ptrs.forEach(p => module._free(p))
|
|
42
|
+
module._free(arrPtr)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const withFloatArray = <R>(arr: number[], fn: (ptr: number) => R): R => {
|
|
47
|
+
const p = module._malloc(Math.max(4 * arr.length, 4))
|
|
48
|
+
arr.forEach((v, i) => { module.HEAPF32[(p >> 2) + i] = v })
|
|
49
|
+
try { return fn(p) } finally { module._free(p) }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const readStringArray = (arrPtr: number, count: number): string[] => {
|
|
53
|
+
const out: string[] = []
|
|
54
|
+
for (let i = 0; i < count; i++) {
|
|
55
|
+
out.push(module.UTF8ToString(module.HEAPU32[(arrPtr >> 2) + i]))
|
|
56
|
+
}
|
|
57
|
+
return out
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------- layout-проход по айтемам ----------
|
|
61
|
+
|
|
62
|
+
const refresh = (inst: VListInstance) => {
|
|
63
|
+
for (const [key, item] of inst.items) {
|
|
64
|
+
inst.screen.updateSizesFor(item.el)
|
|
65
|
+
item.el.style.top = withKey(key, p => module._vlistOffsetOf(inst.nodeId, p)) + "px"
|
|
66
|
+
item.el.style.left = "0px"
|
|
67
|
+
}
|
|
68
|
+
const h = module._getContentHeight(inst.nodeId)
|
|
69
|
+
inst.el.style.setProperty("--scroll-height", h + "px")
|
|
70
|
+
inst.el.style.setProperty("--scroll-width", "0px")
|
|
71
|
+
|
|
72
|
+
if (inst.pendingScroll !== null) {
|
|
73
|
+
if (Math.abs(inst.el.scrollTop - inst.pendingScroll) >= 1) {
|
|
74
|
+
inst.el.scrollTop = inst.pendingScroll // высота уже выставлена — кламп не сработает
|
|
75
|
+
}
|
|
76
|
+
inst.pendingScroll = null
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------- колбэки из C++ (регистрируются один раз) ----------
|
|
81
|
+
|
|
82
|
+
// g_vlSync
|
|
83
|
+
const syncFunc = module.addFunction(
|
|
84
|
+
(_screenId: number, nodePtr: number, unmountArr: number, unmountCount: number, mountArr: number, mountCount: number) => {
|
|
85
|
+
const inst = instances.get(nodePtr)
|
|
86
|
+
if (!inst) return
|
|
87
|
+
const unmountKeys = readStringArray(unmountArr, unmountCount)
|
|
88
|
+
const mountKeys = readStringArray(mountArr, mountCount)
|
|
89
|
+
|
|
90
|
+
// Контракт: интеграционный слой сносит поддеревья ДО вызова JS _syncWindow
|
|
91
|
+
for (const key of unmountKeys) {
|
|
92
|
+
const item = inst.items.get(key)
|
|
93
|
+
if (!item) continue
|
|
94
|
+
inst.items.delete(key)
|
|
95
|
+
item.el.remove()
|
|
96
|
+
inst.screen.releaseSubtree(item.jsSubtree) // nodeMap-чистка + _id = 0
|
|
97
|
+
withKey(key, p => module._vlistItemUnmounted(nodePtr, p)) // free yoga root
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
inst.jsNode._syncWindow(unmountKeys, mountKeys) // mount: render() → vlistMount()
|
|
101
|
+
refresh(inst)
|
|
102
|
+
}, "viiiiii")
|
|
103
|
+
|
|
104
|
+
// g_vlAdjust — якорение / кламп
|
|
105
|
+
const adjustFunc = module.addFunction((_screenId: number, nodePtr: number, scrollY: number) => {
|
|
106
|
+
const inst = instances.get(nodePtr)
|
|
107
|
+
if (!inst) return
|
|
108
|
+
inst.pendingScroll = scrollY // применим в refresh, после высоты
|
|
109
|
+
}, "viif")
|
|
110
|
+
|
|
111
|
+
// g_vlEdge
|
|
112
|
+
const edgeFunc = module.addFunction((_screenId: number, nodePtr: number, endEdge: number) => {
|
|
113
|
+
const inst = instances.get(nodePtr)
|
|
114
|
+
if (!inst) return
|
|
115
|
+
const listeners = endEdge ? (inst.jsNode.erl ?? []) : (inst.jsNode.strl ?? [])
|
|
116
|
+
for (const cb of listeners) cb()
|
|
117
|
+
}, "viii")
|
|
118
|
+
|
|
119
|
+
module._setVListHandlers(syncFunc, adjustFunc, edgeFunc)
|
|
120
|
+
|
|
121
|
+
// ---------- создание (из UIScreen.addNodeRecursive для type === "vlist") ----------
|
|
122
|
+
|
|
123
|
+
const register = (screen: UIScreen, jsNode: any, el: HTMLElement, yogaId: number) => {
|
|
124
|
+
const inst: VListInstance = { nodeId: yogaId, el, jsNode, screen, items: new Map(), pendingScroll: null }
|
|
125
|
+
instances.set(yogaId, inst)
|
|
126
|
+
|
|
127
|
+
module._vlistInit(yogaId, jsNode._overscan, jsNode._inverted ? 1 : 0,
|
|
128
|
+
jsNode._startThreshold, jsNode._endThreshold)
|
|
129
|
+
|
|
130
|
+
if (jsNode._keys.length > 0) {
|
|
131
|
+
setKeysInternal(yogaId, jsNode._keys, jsNode._estimates)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
el.addEventListener("scroll", () => {
|
|
135
|
+
module._vlistOnScroll(yogaId, el.scrollTop)
|
|
136
|
+
refresh(inst)
|
|
137
|
+
for (const cb of jsNode.sl ?? []) cb(el.scrollTop)
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Вызывается из UIScreen.updateSizes для vlist-элемента (после выставления его размеров)
|
|
142
|
+
const updateLayout = (yogaId: number) => {
|
|
143
|
+
const inst = instances.get(yogaId)
|
|
144
|
+
if (!inst) return
|
|
145
|
+
module._vlistUpdateLayout(yogaId) // внутри может дернуть sync → mount
|
|
146
|
+
refresh(inst)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const unregister = (yogaId: number) => {
|
|
150
|
+
const inst = instances.get(yogaId)
|
|
151
|
+
if (!inst) return
|
|
152
|
+
module._vlistFree(yogaId) // освобождает detached-корни
|
|
153
|
+
instances.delete(yogaId)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const setKeysInternal = (yogaId: number, keys: string[], estimates: number[]) => {
|
|
157
|
+
withStringArray(keys, kp =>
|
|
158
|
+
withFloatArray(estimates, ep =>
|
|
159
|
+
module._vlistSetKeys(yogaId, kp, ep, keys.length)))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ---------- API для _creatorUI (зовётся из VListElement) ----------
|
|
163
|
+
|
|
164
|
+
const api = {
|
|
165
|
+
vlistSetKeys: (jsNode: any, keys: string[], estimates: number[]) => {
|
|
166
|
+
setKeysInternal(jsNode._id, keys, estimates)
|
|
167
|
+
},
|
|
168
|
+
vlistInsertKeys: (jsNode: any, index: number, keys: string[], estimates: number[]) => {
|
|
169
|
+
withStringArray(keys, kp =>
|
|
170
|
+
withFloatArray(estimates, ep =>
|
|
171
|
+
module._vlistInsertKeys(jsNode._id, index, kp, ep, keys.length)))
|
|
172
|
+
},
|
|
173
|
+
vlistRemoveKeys: (jsNode: any, keys: string[]) => {
|
|
174
|
+
withStringArray(keys, kp => module._vlistRemoveKeys(jsNode._id, kp, keys.length))
|
|
175
|
+
},
|
|
176
|
+
vlistInvalidate: (jsNode: any, key: string) => {
|
|
177
|
+
withKey(key, p => module._vlistInvalidate(jsNode._id, p))
|
|
178
|
+
},
|
|
179
|
+
// Вызывается из _syncWindow на каждый mount-ключ
|
|
180
|
+
vlistMount: (jsNode: any, key: string, subtree: any) => {
|
|
181
|
+
const inst = instances.get(jsNode._id)
|
|
182
|
+
if (!inst) return
|
|
183
|
+
const rootPtr = withKey(key, p => module._vlistCreateItemRoot(jsNode._id, p))
|
|
184
|
+
const el = inst.screen.buildSubtree(subtree, rootPtr) // addNodeRecursive в detached root
|
|
185
|
+
inst.el.appendChild(el)
|
|
186
|
+
inst.items.set(key, { rootPtr, el, jsSubtree: subtree })
|
|
187
|
+
withKey(key, p => module._vlistItemMounted(jsNode._id, p)) // измерение + кэш + якорение
|
|
188
|
+
},
|
|
189
|
+
command: (jsNode: any, name: string, ...args: any[]) => {
|
|
190
|
+
const inst = instances.get(jsNode._id)
|
|
191
|
+
if (!inst) return
|
|
192
|
+
const behavior = (animated: boolean) => animated ? "smooth" as const : "auto" as const
|
|
193
|
+
switch (name) {
|
|
194
|
+
case "scrollTo":
|
|
195
|
+
inst.el.scrollTo({ top: args[0], behavior: behavior(args[1] ?? true) })
|
|
196
|
+
break
|
|
197
|
+
case "scrollToKey": {
|
|
198
|
+
const off = withKey(args[0], p => module._vlistOffsetOf(inst.nodeId, p))
|
|
199
|
+
if (!isNaN(off)) inst.el.scrollTo({ top: off, behavior: behavior(args[1] ?? true) })
|
|
200
|
+
break
|
|
201
|
+
}
|
|
202
|
+
case "scrollToEnd": {
|
|
203
|
+
const h = module._getContentHeight(inst.nodeId)
|
|
204
|
+
inst.el.scrollTo({ top: h - inst.el.clientHeight, behavior: behavior(args[0] ?? true) })
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { register, updateLayout, unregister, refresh, api, instances }
|
|
212
212
|
}
|
|
File without changes
|
|
File without changes
|