@number10/phaserjsx 0.6.1 → 4.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 (108) hide show
  1. package/README.md +43 -3
  2. package/dist/chunk-C2EiDwsr.cjs +35 -0
  3. package/dist/clip/index.cjs +696 -0
  4. package/dist/clip/index.cjs.map +1 -0
  5. package/dist/clip/index.d.ts +3 -0
  6. package/dist/clip/index.d.ts.map +1 -0
  7. package/dist/clip/index.js +688 -0
  8. package/dist/clip/index.js.map +1 -0
  9. package/dist/clip/stencil-clip-extension.d.ts +18 -0
  10. package/dist/clip/stencil-clip-extension.d.ts.map +1 -0
  11. package/dist/clip/stencil-clip.d.ts +134 -0
  12. package/dist/clip/stencil-clip.d.ts.map +1 -0
  13. package/dist/components/appliers/applyBackground.d.ts +2 -1
  14. package/dist/components/appliers/applyBackground.d.ts.map +1 -1
  15. package/dist/components/appliers/applyGestures.d.ts.map +1 -1
  16. package/dist/components/appliers/applyTooltip.d.ts.map +1 -1
  17. package/dist/components/backgroundImage.d.ts +12 -0
  18. package/dist/components/backgroundImage.d.ts.map +1 -0
  19. package/dist/components/creators/createBackground.d.ts +2 -1
  20. package/dist/components/creators/createBackground.d.ts.map +1 -1
  21. package/dist/components/creators/createGestures.d.ts.map +1 -1
  22. package/dist/components/custom/Accordion.d.ts.map +1 -1
  23. package/dist/components/custom/CharText/CharText.d.ts.map +1 -1
  24. package/dist/components/custom/Dialog.d.ts +1 -1
  25. package/dist/components/custom/Divider.d.ts +1 -1
  26. package/dist/components/custom/Dropdown.d.ts.map +1 -1
  27. package/dist/components/custom/Graphics.d.ts +1 -1
  28. package/dist/components/custom/Graphics.d.ts.map +1 -1
  29. package/dist/components/custom/Icon.d.ts.map +1 -1
  30. package/dist/components/custom/Image.d.ts.map +1 -1
  31. package/dist/components/custom/Joystick.d.ts +1 -1
  32. package/dist/components/custom/NineSlice.d.ts.map +1 -1
  33. package/dist/components/custom/Particles.d.ts +1 -1
  34. package/dist/components/custom/Particles.d.ts.map +1 -1
  35. package/dist/components/custom/Portal.d.ts.map +1 -1
  36. package/dist/components/custom/RefOriginView.d.ts +1 -1
  37. package/dist/components/custom/ScrollView.d.ts.map +1 -1
  38. package/dist/components/custom/Sprite.d.ts +1 -1
  39. package/dist/components/custom/Sprite.d.ts.map +1 -1
  40. package/dist/components/custom/Text.d.ts.map +1 -1
  41. package/dist/components/custom/TileSprite.d.ts +1 -1
  42. package/dist/components/custom/TileSprite.d.ts.map +1 -1
  43. package/dist/components/custom/View.d.ts.map +1 -1
  44. package/dist/components/custom/index.cjs +34 -36
  45. package/dist/components/custom/index.js +2 -37
  46. package/dist/components/primitives/graphics.d.ts.map +1 -1
  47. package/dist/components/primitives/image.d.ts.map +1 -1
  48. package/dist/components/primitives/nineslice.d.ts.map +1 -1
  49. package/dist/components/primitives/particles.d.ts.map +1 -1
  50. package/dist/components/primitives/sprite.d.ts.map +1 -1
  51. package/dist/components/primitives/text.d.ts.map +1 -1
  52. package/dist/components/primitives/tilesprite.d.ts.map +1 -1
  53. package/dist/components/primitives/view.d.ts.map +1 -1
  54. package/dist/custom-C_w8D39m.js +29259 -0
  55. package/dist/custom-C_w8D39m.js.map +1 -0
  56. package/dist/custom-Dp3yAJdU.cjs +30498 -0
  57. package/dist/custom-Dp3yAJdU.cjs.map +1 -0
  58. package/dist/fx/fx-creators/blur.d.ts.map +1 -1
  59. package/dist/fx/fx-creators/color-matrix.d.ts.map +1 -1
  60. package/dist/fx/fx-creators/glow.d.ts.map +1 -1
  61. package/dist/fx/fx-creators/pixelate.d.ts.map +1 -1
  62. package/dist/fx/fx-creators/shadow.d.ts.map +1 -1
  63. package/dist/fx/fx-creators/vignette.d.ts.map +1 -1
  64. package/dist/fx/use-fx.d.ts +3 -3
  65. package/dist/fx/use-fx.d.ts.map +1 -1
  66. package/dist/gestures/gesture-manager.d.ts +3 -1
  67. package/dist/gestures/gesture-manager.d.ts.map +1 -1
  68. package/dist/gestures/gesture-types.d.ts +1 -1
  69. package/dist/gestures/gesture-types.d.ts.map +1 -1
  70. package/dist/hooks.d.ts +9 -8
  71. package/dist/hooks.d.ts.map +1 -1
  72. package/dist/index.cjs +1628 -2837
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.d.ts +1 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +1430 -2866
  77. package/dist/index.js.map +1 -1
  78. package/dist/jsx-dev-runtime.cjs +12 -7
  79. package/dist/jsx-dev-runtime.cjs.map +1 -1
  80. package/dist/jsx-dev-runtime.js +11 -11
  81. package/dist/jsx-dev-runtime.js.map +1 -1
  82. package/dist/jsx-runtime.cjs +55 -33
  83. package/dist/jsx-runtime.cjs.map +1 -1
  84. package/dist/jsx-runtime.js +56 -37
  85. package/dist/jsx-runtime.js.map +1 -1
  86. package/dist/layout/appliers/background-applier.d.ts.map +1 -1
  87. package/dist/layout/layout-engine.d.ts.map +1 -1
  88. package/dist/layout/types.d.ts +2 -1
  89. package/dist/layout/types.d.ts.map +1 -1
  90. package/dist/scene-backgrounds.d.ts +51 -1
  91. package/dist/scene-backgrounds.d.ts.map +1 -1
  92. package/dist/scripts/generate-icon-loaders.js +146 -143
  93. package/dist/scripts/generate-icon-types.js +94 -83
  94. package/dist/scripts/generate-icons.d.ts +1 -2
  95. package/dist/scripts/generate-icons.d.ts.map +1 -1
  96. package/dist/scripts/generate-icons.js +381 -399
  97. package/dist/scripts/icon-generator-config.d.ts +0 -1
  98. package/dist/scripts/icon-generator-config.js +7 -4
  99. package/dist/theme-base.d.ts.map +1 -1
  100. package/dist/vite-plugin-icons.d.ts +1 -2
  101. package/dist/vite-plugin-icons.js +109 -90
  102. package/package.json +29 -22
  103. package/dist/TransformOriginView-Dw_HKnFH.cjs +0 -17116
  104. package/dist/TransformOriginView-Dw_HKnFH.cjs.map +0 -1
  105. package/dist/TransformOriginView-i8uVBHb1.js +0 -17100
  106. package/dist/TransformOriginView-i8uVBHb1.js.map +0 -1
  107. package/dist/components/custom/index.cjs.map +0 -1
  108. package/dist/components/custom/index.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,2931 +1,1495 @@
1
- import { t as equal, v as viewportRegistry, w as parseSize, x as resolveSize, y as getGestureManager, z as calculateLayout, E as themeRegistry, F as normalizeGap, G as getChildSize, H as DebugLogger, K as useTheme, L as getThemedProps, O as register, Q as useEffect, U as useScene, V as useRef, X as useCallback, Y as mountJSX, Z as useState, _ as getPresetWithMode, $ as alpha, a0 as remountAll, a1 as defaultRadiusTokens, a2 as defaultSizeTokens, a3 as defaultSpacingTokens, a4 as createTextStyleTokens } from "./TransformOriginView-i8uVBHb1.js";
2
- import { A, a, B, C, b, af, bj, a7, ab, ac, D, c, d, ag, bc, bo, I, f, J, a8, M, N, g, P, h, R, i, n, j, bk, S, l, m, o, bl, T, p, q, bd, r, s, be, W, bg, bC, ah, bD, k, aj, ak, b2, b7, al, am, an, ao, ap, e, aq, ar, as, at, au, av, aw, ax, ay, az, bz, b5, aA, aB, aC, aD, bu, bv, bK, b3, aW, bA, bE, bF, aM, bB, aE, aL, aO, aJ, a9, bG, aa, aQ, bw, bp, b1, bh, bx, by, b4, bH, b8, b0, a5, a6, b6, bq, br, bI, ba, bf, bJ, aZ, aX, aY, ai, bs, bt, aT, aU, ad, b9, bb, bi, aN, aG, ae, u, aS, aP, aK, aH, aV, a_, a$, bm, bn, aI, aR, aF } from "./TransformOriginView-i8uVBHb1.js";
1
+ import { $ as RadioButton, $n as textCreator, $t as shouldComponentUpdate, A as computed, An as midnightPreset, At as createTheme, B as DOMInputElement, Bn as hexToNumber, Bt as remountAll, C as CharText, Cn as defaultTextStyleTokens, Cr as graphicsCreator, Ct as createWiggleEffect, D as Modal, Dn as generateColorScale, Dr as register, Dt as useGameObjectEffect, E as Dialog, En as forestGreenPreset, Er as nodeRegistry, Et as createZoomOutEffect, F as SpringPhysics, Fn as darken, Ft as getMountStats, G as useSVGTextures, Gn as rgbToHsl, Gt as disposeCtx, H as releaseSVGTexture, Hn as lightenHex, Ht as unmountJSX, I as animatedSignal, In as darkenHex, It as mount, J as Particles, Jn as normalizeCornerRadius, Jt as getLayoutProps, K as svgToTexture, Kn as rgbToNumber, Kt as getBackgroundGraphics, L as isAnimatedSignal, Ln as ensureContrast, Lt as mountJSX, M as useSprings, Mn as presets, Mt as mergeThemes, N as DEFAULT_SPRING_CONFIG, Nn as alpha, Nt as themeRegistry, O as Portal, On as getPreset, Ot as viewCreator, P as SPRING_PRESETS, Pn as createTextStyle, Pt as createElement, Q as RadioGroup, Qn as tileSpritePatcher, Qt as shallowEqual, R as unwrapSignal, Rn as getContrastRatio, Rt as normalizeVNodeLike, S as CharTextInput, Sn as defaultSpacingTokens, Sr as imagePatcher, St as createTadaEffect, T as WrapText, Tn as applyLightMode, Tr as host, Tt as createZoomInEffect, U as releaseSVGTextures, Un as numberToHex, Ut as portalRegistry, V as releaseAllSVGTextures, Vn as lighten, Vt as unmount, W as useSVGTexture, Wn as numberToRgb, Wt as View, X as Text, Xn as normalizeGap, Xt as getLayoutSize, Y as TileSprite, Yn as normalizeEdgeInsets, Yt as getLayoutRect, Z as Sprite, Zn as tileSpriteCreator, Zt as getWorldLayoutRect, _ as Dropdown, _n as createDefaultTheme, _r as buildEmitZone, _t as createShakeEffect, a as Tabs, an as useLayoutRect, ar as spriteCreator, at as resolveEffect, b as calculateSliderSize, bn as defaultRadiusTokens, br as nineSlicePatcher, bt as createSpinEffect, c as Sidebar, cn as useRedraw, cr as particlesPatcher, ct as createFadeEffect, d as NineSlice, dn as useState, dr as applyEmitterConfig, dt as createFlipOutEffect, en as useBackgroundGraphics, er as textPatcher, et as Graphics, f as Joystick, fn as useTheme, fr as getFirstEmitter, ft as createFloatEffect, g as Image, gn as getRenderContext, gr as buildDeathZonesFromLayout, gt as createPulseEffect, h as useIconPreload, hn as withHooks, hr as resolveParticlePreset, ht as createPressEffect, i as TabPanel, in as useLayoutEffect, ir as DevPresets, it as applyEffectByName, j as useSpring, jn as oceanBluePreset, jt as getThemedProps, k as Accordion, kn as getPresetWithMode, kt as viewPatcher, l as RefOriginView, ln as useRef, lr as applyDeathZone, lt as createFlashEffect, m as createIconComponent, mn as useWorldLayoutRect, mr as PARTICLE_PRESET_REGISTRY, mt as createNoneEffect, n as Toggle, nn as useEffect, nr as DebugLogger, nt as DEFAULT_EFFECT, o as RangeSlider, on as useLayoutSize, or as spritePatcher, ot as createBounceEffect, p as Icon, pn as useViewportSize, pr as isParticleEmitter, pt as createJelloEffect, q as registerBuiltins, qn as HexColor, qt as getCurrent, r as Tab, rn as useForceRedraw, rr as DevConfig, rt as EFFECT_REGISTRY, s as Slider, sn as useMemo, sr as particlesCreator, st as createBreatheEffect, t as TransformOriginView, tn as useCallback, tr as viewportRegistry, tt as Button, u as NineSliceButton, un as useScene, ur as applyEmitZone, ut as createFlipInEffect, v as ScrollView, vn as defaultTheme, vr as buildEmitZoneFromLayout, vt as createSlideInEffect, w as AlertDialog, wn as applyDarkMode, wr as graphicsPatcher, wt as createWobbleEffect, x as Divider, xn as defaultSizeTokens, xr as imageCreator, xt as createSwingEffect, y as ScrollSlider, yn as createTextStyleTokens, yr as nineSliceCreator, yt as createSlideOutEffect, z as KeyboardInputManager, zn as hex, zt as patchVNode } from "./custom-C_w8D39m.js";
2
+ import { Fragment, jsx, jsxs } from "./jsx-runtime.js";
3
+ import { applyStencilClip, clearStencilClip, getStencilClipHandle, installStencilClipExtension, isBitmapStencilClipSource, uninstallStencilClipExtension } from "./clip/index.js";
3
4
  import * as Phaser from "phaser";
4
- import { jsx } from "./jsx-runtime.js";
5
- import { Fragment, jsxs } from "./jsx-runtime.js";
6
- import { computed } from "@preact/signals-core";
7
- function shallowEqual(a2, b10) {
8
- if (!a2 || !b10) return a2 === b10;
9
- if (a2.length !== b10.length) return false;
10
- return a2.every((val, i2) => val === b10[i2]);
11
- }
12
- function applyGraphicsProps(node, _prev, next) {
13
- const prevDeps = node.__drawDependencies;
14
- const nextDeps = next.dependencies;
15
- const depsChanged = !shallowEqual(prevDeps, nextDeps);
16
- if (depsChanged && next.onDraw) {
17
- if (next.autoClear !== false) {
18
- node.clear();
19
- }
20
- next.onDraw(node, next);
21
- node.__drawDependencies = nextDeps;
22
- }
23
- }
24
- function applyGraphicsLayout(node, prev, next) {
25
- node.__layoutProps = next;
26
- if (prev.width !== next.width || prev.height !== next.height || prev.headless !== next.headless) {
27
- node.__getLayoutSize = () => {
28
- if (next.headless ?? true) {
29
- return { width: 0.01, height: 0.01 };
30
- }
31
- return {
32
- width: typeof next.width === "number" ? next.width : 0,
33
- height: typeof next.height === "number" ? next.height : 0
34
- };
35
- };
36
- }
37
- }
38
- function normalizeVisible$1(visible) {
39
- if (visible === void 0) return true;
40
- if (typeof visible === "boolean") return visible;
41
- if (visible === "visible") return true;
42
- if (visible === "invisible" || visible === "none") return false;
43
- return true;
44
- }
45
- function applyPhaserProps(node, prev, next) {
46
- if (prev.alpha !== next.alpha && typeof next.alpha === "number") {
47
- node.setAlpha?.(next.alpha);
48
- }
49
- if (prev.depth !== next.depth && typeof next.depth === "number") {
50
- node.setDepth?.(next.depth);
51
- }
52
- if (prev.visible !== next.visible) {
53
- const visibleValue = normalizeVisible$1(next.visible);
54
- node.visible = visibleValue;
55
- }
56
- }
57
- function applyTransformProps(node, prev, next) {
58
- if (prev.x !== next.x && typeof next.x === "number") {
59
- node.x = next.x;
60
- }
61
- if (prev.y !== next.y && typeof next.y === "number") {
62
- node.y = next.y;
63
- }
64
- if (prev.rotation !== next.rotation && typeof next.rotation === "number") {
65
- node.rotation = next.rotation;
66
- }
67
- const nextScale = next.scale;
68
- const nextScaleX = next.scaleX;
69
- const nextScaleY = next.scaleY;
70
- const prevScale = prev.scale;
71
- const prevScaleX = prev.scaleX;
72
- const prevScaleY = prev.scaleY;
73
- if (nextScale !== void 0 && nextScale !== prevScale) {
74
- node.setScale?.(nextScale, nextScale);
75
- } else if (nextScaleX !== prevScaleX || nextScaleY !== prevScaleY) {
76
- const currentScaleX = node.scaleX ?? 1;
77
- const currentScaleY = node.scaleY ?? 1;
78
- const sx = nextScaleX ?? currentScaleX;
79
- const sy = nextScaleY ?? currentScaleY;
80
- node.setScale?.(sx, sy);
81
- }
82
- }
83
- function createGraphicsLayout(graphics, props) {
84
- if (props.headless === false) {
85
- if (typeof props.width !== "number" || typeof props.height !== "number") {
86
- throw new Error(
87
- "Graphics component requires explicit width and height props when headless=false"
88
- );
89
- }
90
- }
91
- graphics.__layoutProps = props;
92
- graphics.__getLayoutSize = () => {
93
- if (graphics.__layoutProps?.headless ?? true) {
94
- return { width: 0.01, height: 0.01 };
95
- }
96
- return {
97
- width: props.width ?? 0,
98
- height: props.height ?? 0
99
- };
100
- };
101
- graphics.__drawDependencies = props.dependencies;
102
- }
103
- function normalizeVisible(visible) {
104
- if (visible === void 0) return true;
105
- if (typeof visible === "boolean") return visible;
106
- if (visible === "visible") return true;
107
- if (visible === "invisible" || visible === "none") return false;
108
- return true;
109
- }
110
- function createPhaser(node, props) {
111
- if (props.visible !== void 0) {
112
- node.visible = normalizeVisible(props.visible);
113
- }
114
- if (props.depth !== void 0) {
115
- node.setDepth(props.depth);
116
- }
117
- if (props.alpha !== void 0) {
118
- node.setAlpha(props.alpha);
119
- }
120
- }
121
- function createTransform(node, props) {
122
- if (props.scaleX !== void 0 || props.scaleY !== void 0) {
123
- node.setScale(
124
- props.scaleX ?? 1,
125
- props.scaleY ?? 1
126
- );
127
- }
128
- if (props.rotation !== void 0) {
129
- node.setRotation(props.rotation);
130
- }
131
- }
132
- const graphicsCreator = (scene, props) => {
133
- const graphics = scene.add.graphics();
134
- graphics.setPosition(props.x ?? 0, props.y ?? 0);
135
- createTransform(graphics, props);
136
- createPhaser(graphics, props);
137
- createGraphicsLayout(graphics, props);
138
- if (props.onDraw) {
139
- props.onDraw(graphics, props);
140
- }
141
- return graphics;
5
+ //#region src/camera/use-camera-fx.ts
6
+ /**
7
+ * Hook and creators for Phaser camera effects
8
+ */
9
+ var createCameraShakeFX = (camera, config) => {
10
+ const duration = config.duration ?? 250;
11
+ const intensity = typeof config.intensity === "object" ? new Phaser.Math.Vector2(config.intensity.x, config.intensity.y) : config.intensity ?? .01;
12
+ camera.shake(duration, intensity, config.force ?? false, config.onComplete);
142
13
  };
143
- const graphicsPatcher = (node, prev, next) => {
144
- applyTransformProps(node, prev, next);
145
- applyPhaserProps(node, prev, next);
146
- applyGraphicsProps(node, prev, next);
147
- applyGraphicsLayout(node, prev, next);
14
+ var createCameraFlashFX = (camera, config) => {
15
+ const duration = config.duration ?? 200;
16
+ const red = config.red ?? 255;
17
+ const green = config.green ?? 255;
18
+ const blue = config.blue ?? 255;
19
+ camera.flash(duration, red, green, blue, config.force ?? false, config.onComplete);
148
20
  };
149
- function calculateFitScale$1(image, targetWidth, targetHeight, fit = "fill") {
150
- const textureWidth = image.width;
151
- const textureHeight = image.height;
152
- if (textureWidth === 0 || textureHeight === 0) {
153
- return { scaleX: 1, scaleY: 1 };
154
- }
155
- if (fit === "fill") {
156
- return {
157
- scaleX: targetWidth / textureWidth,
158
- scaleY: targetHeight / textureHeight
159
- };
160
- }
161
- const targetAspect = targetWidth / targetHeight;
162
- const textureAspect = textureWidth / textureHeight;
163
- if (fit === "contain") {
164
- const scale = targetAspect > textureAspect ? targetHeight / textureHeight : targetWidth / textureWidth;
165
- return { scaleX: scale, scaleY: scale };
166
- }
167
- if (fit === "cover") {
168
- const scale = targetAspect < textureAspect ? targetHeight / textureHeight : targetWidth / textureWidth;
169
- return { scaleX: scale, scaleY: scale };
170
- }
171
- return { scaleX: 1, scaleY: 1 };
172
- }
173
- function applyImageProps(image, prev, next) {
174
- const textureChanged = prev.texture !== next.texture || prev.frame !== next.frame;
175
- if (textureChanged && next.texture) {
176
- image.setTexture(next.texture, next.frame);
177
- }
178
- if (prev.tint !== next.tint) {
179
- if (typeof next.tint === "number") {
180
- image.setTint(next.tint);
181
- } else {
182
- image.clearTint();
183
- }
184
- }
185
- if (prev.originX !== next.originX || prev.originY !== next.originY) {
186
- const originX = next.originX ?? image.originX;
187
- const originY = next.originY ?? image.originY;
188
- image.setOrigin(originX, originY);
189
- }
190
- const displayWidthChanged = prev.displayWidth !== next.displayWidth;
191
- const displayHeightChanged = prev.displayHeight !== next.displayHeight;
192
- const fitChanged = prev.fit !== next.fit;
193
- if (displayWidthChanged || displayHeightChanged || fitChanged || textureChanged) {
194
- if (typeof next.displayWidth === "number" && typeof next.displayHeight === "number") {
195
- const { scaleX, scaleY } = calculateFitScale$1(
196
- image,
197
- next.displayWidth,
198
- next.displayHeight,
199
- next.fit
200
- );
201
- image.setScale(scaleX, scaleY);
202
- } else if (typeof next.displayWidth === "number") {
203
- const scale = next.displayWidth / image.width;
204
- image.setScale(scale);
205
- } else if (typeof next.displayHeight === "number") {
206
- const scale = next.displayHeight / image.height;
207
- image.setScale(scale, scale);
208
- }
209
- }
210
- }
211
- function applyImageLayout(image, prev, next) {
212
- image.__layoutProps = next;
213
- if (prev.headless !== next.headless) {
214
- image.__getLayoutSize = () => {
215
- if (image.__layoutProps?.headless) {
216
- return { width: 0.01, height: 0.01 };
217
- }
218
- return {
219
- width: image.displayWidth,
220
- height: image.displayHeight
221
- };
222
- };
223
- }
224
- }
225
- function createImageLayout(image, props) {
226
- image.__layoutProps = props;
227
- image.__getLayoutSize = () => {
228
- if (image.__layoutProps?.headless) {
229
- return { width: 0.01, height: 0.01 };
230
- }
231
- return {
232
- width: image.displayWidth,
233
- height: image.displayHeight
234
- };
235
- };
236
- }
237
- const imageCreator = (scene, props) => {
238
- const image = scene.add.image(props.x ?? 0, props.y ?? 0, props.texture, props.frame);
239
- if (props.headless) {
240
- image.setOrigin(0.5, 0.5);
241
- } else {
242
- image.setOrigin(0, 0);
243
- }
244
- if (props.originX !== void 0 || props.originY !== void 0) {
245
- image.setOrigin(props.originX ?? image.originX, props.originY ?? image.originY);
246
- }
247
- const normalizedProps = { ...props };
248
- if (props.headless) {
249
- delete normalizedProps.padding;
250
- delete normalizedProps.margin;
251
- delete normalizedProps.gap;
252
- } else {
253
- if (normalizedProps.rotation !== void 0) {
254
- delete normalizedProps.rotation;
255
- }
256
- }
257
- createTransform(image, normalizedProps);
258
- createPhaser(image, normalizedProps);
259
- if (props.tint !== void 0) {
260
- image.setTint(props.tint);
261
- }
262
- if (props.displayWidth !== void 0 || props.displayHeight !== void 0) {
263
- if (props.displayWidth !== void 0 && props.displayHeight !== void 0) {
264
- const fit = props.fit ?? "fill";
265
- const textureWidth = image.width;
266
- const textureHeight = image.height;
267
- if (textureWidth > 0 && textureHeight > 0) {
268
- if (fit === "fill") {
269
- image.setDisplaySize(props.displayWidth, props.displayHeight);
270
- } else if (fit === "contain") {
271
- const targetAspect = props.displayWidth / props.displayHeight;
272
- const textureAspect = textureWidth / textureHeight;
273
- const scale = targetAspect > textureAspect ? props.displayHeight / textureHeight : props.displayWidth / textureWidth;
274
- image.setScale(scale);
275
- } else if (fit === "cover") {
276
- const targetAspect = props.displayWidth / props.displayHeight;
277
- const textureAspect = textureWidth / textureHeight;
278
- const scale = targetAspect < textureAspect ? props.displayHeight / textureHeight : props.displayWidth / textureWidth;
279
- image.setScale(scale);
280
- }
281
- }
282
- } else if (props.displayWidth !== void 0) {
283
- const scale = props.displayWidth / image.width;
284
- image.setScale(scale);
285
- } else if (props.displayHeight !== void 0) {
286
- const scale = props.displayHeight / image.height;
287
- image.setScale(scale);
288
- }
289
- }
290
- createImageLayout(image, normalizedProps);
291
- if (props.onReady) {
292
- props.onReady(image);
293
- }
294
- return image;
21
+ var createCameraFadeInFX = (camera, config) => {
22
+ const duration = config.duration ?? 300;
23
+ const red = config.red ?? 0;
24
+ const green = config.green ?? 0;
25
+ const blue = config.blue ?? 0;
26
+ camera.fadeIn(duration, red, green, blue, config.onComplete);
295
27
  };
296
- const imagePatcher = (node, prev, next) => {
297
- if (prev.headless !== next.headless) {
298
- if (next.headless) {
299
- node.setOrigin(0.5, 0.5);
300
- } else {
301
- node.setOrigin(0, 0);
302
- }
303
- }
304
- const normalizedPrev = { ...prev };
305
- const normalizedNext = { ...next };
306
- if (next.headless) {
307
- delete normalizedNext.padding;
308
- delete normalizedNext.margin;
309
- delete normalizedNext.gap;
310
- } else {
311
- if (normalizedNext.rotation !== void 0) {
312
- delete normalizedNext.rotation;
313
- }
314
- }
315
- if (prev.headless) {
316
- delete normalizedPrev.padding;
317
- delete normalizedPrev.margin;
318
- delete normalizedPrev.gap;
319
- } else {
320
- if (normalizedPrev.rotation !== void 0) {
321
- delete normalizedPrev.rotation;
322
- }
323
- }
324
- applyTransformProps(node, normalizedPrev, normalizedNext);
325
- applyPhaserProps(node, normalizedPrev, normalizedNext);
326
- applyImageProps(node, normalizedPrev, normalizedNext);
327
- applyImageLayout(node, normalizedPrev, normalizedNext);
28
+ var createCameraFadeOutFX = (camera, config) => {
29
+ const duration = config.duration ?? 300;
30
+ const red = config.red ?? 0;
31
+ const green = config.green ?? 0;
32
+ const blue = config.blue ?? 0;
33
+ camera.fadeOut(duration, red, green, blue, config.onComplete);
328
34
  };
329
- function applyNineSliceProps(nineSlice, prev, next) {
330
- const textureChanged = prev.texture !== next.texture || prev.frame !== next.frame;
331
- if (textureChanged && next.texture) {
332
- nineSlice.setTexture(next.texture, next.frame);
333
- }
334
- const sliceChanged = prev.leftWidth !== next.leftWidth || prev.rightWidth !== next.rightWidth || prev.topHeight !== next.topHeight || prev.bottomHeight !== next.bottomHeight;
335
- if (sliceChanged) {
336
- const width = typeof next.width === "number" ? next.width : nineSlice.width;
337
- const height = typeof next.height === "number" ? next.height : nineSlice.height;
338
- nineSlice.setSlices(
339
- width,
340
- height,
341
- next.leftWidth ?? prev.leftWidth ?? 0,
342
- next.rightWidth ?? prev.rightWidth ?? 0,
343
- next.topHeight ?? prev.topHeight,
344
- next.bottomHeight ?? prev.bottomHeight
345
- );
346
- }
347
- const prevWidth = typeof prev.width === "number" ? prev.width : nineSlice.width;
348
- const nextWidth = typeof next.width === "number" ? next.width : nineSlice.width;
349
- const prevHeight = typeof prev.height === "number" ? prev.height : nineSlice.height;
350
- const nextHeight = typeof next.height === "number" ? next.height : nineSlice.height;
351
- if (prevWidth !== nextWidth || prevHeight !== nextHeight) {
352
- nineSlice.setSize(nextWidth, nextHeight);
353
- }
354
- if (prev.tint !== next.tint) {
355
- if (next.tint !== void 0) {
356
- nineSlice.setTint(next.tint);
357
- } else {
358
- nineSlice.clearTint();
359
- }
360
- }
361
- }
362
- function applyNineSliceLayout(nineSlice, prev, next) {
363
- nineSlice.__layoutProps = next;
364
- if (prev.width !== next.width || prev.height !== next.height) {
365
- nineSlice.__getLayoutSize = () => {
366
- return {
367
- width: nineSlice.width,
368
- height: nineSlice.height
369
- };
370
- };
371
- }
372
- }
373
- function createNineSliceLayout(nineSlice, props) {
374
- nineSlice.__layoutProps = props;
375
- nineSlice.__getLayoutSize = () => {
376
- return {
377
- width: nineSlice.width,
378
- height: nineSlice.height
379
- };
380
- };
381
- }
382
- const nineSliceCreator = (scene, props) => {
383
- const initialWidth = typeof props.width === "number" ? props.width : 64;
384
- const initialHeight = typeof props.height === "number" ? props.height : 64;
385
- const nineSlice = scene.add.nineslice(
386
- props.x ?? 0,
387
- props.y ?? 0,
388
- props.texture,
389
- props.frame,
390
- initialWidth,
391
- initialHeight,
392
- props.leftWidth,
393
- props.rightWidth,
394
- props.topHeight,
395
- props.bottomHeight
396
- );
397
- nineSlice.setOrigin(0, 0);
398
- if (props.tint !== void 0) {
399
- nineSlice.setTint(props.tint);
400
- }
401
- createTransform(nineSlice, props);
402
- createPhaser(nineSlice, props);
403
- createNineSliceLayout(nineSlice, props);
404
- return nineSlice;
405
- };
406
- const nineSlicePatcher = (node, prev, next) => {
407
- applyTransformProps(node, prev, next);
408
- applyPhaserProps(node, prev, next);
409
- applyNineSliceProps(node, prev, next);
410
- applyNineSliceLayout(node, prev, next);
411
- };
412
- function resolveNumericSize(value) {
413
- return typeof value === "number" ? value : void 0;
414
- }
415
- function resolveZoneSize(zone, fallback) {
416
- const size = {};
417
- const width = zone.width ?? fallback.width;
418
- const height = zone.height ?? fallback.height;
419
- if (width !== void 0) size.width = width;
420
- if (height !== void 0) size.height = height;
421
- return size;
422
- }
423
- function buildZoneSource(zone, fallbackSize) {
424
- const baseX = zone.x ?? 0;
425
- const baseY = zone.y ?? 0;
426
- const { width, height } = resolveZoneSize(zone, fallbackSize);
427
- switch (zone.shape) {
428
- case "rect":
429
- return new Phaser.Geom.Rectangle(baseX, baseY, width ?? 1, height ?? 1);
430
- case "circle":
431
- return new Phaser.Geom.Circle(baseX, baseY, zone.radius ?? 1);
432
- case "ellipse":
433
- return new Phaser.Geom.Ellipse(baseX, baseY, width ?? 1, height ?? 1);
434
- case "line":
435
- return new Phaser.Geom.Line(
436
- baseX,
437
- baseY,
438
- zone.endX ?? baseX + (width ?? 1),
439
- zone.endY ?? baseY + (height ?? 1)
440
- );
441
- default:
442
- return void 0;
443
- }
444
- }
445
- function buildEmitZone(zone, fallbackSize = {}) {
446
- const type = zone.type ?? "random";
447
- const source = buildZoneSource(zone, fallbackSize);
448
- if (!source) return void 0;
449
- return { type, source };
450
- }
451
- function buildEmitZoneFromLayout(zone, width, height) {
452
- const fallback = {};
453
- const resolvedWidth = resolveNumericSize(width);
454
- const resolvedHeight = resolveNumericSize(height);
455
- if (resolvedWidth !== void 0) fallback.width = resolvedWidth;
456
- if (resolvedHeight !== void 0) fallback.height = resolvedHeight;
457
- return buildEmitZone(zone, fallback);
458
- }
459
- function buildDeathZone(zone, fallbackSize = {}) {
460
- const type = zone.mode ?? "onEnter";
461
- const source = buildZoneSource(zone, fallbackSize);
462
- if (!source) return void 0;
463
- return { type, source };
464
- }
465
- function buildDeathZonesFromLayout(zones, width, height) {
466
- if (!zones) return void 0;
467
- const list = Array.isArray(zones) ? zones : [zones];
468
- const fallback = {};
469
- const resolvedWidth = resolveNumericSize(width);
470
- const resolvedHeight = resolveNumericSize(height);
471
- if (resolvedWidth !== void 0) fallback.width = resolvedWidth;
472
- if (resolvedHeight !== void 0) fallback.height = resolvedHeight;
473
- const deathZones = list.map((zone) => buildDeathZone(zone, fallback)).filter((zone) => Boolean(zone));
474
- return deathZones.length > 0 ? deathZones : void 0;
475
- }
476
- const PARTICLE_PRESET_REGISTRY = {
477
- explosion: {
478
- speed: { min: 120, max: 320 },
479
- scale: { start: 1, end: 0 },
480
- alpha: { start: 1, end: 0 },
481
- lifespan: 600,
482
- quantity: 24,
483
- blendMode: "ADD"
484
- },
485
- trail: {
486
- speed: { min: 30, max: 80 },
487
- scale: { start: 0.6, end: 0 },
488
- alpha: { start: 0.8, end: 0 },
489
- lifespan: 700,
490
- frequency: 40
491
- },
492
- rain: {
493
- speed: { min: 320, max: 520 },
494
- angle: { min: 80, max: 100 },
495
- scale: { start: 0.5, end: 0.2 },
496
- alpha: { start: 0.6, end: 0.1 },
497
- lifespan: 1e3,
498
- frequency: 20
499
- },
500
- snow: {
501
- speed: { min: 40, max: 120 },
502
- angle: { min: 80, max: 100 },
503
- gravityY: 8,
504
- scale: { start: 0.6, end: 0.6 },
505
- alpha: { start: 0.8, end: 0.3 },
506
- lifespan: 2600,
507
- frequency: 80
508
- },
509
- sparkle: {
510
- speed: { min: 20, max: 60 },
511
- scale: { start: 0.4, end: 0 },
512
- alpha: { start: 1, end: 0 },
513
- lifespan: 500,
514
- frequency: 60,
515
- blendMode: "ADD"
516
- }
517
- };
518
- function resolveParticlePreset(preset, config = {}) {
519
- const base = preset ? PARTICLE_PRESET_REGISTRY[preset] : void 0;
520
- if (preset && !base) {
521
- console.warn(`[Particles] Preset "${String(preset)}" not found in registry`);
522
- }
523
- return {
524
- ...base ?? {},
525
- ...config
526
- };
527
- }
528
- function isParticleEmitter(target) {
529
- return !!target && typeof target.explode === "function";
530
- }
531
- function getFirstEmitter(manager) {
532
- if (!manager) return null;
533
- if (manager.__emitter) return manager.__emitter;
534
- const emitters = manager.emitters;
535
- if (Array.isArray(emitters)) {
536
- return emitters[0] ?? null;
537
- }
538
- if (emitters && "list" in emitters && Array.isArray(emitters.list)) {
539
- return emitters.list[0] ?? null;
540
- }
541
- return null;
542
- }
543
- function applyEmitterConfig(emitter, config) {
544
- if (!emitter) return;
545
- const withConfig = emitter;
546
- if (withConfig.setConfig) {
547
- withConfig.setConfig(config);
548
- return;
549
- }
550
- if (withConfig.fromJSON) {
551
- withConfig.fromJSON(config);
552
- return;
553
- }
554
- Object.assign(emitter, config);
555
- }
556
- function applyEmitZone(emitter, emitZone) {
557
- if (!emitter || !emitZone) return;
558
- const withZone = emitter;
559
- if (withZone.setEmitZone) {
560
- withZone.setEmitZone(emitZone);
561
- return;
562
- }
563
- }
564
- function applyDeathZone(emitter, deathZone) {
565
- if (!emitter) return;
566
- const withZone = emitter;
567
- const isDefined = (value) => value !== null && value !== void 0;
568
- const normalized = Array.isArray(deathZone) ? deathZone.filter(isDefined) : deathZone ? [deathZone] : [];
569
- const hasZone = normalized.length > 0;
570
- if (hasZone && withZone.setDeathZone) {
571
- withZone.setDeathZone(normalized);
572
- return;
573
- }
574
- if (!hasZone) {
575
- if (withZone.clearDeathZones) {
576
- withZone.clearDeathZones();
577
- return;
578
- }
579
- if (withZone.deathZones) {
580
- withZone.deathZones = [];
581
- }
582
- return;
583
- }
584
- if (withZone.clearDeathZones) {
585
- withZone.clearDeathZones();
586
- }
587
- if (withZone.addDeathZone) {
588
- withZone.addDeathZone(normalized);
589
- return;
590
- }
591
- if (withZone.deathZones) {
592
- withZone.deathZones = normalized;
593
- }
594
- }
595
- function mergeDeathZones(base, extra) {
596
- const baseList = Array.isArray(base) ? base : base ? [base] : [];
597
- const extraList = extra ?? [];
598
- const merged = [...baseList, ...extraList];
599
- return merged.length > 0 ? merged : void 0;
600
- }
601
- function applyParticlesProps(manager, prev, next) {
602
- if (!manager) return;
603
- const textureChanged = prev.texture !== next.texture || prev.frame !== next.frame;
604
- if (textureChanged && next.texture && "setTexture" in manager && manager.setTexture) {
605
- manager.setTexture(next.texture, next.frame);
606
- }
607
- const configChanged = prev.preset !== next.preset || prev.config !== next.config || prev.zone !== next.zone || prev.excludeZones !== next.excludeZones || prev.width !== next.width || prev.height !== next.height;
608
- if (configChanged) {
609
- const resolvedConfig = resolveParticlePreset(next.preset, next.config);
610
- let emitter = null;
611
- if (isParticleEmitter(manager)) {
612
- emitter = manager;
613
- } else {
614
- const managerLike = manager;
615
- emitter = managerLike.__emitter ?? getFirstEmitter(managerLike);
616
- if (!emitter && managerLike.createEmitter) {
617
- const created = managerLike.createEmitter(resolvedConfig);
618
- emitter = created ?? null;
619
- }
620
- managerLike.__emitter = emitter;
621
- }
622
- if (!emitter) return;
623
- applyEmitterConfig(emitter, resolvedConfig);
624
- if (next.zone) {
625
- const emitZone = buildEmitZoneFromLayout(next.zone, next.width, next.height);
626
- if (emitZone) {
627
- applyEmitZone(emitter, emitZone);
628
- }
629
- }
630
- const deathZones = buildDeathZonesFromLayout(next.excludeZones, next.width, next.height);
631
- const combined = mergeDeathZones(
632
- resolvedConfig.deathZone,
633
- deathZones
634
- );
635
- applyDeathZone(emitter, combined);
636
- }
637
- }
638
- function applyParticlesLayout(particles, _prev, next) {
639
- particles.__layoutProps = next;
640
- }
641
- function createParticlesLayout(particles, props) {
642
- particles.__layoutProps = props;
643
- particles.__getLayoutSize = () => ({ width: 0.01, height: 0.01 });
644
- }
645
- const particlesCreator = (scene, props) => {
646
- const resolvedConfig = {
647
- ...resolveParticlePreset(props.preset, props.config)
648
- };
649
- if (props.zone) {
650
- const emitZone = buildEmitZoneFromLayout(props.zone, props.width, props.height);
651
- if (emitZone) {
652
- const configWithZone = resolvedConfig;
653
- configWithZone.emitZone = emitZone;
654
- }
655
- }
656
- const particles = scene.add.particles(
657
- props.x ?? 0,
658
- props.y ?? 0,
659
- props.texture,
660
- resolvedConfig
661
- );
662
- if (props.frame !== void 0) {
663
- const configWithFrame = resolvedConfig;
664
- configWithFrame.frame = props.frame;
665
- if ("setTexture" in particles && particles.setTexture) {
666
- particles.setTexture(props.texture, props.frame);
667
- }
668
- }
669
- const emitter = isParticleEmitter(particles) ? particles : getFirstEmitter(particles);
670
- if (!isParticleEmitter(particles)) {
671
- particles.__emitter = emitter;
672
- }
673
- if ("setOrigin" in particles && typeof particles.setOrigin === "function") {
674
- particles.setOrigin(0, 0);
675
- }
676
- createTransform(particles, props);
677
- createPhaser(particles, props);
678
- createParticlesLayout(particles, props);
679
- if (props.excludeZones && emitter) {
680
- const deathZones = buildDeathZonesFromLayout(props.excludeZones, props.width, props.height);
681
- const combinedDeathZones = mergeDeathZones(
682
- resolvedConfig.deathZone,
683
- deathZones
684
- );
685
- if (combinedDeathZones) {
686
- applyDeathZone(emitter, combinedDeathZones);
687
- }
688
- }
689
- if (props.onReady) {
690
- props.onReady(particles);
691
- }
692
- return particles;
693
- };
694
- const particlesPatcher = (node, prev, next) => {
695
- applyTransformProps(node, prev, next);
696
- applyPhaserProps(node, prev, next);
697
- applyParticlesProps(node, prev, next);
698
- applyParticlesLayout(node, prev, next);
699
- };
700
- function getOriginalTextureDimensions(sprite) {
701
- const frame = sprite.frame;
702
- return {
703
- width: frame.width,
704
- height: frame.height
705
- };
706
- }
707
- function calculateFitScale(sprite, targetWidth, targetHeight, fit = "fill") {
708
- const { width: textureWidth, height: textureHeight } = getOriginalTextureDimensions(sprite);
709
- if (textureWidth === 0 || textureHeight === 0) {
710
- return { scaleX: 1, scaleY: 1 };
711
- }
712
- if (fit === "fill") {
713
- return {
714
- scaleX: targetWidth / textureWidth,
715
- scaleY: targetHeight / textureHeight
716
- };
717
- }
718
- const targetAspect = targetWidth / targetHeight;
719
- const textureAspect = textureWidth / textureHeight;
720
- if (fit === "contain") {
721
- const scale = targetAspect > textureAspect ? targetHeight / textureHeight : targetWidth / textureWidth;
722
- return { scaleX: scale, scaleY: scale };
723
- }
724
- if (fit === "cover") {
725
- const scale = targetAspect < textureAspect ? targetHeight / textureHeight : targetWidth / textureWidth;
726
- return { scaleX: scale, scaleY: scale };
727
- }
728
- return { scaleX: 1, scaleY: 1 };
729
- }
730
- function applySpriteProps(sprite, prev, next) {
731
- const textureChanged = prev.texture !== next.texture || prev.frame !== next.frame;
732
- if (textureChanged && next.texture) {
733
- sprite.setTexture(next.texture, next.frame);
734
- }
735
- if (prev.tint !== next.tint) {
736
- if (typeof next.tint === "number") {
737
- sprite.setTint(next.tint);
738
- } else {
739
- sprite.clearTint();
740
- }
741
- }
742
- if (prev.originX !== next.originX || prev.originY !== next.originY) {
743
- const originX = next.originX ?? sprite.originX;
744
- const originY = next.originY ?? sprite.originY;
745
- sprite.setOrigin(originX, originY);
746
- }
747
- const displayWidthChanged = prev.displayWidth !== next.displayWidth;
748
- const displayHeightChanged = prev.displayHeight !== next.displayHeight;
749
- const fitChanged = prev.fit !== next.fit;
750
- if (displayWidthChanged || displayHeightChanged || fitChanged || textureChanged) {
751
- if (typeof next.displayWidth === "number" && typeof next.displayHeight === "number") {
752
- const fit = next.fit ?? "fill";
753
- if (fit === "fill") {
754
- sprite.setDisplaySize(next.displayWidth, next.displayHeight);
755
- } else {
756
- const { scaleX, scaleY } = calculateFitScale(
757
- sprite,
758
- next.displayWidth,
759
- next.displayHeight,
760
- fit
761
- );
762
- sprite.setScale(scaleX, scaleY);
763
- }
764
- } else if (typeof next.displayWidth === "number") {
765
- const { width: origWidth } = getOriginalTextureDimensions(sprite);
766
- const scale = next.displayWidth / origWidth;
767
- sprite.setScale(scale);
768
- } else if (typeof next.displayHeight === "number") {
769
- const { height: origHeight } = getOriginalTextureDimensions(sprite);
770
- const scale = next.displayHeight / origHeight;
771
- sprite.setScale(scale, scale);
772
- } else {
773
- sprite.setScale(1);
774
- }
775
- }
776
- const animationChanged = prev.animationKey !== next.animationKey || prev.loop !== next.loop || prev.repeatDelay !== next.repeatDelay;
777
- if (animationChanged) {
778
- if (sprite.anims.isPlaying) {
779
- sprite.anims.stop();
780
- }
781
- if (next.animationKey) {
782
- sprite.anims.play({
783
- key: next.animationKey,
784
- repeat: next.loop ? -1 : 0,
785
- repeatDelay: next.repeatDelay ?? 0
786
- });
787
- }
788
- }
789
- const callbacksChanged = prev.onAnimationStart !== next.onAnimationStart || prev.onAnimationComplete !== next.onAnimationComplete || prev.onAnimationRepeat !== next.onAnimationRepeat || prev.onAnimationUpdate !== next.onAnimationUpdate;
790
- if (callbacksChanged) {
791
- sprite.off("animationstart");
792
- sprite.off("animationcomplete");
793
- sprite.off("animationrepeat");
794
- sprite.off("animationupdate");
795
- if (next.onAnimationStart) {
796
- sprite.on("animationstart", (anim) => {
797
- next.onAnimationStart?.(anim.key);
798
- });
799
- }
800
- if (next.onAnimationComplete) {
801
- sprite.on("animationcomplete", (anim) => {
802
- next.onAnimationComplete?.(anim.key);
803
- });
804
- }
805
- if (next.onAnimationRepeat) {
806
- sprite.on("animationrepeat", (anim) => {
807
- next.onAnimationRepeat?.(anim.key);
808
- });
809
- }
810
- if (next.onAnimationUpdate) {
811
- sprite.on(
812
- "animationupdate",
813
- (anim, frame) => {
814
- next.onAnimationUpdate?.(anim.key, frame);
815
- }
816
- );
817
- }
818
- }
819
- }
820
- function applySpriteLayout(sprite, _prev, next) {
821
- sprite.__layoutProps = next;
822
- }
823
- function createSpriteLayout(sprite, props) {
824
- sprite.__layoutProps = props;
825
- sprite.__getLayoutSize = () => {
826
- return { width: 0.01, height: 0.01 };
827
- };
828
- }
829
- const spriteCreator = (scene, props) => {
830
- const sprite = scene.add.sprite(props.x ?? 0, props.y ?? 0, props.texture, props.frame);
831
- sprite.setOrigin(0.5, 0.5);
832
- if (props.originX !== void 0 || props.originY !== void 0) {
833
- sprite.setOrigin(props.originX ?? sprite.originX, props.originY ?? sprite.originY);
834
- }
835
- createTransform(sprite, props);
836
- createPhaser(sprite, props);
837
- if (props.tint !== void 0) {
838
- sprite.setTint(props.tint);
839
- }
840
- if (props.displayWidth !== void 0 || props.displayHeight !== void 0) {
841
- if (props.displayWidth !== void 0 && props.displayHeight !== void 0) {
842
- const fit = props.fit ?? "fill";
843
- const textureWidth = sprite.width;
844
- const textureHeight = sprite.height;
845
- if (textureWidth > 0 && textureHeight > 0) {
846
- if (fit === "fill") {
847
- sprite.setDisplaySize(props.displayWidth, props.displayHeight);
848
- } else if (fit === "contain") {
849
- const targetAspect = props.displayWidth / props.displayHeight;
850
- const textureAspect = textureWidth / textureHeight;
851
- const scale = targetAspect > textureAspect ? props.displayHeight / textureHeight : props.displayWidth / textureWidth;
852
- sprite.setScale(scale);
853
- } else if (fit === "cover") {
854
- const targetAspect = props.displayWidth / props.displayHeight;
855
- const textureAspect = textureWidth / textureHeight;
856
- const scale = targetAspect < textureAspect ? props.displayHeight / textureHeight : props.displayWidth / textureWidth;
857
- sprite.setScale(scale);
858
- }
859
- }
860
- } else if (props.displayWidth !== void 0) {
861
- const scale = props.displayWidth / sprite.width;
862
- sprite.setScale(scale);
863
- } else if (props.displayHeight !== void 0) {
864
- const scale = props.displayHeight / sprite.height;
865
- sprite.setScale(scale);
866
- }
867
- }
868
- if (props.animationKey) {
869
- if (sprite.scene && sprite.scene.anims.exists(props.animationKey)) {
870
- sprite.anims.play({
871
- key: props.animationKey,
872
- repeat: props.loop ? -1 : 0,
873
- repeatDelay: props.repeatDelay ?? 0
874
- });
875
- }
876
- }
877
- if (props.onAnimationStart) {
878
- sprite.on("animationstart", (anim) => {
879
- props.onAnimationStart?.(anim.key);
880
- });
881
- }
882
- if (props.onAnimationComplete) {
883
- sprite.on("animationcomplete", (anim) => {
884
- props.onAnimationComplete?.(anim.key);
885
- });
886
- }
887
- if (props.onAnimationRepeat) {
888
- sprite.on("animationrepeat", (anim) => {
889
- props.onAnimationRepeat?.(anim.key);
890
- });
891
- }
892
- if (props.onAnimationUpdate) {
893
- sprite.on(
894
- "animationupdate",
895
- (anim, frame) => {
896
- props.onAnimationUpdate?.(anim.key, frame);
897
- }
898
- );
899
- }
900
- createSpriteLayout(sprite, props);
901
- if (props.onReady) {
902
- props.onReady(sprite);
903
- }
904
- return sprite;
905
- };
906
- const spritePatcher = (node, prev, next) => {
907
- applyTransformProps(node, prev, next);
908
- applyPhaserProps(node, prev, next);
909
- applySpriteProps(node, prev, next);
910
- applySpriteLayout(node, prev, next);
911
- };
912
- function applyTextProps(node, prev, next) {
913
- if (node.active === false || node.scene && !node.scene.sys.game) {
914
- return;
915
- }
916
- let needsUpdate = false;
917
- if (prev.text !== next.text && typeof next.text === "string") {
918
- node.setText(next.text);
919
- needsUpdate = true;
920
- }
921
- if (next.style !== void 0 && !equal(next.style, prev.style || {})) {
922
- try {
923
- node.setStyle(next.style);
924
- needsUpdate = true;
925
- } catch (error) {
926
- console.warn("Failed to apply text style (scene may be transitioning):", error);
927
- }
928
- }
929
- if (next.maxWidth !== prev.maxWidth && next.maxWidth !== void 0) {
930
- const viewport = viewportRegistry.getViewport();
931
- const parsedMaxWidth = parseSize(next.maxWidth);
932
- const resolvedMaxWidth = resolveSize(parsedMaxWidth, viewport?.width, void 0, void 0);
933
- node.setWordWrapWidth(resolvedMaxWidth, true);
934
- needsUpdate = true;
935
- }
936
- if (prev.style !== next.style && next.style !== void 0) {
937
- try {
938
- node.setStyle(next.style);
939
- needsUpdate = true;
940
- } catch (error) {
941
- console.warn("Failed to apply text style (scene may be transitioning):", error);
942
- }
943
- }
944
- if (needsUpdate && node.updateText) {
945
- node.updateText();
946
- }
947
- }
948
- function applyTextLayout(text, _prev, next) {
949
- text.__layoutProps = next;
950
- text.__getLayoutSize = () => {
951
- if (text.__layoutProps?.headless) {
952
- return { width: 0.01, height: 0.01 };
953
- }
954
- return {
955
- width: text.width,
956
- height: text.height
957
- };
958
- };
959
- }
960
- function createTextLayout(text, props) {
961
- text.__layoutProps = props;
962
- text.__getLayoutSize = () => {
963
- if (text.__layoutProps?.headless) {
964
- return { width: 0.01, height: 0.01 };
965
- }
966
- return {
967
- width: text.width,
968
- height: text.height
969
- };
970
- };
971
- }
972
- const textCreator = (scene, props) => {
973
- const text = scene.add.text(props.x ?? 0, props.y ?? 0, props.text ?? "", props.style);
974
- if (props.headless) {
975
- text.setOrigin(0.5, 0.5);
976
- } else {
977
- text.setOrigin(0, 0);
978
- }
979
- const normalizedProps = { ...props };
980
- if (props.headless) {
981
- delete normalizedProps.padding;
982
- delete normalizedProps.margin;
983
- delete normalizedProps.gap;
984
- } else {
985
- if (normalizedProps.rotation !== void 0) {
986
- delete normalizedProps.rotation;
987
- }
988
- }
989
- createTransform(text, normalizedProps);
990
- createPhaser(text, normalizedProps);
991
- createTextLayout(text, normalizedProps);
992
- return text;
993
- };
994
- const textPatcher = (node, prev, next) => {
995
- if (prev.headless !== next.headless) {
996
- if (next.headless) {
997
- node.setOrigin(0.5, 0.5);
998
- } else {
999
- node.setOrigin(0, 0);
1000
- }
1001
- }
1002
- const normalizedPrev = { ...prev };
1003
- const normalizedNext = { ...next };
1004
- if (next.headless) {
1005
- delete normalizedNext.padding;
1006
- delete normalizedNext.margin;
1007
- delete normalizedNext.gap;
1008
- } else {
1009
- if (normalizedNext.rotation !== void 0) {
1010
- delete normalizedNext.rotation;
1011
- }
1012
- }
1013
- if (prev.headless) {
1014
- delete normalizedPrev.padding;
1015
- delete normalizedPrev.margin;
1016
- delete normalizedPrev.gap;
1017
- } else {
1018
- if (normalizedPrev.rotation !== void 0) {
1019
- delete normalizedPrev.rotation;
1020
- }
1021
- }
1022
- applyTransformProps(node, normalizedPrev, normalizedNext);
1023
- applyPhaserProps(node, normalizedPrev, normalizedNext);
1024
- applyTextProps(node, normalizedPrev, normalizedNext);
1025
- applyTextLayout(node, normalizedPrev, normalizedNext);
1026
- };
1027
- const tileSpriteCreator = (_scene, _props) => {
1028
- throw new Error(
1029
- "TileSprite component not implemented yet. This is a placeholder for architecture planning."
1030
- );
1031
- };
1032
- const tileSpritePatcher = (_node, _prev, _next) => {
1033
- throw new Error(
1034
- "TileSprite component not implemented yet. This is a placeholder for architecture planning."
1035
- );
1036
- };
1037
- function applyBackgroundProps(container, prev, next) {
1038
- const prevBgColor = prev.backgroundColor;
1039
- const nextBgColor = next.backgroundColor;
1040
- const prevBgAlpha = prev.backgroundAlpha ?? 1;
1041
- const nextBgAlpha = next.backgroundAlpha ?? 1;
1042
- let prevWidth = typeof prev.width === "number" ? prev.width : 100;
1043
- let prevHeight = typeof prev.height === "number" ? prev.height : 100;
1044
- let nextWidth = typeof next.width === "number" ? next.width : 100;
1045
- let nextHeight = typeof next.height === "number" ? next.height : 100;
1046
- if (container.__getLayoutSize) {
1047
- const layoutSize = container.__getLayoutSize();
1048
- prevWidth = layoutSize.width;
1049
- prevHeight = layoutSize.height;
1050
- nextWidth = layoutSize.width;
1051
- nextHeight = layoutSize.height;
1052
- }
1053
- const prevCornerRadius = prev.cornerRadius ?? 0;
1054
- const nextCornerRadius = next.cornerRadius ?? 0;
1055
- const prevBorderColor = prev.borderColor;
1056
- const nextBorderColor = next.borderColor;
1057
- const prevBorderWidth = prev.borderWidth ?? 0;
1058
- const nextBorderWidth = next.borderWidth ?? 0;
1059
- const prevBorderAlpha = prev.borderAlpha ?? 1;
1060
- const nextBorderAlpha = next.borderAlpha ?? 1;
1061
- const prevHasBorder = prevBorderWidth > 0 && prevBorderColor !== void 0;
1062
- const nextHasBorder = nextBorderWidth > 0 && nextBorderColor !== void 0;
1063
- const prevHasGraphics = prevBgColor !== void 0 || prevHasBorder;
1064
- const nextHasGraphics = nextBgColor !== void 0 || nextHasBorder;
1065
- if (prevHasGraphics && !nextHasGraphics) {
1066
- if (container.__background) {
1067
- container.__background.destroy();
1068
- delete container.__background;
1069
- }
1070
- } else if (!prevHasGraphics && nextHasGraphics) {
1071
- if (container.scene) {
1072
- const background = container.scene.add.graphics();
1073
- if (nextBgColor !== void 0) {
1074
- background.fillStyle(nextBgColor, nextBgAlpha);
1075
- }
1076
- if (nextHasBorder) {
1077
- background.lineStyle(nextBorderWidth, nextBorderColor, nextBorderAlpha);
1078
- }
1079
- if (nextCornerRadius !== 0) {
1080
- if (nextBgColor !== void 0) {
1081
- background.fillRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
1082
- }
1083
- if (nextHasBorder) {
1084
- background.strokeRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
1085
- }
1086
- } else {
1087
- if (nextBgColor !== void 0) {
1088
- background.fillRect(0, 0, nextWidth, nextHeight);
1089
- }
1090
- if (nextHasBorder) {
1091
- background.strokeRect(0, 0, nextWidth, nextHeight);
1092
- }
1093
- }
1094
- container.addAt(background, 0);
1095
- container.__background = background;
1096
- background.__isBackground = true;
1097
- }
1098
- } else if (container.__background && nextHasGraphics) {
1099
- const needsRedraw = prevBgColor !== nextBgColor || prevBgAlpha !== nextBgAlpha || prevWidth !== nextWidth || prevHeight !== nextHeight || prevCornerRadius !== nextCornerRadius || prevBorderWidth !== nextBorderWidth || prevBorderColor !== nextBorderColor || prevBorderAlpha !== nextBorderAlpha;
1100
- if (needsRedraw) {
1101
- container.__background.clear();
1102
- if (nextBgColor !== void 0) {
1103
- container.__background.fillStyle(nextBgColor, nextBgAlpha);
1104
- }
1105
- if (nextHasBorder) {
1106
- container.__background.lineStyle(nextBorderWidth, nextBorderColor, nextBorderAlpha);
1107
- }
1108
- if (nextCornerRadius !== 0) {
1109
- if (nextBgColor !== void 0) {
1110
- container.__background.fillRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
1111
- }
1112
- if (nextHasBorder) {
1113
- container.__background.strokeRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
1114
- }
1115
- } else {
1116
- if (nextBgColor !== void 0) {
1117
- container.__background.fillRect(0, 0, nextWidth, nextHeight);
1118
- }
1119
- if (nextHasBorder) {
1120
- container.__background.strokeRect(0, 0, nextWidth, nextHeight);
1121
- }
1122
- }
1123
- }
1124
- }
1125
- }
1126
- function applyGesturesProps(scene, container, prev, next) {
1127
- if (!scene || !scene.sys || !scene.data) {
1128
- console.warn("applyGesturesProps: Invalid scene or scene not initialized");
1129
- return;
1130
- }
1131
- if (scene.sys.game === null) {
1132
- return;
1133
- }
1134
- const hasAnyGesture = !!(next.onTouch || next.onTouchOutside || next.onTouchMove || next.onDoubleTap || next.onLongPress || next.onHoverStart || next.onHoverEnd || next.onWheel);
1135
- const hadAnyGesture = !!(prev.onTouch || prev.onTouchOutside || prev.onTouchMove || prev.onDoubleTap || prev.onLongPress || prev.onHoverStart || prev.onHoverEnd || prev.onWheel);
1136
- const prevEnabled = hadAnyGesture && prev.enableGestures !== false;
1137
- const nextEnabled = hasAnyGesture && next.enableGestures !== false;
1138
- const manager = getGestureManager(scene);
1139
- if (!prevEnabled && nextEnabled && hasAnyGesture) {
1140
- const containerWithLayout = container;
1141
- let width = 100;
1142
- let height = 100;
1143
- if (containerWithLayout.__getLayoutSize) {
1144
- const size = containerWithLayout.__getLayoutSize();
1145
- width = size.width;
1146
- height = size.height;
1147
- } else {
1148
- const bounds = container.getBounds();
1149
- width = bounds.width || 100;
1150
- height = bounds.height || 100;
1151
- }
1152
- const hitArea = new Phaser.Geom.Rectangle(0, 0, width, height);
1153
- const callbacks = {};
1154
- if (next.onTouch) callbacks.onTouch = next.onTouch;
1155
- if (next.onTouchOutside) callbacks.onTouchOutside = next.onTouchOutside;
1156
- if (next.onTouchMove) callbacks.onTouchMove = next.onTouchMove;
1157
- if (next.onDoubleTap) callbacks.onDoubleTap = next.onDoubleTap;
1158
- if (next.onLongPress) callbacks.onLongPress = next.onLongPress;
1159
- if (next.onHoverStart) callbacks.onHoverStart = next.onHoverStart;
1160
- if (next.onHoverEnd) callbacks.onHoverEnd = next.onHoverEnd;
1161
- if (next.onWheel) callbacks.onWheel = next.onWheel;
1162
- const config = {};
1163
- if (next.longPressDuration !== void 0) config.longPressDuration = next.longPressDuration;
1164
- if (next.doubleTapDelay !== void 0) config.doubleTapDelay = next.doubleTapDelay;
1165
- manager.registerContainer(container, callbacks, hitArea, config);
1166
- return;
1167
- }
1168
- if (prevEnabled && (!nextEnabled || !hasAnyGesture)) {
1169
- manager.unregisterContainer(container);
1170
- return;
1171
- }
1172
- if (nextEnabled && hasAnyGesture) {
1173
- const callbacksChanged = prev.onTouch !== next.onTouch || prev.onTouchOutside !== next.onTouchOutside || prev.onTouchMove !== next.onTouchMove || prev.onDoubleTap !== next.onDoubleTap || prev.onLongPress !== next.onLongPress || prev.onHoverStart !== next.onHoverStart || prev.onHoverEnd !== next.onHoverEnd || prev.onWheel !== next.onWheel;
1174
- if (callbacksChanged) {
1175
- const callbacks = {};
1176
- if (next.onTouch) callbacks.onTouch = next.onTouch;
1177
- if (next.onTouchOutside) callbacks.onTouchOutside = next.onTouchOutside;
1178
- if (next.onTouchMove) callbacks.onTouchMove = next.onTouchMove;
1179
- if (next.onDoubleTap) callbacks.onDoubleTap = next.onDoubleTap;
1180
- if (next.onLongPress) callbacks.onLongPress = next.onLongPress;
1181
- if (next.onHoverStart) callbacks.onHoverStart = next.onHoverStart;
1182
- if (next.onHoverEnd) callbacks.onHoverEnd = next.onHoverEnd;
1183
- if (next.onWheel) callbacks.onWheel = next.onWheel;
1184
- manager.updateCallbacks(container, callbacks);
1185
- }
1186
- const containerWithLayout = container;
1187
- let width = 100;
1188
- let height = 100;
1189
- if (containerWithLayout.__getLayoutSize) {
1190
- const size = containerWithLayout.__getLayoutSize();
1191
- width = size.width;
1192
- height = size.height;
1193
- } else {
1194
- const bounds = container.getBounds();
1195
- width = bounds.width || 100;
1196
- height = bounds.height || 100;
1197
- }
1198
- const hitArea = new Phaser.Geom.Rectangle(0, 0, width, height);
1199
- manager.updateHitArea(container, hitArea);
1200
- }
1201
- }
1202
- function updateGestureHitAreaIfNeeded(node) {
1203
- const containerWithLayout = node;
1204
- if (!containerWithLayout.__getLayoutSize) return;
1205
- try {
1206
- const manager = getGestureManager(containerWithLayout.scene);
1207
- const size = containerWithLayout.__getLayoutSize();
1208
- const hitArea = new Phaser.Geom.Rectangle(0, 0, size.width, size.height);
1209
- manager.updateHitArea(node, hitArea);
1210
- } catch {
1211
- }
1212
- }
1213
- const LAYOUT_RELEVANT_PROPS = [
1214
- "width",
1215
- "height",
1216
- "minWidth",
1217
- "maxWidth",
1218
- "minHeight",
1219
- "maxHeight",
1220
- "flex",
1221
- "margin",
1222
- "padding",
1223
- "gap",
1224
- "direction",
1225
- "justifyContent",
1226
- "alignItems",
1227
- "overflow"
1228
- ];
1229
- const DEEP_COMPARE_PROPS = /* @__PURE__ */ new Set(["margin", "padding"]);
1230
- function hasLayoutPropsChanged(prev, next) {
1231
- for (const prop of LAYOUT_RELEVANT_PROPS) {
1232
- const oldVal = prev[prop];
1233
- const newVal = next[prop];
1234
- if (DEEP_COMPARE_PROPS.has(prop)) {
1235
- if (!equal(oldVal, newVal)) {
1236
- return true;
1237
- }
1238
- } else {
1239
- if (oldVal !== newVal) {
1240
- return true;
1241
- }
1242
- }
1243
- }
1244
- return false;
1245
- }
1246
- function getParentLayoutContext(node) {
1247
- const parent = node.parentContainer;
1248
- if (parent && parent.__layoutProps && parent.__getLayoutSize) {
1249
- const parentSize = parent.__getLayoutSize();
1250
- const padding = parent.__layoutProps.padding ?? 0;
1251
- const normPadding = typeof padding === "number" ? { left: padding, right: padding, top: padding, bottom: padding } : {
1252
- left: padding.left ?? 0,
1253
- right: padding.right ?? 0,
1254
- top: padding.top ?? 0,
1255
- bottom: padding.bottom ?? 0
1256
- };
1257
- return {
1258
- parentSize: {
1259
- width: parentSize.width - normPadding.left - normPadding.right,
1260
- height: parentSize.height - normPadding.top - normPadding.bottom
1261
- }
1262
- // Parent already provides content-area, no padding offset needed
1263
- };
1264
- }
1265
- if (node.scene) {
1266
- return {
1267
- parentSize: {
1268
- width: node.scene.scale.width,
1269
- height: node.scene.scale.height
1270
- }
1271
- };
1272
- }
1273
- return {};
1274
- }
1275
- function applyLayoutProps(node, prev, next) {
1276
- node.__layoutProps = next;
1277
- if (hasLayoutPropsChanged(prev, next)) {
1278
- const { parentSize, parentPadding } = getParentLayoutContext(node);
1279
- calculateLayout(node, next, parentSize, parentPadding);
1280
- updateGestureHitAreaIfNeeded(node);
1281
- }
1282
- }
1283
- const tooltipStates = /* @__PURE__ */ new Map();
1284
- function calculateTooltipPosition(targetBounds, position, offset, tooltipWidth, tooltipHeight) {
1285
- const viewport = {
1286
- width: window.innerWidth,
1287
- height: window.innerHeight
1288
- };
1289
- let x = 0;
1290
- let y = 0;
1291
- switch (position) {
1292
- case "top":
1293
- x = targetBounds.centerX - tooltipWidth / 2;
1294
- y = targetBounds.top - tooltipHeight - offset;
1295
- break;
1296
- case "bottom":
1297
- x = targetBounds.centerX - tooltipWidth / 2;
1298
- y = targetBounds.bottom + offset;
1299
- break;
1300
- case "left":
1301
- x = targetBounds.left - tooltipWidth - offset;
1302
- y = targetBounds.centerY - tooltipHeight / 2;
1303
- break;
1304
- case "right":
1305
- x = targetBounds.right + offset;
1306
- y = targetBounds.centerY - tooltipHeight / 2;
1307
- break;
1308
- }
1309
- x = Math.max(8, Math.min(x, viewport.width - tooltipWidth - 8));
1310
- y = Math.max(8, Math.min(y, viewport.height - tooltipHeight - 8));
1311
- return { x, y };
1312
- }
1313
- function showTooltip(scene, container, config) {
1314
- const state = tooltipStates.get(container);
1315
- if (!state || state.isVisible) return;
1316
- state.isVisible = true;
1317
- state.currentConfig = config;
1318
- const theme = themeRegistry.getGlobalTheme();
1319
- const tooltipTheme = theme.Tooltip || {};
1320
- const position = config.position ?? tooltipTheme.position ?? "top";
1321
- const offset = config.offset ?? tooltipTheme.offset ?? 8;
1322
- const content = config.content;
1323
- const targetBounds = container.getBounds();
1324
- const textStyle = tooltipTheme.textStyle ?? {
1325
- fontSize: "14px",
1326
- fontFamily: "Arial",
1327
- color: "#ffffff",
1328
- padding: { x: 8, y: 4 }
1329
- };
1330
- const { backgroundColor: bgColor, ...styleWithoutBg } = textStyle;
1331
- const text = scene.add.text(0, 0, content, styleWithoutBg);
1332
- text.setOrigin(0.5);
1333
- const padding = textStyle.padding ?? { x: 8, y: 4 };
1334
- const paddingX = typeof padding === "number" ? padding : padding.x ?? 8;
1335
- const paddingY = typeof padding === "number" ? padding : padding.y ?? 4;
1336
- const textWidth = text.width;
1337
- const textHeight = text.height;
1338
- const bgWidth = textWidth + paddingX * 2;
1339
- const bgHeight = textHeight + paddingY * 2;
1340
- const cornerRadius = tooltipTheme.cornerRadius ?? 6;
1341
- const graphics = scene.add.graphics();
1342
- const bg2 = bgColor ?? "#000000dd";
1343
- let fillColor = 0;
1344
- let fillAlpha = 0.87;
1345
- if (typeof bg2 === "string") {
1346
- if (bg2.startsWith("#")) {
1347
- const hex = bg2.slice(1);
1348
- if (hex.length === 8) {
1349
- fillColor = parseInt(hex.slice(0, 6), 16);
1350
- fillAlpha = parseInt(hex.slice(6, 8), 16) / 255;
1351
- } else if (hex.length === 6) {
1352
- fillColor = parseInt(hex, 16);
1353
- fillAlpha = 1;
1354
- }
1355
- }
1356
- }
1357
- graphics.fillStyle(fillColor, fillAlpha);
1358
- graphics.fillRoundedRect(-bgWidth / 2, -bgHeight / 2, bgWidth, bgHeight, cornerRadius);
1359
- const tooltipContainer = scene.add.container(0, 0, [graphics, text]);
1360
- tooltipContainer.setDepth(1e4);
1361
- const textBounds = tooltipContainer.getBounds();
1362
- const pos = calculateTooltipPosition(
1363
- targetBounds,
1364
- position,
1365
- offset,
1366
- textBounds.width,
1367
- textBounds.height
1368
- );
1369
- const themeAnim = tooltipTheme.animation || {};
1370
- const anim = config.animation || {};
1371
- const fadeInDuration = anim.fadeIn ?? themeAnim.fadeIn ?? 200;
1372
- const moveOffset = {
1373
- dx: anim.move?.dx ?? themeAnim.move?.dx ?? 0,
1374
- dy: anim.move?.dy ?? themeAnim.move?.dy ?? 0
1375
- };
1376
- const pulse = anim.pulse ?? themeAnim.pulse ?? false;
1377
- const pulseScale = anim.pulseScale ?? [0.75, 1.25];
1378
- tooltipContainer.setPosition(
1379
- pos.x + textBounds.width / 2 - moveOffset.dx,
1380
- pos.y + textBounds.height / 2 - moveOffset.dy
1381
- );
1382
- tooltipContainer.setAlpha(0);
1383
- state.tooltip = tooltipContainer;
1384
- const fadeTween = scene.tweens.add({
1385
- targets: tooltipContainer,
1386
- alpha: 1,
1387
- x: pos.x + textBounds.width / 2,
1388
- y: pos.y + textBounds.height / 2,
1389
- duration: fadeInDuration,
1390
- ease: "Cubic.Out"
1391
- });
1392
- state.activeTweens.push(fadeTween);
1393
- if (pulse) {
1394
- const pulseTween = scene.tweens.add({
1395
- targets: tooltipContainer,
1396
- scale: { from: pulseScale[0], to: pulseScale[1] },
1397
- duration: 600,
1398
- yoyo: true,
1399
- repeat: -1,
1400
- ease: "Sine.InOut"
1401
- });
1402
- state.activeTweens.push(pulseTween);
1403
- }
1404
- if (config.autoDismiss && config.autoDismiss > 0) {
1405
- state.autoDismissTimer = setTimeout(() => {
1406
- hideTooltip(container);
1407
- }, config.autoDismiss);
1408
- }
1409
- }
1410
- function hideTooltip(container) {
1411
- const state = tooltipStates.get(container);
1412
- if (!state || !state.isVisible) return;
1413
- state.isVisible = false;
1414
- const config = state.currentConfig;
1415
- state.currentConfig = null;
1416
- if (state.autoDismissTimer) {
1417
- clearTimeout(state.autoDismissTimer);
1418
- state.autoDismissTimer = null;
1419
- }
1420
- if (!state.tooltip) return;
1421
- const tooltip = state.tooltip;
1422
- const scene = tooltip.scene;
1423
- const theme = themeRegistry.getGlobalTheme();
1424
- const tooltipTheme = theme.Tooltip || {};
1425
- const themeAnim = tooltipTheme.animation || {};
1426
- const anim = config?.animation || {};
1427
- const fadeOutDuration = anim.fadeOut ?? themeAnim.fadeOut ?? 200;
1428
- state.activeTweens.forEach((tween) => tween.stop());
1429
- state.activeTweens = [];
1430
- scene.tweens.add({
1431
- targets: tooltip,
1432
- alpha: 0,
1433
- duration: fadeOutDuration,
1434
- ease: "Cubic.In",
1435
- onComplete: () => {
1436
- tooltip.destroy();
1437
- }
1438
- });
1439
- state.tooltip = null;
1440
- }
1441
- function applyTooltip(scene, container, nextCallback, existingOnHoverStart, existingOnHoverEnd) {
1442
- if (!tooltipStates.has(container)) {
1443
- tooltipStates.set(container, {
1444
- isVisible: false,
1445
- tooltip: null,
1446
- activeTweens: [],
1447
- showTimer: null,
1448
- hideTimer: null,
1449
- autoDismissTimer: null,
1450
- currentConfig: null
1451
- });
1452
- container.once("destroy", () => {
1453
- const state2 = tooltipStates.get(container);
1454
- if (state2) {
1455
- if (state2.showTimer) clearTimeout(state2.showTimer);
1456
- if (state2.hideTimer) clearTimeout(state2.hideTimer);
1457
- if (state2.autoDismissTimer) clearTimeout(state2.autoDismissTimer);
1458
- state2.activeTweens.forEach((tween) => tween.stop());
1459
- hideTooltip(container);
1460
- tooltipStates.delete(container);
1461
- }
1462
- });
1463
- }
1464
- const state = tooltipStates.get(container);
1465
- if (!state) {
1466
- throw new Error("applyTooltip: state not initialized");
1467
- }
1468
- const theme = themeRegistry.getGlobalTheme();
1469
- const tooltipTheme = theme.Tooltip || {};
1470
- const onHoverStart = (data) => {
1471
- if (existingOnHoverStart) existingOnHoverStart(data);
1472
- if (!nextCallback) return;
1473
- const result = nextCallback();
1474
- if (!result) return;
1475
- const config = typeof result === "string" ? { content: result } : result;
1476
- if (config.disabled) return;
1477
- if (state.hideTimer) {
1478
- clearTimeout(state.hideTimer);
1479
- state.hideTimer = null;
1480
- }
1481
- if (state.autoDismissTimer) {
1482
- clearTimeout(state.autoDismissTimer);
1483
- state.autoDismissTimer = null;
1484
- }
1485
- const showDelay = config.showDelay ?? tooltipTheme.showDelay ?? 500;
1486
- state.showTimer = setTimeout(() => {
1487
- showTooltip(scene, container, config);
1488
- }, showDelay);
1489
- };
1490
- const onHoverEnd = (data) => {
1491
- if (existingOnHoverEnd) existingOnHoverEnd(data);
1492
- if (state.showTimer) {
1493
- clearTimeout(state.showTimer);
1494
- state.showTimer = null;
1495
- }
1496
- const hideDelay = state.currentConfig?.hideDelay ?? tooltipTheme.hideDelay ?? 0;
1497
- if (hideDelay > 0) {
1498
- state.hideTimer = setTimeout(() => {
1499
- hideTooltip(container);
1500
- }, hideDelay);
1501
- } else {
1502
- hideTooltip(container);
1503
- }
1504
- };
1505
- return { onHoverStart, onHoverEnd };
1506
- }
1507
- function createBackground(scene, container, props) {
1508
- const hasBackground = props.backgroundColor !== void 0;
1509
- const hasBorder = props.borderColor !== void 0;
1510
- if (hasBackground || hasBorder) {
1511
- const width = typeof props.width === "number" ? props.width : 100;
1512
- const height = typeof props.height === "number" ? props.height : 100;
1513
- const bgColor = props.backgroundColor;
1514
- const bgAlpha = props.backgroundAlpha ?? 1;
1515
- const cornerRadius = props.cornerRadius ?? 0;
1516
- const borderColor = props.borderColor;
1517
- const borderWidth = props.borderWidth ?? 0;
1518
- const borderAlpha = props.borderAlpha ?? 1;
1519
- const background = scene.add.graphics();
1520
- if (bgColor !== void 0) {
1521
- background.fillStyle(bgColor, bgAlpha);
1522
- }
1523
- if (borderWidth > 0 && borderColor !== void 0) {
1524
- background.lineStyle(borderWidth, borderColor, borderAlpha);
1525
- }
1526
- if (cornerRadius !== 0) {
1527
- if (bgColor !== void 0) {
1528
- background.fillRoundedRect(0, 0, width, height, cornerRadius);
1529
- }
1530
- if (borderWidth > 0 && borderColor !== void 0) {
1531
- background.strokeRoundedRect(0, 0, width, height, cornerRadius);
1532
- }
1533
- } else {
1534
- if (bgColor !== void 0) {
1535
- background.fillRect(0, 0, width, height);
1536
- }
1537
- if (borderWidth > 0 && borderColor !== void 0) {
1538
- background.strokeRect(0, 0, width, height);
1539
- }
1540
- }
1541
- container.addAt(background, 0);
1542
- container.__background = background;
1543
- background.__isBackground = true;
1544
- }
1545
- }
1546
- function createGestures(scene, container, props) {
1547
- const hasAnyGesture = !!(props.onTouch || props.onTouchOutside || props.onTouchMove || props.onDoubleTap || props.onLongPress || props.onHoverStart || props.onHoverEnd || props.onWheel);
1548
- const shouldEnable = hasAnyGesture && props.enableGestures !== false;
1549
- if (!shouldEnable) {
1550
- return;
1551
- }
1552
- const manager = getGestureManager(scene);
1553
- const containerWithLayout = container;
1554
- let width = 100;
1555
- let height = 100;
1556
- if (containerWithLayout.__getLayoutSize) {
1557
- const size = containerWithLayout.__getLayoutSize();
1558
- width = size.width;
1559
- height = size.height;
1560
- } else {
1561
- const bounds = container.getBounds();
1562
- width = bounds.width || 100;
1563
- height = bounds.height || 100;
1564
- }
1565
- const hitArea = new Phaser.Geom.Rectangle(0, 0, width, height);
1566
- const callbacks = {};
1567
- if (props.onTouch) callbacks.onTouch = props.onTouch;
1568
- if (props.onTouchOutside) callbacks.onTouchOutside = props.onTouchOutside;
1569
- if (props.onTouchMove) callbacks.onTouchMove = props.onTouchMove;
1570
- if (props.onDoubleTap) callbacks.onDoubleTap = props.onDoubleTap;
1571
- if (props.onLongPress) callbacks.onLongPress = props.onLongPress;
1572
- if (props.onHoverStart) callbacks.onHoverStart = props.onHoverStart;
1573
- if (props.onHoverEnd) callbacks.onHoverEnd = props.onHoverEnd;
1574
- if (props.onWheel) callbacks.onWheel = props.onWheel;
1575
- const config = {};
1576
- if (props.longPressDuration !== void 0) config.longPressDuration = props.longPressDuration;
1577
- if (props.doubleTapDelay !== void 0) config.doubleTapDelay = props.doubleTapDelay;
1578
- if (props.maxTouchDuration !== void 0) config.maxTouchDuration = props.maxTouchDuration;
1579
- manager.registerContainer(container, callbacks, hitArea, config);
1580
- }
1581
- function createLayout(container, props) {
1582
- container.__layoutProps = props;
1583
- container.__getLayoutSize = () => {
1584
- const children = container.list;
1585
- const direction = props.direction ?? "column";
1586
- const paddingRaw = props.padding ?? {};
1587
- const padding = typeof paddingRaw === "number" ? { left: paddingRaw, top: paddingRaw, right: paddingRaw, bottom: paddingRaw } : paddingRaw;
1588
- const paddingLeft = padding.left ?? 0;
1589
- const paddingTop = padding.top ?? 0;
1590
- const paddingRight = padding.right ?? 0;
1591
- const paddingBottom = padding.bottom ?? 0;
1592
- const gapNormalized = normalizeGap(props.gap);
1593
- let maxWidth = 0;
1594
- let maxHeight = 0;
1595
- let totalMainSize = 0;
1596
- let childCount = 0;
1597
- for (const child of children) {
1598
- if (child.__isBackground) {
1599
- continue;
1600
- }
1601
- childCount++;
1602
- const marginRaw = child.__layoutProps?.margin ?? {};
1603
- const margin = typeof marginRaw === "number" ? { top: marginRaw, right: marginRaw, bottom: marginRaw, left: marginRaw } : marginRaw;
1604
- const marginTop = margin.top ?? 0;
1605
- const marginBottom = margin.bottom ?? 0;
1606
- const marginLeft = margin.left ?? 0;
1607
- const marginRight = margin.right ?? 0;
1608
- const childSize = getChildSize(child);
1609
- if (direction === "row") {
1610
- totalMainSize += marginLeft + childSize.width + marginRight;
1611
- const childTotalHeight = marginTop + childSize.height + marginBottom;
1612
- maxHeight = Math.max(maxHeight, childTotalHeight);
1613
- } else {
1614
- const childTotalWidth = marginLeft + childSize.width + marginRight;
1615
- maxWidth = Math.max(maxWidth, childTotalWidth);
1616
- totalMainSize += marginTop + childSize.height + marginBottom;
1617
- }
1618
- }
1619
- if (childCount > 1) {
1620
- const gapValue = direction === "row" ? gapNormalized.horizontal : gapNormalized.vertical;
1621
- totalMainSize += gapValue * (childCount - 1);
1622
- }
1623
- const defaultWidth = direction === "row" ? totalMainSize + paddingLeft + paddingRight : maxWidth + paddingLeft + paddingRight;
1624
- const defaultHeight = direction === "row" ? maxHeight + paddingTop + paddingBottom : totalMainSize + paddingTop + paddingBottom;
1625
- const parsedWidth = parseSize(props.width);
1626
- const finalWidth = resolveSize(parsedWidth, void 0, defaultWidth);
1627
- const parsedHeight = parseSize(props.height);
1628
- const finalHeight = resolveSize(parsedHeight, void 0, defaultHeight);
1629
- return {
1630
- width: finalWidth,
1631
- height: finalHeight
1632
- };
1633
- };
1634
- }
1635
- function normalizeBackgroundProps(props) {
1636
- const bgProps = props;
1637
- const hasBackground = bgProps.backgroundColor !== void 0;
1638
- const hasBorder = bgProps.borderColor !== void 0;
1639
- if (!hasBackground && !hasBorder) {
1640
- return props;
1641
- }
1642
- const normalized = { ...props };
1643
- if (hasBackground && bgProps.backgroundAlpha === void 0) {
1644
- normalized.backgroundAlpha = 1;
1645
- }
1646
- if (hasBorder) {
1647
- if (bgProps.borderWidth === void 0 || bgProps.borderWidth === 0) {
1648
- normalized.borderWidth = 1;
1649
- }
1650
- if (bgProps.borderAlpha === void 0 || bgProps.borderAlpha === 0) {
1651
- normalized.borderAlpha = 1;
1652
- }
1653
- }
1654
- return normalized;
1655
- }
1656
- const viewCreator = (scene, props) => {
1657
- if (props.backgroundColor !== void 0 || props.cornerRadius !== void 0) {
1658
- DebugLogger.log("theme", "View Creator - Props received:", {
1659
- backgroundColor: props.backgroundColor,
1660
- cornerRadius: props.cornerRadius,
1661
- width: props.width,
1662
- height: props.height
1663
- });
1664
- }
1665
- const normalizedProps = normalizeBackgroundProps(props);
1666
- const container = scene.add.container(normalizedProps.x ?? 0, normalizedProps.y ?? 0);
1667
- createTransform(container, normalizedProps);
1668
- createPhaser(container, normalizedProps);
1669
- createBackground(
1670
- scene,
1671
- container,
1672
- normalizedProps
1673
- );
1674
- createLayout(container, normalizedProps);
1675
- if (normalizedProps.onTooltip) {
1676
- const handlers = applyTooltip(
1677
- scene,
1678
- container,
1679
- normalizedProps.onTooltip,
1680
- normalizedProps.onHoverStart,
1681
- normalizedProps.onHoverEnd
1682
- );
1683
- normalizedProps.onHoverStart = handlers.onHoverStart;
1684
- normalizedProps.onHoverEnd = handlers.onHoverEnd;
1685
- }
1686
- createGestures(scene, container, normalizedProps);
1687
- DebugLogger.log(
1688
- "layout",
1689
- "View creator storing __layoutProps with padding:",
1690
- normalizedProps.padding
1691
- );
1692
- return container;
1693
- };
1694
- const viewPatcher = (node, prev, next) => {
1695
- const normalizedPrev = normalizeBackgroundProps(prev);
1696
- const normalizedNext = normalizeBackgroundProps(next);
1697
- applyTransformProps(node, normalizedPrev, normalizedNext);
1698
- applyPhaserProps(node, normalizedPrev, normalizedNext);
1699
- const container = node;
1700
- applyBackgroundProps(container, normalizedPrev, normalizedNext);
1701
- if (container.scene && container.scene.data) {
1702
- if (normalizedNext.onTooltip) {
1703
- const handlers = applyTooltip(
1704
- container.scene,
1705
- container,
1706
- normalizedNext.onTooltip,
1707
- normalizedNext.onHoverStart,
1708
- normalizedNext.onHoverEnd
1709
- );
1710
- normalizedNext.onHoverStart = handlers.onHoverStart;
1711
- normalizedNext.onHoverEnd = handlers.onHoverEnd;
1712
- }
1713
- }
1714
- if (container.scene && container.scene.data) {
1715
- applyGesturesProps(container.scene, container, normalizedPrev, normalizedNext);
1716
- }
1717
- applyLayoutProps(container, normalizedPrev, normalizedNext);
1718
- };
1719
- function Sprite(props) {
1720
- const localTheme = useTheme();
1721
- const { props: themed, nestedTheme } = getThemedProps("Sprite", localTheme, props);
1722
- return /* @__PURE__ */ jsx("sprite", { ...themed, theme: nestedTheme });
1723
- }
1724
- function TileSprite(props) {
1725
- const localTheme = useTheme();
1726
- const { props: themed, nestedTheme } = getThemedProps("TileSprite", localTheme, props);
1727
- return /* @__PURE__ */ jsx("tilesprite", { ...themed, theme: nestedTheme });
1728
- }
1729
- function registerBuiltins() {
1730
- register("view", { create: viewCreator, patch: viewPatcher });
1731
- register("text", { create: textCreator, patch: textPatcher });
1732
- register("nineslice", { create: nineSliceCreator, patch: nineSlicePatcher });
1733
- register("particles", { create: particlesCreator, patch: particlesPatcher });
1734
- register("sprite", { create: spriteCreator, patch: spritePatcher });
1735
- register("image", { create: imageCreator, patch: imagePatcher });
1736
- register("graphics", { create: graphicsCreator, patch: graphicsPatcher });
1737
- register("tilesprite", { create: tileSpriteCreator, patch: tileSpritePatcher });
1738
- register("View", { create: viewCreator, patch: viewPatcher });
1739
- register("Text", { create: textCreator, patch: textPatcher });
1740
- register("NineSlice", { create: nineSliceCreator, patch: nineSlicePatcher });
1741
- register("Particles", { create: particlesCreator, patch: particlesPatcher });
1742
- register("Sprite", { create: spriteCreator, patch: spritePatcher });
1743
- register("Image", { create: imageCreator, patch: imagePatcher });
1744
- register("Graphics", { create: graphicsCreator, patch: graphicsPatcher });
1745
- register("TileSprite", { create: tileSpriteCreator, patch: tileSpritePatcher });
1746
- }
1747
- const createCameraShakeFX = (camera, config) => {
1748
- const duration = config.duration ?? 250;
1749
- const intensity = typeof config.intensity === "object" ? new Phaser.Math.Vector2(config.intensity.x, config.intensity.y) : config.intensity ?? 0.01;
1750
- camera.shake(duration, intensity, config.force ?? false, config.onComplete);
1751
- };
1752
- const createCameraFlashFX = (camera, config) => {
1753
- const duration = config.duration ?? 200;
1754
- const red = config.red ?? 255;
1755
- const green = config.green ?? 255;
1756
- const blue = config.blue ?? 255;
1757
- camera.flash(duration, red, green, blue, config.force ?? false, config.onComplete);
1758
- };
1759
- const createCameraFadeInFX = (camera, config) => {
1760
- const duration = config.duration ?? 300;
1761
- const red = config.red ?? 0;
1762
- const green = config.green ?? 0;
1763
- const blue = config.blue ?? 0;
1764
- camera.fadeIn(duration, red, green, blue, config.onComplete);
1765
- };
1766
- const createCameraFadeOutFX = (camera, config) => {
1767
- const duration = config.duration ?? 300;
1768
- const red = config.red ?? 0;
1769
- const green = config.green ?? 0;
1770
- const blue = config.blue ?? 0;
1771
- camera.fadeOut(duration, red, green, blue, config.onComplete);
1772
- };
1773
- const createCameraZoomFX = (camera, config) => {
1774
- const duration = config.duration ?? 250;
1775
- const targetZoom = config.zoom ?? camera.zoom * 1.1;
1776
- camera.zoomTo(targetZoom, duration, config.ease, config.force ?? false, config.onComplete);
35
+ var createCameraZoomFX = (camera, config) => {
36
+ const duration = config.duration ?? 250;
37
+ const targetZoom = config.zoom ?? camera.zoom * 1.1;
38
+ camera.zoomTo(targetZoom, duration, config.ease, config.force ?? false, config.onComplete);
1777
39
  };
1778
40
  function useCameraFX(cameraRef, options = {}) {
1779
- const scene = useScene();
1780
- const activeFxRef = useRef(/* @__PURE__ */ new Set());
1781
- const baseZoomRef = useRef(null);
1782
- const getCamera = useCallback(() => {
1783
- return cameraRef?.current ?? scene.cameras.main;
1784
- }, [cameraRef, scene]);
1785
- const applyCameraFX = useCallback(
1786
- (fx, config) => {
1787
- const camera = getCamera();
1788
- if (!camera) {
1789
- console.warn("[useCameraFX] No camera available");
1790
- return;
1791
- }
1792
- if (baseZoomRef.current === null) {
1793
- baseZoomRef.current = camera.zoom;
1794
- }
1795
- const cleanup = fx(camera, config);
1796
- if (typeof cleanup === "function") {
1797
- activeFxRef.current.add(cleanup);
1798
- }
1799
- },
1800
- [getCamera]
1801
- );
1802
- const clearCameraFX = useCallback(() => {
1803
- const camera = getCamera();
1804
- activeFxRef.current.forEach((cleanup) => cleanup());
1805
- activeFxRef.current.clear();
1806
- if (!camera) return;
1807
- const cameraWithStops = camera;
1808
- if (typeof cameraWithStops.stopShake === "function") cameraWithStops.stopShake();
1809
- if (typeof cameraWithStops.stopFlash === "function") cameraWithStops.stopFlash();
1810
- if (typeof cameraWithStops.stopFade === "function") cameraWithStops.stopFade();
1811
- if (options.resetZoomOnClear !== false && baseZoomRef.current !== null) {
1812
- camera.setZoom(baseZoomRef.current);
1813
- }
1814
- }, [getCamera, options.resetZoomOnClear]);
1815
- useEffect(() => {
1816
- return () => clearCameraFX();
1817
- }, [clearCameraFX]);
1818
- return { applyCameraFX, clearCameraFX };
41
+ const scene = useScene();
42
+ const activeFxRef = useRef(/* @__PURE__ */ new Set());
43
+ const baseZoomRef = useRef(null);
44
+ const getCamera = useCallback(() => {
45
+ return cameraRef?.current ?? scene.cameras.main;
46
+ }, [cameraRef, scene]);
47
+ const applyCameraFX = useCallback((fx, config) => {
48
+ const camera = getCamera();
49
+ if (!camera) {
50
+ console.warn("[useCameraFX] No camera available");
51
+ return;
52
+ }
53
+ if (baseZoomRef.current === null) baseZoomRef.current = camera.zoom;
54
+ const cleanup = fx(camera, config);
55
+ if (typeof cleanup === "function") activeFxRef.current.add(cleanup);
56
+ }, [getCamera]);
57
+ const clearCameraFX = useCallback(() => {
58
+ const camera = getCamera();
59
+ activeFxRef.current.forEach((cleanup) => cleanup());
60
+ activeFxRef.current.clear();
61
+ if (!camera) return;
62
+ const cameraWithStops = camera;
63
+ if (typeof cameraWithStops.stopShake === "function") cameraWithStops.stopShake();
64
+ if (typeof cameraWithStops.stopFlash === "function") cameraWithStops.stopFlash();
65
+ if (typeof cameraWithStops.stopFade === "function") cameraWithStops.stopFade();
66
+ if (options.resetZoomOnClear !== false && baseZoomRef.current !== null) camera.setZoom(baseZoomRef.current);
67
+ }, [getCamera, options.resetZoomOnClear]);
68
+ useEffect(() => {
69
+ return () => clearCameraFX();
70
+ }, [clearCameraFX]);
71
+ return {
72
+ applyCameraFX,
73
+ clearCameraFX
74
+ };
1819
75
  }
1820
76
  function useScreenShake(config = {}, cameraRef) {
1821
- const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
1822
- useEffect(() => {
1823
- applyCameraFX(createCameraShakeFX, config);
1824
- return () => clearCameraFX();
1825
- }, [applyCameraFX, clearCameraFX, config]);
1826
- return { clearCameraFX };
77
+ const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
78
+ useEffect(() => {
79
+ applyCameraFX(createCameraShakeFX, config);
80
+ return () => clearCameraFX();
81
+ }, [
82
+ applyCameraFX,
83
+ clearCameraFX,
84
+ config
85
+ ]);
86
+ return { clearCameraFX };
1827
87
  }
1828
88
  function useCameraFlash(config = {}, cameraRef) {
1829
- const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
1830
- useEffect(() => {
1831
- applyCameraFX(createCameraFlashFX, config);
1832
- return () => clearCameraFX();
1833
- }, [applyCameraFX, clearCameraFX, config]);
1834
- return { clearCameraFX };
89
+ const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
90
+ useEffect(() => {
91
+ applyCameraFX(createCameraFlashFX, config);
92
+ return () => clearCameraFX();
93
+ }, [
94
+ applyCameraFX,
95
+ clearCameraFX,
96
+ config
97
+ ]);
98
+ return { clearCameraFX };
1835
99
  }
1836
100
  function useCameraFade(config = {}, cameraRef) {
1837
- const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
1838
- useEffect(() => {
1839
- const fx = config.direction === "in" ? createCameraFadeInFX : createCameraFadeOutFX;
1840
- applyCameraFX(fx, config);
1841
- return () => clearCameraFX();
1842
- }, [applyCameraFX, clearCameraFX, config]);
1843
- return { clearCameraFX };
101
+ const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
102
+ useEffect(() => {
103
+ applyCameraFX(config.direction === "in" ? createCameraFadeInFX : createCameraFadeOutFX, config);
104
+ return () => clearCameraFX();
105
+ }, [
106
+ applyCameraFX,
107
+ clearCameraFX,
108
+ config
109
+ ]);
110
+ return { clearCameraFX };
1844
111
  }
1845
112
  function useCameraZoom(config = {}, cameraRef) {
1846
- const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
1847
- useEffect(() => {
1848
- applyCameraFX(createCameraZoomFX, config);
1849
- return () => clearCameraFX();
1850
- }, [applyCameraFX, clearCameraFX, config]);
1851
- return { clearCameraFX };
1852
- }
1853
- const CAMERA_FX_REGISTRY = {
1854
- shake: createCameraShakeFX,
1855
- flash: createCameraFlashFX,
1856
- fadeIn: createCameraFadeInFX,
1857
- fadeOut: createCameraFadeOutFX,
1858
- zoom: createCameraZoomFX
113
+ const { applyCameraFX, clearCameraFX } = useCameraFX(cameraRef);
114
+ useEffect(() => {
115
+ applyCameraFX(createCameraZoomFX, config);
116
+ return () => clearCameraFX();
117
+ }, [
118
+ applyCameraFX,
119
+ clearCameraFX,
120
+ config
121
+ ]);
122
+ return { clearCameraFX };
123
+ }
124
+ //#endregion
125
+ //#region src/camera/camera-fx-registry.ts
126
+ /**
127
+ * Camera FX registry - string-based lookup with extension support
128
+ */
129
+ /**
130
+ * Camera FX registry mapping names to FX creators
131
+ */
132
+ var CAMERA_FX_REGISTRY = {
133
+ shake: createCameraShakeFX,
134
+ flash: createCameraFlashFX,
135
+ fadeIn: createCameraFadeInFX,
136
+ fadeOut: createCameraFadeOutFX,
137
+ zoom: createCameraZoomFX
1859
138
  };
1860
- const DEFAULT_CAMERA_FX = {
1861
- fx: "shake",
1862
- fxConfig: { duration: 200, force: false }
139
+ /**
140
+ * Default camera FX definition
141
+ */
142
+ var DEFAULT_CAMERA_FX = {
143
+ fx: "shake",
144
+ fxConfig: {
145
+ duration: 200,
146
+ force: false
147
+ }
1863
148
  };
149
+ /**
150
+ * Apply a camera FX by name
151
+ */
1864
152
  function applyCameraFXByName(applyCameraFX, fxName, fxConfig) {
1865
- const name = fxName ?? DEFAULT_CAMERA_FX.fx;
1866
- const config = fxConfig ?? DEFAULT_CAMERA_FX.fxConfig;
1867
- const fx = CAMERA_FX_REGISTRY[name];
1868
- if (fx) {
1869
- applyCameraFX(fx, config);
1870
- return true;
1871
- }
1872
- return false;
1873
- }
153
+ const name = fxName ?? DEFAULT_CAMERA_FX.fx;
154
+ const config = fxConfig ?? DEFAULT_CAMERA_FX.fxConfig;
155
+ const fx = CAMERA_FX_REGISTRY[name];
156
+ if (fx) {
157
+ applyCameraFX(fx, config);
158
+ return true;
159
+ }
160
+ return false;
161
+ }
162
+ /**
163
+ * Resolve camera FX definition with priority: props > default
164
+ */
1874
165
  function resolveCameraFX(props) {
1875
- return {
1876
- fx: props?.fx ?? DEFAULT_CAMERA_FX.fx,
1877
- fxConfig: props?.fxConfig ?? DEFAULT_CAMERA_FX.fxConfig
1878
- };
1879
- }
166
+ return {
167
+ fx: props?.fx ?? DEFAULT_CAMERA_FX.fx,
168
+ fxConfig: props?.fxConfig ?? DEFAULT_CAMERA_FX.fxConfig
169
+ };
170
+ }
171
+ //#endregion
172
+ //#region src/fx/use-fx.ts
173
+ /**
174
+ * Hook for applying Phaser PostFX/PreFX pipeline effects
175
+ * Manages FX lifecycle with proper cleanup
176
+ */
177
+ /**
178
+ * Hook for applying FX to GameObject
179
+ * @param ref - Ref to GameObject
180
+ * @returns Object with applyFX and clearFX methods
181
+ *
182
+ * @example
183
+ * ```tsx
184
+ * const ref = useRef(null)
185
+ * const { applyFX, clearFX } = useFX(ref)
186
+ *
187
+ * const handleClick = () => {
188
+ * applyFX(createShadowFX, { offsetX: 4, offsetY: 4, blur: 8 })
189
+ * }
190
+ *
191
+ * return <View ref={ref} onClick={handleClick}>Click me</View>
192
+ * ```
193
+ */
1880
194
  function useFX(ref) {
1881
- const activeEffectsRef = useRef(/* @__PURE__ */ new Set());
1882
- useEffect(() => {
1883
- return () => {
1884
- activeEffectsRef.current.forEach((cleanup) => {
1885
- if (typeof cleanup === "function") {
1886
- cleanup();
1887
- } else if (cleanup?.destroy) {
1888
- cleanup.destroy();
1889
- }
1890
- });
1891
- activeEffectsRef.current.clear();
1892
- };
1893
- }, []);
1894
- const applyFX = (fxCreator, config, type = "post") => {
1895
- const obj = ref.current;
1896
- if (!obj) {
1897
- console.warn("[useFX] No object found in ref");
1898
- return;
1899
- }
1900
- const cleanupOrController = fxCreator(obj, config, type);
1901
- if (cleanupOrController) {
1902
- activeEffectsRef.current.add(cleanupOrController);
1903
- }
1904
- };
1905
- const clearFX = () => {
1906
- activeEffectsRef.current.forEach((cleanup) => {
1907
- if (typeof cleanup === "function") {
1908
- cleanup();
1909
- } else if (cleanup?.destroy) {
1910
- cleanup.destroy();
1911
- }
1912
- });
1913
- activeEffectsRef.current.clear();
1914
- const obj = ref.current;
1915
- if (obj && "postFX" in obj && obj.postFX) {
1916
- obj.postFX.clear();
1917
- }
1918
- if (obj && "preFX" in obj && obj.preFX) {
1919
- obj.preFX.clear();
1920
- }
1921
- };
1922
- return { applyFX, clearFX };
1923
- }
1924
- const createBlurFX = (obj, config, type = "post") => {
1925
- const { quality = 0, x = 2, y = 2, strength = 1, color = 16777215, steps = 4 } = config;
1926
- const pipeline = type === "post" ? obj.postFX : obj.preFX;
1927
- if (!pipeline) {
1928
- console.warn("[createBlurFX] FX pipeline not available on this GameObject");
1929
- return null;
1930
- }
1931
- const blur = pipeline.addBlur(quality, x, y, strength, color, steps);
1932
- return blur;
195
+ const activeEffectsRef = useRef(/* @__PURE__ */ new Set());
196
+ useEffect(() => {
197
+ const obj = ref.current;
198
+ if (obj && typeof obj.enableFilters === "function") obj.enableFilters();
199
+ }, []);
200
+ useEffect(() => {
201
+ return () => {
202
+ activeEffectsRef.current.forEach((cleanup) => {
203
+ if (typeof cleanup === "function") cleanup();
204
+ else if (cleanup?.destroy) cleanup.destroy();
205
+ });
206
+ activeEffectsRef.current.clear();
207
+ };
208
+ }, []);
209
+ const applyFX = (fxCreator, config, type = "internal") => {
210
+ const obj = ref.current;
211
+ if (!obj) {
212
+ console.warn("[useFX] No object found in ref");
213
+ return;
214
+ }
215
+ const cleanupOrController = fxCreator(obj, config, type);
216
+ if (cleanupOrController) activeEffectsRef.current.add(cleanupOrController);
217
+ };
218
+ const clearFX = () => {
219
+ activeEffectsRef.current.forEach((cleanup) => {
220
+ if (typeof cleanup === "function") cleanup();
221
+ else if (cleanup?.destroy) cleanup.destroy();
222
+ });
223
+ activeEffectsRef.current.clear();
224
+ const obj = ref.current;
225
+ if (obj?.filters) {
226
+ obj.filters.internal.clear();
227
+ obj.filters.external.clear();
228
+ }
229
+ };
230
+ return {
231
+ applyFX,
232
+ clearFX
233
+ };
234
+ }
235
+ //#endregion
236
+ //#region src/fx/fx-creators/blur.ts
237
+ /**
238
+ * Create blur FX
239
+ * @param obj - GameObject
240
+ * @param config - Blur configuration
241
+ * @param type - 'post' or 'pre' FX
242
+ * @returns Blur controller
243
+ *
244
+ * @example
245
+ * ```tsx
246
+ * applyFX(createBlurFX, {
247
+ * quality: 1,
248
+ * x: 4,
249
+ * y: 4,
250
+ * strength: 2
251
+ * })
252
+ * ```
253
+ */
254
+ var createBlurFX = (obj, config, type = "internal") => {
255
+ const { quality = 0, x = 2, y = 2, strength = 1, color = 16777215, steps = 4 } = config;
256
+ obj.enableFilters();
257
+ const filters = obj.filters;
258
+ if (!filters) {
259
+ console.warn("[createBlurFX] Filters not available on this GameObject");
260
+ return null;
261
+ }
262
+ const pipeline = type === "internal" ? filters.internal : filters.external;
263
+ if (!pipeline) {
264
+ console.warn("[createBlurFX] FX pipeline not available on this GameObject");
265
+ return null;
266
+ }
267
+ return pipeline.addBlur(quality, x, y, strength, color, steps);
1933
268
  };
1934
- const createColorMatrixFX = (obj, config, type = "post") => {
1935
- const { effect = "grayscale", amount = 1 } = config;
1936
- const pipeline = type === "post" ? obj.postFX : obj.preFX;
1937
- if (!pipeline) {
1938
- console.warn("[createColorMatrixFX] FX pipeline not available on this GameObject");
1939
- return null;
1940
- }
1941
- const colorMatrix = pipeline.addColorMatrix();
1942
- switch (effect) {
1943
- case "grayscale":
1944
- colorMatrix.grayscale(amount);
1945
- break;
1946
- case "sepia":
1947
- colorMatrix.sepia();
1948
- break;
1949
- case "negative":
1950
- colorMatrix.negative();
1951
- break;
1952
- case "blackWhite":
1953
- colorMatrix.blackWhite();
1954
- break;
1955
- case "brown":
1956
- colorMatrix.brown();
1957
- break;
1958
- case "kodachrome":
1959
- colorMatrix.kodachrome();
1960
- break;
1961
- case "technicolor":
1962
- colorMatrix.technicolor();
1963
- break;
1964
- case "polaroid":
1965
- colorMatrix.polaroid();
1966
- break;
1967
- }
1968
- return colorMatrix;
269
+ //#endregion
270
+ //#region src/fx/fx-creators/color-matrix.ts
271
+ /**
272
+ * Create color matrix FX
273
+ * @param obj - GameObject
274
+ * @param config - Color matrix configuration
275
+ * @param type - 'post' or 'pre' FX
276
+ * @returns Color matrix controller
277
+ *
278
+ * @example
279
+ * ```tsx
280
+ * applyFX(createColorMatrixFX, {
281
+ * effect: 'grayscale',
282
+ * amount: 1
283
+ * })
284
+ * ```
285
+ */
286
+ var createColorMatrixFX = (obj, config, type = "internal") => {
287
+ const { effect = "grayscale", amount = 1 } = config;
288
+ obj.enableFilters();
289
+ const filters = obj.filters;
290
+ if (!filters) {
291
+ console.warn("[createColorMatrixFX] Filters not available on this GameObject");
292
+ return null;
293
+ }
294
+ const pipeline = type === "internal" ? filters.internal : filters.external;
295
+ if (!pipeline) {
296
+ console.warn("[createColorMatrixFX] FX pipeline not available on this GameObject");
297
+ return null;
298
+ }
299
+ const colorMatrixFilter = pipeline.addColorMatrix();
300
+ const cm = colorMatrixFilter.colorMatrix;
301
+ switch (effect) {
302
+ case "grayscale":
303
+ cm.grayscale(amount);
304
+ break;
305
+ case "sepia":
306
+ cm.sepia();
307
+ break;
308
+ case "negative":
309
+ cm.negative();
310
+ break;
311
+ case "blackWhite":
312
+ cm.blackWhite();
313
+ break;
314
+ case "brown":
315
+ cm.brown();
316
+ break;
317
+ case "kodachrome":
318
+ cm.kodachrome();
319
+ break;
320
+ case "technicolor":
321
+ cm.technicolor();
322
+ break;
323
+ case "polaroid":
324
+ cm.polaroid();
325
+ break;
326
+ }
327
+ return colorMatrixFilter;
1969
328
  };
1970
- const createGlowFX = (obj, config, type = "post") => {
1971
- const {
1972
- color = 16777215,
1973
- outerStrength = 4,
1974
- innerStrength = 0,
1975
- knockout = false,
1976
- quality = 0.1,
1977
- distance = 10
1978
- } = config;
1979
- const pipeline = type === "post" ? obj.postFX : obj.preFX;
1980
- if (!pipeline) {
1981
- console.warn("[createGlowFX] FX pipeline not available on this GameObject");
1982
- return null;
1983
- }
1984
- const glow = pipeline.addGlow(color, outerStrength, innerStrength, knockout, quality, distance);
1985
- return glow;
329
+ //#endregion
330
+ //#region src/fx/fx-creators/glow.ts
331
+ /**
332
+ * Create glow FX
333
+ * @param obj - GameObject
334
+ * @param config - Glow configuration
335
+ * @param type - 'post' or 'pre' FX
336
+ * @returns Glow controller
337
+ *
338
+ * @example
339
+ * ```tsx
340
+ * applyFX(createGlowFX, {
341
+ * color: 0xff6600,
342
+ * outerStrength: 6,
343
+ * innerStrength: 2
344
+ * })
345
+ * ```
346
+ */
347
+ var createGlowFX = (obj, config, type = "internal") => {
348
+ const { color = 16777215, outerStrength = 4, innerStrength = 0, knockout = false, quality = .1, distance = 10 } = config;
349
+ obj.enableFilters();
350
+ const filters = obj.filters;
351
+ if (!filters) {
352
+ console.warn("[createGlowFX] Filters not available on this GameObject");
353
+ return null;
354
+ }
355
+ const pipeline = type === "internal" ? filters.internal : filters.external;
356
+ if (!pipeline) {
357
+ console.warn("[createGlowFX] FX pipeline not available on this GameObject");
358
+ return null;
359
+ }
360
+ return pipeline.addGlow(color, outerStrength, innerStrength, 1, knockout, quality, distance);
1986
361
  };
1987
- const createPixelateFX = (obj, config, type = "post") => {
1988
- const { amount = 1 } = config;
1989
- const pipeline = type === "post" ? obj.postFX : obj.preFX;
1990
- if (!pipeline) {
1991
- console.warn("[createPixelateFX] FX pipeline not available on this GameObject");
1992
- return null;
1993
- }
1994
- const pixelate = pipeline.addPixelate(amount);
1995
- return pixelate;
362
+ //#endregion
363
+ //#region src/fx/fx-creators/pixelate.ts
364
+ /**
365
+ * Create pixelate FX
366
+ * @param obj - GameObject
367
+ * @param config - Pixelate configuration
368
+ * @param type - 'post' or 'pre' FX
369
+ * @returns Pixelate controller
370
+ *
371
+ * @example
372
+ * ```tsx
373
+ * applyFX(createPixelateFX, {
374
+ * amount: 8
375
+ * })
376
+ * ```
377
+ */
378
+ var createPixelateFX = (obj, config, type = "internal") => {
379
+ const { amount = 1 } = config;
380
+ obj.enableFilters();
381
+ const filters = obj.filters;
382
+ if (!filters) {
383
+ console.warn("[createPixelateFX] Filters not available on this GameObject");
384
+ return null;
385
+ }
386
+ const pipeline = type === "internal" ? filters.internal : filters.external;
387
+ if (!pipeline) {
388
+ console.warn("[createPixelateFX] FX pipeline not available on this GameObject");
389
+ return null;
390
+ }
391
+ return pipeline.addPixelate(amount);
1996
392
  };
1997
- const createShadowFX = (obj, config, type = "post") => {
1998
- const {
1999
- x = 0,
2000
- y = 1,
2001
- decay = 0.05,
2002
- power = 1,
2003
- color = 0,
2004
- samples = 6,
2005
- intensity = 1
2006
- } = config;
2007
- const pipeline = type === "post" ? obj.postFX : obj.preFX;
2008
- if (!pipeline) {
2009
- console.warn("[createShadowFX] FX pipeline not available on this GameObject");
2010
- return null;
2011
- }
2012
- const shadow = pipeline.addShadow(x, y, decay, power, color, samples, intensity);
2013
- return shadow;
393
+ //#endregion
394
+ //#region src/fx/fx-creators/shadow.ts
395
+ /**
396
+ * Create shadow FX
397
+ * @param obj - GameObject
398
+ * @param config - Shadow configuration
399
+ * @param type - 'post' or 'pre' FX
400
+ * @returns Shadow controller
401
+ *
402
+ * @example
403
+ * ```tsx
404
+ * applyFX(createShadowFX, {
405
+ * x: 0,
406
+ * y: 0,
407
+ * decay: 0.1,
408
+ * power: 1,
409
+ * color: 0x000000,
410
+ * samples: 6,
411
+ * intensity: 1
412
+ * })
413
+ * ```
414
+ */
415
+ var createShadowFX = (obj, config, type = "internal") => {
416
+ const { x = 0, y = 1, decay = .05, power = 1, color = 0, samples = 6, intensity = 1 } = config;
417
+ obj.enableFilters();
418
+ const filters = obj.filters;
419
+ if (!filters) {
420
+ console.warn("[createShadowFX] Filters not available on this GameObject");
421
+ return null;
422
+ }
423
+ const pipeline = type === "internal" ? filters.internal : filters.external;
424
+ if (!pipeline) {
425
+ console.warn("[createShadowFX] FX pipeline not available on this GameObject");
426
+ return null;
427
+ }
428
+ return pipeline.addShadow(x, y, decay, power, color, samples, intensity);
2014
429
  };
2015
- const createVignetteFX = (obj, config, type = "post") => {
2016
- const { strength = 0.5, radius = 0.5, x = 0.5, y = 0.5 } = config;
2017
- const pipeline = type === "post" ? obj.postFX : obj.preFX;
2018
- if (!pipeline) {
2019
- console.warn("[createVignetteFX] FX pipeline not available on this GameObject");
2020
- return null;
2021
- }
2022
- const vignette = pipeline.addVignette(x, y, radius, strength);
2023
- return vignette;
430
+ //#endregion
431
+ //#region src/fx/fx-creators/vignette.ts
432
+ /**
433
+ * Create vignette FX
434
+ * @param obj - GameObject
435
+ * @param config - Vignette configuration
436
+ * @param type - 'post' or 'pre' FX
437
+ * @returns Vignette controller
438
+ *
439
+ * @example
440
+ * ```tsx
441
+ * applyFX(createVignetteFX, {
442
+ * x: 0.5,
443
+ * y: 0.5,
444
+ * radius: 0.5,
445
+ * strength: 0.7
446
+ * })
447
+ * ```
448
+ */
449
+ var createVignetteFX = (obj, config, type = "internal") => {
450
+ const { strength = .5, radius = .5, x = .5, y = .5 } = config;
451
+ obj.enableFilters();
452
+ const filters = obj.filters;
453
+ if (!filters) {
454
+ console.warn("[createVignetteFX] Filters not available on this GameObject");
455
+ return null;
456
+ }
457
+ const pipeline = type === "internal" ? filters.internal : filters.external;
458
+ if (!pipeline) {
459
+ console.warn("[createVignetteFX] FX pipeline not available on this GameObject");
460
+ return null;
461
+ }
462
+ return pipeline.addVignette(x, y, radius, strength);
2024
463
  };
2025
- const FX_REGISTRY = {
2026
- shadow: createShadowFX,
2027
- glow: createGlowFX,
2028
- blur: createBlurFX,
2029
- pixelate: createPixelateFX,
2030
- vignette: createVignetteFX,
2031
- grayscale: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "grayscale" }, type),
2032
- sepia: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "sepia" }, type),
2033
- negative: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "negative" }, type),
2034
- blackWhite: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "blackWhite" }, type),
2035
- brown: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "brown" }, type),
2036
- kodachrome: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "kodachrome" }, type),
2037
- technicolor: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "technicolor" }, type),
2038
- polaroid: (obj, config, type) => createColorMatrixFX(obj, { ...config, effect: "polaroid" }, type)
464
+ //#endregion
465
+ //#region src/fx/fx-registry.ts
466
+ /**
467
+ * FX Registry - string-based FX lookup
468
+ * Similar to effect-registry.ts for animation effects
469
+ */
470
+ /**
471
+ * FX Registry mapping names to creator functions
472
+ */
473
+ var FX_REGISTRY = {
474
+ shadow: createShadowFX,
475
+ glow: createGlowFX,
476
+ blur: createBlurFX,
477
+ pixelate: createPixelateFX,
478
+ vignette: createVignetteFX,
479
+ grayscale: (obj, config, type) => createColorMatrixFX(obj, {
480
+ ...config,
481
+ effect: "grayscale"
482
+ }, type),
483
+ sepia: (obj, config, type) => createColorMatrixFX(obj, {
484
+ ...config,
485
+ effect: "sepia"
486
+ }, type),
487
+ negative: (obj, config, type) => createColorMatrixFX(obj, {
488
+ ...config,
489
+ effect: "negative"
490
+ }, type),
491
+ blackWhite: (obj, config, type) => createColorMatrixFX(obj, {
492
+ ...config,
493
+ effect: "blackWhite"
494
+ }, type),
495
+ brown: (obj, config, type) => createColorMatrixFX(obj, {
496
+ ...config,
497
+ effect: "brown"
498
+ }, type),
499
+ kodachrome: (obj, config, type) => createColorMatrixFX(obj, {
500
+ ...config,
501
+ effect: "kodachrome"
502
+ }, type),
503
+ technicolor: (obj, config, type) => createColorMatrixFX(obj, {
504
+ ...config,
505
+ effect: "technicolor"
506
+ }, type),
507
+ polaroid: (obj, config, type) => createColorMatrixFX(obj, {
508
+ ...config,
509
+ effect: "polaroid"
510
+ }, type)
2039
511
  };
2040
- const DEFAULT_FX = "grayscale";
512
+ /**
513
+ * Default FX (none)
514
+ */
515
+ var DEFAULT_FX = "grayscale";
516
+ /**
517
+ * Resolve FX by name or function
518
+ * @param fxOrName - FX name string or creator function
519
+ * @returns FX creator function or null
520
+ */
2041
521
  function resolveFX(fxOrName) {
2042
- if (typeof fxOrName === "function") {
2043
- return fxOrName;
2044
- }
2045
- return FX_REGISTRY[fxOrName] ?? null;
2046
- }
522
+ if (typeof fxOrName === "function") return fxOrName;
523
+ return FX_REGISTRY[fxOrName] ?? null;
524
+ }
525
+ /**
526
+ * Apply FX by name (helper function)
527
+ * @param applyFXFn - applyFX function from useFX hook
528
+ * @param fxName - FX name
529
+ * @param config - FX config
530
+ *
531
+ * @example
532
+ * ```tsx
533
+ * const { applyFX } = useFX(ref)
534
+ * applyFXByName(applyFX, 'shadow', { offsetX: 4, offsetY: 4, blur: 8 })
535
+ * ```
536
+ */
2047
537
  function applyFXByName(applyFXFn, fxName, config = {}) {
2048
- const creator = resolveFX(fxName);
2049
- if (creator) {
2050
- applyFXFn(creator, config);
2051
- } else {
2052
- console.warn(`[applyFXByName] FX "${fxName}" not found in registry`);
2053
- }
2054
- }
538
+ const creator = resolveFX(fxName);
539
+ if (creator) applyFXFn(creator, config);
540
+ else console.warn(`[applyFXByName] FX "${fxName}" not found in registry`);
541
+ }
542
+ //#endregion
543
+ //#region src/fx/convenience-hooks/use-blur.ts
544
+ /**
545
+ * Convenience hook for automatic blur FX
546
+ * Applies blur on mount and updates on config changes
547
+ */
548
+ /**
549
+ * Hook for automatic blur FX
550
+ * @param ref - GameObject ref
551
+ * @param config - Blur config (updates reactively)
552
+ * @returns FX controls
553
+ *
554
+ * @example
555
+ * ```tsx
556
+ * const ref = useRef(null)
557
+ * useBlur(ref, { strength: 8, steps: 4 })
558
+ *
559
+ * return <View ref={ref}>Content</View>
560
+ * ```
561
+ */
2055
562
  function useBlur(ref, config = {}) {
2056
- const { applyFX, clearFX } = useFX(ref);
2057
- useEffect(() => {
2058
- if (!ref.current) return;
2059
- clearFX();
2060
- applyFX(createBlurFX, config);
2061
- return () => clearFX();
2062
- }, [ref, config, applyFX, clearFX]);
2063
- return { clearFX };
2064
- }
563
+ const { applyFX, clearFX } = useFX(ref);
564
+ useEffect(() => {
565
+ if (!ref.current) return;
566
+ clearFX();
567
+ applyFX(createBlurFX, config);
568
+ return () => clearFX();
569
+ }, [
570
+ ref,
571
+ config,
572
+ applyFX,
573
+ clearFX
574
+ ]);
575
+ return { clearFX };
576
+ }
577
+ //#endregion
578
+ //#region src/fx/convenience-hooks/use-glow.ts
579
+ /**
580
+ * Convenience hook for automatic glow FX
581
+ * Applies glow on mount and updates on config changes
582
+ */
583
+ /**
584
+ * Hook for automatic glow FX
585
+ * @param ref - GameObject ref
586
+ * @param config - Glow config (updates reactively)
587
+ * @returns FX controls
588
+ *
589
+ * @example
590
+ * ```tsx
591
+ * const ref = useRef(null)
592
+ * useGlow(ref, { color: 0xff6600, outerStrength: 6, innerStrength: 2 })
593
+ *
594
+ * return <View ref={ref}>Content</View>
595
+ * ```
596
+ */
2065
597
  function useGlow(ref, config = {}) {
2066
- const { applyFX, clearFX } = useFX(ref);
2067
- useEffect(() => {
2068
- if (!ref.current) return;
2069
- clearFX();
2070
- applyFX(createGlowFX, config);
2071
- return () => clearFX();
2072
- }, [ref, config, applyFX, clearFX]);
2073
- return { clearFX };
2074
- }
598
+ const { applyFX, clearFX } = useFX(ref);
599
+ useEffect(() => {
600
+ if (!ref.current) return;
601
+ clearFX();
602
+ applyFX(createGlowFX, config);
603
+ return () => clearFX();
604
+ }, [
605
+ ref,
606
+ config,
607
+ applyFX,
608
+ clearFX
609
+ ]);
610
+ return { clearFX };
611
+ }
612
+ //#endregion
613
+ //#region src/fx/convenience-hooks/use-shadow.ts
614
+ /**
615
+ * Convenience hook for automatic shadow FX
616
+ * Applies shadow on mount and updates on config changes
617
+ */
618
+ /**
619
+ * Hook for automatic shadow FX
620
+ * @param ref - GameObject ref
621
+ * @param config - Shadow config (updates reactively)
622
+ * @returns FX controls
623
+ *
624
+ * @example
625
+ * ```tsx
626
+ * const ref = useRef(null)
627
+ * useShadow(ref, { x: 4, y: 4, decay: 0.1 })
628
+ *
629
+ * return <View ref={ref}>Content</View>
630
+ * ```
631
+ */
2075
632
  function useShadow(ref, config = {}) {
2076
- const { applyFX, clearFX } = useFX(ref);
2077
- useEffect(() => {
2078
- if (!ref.current) return;
2079
- clearFX();
2080
- applyFX(createShadowFX, config);
2081
- return () => clearFX();
2082
- }, [ref, config, applyFX, clearFX]);
2083
- return { clearFX };
2084
- }
633
+ const { applyFX, clearFX } = useFX(ref);
634
+ useEffect(() => {
635
+ if (!ref.current) return;
636
+ clearFX();
637
+ applyFX(createShadowFX, config);
638
+ return () => clearFX();
639
+ }, [
640
+ ref,
641
+ config,
642
+ applyFX,
643
+ clearFX
644
+ ]);
645
+ return { clearFX };
646
+ }
647
+ //#endregion
648
+ //#region src/memo.ts
649
+ /**
650
+ * Safely sets __memo on a VNode, handling frozen/sealed objects from bundlers
651
+ * @param vnode - VNode to set __memo on
652
+ * @param value - Memoization value
653
+ * @returns Original VNode if extensible, or shallow copy with __memo
654
+ */
2085
655
  function setMemoSafe(vnode, value) {
2086
- if (Object.isExtensible(vnode)) {
2087
- vnode.__memo = value;
2088
- return vnode;
2089
- }
2090
- return { ...vnode, __memo: value };
2091
- }
656
+ if (Object.isExtensible(vnode)) {
657
+ vnode.__memo = value;
658
+ return vnode;
659
+ }
660
+ return {
661
+ ...vnode,
662
+ __memo: value
663
+ };
664
+ }
665
+ /**
666
+ * Marks a component to skip memoization (always re-render on prop changes)
667
+ * Use when component has side effects or needs to re-render every time
668
+ *
669
+ * @example
670
+ * ```tsx
671
+ * function AlwaysUpdate({ value }) {
672
+ * console.log('Rendering with:', value)
673
+ * return <Text text={value} />
674
+ * }
675
+ *
676
+ * // Disable memoization
677
+ * <AlwaysUpdate value={counter} __memo={false} />
678
+ * ```
679
+ *
680
+ * @param vnode - VNode to mark
681
+ * @returns Same VNode with memoization disabled (may be a copy if frozen)
682
+ */
2092
683
  function noMemo(vnode) {
2093
- return setMemoSafe(vnode, false);
2094
- }
684
+ return setMemoSafe(vnode, false);
685
+ }
686
+ /**
687
+ * Explicitly enable memoization for a component (default behavior)
688
+ * This is the default - you don't need to call this unless you want to be explicit
689
+ *
690
+ * @param vnode - VNode to mark
691
+ * @returns Same VNode with memoization enabled (may be a copy if frozen)
692
+ */
2095
693
  function memo(vnode) {
2096
- return setMemoSafe(vnode, true);
694
+ return setMemoSafe(vnode, true);
2097
695
  }
696
+ //#endregion
697
+ //#region src/particles/use-particles.ts
698
+ /**
699
+ * Hook for imperative particle control
700
+ */
2098
701
  function useParticles(ref) {
2099
- const getManager = useCallback(() => ref.current ?? null, [ref]);
2100
- const getEmitter = useCallback(() => {
2101
- const current = ref.current;
2102
- if (!current) return null;
2103
- if (isParticleEmitter(current)) return current;
2104
- return getFirstEmitter(current);
2105
- }, [ref]);
2106
- const start = useCallback(() => {
2107
- const emitter = getEmitter();
2108
- emitter?.start();
2109
- }, [getEmitter]);
2110
- const stop = useCallback(() => {
2111
- const emitter = getEmitter();
2112
- emitter?.stop();
2113
- }, [getEmitter]);
2114
- const explode = useCallback(
2115
- (count, x, y) => {
2116
- const emitter = getEmitter();
2117
- emitter?.explode(count, x, y);
2118
- },
2119
- [getEmitter]
2120
- );
2121
- const setConfig = useCallback(
2122
- (config) => {
2123
- const emitter = getEmitter();
2124
- applyEmitterConfig(emitter, config);
2125
- },
2126
- [getEmitter]
2127
- );
2128
- const setEmitZone = useCallback(
2129
- (zone, width, height) => {
2130
- const emitter = getEmitter();
2131
- const emitZone = buildEmitZoneFromLayout(zone, width, height);
2132
- applyEmitZone(emitter, emitZone);
2133
- },
2134
- [getEmitter]
2135
- );
2136
- const setExcludeZones = useCallback(
2137
- (zones, width, height) => {
2138
- const emitter = getEmitter();
2139
- const deathZones = buildDeathZonesFromLayout(zones, width, height);
2140
- applyDeathZone(emitter, deathZones);
2141
- },
2142
- [getEmitter]
2143
- );
2144
- return {
2145
- getManager,
2146
- getEmitter,
2147
- start,
2148
- stop,
2149
- explode,
2150
- setConfig,
2151
- setEmitZone,
2152
- setExcludeZones
2153
- };
2154
- }
2155
- const DEFAULT_LOGO_KEY = "phaser-jsx-logo";
2156
- const DEFAULT_BACKGROUND = {
2157
- type: "grid",
2158
- animation: "lemniscate",
2159
- opacity: 0.15,
2160
- color: 4890367
702
+ const getManager = useCallback(() => ref.current ?? null, [ref]);
703
+ const getEmitter = useCallback(() => {
704
+ const current = ref.current;
705
+ if (!current) return null;
706
+ if (isParticleEmitter(current)) return current;
707
+ return getFirstEmitter(current);
708
+ }, [ref]);
709
+ return {
710
+ getManager,
711
+ getEmitter,
712
+ start: useCallback(() => {
713
+ getEmitter()?.start();
714
+ }, [getEmitter]),
715
+ stop: useCallback(() => {
716
+ getEmitter()?.stop();
717
+ }, [getEmitter]),
718
+ explode: useCallback((count, x, y) => {
719
+ getEmitter()?.explode(count, x, y);
720
+ }, [getEmitter]),
721
+ setConfig: useCallback((config) => {
722
+ applyEmitterConfig(getEmitter(), config);
723
+ }, [getEmitter]),
724
+ setEmitZone: useCallback((zone, width, height) => {
725
+ applyEmitZone(getEmitter(), buildEmitZoneFromLayout(zone, width, height));
726
+ }, [getEmitter]),
727
+ setExcludeZones: useCallback((zones, width, height) => {
728
+ applyDeathZone(getEmitter(), buildDeathZonesFromLayout(zones, width, height));
729
+ }, [getEmitter])
730
+ };
731
+ }
732
+ //#endregion
733
+ //#region src/scene-backgrounds.ts
734
+ var DEFAULT_LOGO_KEY = "phaser-jsx-logo";
735
+ /**
736
+ * Default scene background used when no explicit config is provided.
737
+ */
738
+ var DEFAULT_BACKGROUND = {
739
+ type: "grid",
740
+ animation: "lemniscate",
741
+ opacity: .15,
742
+ color: 4890367
2161
743
  };
744
+ var sceneBackgroundTextureId = 0;
745
+ function colorToCss(color) {
746
+ return `#${color.toString(16).padStart(6, "0")}`;
747
+ }
748
+ function createCanvasTexture(scene, width, height, draw) {
749
+ const textureKey = `__phaserjsx_scene_bg_${sceneBackgroundTextureId++}`;
750
+ const textureWidth = Math.max(1, Math.ceil(width));
751
+ const textureHeight = Math.max(1, Math.ceil(height));
752
+ const texture = scene.textures.createCanvas(textureKey, textureWidth, textureHeight);
753
+ if (!texture) throw new Error("Unable to create scene background texture");
754
+ const ctx = texture.getSourceImage().getContext("2d");
755
+ if (!ctx) throw new Error("Unable to create scene background canvas texture");
756
+ ctx.clearRect(0, 0, textureWidth, textureHeight);
757
+ draw(ctx, textureWidth, textureHeight);
758
+ texture.refresh();
759
+ return textureKey;
760
+ }
761
+ function removeTexture(scene, textureKey) {
762
+ if (textureKey && scene.textures.exists(textureKey)) scene.textures.remove(textureKey);
763
+ }
764
+ /**
765
+ * Adds a decorative background object to a Phaser scene.
766
+ *
767
+ * The helper owns all game objects, tweens, resize listeners, and generated textures it creates.
768
+ * It automatically cleans up on scene shutdown or destroy. Call `handle.destroy()` when removing
769
+ * the background earlier.
770
+ *
771
+ * @param scene - Phaser scene that receives the background.
772
+ * @param config - Background type, colors, opacity, animation, and optional logo texture key.
773
+ * @returns A handle for manual resize and cleanup, or `null` when `config.type` is `'none'`.
774
+ *
775
+ * @example
776
+ * ```ts
777
+ * const handle = addSceneBackground(scene, {
778
+ * type: 'grid',
779
+ * animation: 'wave',
780
+ * color: 0x4a9eff,
781
+ * opacity: 0.12,
782
+ * })
783
+ *
784
+ * // Later, if the background should be removed before scene shutdown:
785
+ * handle?.destroy()
786
+ * ```
787
+ */
2162
788
  function addSceneBackground(scene, config = DEFAULT_BACKGROUND) {
2163
- const bgConfig = config ?? DEFAULT_BACKGROUND;
2164
- if (bgConfig.type === "none") return null;
2165
- let background;
2166
- let backgroundTween;
2167
- const particleTweens = [];
2168
- let resizeFn;
2169
- let destroyed = false;
2170
- const createGridBackground = () => {
2171
- const graphics = scene.add.graphics();
2172
- const gridSize = 40;
2173
- const color = bgConfig.color ?? 4890367;
2174
- const opacity = bgConfig.opacity ?? 0.15;
2175
- const drawGrid = (offsetX, offsetY) => {
2176
- graphics.clear();
2177
- graphics.lineStyle(1, color, opacity);
2178
- const width = scene.scale.width;
2179
- const height = scene.scale.height;
2180
- for (let x = offsetX % gridSize; x < width; x += gridSize) {
2181
- graphics.lineBetween(x, 0, x, height);
2182
- }
2183
- for (let y = offsetY % gridSize; y < height; y += gridSize) {
2184
- graphics.lineBetween(0, y, width, y);
2185
- }
2186
- };
2187
- drawGrid(0, 0);
2188
- graphics.__redrawFn = drawGrid;
2189
- background = graphics;
2190
- resizeFn = () => drawGrid(0, 0);
2191
- };
2192
- const createLogoBackground = () => {
2193
- const container = scene.add.container(scene.scale.width / 2, scene.scale.height / 2);
2194
- const opacity = bgConfig.opacity ?? 0.1;
2195
- const logoKey = bgConfig.logoKey ?? DEFAULT_LOGO_KEY;
2196
- const logo = scene.add.image(0, 0, logoKey);
2197
- logo.setAlpha(opacity);
2198
- logo.setScale(0.5);
2199
- container.add(logo);
2200
- background = container;
2201
- resizeFn = (width, height) => {
2202
- container.setPosition(width / 2, height / 2);
2203
- };
2204
- };
2205
- const createGradientBackground = () => {
2206
- const graphics = scene.add.graphics();
2207
- const color1 = bgConfig.color ?? 4890367;
2208
- const color2 = bgConfig.colorSecondary ?? 7031551;
2209
- const opacity = bgConfig.opacity ?? 0.2;
2210
- const drawGradient = () => {
2211
- graphics.clear();
2212
- graphics.fillGradientStyle(color1, color1, color2, color2, opacity, opacity, opacity, opacity);
2213
- graphics.fillRect(0, 0, scene.scale.width, scene.scale.height);
2214
- };
2215
- drawGradient();
2216
- background = graphics;
2217
- resizeFn = drawGradient;
2218
- };
2219
- const createParticlesBackground = () => {
2220
- const container = scene.add.container(0, 0);
2221
- const particleCount = 20;
2222
- const color = bgConfig.color ?? 4890367;
2223
- const opacity = bgConfig.opacity ?? 0.1;
2224
- const particles = [];
2225
- for (let i2 = 0; i2 < particleCount; i2 += 1) {
2226
- const size = Phaser.Math.Between(2, 6);
2227
- const x = Phaser.Math.Between(0, scene.scale.width);
2228
- const y = Phaser.Math.Between(0, scene.scale.height);
2229
- const particle = scene.add.circle(x, y, size, color, opacity);
2230
- container.add(particle);
2231
- particles.push(particle);
2232
- particleTweens.push(
2233
- scene.tweens.add({
2234
- targets: particle,
2235
- y: particle.y + Phaser.Math.Between(-50, 50),
2236
- x: particle.x + Phaser.Math.Between(-50, 50),
2237
- alpha: opacity * 1.5,
2238
- duration: Phaser.Math.Between(2e3, 4e3),
2239
- yoyo: true,
2240
- repeat: -1,
2241
- ease: "Sine.easeInOut"
2242
- })
2243
- );
2244
- }
2245
- background = container;
2246
- resizeFn = (width, height) => {
2247
- for (const particle of particles) {
2248
- particle.setPosition(Phaser.Math.Between(0, width), Phaser.Math.Between(0, height));
2249
- }
2250
- };
2251
- };
2252
- switch (bgConfig.type) {
2253
- case "grid":
2254
- createGridBackground();
2255
- break;
2256
- case "logo":
2257
- createLogoBackground();
2258
- break;
2259
- case "gradient":
2260
- createGradientBackground();
2261
- break;
2262
- case "particles":
2263
- createParticlesBackground();
2264
- break;
2265
- }
2266
- if (!background) return null;
2267
- if (bgConfig.animation && bgConfig.animation !== "static") {
2268
- backgroundTween = applyAnimation(scene, background, bgConfig.animation);
2269
- }
2270
- const onResize = () => {
2271
- if (resizeFn) resizeFn(scene.scale.width, scene.scale.height);
2272
- };
2273
- const cleanup = () => {
2274
- if (destroyed) return;
2275
- destroyed = true;
2276
- backgroundTween?.stop();
2277
- for (const tween of particleTweens) {
2278
- tween.stop();
2279
- }
2280
- background?.destroy();
2281
- background = void 0;
2282
- scene.scale.off(Phaser.Scale.Events.RESIZE, onResize);
2283
- scene.events.off(Phaser.Scenes.Events.SHUTDOWN, cleanup);
2284
- scene.events.off(Phaser.Scenes.Events.DESTROY, cleanup);
2285
- };
2286
- scene.scale.on(Phaser.Scale.Events.RESIZE, onResize);
2287
- scene.events.once(Phaser.Scenes.Events.SHUTDOWN, cleanup);
2288
- scene.events.once(Phaser.Scenes.Events.DESTROY, cleanup);
2289
- return {
2290
- background,
2291
- destroy: cleanup,
2292
- resize: (width, height) => {
2293
- if (resizeFn) resizeFn(width, height);
2294
- }
2295
- };
789
+ const bgConfig = config ?? DEFAULT_BACKGROUND;
790
+ if (bgConfig.type === "none") return null;
791
+ let background;
792
+ let backgroundTween;
793
+ const particleTweens = [];
794
+ const textureKeys = /* @__PURE__ */ new Set();
795
+ let resizeFn;
796
+ let destroyed = false;
797
+ const createGridBackground = () => {
798
+ const gridSize = 40;
799
+ const color = bgConfig.color ?? 4890367;
800
+ const opacity = bgConfig.opacity ?? .15;
801
+ const textureKey = createCanvasTexture(scene, gridSize, gridSize, (ctx, width, height) => {
802
+ ctx.globalAlpha = opacity;
803
+ ctx.strokeStyle = colorToCss(color);
804
+ ctx.lineWidth = 1;
805
+ ctx.beginPath();
806
+ ctx.moveTo(.5, 0);
807
+ ctx.lineTo(.5, height);
808
+ ctx.moveTo(0, .5);
809
+ ctx.lineTo(width, .5);
810
+ ctx.stroke();
811
+ });
812
+ textureKeys.add(textureKey);
813
+ const tile = scene.add.tileSprite(0, 0, scene.scale.width, scene.scale.height, textureKey);
814
+ tile.setOrigin(0, 0);
815
+ const moveGrid = (offsetX, offsetY) => {
816
+ tile.tilePositionX = -offsetX;
817
+ tile.tilePositionY = -offsetY;
818
+ };
819
+ tile.__redrawFn = moveGrid;
820
+ background = tile;
821
+ resizeFn = (width, height) => {
822
+ tile.setSize(width, height);
823
+ };
824
+ };
825
+ const createLogoBackground = () => {
826
+ const container = scene.add.container(scene.scale.width / 2, scene.scale.height / 2);
827
+ const opacity = bgConfig.opacity ?? .1;
828
+ const logoKey = bgConfig.logoKey ?? DEFAULT_LOGO_KEY;
829
+ const logo = scene.add.image(0, 0, logoKey);
830
+ logo.setAlpha(opacity);
831
+ logo.setScale(.5);
832
+ container.add(logo);
833
+ background = container;
834
+ resizeFn = (width, height) => {
835
+ container.setPosition(width / 2, height / 2);
836
+ };
837
+ };
838
+ const createGradientBackground = () => {
839
+ const color1 = bgConfig.color ?? 4890367;
840
+ const color2 = bgConfig.colorSecondary ?? 7031551;
841
+ const opacity = bgConfig.opacity ?? .2;
842
+ const createGradientTexture = (width, height) => createCanvasTexture(scene, width, height, (ctx, textureWidth, textureHeight) => {
843
+ const gradient = ctx.createLinearGradient(0, 0, 0, textureHeight);
844
+ gradient.addColorStop(0, colorToCss(color1));
845
+ gradient.addColorStop(1, colorToCss(color2));
846
+ ctx.globalAlpha = opacity;
847
+ ctx.fillStyle = gradient;
848
+ ctx.fillRect(0, 0, textureWidth, textureHeight);
849
+ });
850
+ let textureKey = createGradientTexture(scene.scale.width, scene.scale.height);
851
+ textureKeys.add(textureKey);
852
+ const image = scene.add.image(0, 0, textureKey);
853
+ image.setOrigin(0, 0);
854
+ resizeFn = (width, height) => {
855
+ const previousTextureKey = textureKey;
856
+ textureKey = createGradientTexture(width, height);
857
+ textureKeys.add(textureKey);
858
+ image.setTexture(textureKey);
859
+ image.setDisplaySize(width, height);
860
+ textureKeys.delete(previousTextureKey);
861
+ removeTexture(scene, previousTextureKey);
862
+ };
863
+ background = image;
864
+ };
865
+ const createParticlesBackground = () => {
866
+ const container = scene.add.container(0, 0);
867
+ const particleCount = 20;
868
+ const color = bgConfig.color ?? 4890367;
869
+ const opacity = bgConfig.opacity ?? .1;
870
+ const particles = [];
871
+ for (let i = 0; i < particleCount; i += 1) {
872
+ const size = Phaser.Math.Between(2, 6);
873
+ const x = Phaser.Math.Between(0, scene.scale.width);
874
+ const y = Phaser.Math.Between(0, scene.scale.height);
875
+ const particle = scene.add.circle(x, y, size, color, opacity);
876
+ container.add(particle);
877
+ particles.push(particle);
878
+ particleTweens.push(scene.tweens.add({
879
+ targets: particle,
880
+ y: particle.y + Phaser.Math.Between(-50, 50),
881
+ x: particle.x + Phaser.Math.Between(-50, 50),
882
+ alpha: opacity * 1.5,
883
+ duration: Phaser.Math.Between(2e3, 4e3),
884
+ yoyo: true,
885
+ repeat: -1,
886
+ ease: "Sine.easeInOut"
887
+ }));
888
+ }
889
+ background = container;
890
+ resizeFn = (width, height) => {
891
+ for (const particle of particles) particle.setPosition(Phaser.Math.Between(0, width), Phaser.Math.Between(0, height));
892
+ };
893
+ };
894
+ switch (bgConfig.type) {
895
+ case "grid":
896
+ createGridBackground();
897
+ break;
898
+ case "logo":
899
+ createLogoBackground();
900
+ break;
901
+ case "gradient":
902
+ createGradientBackground();
903
+ break;
904
+ case "particles":
905
+ createParticlesBackground();
906
+ break;
907
+ }
908
+ if (!background) return null;
909
+ if (bgConfig.animation && bgConfig.animation !== "static") backgroundTween = applyAnimation(scene, background, bgConfig.animation);
910
+ const onResize = () => {
911
+ if (resizeFn) resizeFn(scene.scale.width, scene.scale.height);
912
+ };
913
+ const cleanup = () => {
914
+ if (destroyed) return;
915
+ destroyed = true;
916
+ backgroundTween?.stop();
917
+ for (const tween of particleTweens) tween.stop();
918
+ background?.destroy();
919
+ background = void 0;
920
+ for (const textureKey of textureKeys) removeTexture(scene, textureKey);
921
+ textureKeys.clear();
922
+ scene.scale.off(Phaser.Scale.Events.RESIZE, onResize);
923
+ scene.events.off(Phaser.Scenes.Events.SHUTDOWN, cleanup);
924
+ scene.events.off(Phaser.Scenes.Events.DESTROY, cleanup);
925
+ };
926
+ scene.scale.on(Phaser.Scale.Events.RESIZE, onResize);
927
+ scene.events.once(Phaser.Scenes.Events.SHUTDOWN, cleanup);
928
+ scene.events.once(Phaser.Scenes.Events.DESTROY, cleanup);
929
+ return {
930
+ background,
931
+ destroy: cleanup,
932
+ resize: (width, height) => {
933
+ if (resizeFn) resizeFn(width, height);
934
+ }
935
+ };
2296
936
  }
2297
937
  function applyAnimation(scene, target, animation) {
2298
- const redrawFn = target.__redrawFn;
2299
- switch (animation) {
2300
- case "lemniscate":
2301
- return redrawFn ? animateLemniscate(scene, redrawFn) : animatePulse(scene, target);
2302
- case "wave":
2303
- return redrawFn ? animateWave(scene, redrawFn) : animatePulse(scene, target);
2304
- case "pulse":
2305
- return animatePulse(scene, target);
2306
- case "rotate":
2307
- return animateRotate(scene, target);
2308
- case "static":
2309
- default:
2310
- return void 0;
2311
- }
938
+ const redrawFn = target.__redrawFn;
939
+ switch (animation) {
940
+ case "lemniscate": return redrawFn ? animateLemniscate(scene, redrawFn) : animatePulse(scene, target);
941
+ case "wave": return redrawFn ? animateWave(scene, redrawFn) : animatePulse(scene, target);
942
+ case "pulse": return animatePulse(scene, target);
943
+ case "rotate": return animateRotate(scene, target);
944
+ default: return;
945
+ }
2312
946
  }
2313
947
  function animateLemniscate(scene, drawFn) {
2314
- const amplitude = 30;
2315
- const duration = 8e3;
2316
- return scene.tweens.addCounter({
2317
- from: 0,
2318
- to: Math.PI * 2,
2319
- duration,
2320
- repeat: -1,
2321
- onUpdate: (tween) => {
2322
- const t = tween.getValue();
2323
- if (t !== null) {
2324
- const offsetX = amplitude * Math.cos(t);
2325
- const offsetY = amplitude * Math.sin(2 * t) / 2;
2326
- drawFn(offsetX, offsetY);
2327
- }
2328
- }
2329
- });
948
+ const amplitude = 30;
949
+ return scene.tweens.addCounter({
950
+ from: 0,
951
+ to: Math.PI * 2,
952
+ duration: 8e3,
953
+ repeat: -1,
954
+ onUpdate: (tween) => {
955
+ const t = tween.getValue();
956
+ if (t !== null) drawFn(amplitude * Math.cos(t), amplitude * Math.sin(2 * t) / 2);
957
+ }
958
+ });
2330
959
  }
2331
960
  function animateWave(scene, drawFn) {
2332
- return scene.tweens.addCounter({
2333
- from: 0,
2334
- to: 40,
2335
- duration: 3e3,
2336
- yoyo: true,
2337
- repeat: -1,
2338
- onUpdate: (tween) => {
2339
- const offset = tween.getValue();
2340
- if (offset !== null) {
2341
- drawFn(offset, 0);
2342
- }
2343
- }
2344
- });
961
+ return scene.tweens.addCounter({
962
+ from: 0,
963
+ to: 40,
964
+ duration: 3e3,
965
+ yoyo: true,
966
+ repeat: -1,
967
+ onUpdate: (tween) => {
968
+ const offset = tween.getValue();
969
+ if (offset !== null) drawFn(offset, 0);
970
+ }
971
+ });
2345
972
  }
2346
973
  function animatePulse(scene, target) {
2347
- return scene.tweens.add({
2348
- targets: target,
2349
- scaleX: 1.1,
2350
- scaleY: 1.1,
2351
- duration: 2e3,
2352
- yoyo: true,
2353
- repeat: -1,
2354
- ease: "Sine.easeInOut"
2355
- });
974
+ return scene.tweens.add({
975
+ targets: target,
976
+ scaleX: 1.1,
977
+ scaleY: 1.1,
978
+ duration: 2e3,
979
+ yoyo: true,
980
+ repeat: -1,
981
+ ease: "Sine.easeInOut"
982
+ });
2356
983
  }
2357
984
  function animateRotate(scene, target) {
2358
- return scene.tweens.add({
2359
- targets: target,
2360
- angle: 360,
2361
- duration: 2e4,
2362
- repeat: -1,
2363
- ease: "Linear"
2364
- });
2365
- }
985
+ return scene.tweens.add({
986
+ targets: target,
987
+ angle: 360,
988
+ duration: 2e4,
989
+ repeat: -1,
990
+ ease: "Linear"
991
+ });
992
+ }
993
+ //#endregion
994
+ //#region src/plugin.ts
995
+ /**
996
+ * PhaserJSX Plugin for automatic JSX mounting
997
+ * Enables declarative initialization via Phaser game config
998
+ */
999
+ /**
1000
+ * Implementation
1001
+ */
2366
1002
  function createPhaserJSXPlugin(config) {
2367
- return {
2368
- key: "PhaserJSX",
2369
- plugin: PhaserJSXPlugin,
2370
- start: true,
2371
- data: config
2372
- };
2373
- }
2374
- class PhaserJSXPlugin extends Phaser.Plugins.BasePlugin {
2375
- config;
2376
- mountHandle;
2377
- container;
2378
- targetScene;
2379
- /**
2380
- * Constructor - receives plugin manager and optional mapping
2381
- */
2382
- constructor(pluginManager) {
2383
- super(pluginManager);
2384
- }
2385
- /**
2386
- * Init lifecycle - called first with config data
2387
- */
2388
- init(data) {
2389
- if (data) {
2390
- this.config = { ...data };
2391
- }
2392
- }
2393
- /**
2394
- * Start lifecycle - called when plugin should start
2395
- */
2396
- start() {
2397
- this.game.events.on("ready", this.onGameReady, this);
2398
- }
2399
- /**
2400
- * Game ready handler - scene system is now initialized
2401
- */
2402
- onGameReady() {
2403
- const scenes = this.game.scene.scenes;
2404
- if (scenes.length > 0) {
2405
- const targetScene = scenes[0];
2406
- if (!targetScene) return;
2407
- this.targetScene = targetScene;
2408
- targetScene.events.once("create", this.onSceneCreate, this);
2409
- if (targetScene.scene.isActive()) {
2410
- this.onSceneCreate();
2411
- }
2412
- } else {
2413
- console.warn("[PhaserJSX Plugin] No scenes found to mount JSX");
2414
- }
2415
- }
2416
- /**
2417
- * Scene create handler - auto-mount JSX
2418
- */
2419
- onSceneCreate() {
2420
- const shouldAutoMount = this.config?.autoMount !== false;
2421
- if (shouldAutoMount && this.config?.component) {
2422
- this.mount();
2423
- if (this.config?.autoResize !== false) {
2424
- this.setupResizeHandler();
2425
- }
2426
- } else {
2427
- console.warn("[PhaserJSX Plugin] Auto-mount disabled or no component configured");
2428
- }
2429
- }
2430
- /**
2431
- * Setup resize event handler
2432
- */
2433
- setupResizeHandler() {
2434
- if (!this.targetScene) return;
2435
- this.targetScene.scale.on("resize", this.onResize, this);
2436
- }
2437
- /**
2438
- * Handle scene resize - update component props
2439
- */
2440
- onResize(gameSize) {
2441
- if (!this.mountHandle || !this.config?.component || !this.container) return;
2442
- const props = this.config.props || {};
2443
- const width = props.width ?? gameSize.width;
2444
- const height = props.height ?? gameSize.height;
2445
- this.mountHandle = mountJSX(this.container, this.config.component, {
2446
- ...props,
2447
- width,
2448
- height
2449
- });
2450
- }
2451
- /**
2452
- * Configure plugin
2453
- * Can be called from scene to set up component dynamically
2454
- */
2455
- configure(component, props) {
2456
- const newConfig = {
2457
- component
2458
- };
2459
- if (props !== void 0) {
2460
- newConfig.props = props;
2461
- }
2462
- this.config = {
2463
- ...this.config,
2464
- ...newConfig
2465
- };
2466
- }
2467
- /**
2468
- * Mount JSX component
2469
- */
2470
- mount() {
2471
- if (!this.targetScene) {
2472
- console.warn("[PhaserJSX Plugin] No scene available for mounting");
2473
- return;
2474
- }
2475
- if (!this.config?.component) {
2476
- console.warn("[PhaserJSX Plugin] No component configured for mounting");
2477
- return;
2478
- }
2479
- if (!this.container) {
2480
- const containerConfig = this.config.container || {};
2481
- this.container = this.targetScene.add.container(
2482
- containerConfig.x ?? 0,
2483
- containerConfig.y ?? 0
2484
- );
2485
- this.container.setDepth(containerConfig.depth ?? 100);
2486
- }
2487
- const props = this.config.props || {};
2488
- const width = props.width ?? this.targetScene.scale.width;
2489
- const height = props.height ?? this.targetScene.scale.height;
2490
- this.mountHandle = mountJSX(this.container, this.config.component, {
2491
- ...props,
2492
- width,
2493
- height
2494
- });
2495
- }
2496
- /**
2497
- * Unmount JSX component
2498
- */
2499
- unmount() {
2500
- if (this.mountHandle) {
2501
- this.mountHandle.unmount();
2502
- this.mountHandle = void 0;
2503
- }
2504
- }
2505
- /**
2506
- * Destroy lifecycle - cleanup
2507
- */
2508
- destroy() {
2509
- this.unmount();
2510
- if (this.container) {
2511
- this.container.destroy();
2512
- this.container = void 0;
2513
- }
2514
- this.game.events.off("ready", this.onGameReady, this);
2515
- if (this.targetScene) {
2516
- this.targetScene.events.off("create", this.onSceneCreate, this);
2517
- this.targetScene.scale.off("resize", this.onResize, this);
2518
- }
2519
- this.targetScene = void 0;
2520
- this.config = void 0;
2521
- super.destroy();
2522
- }
2523
- }
1003
+ return {
1004
+ key: "PhaserJSX",
1005
+ plugin: PhaserJSXPlugin,
1006
+ start: true,
1007
+ data: config
1008
+ };
1009
+ }
1010
+ /**
1011
+ * PhaserJSX Plugin
1012
+ * Provides automatic JSX mounting through Phaser plugin system
1013
+ *
1014
+ * @example
1015
+ * ```typescript
1016
+ * // In game config
1017
+ * plugins: {
1018
+ * global: [{
1019
+ * key: 'PhaserJSX',
1020
+ * plugin: PhaserJSXPlugin,
1021
+ * start: true,
1022
+ * data: {
1023
+ * component: App,
1024
+ * props: { width: '100%', height: '100%' }
1025
+ * }
1026
+ * }]
1027
+ * }
1028
+ * ```
1029
+ */
1030
+ var PhaserJSXPlugin = class extends Phaser.Plugins.BasePlugin {
1031
+ config;
1032
+ mountHandle;
1033
+ container;
1034
+ targetScene;
1035
+ /**
1036
+ * Constructor - receives plugin manager and optional mapping
1037
+ */
1038
+ constructor(pluginManager) {
1039
+ super(pluginManager);
1040
+ }
1041
+ /**
1042
+ * Init lifecycle - called first with config data
1043
+ */
1044
+ init(data) {
1045
+ if (data) this.config = { ...data };
1046
+ }
1047
+ /**
1048
+ * Start lifecycle - called when plugin should start
1049
+ */
1050
+ start() {
1051
+ this.game.events.on("ready", this.onGameReady, this);
1052
+ }
1053
+ /**
1054
+ * Game ready handler - scene system is now initialized
1055
+ */
1056
+ onGameReady() {
1057
+ const scenes = this.game.scene.scenes;
1058
+ if (scenes.length > 0) {
1059
+ const targetScene = scenes[0];
1060
+ if (!targetScene) return;
1061
+ this.targetScene = targetScene;
1062
+ targetScene.events.once("create", this.onSceneCreate, this);
1063
+ if (targetScene.scene.isActive()) this.onSceneCreate();
1064
+ } else console.warn("[PhaserJSX Plugin] No scenes found to mount JSX");
1065
+ }
1066
+ /**
1067
+ * Scene create handler - auto-mount JSX
1068
+ */
1069
+ onSceneCreate() {
1070
+ if (this.config?.autoMount !== false && this.config?.component) {
1071
+ this.mount();
1072
+ if (this.config?.autoResize !== false) this.setupResizeHandler();
1073
+ } else console.warn("[PhaserJSX Plugin] Auto-mount disabled or no component configured");
1074
+ }
1075
+ /**
1076
+ * Setup resize event handler
1077
+ */
1078
+ setupResizeHandler() {
1079
+ if (!this.targetScene) return;
1080
+ this.targetScene.scale.on("resize", this.onResize, this);
1081
+ }
1082
+ /**
1083
+ * Handle scene resize - update component props
1084
+ */
1085
+ onResize(gameSize) {
1086
+ if (!this.mountHandle || !this.config?.component || !this.container) return;
1087
+ const props = this.config.props || {};
1088
+ const width = props.width ?? gameSize.width;
1089
+ const height = props.height ?? gameSize.height;
1090
+ this.mountHandle = mountJSX(this.container, this.config.component, {
1091
+ ...props,
1092
+ width,
1093
+ height
1094
+ });
1095
+ }
1096
+ /**
1097
+ * Configure plugin
1098
+ * Can be called from scene to set up component dynamically
1099
+ */
1100
+ configure(component, props) {
1101
+ const newConfig = { component };
1102
+ if (props !== void 0) newConfig.props = props;
1103
+ this.config = {
1104
+ ...this.config,
1105
+ ...newConfig
1106
+ };
1107
+ }
1108
+ /**
1109
+ * Mount JSX component
1110
+ */
1111
+ mount() {
1112
+ if (!this.targetScene) {
1113
+ console.warn("[PhaserJSX Plugin] No scene available for mounting");
1114
+ return;
1115
+ }
1116
+ if (!this.config?.component) {
1117
+ console.warn("[PhaserJSX Plugin] No component configured for mounting");
1118
+ return;
1119
+ }
1120
+ if (!this.container) {
1121
+ const containerConfig = this.config.container || {};
1122
+ this.container = this.targetScene.add.container(containerConfig.x ?? 0, containerConfig.y ?? 0);
1123
+ this.container.setDepth(containerConfig.depth ?? 100);
1124
+ }
1125
+ const props = this.config.props || {};
1126
+ const width = props.width ?? this.targetScene.scale.width;
1127
+ const height = props.height ?? this.targetScene.scale.height;
1128
+ this.mountHandle = mountJSX(this.container, this.config.component, {
1129
+ ...props,
1130
+ width,
1131
+ height
1132
+ });
1133
+ }
1134
+ /**
1135
+ * Unmount JSX component
1136
+ */
1137
+ unmount() {
1138
+ if (this.mountHandle) {
1139
+ this.mountHandle.unmount();
1140
+ this.mountHandle = void 0;
1141
+ }
1142
+ }
1143
+ /**
1144
+ * Destroy lifecycle - cleanup
1145
+ */
1146
+ destroy() {
1147
+ this.unmount();
1148
+ if (this.container) {
1149
+ this.container.destroy();
1150
+ this.container = void 0;
1151
+ }
1152
+ this.game.events.off("ready", this.onGameReady, this);
1153
+ if (this.targetScene) {
1154
+ this.targetScene.events.off("create", this.onSceneCreate, this);
1155
+ this.targetScene.scale.off("resize", this.onResize, this);
1156
+ }
1157
+ this.targetScene = void 0;
1158
+ this.config = void 0;
1159
+ super.destroy();
1160
+ }
1161
+ };
1162
+ //#endregion
1163
+ //#region src/colors/use-color-mode.ts
1164
+ /**
1165
+ * Color mode management for dynamic theme switching
1166
+ */
1167
+ /**
1168
+ * Hook to manage color mode (light/dark) dynamically
1169
+ * @returns Object with current mode, toggle function, and setter
1170
+ * @example
1171
+ * ```typescript
1172
+ * function ThemeToggle() {
1173
+ * const { colorMode, toggleColorMode } = useColorMode()
1174
+ *
1175
+ * return (
1176
+ * <Button onClick={toggleColorMode}>
1177
+ * {colorMode === 'light' ? 'Dark' : 'Light'} Mode
1178
+ * </Button>
1179
+ * )
1180
+ * }
1181
+ * ```
1182
+ */
2524
1183
  function useColorMode() {
2525
- const [colorMode, setColorModeState] = useState(themeRegistry.getColorMode());
2526
- useEffect(() => {
2527
- const unsubscribe = themeRegistry.subscribe(() => {
2528
- setColorModeState(themeRegistry.getColorMode());
2529
- });
2530
- return unsubscribe;
2531
- }, []);
2532
- const setColorMode2 = (mode) => {
2533
- themeRegistry.setColorMode(mode);
2534
- };
2535
- const toggleColorMode = () => {
2536
- const newMode = colorMode === "light" ? "dark" : "light";
2537
- setColorMode2(newMode);
2538
- };
2539
- return {
2540
- colorMode,
2541
- setColorMode: setColorMode2,
2542
- toggleColorMode
2543
- };
2544
- }
1184
+ const [colorMode, setColorModeState] = useState(themeRegistry.getColorMode());
1185
+ useEffect(() => {
1186
+ return themeRegistry.subscribe(() => {
1187
+ setColorModeState(themeRegistry.getColorMode());
1188
+ });
1189
+ }, []);
1190
+ const setColorMode = (mode) => {
1191
+ themeRegistry.setColorMode(mode);
1192
+ };
1193
+ const toggleColorMode = () => {
1194
+ setColorMode(colorMode === "light" ? "dark" : "light");
1195
+ };
1196
+ return {
1197
+ colorMode,
1198
+ setColorMode,
1199
+ toggleColorMode
1200
+ };
1201
+ }
1202
+ //#endregion
1203
+ //#region src/colors/use-colors.ts
1204
+ /**
1205
+ * Color system hooks for component usage
1206
+ */
1207
+ /**
1208
+ * Hook to access color tokens from theme context
1209
+ * Automatically updates when color mode or preset changes
1210
+ * @returns Current ColorTokens or undefined
1211
+ * @deprecated Use `useThemeTokens()` instead for access to colors, text styles, spacing, and more
1212
+ * @example
1213
+ * ```typescript
1214
+ * // Old way (deprecated):
1215
+ * const colors = useColors()
1216
+ *
1217
+ * // New way:
1218
+ * const tokens = useThemeTokens()
1219
+ * const colors = tokens?.colors
1220
+ *
1221
+ * // Access text styles, spacing, etc.:
1222
+ * <Text style={tokens.textStyles.DEFAULT} />
1223
+ * <View padding={tokens.spacing.lg} />
1224
+ * ```
1225
+ */
2545
1226
  function useColors() {
2546
- const localTheme = useTheme();
2547
- const getInitialColors = () => {
2548
- if (localTheme?.__colorPreset) {
2549
- const preset = getPresetWithMode(
2550
- localTheme.__colorPreset.name,
2551
- localTheme.__colorPreset.mode ?? "light"
2552
- );
2553
- return preset.colors;
2554
- }
2555
- return themeRegistry.getColorTokens();
2556
- };
2557
- const [colors, setColors] = useState(getInitialColors());
2558
- const [, forceUpdate] = useState(0);
2559
- useEffect(() => {
2560
- const unsubscribe = themeRegistry.subscribe(() => {
2561
- if (localTheme?.__colorPreset) {
2562
- const currentMode = themeRegistry.getColorMode();
2563
- const preset = getPresetWithMode(
2564
- localTheme.__colorPreset.name,
2565
- currentMode
2566
- );
2567
- setColors(preset.colors);
2568
- } else {
2569
- setColors(themeRegistry.getColorTokens());
2570
- }
2571
- forceUpdate((n2) => n2 + 1);
2572
- });
2573
- return unsubscribe;
2574
- }, [localTheme]);
2575
- return colors;
2576
- }
1227
+ const localTheme = useTheme();
1228
+ const getInitialColors = () => {
1229
+ if (localTheme?.__colorPreset) return getPresetWithMode(localTheme.__colorPreset.name, localTheme.__colorPreset.mode ?? "light").colors;
1230
+ return themeRegistry.getColorTokens();
1231
+ };
1232
+ const [colors, setColors] = useState(getInitialColors());
1233
+ const [, forceUpdate] = useState(0);
1234
+ useEffect(() => {
1235
+ return themeRegistry.subscribe(() => {
1236
+ if (localTheme?.__colorPreset) {
1237
+ const currentMode = themeRegistry.getColorMode();
1238
+ setColors(getPresetWithMode(localTheme.__colorPreset.name, currentMode).colors);
1239
+ } else setColors(themeRegistry.getColorTokens());
1240
+ forceUpdate((n) => n + 1);
1241
+ });
1242
+ }, [localTheme]);
1243
+ return colors;
1244
+ }
1245
+ /**
1246
+ * Hook to subscribe to theme changes without accessing colors
1247
+ * Use this in parent components that don't need colors themselves
1248
+ * but want to ensure children re-render when theme changes
1249
+ *
1250
+ * Note: Since useColors() now triggers re-renders automatically,
1251
+ * this hook is mainly useful if you don't need the colors themselves
1252
+ * but still want to react to theme changes.
1253
+ *
1254
+ * @example
1255
+ * ```typescript
1256
+ * function ParentComponent() {
1257
+ * useThemeSubscription() // Children will re-render on theme changes
1258
+ * return <ChildThatUsesColors />
1259
+ * }
1260
+ * ```
1261
+ */
2577
1262
  function useThemeSubscription() {
2578
- const [, forceUpdate] = useState(0);
2579
- useEffect(() => {
2580
- const unsubscribe = themeRegistry.subscribe(() => {
2581
- forceUpdate((n2) => n2 + 1);
2582
- });
2583
- return unsubscribe;
2584
- }, []);
2585
- }
1263
+ const [, forceUpdate] = useState(0);
1264
+ useEffect(() => {
1265
+ return themeRegistry.subscribe(() => {
1266
+ forceUpdate((n) => n + 1);
1267
+ });
1268
+ }, []);
1269
+ }
1270
+ //#endregion
1271
+ //#region src/colors/color-theme-helpers.ts
1272
+ /**
1273
+ * Convert ColorTokens to a theme-compatible object
1274
+ * Maps semantic color names to Phaser number format for View/Button components
1275
+ * @param colors - ColorTokens to convert
1276
+ * @returns Object with backgroundColor, borderColor, etc.
1277
+ * @example
1278
+ * ```typescript
1279
+ * const colors = getPreset('oceanBlue').colors
1280
+ * const buttonTheme = {
1281
+ * ...colorsToTheme(colors, 'primary'),
1282
+ * padding: 8,
1283
+ * }
1284
+ * // Returns: { backgroundColor: 0x2196f3, borderColor: 0x... }
1285
+ * ```
1286
+ */
2586
1287
  function colorsToTheme(colors, colorKey, options = {}) {
2587
- const { backgroundShade = "DEFAULT", borderShade = "dark", includeBorder = true } = options;
2588
- const result = {
2589
- backgroundColor: colors[colorKey][backgroundShade].toNumber()
2590
- };
2591
- if (includeBorder) {
2592
- result.borderColor = colors[colorKey][borderShade].toNumber();
2593
- }
2594
- return result;
2595
- }
1288
+ const { backgroundShade = "DEFAULT", borderShade = "dark", includeBorder = true } = options;
1289
+ const result = { backgroundColor: colors[colorKey][backgroundShade].toNumber() };
1290
+ if (includeBorder) result.borderColor = colors[colorKey][borderShade].toNumber();
1291
+ return result;
1292
+ }
1293
+ /**
1294
+ * Get text color from ColorTokens as hex string for Phaser Text style
1295
+ * @param colors - ColorTokens to use
1296
+ * @param shade - Which text shade to use (default: 'DEFAULT')
1297
+ * @param alphaValue - Optional alpha value (0-1)
1298
+ * @returns Hex string or rgba string for Text style
1299
+ * @example
1300
+ * ```typescript
1301
+ * const colors = getPreset('oceanBlue').colors
1302
+ * const textStyle = {
1303
+ * color: getTextColor(colors),
1304
+ * fontSize: '18px'
1305
+ * }
1306
+ * ```
1307
+ */
2596
1308
  function getTextColor(colors, shade = "DEFAULT", alphaValue) {
2597
- const color = colors.text[shade];
2598
- return alphaValue !== void 0 ? alpha(color.toNumber(), alphaValue) : color.toString();
2599
- }
1309
+ const color = colors.text[shade];
1310
+ return alphaValue !== void 0 ? alpha(color.toNumber(), alphaValue) : color.toString();
1311
+ }
1312
+ /**
1313
+ * Get background color from ColorTokens
1314
+ * @param colors - ColorTokens to use
1315
+ * @param shade - Which shade to use (default: 'DEFAULT')
1316
+ * @returns Phaser color number
1317
+ */
2600
1318
  function getBackgroundColor(colors, shade = "DEFAULT") {
2601
- return colors.background[shade].toNumber();
2602
- }
1319
+ return colors.background[shade].toNumber();
1320
+ }
1321
+ /**
1322
+ * Get surface color from ColorTokens
1323
+ * @param colors - ColorTokens to use
1324
+ * @param shade - Which shade to use (default: 'DEFAULT')
1325
+ * @returns Phaser color number
1326
+ */
2603
1327
  function getSurfaceColor(colors, shade = "DEFAULT") {
2604
- return colors.surface[shade].toNumber();
2605
- }
1328
+ return colors.surface[shade].toNumber();
1329
+ }
1330
+ /**
1331
+ * Get border color from ColorTokens
1332
+ * @param colors - ColorTokens to use
1333
+ * @param shade - Which shade to use (default: 'DEFAULT')
1334
+ * @returns Phaser color number
1335
+ */
2606
1336
  function getBorderColor(colors, shade = "DEFAULT") {
2607
- return colors.border[shade].toNumber();
2608
- }
1337
+ return colors.border[shade].toNumber();
1338
+ }
1339
+ //#endregion
1340
+ //#region src/colors/preset-manager.ts
1341
+ /**
1342
+ * Dynamic color preset management
1343
+ */
1344
+ /**
1345
+ * Set the active color preset globally
1346
+ * Applies the preset with the current color mode and updates all components
1347
+ * Triggers complete remount of all active mountJSX instances
1348
+ * @param presetName - Name of the preset to apply
1349
+ * @param colorMode - Optional color mode to apply together with the preset
1350
+ * @example
1351
+ * ```typescript
1352
+ * // Switch to forest green theme
1353
+ * setColorPreset('forestGreen')
1354
+ *
1355
+ * // Apply preset and force dark mode in one go
1356
+ * setColorPreset('midnight', 'dark')
1357
+ *
1358
+ * // Current mode (light/dark) is preserved
1359
+ * ```
1360
+ */
2609
1361
  function setColorPreset(presetName, colorMode) {
2610
- const targetMode = colorMode ?? themeRegistry.getColorMode();
2611
- const preset = getPresetWithMode(presetName, targetMode);
2612
- themeRegistry.setColorTokens(preset.colors);
2613
- themeRegistry.setCurrentPresetName(presetName, true);
2614
- if (colorMode && themeRegistry.getColorMode() !== colorMode) {
2615
- themeRegistry.setColorMode(colorMode);
2616
- return;
2617
- }
2618
- setTimeout(() => {
2619
- remountAll();
2620
- }, 0);
2621
- }
1362
+ const preset = getPresetWithMode(presetName, colorMode ?? themeRegistry.getColorMode());
1363
+ themeRegistry.setColorTokens(preset.colors);
1364
+ themeRegistry.setCurrentPresetName(presetName, true);
1365
+ if (colorMode && themeRegistry.getColorMode() !== colorMode) {
1366
+ themeRegistry.setColorMode(colorMode);
1367
+ return;
1368
+ }
1369
+ setTimeout(() => {
1370
+ remountAll();
1371
+ }, 0);
1372
+ }
1373
+ /**
1374
+ * Get the currently active preset name
1375
+ * @returns Current preset name or undefined
1376
+ */
2622
1377
  function getCurrentPreset() {
2623
- return themeRegistry.getCurrentPresetName();
1378
+ return themeRegistry.getCurrentPresetName();
2624
1379
  }
1380
+ /**
1381
+ * Set the active color mode globally (without changing the preset)
1382
+ * @param mode - Color mode to apply
1383
+ */
2625
1384
  function setColorMode(mode) {
2626
- themeRegistry.setColorMode(mode);
2627
- }
1385
+ themeRegistry.setColorMode(mode);
1386
+ }
1387
+ /**
1388
+ * Get all available preset names
1389
+ * @returns Array of preset names
1390
+ * @example
1391
+ * ```typescript
1392
+ * const presets = getAvailablePresets() // ['oceanBlue', 'forestGreen', 'midnight']
1393
+ * ```
1394
+ */
2628
1395
  function getAvailablePresets() {
2629
- return ["oceanBlue", "forestGreen", "midnight"];
2630
- }
1396
+ return [
1397
+ "oceanBlue",
1398
+ "forestGreen",
1399
+ "midnight"
1400
+ ];
1401
+ }
1402
+ //#endregion
1403
+ //#region src/design-tokens/use-theme-tokens.ts
1404
+ /**
1405
+ * Hook to access complete design token system
1406
+ * Combines colors with text styles, spacing, sizes, and radius tokens
1407
+ */
1408
+ /**
1409
+ * Hook to access complete design token system from theme context
1410
+ * Provides colors, text styles, spacing, sizes, and radius tokens
1411
+ * Automatically updates when color mode or preset changes
1412
+ * @returns Current DesignTokens or undefined
1413
+ * @example
1414
+ * ```typescript
1415
+ * function MyComponent() {
1416
+ * const tokens = useThemeTokens()
1417
+ *
1418
+ * if (!tokens) return null
1419
+ *
1420
+ * return (
1421
+ * <View
1422
+ * backgroundColor={tokens.colors.surface.DEFAULT}
1423
+ * padding={tokens.spacing.lg}
1424
+ * cornerRadius={tokens.radius.md}
1425
+ * >
1426
+ * <Text text="Title" style={tokens.textStyles.title} />
1427
+ * <Text text="Body text" style={tokens.textStyles.DEFAULT} />
1428
+ * </View>
1429
+ * )
1430
+ * }
1431
+ * ```
1432
+ */
2631
1433
  function useThemeTokens() {
2632
- const localTheme = useTheme();
2633
- const getInitialTokens = () => {
2634
- if (localTheme?.__colorPreset) {
2635
- const preset = getPresetWithMode(
2636
- localTheme.__colorPreset.name,
2637
- localTheme.__colorPreset.mode ?? "light"
2638
- );
2639
- return {
2640
- colors: preset.colors,
2641
- textStyles: createTextStyleTokens(preset.colors.text.DEFAULT.toString()),
2642
- spacing: defaultSpacingTokens,
2643
- sizes: defaultSizeTokens,
2644
- radius: defaultRadiusTokens
2645
- };
2646
- }
2647
- const colors = themeRegistry.getColorTokens();
2648
- if (!colors) return void 0;
2649
- return {
2650
- colors,
2651
- textStyles: createTextStyleTokens(colors.text.DEFAULT.toString()),
2652
- spacing: defaultSpacingTokens,
2653
- sizes: defaultSizeTokens,
2654
- radius: defaultRadiusTokens
2655
- };
2656
- };
2657
- const [tokens, setTokens] = useState(getInitialTokens());
2658
- const [, forceUpdate] = useState(0);
2659
- useEffect(() => {
2660
- const unsubscribe = themeRegistry.subscribe(() => {
2661
- if (localTheme?.__colorPreset) {
2662
- const currentMode = themeRegistry.getColorMode();
2663
- const preset = getPresetWithMode(
2664
- localTheme.__colorPreset.name,
2665
- currentMode
2666
- );
2667
- setTokens({
2668
- colors: preset.colors,
2669
- textStyles: createTextStyleTokens(preset.colors.text.DEFAULT.toString()),
2670
- spacing: defaultSpacingTokens,
2671
- sizes: defaultSizeTokens,
2672
- radius: defaultRadiusTokens
2673
- });
2674
- } else {
2675
- const colors = themeRegistry.getColorTokens();
2676
- if (colors) {
2677
- setTokens({
2678
- colors,
2679
- textStyles: createTextStyleTokens(colors.text.DEFAULT.toString()),
2680
- spacing: defaultSpacingTokens,
2681
- sizes: defaultSizeTokens,
2682
- radius: defaultRadiusTokens
2683
- });
2684
- }
2685
- }
2686
- forceUpdate((n2) => n2 + 1);
2687
- });
2688
- return unsubscribe;
2689
- }, [localTheme]);
2690
- return tokens;
2691
- }
1434
+ const localTheme = useTheme();
1435
+ const getInitialTokens = () => {
1436
+ if (localTheme?.__colorPreset) {
1437
+ const preset = getPresetWithMode(localTheme.__colorPreset.name, localTheme.__colorPreset.mode ?? "light");
1438
+ return {
1439
+ colors: preset.colors,
1440
+ textStyles: createTextStyleTokens(preset.colors.text.DEFAULT.toString()),
1441
+ spacing: defaultSpacingTokens,
1442
+ sizes: defaultSizeTokens,
1443
+ radius: defaultRadiusTokens
1444
+ };
1445
+ }
1446
+ const colors = themeRegistry.getColorTokens();
1447
+ if (!colors) return void 0;
1448
+ return {
1449
+ colors,
1450
+ textStyles: createTextStyleTokens(colors.text.DEFAULT.toString()),
1451
+ spacing: defaultSpacingTokens,
1452
+ sizes: defaultSizeTokens,
1453
+ radius: defaultRadiusTokens
1454
+ };
1455
+ };
1456
+ const [tokens, setTokens] = useState(getInitialTokens());
1457
+ const [, forceUpdate] = useState(0);
1458
+ useEffect(() => {
1459
+ return themeRegistry.subscribe(() => {
1460
+ if (localTheme?.__colorPreset) {
1461
+ const currentMode = themeRegistry.getColorMode();
1462
+ const preset = getPresetWithMode(localTheme.__colorPreset.name, currentMode);
1463
+ setTokens({
1464
+ colors: preset.colors,
1465
+ textStyles: createTextStyleTokens(preset.colors.text.DEFAULT.toString()),
1466
+ spacing: defaultSpacingTokens,
1467
+ sizes: defaultSizeTokens,
1468
+ radius: defaultRadiusTokens
1469
+ });
1470
+ } else {
1471
+ const colors = themeRegistry.getColorTokens();
1472
+ if (colors) setTokens({
1473
+ colors,
1474
+ textStyles: createTextStyleTokens(colors.text.DEFAULT.toString()),
1475
+ spacing: defaultSpacingTokens,
1476
+ sizes: defaultSizeTokens,
1477
+ radius: defaultRadiusTokens
1478
+ });
1479
+ }
1480
+ forceUpdate((n) => n + 1);
1481
+ });
1482
+ }, [localTheme]);
1483
+ return tokens;
1484
+ }
1485
+ //#endregion
1486
+ //#region src/index.ts
1487
+ /**
1488
+ * PhaserJSX UI Library
1489
+ * Provides JSX + hooks + VDOM for Phaser 3 game development
1490
+ */
2692
1491
  registerBuiltins();
2693
- export {
2694
- A as Accordion,
2695
- a as AlertDialog,
2696
- B as Button,
2697
- CAMERA_FX_REGISTRY,
2698
- C as CharText,
2699
- b as CharTextInput,
2700
- DEFAULT_BACKGROUND,
2701
- DEFAULT_CAMERA_FX,
2702
- af as DEFAULT_EFFECT,
2703
- DEFAULT_FX,
2704
- bj as DEFAULT_SPRING_CONFIG,
2705
- a7 as DOMInputElement,
2706
- DebugLogger,
2707
- ab as DevConfig,
2708
- ac as DevPresets,
2709
- D as Dialog,
2710
- c as Divider,
2711
- d as Dropdown,
2712
- ag as EFFECT_REGISTRY,
2713
- FX_REGISTRY,
2714
- Fragment,
2715
- bc as Graphics,
2716
- bo as HexColor,
2717
- I as Icon,
2718
- f as Image,
2719
- J as Joystick,
2720
- a8 as KeyboardInputManager,
2721
- M as Modal,
2722
- N as NineSlice,
2723
- g as NineSliceButton,
2724
- PARTICLE_PRESET_REGISTRY,
2725
- P as Particles,
2726
- PhaserJSXPlugin,
2727
- h as Portal,
2728
- R as RadioButton,
2729
- i as RadioGroup,
2730
- n as RangeSlider,
2731
- j as RefOriginView,
2732
- bk as SPRING_PRESETS,
2733
- S as ScrollSlider,
2734
- l as ScrollView,
2735
- m as Sidebar,
2736
- o as Slider,
2737
- bl as SpringPhysics,
2738
- Sprite,
2739
- T as Tab,
2740
- p as TabPanel,
2741
- q as Tabs,
2742
- bd as Text,
2743
- TileSprite,
2744
- r as Toggle,
2745
- s as TransformOriginView,
2746
- be as View,
2747
- W as WrapText,
2748
- addSceneBackground,
2749
- alpha,
2750
- bg as animatedSignal,
2751
- applyCameraFXByName,
2752
- bC as applyDarkMode,
2753
- ah as applyEffectByName,
2754
- applyFXByName,
2755
- bD as applyLightMode,
2756
- buildDeathZonesFromLayout,
2757
- buildEmitZone,
2758
- buildEmitZoneFromLayout,
2759
- k as calculateSliderSize,
2760
- colorsToTheme,
2761
- computed,
2762
- createBlurFX,
2763
- aj as createBounceEffect,
2764
- ak as createBreatheEffect,
2765
- createCameraFadeInFX,
2766
- createCameraFadeOutFX,
2767
- createCameraFlashFX,
2768
- createCameraShakeFX,
2769
- createCameraZoomFX,
2770
- createColorMatrixFX,
2771
- b2 as createDefaultTheme,
2772
- b7 as createElement,
2773
- al as createFadeEffect,
2774
- am as createFlashEffect,
2775
- an as createFlipInEffect,
2776
- ao as createFlipOutEffect,
2777
- ap as createFloatEffect,
2778
- createGlowFX,
2779
- e as createIconComponent,
2780
- aq as createJelloEffect,
2781
- ar as createNoneEffect,
2782
- createPhaserJSXPlugin,
2783
- createPixelateFX,
2784
- as as createPressEffect,
2785
- at as createPulseEffect,
2786
- createShadowFX,
2787
- au as createShakeEffect,
2788
- av as createSlideInEffect,
2789
- aw as createSlideOutEffect,
2790
- ax as createSpinEffect,
2791
- ay as createSwingEffect,
2792
- az as createTadaEffect,
2793
- bz as createTextStyle,
2794
- createTextStyleTokens,
2795
- b5 as createTheme,
2796
- createVignetteFX,
2797
- aA as createWiggleEffect,
2798
- aB as createWobbleEffect,
2799
- aC as createZoomInEffect,
2800
- aD as createZoomOutEffect,
2801
- bu as darken,
2802
- bv as darkenHex,
2803
- defaultRadiusTokens,
2804
- defaultSizeTokens,
2805
- defaultSpacingTokens,
2806
- bK as defaultTextStyleTokens,
2807
- b3 as defaultTheme,
2808
- aW as disposeCtx,
2809
- bA as ensureContrast,
2810
- bE as forestGreenPreset,
2811
- bF as generateColorScale,
2812
- getAvailablePresets,
2813
- getBackgroundColor,
2814
- aM as getBackgroundGraphics,
2815
- getBorderColor,
2816
- bB as getContrastRatio,
2817
- aE as getCurrent,
2818
- getCurrentPreset,
2819
- aL as getLayoutProps,
2820
- aO as getLayoutRect,
2821
- aJ as getLayoutSize,
2822
- a9 as getMountStats,
2823
- bG as getPreset,
2824
- getPresetWithMode,
2825
- aa as getRenderContext,
2826
- getSurfaceColor,
2827
- getTextColor,
2828
- getThemedProps,
2829
- aQ as getWorldLayoutRect,
2830
- graphicsCreator,
2831
- graphicsPatcher,
2832
- bw as hex,
2833
- bp as hexToNumber,
2834
- b1 as host,
2835
- imageCreator,
2836
- imagePatcher,
2837
- bh as isAnimatedSignal,
2838
- jsx,
2839
- jsxs,
2840
- bx as lighten,
2841
- by as lightenHex,
2842
- memo,
2843
- b4 as mergeThemes,
2844
- bH as midnightPreset,
2845
- b8 as mount,
2846
- mountJSX as mountComponent,
2847
- mountJSX,
2848
- nineSliceCreator,
2849
- nineSlicePatcher,
2850
- noMemo,
2851
- b0 as nodeRegistry,
2852
- a5 as normalizeCornerRadius,
2853
- a6 as normalizeEdgeInsets,
2854
- normalizeGap,
2855
- b6 as normalizeVNodeLike,
2856
- bq as numberToHex,
2857
- br as numberToRgb,
2858
- bI as oceanBluePreset,
2859
- particlesCreator,
2860
- particlesPatcher,
2861
- ba as patchVNode,
2862
- bf as portalRegistry,
2863
- bJ as presets,
2864
- register,
2865
- registerBuiltins,
2866
- aZ as releaseAllSVGTextures,
2867
- aX as releaseSVGTexture,
2868
- aY as releaseSVGTextures,
2869
- remountAll,
2870
- resolveCameraFX,
2871
- ai as resolveEffect,
2872
- resolveFX,
2873
- resolveParticlePreset,
2874
- bs as rgbToHsl,
2875
- bt as rgbToNumber,
2876
- setColorMode,
2877
- setColorPreset,
2878
- aT as shallowEqual,
2879
- aU as shouldComponentUpdate,
2880
- spriteCreator,
2881
- spritePatcher,
2882
- ad as svgToTexture,
2883
- textCreator,
2884
- textPatcher,
2885
- themeRegistry,
2886
- tileSpriteCreator,
2887
- tileSpritePatcher,
2888
- b9 as unmount,
2889
- bb as unmountJSX,
2890
- bi as unwrapSignal,
2891
- aN as useBackgroundGraphics,
2892
- useBlur,
2893
- useCallback,
2894
- useCameraFX,
2895
- useCameraFade,
2896
- useCameraFlash,
2897
- useCameraZoom,
2898
- useColorMode,
2899
- useColors,
2900
- useEffect,
2901
- useFX,
2902
- aG as useForceRedraw,
2903
- ae as useGameObjectEffect,
2904
- useGlow,
2905
- u as useIconPreload,
2906
- aS as useLayoutEffect,
2907
- aP as useLayoutRect,
2908
- aK as useLayoutSize,
2909
- aH as useMemo,
2910
- useParticles,
2911
- aV as useRedraw,
2912
- useRef,
2913
- a_ as useSVGTexture,
2914
- a$ as useSVGTextures,
2915
- useScene,
2916
- useScreenShake,
2917
- useShadow,
2918
- bm as useSpring,
2919
- bn as useSprings,
2920
- useState,
2921
- useTheme,
2922
- useThemeSubscription,
2923
- useThemeTokens,
2924
- aI as useViewportSize,
2925
- aR as useWorldLayoutRect,
2926
- viewCreator,
2927
- viewPatcher,
2928
- viewportRegistry,
2929
- aF as withHooks
2930
- };
2931
- //# sourceMappingURL=index.js.map
1492
+ //#endregion
1493
+ export { Accordion, AlertDialog, Button, CAMERA_FX_REGISTRY, CharText, CharTextInput, DEFAULT_BACKGROUND, DEFAULT_CAMERA_FX, DEFAULT_EFFECT, DEFAULT_FX, DEFAULT_SPRING_CONFIG, DOMInputElement, DebugLogger, DevConfig, DevPresets, Dialog, Divider, Dropdown, EFFECT_REGISTRY, FX_REGISTRY, Fragment, Graphics, HexColor, Icon, Image, Joystick, KeyboardInputManager, Modal, NineSlice, NineSliceButton, PARTICLE_PRESET_REGISTRY, Particles, PhaserJSXPlugin, Portal, RadioButton, RadioGroup, RangeSlider, RefOriginView, SPRING_PRESETS, ScrollSlider, ScrollView, Sidebar, Slider, SpringPhysics, Sprite, Tab, TabPanel, Tabs, Text, TileSprite, Toggle, TransformOriginView, View, WrapText, addSceneBackground, alpha, animatedSignal, applyCameraFXByName, applyDarkMode, applyEffectByName, applyFXByName, applyLightMode, applyStencilClip, buildDeathZonesFromLayout, buildEmitZone, buildEmitZoneFromLayout, calculateSliderSize, clearStencilClip, colorsToTheme, computed, createBlurFX, createBounceEffect, createBreatheEffect, createCameraFadeInFX, createCameraFadeOutFX, createCameraFlashFX, createCameraShakeFX, createCameraZoomFX, createColorMatrixFX, createDefaultTheme, createElement, createFadeEffect, createFlashEffect, createFlipInEffect, createFlipOutEffect, createFloatEffect, createGlowFX, createIconComponent, createJelloEffect, createNoneEffect, createPhaserJSXPlugin, createPixelateFX, createPressEffect, createPulseEffect, createShadowFX, createShakeEffect, createSlideInEffect, createSlideOutEffect, createSpinEffect, createSwingEffect, createTadaEffect, createTextStyle, createTextStyleTokens, createTheme, createVignetteFX, createWiggleEffect, createWobbleEffect, createZoomInEffect, createZoomOutEffect, darken, darkenHex, defaultRadiusTokens, defaultSizeTokens, defaultSpacingTokens, defaultTextStyleTokens, defaultTheme, disposeCtx, ensureContrast, forestGreenPreset, generateColorScale, getAvailablePresets, getBackgroundColor, getBackgroundGraphics, getBorderColor, getContrastRatio, getCurrent, getCurrentPreset, getLayoutProps, getLayoutRect, getLayoutSize, getMountStats, getPreset, getPresetWithMode, getRenderContext, getStencilClipHandle, getSurfaceColor, getTextColor, getThemedProps, getWorldLayoutRect, graphicsCreator, graphicsPatcher, hex, hexToNumber, host, imageCreator, imagePatcher, installStencilClipExtension, isAnimatedSignal, isBitmapStencilClipSource, jsx, jsxs, lighten, lightenHex, memo, mergeThemes, midnightPreset, mount, mountJSX as mountComponent, mountJSX, nineSliceCreator, nineSlicePatcher, noMemo, nodeRegistry, normalizeCornerRadius, normalizeEdgeInsets, normalizeGap, normalizeVNodeLike, numberToHex, numberToRgb, oceanBluePreset, particlesCreator, particlesPatcher, patchVNode, portalRegistry, presets, register, registerBuiltins, releaseAllSVGTextures, releaseSVGTexture, releaseSVGTextures, remountAll, resolveCameraFX, resolveEffect, resolveFX, resolveParticlePreset, rgbToHsl, rgbToNumber, setColorMode, setColorPreset, shallowEqual, shouldComponentUpdate, spriteCreator, spritePatcher, svgToTexture, textCreator, textPatcher, themeRegistry, tileSpriteCreator, tileSpritePatcher, uninstallStencilClipExtension, unmount, unmountJSX, unwrapSignal, useBackgroundGraphics, useBlur, useCallback, useCameraFX, useCameraFade, useCameraFlash, useCameraZoom, useColorMode, useColors, useEffect, useFX, useForceRedraw, useGameObjectEffect, useGlow, useIconPreload, useLayoutEffect, useLayoutRect, useLayoutSize, useMemo, useParticles, useRedraw, useRef, useSVGTexture, useSVGTextures, useScene, useScreenShake, useShadow, useSpring, useSprings, useState, useTheme, useThemeSubscription, useThemeTokens, useViewportSize, useWorldLayoutRect, viewCreator, viewPatcher, viewportRegistry, withHooks };
1494
+
1495
+ //# sourceMappingURL=index.js.map