@hkdigital/lib-sveltekit 0.2.8 → 0.2.10
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 +135 -135
- package/dist/assets/autospuiten/car-paint-picker.js +41 -41
- package/dist/assets/autospuiten/labels.js +7 -7
- package/dist/classes/cache/IndexedDbCache.js +1407 -1407
- package/dist/classes/cache/MemoryResponseCache.js +138 -138
- package/dist/classes/cache/index.js +5 -5
- package/dist/classes/cache/typedef.js +41 -41
- package/dist/classes/data/IterableTree.js +243 -243
- package/dist/classes/data/Selector.js +190 -190
- package/dist/classes/data/index.js +2 -2
- package/dist/classes/events/EventEmitter.js +275 -275
- package/dist/classes/events/index.js +2 -2
- package/dist/classes/index.js +4 -4
- package/dist/classes/logging/Logger.js +158 -158
- package/dist/classes/logging/constants.js +18 -18
- package/dist/classes/logging/index.js +4 -4
- package/dist/classes/promise/HkPromise.js +377 -377
- package/dist/classes/promise/index.js +1 -1
- package/dist/classes/services/ServiceBase.js +409 -409
- package/dist/classes/services/ServiceManager.js +1114 -1114
- package/dist/classes/services/constants.js +12 -12
- package/dist/classes/services/index.js +5 -5
- package/dist/classes/stores/SubscribersCount.js +107 -107
- package/dist/classes/stores/index.js +1 -1
- package/dist/classes/streams/LogTransformStream.js +19 -19
- package/dist/classes/streams/ServerEventsStore.js +110 -110
- package/dist/classes/streams/TimeStampSource.js +26 -26
- package/dist/classes/streams/index.js +3 -3
- package/dist/classes/svelte/audio/AudioLoader.svelte.js +58 -58
- package/dist/classes/svelte/audio/AudioScene.svelte.js +324 -324
- package/dist/classes/svelte/audio/mocks.js +35 -35
- package/dist/classes/svelte/finite-state-machine/FiniteStateMachine.svelte.js +133 -133
- package/dist/classes/svelte/finite-state-machine/index.js +1 -1
- package/dist/classes/svelte/image/ImageLoader.svelte.js +45 -45
- package/dist/classes/svelte/image/ImageScene.svelte.js +249 -249
- package/dist/classes/svelte/image/ImageVariantsLoader.svelte.js +152 -152
- package/dist/classes/svelte/image/index.js +4 -4
- package/dist/classes/svelte/image/mocks.js +35 -35
- package/dist/classes/svelte/image/typedef.js +8 -8
- package/dist/classes/svelte/index.js +14 -14
- package/dist/classes/svelte/loading-state-machine/LoadingStateMachine.svelte.js +109 -109
- package/dist/classes/svelte/loading-state-machine/constants.js +16 -16
- package/dist/classes/svelte/loading-state-machine/index.js +3 -3
- package/dist/classes/svelte/network-loader/NetworkLoader.svelte.js +338 -338
- package/dist/classes/svelte/network-loader/constants.js +3 -3
- package/dist/classes/svelte/network-loader/index.js +3 -3
- package/dist/classes/svelte/network-loader/mocks.js +30 -30
- package/dist/classes/svelte/network-loader/typedef.js +8 -8
- package/dist/components/area/HkArea.svelte +49 -49
- package/dist/components/area/HkGridArea.svelte +77 -77
- package/dist/components/area/index.js +2 -2
- package/dist/components/buttons/button/Button.svelte +82 -82
- package/dist/components/buttons/button-icon-steeze/SteezeIconButton.svelte +30 -30
- package/dist/components/buttons/button-text/TextButton.svelte +21 -21
- package/dist/components/buttons/index.js +3 -3
- package/dist/components/debug/debug-panel-design-scaling/DebugPanelDesignScaling.svelte +146 -146
- package/dist/components/debug/index.js +1 -1
- package/dist/components/drag-drop/DragController.js +44 -44
- package/dist/components/drag-drop/DragDropContext.svelte +111 -110
- package/dist/components/drag-drop/Draggable.svelte +502 -512
- package/dist/components/drag-drop/{Dropzone.svelte → DropZone.svelte} +258 -258
- package/dist/components/drag-drop/DropZoneArea.svelte +119 -119
- package/dist/components/drag-drop/DropZoneList.svelte +125 -125
- package/dist/components/drag-drop/actions.d.ts +9 -0
- package/dist/components/drag-drop/actions.js +26 -0
- package/dist/components/drag-drop/drag-state.svelte.d.ts +3 -1
- package/dist/components/drag-drop/drag-state.svelte.js +322 -319
- package/dist/components/drag-drop/drag-state.svelte.js__ +319 -0
- package/dist/components/drag-drop/index.js +7 -7
- package/dist/components/drag-drop/util.d.ts +0 -32
- package/dist/components/drag-drop/util.js +85 -85
- package/dist/components/hkdev/blocks/TextBlock.svelte +46 -46
- package/dist/components/hkdev/buttons/CheckButton.svelte +62 -62
- package/dist/components/icons/HkIcon.svelte +86 -86
- package/dist/components/icons/HkTabIcon.svelte +116 -116
- package/dist/components/icons/SteezeIcon.svelte +97 -97
- package/dist/components/icons/index.js +6 -6
- package/dist/components/icons/typedef.js +16 -16
- package/dist/components/index.js +2 -2
- package/dist/components/inputs/index.js +1 -1
- package/dist/components/inputs/text-input/TestTextInput.svelte__ +102 -102
- package/dist/components/inputs/text-input/TextInput.svelte +223 -223
- package/dist/components/inputs/text-input/TextInput.svelte___ +83 -83
- package/dist/components/inputs/text-input/assets/IconInvalid.svelte +14 -14
- package/dist/components/inputs/text-input/assets/IconValid.svelte +12 -12
- package/dist/components/layout/grid-layers/GridLayers.svelte +63 -63
- package/dist/components/layout/grid-layers/GridLayers.svelte__heightFrom__ +372 -0
- package/dist/components/layout/grid-layers/util.js +74 -74
- package/dist/components/layout/index.js +1 -1
- package/dist/components/panels/index.js +1 -1
- package/dist/components/panels/panel/Panel.svelte +43 -43
- package/dist/components/rows/index.js +3 -3
- package/dist/components/rows/panel-grid-row/PanelGridRow.svelte +104 -104
- package/dist/components/rows/panel-row-2/PanelRow2.svelte +40 -40
- package/dist/components/tab-bar/HkTabBar.state.svelte.js +149 -149
- package/dist/components/tab-bar/HkTabBar.svelte +74 -74
- package/dist/components/tab-bar/HkTabBarSelector.state.svelte.js +93 -93
- package/dist/components/tab-bar/HkTabBarSelector.svelte +49 -49
- package/dist/components/tab-bar/index.js +17 -17
- package/dist/components/tab-bar/typedef.js +11 -11
- package/dist/config/imagetools-config.js +189 -189
- package/dist/config/imagetools.d.ts +72 -72
- package/dist/constants/bases.js +13 -13
- package/dist/constants/errors/api.js +9 -9
- package/dist/constants/errors/generic.js +5 -5
- package/dist/constants/errors/index.js +3 -3
- package/dist/constants/errors/jwt.js +5 -5
- package/dist/constants/http/headers.js +6 -6
- package/dist/constants/http/index.js +2 -2
- package/dist/constants/http/methods.js +2 -2
- package/dist/constants/index.js +3 -3
- package/dist/constants/mime/application.js +5 -5
- package/dist/constants/mime/audio.js +13 -13
- package/dist/constants/mime/image.js +3 -3
- package/dist/constants/mime/index.js +4 -4
- package/dist/constants/mime/text.js +2 -2
- package/dist/constants/regexp/index.js +31 -31
- package/dist/constants/regexp/inspiratie.js__ +95 -95
- package/dist/constants/regexp/text.js +49 -49
- package/dist/constants/regexp/user.js +32 -32
- package/dist/constants/regexp/web.js +3 -3
- package/dist/constants/state-labels/drag-states.js +6 -6
- package/dist/constants/state-labels/drop-states.js +6 -6
- package/dist/constants/state-labels/input-states.js +11 -11
- package/dist/constants/state-labels/submit-states.js +4 -4
- package/dist/constants/time.js +28 -28
- package/dist/css/utilities.css +43 -43
- package/dist/design/design-config.js +73 -73
- package/dist/design/tailwind-theme-extend.js +158 -158
- package/dist/features/button-group/ButtonGroup.svelte +82 -82
- package/dist/features/button-group/typedef.js +10 -10
- package/dist/features/compare-left-right/CompareLeftRight.svelte +179 -179
- package/dist/features/compare-left-right/index.js +1 -1
- package/dist/features/game-box/GameBox.svelte +577 -577
- package/dist/features/game-box/gamebox.util.js +83 -83
- package/dist/features/hk-app-layout/HkAppLayout.state.svelte.js +25 -25
- package/dist/features/hk-app-layout/HkAppLayout.svelte +251 -251
- package/dist/features/image-box/ImageBox.svelte +210 -210
- package/dist/features/image-box/index.js +5 -5
- package/dist/features/image-box/typedef.js +32 -32
- package/dist/features/index.js +23 -23
- package/dist/features/presenter/ImageSlide.svelte +64 -64
- package/dist/features/presenter/Presenter.state.svelte.js +638 -638
- package/dist/features/presenter/Presenter.svelte +142 -142
- package/dist/features/presenter/constants.js +7 -7
- package/dist/features/presenter/index.js +10 -10
- package/dist/features/presenter/typedef.js +106 -106
- package/dist/features/presenter/util.js +210 -210
- package/dist/features/virtual-viewport/VirtualViewport.svelte +196 -196
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/validate-url.js +180 -180
- package/dist/server/index.js +1 -1
- package/dist/server/logger.js +94 -94
- package/dist/states/index.js +1 -1
- package/dist/states/navigation.svelte.js +55 -55
- package/dist/stores/index.js +1 -1
- package/dist/stores/theme.js +80 -80
- package/dist/themes/hkdev/components/blocks/text-block.css +41 -41
- package/dist/themes/hkdev/components/boxes/game-box.css +12 -12
- package/dist/themes/hkdev/components/buttons/button-icon-steeze.css +22 -22
- package/dist/themes/hkdev/components/buttons/button-text.css +32 -32
- package/dist/themes/hkdev/components/buttons/button.css +146 -146
- package/dist/themes/hkdev/components/buttons/skip-button.css +6 -6
- package/dist/themes/hkdev/components/drag-drop/draggable.css +73 -73
- package/dist/themes/hkdev/components/drag-drop/drop-zone.css +48 -48
- package/dist/themes/hkdev/components/icons/icon-steeze.css +22 -22
- package/dist/themes/hkdev/components/inputs/text-input.css +104 -104
- package/dist/themes/hkdev/components/panels/panel.css +27 -27
- package/dist/themes/hkdev/components/rows/panel-grid-row.css +6 -6
- package/dist/themes/hkdev/components/rows/panel-row-2.css +7 -7
- package/dist/themes/hkdev/components.css +53 -53
- package/dist/themes/hkdev/debug.css +1 -1
- package/dist/themes/hkdev/global/layout.css +39 -39
- package/dist/themes/hkdev/global/on-colors.css +53 -53
- package/dist/themes/hkdev/globals.css +11 -11
- package/dist/themes/hkdev/responsive.css +12 -12
- package/dist/themes/hkdev/theme-ext.js +15 -15
- package/dist/themes/hkdev/theme.js +235 -235
- package/dist/themes/index.js +1 -1
- package/dist/typedef/context.js +6 -6
- package/dist/typedef/drag.js +25 -25
- package/dist/typedef/drop.js +12 -12
- package/dist/typedef/image.js +38 -38
- package/dist/typedef/index.js +4 -4
- package/dist/util/array/index.js +436 -436
- package/dist/util/bases/base58.js +262 -262
- package/dist/util/bases/index.js +1 -1
- package/dist/util/compare/index.js +247 -247
- package/dist/util/css/css-vars.js +83 -83
- package/dist/util/css/index.js +1 -1
- package/dist/util/design-system/components/states.js +22 -22
- package/dist/util/design-system/css/clamp.js +66 -66
- package/dist/util/design-system/css/root-design-vars.js +102 -102
- package/dist/util/design-system/index.js +5 -5
- package/dist/util/design-system/layout/scaling.js +228 -228
- package/dist/util/design-system/skeleton.js +208 -208
- package/dist/util/design-system/tailwind.js +288 -288
- package/dist/util/env/index.js +9 -9
- package/dist/util/expect/arrays.js +47 -47
- package/dist/util/expect/index.js +259 -259
- package/dist/util/expect/primitives.js +55 -55
- package/dist/util/expect/url.js +60 -60
- package/dist/util/function/index.js +218 -218
- package/dist/util/geo/index.js +26 -26
- package/dist/util/http/caching.js +263 -263
- package/dist/util/http/errors.js +97 -97
- package/dist/util/http/headers.js +75 -75
- package/dist/util/http/http-request.js +379 -379
- package/dist/util/http/index.js +22 -22
- package/dist/util/http/json-request.js +224 -224
- package/dist/util/http/mocks.js +65 -65
- package/dist/util/http/response.js +294 -294
- package/dist/util/http/test-data__/content-length-test-hkdigital-small.V4HfZyBQ.avif +0 -0
- package/dist/util/http/typedef.js +93 -93
- package/dist/util/http/url.js +52 -52
- package/dist/util/image/index.js +86 -86
- package/dist/util/index.js +2 -2
- package/dist/util/is/index.js +140 -140
- package/dist/util/iterate/index.js +234 -234
- package/dist/util/object/index.js +1361 -1361
- package/dist/util/singleton/index.js +97 -97
- package/dist/util/string/array-path.js +75 -75
- package/dist/util/string/convert.js +54 -54
- package/dist/util/string/fs.js +226 -226
- package/dist/util/string/index.js +5 -5
- package/dist/util/string/interpolate.js +61 -61
- package/dist/util/string/pad.js +10 -10
- package/dist/util/svelte/index.js +4 -4
- package/dist/util/svelte/loading/loading-tracker.svelte.js +108 -108
- package/dist/util/svelte/observe/index.js +49 -49
- package/dist/util/svelte/state-context/index.js +117 -117
- package/dist/util/svelte/wait/index.js +38 -38
- package/dist/util/sveltekit/index.js +1 -1
- package/dist/util/sveltekit/route-folders/index.js +101 -101
- package/dist/util/time/index.js +323 -323
- package/dist/util/unique/index.js +249 -249
- package/dist/valibot/date.js__ +10 -10
- package/dist/valibot/index.js +9 -9
- package/dist/valibot/url.js +95 -95
- package/dist/valibot/user.js +23 -23
- package/dist/zod/all.js +33 -33
- package/dist/zod/generic.js +11 -11
- package/dist/zod/javascript.js +32 -32
- package/dist/zod/user.js +16 -16
- package/dist/zod/web.js +52 -52
- package/package.json +112 -112
@@ -1,577 +1,577 @@
|
|
1
|
-
<script>
|
2
|
-
import { onMount } from 'svelte';
|
3
|
-
|
4
|
-
import {
|
5
|
-
getGameWidthOnLandscape,
|
6
|
-
getGameWidthOnPortrait
|
7
|
-
} from './gamebox.util.js';
|
8
|
-
|
9
|
-
import { enableContainerScaling } from '../../util/design-system/index.js';
|
10
|
-
// import { enableContainerScaling } from '@hkdigital/lib-sveltekit/util/design-system/index.js';
|
11
|
-
|
12
|
-
/**
|
13
|
-
* @typedef {{
|
14
|
-
* isMobile:boolean,
|
15
|
-
* os:'Android'|'iOS',
|
16
|
-
* isFullscreen:boolean,
|
17
|
-
* isDevMode:boolean,
|
18
|
-
* requestDevmode:function,
|
19
|
-
* requestFullscreen:function,
|
20
|
-
* gameWidth: number,
|
21
|
-
* gameHeight: number
|
22
|
-
* }} SnippetParams
|
23
|
-
*/
|
24
|
-
|
25
|
-
/**
|
26
|
-
* @typedef {import('svelte').Snippet<[SnippetParams]>} GameBoxSnippet
|
27
|
-
*/
|
28
|
-
|
29
|
-
/**
|
30
|
-
* @type {{
|
31
|
-
* base?: string,
|
32
|
-
* bg?: string,
|
33
|
-
* classes?: string,
|
34
|
-
* style?: string,
|
35
|
-
* aspectOnLandscape?: number,
|
36
|
-
* aspectOnPortrait?: number,
|
37
|
-
* marginLeft?: number,
|
38
|
-
* marginRight?: number,
|
39
|
-
* marginTop?: number,
|
40
|
-
* marginBottom?: number,
|
41
|
-
* center?: boolean,
|
42
|
-
* enableScaling?: boolean,
|
43
|
-
* designLandscape?: {width: number, height: number},
|
44
|
-
* designPortrait?: {width: number, height: number},
|
45
|
-
* clamping?: {
|
46
|
-
* ui: {min: number, max: number},
|
47
|
-
* textBase: {min: number, max: number},
|
48
|
-
* textHeading: {min: number, max: number},
|
49
|
-
* textUi: {min: number, max: number}
|
50
|
-
* },
|
51
|
-
* snippetLandscape?:GameBoxSnippet,
|
52
|
-
* snippetPortrait?: GameBoxSnippet,
|
53
|
-
* snippetRequireFullscreen?: GameBoxSnippet,
|
54
|
-
* snippetInstallOnHomeScreen?: GameBoxSnippet,
|
55
|
-
* [attr: string]: any
|
56
|
-
* }}
|
57
|
-
*/
|
58
|
-
const {
|
59
|
-
// > Style
|
60
|
-
base = '',
|
61
|
-
bg = '',
|
62
|
-
classes = '',
|
63
|
-
style = '',
|
64
|
-
|
65
|
-
// > Functional properties
|
66
|
-
aspectOnLandscape,
|
67
|
-
aspectOnPortrait,
|
68
|
-
|
69
|
-
marginLeft = 0,
|
70
|
-
marginRight = 0,
|
71
|
-
|
72
|
-
marginTop = 0,
|
73
|
-
marginBottom = 0,
|
74
|
-
|
75
|
-
center,
|
76
|
-
|
77
|
-
// > Scaling options
|
78
|
-
enableScaling = false,
|
79
|
-
designLandscape = { width: 1920, height: 1080 },
|
80
|
-
designPortrait = { width: 1920, height: 1080 },
|
81
|
-
clamping = {
|
82
|
-
ui: { min: 0.3, max: 2 },
|
83
|
-
textBase: { min: 0.75, max: 1.5 },
|
84
|
-
textHeading: { min: 0.75, max: 2.25 },
|
85
|
-
textUi: { min: 0.5, max: 1.25 }
|
86
|
-
},
|
87
|
-
|
88
|
-
// > Snippets
|
89
|
-
snippetLandscape,
|
90
|
-
snippetPortrait,
|
91
|
-
snippetRequireFullscreen,
|
92
|
-
snippetInstallOnHomeScreen
|
93
|
-
} = $props();
|
94
|
-
|
95
|
-
// > Game dimensions and state
|
96
|
-
let windowWidth = $state();
|
97
|
-
let windowHeight = $state();
|
98
|
-
|
99
|
-
let gameWidth = $state();
|
100
|
-
let gameHeight = $state();
|
101
|
-
|
102
|
-
let iosWindowWidth = $state();
|
103
|
-
let iosWindowHeight = $state();
|
104
|
-
|
105
|
-
function getIsLandscape() {
|
106
|
-
if (isPwa && isAppleMobile) {
|
107
|
-
return iosWindowWidth > iosWindowHeight;
|
108
|
-
} else {
|
109
|
-
return windowWidth > windowHeight;
|
110
|
-
}
|
111
|
-
}
|
112
|
-
|
113
|
-
let isLandscape = $state();
|
114
|
-
|
115
|
-
// $derived.by(getIsLandscape);
|
116
|
-
|
117
|
-
$effect(() => {
|
118
|
-
isLandscape = getIsLandscape();
|
119
|
-
});
|
120
|
-
|
121
|
-
// Game container reference
|
122
|
-
let gameContainer = $state();
|
123
|
-
|
124
|
-
// Update game dimensions based on window size and orientation
|
125
|
-
$effect(() => {
|
126
|
-
const width = iosWindowWidth ?? windowWidth;
|
127
|
-
const height = iosWindowHeight ?? windowHeight;
|
128
|
-
|
129
|
-
if (!width || !height) return;
|
130
|
-
|
131
|
-
const availWidth = width - marginLeft - marginRight;
|
132
|
-
const availHeight = height - marginTop - marginBottom;
|
133
|
-
|
134
|
-
// console.debug('GameBox margins:', {
|
135
|
-
// marginLeft,
|
136
|
-
// marginRight,
|
137
|
-
// marginTop,
|
138
|
-
// marginBottom
|
139
|
-
// });
|
140
|
-
|
141
|
-
let gameAspect;
|
142
|
-
|
143
|
-
if (availWidth > availHeight) {
|
144
|
-
gameWidth = getGameWidthOnLandscape({
|
145
|
-
windowWidth: availWidth,
|
146
|
-
windowHeight: availHeight,
|
147
|
-
aspectOnLandscape
|
148
|
-
});
|
149
|
-
gameAspect = aspectOnLandscape;
|
150
|
-
} else {
|
151
|
-
gameWidth = getGameWidthOnPortrait({
|
152
|
-
windowWidth: availWidth,
|
153
|
-
windowHeight: availHeight,
|
154
|
-
aspectOnPortrait
|
155
|
-
});
|
156
|
-
gameAspect = aspectOnPortrait;
|
157
|
-
}
|
158
|
-
|
159
|
-
if (gameAspect) {
|
160
|
-
gameHeight = gameWidth / gameAspect;
|
161
|
-
} else {
|
162
|
-
gameHeight = availHeight;
|
163
|
-
}
|
164
|
-
});
|
165
|
-
|
166
|
-
// Set up scaling if enabled, with orientation awareness
|
167
|
-
$effect(() => {
|
168
|
-
if (!enableScaling || !gameContainer || !gameWidth || !gameHeight) {
|
169
|
-
return () => {}; // No-op cleanup if scaling not enabled or required elements missing
|
170
|
-
}
|
171
|
-
|
172
|
-
// Select the appropriate design based on orientation
|
173
|
-
const activeDesign = isLandscape ? designLandscape : designPortrait;
|
174
|
-
|
175
|
-
// console.debug(
|
176
|
-
// `GameBox scaling [${isLandscape ? 'landscape' : 'portrait'}]:`,
|
177
|
-
// `game: ${gameWidth}x${gameHeight}`,
|
178
|
-
// `design: ${activeDesign.width}x${activeDesign.height}`
|
179
|
-
// );
|
180
|
-
|
181
|
-
// Apply scaling with the current design based on orientation
|
182
|
-
return enableContainerScaling({
|
183
|
-
container: gameContainer,
|
184
|
-
design: activeDesign,
|
185
|
-
clamping,
|
186
|
-
getDimensions: () => ({
|
187
|
-
width: gameWidth,
|
188
|
-
height: gameHeight
|
189
|
-
})
|
190
|
-
});
|
191
|
-
});
|
192
|
-
|
193
|
-
let show = $state(false);
|
194
|
-
|
195
|
-
const isAppleMobile = /iPhone|iPod/.test(navigator.userAgent);
|
196
|
-
|
197
|
-
let isPwa = $state(false);
|
198
|
-
|
199
|
-
let os = $state();
|
200
|
-
|
201
|
-
let isMobile = $state(false);
|
202
|
-
|
203
|
-
let isDevMode = $state(false);
|
204
|
-
|
205
|
-
// Check: always true for home app?
|
206
|
-
let isFullscreen = $state(false);
|
207
|
-
|
208
|
-
let supportsFullscreen = $state(false);
|
209
|
-
|
210
|
-
onMount(() => {
|
211
|
-
supportsFullscreen = document.fullscreenEnabled;
|
212
|
-
|
213
|
-
isMobile = getIsMobile();
|
214
|
-
|
215
|
-
os = getOS();
|
216
|
-
|
217
|
-
// Run before show
|
218
|
-
isFullscreen = !!document.fullscreenElement;
|
219
|
-
|
220
|
-
isPwa = window.matchMedia(
|
221
|
-
'(display-mode: fullscreen) or (display-mode: standalone)'
|
222
|
-
).matches;
|
223
|
-
|
224
|
-
isLandscape = getIsLandscape();
|
225
|
-
|
226
|
-
show = true;
|
227
|
-
|
228
|
-
function updateIosWidthHeight() {
|
229
|
-
// const isPwa = window.matchMedia(
|
230
|
-
// '(display-mode: fullscreen) or (display-mode: standalone)'
|
231
|
-
// ).matches;
|
232
|
-
|
233
|
-
if (isPwa && isAppleMobile) {
|
234
|
-
const angle = screen.orientation.angle;
|
235
|
-
|
236
|
-
if (angle === 90 || angle === 270) {
|
237
|
-
iosWindowWidth = screen.height;
|
238
|
-
iosWindowHeight = screen.width;
|
239
|
-
} else {
|
240
|
-
iosWindowWidth = screen.width;
|
241
|
-
iosWindowHeight = screen.height;
|
242
|
-
}
|
243
|
-
// console.debug( { iosWindowWidth, iosWindowHeight } );
|
244
|
-
}
|
245
|
-
}
|
246
|
-
|
247
|
-
updateIosWidthHeight();
|
248
|
-
|
249
|
-
function updateOrientation(event) {
|
250
|
-
// console.debug('updateOrientation');
|
251
|
-
const type = event.target.type;
|
252
|
-
const angle = event.target.angle;
|
253
|
-
|
254
|
-
// isPwa = window.matchMedia(
|
255
|
-
// '(display-mode: fullscreen) or (display-mode: standalone)'
|
256
|
-
// ).matches;
|
257
|
-
|
258
|
-
updateIosWidthHeight();
|
259
|
-
|
260
|
-
console.debug(
|
261
|
-
`ScreenOrientation change: ${type}, ${angle} degrees.`,
|
262
|
-
isPwa,
|
263
|
-
windowWidth,
|
264
|
-
windowHeight,
|
265
|
-
screen.width,
|
266
|
-
screen.height,
|
267
|
-
iosWindowWidth,
|
268
|
-
iosWindowHeight
|
269
|
-
);
|
270
|
-
|
271
|
-
// if( angle
|
272
|
-
}
|
273
|
-
|
274
|
-
$effect(() => {
|
275
|
-
screen.orientation.addEventListener('change', updateOrientation);
|
276
|
-
|
277
|
-
return () => {
|
278
|
-
screen.orientation.removeEventListener('change', updateOrientation);
|
279
|
-
};
|
280
|
-
});
|
281
|
-
|
282
|
-
//
|
283
|
-
});
|
284
|
-
|
285
|
-
onMount(() => {
|
286
|
-
const gameBoxNoScroll = 'game-box-no-scroll';
|
287
|
-
const html = document.documentElement;
|
288
|
-
html.classList.add(gameBoxNoScroll);
|
289
|
-
|
290
|
-
return () => {
|
291
|
-
html.classList.remove(gameBoxNoScroll);
|
292
|
-
};
|
293
|
-
});
|
294
|
-
|
295
|
-
function getOS() {
|
296
|
-
if (isAppleMobile) {
|
297
|
-
return 'iOS';
|
298
|
-
} else if (/Android/.test(navigator.userAgent)) {
|
299
|
-
return 'Android';
|
300
|
-
}
|
301
|
-
}
|
302
|
-
|
303
|
-
/**
|
304
|
-
* Returns true if a device is a mobile phone (or similar)
|
305
|
-
*/
|
306
|
-
function getIsMobile() {
|
307
|
-
// @ts-ignore
|
308
|
-
if (navigator?.userAgentData?.mobile !== undefined) {
|
309
|
-
// Supports for mobile flag
|
310
|
-
// @ts-ignore
|
311
|
-
return navigator.userAgentData.mobile;
|
312
|
-
} else if (isAppleMobile) {
|
313
|
-
return true;
|
314
|
-
} else if (/Android/.test(navigator.userAgent)) {
|
315
|
-
return true;
|
316
|
-
}
|
317
|
-
|
318
|
-
return false;
|
319
|
-
}
|
320
|
-
|
321
|
-
/**
|
322
|
-
* Returns true if the window is in full screen
|
323
|
-
* - Checks if CSS thinks we're in fullscreen mode
|
324
|
-
* - Checks if there is a fullscreen element (for safari)
|
325
|
-
*/
|
326
|
-
function getIsFullscreen() {
|
327
|
-
if (
|
328
|
-
window.matchMedia(
|
329
|
-
'(display-mode: fullscreen) or (display-mode: standalone)'
|
330
|
-
).matches
|
331
|
-
) {
|
332
|
-
return true;
|
333
|
-
} else if (document.fullscreenElement) {
|
334
|
-
// Safari
|
335
|
-
return true;
|
336
|
-
}
|
337
|
-
|
338
|
-
return false;
|
339
|
-
}
|
340
|
-
|
341
|
-
async function requestFullscreen() {
|
342
|
-
console.debug('Request full screen');
|
343
|
-
show = false;
|
344
|
-
|
345
|
-
await document.documentElement.requestFullscreen();
|
346
|
-
isFullscreen = true;
|
347
|
-
|
348
|
-
setTimeout(() => {
|
349
|
-
show = true;
|
350
|
-
}, 1000);
|
351
|
-
}
|
352
|
-
|
353
|
-
// async function exitFullscreen() {
|
354
|
-
// console.debug('Exit full screen');
|
355
|
-
// show = false;
|
356
|
-
|
357
|
-
// await document.exitFullscreen();
|
358
|
-
// isFullscreen = false;
|
359
|
-
|
360
|
-
// setTimeout( () => { show = true; }, 1000 );
|
361
|
-
// }
|
362
|
-
|
363
|
-
$effect(() => {
|
364
|
-
// Update isFullscreen if window width or height changes
|
365
|
-
|
366
|
-
windowWidth;
|
367
|
-
windowHeight;
|
368
|
-
|
369
|
-
isFullscreen = getIsFullscreen();
|
370
|
-
|
371
|
-
// if( !isFullscreen )
|
372
|
-
// {
|
373
|
-
// show = false;
|
374
|
-
// setTimeout( () => { show = true; }, 1000 );
|
375
|
-
// }
|
376
|
-
|
377
|
-
// console.debug('isFullscreen', isFullscreen);
|
378
|
-
});
|
379
|
-
|
380
|
-
isDevMode = false;
|
381
|
-
|
382
|
-
function requestDevmode() {
|
383
|
-
isDevMode = true;
|
384
|
-
console.debug(isDevMode);
|
385
|
-
}
|
386
|
-
|
387
|
-
$effect(() => {
|
388
|
-
if (location.hostname === 'localhost') {
|
389
|
-
isDevMode = true;
|
390
|
-
}
|
391
|
-
});
|
392
|
-
|
393
|
-
$effect(() => {
|
394
|
-
if (isFullscreen) {
|
395
|
-
const url = new URL(window.location.href);
|
396
|
-
url.searchParams.set('preset', 'cinema');
|
397
|
-
window.history.pushState({}, '', url);
|
398
|
-
}
|
399
|
-
});
|
400
|
-
</script>
|
401
|
-
|
402
|
-
<svelte:window bind:innerWidth={windowWidth} bind:innerHeight={windowHeight} />
|
403
|
-
|
404
|
-
{#if gameHeight}
|
405
|
-
<div class:center>
|
406
|
-
<div
|
407
|
-
data-component="game-box"
|
408
|
-
data-orientation={isLandscape ? 'landscape' : 'portrait'}
|
409
|
-
bind:this={gameContainer}
|
410
|
-
class="{base} {bg} {classes}"
|
411
|
-
class:isMobile
|
412
|
-
style:width="{gameWidth}px"
|
413
|
-
style:height="{gameHeight}px"
|
414
|
-
style:--game-width={gameWidth}
|
415
|
-
style:--game-height={gameHeight}
|
416
|
-
style:margin-left="{marginLeft}px"
|
417
|
-
style:margin-right="{marginRight}px"
|
418
|
-
style:margin-top="{marginTop}px"
|
419
|
-
style:margin-bottom="{marginBottom}px"
|
420
|
-
{style}
|
421
|
-
>
|
422
|
-
{#if show}
|
423
|
-
{#if isLandscape}
|
424
|
-
<!-- Landscape -->
|
425
|
-
{#if snippetRequireFullscreen}
|
426
|
-
<!-- Require fullscreen -->
|
427
|
-
{#if isFullscreen && !isDevMode}
|
428
|
-
{@render snippetLandscape({
|
429
|
-
isMobile,
|
430
|
-
os,
|
431
|
-
isFullscreen,
|
432
|
-
isDevMode,
|
433
|
-
requestDevmode,
|
434
|
-
requestFullscreen,
|
435
|
-
gameWidth,
|
436
|
-
gameHeight
|
437
|
-
})}
|
438
|
-
{:else if supportsFullscreen && !isDevMode}
|
439
|
-
<!-- Require fullscreen (on landscape) -->
|
440
|
-
{@render snippetRequireFullscreen({
|
441
|
-
isMobile,
|
442
|
-
os,
|
443
|
-
isFullscreen,
|
444
|
-
isDevMode,
|
445
|
-
requestDevmode,
|
446
|
-
requestFullscreen,
|
447
|
-
gameWidth,
|
448
|
-
gameHeight
|
449
|
-
})}
|
450
|
-
{:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
|
451
|
-
<!-- Require install on home screen on mobile -->
|
452
|
-
{@render snippetInstallOnHomeScreen({
|
453
|
-
isMobile,
|
454
|
-
os,
|
455
|
-
isFullscreen,
|
456
|
-
isDevMode,
|
457
|
-
requestDevmode,
|
458
|
-
requestFullscreen,
|
459
|
-
gameWidth,
|
460
|
-
gameHeight
|
461
|
-
})}
|
462
|
-
{:else}
|
463
|
-
{@render snippetLandscape({
|
464
|
-
isMobile,
|
465
|
-
os,
|
466
|
-
isFullscreen,
|
467
|
-
isDevMode,
|
468
|
-
requestDevmode,
|
469
|
-
requestFullscreen,
|
470
|
-
gameWidth,
|
471
|
-
gameHeight
|
472
|
-
})}
|
473
|
-
{/if}
|
474
|
-
{:else}
|
475
|
-
<!-- Do not require fullscreen -->
|
476
|
-
<!-- *we do not try install home app -->
|
477
|
-
{@render snippetLandscape({
|
478
|
-
isMobile,
|
479
|
-
os,
|
480
|
-
isFullscreen,
|
481
|
-
isDevMode,
|
482
|
-
requestDevmode,
|
483
|
-
requestFullscreen,
|
484
|
-
gameWidth,
|
485
|
-
gameHeight
|
486
|
-
})}
|
487
|
-
{/if}
|
488
|
-
{:else}
|
489
|
-
<!-- Portrait -->
|
490
|
-
{#if snippetRequireFullscreen}
|
491
|
-
<!-- Require fullscreen -->
|
492
|
-
{#if isFullscreen && !isDevMode}
|
493
|
-
{@render snippetPortrait({
|
494
|
-
isMobile,
|
495
|
-
os,
|
496
|
-
isFullscreen,
|
497
|
-
isDevMode,
|
498
|
-
requestDevmode,
|
499
|
-
requestFullscreen,
|
500
|
-
gameWidth,
|
501
|
-
gameHeight
|
502
|
-
})}
|
503
|
-
{:else if supportsFullscreen && !isDevMode}
|
504
|
-
<!-- Require fullscreen (on landscape) -->
|
505
|
-
{@render snippetRequireFullscreen({
|
506
|
-
isMobile,
|
507
|
-
os,
|
508
|
-
isFullscreen,
|
509
|
-
isDevMode,
|
510
|
-
requestDevmode,
|
511
|
-
requestFullscreen,
|
512
|
-
gameWidth,
|
513
|
-
gameHeight
|
514
|
-
})}
|
515
|
-
{:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
|
516
|
-
<!-- Require install on home screen on mobile -->
|
517
|
-
{@render snippetInstallOnHomeScreen({
|
518
|
-
isMobile,
|
519
|
-
os,
|
520
|
-
isFullscreen,
|
521
|
-
isDevMode,
|
522
|
-
requestDevmode,
|
523
|
-
requestFullscreen,
|
524
|
-
gameWidth,
|
525
|
-
gameHeight
|
526
|
-
})}
|
527
|
-
{:else}
|
528
|
-
{@render snippetPortrait({
|
529
|
-
isMobile,
|
530
|
-
os,
|
531
|
-
isFullscreen,
|
532
|
-
isDevMode,
|
533
|
-
requestDevmode,
|
534
|
-
requestFullscreen,
|
535
|
-
gameWidth,
|
536
|
-
gameHeight
|
537
|
-
})}
|
538
|
-
{/if}
|
539
|
-
{:else}
|
540
|
-
<!-- Do not require fullscreen -->
|
541
|
-
<!-- *we do not try install home app -->
|
542
|
-
{@render snippetPortrait({
|
543
|
-
isMobile,
|
544
|
-
os,
|
545
|
-
isFullscreen,
|
546
|
-
isDevMode,
|
547
|
-
requestDevmode,
|
548
|
-
requestFullscreen,
|
549
|
-
gameWidth,
|
550
|
-
gameHeight
|
551
|
-
})}
|
552
|
-
{/if}
|
553
|
-
{/if}
|
554
|
-
{/if}
|
555
|
-
</div>
|
556
|
-
</div>
|
557
|
-
{/if}
|
558
|
-
|
559
|
-
<style>
|
560
|
-
.center {
|
561
|
-
display: grid;
|
562
|
-
height: 100lvh;
|
563
|
-
display: grid;
|
564
|
-
justify-items: center;
|
565
|
-
align-items: center;
|
566
|
-
/* border: solid 1px red;*/
|
567
|
-
}
|
568
|
-
|
569
|
-
:global(html.game-box-no-scroll) {
|
570
|
-
overflow: clip;
|
571
|
-
scrollbar-width: none; /* Firefox */
|
572
|
-
-ms-overflow-style: none; /* IE and Edge */
|
573
|
-
}
|
574
|
-
:global(html.game-box-no-scroll::-webkit-scrollbar) {
|
575
|
-
display: none;
|
576
|
-
}
|
577
|
-
</style>
|
1
|
+
<script>
|
2
|
+
import { onMount } from 'svelte';
|
3
|
+
|
4
|
+
import {
|
5
|
+
getGameWidthOnLandscape,
|
6
|
+
getGameWidthOnPortrait
|
7
|
+
} from './gamebox.util.js';
|
8
|
+
|
9
|
+
import { enableContainerScaling } from '../../util/design-system/index.js';
|
10
|
+
// import { enableContainerScaling } from '@hkdigital/lib-sveltekit/util/design-system/index.js';
|
11
|
+
|
12
|
+
/**
|
13
|
+
* @typedef {{
|
14
|
+
* isMobile:boolean,
|
15
|
+
* os:'Android'|'iOS',
|
16
|
+
* isFullscreen:boolean,
|
17
|
+
* isDevMode:boolean,
|
18
|
+
* requestDevmode:function,
|
19
|
+
* requestFullscreen:function,
|
20
|
+
* gameWidth: number,
|
21
|
+
* gameHeight: number
|
22
|
+
* }} SnippetParams
|
23
|
+
*/
|
24
|
+
|
25
|
+
/**
|
26
|
+
* @typedef {import('svelte').Snippet<[SnippetParams]>} GameBoxSnippet
|
27
|
+
*/
|
28
|
+
|
29
|
+
/**
|
30
|
+
* @type {{
|
31
|
+
* base?: string,
|
32
|
+
* bg?: string,
|
33
|
+
* classes?: string,
|
34
|
+
* style?: string,
|
35
|
+
* aspectOnLandscape?: number,
|
36
|
+
* aspectOnPortrait?: number,
|
37
|
+
* marginLeft?: number,
|
38
|
+
* marginRight?: number,
|
39
|
+
* marginTop?: number,
|
40
|
+
* marginBottom?: number,
|
41
|
+
* center?: boolean,
|
42
|
+
* enableScaling?: boolean,
|
43
|
+
* designLandscape?: {width: number, height: number},
|
44
|
+
* designPortrait?: {width: number, height: number},
|
45
|
+
* clamping?: {
|
46
|
+
* ui: {min: number, max: number},
|
47
|
+
* textBase: {min: number, max: number},
|
48
|
+
* textHeading: {min: number, max: number},
|
49
|
+
* textUi: {min: number, max: number}
|
50
|
+
* },
|
51
|
+
* snippetLandscape?:GameBoxSnippet,
|
52
|
+
* snippetPortrait?: GameBoxSnippet,
|
53
|
+
* snippetRequireFullscreen?: GameBoxSnippet,
|
54
|
+
* snippetInstallOnHomeScreen?: GameBoxSnippet,
|
55
|
+
* [attr: string]: any
|
56
|
+
* }}
|
57
|
+
*/
|
58
|
+
const {
|
59
|
+
// > Style
|
60
|
+
base = '',
|
61
|
+
bg = '',
|
62
|
+
classes = '',
|
63
|
+
style = '',
|
64
|
+
|
65
|
+
// > Functional properties
|
66
|
+
aspectOnLandscape,
|
67
|
+
aspectOnPortrait,
|
68
|
+
|
69
|
+
marginLeft = 0,
|
70
|
+
marginRight = 0,
|
71
|
+
|
72
|
+
marginTop = 0,
|
73
|
+
marginBottom = 0,
|
74
|
+
|
75
|
+
center,
|
76
|
+
|
77
|
+
// > Scaling options
|
78
|
+
enableScaling = false,
|
79
|
+
designLandscape = { width: 1920, height: 1080 },
|
80
|
+
designPortrait = { width: 1920, height: 1080 },
|
81
|
+
clamping = {
|
82
|
+
ui: { min: 0.3, max: 2 },
|
83
|
+
textBase: { min: 0.75, max: 1.5 },
|
84
|
+
textHeading: { min: 0.75, max: 2.25 },
|
85
|
+
textUi: { min: 0.5, max: 1.25 }
|
86
|
+
},
|
87
|
+
|
88
|
+
// > Snippets
|
89
|
+
snippetLandscape,
|
90
|
+
snippetPortrait,
|
91
|
+
snippetRequireFullscreen,
|
92
|
+
snippetInstallOnHomeScreen
|
93
|
+
} = $props();
|
94
|
+
|
95
|
+
// > Game dimensions and state
|
96
|
+
let windowWidth = $state();
|
97
|
+
let windowHeight = $state();
|
98
|
+
|
99
|
+
let gameWidth = $state();
|
100
|
+
let gameHeight = $state();
|
101
|
+
|
102
|
+
let iosWindowWidth = $state();
|
103
|
+
let iosWindowHeight = $state();
|
104
|
+
|
105
|
+
function getIsLandscape() {
|
106
|
+
if (isPwa && isAppleMobile) {
|
107
|
+
return iosWindowWidth > iosWindowHeight;
|
108
|
+
} else {
|
109
|
+
return windowWidth > windowHeight;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
let isLandscape = $state();
|
114
|
+
|
115
|
+
// $derived.by(getIsLandscape);
|
116
|
+
|
117
|
+
$effect(() => {
|
118
|
+
isLandscape = getIsLandscape();
|
119
|
+
});
|
120
|
+
|
121
|
+
// Game container reference
|
122
|
+
let gameContainer = $state();
|
123
|
+
|
124
|
+
// Update game dimensions based on window size and orientation
|
125
|
+
$effect(() => {
|
126
|
+
const width = iosWindowWidth ?? windowWidth;
|
127
|
+
const height = iosWindowHeight ?? windowHeight;
|
128
|
+
|
129
|
+
if (!width || !height) return;
|
130
|
+
|
131
|
+
const availWidth = width - marginLeft - marginRight;
|
132
|
+
const availHeight = height - marginTop - marginBottom;
|
133
|
+
|
134
|
+
// console.debug('GameBox margins:', {
|
135
|
+
// marginLeft,
|
136
|
+
// marginRight,
|
137
|
+
// marginTop,
|
138
|
+
// marginBottom
|
139
|
+
// });
|
140
|
+
|
141
|
+
let gameAspect;
|
142
|
+
|
143
|
+
if (availWidth > availHeight) {
|
144
|
+
gameWidth = getGameWidthOnLandscape({
|
145
|
+
windowWidth: availWidth,
|
146
|
+
windowHeight: availHeight,
|
147
|
+
aspectOnLandscape
|
148
|
+
});
|
149
|
+
gameAspect = aspectOnLandscape;
|
150
|
+
} else {
|
151
|
+
gameWidth = getGameWidthOnPortrait({
|
152
|
+
windowWidth: availWidth,
|
153
|
+
windowHeight: availHeight,
|
154
|
+
aspectOnPortrait
|
155
|
+
});
|
156
|
+
gameAspect = aspectOnPortrait;
|
157
|
+
}
|
158
|
+
|
159
|
+
if (gameAspect) {
|
160
|
+
gameHeight = gameWidth / gameAspect;
|
161
|
+
} else {
|
162
|
+
gameHeight = availHeight;
|
163
|
+
}
|
164
|
+
});
|
165
|
+
|
166
|
+
// Set up scaling if enabled, with orientation awareness
|
167
|
+
$effect(() => {
|
168
|
+
if (!enableScaling || !gameContainer || !gameWidth || !gameHeight) {
|
169
|
+
return () => {}; // No-op cleanup if scaling not enabled or required elements missing
|
170
|
+
}
|
171
|
+
|
172
|
+
// Select the appropriate design based on orientation
|
173
|
+
const activeDesign = isLandscape ? designLandscape : designPortrait;
|
174
|
+
|
175
|
+
// console.debug(
|
176
|
+
// `GameBox scaling [${isLandscape ? 'landscape' : 'portrait'}]:`,
|
177
|
+
// `game: ${gameWidth}x${gameHeight}`,
|
178
|
+
// `design: ${activeDesign.width}x${activeDesign.height}`
|
179
|
+
// );
|
180
|
+
|
181
|
+
// Apply scaling with the current design based on orientation
|
182
|
+
return enableContainerScaling({
|
183
|
+
container: gameContainer,
|
184
|
+
design: activeDesign,
|
185
|
+
clamping,
|
186
|
+
getDimensions: () => ({
|
187
|
+
width: gameWidth,
|
188
|
+
height: gameHeight
|
189
|
+
})
|
190
|
+
});
|
191
|
+
});
|
192
|
+
|
193
|
+
let show = $state(false);
|
194
|
+
|
195
|
+
const isAppleMobile = /iPhone|iPod/.test(navigator.userAgent);
|
196
|
+
|
197
|
+
let isPwa = $state(false);
|
198
|
+
|
199
|
+
let os = $state();
|
200
|
+
|
201
|
+
let isMobile = $state(false);
|
202
|
+
|
203
|
+
let isDevMode = $state(false);
|
204
|
+
|
205
|
+
// Check: always true for home app?
|
206
|
+
let isFullscreen = $state(false);
|
207
|
+
|
208
|
+
let supportsFullscreen = $state(false);
|
209
|
+
|
210
|
+
onMount(() => {
|
211
|
+
supportsFullscreen = document.fullscreenEnabled;
|
212
|
+
|
213
|
+
isMobile = getIsMobile();
|
214
|
+
|
215
|
+
os = getOS();
|
216
|
+
|
217
|
+
// Run before show
|
218
|
+
isFullscreen = !!document.fullscreenElement;
|
219
|
+
|
220
|
+
isPwa = window.matchMedia(
|
221
|
+
'(display-mode: fullscreen) or (display-mode: standalone)'
|
222
|
+
).matches;
|
223
|
+
|
224
|
+
isLandscape = getIsLandscape();
|
225
|
+
|
226
|
+
show = true;
|
227
|
+
|
228
|
+
function updateIosWidthHeight() {
|
229
|
+
// const isPwa = window.matchMedia(
|
230
|
+
// '(display-mode: fullscreen) or (display-mode: standalone)'
|
231
|
+
// ).matches;
|
232
|
+
|
233
|
+
if (isPwa && isAppleMobile) {
|
234
|
+
const angle = screen.orientation.angle;
|
235
|
+
|
236
|
+
if (angle === 90 || angle === 270) {
|
237
|
+
iosWindowWidth = screen.height;
|
238
|
+
iosWindowHeight = screen.width;
|
239
|
+
} else {
|
240
|
+
iosWindowWidth = screen.width;
|
241
|
+
iosWindowHeight = screen.height;
|
242
|
+
}
|
243
|
+
// console.debug( { iosWindowWidth, iosWindowHeight } );
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
updateIosWidthHeight();
|
248
|
+
|
249
|
+
function updateOrientation(event) {
|
250
|
+
// console.debug('updateOrientation');
|
251
|
+
const type = event.target.type;
|
252
|
+
const angle = event.target.angle;
|
253
|
+
|
254
|
+
// isPwa = window.matchMedia(
|
255
|
+
// '(display-mode: fullscreen) or (display-mode: standalone)'
|
256
|
+
// ).matches;
|
257
|
+
|
258
|
+
updateIosWidthHeight();
|
259
|
+
|
260
|
+
console.debug(
|
261
|
+
`ScreenOrientation change: ${type}, ${angle} degrees.`,
|
262
|
+
isPwa,
|
263
|
+
windowWidth,
|
264
|
+
windowHeight,
|
265
|
+
screen.width,
|
266
|
+
screen.height,
|
267
|
+
iosWindowWidth,
|
268
|
+
iosWindowHeight
|
269
|
+
);
|
270
|
+
|
271
|
+
// if( angle
|
272
|
+
}
|
273
|
+
|
274
|
+
$effect(() => {
|
275
|
+
screen.orientation.addEventListener('change', updateOrientation);
|
276
|
+
|
277
|
+
return () => {
|
278
|
+
screen.orientation.removeEventListener('change', updateOrientation);
|
279
|
+
};
|
280
|
+
});
|
281
|
+
|
282
|
+
//
|
283
|
+
});
|
284
|
+
|
285
|
+
onMount(() => {
|
286
|
+
const gameBoxNoScroll = 'game-box-no-scroll';
|
287
|
+
const html = document.documentElement;
|
288
|
+
html.classList.add(gameBoxNoScroll);
|
289
|
+
|
290
|
+
return () => {
|
291
|
+
html.classList.remove(gameBoxNoScroll);
|
292
|
+
};
|
293
|
+
});
|
294
|
+
|
295
|
+
function getOS() {
|
296
|
+
if (isAppleMobile) {
|
297
|
+
return 'iOS';
|
298
|
+
} else if (/Android/.test(navigator.userAgent)) {
|
299
|
+
return 'Android';
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
/**
|
304
|
+
* Returns true if a device is a mobile phone (or similar)
|
305
|
+
*/
|
306
|
+
function getIsMobile() {
|
307
|
+
// @ts-ignore
|
308
|
+
if (navigator?.userAgentData?.mobile !== undefined) {
|
309
|
+
// Supports for mobile flag
|
310
|
+
// @ts-ignore
|
311
|
+
return navigator.userAgentData.mobile;
|
312
|
+
} else if (isAppleMobile) {
|
313
|
+
return true;
|
314
|
+
} else if (/Android/.test(navigator.userAgent)) {
|
315
|
+
return true;
|
316
|
+
}
|
317
|
+
|
318
|
+
return false;
|
319
|
+
}
|
320
|
+
|
321
|
+
/**
|
322
|
+
* Returns true if the window is in full screen
|
323
|
+
* - Checks if CSS thinks we're in fullscreen mode
|
324
|
+
* - Checks if there is a fullscreen element (for safari)
|
325
|
+
*/
|
326
|
+
function getIsFullscreen() {
|
327
|
+
if (
|
328
|
+
window.matchMedia(
|
329
|
+
'(display-mode: fullscreen) or (display-mode: standalone)'
|
330
|
+
).matches
|
331
|
+
) {
|
332
|
+
return true;
|
333
|
+
} else if (document.fullscreenElement) {
|
334
|
+
// Safari
|
335
|
+
return true;
|
336
|
+
}
|
337
|
+
|
338
|
+
return false;
|
339
|
+
}
|
340
|
+
|
341
|
+
async function requestFullscreen() {
|
342
|
+
console.debug('Request full screen');
|
343
|
+
show = false;
|
344
|
+
|
345
|
+
await document.documentElement.requestFullscreen();
|
346
|
+
isFullscreen = true;
|
347
|
+
|
348
|
+
setTimeout(() => {
|
349
|
+
show = true;
|
350
|
+
}, 1000);
|
351
|
+
}
|
352
|
+
|
353
|
+
// async function exitFullscreen() {
|
354
|
+
// console.debug('Exit full screen');
|
355
|
+
// show = false;
|
356
|
+
|
357
|
+
// await document.exitFullscreen();
|
358
|
+
// isFullscreen = false;
|
359
|
+
|
360
|
+
// setTimeout( () => { show = true; }, 1000 );
|
361
|
+
// }
|
362
|
+
|
363
|
+
$effect(() => {
|
364
|
+
// Update isFullscreen if window width or height changes
|
365
|
+
|
366
|
+
windowWidth;
|
367
|
+
windowHeight;
|
368
|
+
|
369
|
+
isFullscreen = getIsFullscreen();
|
370
|
+
|
371
|
+
// if( !isFullscreen )
|
372
|
+
// {
|
373
|
+
// show = false;
|
374
|
+
// setTimeout( () => { show = true; }, 1000 );
|
375
|
+
// }
|
376
|
+
|
377
|
+
// console.debug('isFullscreen', isFullscreen);
|
378
|
+
});
|
379
|
+
|
380
|
+
isDevMode = false;
|
381
|
+
|
382
|
+
function requestDevmode() {
|
383
|
+
isDevMode = true;
|
384
|
+
console.debug(isDevMode);
|
385
|
+
}
|
386
|
+
|
387
|
+
$effect(() => {
|
388
|
+
if (location.hostname === 'localhost') {
|
389
|
+
isDevMode = true;
|
390
|
+
}
|
391
|
+
});
|
392
|
+
|
393
|
+
$effect(() => {
|
394
|
+
if (isFullscreen) {
|
395
|
+
const url = new URL(window.location.href);
|
396
|
+
url.searchParams.set('preset', 'cinema');
|
397
|
+
window.history.pushState({}, '', url);
|
398
|
+
}
|
399
|
+
});
|
400
|
+
</script>
|
401
|
+
|
402
|
+
<svelte:window bind:innerWidth={windowWidth} bind:innerHeight={windowHeight} />
|
403
|
+
|
404
|
+
{#if gameHeight}
|
405
|
+
<div class:center>
|
406
|
+
<div
|
407
|
+
data-component="game-box"
|
408
|
+
data-orientation={isLandscape ? 'landscape' : 'portrait'}
|
409
|
+
bind:this={gameContainer}
|
410
|
+
class="{base} {bg} {classes}"
|
411
|
+
class:isMobile
|
412
|
+
style:width="{gameWidth}px"
|
413
|
+
style:height="{gameHeight}px"
|
414
|
+
style:--game-width={gameWidth}
|
415
|
+
style:--game-height={gameHeight}
|
416
|
+
style:margin-left="{marginLeft}px"
|
417
|
+
style:margin-right="{marginRight}px"
|
418
|
+
style:margin-top="{marginTop}px"
|
419
|
+
style:margin-bottom="{marginBottom}px"
|
420
|
+
{style}
|
421
|
+
>
|
422
|
+
{#if show}
|
423
|
+
{#if isLandscape}
|
424
|
+
<!-- Landscape -->
|
425
|
+
{#if snippetRequireFullscreen}
|
426
|
+
<!-- Require fullscreen -->
|
427
|
+
{#if isFullscreen && !isDevMode}
|
428
|
+
{@render snippetLandscape({
|
429
|
+
isMobile,
|
430
|
+
os,
|
431
|
+
isFullscreen,
|
432
|
+
isDevMode,
|
433
|
+
requestDevmode,
|
434
|
+
requestFullscreen,
|
435
|
+
gameWidth,
|
436
|
+
gameHeight
|
437
|
+
})}
|
438
|
+
{:else if supportsFullscreen && !isDevMode}
|
439
|
+
<!-- Require fullscreen (on landscape) -->
|
440
|
+
{@render snippetRequireFullscreen({
|
441
|
+
isMobile,
|
442
|
+
os,
|
443
|
+
isFullscreen,
|
444
|
+
isDevMode,
|
445
|
+
requestDevmode,
|
446
|
+
requestFullscreen,
|
447
|
+
gameWidth,
|
448
|
+
gameHeight
|
449
|
+
})}
|
450
|
+
{:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
|
451
|
+
<!-- Require install on home screen on mobile -->
|
452
|
+
{@render snippetInstallOnHomeScreen({
|
453
|
+
isMobile,
|
454
|
+
os,
|
455
|
+
isFullscreen,
|
456
|
+
isDevMode,
|
457
|
+
requestDevmode,
|
458
|
+
requestFullscreen,
|
459
|
+
gameWidth,
|
460
|
+
gameHeight
|
461
|
+
})}
|
462
|
+
{:else}
|
463
|
+
{@render snippetLandscape({
|
464
|
+
isMobile,
|
465
|
+
os,
|
466
|
+
isFullscreen,
|
467
|
+
isDevMode,
|
468
|
+
requestDevmode,
|
469
|
+
requestFullscreen,
|
470
|
+
gameWidth,
|
471
|
+
gameHeight
|
472
|
+
})}
|
473
|
+
{/if}
|
474
|
+
{:else}
|
475
|
+
<!-- Do not require fullscreen -->
|
476
|
+
<!-- *we do not try install home app -->
|
477
|
+
{@render snippetLandscape({
|
478
|
+
isMobile,
|
479
|
+
os,
|
480
|
+
isFullscreen,
|
481
|
+
isDevMode,
|
482
|
+
requestDevmode,
|
483
|
+
requestFullscreen,
|
484
|
+
gameWidth,
|
485
|
+
gameHeight
|
486
|
+
})}
|
487
|
+
{/if}
|
488
|
+
{:else}
|
489
|
+
<!-- Portrait -->
|
490
|
+
{#if snippetRequireFullscreen}
|
491
|
+
<!-- Require fullscreen -->
|
492
|
+
{#if isFullscreen && !isDevMode}
|
493
|
+
{@render snippetPortrait({
|
494
|
+
isMobile,
|
495
|
+
os,
|
496
|
+
isFullscreen,
|
497
|
+
isDevMode,
|
498
|
+
requestDevmode,
|
499
|
+
requestFullscreen,
|
500
|
+
gameWidth,
|
501
|
+
gameHeight
|
502
|
+
})}
|
503
|
+
{:else if supportsFullscreen && !isDevMode}
|
504
|
+
<!-- Require fullscreen (on landscape) -->
|
505
|
+
{@render snippetRequireFullscreen({
|
506
|
+
isMobile,
|
507
|
+
os,
|
508
|
+
isFullscreen,
|
509
|
+
isDevMode,
|
510
|
+
requestDevmode,
|
511
|
+
requestFullscreen,
|
512
|
+
gameWidth,
|
513
|
+
gameHeight
|
514
|
+
})}
|
515
|
+
{:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
|
516
|
+
<!-- Require install on home screen on mobile -->
|
517
|
+
{@render snippetInstallOnHomeScreen({
|
518
|
+
isMobile,
|
519
|
+
os,
|
520
|
+
isFullscreen,
|
521
|
+
isDevMode,
|
522
|
+
requestDevmode,
|
523
|
+
requestFullscreen,
|
524
|
+
gameWidth,
|
525
|
+
gameHeight
|
526
|
+
})}
|
527
|
+
{:else}
|
528
|
+
{@render snippetPortrait({
|
529
|
+
isMobile,
|
530
|
+
os,
|
531
|
+
isFullscreen,
|
532
|
+
isDevMode,
|
533
|
+
requestDevmode,
|
534
|
+
requestFullscreen,
|
535
|
+
gameWidth,
|
536
|
+
gameHeight
|
537
|
+
})}
|
538
|
+
{/if}
|
539
|
+
{:else}
|
540
|
+
<!-- Do not require fullscreen -->
|
541
|
+
<!-- *we do not try install home app -->
|
542
|
+
{@render snippetPortrait({
|
543
|
+
isMobile,
|
544
|
+
os,
|
545
|
+
isFullscreen,
|
546
|
+
isDevMode,
|
547
|
+
requestDevmode,
|
548
|
+
requestFullscreen,
|
549
|
+
gameWidth,
|
550
|
+
gameHeight
|
551
|
+
})}
|
552
|
+
{/if}
|
553
|
+
{/if}
|
554
|
+
{/if}
|
555
|
+
</div>
|
556
|
+
</div>
|
557
|
+
{/if}
|
558
|
+
|
559
|
+
<style>
|
560
|
+
.center {
|
561
|
+
display: grid;
|
562
|
+
height: 100lvh;
|
563
|
+
display: grid;
|
564
|
+
justify-items: center;
|
565
|
+
align-items: center;
|
566
|
+
/* border: solid 1px red;*/
|
567
|
+
}
|
568
|
+
|
569
|
+
:global(html.game-box-no-scroll) {
|
570
|
+
overflow: clip;
|
571
|
+
scrollbar-width: none; /* Firefox */
|
572
|
+
-ms-overflow-style: none; /* IE and Edge */
|
573
|
+
}
|
574
|
+
:global(html.game-box-no-scroll::-webkit-scrollbar) {
|
575
|
+
display: none;
|
576
|
+
}
|
577
|
+
</style>
|