@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
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { Store } from '@reduxjs/toolkit';
|
|
2
2
|
import { IActivityHistoryItem } from '../../domain/model';
|
|
3
3
|
|
|
4
|
+
export declare const getUpFilter: (items: IActivityHistoryItem[]) => {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
values: {
|
|
8
|
+
label: any;
|
|
9
|
+
value: any;
|
|
10
|
+
}[];
|
|
11
|
+
enabledValues: never[];
|
|
12
|
+
} | undefined;
|
|
4
13
|
export declare const handleAddUpFilter: (items: IActivityHistoryItem[], store: Store) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface VirtualScrollerOptions<T> {
|
|
2
|
+
items: T[];
|
|
3
|
+
estimatedItemHeight: number;
|
|
4
|
+
bufferSize: number;
|
|
5
|
+
}
|
|
6
|
+
export interface VirtualScrollerState {
|
|
7
|
+
viewportHeight: number;
|
|
8
|
+
scrollTop: number;
|
|
9
|
+
totalHeight: number;
|
|
10
|
+
visibleRange: {
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
};
|
|
14
|
+
containerOffset: number;
|
|
15
|
+
visibleItems: any[];
|
|
16
|
+
}
|
|
17
|
+
export declare class VirtualScroller<T = any> {
|
|
18
|
+
private _itemHeights;
|
|
19
|
+
private _itemOffsets;
|
|
20
|
+
private _totalHeight;
|
|
21
|
+
private _viewportHeight;
|
|
22
|
+
private _scrollTop;
|
|
23
|
+
private _visibleRange;
|
|
24
|
+
private _options;
|
|
25
|
+
private _viewport?;
|
|
26
|
+
private _content?;
|
|
27
|
+
private _resizeObserver?;
|
|
28
|
+
private _isScrolling;
|
|
29
|
+
private _scrollTimeout?;
|
|
30
|
+
constructor(options: VirtualScrollerOptions<T>);
|
|
31
|
+
private _initializeHeights;
|
|
32
|
+
setup(viewport: HTMLElement, content: HTMLElement, onStateChange: (state: VirtualScrollerState) => void): () => void;
|
|
33
|
+
updateItems(items: T[]): void;
|
|
34
|
+
private _measureActualHeights;
|
|
35
|
+
private _updateVisibleRange;
|
|
36
|
+
scrollToIndex(index: number, behavior?: ScrollBehavior): void;
|
|
37
|
+
getState(): VirtualScrollerState;
|
|
38
|
+
findClosestIndex<K>(getValue: (item: T) => K, targetValue: K, compare: (a: K, b: K) => number): number;
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { getUpFilter } from "./handle-add-up-options-from-item";
|
|
3
|
+
|
|
4
|
+
// Mock de las dependencias
|
|
5
|
+
const mockMapArrayToLabelValue = vi.fn((arr, descField, idField) =>
|
|
6
|
+
arr.map(item => ({
|
|
7
|
+
label: item[descField] || item[idField], // Usa el ID como fallback si description está vacía
|
|
8
|
+
value: item[idField]
|
|
9
|
+
}))
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
vi.mock('./path/to/your/utils', () => ({
|
|
13
|
+
mapArrayToLabelValue: mockMapArrayToLabelValue,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const upFilterId = 'history-up'; // Define el valor que uses en tu código
|
|
17
|
+
|
|
18
|
+
describe('getUpFilter', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
it('debe retornar filtro cuando hay UPs válidos con ID', () => {
|
|
23
|
+
const items = [
|
|
24
|
+
{
|
|
25
|
+
id: '1',
|
|
26
|
+
up: [
|
|
27
|
+
{ id: 'up1', description: 'UP 1' },
|
|
28
|
+
{ id: 'up2', description: 'UP 2' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const result = getUpFilter(items);
|
|
34
|
+
|
|
35
|
+
expect(result).toEqual({
|
|
36
|
+
id: upFilterId,
|
|
37
|
+
title: 'UP',
|
|
38
|
+
values: [
|
|
39
|
+
{ label: 'UP 1', value: 'up1' },
|
|
40
|
+
{ label: 'UP 2', value: 'up2' },
|
|
41
|
+
],
|
|
42
|
+
enabledValues: [],
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('debe permitir UPs con description vacía pero ID válido', () => {
|
|
47
|
+
const items = [
|
|
48
|
+
{
|
|
49
|
+
id: '1',
|
|
50
|
+
up: [
|
|
51
|
+
{ id: 'up1', description: '' },
|
|
52
|
+
{ id: 'up2', description: null },
|
|
53
|
+
{ id: 'up3', description: undefined },
|
|
54
|
+
{ id: 'up4', description: 'UP 4' },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const result = getUpFilter(items);
|
|
60
|
+
|
|
61
|
+
expect(result).toEqual({
|
|
62
|
+
id: upFilterId,
|
|
63
|
+
title: 'UP',
|
|
64
|
+
values: [
|
|
65
|
+
{ label: 'up1', value: 'up1' },
|
|
66
|
+
{ label: 'up2', value: 'up2' },
|
|
67
|
+
{ label: 'up3', value: 'up3' },
|
|
68
|
+
{ label: 'UP 4', value: 'up4' },
|
|
69
|
+
],
|
|
70
|
+
enabledValues: [],
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('no debe incluir UPs con ID vacío, null, undefined o solo espacios', () => {
|
|
75
|
+
const items = [
|
|
76
|
+
{
|
|
77
|
+
id: '1',
|
|
78
|
+
up: [
|
|
79
|
+
{ id: '', description: 'UP con ID vacío' },
|
|
80
|
+
{ id: null, description: 'UP con ID null' },
|
|
81
|
+
{ id: undefined, description: 'UP con ID undefined' },
|
|
82
|
+
{ id: ' ', description: 'UP con ID solo espacios' },
|
|
83
|
+
{ id: '\t\n ', description: 'UP con ID espacios y tabs' },
|
|
84
|
+
{ id: 'up1', description: 'UP válido' },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const result = getUpFilter(items);
|
|
90
|
+
|
|
91
|
+
expect(result).toEqual({
|
|
92
|
+
id: upFilterId,
|
|
93
|
+
title: 'UP',
|
|
94
|
+
values: [{ label: 'UP válido', value: 'up1' }],
|
|
95
|
+
enabledValues: [],
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('debe filtrar UPs duplicados por ID', () => {
|
|
100
|
+
const items = [
|
|
101
|
+
{
|
|
102
|
+
id: '1',
|
|
103
|
+
up: [
|
|
104
|
+
{ id: 'up1', description: 'UP 1 - Primera aparición' },
|
|
105
|
+
{ id: 'up2', description: 'UP 2' },
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: '2',
|
|
110
|
+
up: [
|
|
111
|
+
{ id: 'up1', description: 'UP 1 - Duplicado' },
|
|
112
|
+
{ id: 'up3', description: 'UP 3' },
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const result = getUpFilter(items);
|
|
118
|
+
|
|
119
|
+
expect(result).toEqual({
|
|
120
|
+
id: upFilterId,
|
|
121
|
+
title: 'UP',
|
|
122
|
+
values: [
|
|
123
|
+
{ label: 'UP 1 - Primera aparición', value: 'up1' },
|
|
124
|
+
{ label: 'UP 2', value: 'up2' },
|
|
125
|
+
{ label: 'UP 3', value: 'up3' },
|
|
126
|
+
],
|
|
127
|
+
enabledValues: [],
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('debe manejar items sin propiedad up', () => {
|
|
132
|
+
const items = [
|
|
133
|
+
{ id: '1' },
|
|
134
|
+
{ id: '2', up: null },
|
|
135
|
+
{ id: '3', up: undefined },
|
|
136
|
+
{ id: '4', up: [{ id: 'up1', description: 'UP 1' }] },
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
const result = getUpFilter(items);
|
|
140
|
+
|
|
141
|
+
expect(result).toEqual({
|
|
142
|
+
id: upFilterId,
|
|
143
|
+
title: 'UP',
|
|
144
|
+
values: [{ label: 'UP 1', value: 'up1' }],
|
|
145
|
+
enabledValues: [],
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('debe manejar array up vacío', () => {
|
|
150
|
+
const items = [
|
|
151
|
+
{ id: '1', up: [] },
|
|
152
|
+
{ id: '2', up: [{ id: 'up1', description: 'UP 1' }] },
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
const result = getUpFilter(items);
|
|
156
|
+
|
|
157
|
+
expect(result).toEqual({
|
|
158
|
+
id: upFilterId,
|
|
159
|
+
title: 'UP',
|
|
160
|
+
values: [{ label: 'UP 1', value: 'up1' }],
|
|
161
|
+
enabledValues: [],
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('debe retornar undefined cuando no hay UPs válidos', () => {
|
|
166
|
+
const items = [
|
|
167
|
+
{
|
|
168
|
+
id: '1',
|
|
169
|
+
up: [
|
|
170
|
+
{ id: '', description: 'UP con ID vacío' },
|
|
171
|
+
{ id: null, description: 'UP con ID null' },
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const result = getUpFilter(items);
|
|
177
|
+
|
|
178
|
+
expect(result).toBeUndefined();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('debe retornar undefined cuando items está vacío', () => {
|
|
182
|
+
const items = [];
|
|
183
|
+
|
|
184
|
+
const result = getUpFilter(items);
|
|
185
|
+
|
|
186
|
+
expect(result).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('debe manejar objetos up que son null o undefined en el array', () => {
|
|
190
|
+
const items = [
|
|
191
|
+
{
|
|
192
|
+
id: '1',
|
|
193
|
+
up: [
|
|
194
|
+
null,
|
|
195
|
+
undefined,
|
|
196
|
+
{ id: 'up1', description: 'UP válido' },
|
|
197
|
+
null,
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const result = getUpFilter(items);
|
|
203
|
+
|
|
204
|
+
expect(result).toEqual({
|
|
205
|
+
id: upFilterId,
|
|
206
|
+
title: 'UP',
|
|
207
|
+
values: [{ label: 'UP válido', value: 'up1' }],
|
|
208
|
+
enabledValues: [],
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('debe rechazar IDs que son solo espacios en blanco', () => {
|
|
213
|
+
const items = [
|
|
214
|
+
{
|
|
215
|
+
id: '1',
|
|
216
|
+
up: [
|
|
217
|
+
{ id: ' ', description: 'UP con ID solo espacios' },
|
|
218
|
+
{ id: '\t\n \r', description: 'UP con tabs y saltos' },
|
|
219
|
+
{ id: 'up1', description: 'UP válido' },
|
|
220
|
+
{ id: ' up2 ', description: 'UP válido con espacios al inicio/final' },
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const result = getUpFilter(items);
|
|
226
|
+
|
|
227
|
+
expect(result).toEqual({
|
|
228
|
+
id: upFilterId,
|
|
229
|
+
title: 'UP',
|
|
230
|
+
values: [
|
|
231
|
+
{ label: 'UP válido', value: 'up1' },
|
|
232
|
+
{ label: 'UP válido con espacios al inicio/final', value: ' up2 ' },
|
|
233
|
+
],
|
|
234
|
+
enabledValues: [],
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|
|
@@ -4,8 +4,11 @@ import { IActivityHistoryItem } from "../../domain/model";
|
|
|
4
4
|
import { upFilterId } from "../model";
|
|
5
5
|
import { addUpFilters } from "./add/actions";
|
|
6
6
|
|
|
7
|
-
export const
|
|
8
|
-
const allUps = items
|
|
7
|
+
export const getUpFilter = (items: IActivityHistoryItem[]) => {
|
|
8
|
+
const allUps = items
|
|
9
|
+
.flatMap((item) => item.up ?? [])
|
|
10
|
+
.filter((up) => up?.id?.trim()) // Filtrar UPs que tienen ID válido (no vacío ni solo espacios)
|
|
11
|
+
.filter((up, index, self) => index === self.findIndex((u) => u.id === up.id)); // Filtrar duplicados
|
|
9
12
|
|
|
10
13
|
if (allUps.length > 0) {
|
|
11
14
|
const newUpFilter = {
|
|
@@ -15,6 +18,12 @@ export const handleAddUpFilter = (items: IActivityHistoryItem[], store: Store) =
|
|
|
15
18
|
enabledValues: [],
|
|
16
19
|
};
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
return newUpFilter;
|
|
19
22
|
}
|
|
20
23
|
};
|
|
24
|
+
|
|
25
|
+
export const handleAddUpFilter = (items: IActivityHistoryItem[], store: Store) => {
|
|
26
|
+
const newUpFilter = getUpFilter(items);
|
|
27
|
+
if (newUpFilter)
|
|
28
|
+
store.dispatch(addUpFilters(newUpFilter));
|
|
29
|
+
};
|
|
@@ -8,8 +8,7 @@ import styles from "./styles.css?inline";
|
|
|
8
8
|
import { template } from "./template";
|
|
9
9
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
10
10
|
import { isSomeCollectionLoadedAndOtherLoading, isCollectionInitialState } from "../../../add/handle-busy/selectors";
|
|
11
|
-
import {
|
|
12
|
-
import "@lit-labs/virtualizer";
|
|
11
|
+
import { VirtualScroller, VirtualScrollerState } from "./virtual-scroller";
|
|
13
12
|
|
|
14
13
|
const createZIndexStyles = (max: number) => {
|
|
15
14
|
let styles = "";
|
|
@@ -50,11 +49,27 @@ export class ActivityHistoryTimeline extends LitElement {
|
|
|
50
49
|
@connectedProperty(activityHistorySearchStringSelector)
|
|
51
50
|
searchString: string;
|
|
52
51
|
|
|
53
|
-
@query(".
|
|
52
|
+
@query(".virtual-container") private _virtualContainer!: HTMLElement;
|
|
53
|
+
@query(".virtual-viewport") private _viewport!: HTMLElement;
|
|
54
|
+
@query(".virtual-content") private _content!: HTMLElement;
|
|
54
55
|
|
|
55
56
|
@state()
|
|
56
57
|
renderGroupsControlTimeExpired = false;
|
|
57
58
|
|
|
59
|
+
// Virtual scroller instance and state
|
|
60
|
+
private _virtualScroller?: VirtualScroller<IActivityHistoryGroup>;
|
|
61
|
+
private _cleanupVirtualScroller?: () => void;
|
|
62
|
+
|
|
63
|
+
@state()
|
|
64
|
+
_virtualScrollerState: VirtualScrollerState = {
|
|
65
|
+
viewportHeight: 0,
|
|
66
|
+
scrollTop: 0,
|
|
67
|
+
totalHeight: 0,
|
|
68
|
+
visibleRange: { start: 0, end: 0 },
|
|
69
|
+
containerOffset: 0,
|
|
70
|
+
visibleItems: []
|
|
71
|
+
};
|
|
72
|
+
|
|
58
73
|
connectedCallback(): void {
|
|
59
74
|
super.connectedCallback();
|
|
60
75
|
setTimeout(() => {
|
|
@@ -64,12 +79,19 @@ export class ActivityHistoryTimeline extends LitElement {
|
|
|
64
79
|
|
|
65
80
|
disconnectedCallback(): void {
|
|
66
81
|
super.disconnectedCallback();
|
|
82
|
+
this._cleanupVirtualScroller?.();
|
|
67
83
|
}
|
|
68
84
|
|
|
69
85
|
async updated(changedProperties) {
|
|
70
86
|
if (changedProperties.has("historyGroups") && this.historyGroups?.length) {
|
|
71
87
|
// Resolver los elementos antes de actualizar el template
|
|
72
88
|
await this.prepareComponents(this.historyGroups);
|
|
89
|
+
this._updateVirtualScroller();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Setup virtualizer when DOM elements are available
|
|
93
|
+
if (this._viewport && this._content && !this._virtualScroller) {
|
|
94
|
+
setTimeout(() => this._setupVirtualScroller(), 0);
|
|
73
95
|
}
|
|
74
96
|
}
|
|
75
97
|
|
|
@@ -93,7 +115,6 @@ export class ActivityHistoryTimeline extends LitElement {
|
|
|
93
115
|
);
|
|
94
116
|
}
|
|
95
117
|
}
|
|
96
|
-
this.requestUpdate(); // 🔥 Forzar actualización del template
|
|
97
118
|
}
|
|
98
119
|
|
|
99
120
|
highlightMatch(text: string, searchString: string): string {
|
|
@@ -106,29 +127,70 @@ export class ActivityHistoryTimeline extends LitElement {
|
|
|
106
127
|
return text ? unsafeHTML(this.highlightMatch(text, this.searchString)) : null;
|
|
107
128
|
}
|
|
108
129
|
|
|
130
|
+
// Public API for scrolling to date (maintains original functionality)
|
|
109
131
|
_scrollIntoDate(selectedDate: number) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
if (!this._virtualScroller) return;
|
|
133
|
+
|
|
134
|
+
const closestIndex = this._virtualScroller.findClosestIndex(
|
|
135
|
+
(group) => {
|
|
136
|
+
const dateStr = group.items?.[0]?.date || group.subGroups?.[0]?.items?.[0]?.date;
|
|
137
|
+
return dateStr ? new Date(dateStr).getTime() : 0;
|
|
138
|
+
},
|
|
139
|
+
selectedDate,
|
|
140
|
+
(a, b) => a - b
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (closestIndex !== -1) {
|
|
144
|
+
this._virtualScroller.scrollToIndex(closestIndex);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
116
147
|
|
|
117
|
-
|
|
118
|
-
|
|
148
|
+
// === VIRTUAL SCROLLER LOGIC ===
|
|
149
|
+
|
|
150
|
+
private _setupVirtualScroller() {
|
|
151
|
+
if (!this._viewport || !this._content || this._virtualScroller) return;
|
|
152
|
+
|
|
153
|
+
this._virtualScroller = new VirtualScroller({
|
|
154
|
+
items: this.historyGroups || [],
|
|
155
|
+
estimatedItemHeight: 200,
|
|
156
|
+
bufferSize: 50 // 50% buffer
|
|
157
|
+
});
|
|
119
158
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
159
|
+
this._cleanupVirtualScroller = this._virtualScroller.setup(
|
|
160
|
+
this._viewport,
|
|
161
|
+
this._content,
|
|
162
|
+
(state) => {
|
|
163
|
+
this._virtualScrollerState = state;
|
|
164
|
+
this.requestUpdate();
|
|
123
165
|
}
|
|
124
|
-
|
|
166
|
+
);
|
|
125
167
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
// Get initial state
|
|
169
|
+
this._virtualScrollerState = this._virtualScroller.getState();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private _updateVirtualScroller() {
|
|
173
|
+
if (!this._virtualScroller || !this.historyGroups?.length) return;
|
|
174
|
+
|
|
175
|
+
this._virtualScroller.updateItems(this.historyGroups);
|
|
176
|
+
this._virtualScrollerState = this._virtualScroller.getState();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Getters for template (maintaining original API)
|
|
180
|
+
get _visibleItems() {
|
|
181
|
+
return this._virtualScrollerState.visibleItems;
|
|
133
182
|
}
|
|
134
|
-
|
|
183
|
+
|
|
184
|
+
get _containerOffset() {
|
|
185
|
+
return this._virtualScrollerState.containerOffset;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
get _visibleRange() {
|
|
189
|
+
return this._virtualScrollerState.visibleRange;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Scroll handler for template
|
|
193
|
+
_onScroll = () => {
|
|
194
|
+
// Virtual scroller handles this internally
|
|
195
|
+
};
|
|
196
|
+
}
|
package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/styles.css
CHANGED
|
@@ -5,11 +5,26 @@
|
|
|
5
5
|
flex: 1 1 0%;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
.
|
|
8
|
+
.virtual-container {
|
|
9
9
|
height: 100%;
|
|
10
|
+
position: relative;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.virtual-viewport {
|
|
14
|
+
height: 100%;
|
|
15
|
+
overflow: auto;
|
|
10
16
|
padding-inline: 16px;
|
|
11
17
|
}
|
|
12
18
|
|
|
19
|
+
.virtual-content {
|
|
20
|
+
position: relative;
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.virtual-items {
|
|
25
|
+
position: relative;
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
.container {
|
|
14
29
|
min-height: 1px;
|
|
15
30
|
height: 100%;
|
package/src/internal-plugins/activity-history/activity-history-item/list/UI/timeline/template.ts
CHANGED
|
@@ -27,53 +27,64 @@ export const template = (props: ActivityHistoryTimeline) => {
|
|
|
27
27
|
${
|
|
28
28
|
props.historyGroups?.length > 0
|
|
29
29
|
? html`
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<div class="visit zIndex${props.historyGroups.length - index}"
|
|
35
|
-
data-date=${ifDefined(itemGroup?.items[0]?.date || itemGroup?.subGroups[0]?.items[0]?.date)}>
|
|
36
|
-
${visitHeaderTemplate(props, itemGroup?.items[0] || itemGroup?.subGroups[0]?.items[0])}
|
|
37
|
-
<div class="visit__items">
|
|
30
|
+
<div class="virtual-container">
|
|
31
|
+
<div class="virtual-viewport" @scroll=${props._onScroll}>
|
|
32
|
+
<div class="virtual-content">
|
|
33
|
+
<div class="virtual-items" style="transform: translateY(${props._containerOffset}px)">
|
|
38
34
|
${repeat(
|
|
39
|
-
|
|
40
|
-
(
|
|
41
|
-
(
|
|
42
|
-
|
|
43
|
-
const item = entry.item;
|
|
44
|
-
return html`
|
|
45
|
-
<div
|
|
46
|
-
class="item"
|
|
47
|
-
?has-divider=${hasItemDivider(item, itemGroup?.items as IActivityHistoryItemWithComponent[])}
|
|
48
|
-
>
|
|
49
|
-
${item.component}
|
|
50
|
-
</div>
|
|
51
|
-
`;
|
|
52
|
-
}
|
|
53
|
-
const subGroup = entry.subGroup;
|
|
35
|
+
props._visibleItems,
|
|
36
|
+
(itemGroup, index) => itemGroup?.items?.[0]?.id || itemGroup?.subGroups?.[0]?.items?.[0]?.id,
|
|
37
|
+
(itemGroup: IActivityHistoryGroup, localIndex: number) => {
|
|
38
|
+
const globalIndex = props._visibleRange.start + localIndex;
|
|
54
39
|
return html`
|
|
55
|
-
<div class="
|
|
56
|
-
|
|
57
|
-
|
|
40
|
+
<div class="visit zIndex${props.historyGroups.length - globalIndex}"
|
|
41
|
+
data-date=${ifDefined(itemGroup?.items[0]?.date || itemGroup?.subGroups[0]?.items[0]?.date)}>
|
|
42
|
+
${visitHeaderTemplate(props, itemGroup?.items[0] || itemGroup?.subGroups[0]?.items[0])}
|
|
43
|
+
<div class="visit__items">
|
|
58
44
|
${repeat(
|
|
59
|
-
|
|
60
|
-
(
|
|
61
|
-
(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
mergeHistoryItemsAndSubgroups(itemGroup),
|
|
46
|
+
(entry) => (entry.type === "item" ? entry.item.id : entry.subGroup.id),
|
|
47
|
+
(entry: ActivityHistoryEntry) => {
|
|
48
|
+
if (entry.type === "item") {
|
|
49
|
+
const item = entry.item;
|
|
50
|
+
return html`
|
|
51
|
+
<div
|
|
52
|
+
class="item"
|
|
53
|
+
?has-divider=${hasItemDivider(item, itemGroup?.items as IActivityHistoryItemWithComponent[])}
|
|
54
|
+
>
|
|
55
|
+
${item.component}
|
|
56
|
+
</div>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
const subGroup = entry.subGroup;
|
|
60
|
+
return html`
|
|
61
|
+
<div class="diagnostics">
|
|
62
|
+
${diagnosticHeaderTemplate(subGroup?.items[0])}
|
|
63
|
+
<div class="diagnostics__items">
|
|
64
|
+
${repeat(
|
|
65
|
+
subGroup?.items,
|
|
66
|
+
(item) => item.id,
|
|
67
|
+
(item: IActivityHistoryItemWithComponent) => html`
|
|
68
|
+
<div class="item"
|
|
69
|
+
?has-divider=${hasItemDivider(item, subGroup?.items as IActivityHistoryItemWithComponent[])}>
|
|
70
|
+
${item.component}
|
|
71
|
+
</div>
|
|
72
|
+
`,
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
`;
|
|
77
|
+
},
|
|
67
78
|
)}
|
|
68
79
|
</div>
|
|
69
80
|
</div>
|
|
70
81
|
`;
|
|
71
|
-
}
|
|
82
|
+
}
|
|
72
83
|
)}
|
|
73
84
|
</div>
|
|
74
85
|
</div>
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
77
88
|
`
|
|
78
89
|
: noGroupsTemplate(props)
|
|
79
90
|
}
|