@hkdigital/lib-core 0.5.7 → 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,24 +203,13 @@
|
|
|
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
215
|
// $inspect('isLandscape', isLandscape);
|
|
@@ -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);
|
|
@@ -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';
|
|
@@ -469,7 +520,8 @@
|
|
|
469
520
|
requestDevmode,
|
|
470
521
|
requestFullscreen,
|
|
471
522
|
gameWidth,
|
|
472
|
-
gameHeight
|
|
523
|
+
gameHeight,
|
|
524
|
+
iosLandscapeHeightQuirk
|
|
473
525
|
})}
|
|
474
526
|
</ScaledContainer>
|
|
475
527
|
<!-- Portrait content -->
|
|
@@ -493,7 +545,8 @@
|
|
|
493
545
|
requestDevmode,
|
|
494
546
|
requestFullscreen,
|
|
495
547
|
gameWidth,
|
|
496
|
-
gameHeight
|
|
548
|
+
gameHeight,
|
|
549
|
+
iosLandscapeHeightQuirk
|
|
497
550
|
})}
|
|
498
551
|
</ScaledContainer>
|
|
499
552
|
{:else if supportsFullscreen && !isDevMode}
|
|
@@ -517,7 +570,8 @@
|
|
|
517
570
|
requestDevmode,
|
|
518
571
|
requestFullscreen,
|
|
519
572
|
gameWidth,
|
|
520
|
-
gameHeight
|
|
573
|
+
gameHeight,
|
|
574
|
+
iosLandscapeHeightQuirk
|
|
521
575
|
})}
|
|
522
576
|
</ScaledContainer>
|
|
523
577
|
{:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
|
|
@@ -541,7 +595,8 @@
|
|
|
541
595
|
requestDevmode,
|
|
542
596
|
requestFullscreen,
|
|
543
597
|
gameWidth,
|
|
544
|
-
gameHeight
|
|
598
|
+
gameHeight,
|
|
599
|
+
iosLandscapeHeightQuirk
|
|
545
600
|
})}
|
|
546
601
|
</ScaledContainer>
|
|
547
602
|
{:else}
|
|
@@ -566,7 +621,8 @@
|
|
|
566
621
|
requestDevmode,
|
|
567
622
|
requestFullscreen,
|
|
568
623
|
gameWidth,
|
|
569
|
-
gameHeight
|
|
624
|
+
gameHeight,
|
|
625
|
+
iosLandscapeHeightQuirk
|
|
570
626
|
})}
|
|
571
627
|
</ScaledContainer>
|
|
572
628
|
<!-- Portrait content -->
|
|
@@ -590,7 +646,8 @@
|
|
|
590
646
|
requestDevmode,
|
|
591
647
|
requestFullscreen,
|
|
592
648
|
gameWidth,
|
|
593
|
-
gameHeight
|
|
649
|
+
gameHeight,
|
|
650
|
+
iosLandscapeHeightQuirk
|
|
594
651
|
})}
|
|
595
652
|
</ScaledContainer>
|
|
596
653
|
{/if}
|
|
@@ -609,13 +666,16 @@
|
|
|
609
666
|
isLandscape,
|
|
610
667
|
isPortrait: !isLandscape,
|
|
611
668
|
isMobile,
|
|
669
|
+
isIos,
|
|
670
|
+
isAndroid,
|
|
612
671
|
os,
|
|
613
672
|
isFullscreen,
|
|
614
673
|
isDevMode,
|
|
615
674
|
requestDevmode,
|
|
616
675
|
requestFullscreen,
|
|
617
676
|
gameWidth,
|
|
618
|
-
gameHeight
|
|
677
|
+
gameHeight,
|
|
678
|
+
iosLandscapeHeightQuirk
|
|
619
679
|
})}
|
|
620
680
|
</ScaledContainer>
|
|
621
681
|
<!-- Portrait content -->
|
|
@@ -631,13 +691,16 @@
|
|
|
631
691
|
isLandscape,
|
|
632
692
|
isPortrait: !isLandscape,
|
|
633
693
|
isMobile,
|
|
694
|
+
isIos,
|
|
695
|
+
isAndroid,
|
|
634
696
|
os,
|
|
635
697
|
isFullscreen,
|
|
636
698
|
isDevMode,
|
|
637
699
|
requestDevmode,
|
|
638
700
|
requestFullscreen,
|
|
639
701
|
gameWidth,
|
|
640
|
-
gameHeight
|
|
702
|
+
gameHeight,
|
|
703
|
+
iosLandscapeHeightQuirk
|
|
641
704
|
})}
|
|
642
705
|
</ScaledContainer>
|
|
643
706
|
{/if}
|
|
@@ -657,10 +720,17 @@
|
|
|
657
720
|
}
|
|
658
721
|
|
|
659
722
|
:global(html.game-box-no-scroll) {
|
|
660
|
-
overflow:
|
|
723
|
+
overflow: hidden; /* allow resize events, prevent via JavaScript */
|
|
661
724
|
scrollbar-width: none; /* Firefox */
|
|
662
725
|
-ms-overflow-style: none; /* IE and Edge */
|
|
663
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
|
+
}
|
|
664
734
|
:global(html.game-box-no-scroll::-webkit-scrollbar) {
|
|
665
735
|
display: none;
|
|
666
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
|
};
|