@hkdigital/lib-core 0.5.6 → 0.5.8
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.
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* requestDevmode:function,
|
|
22
22
|
* requestFullscreen:function,
|
|
23
23
|
* gameWidth: number,
|
|
24
|
-
* gameHeight: number
|
|
24
|
+
* gameHeight: number,
|
|
25
|
+
* iosLandscapeHeightQuirk: boolean
|
|
25
26
|
* }} SnippetParams
|
|
26
27
|
*/
|
|
27
28
|
|
|
@@ -129,6 +130,38 @@
|
|
|
129
130
|
|
|
130
131
|
const isAppleMobile = /iPhone|iPod/.test(navigator.userAgent);
|
|
131
132
|
|
|
133
|
+
let os = $state();
|
|
134
|
+
let isIos = $derived(os === 'iOS');
|
|
135
|
+
let isAndroid = $derived(os === 'Android');
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Detect iOS landscape height quirk (status bar appears/disappears)
|
|
139
|
+
*
|
|
140
|
+
* Detection: in landscape, innerHeight is ~20px less than screen.width
|
|
141
|
+
*
|
|
142
|
+
* NOTE: This detection is reactive and will update when dimensions change.
|
|
143
|
+
* iOS PWA only fires viewport resize events when there's scrollable
|
|
144
|
+
* overflow content, which we add via CSS ::after when quirk is detected.
|
|
145
|
+
*/
|
|
146
|
+
let iosLandscapeHeightQuirk = $derived.by(() => {
|
|
147
|
+
// Force reactivity by accessing these variables
|
|
148
|
+
const currentLandscape = isLandscape;
|
|
149
|
+
const currentIos = isIos;
|
|
150
|
+
const width = iosWindowWidth ?? windowWidth;
|
|
151
|
+
const height = iosWindowHeight ?? windowHeight;
|
|
152
|
+
|
|
153
|
+
if (!currentLandscape || !currentIos) return false;
|
|
154
|
+
|
|
155
|
+
if (!width || !height || !window.screen) return false;
|
|
156
|
+
|
|
157
|
+
// In landscape: window.innerHeight should equal screen.width
|
|
158
|
+
// If it's 20px less, the quirk is active
|
|
159
|
+
const screenWidth = window.screen.width;
|
|
160
|
+
const heightDifference = screenWidth - height;
|
|
161
|
+
|
|
162
|
+
return heightDifference >= 18 && heightDifference <= 22;
|
|
163
|
+
});
|
|
164
|
+
|
|
132
165
|
// Debounce window dimensions on iOS to skip intermediate resize events
|
|
133
166
|
let skipNextResize = false;
|
|
134
167
|
let resetTimer;
|
|
@@ -170,29 +203,18 @@
|
|
|
170
203
|
});
|
|
171
204
|
|
|
172
205
|
$effect(() => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
debouncedWindowWidth,
|
|
179
|
-
debouncedWindowHeight,
|
|
180
|
-
iosWindowWidth,
|
|
181
|
-
iosWindowHeight
|
|
182
|
-
});
|
|
206
|
+
// Use matchMedia for orientation detection (works on all iOS versions)
|
|
207
|
+
// This is more reliable than screen.orientation.angle
|
|
208
|
+
const isPortraitMedia =
|
|
209
|
+
typeof window !== 'undefined' &&
|
|
210
|
+
window.matchMedia('(orientation: portrait)').matches;
|
|
183
211
|
|
|
184
|
-
|
|
185
|
-
// For iOS PWA, use iOS-specific dimensions
|
|
186
|
-
isLandscape = iosWindowWidth > iosWindowHeight;
|
|
187
|
-
} else {
|
|
188
|
-
// For non-PWA, use debounced window dimensions
|
|
189
|
-
isLandscape = debouncedWindowWidth > debouncedWindowHeight;
|
|
190
|
-
}
|
|
212
|
+
isLandscape = !isPortraitMedia;
|
|
191
213
|
});
|
|
192
214
|
|
|
193
|
-
$inspect('isLandscape', isLandscape);
|
|
194
|
-
$inspect('windowWidth/Height', windowWidth, windowHeight);
|
|
195
|
-
$inspect('iosWindowWidth/Height', iosWindowWidth, iosWindowHeight);
|
|
215
|
+
// $inspect('isLandscape', isLandscape);
|
|
216
|
+
// $inspect('windowWidth/Height', windowWidth, windowHeight);
|
|
217
|
+
// $inspect('iosWindowWidth/Height', iosWindowWidth, iosWindowHeight);
|
|
196
218
|
|
|
197
219
|
// Update game dimensions based on window size and orientation
|
|
198
220
|
$effect(() => {
|
|
@@ -250,11 +272,6 @@
|
|
|
250
272
|
|
|
251
273
|
let isPwa = $state(false);
|
|
252
274
|
|
|
253
|
-
let os = $state();
|
|
254
|
-
|
|
255
|
-
let isIos = $derived(os === 'iOS');
|
|
256
|
-
let isAndroid = $derived(os === 'Android');
|
|
257
|
-
|
|
258
275
|
let isMobile = $state(false);
|
|
259
276
|
|
|
260
277
|
let isDevMode = $state(false);
|
|
@@ -277,13 +294,13 @@
|
|
|
277
294
|
iosWindowWidth = window.innerWidth;
|
|
278
295
|
iosWindowHeight = window.innerHeight;
|
|
279
296
|
}
|
|
280
|
-
console.debug('updateIosWidthHeight', {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
});
|
|
297
|
+
// console.debug('updateIosWidthHeight', {
|
|
298
|
+
// angle,
|
|
299
|
+
// 'window.innerWidth': window.innerWidth,
|
|
300
|
+
// 'window.innerHeight': window.innerHeight,
|
|
301
|
+
// iosWindowWidth,
|
|
302
|
+
// iosWindowHeight
|
|
303
|
+
// });
|
|
287
304
|
}
|
|
288
305
|
}
|
|
289
306
|
|
|
@@ -302,7 +319,23 @@
|
|
|
302
319
|
|
|
303
320
|
updateIosWidthHeight();
|
|
304
321
|
|
|
322
|
+
// Listen for orientation changes using matchMedia (works on all iOS)
|
|
323
|
+
const portraitMediaQuery = window.matchMedia('(orientation: portrait)');
|
|
324
|
+
const handleOrientationChange = (e) => {
|
|
325
|
+
isLandscape = !e.matches;
|
|
326
|
+
|
|
327
|
+
// Update iOS dimensions if needed
|
|
328
|
+
if (isPwa && isAppleMobile) {
|
|
329
|
+
updateIosWidthHeight();
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
portraitMediaQuery.addEventListener('change', handleOrientationChange);
|
|
333
|
+
|
|
305
334
|
show = true;
|
|
335
|
+
|
|
336
|
+
return () => {
|
|
337
|
+
portraitMediaQuery.removeEventListener('change', handleOrientationChange);
|
|
338
|
+
};
|
|
306
339
|
});
|
|
307
340
|
|
|
308
341
|
onMount(() => {
|
|
@@ -310,11 +343,29 @@
|
|
|
310
343
|
const html = document.documentElement;
|
|
311
344
|
html.classList.add(gameBoxNoScroll);
|
|
312
345
|
|
|
346
|
+
// Prevent page scroll while allowing child elements to scroll
|
|
347
|
+
const preventPageScroll = () => {
|
|
348
|
+
window.scrollTo(0, 0);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
window.addEventListener('scroll', preventPageScroll, { passive: true });
|
|
352
|
+
|
|
313
353
|
return () => {
|
|
314
354
|
html.classList.remove(gameBoxNoScroll);
|
|
355
|
+
window.removeEventListener('scroll', preventPageScroll);
|
|
315
356
|
};
|
|
316
357
|
});
|
|
317
358
|
|
|
359
|
+
// Toggle overflow content based on quirk detection
|
|
360
|
+
$effect(() => {
|
|
361
|
+
const html = document.documentElement;
|
|
362
|
+
if (iosLandscapeHeightQuirk) {
|
|
363
|
+
html.classList.add('game-box-has-quirk');
|
|
364
|
+
} else {
|
|
365
|
+
html.classList.remove('game-box-has-quirk');
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
318
369
|
function getOS() {
|
|
319
370
|
if (isAppleMobile) {
|
|
320
371
|
return 'iOS';
|
|
@@ -427,7 +478,10 @@
|
|
|
427
478
|
<!-- margin: /* top | right | bottom | left */ -->
|
|
428
479
|
|
|
429
480
|
{#if gameHeight}
|
|
430
|
-
<div
|
|
481
|
+
<div
|
|
482
|
+
class:center
|
|
483
|
+
style:height={center ? `${iosWindowHeight ?? windowHeight}px` : undefined}
|
|
484
|
+
>
|
|
431
485
|
<div
|
|
432
486
|
data-component="game-box"
|
|
433
487
|
data-orientation={isLandscape ? 'landscape' : 'portrait'}
|
|
@@ -466,7 +520,8 @@
|
|
|
466
520
|
requestDevmode,
|
|
467
521
|
requestFullscreen,
|
|
468
522
|
gameWidth,
|
|
469
|
-
gameHeight
|
|
523
|
+
gameHeight,
|
|
524
|
+
iosLandscapeHeightQuirk
|
|
470
525
|
})}
|
|
471
526
|
</ScaledContainer>
|
|
472
527
|
<!-- Portrait content -->
|
|
@@ -490,7 +545,8 @@
|
|
|
490
545
|
requestDevmode,
|
|
491
546
|
requestFullscreen,
|
|
492
547
|
gameWidth,
|
|
493
|
-
gameHeight
|
|
548
|
+
gameHeight,
|
|
549
|
+
iosLandscapeHeightQuirk
|
|
494
550
|
})}
|
|
495
551
|
</ScaledContainer>
|
|
496
552
|
{:else if supportsFullscreen && !isDevMode}
|
|
@@ -514,7 +570,8 @@
|
|
|
514
570
|
requestDevmode,
|
|
515
571
|
requestFullscreen,
|
|
516
572
|
gameWidth,
|
|
517
|
-
gameHeight
|
|
573
|
+
gameHeight,
|
|
574
|
+
iosLandscapeHeightQuirk
|
|
518
575
|
})}
|
|
519
576
|
</ScaledContainer>
|
|
520
577
|
{:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
|
|
@@ -538,7 +595,8 @@
|
|
|
538
595
|
requestDevmode,
|
|
539
596
|
requestFullscreen,
|
|
540
597
|
gameWidth,
|
|
541
|
-
gameHeight
|
|
598
|
+
gameHeight,
|
|
599
|
+
iosLandscapeHeightQuirk
|
|
542
600
|
})}
|
|
543
601
|
</ScaledContainer>
|
|
544
602
|
{:else}
|
|
@@ -563,7 +621,8 @@
|
|
|
563
621
|
requestDevmode,
|
|
564
622
|
requestFullscreen,
|
|
565
623
|
gameWidth,
|
|
566
|
-
gameHeight
|
|
624
|
+
gameHeight,
|
|
625
|
+
iosLandscapeHeightQuirk
|
|
567
626
|
})}
|
|
568
627
|
</ScaledContainer>
|
|
569
628
|
<!-- Portrait content -->
|
|
@@ -587,7 +646,8 @@
|
|
|
587
646
|
requestDevmode,
|
|
588
647
|
requestFullscreen,
|
|
589
648
|
gameWidth,
|
|
590
|
-
gameHeight
|
|
649
|
+
gameHeight,
|
|
650
|
+
iosLandscapeHeightQuirk
|
|
591
651
|
})}
|
|
592
652
|
</ScaledContainer>
|
|
593
653
|
{/if}
|
|
@@ -606,13 +666,16 @@
|
|
|
606
666
|
isLandscape,
|
|
607
667
|
isPortrait: !isLandscape,
|
|
608
668
|
isMobile,
|
|
669
|
+
isIos,
|
|
670
|
+
isAndroid,
|
|
609
671
|
os,
|
|
610
672
|
isFullscreen,
|
|
611
673
|
isDevMode,
|
|
612
674
|
requestDevmode,
|
|
613
675
|
requestFullscreen,
|
|
614
676
|
gameWidth,
|
|
615
|
-
gameHeight
|
|
677
|
+
gameHeight,
|
|
678
|
+
iosLandscapeHeightQuirk
|
|
616
679
|
})}
|
|
617
680
|
</ScaledContainer>
|
|
618
681
|
<!-- Portrait content -->
|
|
@@ -628,13 +691,16 @@
|
|
|
628
691
|
isLandscape,
|
|
629
692
|
isPortrait: !isLandscape,
|
|
630
693
|
isMobile,
|
|
694
|
+
isIos,
|
|
695
|
+
isAndroid,
|
|
631
696
|
os,
|
|
632
697
|
isFullscreen,
|
|
633
698
|
isDevMode,
|
|
634
699
|
requestDevmode,
|
|
635
700
|
requestFullscreen,
|
|
636
701
|
gameWidth,
|
|
637
|
-
gameHeight
|
|
702
|
+
gameHeight,
|
|
703
|
+
iosLandscapeHeightQuirk
|
|
638
704
|
})}
|
|
639
705
|
</ScaledContainer>
|
|
640
706
|
{/if}
|
|
@@ -654,10 +720,17 @@
|
|
|
654
720
|
}
|
|
655
721
|
|
|
656
722
|
:global(html.game-box-no-scroll) {
|
|
657
|
-
overflow:
|
|
723
|
+
overflow: hidden; /* allow resize events, prevent via JavaScript */
|
|
658
724
|
scrollbar-width: none; /* Firefox */
|
|
659
725
|
-ms-overflow-style: none; /* IE and Edge */
|
|
660
726
|
}
|
|
727
|
+
/* Only add overflow content when quirk is detected */
|
|
728
|
+
:global(html.game-box-no-scroll.game-box-has-quirk::after) {
|
|
729
|
+
content: '\A'; /* newline character */
|
|
730
|
+
white-space: pre; /* preserve newline */
|
|
731
|
+
display: block;
|
|
732
|
+
height: 20px; /* create overflow to trigger iOS resize events */
|
|
733
|
+
}
|
|
661
734
|
:global(html.game-box-no-scroll::-webkit-scrollbar) {
|
|
662
735
|
display: none;
|
|
663
736
|
}
|
|
@@ -100,6 +100,7 @@ declare const GameBox: import("svelte").Component<{
|
|
|
100
100
|
requestFullscreen: Function;
|
|
101
101
|
gameWidth: number;
|
|
102
102
|
gameHeight: number;
|
|
103
|
+
iosLandscapeHeightQuirk: boolean;
|
|
103
104
|
}]>;
|
|
104
105
|
snippetPortrait?: import("svelte").Snippet<[{
|
|
105
106
|
isLandscape: boolean;
|
|
@@ -114,6 +115,7 @@ declare const GameBox: import("svelte").Component<{
|
|
|
114
115
|
requestFullscreen: Function;
|
|
115
116
|
gameWidth: number;
|
|
116
117
|
gameHeight: number;
|
|
118
|
+
iosLandscapeHeightQuirk: boolean;
|
|
117
119
|
}]>;
|
|
118
120
|
snippetRequireFullscreen?: import("svelte").Snippet<[{
|
|
119
121
|
isLandscape: boolean;
|
|
@@ -128,6 +130,7 @@ declare const GameBox: import("svelte").Component<{
|
|
|
128
130
|
requestFullscreen: Function;
|
|
129
131
|
gameWidth: number;
|
|
130
132
|
gameHeight: number;
|
|
133
|
+
iosLandscapeHeightQuirk: boolean;
|
|
131
134
|
}]>;
|
|
132
135
|
snippetInstallOnHomeScreen?: import("svelte").Snippet<[{
|
|
133
136
|
isLandscape: boolean;
|
|
@@ -142,6 +145,7 @@ declare const GameBox: import("svelte").Component<{
|
|
|
142
145
|
requestFullscreen: Function;
|
|
143
146
|
gameWidth: number;
|
|
144
147
|
gameHeight: number;
|
|
148
|
+
iosLandscapeHeightQuirk: boolean;
|
|
145
149
|
}]>;
|
|
146
150
|
}, {}, "">;
|
|
147
151
|
type SnippetParams = {
|
|
@@ -157,4 +161,5 @@ type SnippetParams = {
|
|
|
157
161
|
requestFullscreen: Function;
|
|
158
162
|
gameWidth: number;
|
|
159
163
|
gameHeight: number;
|
|
164
|
+
iosLandscapeHeightQuirk: boolean;
|
|
160
165
|
};
|