···
69
69
let selectedQr = $derived(selectedLink?.qr ?? null);
70
70
let selectedUrl = $derived(selectedLink?.url);
71
71
72
72
-
// Grid calculation state
72
72
+
// Grid calculation - simple defaults, will be calculated on mount
73
73
let gridEl = $state(/** @type {HTMLElement | null} */ (null));
74
74
-
let size = $state(0);
75
75
-
let cols = $state(1);
74
74
+
let size = $state(150);
75
75
+
let cols = $state(Math.ceil(Math.sqrt(links.length)));
76
76
77
77
function updateGrid() {
78
78
if (!gridEl) return;
···
96
96
cols = bestCols;
97
97
}
98
98
99
99
-
// Set up ResizeObserver when gridEl becomes available
100
100
-
$effect(() => {
101
101
-
if (!gridEl) return;
102
102
-
103
103
-
updateGrid();
104
104
-
const obs = new ResizeObserver(updateGrid);
105
105
-
obs.observe(gridEl);
106
106
-
107
107
-
return () => obs.disconnect();
108
108
-
});
109
109
-
110
99
onMount(() => {
111
100
/**
112
101
* @param {TouchEvent} e
···
120
109
121
110
document.body.addEventListener("touchstart", handleBodyTouch);
122
111
112
112
+
// Set up ResizeObserver for qrs grid
113
113
+
let obs = /** @type {ResizeObserver | null} */ (null);
114
114
+
115
115
+
// Use MutationObserver to wait for gridEl to appear in DOM
116
116
+
const mutObs = new MutationObserver(() => {
117
117
+
if (gridEl && !obs) {
118
118
+
updateGrid();
119
119
+
obs = new ResizeObserver(updateGrid);
120
120
+
obs.observe(gridEl);
121
121
+
}
122
122
+
});
123
123
+
mutObs.observe(document.body, { childList: true, subtree: true });
124
124
+
125
125
+
// Also check immediately in case element already exists
126
126
+
if (gridEl) {
127
127
+
updateGrid();
128
128
+
obs = new ResizeObserver(updateGrid);
129
129
+
obs.observe(gridEl);
130
130
+
}
131
131
+
123
132
return () => {
124
133
document.body.removeEventListener("touchstart", handleBodyTouch);
134
134
+
obs?.disconnect();
135
135
+
mutObs.disconnect();
125
136
};
126
137
});
127
138
···
140
151
<div
141
152
class="qrs-grid"
142
153
bind:this={gridEl}
143
143
-
style="grid-template-columns: repeat({cols}, {size || 100}px)"
154
154
+
style="grid-template-columns: repeat({cols}, {size}px)"
144
155
>
145
156
{#each links as link, index (link.title)}
146
157
<a
147
158
href={link.url}
148
159
target="_blank"
149
160
class="qr-card"
150
150
-
style="width: {size || 100}px; height: {size || 100}px;"
151
151
-
transition:fade={{ duration: 400, delay: 80 * index }}
161
161
+
style="width: {size}px; height: {size}px; animation-delay: {index * 50}ms;"
152
162
>
153
163
<span class="qr-card-title">{link.title}</span>
154
164
<div class="qr-card-code">
···
267
277
height: 100%;
268
278
overflow: hidden;
269
279
box-sizing: border-box;
280
280
+
transition: grid-template-columns 0.3s ease;
270
281
}
271
282
272
283
.qr-card {
···
276
287
justify-content: center;
277
288
text-decoration: none;
278
289
box-sizing: border-box;
290
290
+
transition: width 0.3s ease, height 0.3s ease;
291
291
+
animation: fadeIn 0.4s ease forwards;
292
292
+
opacity: 0;
293
293
+
}
294
294
+
295
295
+
@keyframes fadeIn {
296
296
+
to {
297
297
+
opacity: 1;
298
298
+
}
279
299
}
280
300
281
301
.qr-card-title {