mnfst 0.5.79 → 0.5.81
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/lib/manifest.appwrite.auth.js +66 -33
- package/lib/manifest.code.css +19 -7
- package/lib/manifest.code.min.css +1 -1
- package/lib/manifest.components.js +25 -155
- package/lib/manifest.css +47 -45
- package/lib/manifest.data.js +42 -1
- package/lib/manifest.integrity.json +26 -0
- package/lib/manifest.js +60 -5
- package/lib/manifest.markdown.js +95 -3
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.router.js +49 -76
- package/lib/manifest.schema.json +1 -1
- package/lib/manifest.sidebar.css +3 -3
- package/lib/manifest.svg.js +68 -5
- package/lib/manifest.typography.css +37 -41
- package/lib/manifest.utilities.css +7 -1
- package/lib/manifest.utilities.js +9 -29
- package/package.json +4 -7
- package/lib/manifest.export.js +0 -509
- package/lib/manifest.virtual.js +0 -319
package/lib/manifest.virtual.js
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
/* Manifest Virtual — variable-height list virtualization for Alpine.
|
|
2
|
-
*
|
|
3
|
-
* Renders only the rows visible in the scroll viewport (plus an overscan
|
|
4
|
-
* buffer), so a list of tens of thousands of rows can scroll smoothly with
|
|
5
|
-
* a low DOM count. Row heights are measured on render and the spacer
|
|
6
|
-
* recalculates, so authors aren't bound to a fixed row height.
|
|
7
|
-
*
|
|
8
|
-
* Usage — wrap an x-for template with x-virtual on the scrolling container:
|
|
9
|
-
*
|
|
10
|
-
* <div x-virtual style="height: 600px; overflow: auto">
|
|
11
|
-
* <template x-for="row in $x.customers" :key="row.id">
|
|
12
|
-
* <div class="row">
|
|
13
|
-
* <span x-text="row.name"></span>
|
|
14
|
-
* </div>
|
|
15
|
-
* </template>
|
|
16
|
-
* </div>
|
|
17
|
-
*
|
|
18
|
-
* Options (object expression on the directive):
|
|
19
|
-
*
|
|
20
|
-
* <div x-virtual="{ estimate: 48, overscan: 5 }" style="height: 600px">
|
|
21
|
-
*
|
|
22
|
-
* estimate Initial per-row height in px (default 50). Used for rows
|
|
23
|
-
* that haven't been measured yet. Closer estimates mean
|
|
24
|
-
* less scroll-position drift on first render.
|
|
25
|
-
* overscan Rows to render above/below the visible window (default 3).
|
|
26
|
-
* Higher = smoother scroll, more DOM.
|
|
27
|
-
*
|
|
28
|
-
* Notes:
|
|
29
|
-
*
|
|
30
|
-
* - Only one template child is supported. It must have x-for and :key.
|
|
31
|
-
* - The container element must have a bounded height (CSS height /
|
|
32
|
-
* max-height) and scroll. The plugin sets overflow: auto + position:
|
|
33
|
-
* relative if not already set.
|
|
34
|
-
* - Heights are remeasured automatically if a row's content changes.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
function initializeVirtualPlugin() {
|
|
38
|
-
|
|
39
|
-
Alpine.directive('virtual', (el, { expression }, { effect, evaluate, evaluateLater, cleanup }) => {
|
|
40
|
-
|
|
41
|
-
// --- Find and parse the template ---
|
|
42
|
-
const template = el.querySelector(':scope > template');
|
|
43
|
-
if (!template) {
|
|
44
|
-
console.warn('[x-virtual] expects a child <template> with x-for, e.g. <template x-for="row in $x.items" :key="row.id">…');
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
const forExpr = template.getAttribute('x-for');
|
|
48
|
-
if (!forExpr) {
|
|
49
|
-
console.warn('[x-virtual] child <template> must have x-for');
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const m = /^\s*(\S+|\(\s*\S+\s*,\s*\S+\s*\))\s+(?:in|of)\s+(.+?)\s*$/.exec(forExpr);
|
|
53
|
-
if (!m) {
|
|
54
|
-
console.warn('[x-virtual] could not parse x-for expression: ' + forExpr);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const itemName = m[1].trim();
|
|
58
|
-
const sourceExpr = m[2].trim();
|
|
59
|
-
const keyExpr =
|
|
60
|
-
template.getAttribute(':key') ||
|
|
61
|
-
template.getAttribute('x-bind:key') ||
|
|
62
|
-
`${itemName}.id`;
|
|
63
|
-
|
|
64
|
-
// Remove x-for/:key so Alpine doesn't try to render the full list, but
|
|
65
|
-
// KEEP the template in the DOM as our render source. We'll clone its
|
|
66
|
-
// contents per visible row.
|
|
67
|
-
template.removeAttribute('x-for');
|
|
68
|
-
template.removeAttribute(':key');
|
|
69
|
-
template.removeAttribute('x-bind:key');
|
|
70
|
-
|
|
71
|
-
// --- Options ---
|
|
72
|
-
const options = expression ? evaluate(expression) || {} : {};
|
|
73
|
-
const initialEstimate = Number(options.estimate) > 0 ? Number(options.estimate) : 50;
|
|
74
|
-
const overscan = Number.isFinite(options.overscan) && options.overscan >= 0 ? Number(options.overscan) : 3;
|
|
75
|
-
|
|
76
|
-
// --- Container setup ---
|
|
77
|
-
const cs = getComputedStyle(el);
|
|
78
|
-
if (cs.overflow === 'visible' && cs.overflowY === 'visible') el.style.overflow = 'auto';
|
|
79
|
-
if (cs.position === 'static') el.style.position = 'relative';
|
|
80
|
-
|
|
81
|
-
// The spacer holds the rendered (absolutely positioned) rows and sizes
|
|
82
|
-
// itself to the total virtual height so the scrollbar is correct.
|
|
83
|
-
const spacer = document.createElement('div');
|
|
84
|
-
spacer.dataset.virtualSpacer = '';
|
|
85
|
-
spacer.style.position = 'relative';
|
|
86
|
-
spacer.style.width = '100%';
|
|
87
|
-
spacer.style.height = '0px';
|
|
88
|
-
el.appendChild(spacer);
|
|
89
|
-
|
|
90
|
-
// --- State ---
|
|
91
|
-
// heights: key -> measured pixel height (only for rows that have been
|
|
92
|
-
// mounted at least once and measured).
|
|
93
|
-
const heights = new Map();
|
|
94
|
-
let measuredSum = 0;
|
|
95
|
-
let measuredCount = 0;
|
|
96
|
-
// rendered: key -> wrapper element currently in the DOM
|
|
97
|
-
const rendered = new Map();
|
|
98
|
-
// data: latest snapshot of the source array
|
|
99
|
-
let data = [];
|
|
100
|
-
// Cached cumulative offsets — index i holds the sum of heights of rows
|
|
101
|
-
// 0..(i-1). Length is data.length + 1; final entry is total height.
|
|
102
|
-
let cumulative = new Float64Array(1);
|
|
103
|
-
|
|
104
|
-
const getAvg = () => (measuredCount > 0 ? measuredSum / measuredCount : initialEstimate);
|
|
105
|
-
const rowHeightFor = (key) => heights.get(key) ?? getAvg();
|
|
106
|
-
|
|
107
|
-
// Evaluate the key expression against an item without going through
|
|
108
|
-
// Alpine — `new Function` is fast and isolates from the surrounding
|
|
109
|
-
// scope. Expression usually looks like `row.id` or `row.$id`.
|
|
110
|
-
const keyFn = buildKeyFn(itemName, keyExpr);
|
|
111
|
-
|
|
112
|
-
function rebuildCumulative() {
|
|
113
|
-
const n = data.length;
|
|
114
|
-
cumulative = new Float64Array(n + 1);
|
|
115
|
-
let y = 0;
|
|
116
|
-
for (let i = 0; i < n; i++) {
|
|
117
|
-
cumulative[i] = y;
|
|
118
|
-
const k = keyFn(data[i]);
|
|
119
|
-
y += rowHeightFor(k);
|
|
120
|
-
}
|
|
121
|
-
cumulative[n] = y;
|
|
122
|
-
spacer.style.height = y + 'px';
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Find the first index whose offset is >= scrollTop. Cumulative is
|
|
126
|
-
// monotonic so binary search works.
|
|
127
|
-
function findStartIndex(scrollTop) {
|
|
128
|
-
let lo = 0, hi = data.length;
|
|
129
|
-
while (lo < hi) {
|
|
130
|
-
const mid = (lo + hi) >>> 1;
|
|
131
|
-
if (cumulative[mid + 1] <= scrollTop) lo = mid + 1;
|
|
132
|
-
else hi = mid;
|
|
133
|
-
}
|
|
134
|
-
return Math.max(0, lo - overscan);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function findEndIndex(scrollBottom, startHint) {
|
|
138
|
-
let i = startHint;
|
|
139
|
-
const n = data.length;
|
|
140
|
-
while (i < n && cumulative[i] < scrollBottom) i++;
|
|
141
|
-
return Math.min(n, i + overscan);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function renderVisible() {
|
|
145
|
-
if (!data.length) {
|
|
146
|
-
for (const [, node] of rendered) node.remove();
|
|
147
|
-
rendered.clear();
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const scrollTop = el.scrollTop;
|
|
151
|
-
const viewportHeight = el.clientHeight;
|
|
152
|
-
const start = findStartIndex(scrollTop);
|
|
153
|
-
const end = findEndIndex(scrollTop + viewportHeight, start);
|
|
154
|
-
|
|
155
|
-
// Track which keys remain visible
|
|
156
|
-
const stillVisible = new Set();
|
|
157
|
-
for (let i = start; i < end; i++) {
|
|
158
|
-
const item = data[i];
|
|
159
|
-
if (item == null) continue;
|
|
160
|
-
const key = keyFn(item);
|
|
161
|
-
if (key == null) continue; // skip un-keyable rows
|
|
162
|
-
stillVisible.add(key);
|
|
163
|
-
|
|
164
|
-
let node = rendered.get(key);
|
|
165
|
-
if (!node) {
|
|
166
|
-
node = mountRow(i);
|
|
167
|
-
if (!node) continue;
|
|
168
|
-
rendered.set(key, node);
|
|
169
|
-
spacer.appendChild(node);
|
|
170
|
-
// x-data on the row needs the parent scope (where the
|
|
171
|
-
// source array lives) to resolve, so we MUST init after
|
|
172
|
-
// append, not before.
|
|
173
|
-
Alpine.initTree(node);
|
|
174
|
-
// Measure on next frame so Alpine has bound everything.
|
|
175
|
-
requestAnimationFrame(() => measureRow(key, node));
|
|
176
|
-
}
|
|
177
|
-
node.style.top = cumulative[i] + 'px';
|
|
178
|
-
node.dataset.virtualIndex = i;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Remove rows no longer in the window
|
|
182
|
-
for (const [key, node] of rendered) {
|
|
183
|
-
if (!stillVisible.has(key)) {
|
|
184
|
-
node.remove();
|
|
185
|
-
rendered.delete(key);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function mountRow(index) {
|
|
191
|
-
const tplChild = template.content.firstElementChild;
|
|
192
|
-
if (!tplChild) return null;
|
|
193
|
-
const node = tplChild.cloneNode(true);
|
|
194
|
-
node.style.position = 'absolute';
|
|
195
|
-
node.style.left = '0';
|
|
196
|
-
node.style.right = '0';
|
|
197
|
-
// Inject a per-row Alpine scope. Because we reference the source
|
|
198
|
-
// expression with the index baked in via a getter, Alpine tracks
|
|
199
|
-
// the dependency and re-renders this row when its data updates.
|
|
200
|
-
const scopeExpr = `{ get ${itemName}() { return (${sourceExpr})[${index}]; } }`;
|
|
201
|
-
// Merge with any existing x-data on the cloned root.
|
|
202
|
-
const existing = node.getAttribute('x-data');
|
|
203
|
-
node.setAttribute('x-data', existing ? `Object.assign({}, ${scopeExpr}, ${existing})` : scopeExpr);
|
|
204
|
-
// Note: caller must Alpine.initTree(node) AFTER appending to the
|
|
205
|
-
// DOM, otherwise the scope can't resolve identifiers (e.g. the
|
|
206
|
-
// source array) from outer x-data contexts.
|
|
207
|
-
return node;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function measureRow(key, node) {
|
|
211
|
-
if (!node.isConnected) return;
|
|
212
|
-
const h = node.offsetHeight;
|
|
213
|
-
if (!h) return;
|
|
214
|
-
const prev = heights.get(key);
|
|
215
|
-
if (prev === h) return;
|
|
216
|
-
if (prev !== undefined) measuredSum -= prev;
|
|
217
|
-
else measuredCount++;
|
|
218
|
-
measuredSum += h;
|
|
219
|
-
heights.set(key, h);
|
|
220
|
-
// Recompute cumulative offsets and re-render so positions reflect
|
|
221
|
-
// the new heights AND any rows now in/out of the visible window.
|
|
222
|
-
rebuildCumulative();
|
|
223
|
-
renderVisible();
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// --- Reactive data source subscription ---
|
|
227
|
-
const sourceGetter = evaluateLater(sourceExpr);
|
|
228
|
-
effect(() => {
|
|
229
|
-
sourceGetter((value) => {
|
|
230
|
-
data = Array.isArray(value) ? value : (value ? Array.from(value) : []);
|
|
231
|
-
// When the data identity or length changes, drop any rendered
|
|
232
|
-
// rows whose keys no longer exist in the new data.
|
|
233
|
-
const validKeys = new Set();
|
|
234
|
-
for (const item of data) {
|
|
235
|
-
if (item != null) validKeys.add(keyFn(item));
|
|
236
|
-
}
|
|
237
|
-
for (const [key, node] of rendered) {
|
|
238
|
-
if (!validKeys.has(key)) {
|
|
239
|
-
node.remove();
|
|
240
|
-
rendered.delete(key);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
rebuildCumulative();
|
|
244
|
-
renderVisible();
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// --- Scroll + resize handlers ---
|
|
249
|
-
let scrollScheduled = false;
|
|
250
|
-
const onScroll = () => {
|
|
251
|
-
if (scrollScheduled) return;
|
|
252
|
-
scrollScheduled = true;
|
|
253
|
-
requestAnimationFrame(() => {
|
|
254
|
-
scrollScheduled = false;
|
|
255
|
-
renderVisible();
|
|
256
|
-
});
|
|
257
|
-
};
|
|
258
|
-
el.addEventListener('scroll', onScroll, { passive: true });
|
|
259
|
-
|
|
260
|
-
const ro = new ResizeObserver(() => renderVisible());
|
|
261
|
-
ro.observe(el);
|
|
262
|
-
|
|
263
|
-
cleanup(() => {
|
|
264
|
-
el.removeEventListener('scroll', onScroll);
|
|
265
|
-
ro.disconnect();
|
|
266
|
-
for (const [, node] of rendered) node.remove();
|
|
267
|
-
rendered.clear();
|
|
268
|
-
spacer.remove();
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Build a key-evaluator function for a given itemName + keyExpr.
|
|
275
|
-
// `keyFn(item)` returns the row's key. Falls back to identity if it fails.
|
|
276
|
-
function buildKeyFn(itemName, keyExpr) {
|
|
277
|
-
try {
|
|
278
|
-
// eslint-disable-next-line no-new-func
|
|
279
|
-
const fn = new Function(itemName, `return (${keyExpr});`);
|
|
280
|
-
return (item) => {
|
|
281
|
-
try { return fn(item); } catch { return item; }
|
|
282
|
-
};
|
|
283
|
-
} catch {
|
|
284
|
-
return (item) => item;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Track initialization to prevent duplicates
|
|
289
|
-
let virtualPluginInitialized = false;
|
|
290
|
-
|
|
291
|
-
function ensureVirtualPluginInitialized() {
|
|
292
|
-
if (virtualPluginInitialized) return;
|
|
293
|
-
if (!window.Alpine || typeof window.Alpine.directive !== 'function') return;
|
|
294
|
-
virtualPluginInitialized = true;
|
|
295
|
-
initializeVirtualPlugin();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Expose on window for loader to call if needed
|
|
299
|
-
window.ensureVirtualPluginInitialized = ensureVirtualPluginInitialized;
|
|
300
|
-
|
|
301
|
-
// Handle both DOMContentLoaded and alpine:init
|
|
302
|
-
if (document.readyState === 'loading') {
|
|
303
|
-
document.addEventListener('DOMContentLoaded', ensureVirtualPluginInitialized);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
document.addEventListener('alpine:init', ensureVirtualPluginInitialized);
|
|
307
|
-
|
|
308
|
-
// If Alpine is already initialized when this script loads, initialize immediately
|
|
309
|
-
if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
310
|
-
setTimeout(ensureVirtualPluginInitialized, 0);
|
|
311
|
-
} else {
|
|
312
|
-
const checkAlpine = setInterval(() => {
|
|
313
|
-
if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
314
|
-
clearInterval(checkAlpine);
|
|
315
|
-
ensureVirtualPluginInitialized();
|
|
316
|
-
}
|
|
317
|
-
}, 10);
|
|
318
|
-
setTimeout(() => clearInterval(checkAlpine), 5000);
|
|
319
|
-
}
|