@uxland/primary-shell 6.0.4 → 6.0.6
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/index.js +1934 -2408
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +113 -149
- package/dist/index.umd.cjs.map +1 -1
- package/dist/internal-plugins/activity-history/activity-history-item/filter/up-filters/handle-add-up-options-from-item.d.ts +9 -0
- package/dist/internal-plugins/activity-history/activity-history-item/filter/up-filters/handle-add-up-options-from-item.test.d.ts +1 -0
- package/dist/internal-plugins/activity-history/activity-history-item/list/UI/timeline/virtual-scroller.d.ts +39 -0
- package/package.json +1 -1
- package/src/internal-plugins/activity-history/activity-history-item/filter/up-filters/handle-add-up-options-from-item.test.ts +237 -0
- package/src/internal-plugins/activity-history/activity-history-item/filter/up-filters/handle-add-up-options-from-item.ts +12 -3
- package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/activity-history-timeline.ts +86 -24
- package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/styles.css +16 -1
- package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/template.ts +48 -37
- package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/virtual-scroller.ts +239 -0
- package/dist/flow-D-0MTYCm.js +0 -511
- package/dist/flow-D-0MTYCm.js.map +0 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
export interface VirtualScrollerOptions<T> {
|
|
2
|
+
items: T[];
|
|
3
|
+
estimatedItemHeight: number;
|
|
4
|
+
bufferSize: number; // percentage of viewport height
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface VirtualScrollerState {
|
|
8
|
+
viewportHeight: number;
|
|
9
|
+
scrollTop: number;
|
|
10
|
+
totalHeight: number;
|
|
11
|
+
visibleRange: { start: number; end: number };
|
|
12
|
+
containerOffset: number;
|
|
13
|
+
visibleItems: any[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class VirtualScroller<T = any> {
|
|
17
|
+
private _itemHeights: Map<number, number> = new Map();
|
|
18
|
+
private _itemOffsets: Map<number, number> = new Map();
|
|
19
|
+
private _totalHeight = 0;
|
|
20
|
+
private _viewportHeight = 0;
|
|
21
|
+
private _scrollTop = 0;
|
|
22
|
+
private _visibleRange = { start: 0, end: 0 };
|
|
23
|
+
private _options: VirtualScrollerOptions<T>;
|
|
24
|
+
|
|
25
|
+
private _viewport?: HTMLElement;
|
|
26
|
+
private _content?: HTMLElement;
|
|
27
|
+
private _resizeObserver?: ResizeObserver;
|
|
28
|
+
private _isScrolling = false;
|
|
29
|
+
private _scrollTimeout?: number;
|
|
30
|
+
|
|
31
|
+
constructor(options: VirtualScrollerOptions<T>) {
|
|
32
|
+
this._options = options;
|
|
33
|
+
this._initializeHeights();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Initialize with estimated heights
|
|
37
|
+
private _initializeHeights() {
|
|
38
|
+
const { items, estimatedItemHeight } = this._options;
|
|
39
|
+
let totalHeight = 0;
|
|
40
|
+
|
|
41
|
+
this._itemHeights.clear();
|
|
42
|
+
this._itemOffsets.clear();
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < items.length; i++) {
|
|
45
|
+
this._itemOffsets.set(i, totalHeight);
|
|
46
|
+
this._itemHeights.set(i, estimatedItemHeight);
|
|
47
|
+
totalHeight += estimatedItemHeight;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this._totalHeight = totalHeight;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Setup DOM references and observers
|
|
54
|
+
setup(viewport: HTMLElement, content: HTMLElement, onStateChange: (state: VirtualScrollerState) => void) {
|
|
55
|
+
this._viewport = viewport;
|
|
56
|
+
this._content = content;
|
|
57
|
+
|
|
58
|
+
// Update viewport height
|
|
59
|
+
this._viewportHeight = viewport.clientHeight;
|
|
60
|
+
|
|
61
|
+
// Set initial content height
|
|
62
|
+
content.style.height = this._totalHeight + 'px';
|
|
63
|
+
|
|
64
|
+
// Calculate initial visible range
|
|
65
|
+
this._updateVisibleRange();
|
|
66
|
+
|
|
67
|
+
// Setup scroll listener
|
|
68
|
+
const onScroll = () => {
|
|
69
|
+
if (!this._viewport) return;
|
|
70
|
+
|
|
71
|
+
this._scrollTop = this._viewport.scrollTop;
|
|
72
|
+
this._isScrolling = true;
|
|
73
|
+
|
|
74
|
+
if (this._scrollTimeout) {
|
|
75
|
+
clearTimeout(this._scrollTimeout);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._scrollTimeout = window.setTimeout(() => {
|
|
79
|
+
this._isScrolling = false;
|
|
80
|
+
// Measure heights after scrolling stops
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
this._measureActualHeights();
|
|
83
|
+
onStateChange(this.getState());
|
|
84
|
+
}, 50);
|
|
85
|
+
}, 150);
|
|
86
|
+
|
|
87
|
+
const oldRange = {...this._visibleRange};
|
|
88
|
+
this._updateVisibleRange();
|
|
89
|
+
|
|
90
|
+
// Only update if visible range changed
|
|
91
|
+
if (Math.abs(oldRange.start - this._visibleRange.start) > 0 ||
|
|
92
|
+
Math.abs(oldRange.end - this._visibleRange.end) > 0) {
|
|
93
|
+
onStateChange(this.getState());
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
viewport.addEventListener('scroll', onScroll);
|
|
98
|
+
|
|
99
|
+
// Setup resize observer
|
|
100
|
+
this._resizeObserver = new ResizeObserver(() => {
|
|
101
|
+
this._viewportHeight = viewport.clientHeight;
|
|
102
|
+
this._updateVisibleRange();
|
|
103
|
+
onStateChange(this.getState());
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this._resizeObserver.observe(viewport);
|
|
107
|
+
|
|
108
|
+
// Return cleanup function
|
|
109
|
+
return () => {
|
|
110
|
+
viewport.removeEventListener('scroll', onScroll);
|
|
111
|
+
this._resizeObserver?.disconnect();
|
|
112
|
+
if (this._scrollTimeout) {
|
|
113
|
+
clearTimeout(this._scrollTimeout);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Update items (when data changes)
|
|
119
|
+
updateItems(items: T[]) {
|
|
120
|
+
this._options.items = items;
|
|
121
|
+
this._initializeHeights();
|
|
122
|
+
|
|
123
|
+
if (this._content) {
|
|
124
|
+
this._content.style.height = this._totalHeight + 'px';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this._updateVisibleRange();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Measure actual heights of rendered items
|
|
131
|
+
private _measureActualHeights() {
|
|
132
|
+
if (!this._content || this._isScrolling) return;
|
|
133
|
+
|
|
134
|
+
const visitElements = this._content.querySelectorAll('.visit');
|
|
135
|
+
if (visitElements.length === 0) return;
|
|
136
|
+
|
|
137
|
+
let hasChanges = false;
|
|
138
|
+
|
|
139
|
+
visitElements.forEach((element, index) => {
|
|
140
|
+
const actualHeight = element.getBoundingClientRect().height;
|
|
141
|
+
const globalIndex = this._visibleRange.start + index;
|
|
142
|
+
const currentHeight = this._itemHeights.get(globalIndex) || 0;
|
|
143
|
+
|
|
144
|
+
// Only update if there's a significant difference
|
|
145
|
+
if (Math.abs(actualHeight - currentHeight) > 5) {
|
|
146
|
+
hasChanges = true;
|
|
147
|
+
this._itemHeights.set(globalIndex, actualHeight);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (hasChanges) {
|
|
152
|
+
// Recalculate offsets and total height
|
|
153
|
+
let totalHeight = 0;
|
|
154
|
+
for (let i = 0; i < this._options.items.length; i++) {
|
|
155
|
+
this._itemOffsets.set(i, totalHeight);
|
|
156
|
+
const height = this._itemHeights.get(i) || this._options.estimatedItemHeight;
|
|
157
|
+
totalHeight += height;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this._totalHeight = totalHeight;
|
|
161
|
+
if (this._content) {
|
|
162
|
+
this._content.style.height = this._totalHeight + 'px';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Calculate visible range based on scroll position
|
|
168
|
+
private _updateVisibleRange() {
|
|
169
|
+
if (!this._options.items.length || this._viewportHeight === 0) return;
|
|
170
|
+
|
|
171
|
+
const buffer = Math.floor(this._viewportHeight * (this._options.bufferSize / 100));
|
|
172
|
+
const startOffset = Math.max(0, this._scrollTop - buffer);
|
|
173
|
+
const endOffset = this._scrollTop + this._viewportHeight + buffer;
|
|
174
|
+
|
|
175
|
+
let start = 0;
|
|
176
|
+
let end = this._options.items.length - 1;
|
|
177
|
+
|
|
178
|
+
// Find start index
|
|
179
|
+
for (let i = 0; i < this._options.items.length; i++) {
|
|
180
|
+
const offset = this._itemOffsets.get(i) || 0;
|
|
181
|
+
if (offset >= startOffset) {
|
|
182
|
+
start = Math.max(0, i - 1);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Find end index
|
|
188
|
+
for (let i = start; i < this._options.items.length; i++) {
|
|
189
|
+
const offset = this._itemOffsets.get(i) || 0;
|
|
190
|
+
if (offset > endOffset) {
|
|
191
|
+
end = i;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this._visibleRange = { start, end };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Scroll to specific item index
|
|
200
|
+
scrollToIndex(index: number, behavior: ScrollBehavior = 'smooth') {
|
|
201
|
+
if (!this._viewport) return;
|
|
202
|
+
|
|
203
|
+
const offset = this._itemOffsets.get(index) || 0;
|
|
204
|
+
this._viewport.scrollTo({
|
|
205
|
+
top: offset,
|
|
206
|
+
behavior
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Get current state for rendering
|
|
211
|
+
getState(): VirtualScrollerState {
|
|
212
|
+
return {
|
|
213
|
+
viewportHeight: this._viewportHeight,
|
|
214
|
+
scrollTop: this._scrollTop,
|
|
215
|
+
totalHeight: this._totalHeight,
|
|
216
|
+
visibleRange: this._visibleRange,
|
|
217
|
+
containerOffset: this._itemOffsets.get(this._visibleRange.start) || 0,
|
|
218
|
+
visibleItems: this._options.items.slice(this._visibleRange.start, this._visibleRange.end + 1)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Find closest item to a specific condition
|
|
223
|
+
findClosestIndex<K>(getValue: (item: T) => K, targetValue: K, compare: (a: K, b: K) => number): number {
|
|
224
|
+
let closestIndex = -1;
|
|
225
|
+
let closestDifference = Number.POSITIVE_INFINITY;
|
|
226
|
+
|
|
227
|
+
this._options.items.forEach((item, index) => {
|
|
228
|
+
const value = getValue(item);
|
|
229
|
+
const difference = Math.abs(compare(value, targetValue));
|
|
230
|
+
|
|
231
|
+
if (difference < closestDifference) {
|
|
232
|
+
closestDifference = difference;
|
|
233
|
+
closestIndex = index;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return closestIndex;
|
|
238
|
+
}
|
|
239
|
+
}
|