@yasanchezz/vue-infinite-grid 0.1.11 → 0.2.1
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/README.md +21 -6
- package/dist/VueGrid.d.ts +4 -2
- package/dist/vue-infinite-grid.css +1 -1
- package/dist/vue-infinite-grid.js +117 -82
- package/package.json +8 -7
- package/dist/favicon.ico +0 -0
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ createApp(App)
|
|
|
33
33
|
ref="grid"
|
|
34
34
|
:rows="rows"
|
|
35
35
|
:rows-count="10"
|
|
36
|
-
:height="200"
|
|
36
|
+
:row-initial-height="200"
|
|
37
37
|
class="my-grid"
|
|
38
38
|
>
|
|
39
39
|
<template #row="{ row }">
|
|
@@ -59,17 +59,20 @@ createApp(App)
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
Each item in `rows` must have a unique `id: string`, which `VueGrid` uses as
|
|
62
|
-
the `:key` for the row and to track its measured height.
|
|
62
|
+
the `:key` for the row and to track its measured height. A row can also set
|
|
63
|
+
`sticky: true` to stay pinned to the top of the scroll container while it's
|
|
64
|
+
part of the rendered window (see [Styling notes](#styling-notes)).
|
|
63
65
|
|
|
64
66
|
## Props
|
|
65
67
|
|
|
66
68
|
| Prop | Type | Required | Default | Description |
|
|
67
69
|
| --- | --- | --- | --- | --- |
|
|
68
|
-
| `rows` | `GridRow[]` (`{ id: string }`) | yes | - | The full list of rows. Only a window of these is actually rendered at a time. |
|
|
70
|
+
| `rows` | `GridRow[]` (`{ id: string, sticky?: boolean }`) | yes | - | The full list of rows. Only a window of these is actually rendered at a time. |
|
|
69
71
|
| `rowsCount` | `number` | yes | - | How many rows to render at once (the size of the rendered window). |
|
|
70
|
-
| `
|
|
72
|
+
| `rowInitialHeight` | `number` | yes | - | Estimated row height in px, used for scroll/offset math before a row has actually been measured. |
|
|
71
73
|
| `container` | `Element \| null \| ((container: Element \| null) => Element \| null \| undefined)` | no | `document.documentElement` | The scrollable element to listen to. Can be a function that receives the grid's root element (useful for resolving a custom scroll container after mount). |
|
|
72
|
-
| `
|
|
74
|
+
| `rowsCountAutoCalculated` | `boolean` | no | `false` | When `true`, grows the rendered window beyond `rowsCount` based on measured row heights so it always covers the viewport (useful when actual row heights are smaller than the estimated `rowInitialHeight`). |
|
|
75
|
+
| `rowsOffset` | `number` | no | `rowInitialHeight * Math.trunc(rowsCount / 5)` (or `1000` if that's `0`) | Extra px of buffer kept rendered above/below the viewport before the window shifts, to reduce blank space while scrolling fast. |
|
|
73
76
|
|
|
74
77
|
## Slots
|
|
75
78
|
|
|
@@ -97,7 +100,12 @@ window.addEventListener('resize', () => grid.value.init()) // recalculate layout
|
|
|
97
100
|
</script>
|
|
98
101
|
|
|
99
102
|
<template>
|
|
100
|
-
<VueGrid
|
|
103
|
+
<VueGrid
|
|
104
|
+
ref="grid"
|
|
105
|
+
:rows="rows"
|
|
106
|
+
:rows-count="10"
|
|
107
|
+
:row-initial-height="200"
|
|
108
|
+
/>
|
|
101
109
|
</template>
|
|
102
110
|
```
|
|
103
111
|
|
|
@@ -107,3 +115,10 @@ window.addEventListener('resize', () => grid.value.init()) // recalculate layout
|
|
|
107
115
|
column layout on the root element (e.g. `grid-template-columns`) and have
|
|
108
116
|
each row use `display: grid; grid-template-columns: subgrid;` spanning
|
|
109
117
|
`grid-column: 1 / -1` so it inherits those columns.
|
|
118
|
+
|
|
119
|
+
### Sticky rows
|
|
120
|
+
|
|
121
|
+
Setting `sticky: true` on a row makes `VueGrid` add a `grid-row--sticky`
|
|
122
|
+
class to it (`position: sticky; top: 0;`) and keep that row rendered and
|
|
123
|
+
pinned to the top of the scroll container even after it scrolls out of the
|
|
124
|
+
regular rendered window, until a later sticky row takes its place.
|
package/dist/VueGrid.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
declare const __VLS_export: <GridRow extends {
|
|
2
2
|
id: string;
|
|
3
|
+
sticky?: boolean;
|
|
3
4
|
}>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
5
|
props: import('vue').PublicProps & __VLS_PrettifyLocal<{
|
|
5
|
-
autoCalculatedRowsCount?: boolean;
|
|
6
6
|
container?: (Element | null | undefined) | ((container: Element | null) => Element | null | undefined);
|
|
7
|
+
rowInitialHeight: number;
|
|
7
8
|
rows: GridRow[];
|
|
8
9
|
rowsCount: number;
|
|
9
|
-
|
|
10
|
+
rowsCountAutoCalculated?: boolean;
|
|
11
|
+
rowsOffset?: number;
|
|
10
12
|
}> & (typeof globalThis extends {
|
|
11
13
|
__VLS_PROPS_FALLBACK: infer P;
|
|
12
14
|
} ? P : {});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.infinite-grid[data-v-
|
|
1
|
+
@layer VueGrid{.infinite-grid[data-v-e50d93db]{overflow-anchor:none;align-content:start;width:max-content;display:grid;overflow:clip}.grid-row[data-v-e50d93db]{grid-column:1/-1;grid-template-columns:subgrid;display:grid}.grid-row--sticky[data-v-e50d93db]{position:sticky;top:0}.content[data-v-e50d93db]{display:contents}.before[data-v-e50d93db],.after[data-v-e50d93db]{pointer-events:none;grid-column:1/-1}}
|
|
2
2
|
/*$vite$:1*/
|
|
@@ -1,147 +1,182 @@
|
|
|
1
|
-
import { Fragment as e, computed as t, createElementBlock as n, createElementVNode as r, defineComponent as i,
|
|
1
|
+
import { Fragment as e, computed as t, createElementBlock as n, createElementVNode as r, defineComponent as i, normalizeClass as a, normalizeStyle as o, onMounted as s, onUnmounted as c, openBlock as l, ref as u, renderList as ee, renderSlot as te, shallowRef as d, useTemplateRef as f, watch as p, watchEffect as m } from "vue";
|
|
2
2
|
//#region src/VueGrid.vue?vue&type=script&setup=true&lang.ts
|
|
3
|
-
var
|
|
3
|
+
var h = ["data-index"], g = /*#__PURE__*/ ((e, t) => {
|
|
4
4
|
let n = e.__vccOpts || e;
|
|
5
5
|
for (let [e, r] of t) n[e] = r;
|
|
6
6
|
return n;
|
|
7
7
|
})(/* @__PURE__ */ i({
|
|
8
8
|
__name: "VueGrid",
|
|
9
9
|
props: {
|
|
10
|
-
autoCalculatedRowsCount: {
|
|
11
|
-
type: Boolean,
|
|
12
|
-
default: !1
|
|
13
|
-
},
|
|
14
10
|
container: {
|
|
15
11
|
type: [null, Function],
|
|
16
|
-
default:
|
|
12
|
+
default: null
|
|
17
13
|
},
|
|
14
|
+
rowInitialHeight: {},
|
|
18
15
|
rows: {},
|
|
19
16
|
rowsCount: {},
|
|
20
|
-
|
|
17
|
+
rowsCountAutoCalculated: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: !1
|
|
20
|
+
},
|
|
21
|
+
rowsOffset: { default: 0 }
|
|
21
22
|
},
|
|
22
|
-
setup(i, { expose:
|
|
23
|
-
let
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
setup(i, { expose: g }) {
|
|
24
|
+
let _ = d([]), v = f("grid"), y = f("content"), b = t(() => i.container instanceof Function ? i.container(v.value) ?? document.documentElement : i.container ?? document.documentElement), x = t(() => b.value === document.documentElement || b.value === document.body ? window : b.value), S = /* @__PURE__ */ new Map(), C = i.rowsOffset || i.rowInitialHeight * Math.trunc(i.rowsCount / 5) || 1e3, w, T, E = /* @__PURE__ */ new Map();
|
|
25
|
+
function D() {
|
|
26
|
+
E = new Map(i.rows.map((e, t) => [e.id, t]));
|
|
27
|
+
}
|
|
28
|
+
let O = new Float32Array(i.rows.length);
|
|
29
|
+
function k() {
|
|
30
|
+
O = new Float32Array(i.rows.map((e) => S.get(e.id) ?? i.rowInitialHeight));
|
|
31
|
+
}
|
|
32
|
+
function A(e, t) {
|
|
33
|
+
S.set(e, t);
|
|
34
|
+
let n = E.get(e);
|
|
35
|
+
n != null && (O[n] = t);
|
|
29
36
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
let e = 0, t = y.value;
|
|
33
|
-
for (; t instanceof HTMLElement && t != x.value;) e += t.offsetTop, t = t.offsetParent;
|
|
34
|
-
k = e;
|
|
37
|
+
function j(e) {
|
|
38
|
+
return O.at(e) ?? i.rowInitialHeight;
|
|
35
39
|
}
|
|
36
|
-
let
|
|
40
|
+
let M = i.rowsCount;
|
|
37
41
|
function N() {
|
|
38
|
-
|
|
42
|
+
if (i.rowsCountAutoCalculated) {
|
|
43
|
+
let e = window.innerHeight + i.rowInitialHeight, t = z - C, n = t < e ? Math.ceil(e / t * M) : M;
|
|
44
|
+
M = Number.isFinite(n) && n >= i.rowsCount ? n : i.rowsCount;
|
|
45
|
+
}
|
|
39
46
|
}
|
|
40
47
|
let P = 0;
|
|
41
48
|
function F() {
|
|
42
|
-
|
|
49
|
+
let e = 0, t = v.value;
|
|
50
|
+
for (; t instanceof HTMLElement && t != b.value;) e += t.offsetTop, t = t.offsetParent;
|
|
51
|
+
P = e;
|
|
43
52
|
}
|
|
44
|
-
let I =
|
|
53
|
+
let I = u({ height: "0px" }), L = 0;
|
|
45
54
|
function R() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
L = O.reduce((e, t) => e + t, 0), I.value.height = `${L}px`;
|
|
56
|
+
}
|
|
57
|
+
let z = 0, B = 0, V = M;
|
|
58
|
+
function H() {
|
|
59
|
+
z = O.slice(B, V).reduce((e, t) => e + t, 0);
|
|
60
|
+
}
|
|
61
|
+
let U = u({ height: "0px" }), W = 0;
|
|
62
|
+
function G() {
|
|
63
|
+
let e = b.value.scrollTop - C - P, t = 0;
|
|
64
|
+
for (let n of i.rows.keys()) {
|
|
65
|
+
let r = j(n);
|
|
49
66
|
if (t + r > e) break;
|
|
50
67
|
t += r;
|
|
51
68
|
}
|
|
52
|
-
let n =
|
|
53
|
-
|
|
69
|
+
let n = L - z;
|
|
70
|
+
W = t > n ? n : t;
|
|
54
71
|
}
|
|
55
|
-
let
|
|
56
|
-
function
|
|
57
|
-
|
|
72
|
+
let K = 0;
|
|
73
|
+
function q() {
|
|
74
|
+
let e = W - K;
|
|
75
|
+
e = e < 0 ? 0 : e, U.value.height = `${e}px`;
|
|
58
76
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
let J = u({ height: "0px" }), Y = 0;
|
|
78
|
+
function X() {
|
|
79
|
+
Y = L - W - z, J.value.height = `${Y}px`;
|
|
80
|
+
}
|
|
81
|
+
function ne() {
|
|
82
|
+
F(), H(), R(), G(), q(), X();
|
|
83
|
+
let e = 0, t = 0, n, r;
|
|
84
|
+
for (let [a, o] of i.rows.entries()) {
|
|
85
|
+
let i = j(a);
|
|
86
|
+
if (e += i, e > W) {
|
|
87
|
+
t = a;
|
|
88
|
+
break;
|
|
69
89
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
o.sticky && (n = o, r = a, K = i);
|
|
91
|
+
}
|
|
92
|
+
[B, V] = t + M > i.rows.length || !Y ? [i.rows.length - M, i.rows.length] : [t, t + M], B < 0 && (B = 0);
|
|
93
|
+
let a = n && r != null && r < B ? [n] : [], o = a.length, s = i.rows.slice(B, V), c = a.concat(s);
|
|
94
|
+
o || (K = 0), (c.at(o) !== _.value.at(o) || c.length !== _.value.length) && (_.value = c), H(), R(), G(), q(), X();
|
|
95
|
+
}
|
|
96
|
+
function Z() {
|
|
97
|
+
cancelAnimationFrame(w), w = requestAnimationFrame(ne);
|
|
73
98
|
}
|
|
74
|
-
let
|
|
99
|
+
let Q = new ResizeObserver((e) => {
|
|
75
100
|
for (let t of e) {
|
|
76
|
-
let e = t.target, n = t.borderBoxSize.at(0)?.blockSize ?? i.
|
|
101
|
+
let e = t.target, n = t.borderBoxSize.at(0)?.blockSize ?? i.rowInitialHeight;
|
|
77
102
|
if (e instanceof HTMLElement) {
|
|
78
103
|
let t = e.dataset.index;
|
|
79
|
-
t != null &&
|
|
104
|
+
t != null && A(t, n);
|
|
80
105
|
}
|
|
81
106
|
}
|
|
82
|
-
cancelAnimationFrame(
|
|
83
|
-
|
|
107
|
+
cancelAnimationFrame(T), T = requestAnimationFrame(() => {
|
|
108
|
+
H(), N(), Z();
|
|
84
109
|
});
|
|
85
|
-
}),
|
|
86
|
-
for (let t of e) t.addedNodes.values().filter((e) => e instanceof HTMLElement).forEach((e) =>
|
|
110
|
+
}), $ = new MutationObserver((e) => {
|
|
111
|
+
for (let t of e) t.addedNodes.values().filter((e) => e instanceof HTMLElement).forEach((e) => Q.observe(e)), t.removedNodes.values().filter((e) => e instanceof HTMLElement).forEach((e) => Q.unobserve(e));
|
|
87
112
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}),
|
|
91
|
-
|
|
92
|
-
}, {
|
|
93
|
-
|
|
94
|
-
}), s(() => {
|
|
95
|
-
|
|
96
|
-
}),
|
|
113
|
+
m(() => {
|
|
114
|
+
$.disconnect(), y.value && $.observe(y.value, { childList: !0 });
|
|
115
|
+
}), p(() => i.rows, () => {
|
|
116
|
+
D(), k(), Z();
|
|
117
|
+
}, { flush: "post" }), p(x, (e, t) => {
|
|
118
|
+
t?.removeEventListener("scroll", Z), e.addEventListener("scroll", Z, { passive: !0 });
|
|
119
|
+
}, { immediate: !0 }), s(() => {
|
|
120
|
+
D(), k(), Z();
|
|
121
|
+
}), c(() => {
|
|
122
|
+
Q.disconnect(), $.disconnect(), x.value.removeEventListener("scroll", Z), S.clear(), E.clear(), O = /* @__PURE__ */ new Float32Array();
|
|
123
|
+
}), g({
|
|
97
124
|
init() {
|
|
98
|
-
|
|
125
|
+
H(), N(), Z();
|
|
99
126
|
},
|
|
100
127
|
async scrollTo(e) {
|
|
101
128
|
let t = 0;
|
|
102
|
-
for (let n of i.rows) {
|
|
103
|
-
if (
|
|
104
|
-
t +=
|
|
129
|
+
for (let [n, r] of i.rows.entries()) {
|
|
130
|
+
if (r.id === e) break;
|
|
131
|
+
t += j(n);
|
|
105
132
|
}
|
|
106
|
-
return
|
|
107
|
-
let n =
|
|
133
|
+
return b.value.scrollTo(0, t + P), new Promise((t) => {
|
|
134
|
+
let n = b.value.querySelector(`[data-index="${e}"]`);
|
|
108
135
|
if (n instanceof HTMLElement) return t(n);
|
|
109
136
|
let r = new MutationObserver((n) => {
|
|
110
137
|
let i = n.flatMap((e) => Array.from(e.addedNodes)).filter((e) => e instanceof HTMLElement).find((t) => t.dataset.index === e);
|
|
111
138
|
i && (t(i), r.disconnect());
|
|
112
139
|
});
|
|
113
|
-
|
|
114
|
-
r.disconnect(),
|
|
115
|
-
}),
|
|
140
|
+
m(() => {
|
|
141
|
+
r.disconnect(), y.value && r.observe(y.value, { childList: !0 });
|
|
142
|
+
}), c(() => r.disconnect());
|
|
116
143
|
});
|
|
117
144
|
}
|
|
118
|
-
})
|
|
145
|
+
});
|
|
146
|
+
function re(e) {
|
|
147
|
+
let t = "grid-row";
|
|
148
|
+
return {
|
|
149
|
+
[t]: !0,
|
|
150
|
+
[`${t}--sticky`]: e.sticky
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return (t, i) => (l(), n("div", {
|
|
119
154
|
ref_key: "grid",
|
|
120
|
-
ref:
|
|
155
|
+
ref: v,
|
|
121
156
|
class: "infinite-grid",
|
|
122
|
-
style:
|
|
157
|
+
style: o(I.value)
|
|
123
158
|
}, [
|
|
124
159
|
r("div", {
|
|
125
160
|
class: "before",
|
|
126
|
-
style:
|
|
161
|
+
style: o(U.value)
|
|
127
162
|
}, null, 4),
|
|
128
163
|
r("div", {
|
|
129
164
|
ref_key: "content",
|
|
130
|
-
ref:
|
|
165
|
+
ref: y,
|
|
131
166
|
class: "content"
|
|
132
|
-
}, [(
|
|
167
|
+
}, [(l(!0), n(e, null, ee(_.value, (e) => (l(), n("div", {
|
|
133
168
|
key: e.id,
|
|
134
|
-
class:
|
|
169
|
+
class: a(re(e)),
|
|
135
170
|
"data-index": e.id
|
|
136
|
-
}, [
|
|
171
|
+
}, [te(t.$slots, "row", { row: e }, void 0, !0)], 10, h))), 128))], 512),
|
|
137
172
|
r("div", {
|
|
138
173
|
class: "after",
|
|
139
|
-
style:
|
|
174
|
+
style: o(J.value)
|
|
140
175
|
}, null, 4)
|
|
141
176
|
], 4));
|
|
142
177
|
}
|
|
143
|
-
}), [["__scopeId", "data-v-
|
|
144
|
-
e.component("VueGrid",
|
|
178
|
+
}), [["__scopeId", "data-v-e50d93db"]]), _ = { install(e) {
|
|
179
|
+
e.component("VueGrid", g);
|
|
145
180
|
} };
|
|
146
181
|
//#endregion
|
|
147
|
-
export {
|
|
182
|
+
export { _ as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yasanchezz/vue-infinite-grid",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -16,24 +16,25 @@
|
|
|
16
16
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
17
17
|
"@tsconfig/node24": "^24.0.4",
|
|
18
18
|
"@types/node": "^24.13.3",
|
|
19
|
-
"@typescript-eslint/utils": "^8.
|
|
19
|
+
"@typescript-eslint/utils": "^8.67.0",
|
|
20
20
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
21
21
|
"@vue/eslint-config-typescript": "^14.9.0",
|
|
22
22
|
"@vue/tsconfig": "^0.9.1",
|
|
23
|
-
"eslint": "^10.
|
|
23
|
+
"eslint": "^10.8.1",
|
|
24
24
|
"eslint-config-prettier": "^10.1.8",
|
|
25
25
|
"eslint-plugin-oxlint": "~1.69.0",
|
|
26
26
|
"eslint-plugin-vue": "~10.9.2",
|
|
27
27
|
"jiti": "^2.7.0",
|
|
28
|
-
"npm-run-all2": "^9.0.
|
|
28
|
+
"npm-run-all2": "^9.0.3",
|
|
29
29
|
"oxfmt": "^0.54.0",
|
|
30
30
|
"oxlint": "~1.69.0",
|
|
31
|
+
"sass-embedded": "^1.100.0",
|
|
31
32
|
"typescript": "~6.0.3",
|
|
32
|
-
"vite": "^8.1
|
|
33
|
+
"vite": "^8.2.1",
|
|
33
34
|
"vite-plugin-dts": "^5.0.3",
|
|
34
|
-
"vite-plugin-vue-devtools": "^8.1
|
|
35
|
+
"vite-plugin-vue-devtools": "^8.2.1",
|
|
35
36
|
"vue-eslint-parser": "^10.4.1",
|
|
36
|
-
"vue-tsc": "^3.3.
|
|
37
|
+
"vue-tsc": "^3.3.9"
|
|
37
38
|
},
|
|
38
39
|
"engines": {
|
|
39
40
|
"node": ">= 24.12.0",
|
package/dist/favicon.ico
DELETED
|
Binary file
|