@hkdigital/lib-core 0.5.38 → 0.5.40

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.
@@ -392,7 +392,7 @@ export async function httpRequest(options) {
392
392
 
393
393
  if (!isTestEnv) {
394
394
  if (cachedResponse) {
395
- console.debug(`http:cache-hit [${url.pathname}]`);
395
+ // console.debug(`http:cache-hit [${url.pathname}]`);
396
396
  // console.debug(`cached-response has body: ${!!cachedResponse.body}`);
397
397
  // console.debug(`cached-response content-length: ${cachedResponse.headers.get('content-length')}`);
398
398
  return cachedResponse;
@@ -12,10 +12,7 @@
12
12
  getIsMobile
13
13
  } from '../../../browser/info/device.js';
14
14
 
15
- import {
16
- getIsPwa,
17
- getIsFullscreen
18
- } from '../../../browser/info/display.js';
15
+ import { getIsPwa, getIsFullscreen } from '../../../browser/info/display.js';
19
16
 
20
17
  import ScaledContainer from './ScaledContainer.svelte';
21
18
 
@@ -53,6 +50,7 @@
53
50
  * snippetPortrait?: GameBoxSnippet,
54
51
  * snippetRequireFullscreen?: GameBoxSnippet,
55
52
  * snippetInstallOnHomeScreen?: GameBoxSnippet,
53
+ * allowDevModeOnLocalhost?: boolean,
56
54
  * debug?: boolean,
57
55
  * [attr: string]: any
58
56
  * }}
@@ -87,6 +85,7 @@
87
85
  snippetRequireFullscreen,
88
86
  snippetInstallOnHomeScreen,
89
87
 
88
+ allowDevModeOnLocalhost = true,
90
89
  debug = false
91
90
  } = $props();
92
91
 
@@ -105,23 +104,21 @@
105
104
 
106
105
  let isLandscape = $state();
107
106
 
108
- let gameWidth = $derived.by( () => {
109
- if( isLandscape ) {
107
+ let gameWidth = $derived.by(() => {
108
+ if (isLandscape) {
110
109
  return gameWidthOnLandscape;
111
- }
112
- else {
110
+ } else {
113
111
  return gameWidthOnPortrait;
114
112
  }
115
- } );
113
+ });
116
114
 
117
- let gameHeight = $derived.by( () => {
118
- if( isLandscape ) {
115
+ let gameHeight = $derived.by(() => {
116
+ if (isLandscape) {
119
117
  return gameHeightOnLandscape;
120
- }
121
- else {
118
+ } else {
122
119
  return gameHeightOnPortrait;
123
120
  }
124
- } );
121
+ });
125
122
 
126
123
  // iPad is also considered Apple mobile
127
124
  const isAppleMobile = getIsAppleMobile();
@@ -132,12 +129,7 @@
132
129
 
133
130
  // Update iOS dimensions when window size changes
134
131
  $effect(() => {
135
- if (
136
- isPwa &&
137
- isAppleMobile &&
138
- windowWidth &&
139
- windowHeight
140
- ) {
132
+ if (isPwa && isAppleMobile && windowWidth && windowHeight) {
141
133
  updateIosWidthHeightAndOrientation();
142
134
  }
143
135
  });
@@ -146,8 +138,9 @@
146
138
  // Use matchMedia as a trigger for orientation changes
147
139
  // The actual orientation is determined in updateIosWidthHeightAndOrientation()
148
140
  if (typeof window !== 'undefined') {
149
- const isPortraitMedia =
150
- window.matchMedia('(orientation: portrait)').matches;
141
+ const isPortraitMedia = window.matchMedia(
142
+ '(orientation: portrait)'
143
+ ).matches;
151
144
 
152
145
  // Trigger iOS dimension update when orientation might have changed
153
146
  if (isPwa && isAppleMobile) {
@@ -164,8 +157,20 @@
164
157
  if (debug) {
165
158
  console.log('[GameBox] isLandscape:', isLandscape);
166
159
  console.log('[GameBox] windowWidth/Height:', windowWidth, windowHeight);
167
- console.log('[GameBox] iosWindowWidth/Height:',
168
- iosWindowWidth, iosWindowHeight);
160
+ console.log(
161
+ '[GameBox] iosWindowWidth/Height:',
162
+ iosWindowWidth,
163
+ iosWindowHeight
164
+ );
165
+
166
+ console.log({
167
+ isPwa,
168
+ isAppleMobile,
169
+ isMobile,
170
+ isIos,
171
+ isAndroid,
172
+ isFullscreen
173
+ });
169
174
  }
170
175
  });
171
176
 
@@ -179,8 +184,7 @@
179
184
  const availWidth = width - marginLeft - marginRight;
180
185
  const availHeight = height - marginTop - marginBottom;
181
186
 
182
- if( debug )
183
- {
187
+ if (debug) {
184
188
  console.debug('GameBox margins:', {
185
189
  marginLeft,
186
190
  marginRight,
@@ -199,30 +203,24 @@
199
203
  aspectOnLandscape
200
204
  });
201
205
 
202
- if( aspectOnLandscape )
203
- {
206
+ if (aspectOnLandscape) {
204
207
  gameHeightOnLandscape = gameWidthOnLandscape / aspectOnLandscape;
205
- }
206
- else {
208
+ } else {
207
209
  gameHeightOnLandscape = availHeight;
208
210
  }
209
- }
210
- else {
211
+ } else {
211
212
  gameWidthOnPortrait = getGameWidthOnPortrait({
212
213
  windowWidth: availWidth,
213
214
  windowHeight: availHeight,
214
215
  aspectOnPortrait
215
216
  });
216
217
 
217
- if( aspectOnPortrait )
218
- {
218
+ if (aspectOnPortrait) {
219
219
  gameHeightOnPortrait = gameWidthOnPortrait / aspectOnPortrait;
220
- }
221
- else {
220
+ } else {
222
221
  gameHeightOnPortrait = availHeight;
223
222
  }
224
223
  }
225
-
226
224
  });
227
225
 
228
226
  let show = $state(false);
@@ -240,7 +238,6 @@
240
238
 
241
239
  function updateIosWidthHeightAndOrientation() {
242
240
  if (isAppleMobile) {
243
-
244
241
  // unreliable on ios >>
245
242
  // const angle = screen.orientation.angle;
246
243
  // if( window.matchMedia('(orientation: portrait)').matches ) {
@@ -274,16 +271,13 @@
274
271
  iosWindowWidth = window.innerWidth;
275
272
  iosWindowHeight = window.innerHeight;
276
273
 
277
- if( iosWindowHeight > iosWindowWidth )
278
- {
274
+ if (iosWindowHeight > iosWindowWidth) {
279
275
  isLandscape = false;
280
- }
281
- else {
276
+ } else {
282
277
  isLandscape = true;
283
278
  }
284
279
 
285
- if( debug )
286
- {
280
+ if (debug) {
287
281
  console.debug('updateIosWidthHeightAndOrientation', {
288
282
  'screen.orientation.type': screen.orientation.type,
289
283
  isLandscape,
@@ -328,8 +322,7 @@
328
322
  // App visibility detection for iOS debugging
329
323
  const handleVisibilityChange = () => {
330
324
  if (document.visibilityState === 'visible') {
331
-
332
- if( debug ) {
325
+ if (debug) {
333
326
  console.log('App became visible:', {
334
327
  'window.innerWidth': window.innerWidth,
335
328
  'window.innerHeight': window.innerHeight,
@@ -337,13 +330,13 @@
337
330
  'screen.height': screen.height,
338
331
  'screen.orientation.angle': screen.orientation.angle,
339
332
  'screen.orientation.type': screen.orientation.type,
340
- 'matchMedia portrait':
341
- window.matchMedia('(orientation: portrait)').matches,
342
- 'isLandscape': isLandscape,
343
- 'gameWidth': gameWidth,
344
- 'gameHeight': gameHeight,
345
- 'iosWindowWidth': iosWindowWidth,
346
- 'iosWindowHeight': iosWindowHeight
333
+ 'matchMedia portrait': window.matchMedia('(orientation: portrait)')
334
+ .matches,
335
+ isLandscape: isLandscape,
336
+ gameWidth: gameWidth,
337
+ gameHeight: gameHeight,
338
+ iosWindowWidth: iosWindowWidth,
339
+ iosWindowHeight: iosWindowHeight
347
340
  });
348
341
  }
349
342
 
@@ -382,7 +375,7 @@
382
375
  // };
383
376
  return () => {
384
377
  html.classList.remove(gameBoxNoScroll);
385
- }
378
+ };
386
379
  });
387
380
 
388
381
  async function requestFullscreen() {
@@ -430,7 +423,7 @@
430
423
  }
431
424
 
432
425
  $effect(() => {
433
- if (location.hostname === 'localhost') {
426
+ if (allowDevModeOnLocalhost && location.hostname === 'localhost') {
434
427
  isDevMode = true;
435
428
  }
436
429
  });
@@ -442,6 +435,24 @@
442
435
  window.history.pushState({}, '', url);
443
436
  }
444
437
  });
438
+
439
+ let snippetParams = $derived({
440
+ isLandscape,
441
+ isPortrait: !isLandscape,
442
+ isMobile,
443
+ isIos,
444
+ isAndroid,
445
+ isIpadOS,
446
+ isPwa,
447
+ isFullscreen,
448
+ isDevMode,
449
+ requestDevmode,
450
+ requestFullscreen,
451
+ gameWidth,
452
+ gameHeight
453
+ });
454
+
455
+ // $inspect('snippetParams', snippetParams);
445
456
  </script>
446
457
 
447
458
  <svelte:window bind:innerWidth={windowWidth} bind:innerHeight={windowHeight} />
@@ -465,213 +476,144 @@
465
476
  style:margin="{marginTop}px {marginRight}px {marginBottom}px {marginLeft}px"
466
477
  {style}
467
478
  >
468
- {#if show}
469
- <!-- Render both orientations, toggle visibility to preserve state -->
479
+ {#if show && clamping }
480
+
470
481
  {#if snippetRequireFullscreen}
471
- <!-- Require fullscreen -->
482
+
472
483
  {#if isFullscreen && !isDevMode}
484
+
485
+ <!--
486
+ snippetRequireFullscreen &&
487
+ Fullscreen
488
+ => Render both orientations, set visibility to preserve state
489
+ -->
490
+
473
491
  <!-- Landscape content -->
474
492
  <ScaledContainer
475
- enableScaling={enableScaling}
493
+ {enableScaling}
476
494
  design={designLandscape}
477
495
  {clamping}
478
496
  width={gameWidthOnLandscape}
479
497
  height={gameHeightOnLandscape}
498
+ snippet={snippetLandscape}
499
+ {snippetParams}
480
500
  hidden={!isLandscape}
481
- >
482
- {@render snippetLandscape({
483
- isLandscape,
484
- isPortrait: !isLandscape,
485
- isMobile,
486
- isIos,
487
- isAndroid,
488
- isIpadOS,
489
- isPwa,
490
- isFullscreen,
491
- isDevMode,
492
- requestDevmode,
493
- requestFullscreen,
494
- gameWidth,
495
- gameHeight
496
- })}
497
- </ScaledContainer>
501
+ ></ScaledContainer>
502
+
498
503
  <!-- Portrait content -->
499
504
  <ScaledContainer
500
- enableScaling={enableScaling}
505
+ {enableScaling}
501
506
  design={designPortrait}
502
507
  {clamping}
503
508
  width={gameWidthOnPortrait}
504
509
  height={gameHeightOnPortrait}
510
+ snippet={snippetPortrait}
511
+ {snippetParams}
505
512
  hidden={isLandscape}
506
- >
507
- {@render snippetPortrait({
508
- isLandscape,
509
- isPortrait: !isLandscape,
510
- isMobile,
511
- isIos,
512
- isAndroid,
513
- isIpadOS,
514
- isPwa,
515
- isFullscreen,
516
- isDevMode,
517
- requestDevmode,
518
- requestFullscreen,
519
- gameWidth,
520
- gameHeight
521
- })}
522
- </ScaledContainer>
523
- {:else if supportsFullscreen && !isDevMode}
524
- <!-- Require fullscreen -->
513
+ ></ScaledContainer>
514
+
515
+ {:else if isMobile && snippetInstallOnHomeScreen && !isPwa && !isDevMode}
516
+
517
+ <!--
518
+ snippetRequireFullscreen &&
519
+ isMobile &&
520
+ snippetInstallOnHomeScreen &&
521
+ Not PWA
522
+ => show install on home screen message
523
+ -->
524
+
525
525
  <ScaledContainer
526
- enableScaling={enableScaling}
526
+ {enableScaling}
527
527
  design={isLandscape ? designLandscape : designPortrait}
528
528
  {clamping}
529
529
  width={gameWidth}
530
530
  height={gameHeight}
531
- >
532
- {@render snippetRequireFullscreen({
533
- isLandscape,
534
- isPortrait: !isLandscape,
535
- isMobile,
536
- isIos,
537
- isAndroid,
538
- isIpadOS,
539
- isPwa,
540
- isFullscreen,
541
- isDevMode,
542
- requestDevmode,
543
- requestFullscreen,
544
- gameWidth,
545
- gameHeight
546
- })}
547
- </ScaledContainer>
548
- {:else if isMobile && snippetInstallOnHomeScreen && !isPwa && !isDevMode}
549
- <!-- Require install on home screen on mobile -->
531
+ snippet={snippetInstallOnHomeScreen}
532
+ {snippetParams}
533
+ ></ScaledContainer>
534
+
535
+ {:else if supportsFullscreen && !isDevMode}
536
+
537
+ <!--
538
+ snippetRequireFullscreen &&
539
+ Fullscreen supported &&
540
+ Not fullscreen &&
541
+ (Not mobile OR no install snippet OR already PWA)
542
+ => show fullscreen required message
543
+ -->
544
+
550
545
  <ScaledContainer
551
- enableScaling={enableScaling}
546
+ {enableScaling}
552
547
  design={isLandscape ? designLandscape : designPortrait}
553
548
  {clamping}
554
549
  width={gameWidth}
555
550
  height={gameHeight}
556
- >
557
- {@render snippetInstallOnHomeScreen({
558
- isLandscape,
559
- isPortrait: !isLandscape,
560
- isMobile,
561
- isIos,
562
- isAndroid,
563
- isIpadOS,
564
- isPwa,
565
- isFullscreen,
566
- isDevMode,
567
- requestDevmode,
568
- requestFullscreen,
569
- gameWidth,
570
- gameHeight
571
- })}
572
- </ScaledContainer>
551
+ snippet={snippetRequireFullscreen}
552
+ {snippetParams}
553
+ ></ScaledContainer>
554
+
573
555
  {:else}
556
+ <!--
557
+ snippetRequireFullscreen &&
558
+ (Dev mode OR
559
+ Fullscreen not supported OR
560
+ Not mobile OR
561
+ No install snippet OR
562
+ Already PWA)
563
+ => show game content
564
+ -->
565
+
574
566
  <!-- Landscape content -->
567
+
575
568
  <ScaledContainer
576
- enableScaling={enableScaling}
569
+ {enableScaling}
577
570
  design={designLandscape}
578
571
  {clamping}
579
572
  width={gameWidthOnLandscape}
580
573
  height={gameHeightOnLandscape}
574
+ snippet={snippetLandscape}
575
+ {snippetParams}
581
576
  hidden={!isLandscape}
582
- >
583
- {@render snippetLandscape({
584
- isLandscape,
585
- isPortrait: !isLandscape,
586
- isMobile,
587
- isIos,
588
- isAndroid,
589
- isIpadOS,
590
- isPwa,
591
- isFullscreen,
592
- isDevMode,
593
- requestDevmode,
594
- requestFullscreen,
595
- gameWidth,
596
- gameHeight
597
- })}
598
- </ScaledContainer>
577
+ ></ScaledContainer>
578
+
599
579
  <!-- Portrait content -->
600
580
  <ScaledContainer
601
- enableScaling={enableScaling}
581
+ {enableScaling}
602
582
  design={designPortrait}
603
583
  {clamping}
604
584
  width={gameWidthOnPortrait}
605
585
  height={gameHeightOnPortrait}
586
+ snippet={snippetPortrait}
587
+ {snippetParams}
606
588
  hidden={isLandscape}
607
- >
608
- {@render snippetPortrait({
609
- isLandscape,
610
- isPortrait: !isLandscape,
611
- isMobile,
612
- isIos,
613
- isAndroid,
614
- isIpadOS,
615
- isPwa,
616
- isFullscreen,
617
- isDevMode,
618
- requestDevmode,
619
- requestFullscreen,
620
- gameWidth,
621
- gameHeight
622
- })}
623
- </ScaledContainer>
589
+ ></ScaledContainer>
624
590
  {/if}
625
591
  {:else}
626
592
  <!-- Do not require fullscreen -->
593
+
627
594
  <!-- Landscape content -->
628
595
  <ScaledContainer
629
- enableScaling={enableScaling}
596
+ {enableScaling}
630
597
  design={designLandscape}
631
598
  {clamping}
632
599
  width={gameWidthOnLandscape}
633
600
  height={gameHeightOnLandscape}
601
+ snippet={snippetLandscape}
602
+ {snippetParams}
634
603
  hidden={!isLandscape}
635
- >
636
- {@render snippetLandscape({
637
- isLandscape,
638
- isPortrait: !isLandscape,
639
- isMobile,
640
- isIos,
641
- isAndroid,
642
- os,
643
- isFullscreen,
644
- isDevMode,
645
- requestDevmode,
646
- requestFullscreen,
647
- gameWidth,
648
- gameHeight
649
- })}
650
- </ScaledContainer>
604
+ ></ScaledContainer>
605
+
651
606
  <!-- Portrait content -->
652
607
  <ScaledContainer
653
- enableScaling={enableScaling}
608
+ {enableScaling}
654
609
  design={designPortrait}
655
610
  {clamping}
656
611
  width={gameWidthOnPortrait}
657
612
  height={gameHeightOnPortrait}
613
+ snippet={snippetPortrait}
614
+ {snippetParams}
658
615
  hidden={isLandscape}
659
- >
660
- {@render snippetPortrait({
661
- isLandscape,
662
- isPortrait: !isLandscape,
663
- isMobile,
664
- isIos,
665
- isAndroid,
666
- os,
667
- isFullscreen,
668
- isDevMode,
669
- requestDevmode,
670
- requestFullscreen,
671
- gameWidth,
672
- gameHeight
673
- })}
674
- </ScaledContainer>
616
+ ></ScaledContainer>
675
617
  {/if}
676
618
  {/if}
677
619
  </div>
@@ -45,6 +45,7 @@ type GameBox = {
45
45
  snippetPortrait?: Snippet<[SnippetParams]> | undefined;
46
46
  snippetRequireFullscreen?: Snippet<[SnippetParams]> | undefined;
47
47
  snippetInstallOnHomeScreen?: Snippet<[SnippetParams]> | undefined;
48
+ allowDevModeOnLocalhost?: boolean | undefined;
48
49
  debug?: boolean | undefined;
49
50
  }>): void;
50
51
  };
@@ -92,5 +93,6 @@ declare const GameBox: import("svelte").Component<{
92
93
  snippetPortrait?: import("svelte").Snippet<[import("./typedef.js").SnippetParams]>;
93
94
  snippetRequireFullscreen?: import("svelte").Snippet<[import("./typedef.js").SnippetParams]>;
94
95
  snippetInstallOnHomeScreen?: import("svelte").Snippet<[import("./typedef.js").SnippetParams]>;
96
+ allowDevModeOnLocalhost?: boolean;
95
97
  debug?: boolean;
96
98
  }, {}, "">;
@@ -1,22 +1,5 @@
1
1
  <script>
2
- import { enableContainerScaling } from '../../../design/index.js';
3
-
4
- /**
5
- * @typedef {{
6
- * enableScaling?: boolean,
7
- * design?: {width: number, height: number},
8
- * clamping?: {
9
- * ui: {min: number, max: number},
10
- * textBase: {min: number, max: number},
11
- * textHeading: {min: number, max: number},
12
- * textUi: {min: number, max: number}
13
- * },
14
- * width: number,
15
- * height: number,
16
- * hidden?: boolean,
17
- * children: import('svelte').Snippet
18
- * }}
19
- */
2
+ import { clamp, enableContainerScaling } from '../../../design/index.js';
20
3
 
21
4
  /**
22
5
  * Wrapper component that applies container scaling to its children
@@ -24,7 +7,7 @@
24
7
  * @type {{
25
8
  * enableScaling?: boolean,
26
9
  * design?: {width: number, height: number},
27
- * clamping?: {
10
+ * clamping: {
28
11
  * ui: {min: number, max: number},
29
12
  * textBase: {min: number, max: number},
30
13
  * textHeading: {min: number, max: number},
@@ -33,17 +16,19 @@
33
16
  * width: number,
34
17
  * height: number,
35
18
  * hidden?: boolean,
36
- * children: import('svelte').Snippet
19
+ * snippet?: import('./typedef.js').GameBoxSnippet,
20
+ * snippetParams: import('./typedef.js').SnippetParams
37
21
  * }}
38
22
  */
39
23
  let {
40
24
  enableScaling = false,
41
- design = undefined,
42
- clamping = undefined,
25
+ design,
26
+ clamping,
43
27
  width,
44
28
  height,
45
29
  hidden = false,
46
- children
30
+ snippet,
31
+ snippetParams
47
32
  } = $props();
48
33
 
49
34
  let container = $state();
@@ -51,12 +36,13 @@
51
36
  // Apply container scaling when enabled and not hidden
52
37
  $effect(() => {
53
38
  if (
54
- !enableScaling ||
55
39
  !container ||
40
+ !enableScaling ||
56
41
  !width ||
57
42
  !height ||
58
- hidden ||
59
- !design
43
+ !design ||
44
+ !clamping ||
45
+ hidden
60
46
  ) {
61
47
  return;
62
48
  }
@@ -72,14 +58,16 @@
72
58
  });
73
59
  </script>
74
60
 
75
- <div
76
- bind:this={container}
77
- class:hidden
78
- style:width="{width}px"
79
- style:height="{height}px"
80
- >
81
- {@render children()}
82
- </div>
61
+ {#if snippet && snippetParams}
62
+ <div
63
+ bind:this={container}
64
+ class:hidden
65
+ style:width="{width}px"
66
+ style:height="{height}px"
67
+ >
68
+ {@render snippet(snippetParams)}
69
+ </div>
70
+ {/if}
83
71
 
84
72
  <style>
85
73
  .hidden {
@@ -7,7 +7,7 @@ type ScaledContainer = {
7
7
  width: number;
8
8
  height: number;
9
9
  } | undefined;
10
- clamping?: {
10
+ clamping: {
11
11
  ui: {
12
12
  min: number;
13
13
  max: number;
@@ -24,11 +24,12 @@ type ScaledContainer = {
24
24
  min: number;
25
25
  max: number;
26
26
  };
27
- } | undefined;
27
+ };
28
28
  width: number;
29
29
  height: number;
30
30
  hidden?: boolean | undefined;
31
- children: Snippet<[]>;
31
+ snippet?: Snippet<[SnippetParams]> | undefined;
32
+ snippetParams: SnippetParams;
32
33
  }>): void;
33
34
  };
34
35
  declare const ScaledContainer: import("svelte").Component<{
@@ -37,7 +38,7 @@ declare const ScaledContainer: import("svelte").Component<{
37
38
  width: number;
38
39
  height: number;
39
40
  };
40
- clamping?: {
41
+ clamping: {
41
42
  ui: {
42
43
  min: number;
43
44
  max: number;
@@ -58,5 +59,6 @@ declare const ScaledContainer: import("svelte").Component<{
58
59
  width: number;
59
60
  height: number;
60
61
  hidden?: boolean;
61
- children: import("svelte").Snippet;
62
+ snippet?: import("./typedef.js").GameBoxSnippet;
63
+ snippetParams: import("./typedef.js").SnippetParams;
62
64
  }, {}, "">;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.5.38",
3
+ "version": "0.5.40",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"