@joydle/ui 0.1.0

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.
Files changed (161) hide show
  1. package/dist/controls/ActionButton.d.ts +18 -0
  2. package/dist/controls/ActionButton.d.ts.map +1 -0
  3. package/dist/controls/ActionButton.js +108 -0
  4. package/dist/controls/ActionButton.js.map +1 -0
  5. package/dist/controls/ActionButtonGroup.d.ts +15 -0
  6. package/dist/controls/ActionButtonGroup.d.ts.map +1 -0
  7. package/dist/controls/ActionButtonGroup.js +55 -0
  8. package/dist/controls/ActionButtonGroup.js.map +1 -0
  9. package/dist/controls/Joystick.d.ts +9 -0
  10. package/dist/controls/Joystick.d.ts.map +1 -0
  11. package/dist/controls/Joystick.js +168 -0
  12. package/dist/controls/Joystick.js.map +1 -0
  13. package/dist/controls/SwipeZone.d.ts +10 -0
  14. package/dist/controls/SwipeZone.d.ts.map +1 -0
  15. package/dist/controls/SwipeZone.js +84 -0
  16. package/dist/controls/SwipeZone.js.map +1 -0
  17. package/dist/controls/TouchDPad.d.ts +9 -0
  18. package/dist/controls/TouchDPad.d.ts.map +1 -0
  19. package/dist/controls/TouchDPad.js +90 -0
  20. package/dist/controls/TouchDPad.js.map +1 -0
  21. package/dist/controls/index.d.ts +6 -0
  22. package/dist/controls/index.d.ts.map +1 -0
  23. package/dist/controls/index.js +9 -0
  24. package/dist/controls/index.js.map +1 -0
  25. package/dist/core/GameShell.d.ts +43 -0
  26. package/dist/core/GameShell.d.ts.map +1 -0
  27. package/dist/core/GameShell.js +91 -0
  28. package/dist/core/GameShell.js.map +1 -0
  29. package/dist/core/GameState.d.ts +35 -0
  30. package/dist/core/GameState.d.ts.map +1 -0
  31. package/dist/core/GameState.js +74 -0
  32. package/dist/core/GameState.js.map +1 -0
  33. package/dist/core/InputManager.d.ts +39 -0
  34. package/dist/core/InputManager.d.ts.map +1 -0
  35. package/dist/core/InputManager.js +149 -0
  36. package/dist/core/InputManager.js.map +1 -0
  37. package/dist/core/ScaleManager.d.ts +38 -0
  38. package/dist/core/ScaleManager.d.ts.map +1 -0
  39. package/dist/core/ScaleManager.js +106 -0
  40. package/dist/core/ScaleManager.js.map +1 -0
  41. package/dist/core/index.d.ts +9 -0
  42. package/dist/core/index.d.ts.map +1 -0
  43. package/dist/core/index.js +14 -0
  44. package/dist/core/index.js.map +1 -0
  45. package/dist/core/types.d.ts +113 -0
  46. package/dist/core/types.d.ts.map +1 -0
  47. package/dist/core/types.js +5 -0
  48. package/dist/core/types.js.map +1 -0
  49. package/dist/core/useGame.d.ts +22 -0
  50. package/dist/core/useGame.d.ts.map +1 -0
  51. package/dist/core/useGame.js +40 -0
  52. package/dist/core/useGame.js.map +1 -0
  53. package/dist/core/useGameLoop.d.ts +16 -0
  54. package/dist/core/useGameLoop.d.ts.map +1 -0
  55. package/dist/core/useGameLoop.js +35 -0
  56. package/dist/core/useGameLoop.js.map +1 -0
  57. package/dist/core/useGameSetup.d.ts +16 -0
  58. package/dist/core/useGameSetup.d.ts.map +1 -0
  59. package/dist/core/useGameSetup.js +27 -0
  60. package/dist/core/useGameSetup.js.map +1 -0
  61. package/dist/hud/BottomHint.d.ts +17 -0
  62. package/dist/hud/BottomHint.d.ts.map +1 -0
  63. package/dist/hud/BottomHint.js +68 -0
  64. package/dist/hud/BottomHint.js.map +1 -0
  65. package/dist/hud/ComboLabel.d.ts +18 -0
  66. package/dist/hud/ComboLabel.d.ts.map +1 -0
  67. package/dist/hud/ComboLabel.js +125 -0
  68. package/dist/hud/ComboLabel.js.map +1 -0
  69. package/dist/hud/HUD.d.ts +18 -0
  70. package/dist/hud/HUD.d.ts.map +1 -0
  71. package/dist/hud/HUD.js +72 -0
  72. package/dist/hud/HUD.js.map +1 -0
  73. package/dist/hud/Lives.d.ts +21 -0
  74. package/dist/hud/Lives.d.ts.map +1 -0
  75. package/dist/hud/Lives.js +92 -0
  76. package/dist/hud/Lives.js.map +1 -0
  77. package/dist/hud/Meter.d.ts +24 -0
  78. package/dist/hud/Meter.d.ts.map +1 -0
  79. package/dist/hud/Meter.js +133 -0
  80. package/dist/hud/Meter.js.map +1 -0
  81. package/dist/hud/MiniMap.d.ts +41 -0
  82. package/dist/hud/MiniMap.d.ts.map +1 -0
  83. package/dist/hud/MiniMap.js +103 -0
  84. package/dist/hud/MiniMap.js.map +1 -0
  85. package/dist/hud/Score.d.ts +15 -0
  86. package/dist/hud/Score.d.ts.map +1 -0
  87. package/dist/hud/Score.js +74 -0
  88. package/dist/hud/Score.js.map +1 -0
  89. package/dist/hud/Timer.d.ts +21 -0
  90. package/dist/hud/Timer.d.ts.map +1 -0
  91. package/dist/hud/Timer.js +111 -0
  92. package/dist/hud/Timer.js.map +1 -0
  93. package/dist/hud/WaveLabel.d.ts +15 -0
  94. package/dist/hud/WaveLabel.d.ts.map +1 -0
  95. package/dist/hud/WaveLabel.js +70 -0
  96. package/dist/hud/WaveLabel.js.map +1 -0
  97. package/dist/hud/index.d.ts +10 -0
  98. package/dist/hud/index.d.ts.map +1 -0
  99. package/dist/hud/index.js +13 -0
  100. package/dist/hud/index.js.map +1 -0
  101. package/dist/index.d.ts +8 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +13 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/screens/GameOverScreen.d.ts +36 -0
  106. package/dist/screens/GameOverScreen.d.ts.map +1 -0
  107. package/dist/screens/GameOverScreen.js +255 -0
  108. package/dist/screens/GameOverScreen.js.map +1 -0
  109. package/dist/screens/LoadingScreen.d.ts +21 -0
  110. package/dist/screens/LoadingScreen.d.ts.map +1 -0
  111. package/dist/screens/LoadingScreen.js +129 -0
  112. package/dist/screens/LoadingScreen.js.map +1 -0
  113. package/dist/screens/LobbyScreen.d.ts +39 -0
  114. package/dist/screens/LobbyScreen.d.ts.map +1 -0
  115. package/dist/screens/LobbyScreen.js +266 -0
  116. package/dist/screens/LobbyScreen.js.map +1 -0
  117. package/dist/screens/PauseOverlay.d.ts +29 -0
  118. package/dist/screens/PauseOverlay.d.ts.map +1 -0
  119. package/dist/screens/PauseOverlay.js +158 -0
  120. package/dist/screens/PauseOverlay.js.map +1 -0
  121. package/dist/screens/ScreenManager.d.ts +26 -0
  122. package/dist/screens/ScreenManager.d.ts.map +1 -0
  123. package/dist/screens/ScreenManager.js +61 -0
  124. package/dist/screens/ScreenManager.js.map +1 -0
  125. package/dist/screens/TitleScreen.d.ts +30 -0
  126. package/dist/screens/TitleScreen.d.ts.map +1 -0
  127. package/dist/screens/TitleScreen.js +263 -0
  128. package/dist/screens/TitleScreen.js.map +1 -0
  129. package/dist/screens/index.d.ts +7 -0
  130. package/dist/screens/index.d.ts.map +1 -0
  131. package/dist/screens/index.js +10 -0
  132. package/dist/screens/index.js.map +1 -0
  133. package/dist/theme/ThemeProvider.d.ts +25 -0
  134. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  135. package/dist/theme/ThemeProvider.js +51 -0
  136. package/dist/theme/ThemeProvider.js.map +1 -0
  137. package/dist/theme/index.d.ts +4 -0
  138. package/dist/theme/index.d.ts.map +1 -0
  139. package/dist/theme/index.js +7 -0
  140. package/dist/theme/index.js.map +1 -0
  141. package/dist/theme/palettes.d.ts +10 -0
  142. package/dist/theme/palettes.d.ts.map +1 -0
  143. package/dist/theme/palettes.js +173 -0
  144. package/dist/theme/palettes.js.map +1 -0
  145. package/dist/theme/theme.d.ts +24 -0
  146. package/dist/theme/theme.d.ts.map +1 -0
  147. package/dist/theme/theme.js +29 -0
  148. package/dist/theme/theme.js.map +1 -0
  149. package/dist/transitions/ScreenTransition.d.ts +19 -0
  150. package/dist/transitions/ScreenTransition.d.ts.map +1 -0
  151. package/dist/transitions/ScreenTransition.js +105 -0
  152. package/dist/transitions/ScreenTransition.js.map +1 -0
  153. package/dist/transitions/index.d.ts +3 -0
  154. package/dist/transitions/index.d.ts.map +1 -0
  155. package/dist/transitions/index.js +6 -0
  156. package/dist/transitions/index.js.map +1 -0
  157. package/dist/transitions/presets.d.ts +21 -0
  158. package/dist/transitions/presets.d.ts.map +1 -0
  159. package/dist/transitions/presets.js +37 -0
  160. package/dist/transitions/presets.js.map +1 -0
  161. package/package.json +62 -0
@@ -0,0 +1,18 @@
1
+ import type { JSX } from 'preact';
2
+ export type ActionButtonSize = 'small' | 'medium' | 'large';
3
+ export interface ActionButtonProps {
4
+ position: 'bottom-left' | 'bottom-right' | 'fullscreen';
5
+ label: string;
6
+ action?: string;
7
+ size?: ActionButtonSize;
8
+ opacity?: number;
9
+ /**
10
+ * When rendered inside an ActionButtonGroup, positioning is managed by the
11
+ * parent. Setting `_managed` suppresses fixed positioning and the media
12
+ * query class so the group can handle layout.
13
+ * @internal
14
+ */
15
+ _managed?: boolean;
16
+ }
17
+ export declare function ActionButton({ position, label, action, size, opacity, _managed, }: ActionButtonProps): JSX.Element;
18
+ //# sourceMappingURL=ActionButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionButton.d.ts","sourceRoot":"","sources":["../../src/controls/ActionButton.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAKlC,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,aAAa,GAAG,cAAc,GAAG,YAAY,CAAC;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA6BD,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,MAAiB,EACjB,IAAe,EACf,OAAa,EACb,QAAgB,GACjB,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,CA8GjC"}
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ // ---------------------------------------------------------------------------
3
+ // @joydle/ui — Single action button (touch)
4
+ // ---------------------------------------------------------------------------
5
+ import { useContext, useRef, useCallback } from 'preact/hooks';
6
+ import { GameContext } from '../core/index.js';
7
+ // ---- Size map -------------------------------------------------------------
8
+ const SIZE_MAP = {
9
+ small: 48,
10
+ medium: 64,
11
+ large: 80,
12
+ };
13
+ // ---- Helpers --------------------------------------------------------------
14
+ /** CSS to show only on touch devices. */
15
+ const TOUCH_ONLY = `
16
+ .joydle-action-btn { display: none; }
17
+ @media (pointer: coarse) { .joydle-action-btn { display: flex; } }
18
+ `;
19
+ let styleInjected = false;
20
+ function injectStyle() {
21
+ if (styleInjected)
22
+ return;
23
+ styleInjected = true;
24
+ const el = document.createElement('style');
25
+ el.textContent = TOUCH_ONLY;
26
+ document.head.appendChild(el);
27
+ }
28
+ // ---- Component ------------------------------------------------------------
29
+ export function ActionButton({ position, label, action = 'action', size = 'medium', opacity = 0.6, _managed = false, }) {
30
+ const ctx = useContext(GameContext);
31
+ const inputManager = ctx?.inputManager ?? null;
32
+ const pressedRef = useRef(false);
33
+ injectStyle();
34
+ const px = SIZE_MAP[size];
35
+ const handleTouchStart = useCallback((e) => {
36
+ e.preventDefault();
37
+ pressedRef.current = true;
38
+ if (inputManager)
39
+ inputManager.setAction(action, true);
40
+ // Visual feedback via the element itself.
41
+ const el = e.currentTarget;
42
+ if (el)
43
+ el.style.transform = 'scale(0.9)';
44
+ }, [inputManager, action]);
45
+ const handleTouchEnd = useCallback((e) => {
46
+ e.preventDefault();
47
+ pressedRef.current = false;
48
+ if (inputManager)
49
+ inputManager.setAction(action, false);
50
+ const el = e.currentTarget;
51
+ if (el)
52
+ el.style.transform = 'scale(1)';
53
+ }, [inputManager, action]);
54
+ // --- Fullscreen mode: invisible overlay covering the entire screen --------
55
+ if (position === 'fullscreen') {
56
+ const fullStyle = {
57
+ position: 'fixed',
58
+ top: 0,
59
+ left: 0,
60
+ width: '100%',
61
+ height: '100%',
62
+ zIndex: 999,
63
+ pointerEvents: 'auto',
64
+ touchAction: 'none',
65
+ background: 'transparent',
66
+ // Invisible but touchable.
67
+ opacity: 0,
68
+ };
69
+ return (_jsx("div", { class: "joydle-action-btn", style: fullStyle, onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, onTouchCancel: handleTouchEnd }));
70
+ }
71
+ // --- Standard circular button ---------------------------------------------
72
+ const isLeft = position === 'bottom-left';
73
+ const baseStyle = {
74
+ width: px,
75
+ height: px,
76
+ borderRadius: '50%',
77
+ background: 'rgba(255,255,255,0.25)',
78
+ border: '2px solid rgba(255,255,255,0.5)',
79
+ display: 'flex',
80
+ alignItems: 'center',
81
+ justifyContent: 'center',
82
+ pointerEvents: 'auto',
83
+ touchAction: 'none',
84
+ userSelect: 'none',
85
+ WebkitUserSelect: 'none',
86
+ transition: 'transform 0.08s ease',
87
+ color: 'white',
88
+ fontSize: Math.round(px * 0.3),
89
+ fontWeight: 700,
90
+ lineHeight: 1,
91
+ boxSizing: 'border-box',
92
+ };
93
+ // When managed by a group, skip fixed positioning.
94
+ if (!_managed) {
95
+ Object.assign(baseStyle, {
96
+ position: 'fixed',
97
+ bottom: 24,
98
+ [isLeft ? 'left' : 'right']: 24,
99
+ opacity,
100
+ zIndex: 1000,
101
+ });
102
+ }
103
+ else {
104
+ baseStyle.opacity = opacity;
105
+ }
106
+ return (_jsx("div", { class: _managed ? undefined : 'joydle-action-btn', style: baseStyle, onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, onTouchCancel: handleTouchEnd, children: label }));
107
+ }
108
+ //# sourceMappingURL=ActionButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionButton.js","sourceRoot":"","sources":["../../src/controls/ActionButton.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { JSX } from 'preact';
2
+ import { type ActionButtonSize } from './ActionButton.js';
3
+ export interface ActionButtonConfig {
4
+ label: string;
5
+ action: string;
6
+ size?: ActionButtonSize;
7
+ }
8
+ export interface ActionButtonGroupProps {
9
+ position: 'bottom-left' | 'bottom-right';
10
+ buttons: ActionButtonConfig[];
11
+ layout: 'row' | 'column' | 'triangle';
12
+ opacity?: number;
13
+ }
14
+ export declare function ActionButtonGroup({ position, buttons, layout, opacity, }: ActionButtonGroupProps): JSX.Element;
15
+ //# sourceMappingURL=ActionButtonGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionButtonGroup.d.ts","sourceRoot":"","sources":["../../src/controls/ActionButtonGroup.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAIxE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,aAAa,GAAG,cAAc,CAAC;IACzC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,UAAU,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAqBD,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAa,GACd,EAAE,sBAAsB,GAAG,GAAG,CAAC,OAAO,CAuFtC"}
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { ActionButton } from './ActionButton.js';
3
+ // ---- Helpers --------------------------------------------------------------
4
+ /** CSS to show only on touch devices. */
5
+ const TOUCH_ONLY = `
6
+ .joydle-action-group { display: none; }
7
+ @media (pointer: coarse) { .joydle-action-group { display: flex; } }
8
+ `;
9
+ let styleInjected = false;
10
+ function injectStyle() {
11
+ if (styleInjected)
12
+ return;
13
+ styleInjected = true;
14
+ const el = document.createElement('style');
15
+ el.textContent = TOUCH_ONLY;
16
+ document.head.appendChild(el);
17
+ }
18
+ // ---- Component ------------------------------------------------------------
19
+ export function ActionButtonGroup({ position, buttons, layout, opacity = 0.6, }) {
20
+ injectStyle();
21
+ const isLeft = position === 'bottom-left';
22
+ const gap = 12;
23
+ // --- Triangle layout ------------------------------------------------------
24
+ // First button is primary (larger), remaining are arranged diagonally below.
25
+ if (layout === 'triangle' && buttons.length >= 1) {
26
+ const containerStyle = {
27
+ position: 'fixed',
28
+ bottom: 24,
29
+ [isLeft ? 'left' : 'right']: 24,
30
+ zIndex: 1000,
31
+ pointerEvents: 'auto',
32
+ display: 'flex',
33
+ flexDirection: 'column',
34
+ alignItems: 'center',
35
+ gap,
36
+ };
37
+ const primary = buttons[0];
38
+ const secondary = buttons.slice(1);
39
+ return (_jsxs("div", { class: "joydle-action-group", style: containerStyle, children: [secondary.length > 0 && (_jsx("div", { style: { display: 'flex', gap, justifyContent: 'center' }, children: secondary.map((btn) => (_jsx(ActionButton, { position: position, label: btn.label, action: btn.action, size: btn.size ?? 'small', opacity: opacity, _managed: true }, btn.action))) })), _jsx(ActionButton, { position: position, label: primary.label, action: primary.action, size: primary.size ?? 'large', opacity: opacity, _managed: true })] }));
40
+ }
41
+ // --- Row / Column layout --------------------------------------------------
42
+ const containerStyle = {
43
+ position: 'fixed',
44
+ bottom: 24,
45
+ [isLeft ? 'left' : 'right']: 24,
46
+ zIndex: 1000,
47
+ pointerEvents: 'auto',
48
+ display: 'flex',
49
+ flexDirection: layout === 'row' ? 'row' : 'column',
50
+ alignItems: 'center',
51
+ gap,
52
+ };
53
+ return (_jsx("div", { class: "joydle-action-group", style: containerStyle, children: buttons.map((btn) => (_jsx(ActionButton, { position: position, label: btn.label, action: btn.action, size: btn.size ?? 'medium', opacity: opacity, _managed: true }, btn.action))) }));
54
+ }
55
+ //# sourceMappingURL=ActionButtonGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionButtonGroup.js","sourceRoot":"","sources":["../../src/controls/ActionButtonGroup.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { JSX } from 'preact';
2
+ export interface JoystickProps {
3
+ position: 'bottom-left' | 'bottom-right';
4
+ deadzone?: number;
5
+ size?: number;
6
+ opacity?: number;
7
+ }
8
+ export declare function Joystick({ position, deadzone, size, opacity, }: JoystickProps): JSX.Element;
9
+ //# sourceMappingURL=Joystick.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Joystick.d.ts","sourceRoot":"","sources":["../../src/controls/Joystick.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAKlC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,GAAG,cAAc,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAqBD,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,QAAe,EACf,IAAU,EACV,OAAa,GACd,EAAE,aAAa,GAAG,GAAG,CAAC,OAAO,CA6L7B"}
@@ -0,0 +1,168 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ // ---------------------------------------------------------------------------
3
+ // @joydle/ui — Virtual analog joystick
4
+ // ---------------------------------------------------------------------------
5
+ import { useContext, useRef, useCallback, useEffect } from 'preact/hooks';
6
+ import { GameContext } from '../core/index.js';
7
+ // ---- Helpers --------------------------------------------------------------
8
+ /** CSS to show only on touch devices. */
9
+ const TOUCH_ONLY = `
10
+ .joydle-joystick { display: none; }
11
+ @media (pointer: coarse) { .joydle-joystick { display: block; } }
12
+ `;
13
+ let styleInjected = false;
14
+ function injectStyle() {
15
+ if (styleInjected)
16
+ return;
17
+ styleInjected = true;
18
+ const el = document.createElement('style');
19
+ el.textContent = TOUCH_ONLY;
20
+ document.head.appendChild(el);
21
+ }
22
+ // ---- Component ------------------------------------------------------------
23
+ export function Joystick({ position, deadzone = 0.15, size = 120, opacity = 0.6, }) {
24
+ const ctx = useContext(GameContext);
25
+ const inputManager = ctx?.inputManager ?? null;
26
+ const outerRef = useRef(null);
27
+ const thumbRef = useRef(null);
28
+ const trackingRef = useRef(null);
29
+ const centerRef = useRef({ x: 0, y: 0 });
30
+ injectStyle();
31
+ const radius = size / 2;
32
+ const thumbSize = Math.round(size * 0.4);
33
+ const resetThumb = useCallback(() => {
34
+ if (thumbRef.current) {
35
+ thumbRef.current.style.transform = 'translate(-50%, -50%)';
36
+ thumbRef.current.style.left = '50%';
37
+ thumbRef.current.style.top = '50%';
38
+ }
39
+ }, []);
40
+ const releaseAll = useCallback(() => {
41
+ if (!inputManager)
42
+ return;
43
+ inputManager.setAction('left', false);
44
+ inputManager.setAction('right', false);
45
+ inputManager.setAction('up', false);
46
+ inputManager.setAction('down', false);
47
+ }, [inputManager]);
48
+ const updateFromDelta = useCallback((dx, dy) => {
49
+ if (!inputManager)
50
+ return;
51
+ const dist = Math.sqrt(dx * dx + dy * dy);
52
+ const magnitude = Math.min(dist / radius, 1);
53
+ // Clamp thumb position to outer ring boundary.
54
+ const clampedDist = Math.min(dist, radius);
55
+ const angle = Math.atan2(dy, dx);
56
+ const clampedX = Math.cos(angle) * clampedDist;
57
+ const clampedY = Math.sin(angle) * clampedDist;
58
+ if (thumbRef.current) {
59
+ thumbRef.current.style.left = `${radius + clampedX}px`;
60
+ thumbRef.current.style.top = `${radius + clampedY}px`;
61
+ thumbRef.current.style.transform = 'translate(-50%, -50%)';
62
+ }
63
+ // Inside deadzone: release all.
64
+ if (magnitude < deadzone) {
65
+ releaseAll();
66
+ return;
67
+ }
68
+ // Map angle to discrete directions.
69
+ // angle 0 = right, PI/2 = down, PI = left, -PI/2 = up
70
+ const nx = Math.cos(angle);
71
+ const ny = Math.sin(angle);
72
+ // Threshold for activating a direction (cos 45deg ~ 0.707).
73
+ const threshold = 0.383; // cos(67.5deg) — generous diagonal zone
74
+ inputManager.setAction('right', nx > threshold);
75
+ inputManager.setAction('left', nx < -threshold);
76
+ inputManager.setAction('down', ny > threshold);
77
+ inputManager.setAction('up', ny < -threshold);
78
+ }, [inputManager, radius, deadzone, releaseAll]);
79
+ const handleTouchStart = useCallback((e) => {
80
+ e.preventDefault();
81
+ if (trackingRef.current !== null)
82
+ return; // already tracking
83
+ const touch = e.changedTouches[0];
84
+ if (!touch)
85
+ return;
86
+ const outer = outerRef.current;
87
+ if (!outer)
88
+ return;
89
+ const rect = outer.getBoundingClientRect();
90
+ centerRef.current = {
91
+ x: rect.left + rect.width / 2,
92
+ y: rect.top + rect.height / 2,
93
+ };
94
+ trackingRef.current = touch.identifier;
95
+ const dx = touch.clientX - centerRef.current.x;
96
+ const dy = touch.clientY - centerRef.current.y;
97
+ updateFromDelta(dx, dy);
98
+ }, [updateFromDelta]);
99
+ const handleTouchMove = useCallback((e) => {
100
+ e.preventDefault();
101
+ if (trackingRef.current === null)
102
+ return;
103
+ for (let i = 0; i < e.changedTouches.length; i++) {
104
+ const touch = e.changedTouches[i];
105
+ if (touch.identifier === trackingRef.current) {
106
+ const dx = touch.clientX - centerRef.current.x;
107
+ const dy = touch.clientY - centerRef.current.y;
108
+ updateFromDelta(dx, dy);
109
+ return;
110
+ }
111
+ }
112
+ }, [updateFromDelta]);
113
+ const handleTouchEnd = useCallback((e) => {
114
+ e.preventDefault();
115
+ if (trackingRef.current === null)
116
+ return;
117
+ for (let i = 0; i < e.changedTouches.length; i++) {
118
+ if (e.changedTouches[i].identifier === trackingRef.current) {
119
+ trackingRef.current = null;
120
+ resetThumb();
121
+ releaseAll();
122
+ return;
123
+ }
124
+ }
125
+ }, [resetThumb, releaseAll]);
126
+ // Cleanup on unmount.
127
+ useEffect(() => {
128
+ return () => {
129
+ releaseAll();
130
+ };
131
+ }, [releaseAll]);
132
+ const isLeft = position === 'bottom-left';
133
+ const containerStyle = {
134
+ position: 'fixed',
135
+ bottom: 24,
136
+ [isLeft ? 'left' : 'right']: 24,
137
+ width: size,
138
+ height: size,
139
+ opacity,
140
+ pointerEvents: 'auto',
141
+ zIndex: 1000,
142
+ touchAction: 'none',
143
+ };
144
+ const outerStyle = {
145
+ position: 'relative',
146
+ width: size,
147
+ height: size,
148
+ borderRadius: '50%',
149
+ background: 'rgba(255,255,255,0.15)',
150
+ border: '2px solid rgba(255,255,255,0.4)',
151
+ boxSizing: 'border-box',
152
+ };
153
+ const thumbStyle = {
154
+ position: 'absolute',
155
+ width: thumbSize,
156
+ height: thumbSize,
157
+ borderRadius: '50%',
158
+ background: 'rgba(255,255,255,0.5)',
159
+ border: '2px solid rgba(255,255,255,0.8)',
160
+ left: '50%',
161
+ top: '50%',
162
+ transform: 'translate(-50%, -50%)',
163
+ pointerEvents: 'none',
164
+ boxSizing: 'border-box',
165
+ };
166
+ return (_jsx("div", { class: "joydle-joystick", style: containerStyle, children: _jsx("div", { ref: outerRef, style: outerStyle, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onTouchCancel: handleTouchEnd, children: _jsx("div", { ref: thumbRef, style: thumbStyle }) }) }));
167
+ }
168
+ //# sourceMappingURL=Joystick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Joystick.js","sourceRoot":"","sources":["../../src/controls/Joystick.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { JSX, ComponentChildren } from 'preact';
2
+ export type SwipeDirection = 'left' | 'right' | 'up' | 'down';
3
+ export interface SwipeZoneProps {
4
+ direction: 'horizontal' | 'vertical' | 'both';
5
+ onSwipe: (direction: SwipeDirection) => void;
6
+ threshold?: number;
7
+ children?: ComponentChildren;
8
+ }
9
+ export declare function SwipeZone({ direction, onSwipe, threshold, children, }: SwipeZoneProps): JSX.Element;
10
+ //# sourceMappingURL=SwipeZone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SwipeZone.d.ts","sourceRoot":"","sources":["../../src/controls/SwipeZone.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAIrD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;IAC9C,OAAO,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAqBD,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,OAAO,EACP,SAAc,EACd,QAAQ,GACT,EAAE,cAAc,GAAG,GAAG,CAAC,OAAO,CAoF9B"}
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ // ---------------------------------------------------------------------------
3
+ // @joydle/ui — Swipe detection zone
4
+ // ---------------------------------------------------------------------------
5
+ import { useRef, useCallback } from 'preact/hooks';
6
+ // ---- Helpers --------------------------------------------------------------
7
+ /** CSS to show only on touch devices. */
8
+ const TOUCH_ONLY = `
9
+ .joydle-swipe-zone { display: none; }
10
+ @media (pointer: coarse) { .joydle-swipe-zone { display: block; } }
11
+ `;
12
+ let styleInjected = false;
13
+ function injectStyle() {
14
+ if (styleInjected)
15
+ return;
16
+ styleInjected = true;
17
+ const el = document.createElement('style');
18
+ el.textContent = TOUCH_ONLY;
19
+ document.head.appendChild(el);
20
+ }
21
+ // ---- Component ------------------------------------------------------------
22
+ export function SwipeZone({ direction, onSwipe, threshold = 30, children, }) {
23
+ injectStyle();
24
+ const startRef = useRef(null);
25
+ const handleTouchStart = useCallback((e) => {
26
+ e.preventDefault();
27
+ const touch = e.changedTouches[0];
28
+ if (!touch)
29
+ return;
30
+ startRef.current = {
31
+ x: touch.clientX,
32
+ y: touch.clientY,
33
+ id: touch.identifier,
34
+ };
35
+ }, []);
36
+ const handleTouchEnd = useCallback((e) => {
37
+ e.preventDefault();
38
+ const start = startRef.current;
39
+ if (!start)
40
+ return;
41
+ // Find the matching touch.
42
+ let endTouch = null;
43
+ for (let i = 0; i < e.changedTouches.length; i++) {
44
+ if (e.changedTouches[i].identifier === start.id) {
45
+ endTouch = e.changedTouches[i];
46
+ break;
47
+ }
48
+ }
49
+ if (!endTouch)
50
+ return;
51
+ const dx = endTouch.clientX - start.x;
52
+ const dy = endTouch.clientY - start.y;
53
+ const absDx = Math.abs(dx);
54
+ const absDy = Math.abs(dy);
55
+ startRef.current = null;
56
+ // Determine dominant axis and check threshold.
57
+ if (direction === 'horizontal' || direction === 'both') {
58
+ if (absDx >= threshold && absDx >= absDy) {
59
+ onSwipe(dx < 0 ? 'left' : 'right');
60
+ return;
61
+ }
62
+ }
63
+ if (direction === 'vertical' || direction === 'both') {
64
+ if (absDy >= threshold && absDy >= absDx) {
65
+ onSwipe(dy < 0 ? 'up' : 'down');
66
+ return;
67
+ }
68
+ }
69
+ }, [direction, onSwipe, threshold]);
70
+ const style = {
71
+ position: 'absolute',
72
+ top: 0,
73
+ left: 0,
74
+ width: '100%',
75
+ height: '100%',
76
+ pointerEvents: 'auto',
77
+ touchAction: 'none',
78
+ // Invisible.
79
+ background: 'transparent',
80
+ zIndex: 998,
81
+ };
82
+ return (_jsx("div", { class: "joydle-swipe-zone", style: style, onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, onTouchCancel: handleTouchEnd, children: children }));
83
+ }
84
+ //# sourceMappingURL=SwipeZone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SwipeZone.js","sourceRoot":"","sources":["../../src/controls/SwipeZone.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { JSX } from 'preact';
2
+ export interface TouchDPadProps {
3
+ position: 'bottom-left' | 'bottom-right';
4
+ directions: 'horizontal' | 'four-way';
5
+ size?: number;
6
+ opacity?: number;
7
+ }
8
+ export declare function TouchDPad({ position, directions, size, opacity, }: TouchDPadProps): JSX.Element;
9
+ //# sourceMappingURL=TouchDPad.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TouchDPad.d.ts","sourceRoot":"","sources":["../../src/controls/TouchDPad.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAKlC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,GAAG,cAAc,CAAC;IACzC,UAAU,EAAE,YAAY,GAAG,UAAU,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA8BD,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,UAAU,EACV,IAAU,EACV,OAAa,GACd,EAAE,cAAc,GAAG,GAAG,CAAC,OAAO,CA8G9B"}
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ // ---------------------------------------------------------------------------
3
+ // @joydle/ui — Touch D-Pad control (2-dir or 4-dir)
4
+ // ---------------------------------------------------------------------------
5
+ import { useContext, useRef, useCallback } from 'preact/hooks';
6
+ import { GameContext } from '../core/index.js';
7
+ const ARROW_PATHS = {
8
+ left: 'M 70 50 L 30 50 L 45 35 M 30 50 L 45 65',
9
+ right: 'M 30 50 L 70 50 L 55 35 M 70 50 L 55 65',
10
+ up: 'M 50 70 L 50 30 L 35 45 M 50 30 L 65 45',
11
+ down: 'M 50 30 L 50 70 L 35 55 M 50 70 L 65 55',
12
+ };
13
+ /** CSS to show only on touch devices. */
14
+ const TOUCH_ONLY = `
15
+ .joydle-touch-dpad { display: none; }
16
+ @media (pointer: coarse) { .joydle-touch-dpad { display: flex; } }
17
+ `;
18
+ let styleInjected = false;
19
+ function injectStyle() {
20
+ if (styleInjected)
21
+ return;
22
+ styleInjected = true;
23
+ const el = document.createElement('style');
24
+ el.textContent = TOUCH_ONLY;
25
+ document.head.appendChild(el);
26
+ }
27
+ // ---- Component ------------------------------------------------------------
28
+ export function TouchDPad({ position, directions, size = 120, opacity = 0.6, }) {
29
+ const ctx = useContext(GameContext);
30
+ const activeRef = useRef(new Set());
31
+ injectStyle();
32
+ const inputManager = ctx?.inputManager ?? null;
33
+ const pressDirection = useCallback((dir, down) => {
34
+ if (!inputManager)
35
+ return;
36
+ if (down) {
37
+ activeRef.current.add(dir);
38
+ }
39
+ else {
40
+ activeRef.current.delete(dir);
41
+ }
42
+ inputManager.setAction(dir, down);
43
+ }, [inputManager]);
44
+ const handleTouchStart = useCallback((dir) => (e) => {
45
+ e.preventDefault();
46
+ pressDirection(dir, true);
47
+ }, [pressDirection]);
48
+ const handleTouchEnd = useCallback((dir) => (e) => {
49
+ e.preventDefault();
50
+ pressDirection(dir, false);
51
+ }, [pressDirection]);
52
+ // Button size is proportional to the overall pad size.
53
+ const btnSize = Math.round(size * 0.36);
54
+ const isLeft = position === 'bottom-left';
55
+ const containerStyle = {
56
+ position: 'fixed',
57
+ bottom: 24,
58
+ [isLeft ? 'left' : 'right']: 24,
59
+ width: size,
60
+ height: directions === 'horizontal' ? btnSize : size,
61
+ opacity,
62
+ pointerEvents: 'auto',
63
+ zIndex: 1000,
64
+ };
65
+ const btnBase = {
66
+ position: 'absolute',
67
+ width: btnSize,
68
+ height: btnSize,
69
+ borderRadius: '50%',
70
+ background: 'rgba(255,255,255,0.25)',
71
+ border: '2px solid rgba(255,255,255,0.5)',
72
+ display: 'flex',
73
+ alignItems: 'center',
74
+ justifyContent: 'center',
75
+ touchAction: 'none',
76
+ userSelect: 'none',
77
+ WebkitUserSelect: 'none',
78
+ };
79
+ // Layout positions for buttons within the container.
80
+ const center = Math.round((size - btnSize) / 2);
81
+ const edge = 0;
82
+ const far = size - btnSize;
83
+ const renderBtn = (dir, style) => (_jsx("div", { style: { ...btnBase, ...style }, onTouchStart: handleTouchStart(dir), onTouchEnd: handleTouchEnd(dir), onTouchCancel: handleTouchEnd(dir), children: _jsx("svg", { width: btnSize * 0.6, height: btnSize * 0.6, viewBox: "0 0 100 100", children: _jsx("path", { d: ARROW_PATHS[dir], stroke: "white", "stroke-width": "6", "stroke-linecap": "round", fill: "none" }) }) }));
84
+ if (directions === 'horizontal') {
85
+ return (_jsxs("div", { class: "joydle-touch-dpad", style: containerStyle, children: [renderBtn('left', { left: edge, top: 0 }), renderBtn('right', { right: edge, top: 0 })] }));
86
+ }
87
+ // Four-way layout: up top-center, down bottom-center, left center-left, right center-right.
88
+ return (_jsxs("div", { class: "joydle-touch-dpad", style: containerStyle, children: [renderBtn('up', { left: center, top: edge }), renderBtn('down', { left: center, top: far }), renderBtn('left', { left: edge, top: center }), renderBtn('right', { left: far, top: center })] }));
89
+ }
90
+ //# sourceMappingURL=TouchDPad.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TouchDPad.js","sourceRoot":"","sources":["../../src/controls/TouchDPad.tsx"],"names":[],"mappings":"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"}