@keenmate/pure-admin-core 1.0.0-rc01
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/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/css/main.css +11542 -0
- package/dist/fonts/Delivery/Delivery_W_Bd.woff2 +0 -0
- package/dist/fonts/Delivery/Delivery_W_BdIt.woff2 +0 -0
- package/dist/fonts/Delivery/Delivery_W_CdBlk.woff2 +0 -0
- package/dist/fonts/Delivery/Delivery_W_CdLt.woff2 +0 -0
- package/dist/fonts/Delivery/Delivery_W_It.woff2 +0 -0
- package/dist/fonts/Delivery/Delivery_W_Lt.woff2 +0 -0
- package/dist/fonts/Delivery/Delivery_W_LtIt.woff2 +0 -0
- package/dist/fonts/Delivery/Delivery_W_Rg.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8UH-qVHQ.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8UH-qVHQ.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8UH-qVHQ.woff2.2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8VH-qVHQ.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8VH-qVHQ.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8VH-qVHQ.woff2.2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8bH-o.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8bH-o.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8fH-qVHQ.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8fH-qVHQ.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8fH-qVHQ.woff2.2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtE2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtU2Hw.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtY2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtg2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvto2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvts2H68T.woff2 +0 -0
- package/dist/fonts/google/7Auup_AqnyWWAxW2Wk3swUz56MS91Eww8SX21nejog.woff2 +0 -0
- package/dist/fonts/google/7Auup_AqnyWWAxW2Wk3swUz56MS91Eww8SX21nijogp5.woff2 +0 -0
- package/dist/fonts/google/7Auup_AqnyWWAxW2Wk3swUz56MS91Eww8SX21nmjogp5.woff2 +0 -0
- package/dist/fonts/google/PN_xRfK9oXHga0XdZ8g_vT0.woff2 +0 -0
- package/dist/fonts/google/PN_xRfK9oXHga0XdZsg_.woff2 +0 -0
- package/dist/fonts/google/PN_xRfK9oXHga0XdaMg_vT0.woff2 +0 -0
- package/dist/fonts/google/TK3tWkYFABsmjsphPho.woff2 +0 -0
- package/dist/fonts/google/TK3tWkYFABsmjspuPho7vA.woff2 +0 -0
- package/dist/fonts/google/TK3tWkYFABsmjspvPho7vA.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjTYJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjXYJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6Vj_YJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjbYJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjvYJw.woff2 +0 -0
- package/dist/fonts/google/fonts-tracklist.txt +48 -0
- package/dist/fonts/google/vEFO2_JTCgwQ5ejvMV0O96D01E8J0tJXHKbBjM4.woff2 +0 -0
- package/dist/fonts/google/vEFO2_JTCgwQ5ejvMV0O96D01E8J0tJXHKbOjM7sfA.woff2 +0 -0
- package/dist/fonts/google/vEFO2_JTCgwQ5ejvMV0O96D01E8J0tJXHKbPjM7sfA.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfLtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfXtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfbtrQ.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfjtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfntrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfrtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfvtrftV.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_B-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_C-bk.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_G-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_M-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_N-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_P-bnBeA.woff2 +0 -0
- package/package.json +60 -0
- package/snippets/alerts.html +281 -0
- package/snippets/badges.html +212 -0
- package/snippets/buttons.html +287 -0
- package/snippets/cards.html +393 -0
- package/snippets/checkbox-lists.html +490 -0
- package/snippets/code.html +225 -0
- package/snippets/command-palette.html +210 -0
- package/snippets/comparison.html +428 -0
- package/snippets/customization.html +142 -0
- package/snippets/forms.html +477 -0
- package/snippets/grid.html +338 -0
- package/snippets/layout.html +598 -0
- package/snippets/lists.html +232 -0
- package/snippets/loaders.html +183 -0
- package/snippets/manifest.json +388 -0
- package/snippets/modal-dialogs.html +411 -0
- package/snippets/modals.html +310 -0
- package/snippets/popconfirm.html +253 -0
- package/snippets/profile.html +264 -0
- package/snippets/tables.html +317 -0
- package/snippets/tabs.html +930 -0
- package/snippets/timeline.html +364 -0
- package/snippets/toasts.html +154 -0
- package/snippets/tooltips.html +411 -0
- package/snippets/typography.html +101 -0
- package/snippets/utilities.html +595 -0
- package/snippets/virtual-scroll.html +322 -0
- package/snippets/web-daterangepicker.html +634 -0
- package/snippets/web-multiselect.html +362 -0
- package/src/scss/.claude/settings.local.json +11 -0
- package/src/scss/_base-css-variables.scss +348 -0
- package/src/scss/_core.scss +99 -0
- package/src/scss/_fonts.scss +67 -0
- package/src/scss/_purecss-grid-responsive.scss +138 -0
- package/src/scss/_purecss-grid.scss +58 -0
- package/src/scss/_variables.scss +14 -0
- package/src/scss/core-components/_alerts.scss +212 -0
- package/src/scss/core-components/_badges.scss +16 -0
- package/src/scss/core-components/_base.scss +124 -0
- package/src/scss/core-components/_buttons.scss +473 -0
- package/src/scss/core-components/_cards.scss +285 -0
- package/src/scss/core-components/_checkbox-lists.scss +289 -0
- package/src/scss/core-components/_code.scss +141 -0
- package/src/scss/core-components/_command-palette.scss +518 -0
- package/src/scss/core-components/_comparison.scss +172 -0
- package/src/scss/core-components/_file-selector.scss +780 -0
- package/src/scss/core-components/_forms.scss +16 -0
- package/src/scss/core-components/_grid.scss +264 -0
- package/src/scss/core-components/_layout.scss +15 -0
- package/src/scss/core-components/_lists.scss +211 -0
- package/src/scss/core-components/_loaders.scss +277 -0
- package/src/scss/core-components/_logic-tree.scss +280 -0
- package/src/scss/core-components/_modals.scss +209 -0
- package/src/scss/core-components/_notifications.scss +253 -0
- package/src/scss/core-components/_pagers.scss +141 -0
- package/src/scss/core-components/_popconfirm.scss +170 -0
- package/src/scss/core-components/_profile.scss +281 -0
- package/src/scss/core-components/_settings-panel.scss +128 -0
- package/src/scss/core-components/_statistics.scss +200 -0
- package/src/scss/core-components/_tables.scss +555 -0
- package/src/scss/core-components/_tabs.scss +438 -0
- package/src/scss/core-components/_timeline.scss +589 -0
- package/src/scss/core-components/_toasts.scss +281 -0
- package/src/scss/core-components/_tooltips.scss +503 -0
- package/src/scss/core-components/_utilities.scss +241 -0
- package/src/scss/core-components/_web-components-theme.scss +294 -0
- package/src/scss/core-components/badges/_badge-base.scss +131 -0
- package/src/scss/core-components/badges/_badge-group.scss +25 -0
- package/src/scss/core-components/badges/_composite-badge-variants.scss +396 -0
- package/src/scss/core-components/badges/_composite-badge.scss +70 -0
- package/src/scss/core-components/badges/_index.scss +10 -0
- package/src/scss/core-components/badges/_labels.scss +155 -0
- package/src/scss/core-components/forms/_checkboxes-radios.scss +205 -0
- package/src/scss/core-components/forms/_form-inputs.scss +100 -0
- package/src/scss/core-components/forms/_form-layout.scss +66 -0
- package/src/scss/core-components/forms/_form-states.scss +89 -0
- package/src/scss/core-components/forms/_index.scss +12 -0
- package/src/scss/core-components/forms/_input-groups.scss +149 -0
- package/src/scss/core-components/forms/_input-wrapper.scss +89 -0
- package/src/scss/core-components/forms/_query-editor.scss +313 -0
- package/src/scss/core-components/layout/_index.scss +11 -0
- package/src/scss/core-components/layout/_layout-container.scss +105 -0
- package/src/scss/core-components/layout/_layout-responsive.scss +100 -0
- package/src/scss/core-components/layout/_navbar-elements.scss +238 -0
- package/src/scss/core-components/layout/_navbar.scss +71 -0
- package/src/scss/core-components/layout/_sidebar-states.scss +228 -0
- package/src/scss/core-components/layout/_sidebar.scss +177 -0
- package/src/scss/main.scss +7 -0
- package/src/scss/themes/_dark-base.scss +207 -0
- package/src/scss/themes/audi-light.scss +311 -0
- package/src/scss/themes/audi.scss +288 -0
- package/src/scss/themes/corporate.scss +203 -0
- package/src/scss/themes/dark-blue.scss +152 -0
- package/src/scss/themes/dark-green.scss +156 -0
- package/src/scss/themes/dark-red.scss +160 -0
- package/src/scss/themes/dark.scss +145 -0
- package/src/scss/themes/express.scss +281 -0
- package/src/scss/themes/minimal.scss +121 -0
- package/src/scss/utilities.scss +481 -0
- package/src/scss/variables/_base.scss +81 -0
- package/src/scss/variables/_colors.scss +148 -0
- package/src/scss/variables/_components.scss +509 -0
- package/src/scss/variables/_index.scss +13 -0
- package/src/scss/variables/_layout.scss +65 -0
- package/src/scss/variables/_spacing.scss +66 -0
- package/src/scss/variables/_system.scss +80 -0
- package/src/scss/variables/_typography.scss +37 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
<!-- Virtual Scroll & Infinite Scroll Snippets -->
|
|
2
|
+
<!-- Clean HTML and JavaScript examples for efficient large dataset rendering -->
|
|
3
|
+
|
|
4
|
+
<!-- ========================================
|
|
5
|
+
Virtual Scroll (True Virtualization)
|
|
6
|
+
Constant DOM size, handles millions of items
|
|
7
|
+
======================================== -->
|
|
8
|
+
|
|
9
|
+
<!-- Timeline Virtual Scroll -->
|
|
10
|
+
<div id="timeline-virtual-container" style="height: 400px; overflow-y: auto;">
|
|
11
|
+
<ul class="pa-timeline pa-timeline--feed" id="timeline-virtual-list"></ul>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
const timelineContainer = document.getElementById('timeline-virtual-container');
|
|
16
|
+
const timelineList = document.getElementById('timeline-virtual-list');
|
|
17
|
+
|
|
18
|
+
let scrollTop = 0;
|
|
19
|
+
const itemHeight = 50; // Fixed height per item
|
|
20
|
+
const totalItems = 5000; // Total dataset size
|
|
21
|
+
const bufferSize = 10; // Extra items above/below viewport
|
|
22
|
+
const viewportHeight = 400; // Container height
|
|
23
|
+
|
|
24
|
+
function renderTimelineItems() {
|
|
25
|
+
// Calculate visible range
|
|
26
|
+
const startIndex = Math.floor(scrollTop / itemHeight);
|
|
27
|
+
const endIndex = Math.ceil((scrollTop + viewportHeight) / itemHeight);
|
|
28
|
+
const visibleStart = Math.max(0, startIndex - bufferSize);
|
|
29
|
+
const visibleEnd = Math.min(totalItems, endIndex + bufferSize);
|
|
30
|
+
|
|
31
|
+
// Set list height to maintain scroll
|
|
32
|
+
timelineList.style.height = `${totalItems * itemHeight}px`;
|
|
33
|
+
timelineList.style.position = 'relative';
|
|
34
|
+
|
|
35
|
+
// Clear and render visible items only
|
|
36
|
+
timelineList.innerHTML = '';
|
|
37
|
+
|
|
38
|
+
for (let i = visibleStart; i < visibleEnd; i++) {
|
|
39
|
+
const li = document.createElement('li');
|
|
40
|
+
li.className = 'pa-timeline__item';
|
|
41
|
+
|
|
42
|
+
// Absolute positioning at calculated offset
|
|
43
|
+
li.style.position = 'absolute';
|
|
44
|
+
li.style.top = `${i * itemHeight}px`;
|
|
45
|
+
li.style.left = '0';
|
|
46
|
+
li.style.right = '0';
|
|
47
|
+
|
|
48
|
+
// Render item content
|
|
49
|
+
li.innerHTML = `
|
|
50
|
+
<div class="pa-timeline__time">09:30</div>
|
|
51
|
+
<div class="pa-timeline__content">
|
|
52
|
+
<div class="pa-timeline__avatar">
|
|
53
|
+
<img src="avatar.jpg" alt="User">
|
|
54
|
+
</div>
|
|
55
|
+
<span>Action #${i}</span>
|
|
56
|
+
</div>
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
timelineList.appendChild(li);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Listen to scroll events
|
|
64
|
+
timelineContainer.addEventListener('scroll', () => {
|
|
65
|
+
scrollTop = timelineContainer.scrollTop;
|
|
66
|
+
renderTimelineItems();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Initial render
|
|
70
|
+
renderTimelineItems();
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<!-- ========================================
|
|
74
|
+
Table Virtual Scroll (with spacer rows)
|
|
75
|
+
Preserves table structure
|
|
76
|
+
======================================== -->
|
|
77
|
+
|
|
78
|
+
<div id="table-virtual-container" style="height: 400px; overflow-y: auto;">
|
|
79
|
+
<table class="pa-table pa-table--striped">
|
|
80
|
+
<thead style="position: sticky; top: 0; background: var(--base-primary-bg); z-index: 10;">
|
|
81
|
+
<tr>
|
|
82
|
+
<th>#</th>
|
|
83
|
+
<th>Name</th>
|
|
84
|
+
<th>Email</th>
|
|
85
|
+
<th>Status</th>
|
|
86
|
+
</tr>
|
|
87
|
+
</thead>
|
|
88
|
+
<tbody id="table-virtual-body"></tbody>
|
|
89
|
+
</table>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<script>
|
|
93
|
+
const tableContainer = document.getElementById('table-virtual-container');
|
|
94
|
+
const tableBody = document.getElementById('table-virtual-body');
|
|
95
|
+
|
|
96
|
+
let tableScrollTop = 0;
|
|
97
|
+
const tableItemHeight = 38; // Table row height
|
|
98
|
+
const tableTotalItems = 10000; // Total rows
|
|
99
|
+
const tableBufferSize = 10;
|
|
100
|
+
const tableViewportHeight = 400;
|
|
101
|
+
|
|
102
|
+
function renderTableRows() {
|
|
103
|
+
// Calculate visible range
|
|
104
|
+
const startIndex = Math.floor(tableScrollTop / tableItemHeight);
|
|
105
|
+
const endIndex = Math.ceil((tableScrollTop + tableViewportHeight) / tableItemHeight);
|
|
106
|
+
const visibleStart = Math.max(0, startIndex - tableBufferSize);
|
|
107
|
+
const visibleEnd = Math.min(tableTotalItems, endIndex + tableBufferSize);
|
|
108
|
+
|
|
109
|
+
// Clear tbody
|
|
110
|
+
tableBody.innerHTML = '';
|
|
111
|
+
|
|
112
|
+
// Top spacer row (pushes content down)
|
|
113
|
+
if (visibleStart > 0) {
|
|
114
|
+
const topSpacer = document.createElement('tr');
|
|
115
|
+
topSpacer.style.height = `${visibleStart * tableItemHeight}px`;
|
|
116
|
+
topSpacer.innerHTML = '<td colspan="4"></td>';
|
|
117
|
+
tableBody.appendChild(topSpacer);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Visible rows
|
|
121
|
+
for (let i = visibleStart; i < visibleEnd; i++) {
|
|
122
|
+
const tr = document.createElement('tr');
|
|
123
|
+
tr.innerHTML = `
|
|
124
|
+
<td>${i + 1}</td>
|
|
125
|
+
<td>John Doe</td>
|
|
126
|
+
<td>john.doe@example.com</td>
|
|
127
|
+
<td><span class="pa-badge pa-badge--success">Active</span></td>
|
|
128
|
+
`;
|
|
129
|
+
tableBody.appendChild(tr);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Bottom spacer row (maintains scroll height)
|
|
133
|
+
if (visibleEnd < tableTotalItems) {
|
|
134
|
+
const bottomSpacer = document.createElement('tr');
|
|
135
|
+
bottomSpacer.style.height = `${(tableTotalItems - visibleEnd) * tableItemHeight}px`;
|
|
136
|
+
bottomSpacer.innerHTML = '<td colspan="4"></td>';
|
|
137
|
+
tableBody.appendChild(bottomSpacer);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Listen to scroll events
|
|
142
|
+
tableContainer.addEventListener('scroll', () => {
|
|
143
|
+
tableScrollTop = tableContainer.scrollTop;
|
|
144
|
+
renderTableRows();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Initial render
|
|
148
|
+
renderTableRows();
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<!-- ========================================
|
|
152
|
+
Infinite Scroll (Lazy Loading)
|
|
153
|
+
Growing DOM, good for thousands of items
|
|
154
|
+
======================================== -->
|
|
155
|
+
|
|
156
|
+
<!-- Infinite Scroll Timeline -->
|
|
157
|
+
<div class="pa-timeline__scroll-container" id="infinite-scroll-container" style="max-height: 400px; overflow-y: auto;">
|
|
158
|
+
<ul class="pa-timeline pa-timeline--feed" id="infinite-scroll-timeline"></ul>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="pa-timeline__loader" id="infinite-scroll-loader">
|
|
161
|
+
<div class="pa-loader pa-loader--sm d-none" id="infinite-scroll-spinner">
|
|
162
|
+
<div class="pa-spinner"></div>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="pa-timeline__loader-text d-none" id="infinite-scroll-text">
|
|
165
|
+
Loading more entries...
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<script>
|
|
170
|
+
let infinitePage = 0;
|
|
171
|
+
let isLoadingInfinite = false;
|
|
172
|
+
const infiniteContainer = document.getElementById('infinite-scroll-container');
|
|
173
|
+
const infiniteTimeline = document.getElementById('infinite-scroll-timeline');
|
|
174
|
+
const infiniteSpinner = document.getElementById('infinite-scroll-spinner');
|
|
175
|
+
const infiniteText = document.getElementById('infinite-scroll-text');
|
|
176
|
+
|
|
177
|
+
function loadInfiniteItems() {
|
|
178
|
+
if (isLoadingInfinite || infinitePage >= 100) {
|
|
179
|
+
return; // Stop at 100 pages (2000 items)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
isLoadingInfinite = true;
|
|
183
|
+
infiniteSpinner.style.display = 'block';
|
|
184
|
+
infiniteText.style.display = 'block';
|
|
185
|
+
|
|
186
|
+
// Simulate async data loading
|
|
187
|
+
setTimeout(() => {
|
|
188
|
+
// Load 20 items per page
|
|
189
|
+
for (let i = 0; i < 20; i++) {
|
|
190
|
+
const index = infinitePage * 20 + i;
|
|
191
|
+
const li = document.createElement('li');
|
|
192
|
+
li.className = 'pa-timeline__item';
|
|
193
|
+
li.innerHTML = `
|
|
194
|
+
<div class="pa-timeline__time">09:30</div>
|
|
195
|
+
<div class="pa-timeline__content">
|
|
196
|
+
<div class="pa-timeline__avatar">
|
|
197
|
+
<img src="avatar.jpg" alt="User">
|
|
198
|
+
</div>
|
|
199
|
+
<span>Action #${index}</span>
|
|
200
|
+
</div>
|
|
201
|
+
`;
|
|
202
|
+
infiniteTimeline.appendChild(li); // Append, never remove
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
infinitePage++;
|
|
206
|
+
infiniteSpinner.style.display = 'none';
|
|
207
|
+
infiniteText.style.display = 'none';
|
|
208
|
+
isLoadingInfinite = false;
|
|
209
|
+
}, 500);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Load initial items
|
|
213
|
+
loadInfiniteItems();
|
|
214
|
+
|
|
215
|
+
// Listen to scroll events
|
|
216
|
+
infiniteContainer.addEventListener('scroll', () => {
|
|
217
|
+
const scrollTop = infiniteContainer.scrollTop;
|
|
218
|
+
const scrollHeight = infiniteContainer.scrollHeight;
|
|
219
|
+
const clientHeight = infiniteContainer.clientHeight;
|
|
220
|
+
|
|
221
|
+
// Check if scrolled near bottom (50px threshold)
|
|
222
|
+
if (scrollTop + clientHeight >= scrollHeight - 50 && !isLoadingInfinite) {
|
|
223
|
+
loadInfiniteItems();
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
</script>
|
|
227
|
+
|
|
228
|
+
<!-- ========================================
|
|
229
|
+
Using VirtualScroll Class (General Purpose)
|
|
230
|
+
======================================== -->
|
|
231
|
+
|
|
232
|
+
<script src="/dist/js/virtual-scroll.js"></script>
|
|
233
|
+
|
|
234
|
+
<div id="my-container" style="height: 400px; overflow-y: auto;"></div>
|
|
235
|
+
|
|
236
|
+
<script>
|
|
237
|
+
const vs = new VirtualScroll({
|
|
238
|
+
container: document.getElementById('my-container'),
|
|
239
|
+
itemHeight: 50, // Fixed height per item (required)
|
|
240
|
+
totalItems: 10000, // Total dataset size
|
|
241
|
+
bufferSize: 10, // Extra items above/below (optional)
|
|
242
|
+
renderItem: (index) => {
|
|
243
|
+
// Return DOM element for this item
|
|
244
|
+
const div = document.createElement('div');
|
|
245
|
+
div.textContent = `Item ${index}`;
|
|
246
|
+
return div;
|
|
247
|
+
},
|
|
248
|
+
onScroll: (scrollTop) => {
|
|
249
|
+
// Optional: callback on scroll
|
|
250
|
+
console.log('Scrolled to:', scrollTop);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// API methods
|
|
255
|
+
vs.setTotalItems(20000); // Update total items
|
|
256
|
+
vs.scrollToItem(5000); // Scroll to specific item
|
|
257
|
+
const range = vs.getVisibleRange(); // Get current visible range
|
|
258
|
+
vs.destroy(); // Cleanup
|
|
259
|
+
</script>
|
|
260
|
+
|
|
261
|
+
<!-- ========================================
|
|
262
|
+
Performance Comparison
|
|
263
|
+
======================================== -->
|
|
264
|
+
<!--
|
|
265
|
+
VIRTUAL SCROLL (Constant DOM):
|
|
266
|
+
- DOM size: ~20-30 elements regardless of dataset
|
|
267
|
+
- Performance: Handles millions of items smoothly
|
|
268
|
+
- Requirements: Fixed item heights, uniform layout
|
|
269
|
+
- Best for: Tables, logs, file lists, data grids
|
|
270
|
+
- Strategy: Items added/removed dynamically as you scroll
|
|
271
|
+
|
|
272
|
+
INFINITE SCROLL (Growing DOM):
|
|
273
|
+
- DOM size: Grows with each load (e.g., 2000 elements for 2000 items)
|
|
274
|
+
- Performance: Good for hundreds/low thousands
|
|
275
|
+
- Requirements: Works with variable heights
|
|
276
|
+
- Best for: Social feeds, news lists, image galleries
|
|
277
|
+
- Strategy: Items appended, never removed
|
|
278
|
+
|
|
279
|
+
WHEN TO USE WHAT:
|
|
280
|
+
- 10,000+ uniform items → Virtual Scroll
|
|
281
|
+
- Rich variable content → Infinite Scroll
|
|
282
|
+
- Complex nested layouts → Infinite Scroll
|
|
283
|
+
- Simple repeating rows → Virtual Scroll
|
|
284
|
+
-->
|
|
285
|
+
|
|
286
|
+
<!-- ========================================
|
|
287
|
+
Configuration Guidelines
|
|
288
|
+
======================================== -->
|
|
289
|
+
<!--
|
|
290
|
+
VIRTUAL SCROLL SETTINGS:
|
|
291
|
+
|
|
292
|
+
itemHeight:
|
|
293
|
+
- Must be exact and consistent
|
|
294
|
+
- Measure with: element.offsetHeight
|
|
295
|
+
- Examples:
|
|
296
|
+
- Timeline item: 50px
|
|
297
|
+
- Table row: 38px
|
|
298
|
+
- List item: 44px
|
|
299
|
+
|
|
300
|
+
bufferSize:
|
|
301
|
+
- Recommended: 5-15 items
|
|
302
|
+
- Larger = smoother scroll, more DOM
|
|
303
|
+
- Smaller = less DOM, potential flicker
|
|
304
|
+
|
|
305
|
+
totalItems:
|
|
306
|
+
- Can be updated dynamically
|
|
307
|
+
- Example: vs.setTotalItems(newCount)
|
|
308
|
+
|
|
309
|
+
INFINITE SCROLL SETTINGS:
|
|
310
|
+
|
|
311
|
+
Threshold:
|
|
312
|
+
- Recommended: 50-100px from bottom
|
|
313
|
+
- Trigger early for smooth experience
|
|
314
|
+
|
|
315
|
+
Page size:
|
|
316
|
+
- Recommended: 20-50 items per load
|
|
317
|
+
- Balance between requests and UX
|
|
318
|
+
|
|
319
|
+
Max pages:
|
|
320
|
+
- Set a limit to prevent memory issues
|
|
321
|
+
- Example: 100 pages = 2000 items max
|
|
322
|
+
-->
|