@number10/phaserjsx 4.3.0 → 4.4.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 (49) hide show
  1. package/README.md +3 -2
  2. package/dist/colors/preset-manager.d.ts.map +1 -1
  3. package/dist/components/backgroundImage.d.ts.map +1 -1
  4. package/dist/components/custom/Badge.d.ts.map +1 -1
  5. package/dist/components/custom/Button.d.ts +23 -2
  6. package/dist/components/custom/Button.d.ts.map +1 -1
  7. package/dist/components/custom/CharTextInput.d.ts +10 -0
  8. package/dist/components/custom/CharTextInput.d.ts.map +1 -1
  9. package/dist/components/custom/Checkbox.d.ts.map +1 -1
  10. package/dist/components/custom/Dropdown.d.ts +1 -1
  11. package/dist/components/custom/Dropdown.d.ts.map +1 -1
  12. package/dist/components/custom/Popover.d.ts +9 -0
  13. package/dist/components/custom/Popover.d.ts.map +1 -1
  14. package/dist/components/custom/ProgressBar.d.ts.map +1 -1
  15. package/dist/components/custom/RadioButton.d.ts +8 -0
  16. package/dist/components/custom/RadioButton.d.ts.map +1 -1
  17. package/dist/components/custom/RadioGroup.d.ts +4 -0
  18. package/dist/components/custom/RadioGroup.d.ts.map +1 -1
  19. package/dist/components/custom/ScrollSlider.d.ts +7 -1
  20. package/dist/components/custom/ScrollSlider.d.ts.map +1 -1
  21. package/dist/components/custom/ScrollView.d.ts.map +1 -1
  22. package/dist/components/custom/Toggle.d.ts.map +1 -1
  23. package/dist/components/custom/index.cjs +1 -1
  24. package/dist/components/custom/index.js +1 -1
  25. package/dist/components/custom/useOverlayPresence.d.ts +9 -0
  26. package/dist/components/custom/useOverlayPresence.d.ts.map +1 -0
  27. package/dist/{custom-DMZASXll.js → custom-BPY0TbuS.js} +927 -398
  28. package/dist/custom-BPY0TbuS.js.map +1 -0
  29. package/dist/{custom-37gL0VZG.cjs → custom-Bnit70lx.cjs} +926 -397
  30. package/dist/custom-Bnit70lx.cjs.map +1 -0
  31. package/dist/index.cjs +4 -2
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.js +4 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/jsx-runtime.cjs +4 -1
  36. package/dist/jsx-runtime.cjs.map +1 -1
  37. package/dist/jsx-runtime.d.ts.map +1 -1
  38. package/dist/jsx-runtime.js +4 -1
  39. package/dist/jsx-runtime.js.map +1 -1
  40. package/dist/layout/utils/child-utils.d.ts.map +1 -1
  41. package/dist/layout/utils/dimension-calculator.d.ts.map +1 -1
  42. package/dist/theme-custom.d.ts +55 -8
  43. package/dist/theme-custom.d.ts.map +1 -1
  44. package/dist/theme-defaults.d.ts.map +1 -1
  45. package/dist/theme.d.ts.map +1 -1
  46. package/dist/vdom.d.ts.map +1 -1
  47. package/package.json +1 -1
  48. package/dist/custom-37gL0VZG.cjs.map +0 -1
  49. package/dist/custom-DMZASXll.js.map +0 -1
@@ -16761,6 +16761,23 @@ function hasRenderableBackground(props) {
16761
16761
  const hasBorder = (props.borderWidth ?? 0) > 0 && props.borderColor !== void 0;
16762
16762
  return hasBackground || hasBorder;
16763
16763
  }
16764
+ function hasRoundedCorners(radius) {
16765
+ if (typeof radius === "number") return radius !== 0;
16766
+ return (radius.tl ?? 0) !== 0 || (radius.tr ?? 0) !== 0 || (radius.bl ?? 0) !== 0 || (radius.br ?? 0) !== 0;
16767
+ }
16768
+ function insetCornerRadius(radius, inset) {
16769
+ const insetSingleRadius = (value) => {
16770
+ const resolved = value ?? 0;
16771
+ return (resolved < 0 ? -1 : 1) * Math.max(0, Math.abs(resolved) - inset);
16772
+ };
16773
+ if (typeof radius === "number") return insetSingleRadius(radius);
16774
+ return {
16775
+ tl: insetSingleRadius(radius.tl),
16776
+ tr: insetSingleRadius(radius.tr),
16777
+ bl: insetSingleRadius(radius.bl),
16778
+ br: insetSingleRadius(radius.br)
16779
+ };
16780
+ }
16764
16781
  function drawBackground(graphics, props, width, height) {
16765
16782
  const bgColor = props.backgroundColor;
16766
16783
  const bgAlpha = props.backgroundAlpha ?? 1;
@@ -16769,14 +16786,21 @@ function drawBackground(graphics, props, width, height) {
16769
16786
  const borderWidth = props.borderWidth ?? 0;
16770
16787
  const borderAlpha = props.borderAlpha ?? 1;
16771
16788
  const hasBorder = borderWidth > 0 && borderColor !== void 0;
16789
+ const rounded = hasRoundedCorners(cornerRadius);
16772
16790
  if (bgColor !== void 0) graphics.fillStyle(bgColor, bgAlpha);
16773
16791
  if (hasBorder) graphics.lineStyle(borderWidth, borderColor, borderAlpha);
16774
- if (cornerRadius !== 0) {
16792
+ if (rounded) {
16775
16793
  if (bgColor !== void 0) graphics.fillRoundedRect(0, 0, width, height, cornerRadius);
16776
- if (hasBorder) graphics.strokeRoundedRect(0, 0, width, height, cornerRadius);
16794
+ if (hasBorder) {
16795
+ const inset = borderWidth / 2;
16796
+ graphics.strokeRoundedRect(inset, inset, Math.max(0, width - borderWidth), Math.max(0, height - borderWidth), insetCornerRadius(cornerRadius, inset));
16797
+ }
16777
16798
  } else {
16778
16799
  if (bgColor !== void 0) graphics.fillRect(0, 0, width, height);
16779
- if (hasBorder) graphics.strokeRect(0, 0, width, height);
16800
+ if (hasBorder) {
16801
+ const inset = borderWidth / 2;
16802
+ graphics.strokeRect(inset, inset, Math.max(0, width - borderWidth), Math.max(0, height - borderWidth));
16803
+ }
16780
16804
  }
16781
16805
  }
16782
16806
  function getSceneBackgroundTextureCache(scene) {
@@ -18288,6 +18312,12 @@ function getMargin(child) {
18288
18312
  };
18289
18313
  return margin ?? {};
18290
18314
  }
18315
+ function getHorizontalMargin$1(margin) {
18316
+ return (margin.left ?? 0) + (margin.right ?? 0);
18317
+ }
18318
+ function getVerticalMargin$1(margin) {
18319
+ return (margin.top ?? 0) + (margin.bottom ?? 0);
18320
+ }
18291
18321
  /**
18292
18322
  * Get effective size of a child
18293
18323
  * Calls __getLayoutSize if available, otherwise falls back to layoutProps or default
@@ -18301,8 +18331,13 @@ function getChildSize(child, parentSize, parentPadding) {
18301
18331
  if (child.__layoutProps && (child.__layoutProps.width !== void 0 || child.__layoutProps.height !== void 0)) {
18302
18332
  const layoutWidth = child.__layoutProps.width;
18303
18333
  const layoutHeight = child.__layoutProps.height;
18304
- let width = resolveSize(parseSize(layoutWidth), parentSize?.width, child.width ?? 100, parentPadding?.horizontal);
18305
- let height = resolveSize(parseSize(layoutHeight), parentSize?.height, child.height ?? 20, parentPadding?.vertical);
18334
+ const margin = getMargin(child);
18335
+ const parsedWidth = parseSize(layoutWidth);
18336
+ let width = resolveSize(parsedWidth, parentSize?.width, child.width ?? 100, parentPadding?.horizontal);
18337
+ if (parsedWidth.type === "fill") width = Math.max(0, width - getHorizontalMargin$1(margin));
18338
+ const parsedHeight = parseSize(layoutHeight);
18339
+ let height = resolveSize(parsedHeight, parentSize?.height, child.height ?? 20, parentPadding?.vertical);
18340
+ if (parsedHeight.type === "fill") height = Math.max(0, height - getVerticalMargin$1(margin));
18306
18341
  const { minWidth, maxWidth, minHeight, maxHeight } = child.__layoutProps;
18307
18342
  width = clampSize(width, minWidth, maxWidth, parentSize?.width, child.width, parentPadding?.horizontal);
18308
18343
  height = clampSize(height, minHeight, maxHeight, parentSize?.height, child.height, parentPadding?.vertical);
@@ -18350,6 +18385,21 @@ function processNestedContainer(child, calculateLayoutFn, parentSize, parentPadd
18350
18385
  }
18351
18386
  //#endregion
18352
18387
  //#region src/layout/utils/dimension-calculator.ts
18388
+ function normalizeMargin(margin) {
18389
+ if (typeof margin === "number") return {
18390
+ left: margin,
18391
+ top: margin,
18392
+ right: margin,
18393
+ bottom: margin
18394
+ };
18395
+ return margin ?? {};
18396
+ }
18397
+ function getHorizontalMargin(margin) {
18398
+ return (margin.left ?? 0) + (margin.right ?? 0);
18399
+ }
18400
+ function getVerticalMargin(margin) {
18401
+ return (margin.top ?? 0) + (margin.bottom ?? 0);
18402
+ }
18353
18403
  /**
18354
18404
  * Calculate final container dimensions
18355
18405
  * @param props - Layout properties
@@ -18370,8 +18420,13 @@ function calculateContainerSize(props, metrics, padding, direction, gap, childCo
18370
18420
  }
18371
18421
  const contentWidth = direction === "row" ? totalMainSizeWithGaps + padding.left + padding.right : metrics.maxWidth + padding.left + padding.right;
18372
18422
  const contentHeight = direction === "row" ? metrics.maxHeight + padding.top + padding.bottom : totalMainSizeWithGaps + padding.top + padding.bottom;
18373
- let width = resolveSize(parseSize(props.width), parentSize?.width, contentWidth, parentPadding?.horizontal);
18374
- let height = resolveSize(parseSize(props.height), parentSize?.height, contentHeight, parentPadding?.vertical);
18423
+ const parsedWidth = parseSize(props.width);
18424
+ let width = resolveSize(parsedWidth, parentSize?.width, contentWidth, parentPadding?.horizontal);
18425
+ const margin = normalizeMargin(props.margin);
18426
+ if (parsedWidth.type === "fill") width = Math.max(0, width - getHorizontalMargin(margin));
18427
+ const parsedHeight = parseSize(props.height);
18428
+ let height = resolveSize(parsedHeight, parentSize?.height, contentHeight, parentPadding?.vertical);
18429
+ if (parsedHeight.type === "fill") height = Math.max(0, height - getVerticalMargin(margin));
18375
18430
  width = clampSize(width, props.minWidth, props.maxWidth, parentSize?.width, contentWidth, void 0);
18376
18431
  height = clampSize(height, props.minHeight, props.maxHeight, parentSize?.height, contentHeight, void 0);
18377
18432
  return {
@@ -19986,10 +20041,13 @@ function buildDefaultTheme(colors) {
19986
20041
  },
19987
20042
  RadioButton: {
19988
20043
  selectedColor: colors.primary.DEFAULT.toNumber(),
19989
- color: colors.border.medium.toNumber(),
19990
- gap: 10,
19991
- size: 16,
19992
- innerSize: 16,
20044
+ color: colors.border.dark.toNumber(),
20045
+ borderWidth: 2,
20046
+ gap: 8,
20047
+ size: 18,
20048
+ innerSize: 8,
20049
+ disabledAlpha: .5,
20050
+ labelPosition: "right",
19993
20051
  labelStyle: {
19994
20052
  color: colors.text.DEFAULT.toString(),
19995
20053
  fontSize: "14px"
@@ -20014,7 +20072,7 @@ function buildDefaultTheme(colors) {
20014
20072
  size: "medium",
20015
20073
  maxCount: 99,
20016
20074
  disabledAlpha: .5,
20017
- textStyle: { fontStyle: "bold" }
20075
+ textStyle: { fontFamily: "Arial" }
20018
20076
  },
20019
20077
  Tag: {
20020
20078
  tone: "neutral",
@@ -20022,7 +20080,7 @@ function buildDefaultTheme(colors) {
20022
20080
  size: "medium",
20023
20081
  closeSize: 16,
20024
20082
  disabledAlpha: .5,
20025
- textStyle: { fontStyle: "bold" }
20083
+ textStyle: { fontFamily: "Arial" }
20026
20084
  },
20027
20085
  Popover: {
20028
20086
  placement: "bottom",
@@ -20031,6 +20089,8 @@ function buildDefaultTheme(colors) {
20031
20089
  closeOnOutside: true,
20032
20090
  closeOnEscape: true,
20033
20091
  viewportPadding: 8,
20092
+ openDuration: 120,
20093
+ closeDuration: 100,
20034
20094
  backgroundColor: colors.surface.dark.toNumber(),
20035
20095
  borderColor: colors.border.medium.toNumber(),
20036
20096
  borderWidth: 1,
@@ -20081,65 +20141,226 @@ function buildDefaultTheme(colors) {
20081
20141
  }
20082
20142
  },
20083
20143
  ScrollSlider: {
20084
- borderColor: colors.border.dark.toNumber(),
20144
+ borderColor: colors.border.medium.toNumber(),
20085
20145
  trackColor: colors.surface.dark.toNumber(),
20086
- thumbColor: colors.primary.dark.toNumber(),
20087
- borderWidth: 2,
20146
+ trackCornerRadius: 0,
20147
+ thumbColor: colors.primary.DEFAULT.toNumber(),
20148
+ thumbActiveColor: colors.primary.dark.toNumber(),
20149
+ thumbBorderColor: colors.primary.light.toNumber(),
20150
+ thumbBorderWidth: 1,
20151
+ thumbCornerRadius: 0,
20152
+ borderWidth: 1,
20153
+ cornerRadius: 0,
20088
20154
  minThumbSize: 30,
20089
20155
  size: 24
20090
20156
  },
20091
20157
  Button: {
20092
- disabledColor: colors.background.DEFAULT.toNumber(),
20093
- iconSize: 24,
20094
- backgroundColor: colors.primary.DEFAULT.toNumber(),
20158
+ variant: "primary",
20159
+ size: "medium",
20160
+ disabledColor: colors.surface.medium.toNumber(),
20161
+ disabledAlpha: .48,
20162
+ iconSize: 18,
20163
+ minWidth: 92,
20164
+ height: 40,
20165
+ backgroundColor: colors.primary.medium.toNumber(),
20095
20166
  backgroundAlpha: 1,
20096
20167
  borderColor: colors.primary.dark.toNumber(),
20097
20168
  borderWidth: 1,
20098
- cornerRadius: 6,
20099
- padding: 8,
20169
+ cornerRadius: 8,
20170
+ padding: {
20171
+ top: 8,
20172
+ bottom: 8,
20173
+ left: 16,
20174
+ right: 16
20175
+ },
20100
20176
  gap: 8,
20101
20177
  justifyContent: "center",
20102
20178
  alignItems: "center",
20179
+ effect: "press",
20180
+ effectConfig: {
20181
+ intensity: .94,
20182
+ time: 140
20183
+ },
20184
+ textStyle: {
20185
+ ...textStyles.medium,
20186
+ color: "#ffffff",
20187
+ fontStyle: "bold"
20188
+ },
20189
+ Text: { style: {
20190
+ ...textStyles.medium,
20191
+ color: "#ffffff",
20192
+ fontStyle: "bold"
20193
+ } },
20194
+ Icon: {
20195
+ size: 18,
20196
+ tint: 16777215
20197
+ },
20103
20198
  primary: {
20104
20199
  backgroundColor: colors.primary.medium.toNumber(),
20105
- borderColor: colors.primary.dark.toNumber()
20200
+ borderColor: colors.primary.dark.toNumber(),
20201
+ textStyle: {
20202
+ color: "#ffffff",
20203
+ fontStyle: "bold"
20204
+ },
20205
+ Icon: { tint: 16777215 }
20106
20206
  },
20107
20207
  secondary: {
20108
- backgroundColor: colors.secondary.DEFAULT.toNumber(),
20109
- borderColor: colors.secondary.dark.toNumber(),
20110
- effect: "press",
20111
- effectConfig: {
20112
- intensity: .9,
20113
- time: 200
20114
- }
20208
+ backgroundColor: colors.surface.lightest.toNumber(),
20209
+ backgroundAlpha: .92,
20210
+ borderColor: colors.border.light.toNumber(),
20211
+ borderWidth: 1,
20212
+ textStyle: {
20213
+ color: colors.text.dark.toString(),
20214
+ fontStyle: "bold"
20215
+ },
20216
+ Icon: { tint: colors.text.dark.toNumber() }
20115
20217
  },
20116
20218
  outline: {
20117
- backgroundColor: 0,
20118
- backgroundAlpha: 0,
20119
- borderColor: colors.accent.DEFAULT.toNumber(),
20219
+ backgroundColor: colors.primary.dark.toNumber(),
20220
+ backgroundAlpha: .08,
20221
+ borderColor: colors.primary.light.toNumber(),
20120
20222
  borderWidth: 2,
20121
- effect: "flash",
20122
- effectConfig: {
20123
- intensity: 1.15,
20124
- time: 200
20223
+ textStyle: {
20224
+ color: colors.primary.light.toString(),
20225
+ fontStyle: "bold"
20226
+ },
20227
+ Icon: { tint: colors.primary.light.toNumber() }
20228
+ },
20229
+ ghost: {
20230
+ backgroundColor: colors.surface.light.toNumber(),
20231
+ backgroundAlpha: .18,
20232
+ borderColor: colors.border.medium.toNumber(),
20233
+ borderAlpha: .35,
20234
+ borderWidth: 1,
20235
+ textStyle: {
20236
+ color: colors.text.light.toString(),
20237
+ fontStyle: "bold"
20238
+ },
20239
+ Icon: { tint: colors.text.light.toNumber() }
20240
+ },
20241
+ danger: {
20242
+ backgroundColor: colors.error.medium.toNumber(),
20243
+ borderColor: colors.error.dark.toNumber(),
20244
+ textStyle: {
20245
+ color: "#ffffff",
20246
+ fontStyle: "bold"
20247
+ },
20248
+ Icon: { tint: 16777215 }
20249
+ },
20250
+ variants: {
20251
+ primary: {
20252
+ backgroundColor: colors.primary.medium.toNumber(),
20253
+ borderColor: colors.primary.dark.toNumber()
20254
+ },
20255
+ secondary: {
20256
+ backgroundColor: colors.surface.lightest.toNumber(),
20257
+ backgroundAlpha: .92,
20258
+ borderColor: colors.border.light.toNumber(),
20259
+ borderWidth: 1
20260
+ },
20261
+ outline: {
20262
+ backgroundColor: colors.primary.dark.toNumber(),
20263
+ backgroundAlpha: .08,
20264
+ borderColor: colors.primary.light.toNumber(),
20265
+ borderWidth: 2
20266
+ },
20267
+ ghost: {
20268
+ backgroundColor: colors.surface.light.toNumber(),
20269
+ backgroundAlpha: .18,
20270
+ borderColor: colors.border.medium.toNumber(),
20271
+ borderAlpha: .35,
20272
+ borderWidth: 1
20273
+ },
20274
+ danger: {
20275
+ backgroundColor: colors.error.medium.toNumber(),
20276
+ borderColor: colors.error.dark.toNumber()
20125
20277
  }
20126
20278
  },
20127
20279
  small: {
20128
- padding: 6,
20129
- cornerRadius: 4
20280
+ minWidth: 72,
20281
+ height: 32,
20282
+ padding: {
20283
+ top: 6,
20284
+ bottom: 6,
20285
+ left: 12,
20286
+ right: 12
20287
+ },
20288
+ cornerRadius: 7,
20289
+ gap: 6,
20290
+ iconSize: 15,
20291
+ textStyle: { fontSize: "12px" },
20292
+ Icon: { size: 15 }
20130
20293
  },
20131
20294
  medium: {
20132
- padding: 8,
20133
- cornerRadius: 6
20295
+ minWidth: 92,
20296
+ height: 40,
20297
+ padding: {
20298
+ top: 8,
20299
+ bottom: 8,
20300
+ left: 16,
20301
+ right: 16
20302
+ },
20303
+ cornerRadius: 8,
20304
+ gap: 8,
20305
+ iconSize: 18,
20306
+ Icon: { size: 18 }
20134
20307
  },
20135
20308
  large: {
20309
+ minWidth: 116,
20310
+ height: 48,
20136
20311
  padding: {
20137
- top: 12,
20138
- bottom: 12,
20139
- left: 8,
20140
- right: 8
20312
+ top: 10,
20313
+ bottom: 10,
20314
+ left: 20,
20315
+ right: 20
20141
20316
  },
20142
- cornerRadius: 8
20317
+ cornerRadius: 10,
20318
+ gap: 10,
20319
+ iconSize: 22,
20320
+ textStyle: { fontSize: "18px" },
20321
+ Icon: { size: 22 }
20322
+ },
20323
+ sizes: {
20324
+ small: {
20325
+ minWidth: 72,
20326
+ height: 32,
20327
+ padding: {
20328
+ top: 6,
20329
+ bottom: 6,
20330
+ left: 12,
20331
+ right: 12
20332
+ },
20333
+ cornerRadius: 7,
20334
+ gap: 6,
20335
+ iconSize: 15
20336
+ },
20337
+ medium: {
20338
+ minWidth: 92,
20339
+ height: 40,
20340
+ padding: {
20341
+ top: 8,
20342
+ bottom: 8,
20343
+ left: 16,
20344
+ right: 16
20345
+ },
20346
+ cornerRadius: 8,
20347
+ gap: 8,
20348
+ iconSize: 18
20349
+ },
20350
+ large: {
20351
+ minWidth: 116,
20352
+ height: 48,
20353
+ padding: {
20354
+ top: 10,
20355
+ bottom: 10,
20356
+ left: 20,
20357
+ right: 20
20358
+ },
20359
+ cornerRadius: 10,
20360
+ gap: 10,
20361
+ iconSize: 22,
20362
+ textStyle: { fontSize: "18px" }
20363
+ }
20143
20364
  }
20144
20365
  },
20145
20366
  Sidebar: {
@@ -20371,64 +20592,110 @@ function buildDefaultTheme(colors) {
20371
20592
  lineHeight: 1.2,
20372
20593
  wordWrap: false,
20373
20594
  disabledColor: colors.border.dark.toNumber(),
20374
- focusedBorderColor: colors.accent.lightest.toNumber(),
20375
- backgroundColor: colors.surface.medium.toNumber(),
20595
+ disabledBackgroundColor: colors.surface.medium.toNumber(),
20596
+ disabledBorderColor: colors.border.light.toNumber(),
20597
+ disabledAlpha: .6,
20598
+ focusedBorderColor: colors.primary.DEFAULT.toNumber(),
20599
+ backgroundColor: colors.surface.lightest.toNumber(),
20376
20600
  borderColor: colors.border.medium.toNumber(),
20377
20601
  borderWidth: 1,
20378
- padding: 8,
20379
- textStyle: textStyles.DEFAULT
20602
+ cornerRadius: 7,
20603
+ padding: {
20604
+ left: 10,
20605
+ right: 10,
20606
+ top: 8,
20607
+ bottom: 8
20608
+ },
20609
+ textStyle: {
20610
+ ...textStyles.DEFAULT,
20611
+ color: colors.text.dark.toString()
20612
+ },
20613
+ placeholderStyle: {
20614
+ ...textStyles.DEFAULT,
20615
+ color: colors.text.light.toString()
20616
+ }
20380
20617
  },
20381
20618
  Dropdown: {
20382
20619
  trigger: {
20383
- backgroundColor: colors.surface.medium.toNumber(),
20620
+ backgroundColor: colors.surface.light.toNumber(),
20384
20621
  borderColor: colors.border.medium.toNumber(),
20385
20622
  borderWidth: 1,
20386
- cornerRadius: 4,
20623
+ cornerRadius: 7,
20387
20624
  padding: {
20388
20625
  left: 12,
20389
- right: 12,
20390
- top: 8,
20391
- bottom: 8
20626
+ right: 10,
20627
+ top: 9,
20628
+ bottom: 9
20392
20629
  },
20393
- width: "fill"
20630
+ width: "fill",
20631
+ gap: 8
20394
20632
  },
20395
20633
  triggerHover: { borderColor: colors.primary.DEFAULT.toNumber() },
20396
20634
  triggerOpen: {
20397
20635
  borderColor: colors.primary.DEFAULT.toNumber(),
20398
- backgroundColor: colors.surface.dark.toNumber()
20636
+ borderWidth: 2,
20637
+ backgroundColor: colors.surface.lightest.toNumber()
20399
20638
  },
20400
20639
  triggerDisabled: {
20401
20640
  backgroundColor: colors.surface.light.toNumber(),
20402
20641
  alpha: .5
20403
20642
  },
20404
20643
  arrow: {
20405
- color: colors.text.DEFAULT.toNumber(),
20406
- size: 12
20644
+ color: colors.text.dark.toNumber(),
20645
+ size: 12,
20646
+ strokeWidth: 2
20647
+ },
20648
+ selectionIndicator: {
20649
+ color: colors.primary.dark.toNumber(),
20650
+ size: 16,
20651
+ strokeWidth: 2
20407
20652
  },
20408
20653
  overlay: {
20409
- backgroundColor: colors.surface.medium.toNumber(),
20654
+ backgroundColor: colors.surface.lightest.toNumber(),
20410
20655
  borderColor: colors.border.medium.toNumber(),
20411
20656
  borderWidth: 1,
20412
- cornerRadius: 4,
20657
+ cornerRadius: 7,
20413
20658
  maxHeight: 300,
20414
- padding: 4
20659
+ padding: 6
20660
+ },
20661
+ option: {
20662
+ padding: {
20663
+ left: 10,
20664
+ right: 10,
20665
+ top: 8,
20666
+ bottom: 8
20667
+ },
20668
+ cornerRadius: 5,
20669
+ gap: 8
20415
20670
  },
20416
- option: { padding: {
20417
- left: 12,
20418
- right: 12,
20419
- top: 8,
20420
- bottom: 8
20421
- } },
20422
20671
  optionSelected: {
20423
- backgroundColor: colors.primary.light.toNumber(),
20424
- Text: { style: textStyles.DEFAULT }
20672
+ backgroundColor: colors.primary.lightest.toNumber(),
20673
+ borderColor: colors.primary.light.toNumber(),
20674
+ borderWidth: 1,
20675
+ backgroundAlpha: .9,
20676
+ Text: { style: {
20677
+ ...textStyles.DEFAULT,
20678
+ color: colors.text.dark.toString(),
20679
+ fontStyle: "bold"
20680
+ } }
20425
20681
  },
20426
20682
  optionDisabled: { alpha: .3 },
20427
20683
  textStyle: textStyles.DEFAULT,
20428
- placeholderStyle: textStyles.DEFAULT,
20684
+ placeholderStyle: {
20685
+ ...textStyles.DEFAULT,
20686
+ color: colors.text.light.toString()
20687
+ },
20429
20688
  filterInput: {
20430
20689
  backgroundColor: colors.surface.lightest.toNumber(),
20431
- borderColor: colors.border.medium.toNumber()
20690
+ borderColor: colors.border.medium.toNumber(),
20691
+ borderWidth: 1,
20692
+ cornerRadius: 6,
20693
+ padding: {
20694
+ left: 10,
20695
+ right: 10,
20696
+ top: 6,
20697
+ bottom: 6
20698
+ }
20432
20699
  },
20433
20700
  animationConfig: "stiff",
20434
20701
  optionGap: 2
@@ -20479,9 +20746,15 @@ function buildDefaultTheme(colors) {
20479
20746
  height: 28,
20480
20747
  thumbSize: 24,
20481
20748
  trackColorOff: colors.surface.dark.toNumber(),
20482
- trackColorOn: colors.success.DEFAULT.toNumber(),
20749
+ trackColorOn: colors.primary.DEFAULT.toNumber(),
20750
+ trackBorderColorOff: colors.border.medium.toNumber(),
20751
+ trackBorderColorOn: colors.primary.dark.toNumber(),
20752
+ trackBorderWidth: 1,
20483
20753
  thumbColor: colors.surface.lightest.toNumber(),
20754
+ thumbBorderColor: colors.border.light.toNumber(),
20755
+ thumbBorderWidth: 1,
20484
20756
  disabledColor: colors.border.medium.toNumber(),
20757
+ disabledAlpha: .5,
20485
20758
  padding: 2,
20486
20759
  duration: 200,
20487
20760
  gap: 8,
@@ -22073,13 +22346,16 @@ function patchVNode(parent, oldV, newV) {
22073
22346
  }
22074
22347
  if (newV.props !== void 0) ctx.componentVNode.props = newV.props;
22075
22348
  if (newV.children !== void 0) ctx.componentVNode.children = newV.children;
22349
+ const previousTheme = ctx.theme;
22076
22350
  if (newV.__theme !== void 0) ctx.theme = newV.__theme;
22077
22351
  const newVWithCtx = setVNodePropSafe(newV, "__ctx", ctx);
22078
22352
  const propsWithChildren = ctx.componentVNode.children?.length ? {
22079
22353
  ...ctx.componentVNode.props ?? {},
22080
22354
  children: ctx.componentVNode.children
22081
22355
  } : ctx.componentVNode.props;
22082
- if (!shouldComponentUpdate(ctx, propsWithChildren)) return;
22356
+ const themeChanged = !Object.is(previousTheme, ctx.theme);
22357
+ const propsChanged = shouldComponentUpdate(ctx, propsWithChildren);
22358
+ if (!themeChanged && !propsChanged) return;
22083
22359
  const renderedNext = normalizeVNodeLike(withHooks(ctx, () => newVWithCtx.type(propsWithChildren)));
22084
22360
  if (!renderedNext) {
22085
22361
  ctx.vnode = renderedNext;
@@ -22384,6 +22660,7 @@ var ThemeRegistry = class {
22384
22660
  ...styles
22385
22661
  });
22386
22662
  }
22663
+ this.notifyListeners();
22387
22664
  }
22388
22665
  /**
22389
22666
  * Set the entire global theme (replaces current theme)
@@ -22577,12 +22854,21 @@ function deepMergeDefined(base, override) {
22577
22854
  function extractNestedThemes(theme) {
22578
22855
  const ownProps = { ...theme };
22579
22856
  const nestedThemes = {};
22857
+ const internalPrimitiveNames = new Set([
22858
+ "view",
22859
+ "text",
22860
+ "nineslice",
22861
+ "sprite",
22862
+ "image",
22863
+ "graphics",
22864
+ "tilesprite",
22865
+ "particles"
22866
+ ]);
22580
22867
  const allComponentNames = new Set([
22581
- "View",
22582
- "Text",
22583
- "NineSlice",
22868
+ ...Object.keys(defaultTheme),
22869
+ ...Object.keys(themeRegistry.getGlobalTheme()),
22584
22870
  ...Array.from(themeRegistry.getCustomComponentNames())
22585
- ]);
22871
+ ].filter((key) => key !== "__colorPreset" && !internalPrimitiveNames.has(key)));
22586
22872
  for (const key in ownProps) if (allComponentNames.has(key)) {
22587
22873
  nestedThemes[key] = ownProps[key];
22588
22874
  delete ownProps[key];
@@ -23085,41 +23371,41 @@ var DEFAULT_TONES = {
23085
23371
  };
23086
23372
  var DEFAULT_SIZES = {
23087
23373
  small: {
23088
- height: 20,
23374
+ height: 22,
23089
23375
  padding: {
23090
- left: 7,
23091
- right: 7,
23092
- top: 2,
23093
- bottom: 2
23376
+ left: 8,
23377
+ right: 8,
23378
+ top: 3,
23379
+ bottom: 3
23094
23380
  },
23095
- fontSize: 11,
23096
- cornerRadius: 10,
23381
+ fontSize: 12,
23382
+ cornerRadius: 11,
23097
23383
  gap: 5,
23098
23384
  dotSize: 8
23099
23385
  },
23100
23386
  medium: {
23101
- height: 24,
23387
+ height: 26,
23102
23388
  padding: {
23103
- left: 9,
23104
- right: 9,
23105
- top: 3,
23106
- bottom: 3
23389
+ left: 10,
23390
+ right: 10,
23391
+ top: 4,
23392
+ bottom: 4
23107
23393
  },
23108
- fontSize: 13,
23109
- cornerRadius: 12,
23394
+ fontSize: 14,
23395
+ cornerRadius: 13,
23110
23396
  gap: 6,
23111
23397
  dotSize: 10
23112
23398
  },
23113
23399
  large: {
23114
- height: 30,
23400
+ height: 32,
23115
23401
  padding: {
23116
- left: 12,
23117
- right: 12,
23118
- top: 4,
23119
- bottom: 4
23402
+ left: 13,
23403
+ right: 13,
23404
+ top: 5,
23405
+ bottom: 5
23120
23406
  },
23121
23407
  fontSize: 18,
23122
- cornerRadius: 15,
23408
+ cornerRadius: 16,
23123
23409
  gap: 8,
23124
23410
  dotSize: 12
23125
23411
  }
@@ -23173,7 +23459,8 @@ function resolveBadgeTextStyle(options) {
23173
23459
  }
23174
23460
  function Badge(props) {
23175
23461
  const { children, label, count, maxCount, dot = false, tone: explicitTone, variant: explicitVariant, size: explicitSize, textStyle: explicitTextStyle, disabled = false, disabledAlpha: explicitDisabledAlpha, theme, width, height, padding, gap, cornerRadius, backgroundColor, backgroundAlpha, borderColor, borderWidth, alpha, ...viewProps } = props;
23176
- const { props: themed, nestedTheme } = getThemedProps("Badge", useTheme(), theme ?? {});
23462
+ const localTheme = useTheme();
23463
+ const { props: themed, nestedTheme } = getThemedProps("Badge", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
23177
23464
  const tone = explicitTone ?? themed.tone ?? "neutral";
23178
23465
  const variant = explicitVariant ?? themed.variant ?? "solid";
23179
23466
  const size = explicitSize ?? themed.size ?? "medium";
@@ -23229,13 +23516,16 @@ function Badge(props) {
23229
23516
  }
23230
23517
  function Tag(props) {
23231
23518
  const { selected = false, onRemove, closeLabel = "x", tone, variant, size, theme, children, label, textStyle, ...badgeProps } = props;
23232
- const { props: themed, nestedTheme } = getThemedProps("Tag", useTheme(), theme ?? {});
23519
+ const localTheme = useTheme();
23520
+ const { props: themed, nestedTheme } = getThemedProps("Tag", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
23233
23521
  const resolvedTone = tone ?? themed.tone ?? (selected ? "primary" : "neutral");
23234
23522
  const resolvedVariant = variant ?? themed.variant ?? (selected ? "solid" : "soft");
23235
23523
  const resolvedSize = size ?? themed.size ?? "medium";
23524
+ const colors = resolveBadgeColors(resolvedTone, resolvedVariant);
23525
+ const closeSize = themed.closeSize ?? 16;
23236
23526
  const resolvedTextStyle = resolveBadgeTextStyle({
23237
23527
  size: resolvedSize,
23238
- textColor: resolveBadgeColors(resolvedTone, resolvedVariant).textColor,
23528
+ textColor: colors.textColor,
23239
23529
  themedTextStyle: themed.textStyle,
23240
23530
  explicitTextStyle: textStyle
23241
23531
  });
@@ -23254,16 +23544,15 @@ function Tag(props) {
23254
23544
  }),
23255
23545
  children,
23256
23546
  onRemove && /* @__PURE__ */ jsx(Button, {
23257
- width: themed.closeSize ?? 16,
23258
- height: themed.closeSize ?? 16,
23547
+ width: closeSize,
23548
+ height: closeSize,
23549
+ minWidth: closeSize,
23259
23550
  padding: 0,
23260
- cornerRadius: (themed.closeSize ?? 16) / 2,
23551
+ cornerRadius: closeSize / 2,
23261
23552
  onClick: onRemove,
23262
23553
  theme: nestedTheme,
23263
- children: /* @__PURE__ */ jsx(Text, {
23264
- text: closeLabel,
23265
- style: resolvedTextStyle
23266
- })
23554
+ label: closeLabel,
23555
+ textStyle: resolvedTextStyle
23267
23556
  })
23268
23557
  ]
23269
23558
  });
@@ -24186,6 +24475,37 @@ function resolveEffect(props, theme) {
24186
24475
  }
24187
24476
  //#endregion
24188
24477
  //#region src/components/custom/Button.tsx
24478
+ function mergeButtonTheme(base, override) {
24479
+ return override ? {
24480
+ ...base,
24481
+ ...override
24482
+ } : base;
24483
+ }
24484
+ function resolveButtonSlotTheme(theme, group, name) {
24485
+ if (!name) return void 0;
24486
+ return {
24487
+ ...theme[group]?.[name] ?? {},
24488
+ ...theme[name] ?? {}
24489
+ };
24490
+ }
24491
+ function buildButtonContentTheme(theme) {
24492
+ const textTheme = theme.Text ?? {};
24493
+ const iconTheme = theme.Icon ?? {};
24494
+ const mergedTextStyle = theme.textStyle || textTheme.style ? {
24495
+ ...textTheme.style ?? {},
24496
+ ...theme.textStyle ?? {}
24497
+ } : void 0;
24498
+ return {
24499
+ Text: mergedTextStyle ? {
24500
+ ...textTheme,
24501
+ style: mergedTextStyle
24502
+ } : textTheme,
24503
+ Icon: {
24504
+ ...iconTheme,
24505
+ ...theme.iconSize !== void 0 ? { size: theme.iconSize } : {}
24506
+ }
24507
+ };
24508
+ }
24189
24509
  /**
24190
24510
  * Generic Button component
24191
24511
  * Provides variant, size, and disabled state support
@@ -24204,50 +24524,61 @@ function resolveEffect(props, theme) {
24204
24524
  * ```
24205
24525
  */
24206
24526
  function Button(props) {
24207
- const { children, onClick, disabled, variant, size, width, height, ...restProps } = props;
24208
- const { props: themed } = getThemedProps("Button", void 0, {});
24527
+ const { children, label, text, onClick, disabled = false, variant, size, width, height, textStyle, iconSize, disabledAlpha, alpha, theme, onTouch, ...restProps } = props;
24528
+ const localTheme = useTheme();
24529
+ const { props: themed, nestedTheme } = getThemedProps("Button", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
24209
24530
  const ref = useRef(null);
24210
24531
  const { applyEffect } = useGameObjectEffect(ref);
24211
24532
  const themedButton = themed;
24212
- let variantTheme = { ...themedButton };
24213
- if (variant && themedButton[variant]) {
24214
- const variantOverrides = themedButton[variant];
24215
- variantTheme = {
24216
- ...variantTheme,
24217
- ...variantOverrides
24218
- };
24219
- }
24220
- let sizeTheme = { ...variantTheme };
24221
- if (size && themedButton[size]) {
24222
- const sizeOverrides = themedButton[size];
24223
- sizeTheme = {
24224
- ...sizeTheme,
24225
- ...sizeOverrides
24226
- };
24227
- }
24533
+ const resolvedVariant = variant ?? themedButton.variant ?? "primary";
24534
+ const resolvedSize = size ?? themedButton.size ?? "medium";
24535
+ const sizeTheme = mergeButtonTheme(mergeButtonTheme({ ...themedButton }, resolveButtonSlotTheme(themedButton, "variants", resolvedVariant)), resolveButtonSlotTheme(themedButton, "sizes", resolvedSize));
24536
+ const resolvedTextStyle = sizeTheme.textStyle || textStyle ? {
24537
+ ...sizeTheme.textStyle ?? {},
24538
+ ...textStyle ?? {}
24539
+ } : void 0;
24540
+ const resolvedIconSize = iconSize ?? sizeTheme.iconSize;
24541
+ const resolvedDisabledAlpha = disabledAlpha ?? sizeTheme.disabledAlpha ?? .5;
24542
+ const contentStyleProps = {
24543
+ ...resolvedTextStyle ? { textStyle: resolvedTextStyle } : {},
24544
+ ...resolvedIconSize !== void 0 ? { iconSize: resolvedIconSize } : {}
24545
+ };
24228
24546
  const effectiveTheme = disabled ? {
24229
24547
  ...sizeTheme,
24230
- backgroundColor: themedButton.disabledColor ?? sizeTheme?.backgroundColor,
24231
- alpha: .5
24232
- } : sizeTheme;
24233
- const handleTouch = !disabled ? () => {
24234
- const resolved = resolveEffect(props, themed);
24548
+ backgroundColor: sizeTheme.disabledColor ?? themedButton.disabledColor ?? sizeTheme.backgroundColor,
24549
+ alpha: alpha ?? resolvedDisabledAlpha,
24550
+ ...contentStyleProps
24551
+ } : {
24552
+ ...sizeTheme,
24553
+ ...alpha !== void 0 ? { alpha } : {},
24554
+ ...contentStyleProps
24555
+ };
24556
+ const handleTouch = !disabled ? (event) => {
24557
+ const resolved = resolveEffect(props, effectiveTheme);
24235
24558
  applyEffectByName(applyEffect, resolved.effect, resolved.effectConfig);
24559
+ onTouch?.(event);
24236
24560
  onClick?.();
24237
24561
  } : void 0;
24238
- const { disabledColor: _disabledColor, effect: _effect, effectConfig: _effectConfig, primary: _primary, secondary: _secondary, outline: _outline, small: _small, medium: _medium, large: _large, ...viewThemeProps } = effectiveTheme;
24562
+ const generatedText = label ?? text;
24563
+ const content = children ?? (generatedText !== void 0 ? /* @__PURE__ */ jsx(Text, {
24564
+ text: `${generatedText}`,
24565
+ ...resolvedTextStyle ? { style: resolvedTextStyle } : {}
24566
+ }) : null);
24567
+ const contentTheme = mergeThemes(nestedTheme, buildButtonContentTheme(effectiveTheme));
24568
+ const { disabledColor: _disabledColor, disabledAlpha: _disabledAlpha, effect: _effect, effectConfig: _effectConfig, textStyle: _textStyle, iconSize: _iconSize, variant: _variant, size: _size, variants: _variants, sizes: _sizes, primary: _primary, secondary: _secondary, outline: _outline, ghost: _ghost, danger: _danger, small: _small, medium: _medium, large: _large, Text: _Text, Icon: _Icon, ...viewThemeProps } = effectiveTheme;
24239
24569
  return /* @__PURE__ */ jsx(View, {
24240
24570
  ref,
24241
- width,
24242
- height,
24243
24571
  enableGestures: !disabled,
24244
24572
  direction: "row",
24245
24573
  alignItems: "center",
24246
24574
  justifyContent: "center",
24247
- ...handleTouch && { onTouch: handleTouch },
24248
24575
  ...viewThemeProps,
24576
+ width: width ?? effectiveTheme.width,
24577
+ height: height ?? effectiveTheme.height,
24249
24578
  ...restProps,
24250
- children
24579
+ ...handleTouch && { onTouch: handleTouch },
24580
+ theme: contentTheme,
24581
+ children: content
24251
24582
  });
24252
24583
  }
24253
24584
  //#endregion
@@ -24287,7 +24618,8 @@ function drawDefaultIndicator(graphics, indicatorProps) {
24287
24618
  }
24288
24619
  }
24289
24620
  function Checkbox(props) {
24290
- const { props: themed, nestedTheme } = getThemedProps("Checkbox", useTheme(), props.theme ?? {});
24621
+ const localTheme = useTheme();
24622
+ const { props: themed, nestedTheme } = getThemedProps("Checkbox", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
24291
24623
  const size = themed.size ?? 20;
24292
24624
  const gap = themed.gap ?? 8;
24293
24625
  const labelPosition = props.labelPosition ?? themed.labelPosition ?? "right";
@@ -24524,7 +24856,7 @@ function collectSnapshot(scene) {
24524
24856
  frameMs,
24525
24857
  phaserVersion: Phaser.VERSION,
24526
24858
  renderer: resolveRendererName(scene),
24527
- viewport: `${scene.scale.width}x${scene.scale.height}`,
24859
+ viewport: `${scene.scale.width.toFixed(0)}x${scene.scale.height.toFixed(0)}`,
24528
24860
  textureCount: resolveTextureCount(scene),
24529
24861
  mountsTotal: mountStats.totalMounts,
24530
24862
  mountsByType: summarizeMap(mountStats.byType),
@@ -24639,38 +24971,61 @@ function Graphics(props) {
24639
24971
  * @returns RadioButton JSX element
24640
24972
  */
24641
24973
  function RadioButton(props) {
24642
- const { props: themed, nestedTheme } = getThemedProps("RadioButton", void 0, {});
24643
- const size = themed.size ?? 16;
24644
- const innerSize = themed.innerSize ?? size * .75;
24974
+ const localTheme = useTheme();
24975
+ const { props: themed, nestedTheme } = getThemedProps("RadioButton", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
24976
+ const size = themed.size ?? 18;
24977
+ const innerSize = themed.innerSize ?? Math.max(6, Math.round(size * .45));
24645
24978
  const innerRadius = innerSize * .5;
24646
24979
  const outerRadius = size * .5;
24980
+ const borderWidth = themed.borderWidth ?? Math.max(2, Math.round(size * .11));
24981
+ const selected = props.selected ?? false;
24982
+ const disabled = props.disabled ?? false;
24983
+ const disabledAlpha = themed.disabledAlpha ?? .5;
24984
+ const labelPosition = props.labelPosition ?? themed.labelPosition ?? "right";
24985
+ const selectedColor = themed.selectedColor ?? 2201331;
24986
+ const idleColor = themed.color ?? 7697781;
24987
+ const activeColor = selected ? selectedColor : idleColor;
24988
+ const indicatorFillColor = themed.backgroundColor;
24989
+ const gap = themed.gap ?? 8;
24990
+ const label = labelPosition !== "none" ? /* @__PURE__ */ jsx(Text, {
24991
+ text: props.label,
24992
+ style: themed.labelStyle,
24993
+ alpha: disabled ? disabledAlpha : 1
24994
+ }) : null;
24995
+ const handleClick = () => {
24996
+ if (disabled) return;
24997
+ props.onClick?.();
24998
+ };
24647
24999
  return /* @__PURE__ */ jsxs(View, {
24648
25000
  direction: "row",
24649
25001
  alignItems: "center",
24650
- enableGestures: true,
24651
- onTouch: () => props.onClick?.(),
25002
+ enableGestures: !disabled,
25003
+ onTouch: handleClick,
24652
25004
  theme: nestedTheme,
24653
- gap: themed.gap,
24654
- children: [/* @__PURE__ */ jsx(View, {
24655
- width: size,
24656
- height: size,
24657
- backgroundColor: themed.color,
24658
- alignItems: "center",
24659
- justifyContent: "center",
24660
- backgroundAlpha: 1,
24661
- padding: 0,
24662
- cornerRadius: outerRadius,
24663
- children: /* @__PURE__ */ jsx(View, {
24664
- width: innerSize,
24665
- height: innerSize,
24666
- backgroundColor: themed.selectedColor,
24667
- visible: props.selected ?? false,
24668
- cornerRadius: innerRadius
24669
- })
24670
- }), /* @__PURE__ */ jsx(Text, {
24671
- text: props.label,
24672
- style: themed.labelStyle
24673
- })]
25005
+ gap,
25006
+ alpha: disabled ? disabledAlpha : 1,
25007
+ children: [
25008
+ labelPosition === "left" && label,
25009
+ /* @__PURE__ */ jsx(View, {
25010
+ width: size,
25011
+ height: size,
25012
+ ...indicatorFillColor !== void 0 && { backgroundColor: indicatorFillColor },
25013
+ borderColor: activeColor,
25014
+ borderWidth,
25015
+ alignItems: "center",
25016
+ justifyContent: "center",
25017
+ padding: 0,
25018
+ cornerRadius: outerRadius,
25019
+ children: /* @__PURE__ */ jsx(View, {
25020
+ width: innerSize,
25021
+ height: innerSize,
25022
+ backgroundColor: selectedColor,
25023
+ visible: selected,
25024
+ cornerRadius: innerRadius
25025
+ })
25026
+ }),
25027
+ labelPosition !== "left" && label
25028
+ ]
24674
25029
  }, props.key);
24675
25030
  }
24676
25031
  //#endregion
@@ -24685,10 +25040,12 @@ function RadioButton(props) {
24685
25040
  * @returns RadioGroup JSX element
24686
25041
  */
24687
25042
  function RadioGroup(props) {
24688
- const { props: themed, nestedTheme } = getThemedProps("RadioButton", void 0, {});
24689
- const [selected, setSelected] = useState(props.value ?? "");
25043
+ const { props: themed, nestedTheme } = getThemedProps("RadioButton", useTheme(), {});
25044
+ const [internalSelected, setInternalSelected] = useState(props.value ?? props.defaultValue ?? "");
25045
+ const selected = props.value ?? internalSelected;
24690
25046
  const handleSelect = (value) => {
24691
- setSelected(value);
25047
+ if (props.disabled) return;
25048
+ if (props.value === void 0) setInternalSelected(value);
24692
25049
  props.onChange?.(value);
24693
25050
  };
24694
25051
  return /* @__PURE__ */ jsx(View, {
@@ -24697,36 +25054,11 @@ function RadioGroup(props) {
24697
25054
  gap: themed.gap,
24698
25055
  children: props.options.map((option) => {
24699
25056
  const isSelected = selected === option.value;
24700
- const size = themed.size ?? 16;
24701
- const innerSize = themed.innerSize ?? size * .75;
24702
- const innerRadius = innerSize * .5;
24703
- const outerRadius = size * .5;
24704
- return /* @__PURE__ */ jsxs(View, {
24705
- direction: "row",
24706
- gap: themed.gap,
24707
- alignItems: "center",
24708
- enableGestures: true,
24709
- onTouch: () => handleSelect(option.value),
24710
- children: [/* @__PURE__ */ jsx(View, {
24711
- width: size,
24712
- height: size,
24713
- backgroundColor: themed.color,
24714
- alignItems: "center",
24715
- justifyContent: "center",
24716
- backgroundAlpha: 1,
24717
- padding: 0,
24718
- cornerRadius: outerRadius,
24719
- children: /* @__PURE__ */ jsx(View, {
24720
- width: innerSize,
24721
- height: innerSize,
24722
- backgroundColor: themed.selectedColor,
24723
- visible: isSelected,
24724
- cornerRadius: innerRadius
24725
- })
24726
- }), /* @__PURE__ */ jsx(Text, {
24727
- text: option.label,
24728
- style: themed.labelStyle
24729
- })]
25057
+ return /* @__PURE__ */ jsx(RadioButton, {
25058
+ label: option.label,
25059
+ selected: isSelected,
25060
+ disabled: props.disabled ?? false,
25061
+ onClick: () => handleSelect(option.value)
24730
25062
  }, option.value);
24731
25063
  })
24732
25064
  });
@@ -24948,6 +25280,31 @@ function Portal(props) {
24948
25280
  return null;
24949
25281
  }
24950
25282
  //#endregion
25283
+ //#region src/components/custom/useOverlayPresence.ts
25284
+ function useOverlayPresence(show) {
25285
+ const [isPresent, setIsPresent] = useState(show);
25286
+ const [phase, setPhase] = useState(show ? "entered" : "exited");
25287
+ const previousShow = useRef(show);
25288
+ useEffect(() => {
25289
+ if (show) {
25290
+ setIsPresent(true);
25291
+ if (!previousShow.current) setPhase("entering");
25292
+ } else if (previousShow.current) setPhase("exiting");
25293
+ previousShow.current = show;
25294
+ }, [show]);
25295
+ return {
25296
+ isPresent,
25297
+ phase,
25298
+ finishEnter: useCallback(() => {
25299
+ setPhase((current) => current === "entering" ? "entered" : current);
25300
+ }, []),
25301
+ finishExit: useCallback(() => {
25302
+ setIsPresent(false);
25303
+ setPhase("exited");
25304
+ }, [])
25305
+ };
25306
+ }
25307
+ //#endregion
24951
25308
  //#region src/components/custom/Popover.tsx
24952
25309
  function getMainPlacement(placement) {
24953
25310
  return placement.split("-")[0];
@@ -25031,7 +25388,8 @@ function assignRef(ref, value) {
25031
25388
  ref.current = value;
25032
25389
  }
25033
25390
  function Popover(props) {
25034
- const { props: themed, nestedTheme } = getThemedProps("Popover", useTheme(), props.theme ?? {});
25391
+ const localTheme = useTheme();
25392
+ const { props: themed, nestedTheme } = getThemedProps("Popover", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
25035
25393
  const scene = useScene();
25036
25394
  const triggerRef = useRef(null);
25037
25395
  const contentRef = useRef(null);
@@ -25039,12 +25397,18 @@ function Popover(props) {
25039
25397
  const [internalOpen, setInternalOpen] = useState(props.defaultOpen ?? false);
25040
25398
  const isControlled = props.isOpen !== void 0;
25041
25399
  const isOpen = isControlled ? props.isOpen === true : internalOpen;
25400
+ const presence = useOverlayPresence(isOpen);
25401
+ const { applyEffect: contentAnimation, stopEffects: stopContentEffects } = useGameObjectEffect(contentRef);
25042
25402
  const placement = props.placement ?? themed.placement ?? "bottom";
25043
25403
  const offset = props.offset ?? themed.offset ?? 8;
25044
25404
  const depth = props.depth ?? themed.depth ?? 1100;
25045
25405
  const closeOnOutside = props.closeOnOutside ?? themed.closeOnOutside ?? true;
25046
25406
  const closeOnEscape = props.closeOnEscape ?? themed.closeOnEscape ?? true;
25047
25407
  const viewportPadding = props.viewportPadding ?? themed.viewportPadding ?? 8;
25408
+ const openEffect = props.openEffect ?? themed.openEffect ?? createFadeInEffect;
25409
+ const closeEffect = props.closeEffect ?? themed.closeEffect ?? createFadeOutEffect;
25410
+ const openDuration = props.openDuration ?? themed.openDuration ?? 120;
25411
+ const closeDuration = props.closeDuration ?? themed.closeDuration ?? 100;
25048
25412
  const explicitContentWidth = props.contentWidth ?? themed.contentWidth;
25049
25413
  const explicitContentHeight = props.contentHeight ?? themed.contentHeight;
25050
25414
  const viewport = portalRegistry.getViewportSize(scene);
@@ -25093,7 +25457,7 @@ function Popover(props) {
25093
25457
  return () => window.removeEventListener("keydown", handleKeyDown);
25094
25458
  }, [closeOnEscape, isOpen]);
25095
25459
  useEffect(() => {
25096
- if (!isOpen) return;
25460
+ if (!presence.isPresent) return;
25097
25461
  DeferredLayoutQueue.defer(() => {
25098
25462
  const size = getLayoutSize(contentRef.current);
25099
25463
  if (size.width <= 0 || size.height <= 0) return;
@@ -25106,18 +25470,47 @@ function Popover(props) {
25106
25470
  });
25107
25471
  });
25108
25472
  }, [
25109
- isOpen,
25473
+ presence.isPresent,
25110
25474
  props.children,
25111
25475
  explicitContentWidth,
25112
25476
  explicitContentHeight
25113
25477
  ]);
25478
+ useEffect(() => {
25479
+ const content = contentRef.current;
25480
+ if (!content || !presence.isPresent) return;
25481
+ if (presence.phase === "entering") {
25482
+ if (!isPositionReady) return;
25483
+ stopContentEffects();
25484
+ content.setVisible(true);
25485
+ contentAnimation(openEffect, {
25486
+ time: openDuration,
25487
+ onComplete: presence.finishEnter
25488
+ });
25489
+ } else if (presence.phase === "exiting") {
25490
+ stopContentEffects();
25491
+ contentAnimation(closeEffect, {
25492
+ time: closeDuration,
25493
+ onComplete: () => {
25494
+ contentRef.current?.setVisible(false);
25495
+ setMeasuredContentSize(null);
25496
+ presence.finishExit();
25497
+ }
25498
+ });
25499
+ }
25500
+ }, [
25501
+ presence.phase,
25502
+ presence.isPresent,
25503
+ isPositionReady,
25504
+ openDuration,
25505
+ closeDuration
25506
+ ]);
25114
25507
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(View, {
25115
25508
  ...props.triggerProps ?? {},
25116
25509
  ref: triggerRef,
25117
25510
  enableGestures: !props.disabled,
25118
25511
  onTouch: toggleOpen,
25119
25512
  children: props.trigger
25120
- }), isOpen && /* @__PURE__ */ jsxs(Portal, {
25513
+ }), presence.isPresent && /* @__PURE__ */ jsxs(Portal, {
25121
25514
  depth,
25122
25515
  blockEvents: false,
25123
25516
  children: [closeOnOutside && /* @__PURE__ */ jsx(View, {
@@ -25128,11 +25521,6 @@ function Popover(props) {
25128
25521
  onTouch: close,
25129
25522
  onTouchMove: (event) => event.stopPropagation()
25130
25523
  }), /* @__PURE__ */ jsx(View, {
25131
- ...contentProps,
25132
- ref: handleContentRef,
25133
- x: overlayPosition.x,
25134
- y: overlayPosition.y,
25135
- ...props.matchTriggerWidth || explicitContentWidth !== void 0 ? { width: contentWidth } : {},
25136
25524
  direction: "column",
25137
25525
  backgroundColor: themed.backgroundColor ?? 1120295,
25138
25526
  borderColor: themed.borderColor ?? 3359061,
@@ -25140,6 +25528,11 @@ function Popover(props) {
25140
25528
  cornerRadius: themed.cornerRadius ?? 8,
25141
25529
  padding: themed.padding ?? 10,
25142
25530
  gap: themed.gap ?? 8,
25531
+ ...contentProps,
25532
+ ref: handleContentRef,
25533
+ x: overlayPosition.x,
25534
+ y: overlayPosition.y,
25535
+ ...props.matchTriggerWidth || explicitContentWidth !== void 0 ? { width: contentWidth } : {},
25143
25536
  ...isPositionReady ? contentPropsAlpha !== void 0 ? { alpha: contentPropsAlpha } : {} : { alpha: 0 },
25144
25537
  onTouch: (event) => event.stopPropagation(),
25145
25538
  theme: nestedTheme,
@@ -25149,7 +25542,8 @@ function Popover(props) {
25149
25542
  }
25150
25543
  function ContextMenu(props) {
25151
25544
  const { items, width: explicitWidth, onSelect, isOpen: explicitOpen, defaultOpen, onOpenChange, ...popoverProps } = props;
25152
- const { props: themed, nestedTheme } = getThemedProps("ContextMenu", useTheme(), props.theme ?? {});
25545
+ const localTheme = useTheme();
25546
+ const { props: themed, nestedTheme } = getThemedProps("ContextMenu", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
25153
25547
  const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
25154
25548
  const isControlled = explicitOpen !== void 0;
25155
25549
  const isOpen = isControlled ? explicitOpen === true : internalOpen;
@@ -25241,7 +25635,8 @@ function formatDefaultValue(props) {
25241
25635
  }
25242
25636
  function ProgressBar(props) {
25243
25637
  const { value, min = 0, max = 100, orientation: explicitOrientation, label, showValue, labelPosition: explicitLabelPosition, formatValue, trackColor: explicitTrackColor, fillColor: explicitFillColor, borderColor: explicitBorderColor, borderWidth: explicitBorderWidth, cornerRadius: explicitCornerRadius, labelStyle: explicitLabelStyle, disabled = false, disabledAlpha: explicitDisabledAlpha, theme, width, height, alpha, ...viewProps } = props;
25244
- const { props: themed, nestedTheme } = getThemedProps("ProgressBar", useTheme(), theme ?? {});
25638
+ const localTheme = useTheme();
25639
+ const { props: themed, nestedTheme } = getThemedProps("ProgressBar", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
25245
25640
  const orientation = explicitOrientation ?? themed.orientation ?? "horizontal";
25246
25641
  const ratio = getProgressRatio(value, min, max);
25247
25642
  const percent = ratio * 100;
@@ -27602,18 +27997,24 @@ function CharText(props) {
27602
27997
  * - Scroll-to-cursor behavior on input
27603
27998
  */
27604
27999
  function CharTextInput(props) {
28000
+ const localTheme = useTheme();
28001
+ const { props: themed, nestedTheme } = getThemedProps("CharTextInput", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
28002
+ const inputProps = {
28003
+ ...themed,
28004
+ ...props
28005
+ };
27605
28006
  const containerRef = useRef(null);
27606
28007
  const inputManagerRef = useRef(null);
27607
28008
  const charTextApiRef = useRef(null);
27608
- const [internalValue, setInternalValue] = useState(props.value ?? "");
28009
+ const [internalValue, setInternalValue] = useState(inputProps.value ?? "");
27609
28010
  const [cursorPosition, setCursorPosition] = useState(0);
27610
28011
  const [selectionAnchor, setSelectionAnchor] = useState(-1);
27611
28012
  const [isFocused, setIsFocused] = useState(false);
27612
- const refCurrentValue = useRef(props.value ?? "");
28013
+ const refCurrentValue = useRef(inputProps.value ?? "");
27613
28014
  const refCursorPosition = useRef(0);
27614
28015
  const refSelectionAnchor = useRef(-1);
27615
- const currentValue = props.value !== void 0 ? props.value : internalValue;
27616
- const isControlled = props.value !== void 0;
28016
+ const currentValue = inputProps.value !== void 0 ? inputProps.value : internalValue;
28017
+ const isControlled = inputProps.value !== void 0;
27617
28018
  refCurrentValue.current = currentValue;
27618
28019
  refCursorPosition.current = cursorPosition;
27619
28020
  refSelectionAnchor.current = selectionAnchor;
@@ -27631,14 +28032,14 @@ function CharTextInput(props) {
27631
28032
  if (!containerRef.current) return;
27632
28033
  const container = containerRef.current;
27633
28034
  inputManagerRef.current = new KeyboardInputManager(container, {
27634
- ...props.maxLength !== void 0 && { maxLength: props.maxLength },
27635
- ...props.disabled !== void 0 && { disabled: props.disabled },
27636
- debug: props.debugHtmlInput ?? false,
28035
+ ...inputProps.maxLength !== void 0 && { maxLength: inputProps.maxLength },
28036
+ ...inputProps.disabled !== void 0 && { disabled: inputProps.disabled },
28037
+ debug: inputProps.debugHtmlInput ?? false,
27637
28038
  onInput: (_value, _event) => {},
27638
28039
  onKeyDown: (event) => {
27639
- if (event.key === "Enter") if (!props.multiline) {
28040
+ if (event.key === "Enter") if (!inputProps.multiline) {
27640
28041
  event.preventDefault();
27641
- props.onSubmit?.(refCurrentValue.current);
28042
+ inputProps.onSubmit?.(refCurrentValue.current);
27642
28043
  } else {
27643
28044
  event.preventDefault();
27644
28045
  handleCharacterInput("\n");
@@ -27662,13 +28063,13 @@ function CharTextInput(props) {
27662
28063
  setIsFocused(true);
27663
28064
  inputManagerRef.current?.setPointerEvents(false);
27664
28065
  setCursorPosition(currentValue.length);
27665
- props.onFocus?.();
28066
+ inputProps.onFocus?.();
27666
28067
  },
27667
28068
  onBlur: () => {
27668
28069
  setIsFocused(false);
27669
28070
  setSelectionAnchor(-1);
27670
28071
  inputManagerRef.current?.setPointerEvents(true);
27671
- props.onBlur?.();
28072
+ inputProps.onBlur?.();
27672
28073
  }
27673
28074
  });
27674
28075
  return () => {
@@ -27677,9 +28078,9 @@ function CharTextInput(props) {
27677
28078
  };
27678
28079
  }, [
27679
28080
  containerRef.current,
27680
- props.maxLength,
27681
- props.disabled,
27682
- props.multiline,
28081
+ inputProps.maxLength,
28082
+ inputProps.disabled,
28083
+ inputProps.multiline,
27683
28084
  isControlled
27684
28085
  ]);
27685
28086
  /**
@@ -27708,11 +28109,11 @@ function CharTextInput(props) {
27708
28109
  newValue = currentValue.slice(0, cursorPosition) + char + currentValue.slice(cursorPosition);
27709
28110
  newCursorPos = cursorPosition + char.length;
27710
28111
  }
27711
- if (props.maxLength !== void 0 && newValue.length > props.maxLength) return;
27712
- if (props.multiline && char === "\n" && props.maxLines !== void 0) {
27713
- if (newValue.split("\n").length > props.maxLines) return;
28112
+ if (inputProps.maxLength !== void 0 && newValue.length > inputProps.maxLength) return;
28113
+ if (inputProps.multiline && char === "\n" && inputProps.maxLines !== void 0) {
28114
+ if (newValue.split("\n").length > inputProps.maxLines) return;
27714
28115
  }
27715
- if (!props.multiline && charTextApiRef.current) {
28116
+ if (!inputProps.multiline && charTextApiRef.current) {
27716
28117
  const insertPosition = anchor >= 0 ? Math.min(anchor, cursorPosition) : cursorPosition;
27717
28118
  if (!charTextApiRef.current.canFitChar(char, insertPosition)) return;
27718
28119
  }
@@ -27830,10 +28231,10 @@ function CharTextInput(props) {
27830
28231
  * Update value (controlled or uncontrolled)
27831
28232
  */
27832
28233
  const updateValue = (newValue) => {
27833
- if (isControlled) props.onChange?.(newValue);
28234
+ if (isControlled) inputProps.onChange?.(newValue);
27834
28235
  else {
27835
28236
  setInternalValue(newValue);
27836
- props.onChange?.(newValue);
28237
+ inputProps.onChange?.(newValue);
27837
28238
  }
27838
28239
  };
27839
28240
  /**
@@ -27852,12 +28253,31 @@ function CharTextInput(props) {
27852
28253
  setCursorPosition(end);
27853
28254
  } else setSelectionAnchor(-1);
27854
28255
  };
27855
- const { value: _value, placeholder: _placeholder, onChange: _onChange, onFocus: _onFocus, onBlur: _onBlur, onSubmit: _onSubmit, ...viewProps } = props;
27856
- const displayText = currentValue || (props.placeholder && !isFocused ? props.placeholder : "");
28256
+ const { value: _value, placeholder: _placeholder, placeholderStyle, focusedBorderColor, disabledBackgroundColor, disabledBorderColor, disabledAlpha, onChange: _onChange, onFocus: _onFocus, onBlur: _onBlur, onSubmit: _onSubmit, ...viewProps } = inputProps;
28257
+ const isPlaceholderVisible = currentValue.length === 0 && !!inputProps.placeholder && !isFocused;
28258
+ const displayText = currentValue || (isPlaceholderVisible ? inputProps.placeholder ?? "" : "");
28259
+ const textStyle = isPlaceholderVisible ? {
28260
+ ...inputProps.textStyle ?? {},
28261
+ ...placeholderStyle ?? {}
28262
+ } : inputProps.textStyle;
28263
+ const disabled = inputProps.disabled ?? false;
28264
+ const focusedViewProps = isFocused ? {
28265
+ borderColor: focusedBorderColor ?? inputProps.borderColor,
28266
+ borderWidth: Math.max(2, inputProps.borderWidth ?? 1)
28267
+ } : {};
28268
+ const disabledViewProps = disabled ? {
28269
+ backgroundColor: disabledBackgroundColor ?? inputProps.backgroundColor,
28270
+ borderColor: disabledBorderColor ?? inputProps.borderColor,
28271
+ alpha: inputProps.alpha ?? disabledAlpha ?? .6
28272
+ } : {};
27857
28273
  return /* @__PURE__ */ jsx(CharText, {
27858
28274
  forwardRef: (r) => containerRef.current = r,
27859
28275
  ...viewProps,
28276
+ ...focusedViewProps,
28277
+ ...disabledViewProps,
28278
+ theme: nestedTheme,
27860
28279
  text: displayText,
28280
+ ...textStyle !== void 0 && { textStyle },
27861
28281
  showCursor: isFocused,
27862
28282
  cursorPosition,
27863
28283
  selectionStart,
@@ -27888,10 +28308,11 @@ function Divider(props) {
27888
28308
  /**
27889
28309
  * Calculate slider dimensions based on size variant and theme
27890
28310
  * @param size - Size variant
28311
+ * @param theme - Optional resolved ScrollSlider theme values
27891
28312
  * @returns Calculated dimensions
27892
28313
  */
27893
- function calculateSliderSize(size) {
27894
- const { props: themed } = getThemedProps("ScrollSlider", void 0, {});
28314
+ function calculateSliderSize(size, theme) {
28315
+ const themed = theme ?? getThemedProps("ScrollSlider", void 0, {}).props;
27895
28316
  const sizeFactor = size === "large" ? 1.25 : size === "small" ? .75 : size === "tiny" ? .5 : size === "micro" ? .25 : size === "nano" ? .125 : 1;
27896
28317
  const border = (themed.borderWidth ?? 1) * sizeFactor;
27897
28318
  const outer = (themed.size ?? 24) * sizeFactor;
@@ -27908,15 +28329,21 @@ function calculateSliderSize(size) {
27908
28329
  */
27909
28330
  function ScrollSlider(props) {
27910
28331
  const { direction, scrollPosition, viewportSize, contentSize, onScroll, momentum = true, onMomentumEnd } = props;
27911
- const { props: themed } = getThemedProps("ScrollSlider", void 0, {});
28332
+ const { props: themed } = getThemedProps("ScrollSlider", useTheme(), {});
27912
28333
  const sliderRef = useRef(null);
27913
28334
  const isDraggingRef = useRef(false);
28335
+ const [isDragging, setIsDragging] = useState(false);
27914
28336
  const trackContainerRef = useRef(null);
27915
28337
  const velocityRef = useRef(0);
27916
28338
  const lastTimeRef = useRef(0);
27917
28339
  const tweenRef = useRef(null);
27918
28340
  const isVertical = direction === "vertical";
27919
- const { border, outer, dimension } = calculateSliderSize(props.size);
28341
+ const { border, outer, dimension } = calculateSliderSize(props.size, themed);
28342
+ const sizeFactor = outer / (themed.size ?? 24);
28343
+ const thumbBorderWidth = Math.min((themed.thumbBorderWidth ?? 1) * sizeFactor, Math.max(0, dimension / 3));
28344
+ const outerRadius = themed.cornerRadius ?? outer / 2;
28345
+ const trackRadius = themed.trackCornerRadius ?? dimension / 2;
28346
+ const thumbRadius = themed.thumbCornerRadius ?? dimension / 2;
27920
28347
  const containerWithLayout = trackContainerRef.current;
27921
28348
  const trackSizeInner = (containerWithLayout?.__getLayoutSize ? isVertical ? containerWithLayout.__getLayoutSize().height : containerWithLayout.__getLayoutSize().width : containerWithLayout && containerWithLayout.width > 0 && containerWithLayout.height > 0 ? isVertical ? containerWithLayout.height : containerWithLayout.width : 800) - border * 2;
27922
28349
  const minThumbSize = themed.minThumbSize ?? 20;
@@ -27929,6 +28356,7 @@ function ScrollSlider(props) {
27929
28356
  data.stopPropagation();
27930
28357
  if (data.state === "start") {
27931
28358
  isDraggingRef.current = true;
28359
+ setIsDragging(true);
27932
28360
  velocityRef.current = 0;
27933
28361
  lastTimeRef.current = Date.now();
27934
28362
  if (tweenRef.current) {
@@ -27939,6 +28367,7 @@ function ScrollSlider(props) {
27939
28367
  }
27940
28368
  if (data.state === "end") {
27941
28369
  isDraggingRef.current = false;
28370
+ setIsDragging(false);
27942
28371
  if (momentum && Math.abs(velocityRef.current) > .1) startMomentum(scrollPosition);
27943
28372
  else if (onMomentumEnd) onMomentumEnd();
27944
28373
  return;
@@ -27986,12 +28415,14 @@ function ScrollSlider(props) {
27986
28415
  width: isVertical ? outer : "100%",
27987
28416
  height: isVertical ? "100%" : outer,
27988
28417
  backgroundColor: themed.borderColor ?? 0,
28418
+ cornerRadius: outerRadius,
27989
28419
  padding: border,
27990
28420
  children: /* @__PURE__ */ jsxs(View, {
27991
28421
  ref: sliderRef,
27992
28422
  width: isVertical ? dimension : "100%",
27993
28423
  height: isVertical ? "100%" : dimension,
27994
28424
  backgroundColor: themed.trackColor ?? 14540253,
28425
+ cornerRadius: trackRadius,
27995
28426
  direction: "stack",
27996
28427
  padding: 0,
27997
28428
  children: [/* @__PURE__ */ jsx(View, {
@@ -28000,6 +28431,7 @@ function ScrollSlider(props) {
28000
28431
  x: 0,
28001
28432
  y: 0,
28002
28433
  backgroundColor: themed.trackColor ?? 11184810,
28434
+ cornerRadius: trackRadius,
28003
28435
  enableGestures: true,
28004
28436
  onTouch: handleBackgroundTouch
28005
28437
  }), /* @__PURE__ */ jsx(View, {
@@ -28007,7 +28439,10 @@ function ScrollSlider(props) {
28007
28439
  height: isVertical ? thumbSize : dimension,
28008
28440
  x: isVertical ? 0 : thumbPosition,
28009
28441
  y: isVertical ? thumbPosition : 0,
28010
- backgroundColor: themed.thumbColor ?? 15658683,
28442
+ backgroundColor: isDragging ? themed.thumbActiveColor ?? themed.thumbColor ?? 15658683 : themed.thumbColor ?? 15658683,
28443
+ borderColor: themed.thumbBorderColor,
28444
+ borderWidth: thumbBorderWidth,
28445
+ cornerRadius: thumbRadius,
28011
28446
  enableGestures: true,
28012
28447
  onTouchMove: handleThumbTouchMove
28013
28448
  })]
@@ -28022,7 +28457,8 @@ function ScrollSlider(props) {
28022
28457
  * @returns JSX element
28023
28458
  */
28024
28459
  function ScrollView(props) {
28025
- const { children, showVerticalSlider = "auto", showHorizontalSlider = "auto", scroll: initialScroll, onScrollInfoChange, snap = false, snapAlignment = "start", snapThreshold = 20, momentum = true, onSnap, ...viewProps } = props;
28460
+ const { children, showVerticalSlider = "auto", showHorizontalSlider = "auto", scroll: initialScroll, onScrollInfoChange, snap = false, snapAlignment = "start", snapThreshold = 20, momentum = true, onSnap, onSliderSize, sliderSize: sliderSizeVariant, ...viewProps } = props;
28461
+ const { props: sliderTheme } = getThemedProps("ScrollSlider", useTheme(), {});
28026
28462
  const [scroll, setScroll] = useState({
28027
28463
  dx: initialScroll?.dx ?? 0,
28028
28464
  dy: initialScroll?.dy ?? 0
@@ -28056,9 +28492,9 @@ function ScrollView(props) {
28056
28492
  const needsHorizontalScroll = effectiveContentWidth > viewportWidth + epsilon;
28057
28493
  const showVerticalSliderActual = showVerticalSlider === true || needsVerticalScroll && showVerticalSlider === "auto";
28058
28494
  const showHorizontalSliderActual = showHorizontalSlider === true || needsHorizontalScroll && showHorizontalSlider === "auto";
28059
- const { outer: sliderSize } = calculateSliderSize(props.sliderSize);
28060
- props.onSliderSize?.({
28061
- width: showVerticalSlider ? sliderSize : 0,
28495
+ const { outer: sliderSize } = calculateSliderSize(sliderSizeVariant, sliderTheme);
28496
+ onSliderSize?.({
28497
+ width: showVerticalSliderActual ? sliderSize : 0,
28062
28498
  height: showHorizontalSliderActual ? sliderSize : 0
28063
28499
  });
28064
28500
  const maxScrollY = Math.max(0, effectiveContentHeight - viewportHeight);
@@ -28521,7 +28957,7 @@ function ScrollView(props) {
28521
28957
  visible: showHorizontalSliderActual ? true : "none",
28522
28958
  children: /* @__PURE__ */ jsx(ScrollSlider, {
28523
28959
  direction: "horizontal",
28524
- size: props.sliderSize,
28960
+ size: sliderSizeVariant,
28525
28961
  scrollPosition: scroll.dx,
28526
28962
  viewportSize: viewportWidth,
28527
28963
  contentSize: contentWidth,
@@ -28538,7 +28974,7 @@ function ScrollView(props) {
28538
28974
  flex: 1,
28539
28975
  children: /* @__PURE__ */ jsx(ScrollSlider, {
28540
28976
  direction: "vertical",
28541
- size: props.sliderSize,
28977
+ size: sliderSizeVariant,
28542
28978
  scrollPosition: scroll.dy,
28543
28979
  viewportSize: viewportHeight,
28544
28980
  contentSize: effectiveContentHeight,
@@ -28555,25 +28991,114 @@ function ScrollView(props) {
28555
28991
  });
28556
28992
  }
28557
28993
  //#endregion
28994
+ //#region src/components/custom/TransformOriginView.tsx
28995
+ /**
28996
+ * TransformOriginView component - declarative transforms around custom origin point
28997
+ *
28998
+ * Uses three nested Views to apply rotation/scale around configurable origin:
28999
+ * - Outer View: Defines bounding box, receives layout/position props
29000
+ * - Middle View: Positioned at origin point, receives transform/visual props
29001
+ * - Inner View: Contains content, offset to align with origin
29002
+ *
29003
+ * @param props - TransformOriginView props
29004
+ * @returns JSX element
29005
+ */
29006
+ function TransformOriginView({ originX = .5, originY = .5, width, height, x = 0, y = 0, children, rotation, scale, scaleX, scaleY, backgroundColor, backgroundAlpha, cornerRadius, borderWidth, borderColor, borderAlpha, ...outerViewProps }) {
29007
+ if (typeof width !== "number" || typeof height !== "number") throw new Error("TransformOriginView requires numeric width and height");
29008
+ const offsetX = width * originX;
29009
+ const offsetY = height * originY;
29010
+ const middleViewProps = {};
29011
+ if (rotation !== void 0) middleViewProps.rotation = rotation;
29012
+ if (scale !== void 0) middleViewProps.scale = scale;
29013
+ if (scaleX !== void 0) middleViewProps.scaleX = scaleX;
29014
+ if (scaleY !== void 0) middleViewProps.scaleY = scaleY;
29015
+ if (backgroundColor !== void 0) middleViewProps.backgroundColor = backgroundColor;
29016
+ if (backgroundAlpha !== void 0) middleViewProps.backgroundAlpha = backgroundAlpha;
29017
+ if (cornerRadius !== void 0) middleViewProps.cornerRadius = cornerRadius;
29018
+ if (borderWidth !== void 0) middleViewProps.borderWidth = borderWidth;
29019
+ if (borderColor !== void 0) middleViewProps.borderColor = borderColor;
29020
+ if (borderAlpha !== void 0) middleViewProps.borderAlpha = borderAlpha;
29021
+ return /* @__PURE__ */ jsx(View, {
29022
+ x,
29023
+ y,
29024
+ width,
29025
+ height,
29026
+ padding: 0,
29027
+ direction: "stack",
29028
+ ...outerViewProps,
29029
+ children: /* @__PURE__ */ jsx(View, {
29030
+ x: offsetX,
29031
+ y: offsetY,
29032
+ width,
29033
+ height,
29034
+ padding: 0,
29035
+ direction: "stack",
29036
+ ...middleViewProps,
29037
+ children: /* @__PURE__ */ jsx(View, {
29038
+ x: -offsetX,
29039
+ y: -offsetY,
29040
+ width,
29041
+ height,
29042
+ padding: 0,
29043
+ direction: "stack",
29044
+ backgroundAlpha: 0,
29045
+ children
29046
+ })
29047
+ })
29048
+ });
29049
+ }
29050
+ //#endregion
28558
29051
  //#region src/components/custom/Dropdown.tsx
28559
29052
  /**
28560
- * Default arrow component - simple Graphics triangle
29053
+ * Default arrow component - chevron indicator.
28561
29054
  */
28562
29055
  function DefaultArrow(props) {
28563
29056
  const color = props.color ?? 16777215;
28564
29057
  const size = props.size ?? 8;
29058
+ const strokeWidth = props.strokeWidth ?? Math.max(2, Math.round(size * .16));
28565
29059
  return /* @__PURE__ */ jsx(Graphics, {
28566
29060
  width: size,
28567
29061
  height: size,
28568
29062
  onDraw: (g) => {
28569
29063
  g.clear();
28570
- g.fillStyle(color, 1);
29064
+ g.lineStyle(strokeWidth, color, 1);
28571
29065
  g.beginPath();
28572
- g.moveTo(0, 0);
28573
- g.lineTo(size, 0);
28574
- g.lineTo(size / 2, size);
28575
- g.closePath();
28576
- g.fillPath();
29066
+ g.moveTo(size * .18, size * .35);
29067
+ g.lineTo(size * .5, size * .68);
29068
+ g.lineTo(size * .82, size * .35);
29069
+ g.strokePath();
29070
+ }
29071
+ });
29072
+ }
29073
+ function DefaultOptionIndicator(props) {
29074
+ const size = props.size ?? 16;
29075
+ const color = props.color ?? 16777215;
29076
+ const strokeWidth = props.strokeWidth ?? 2;
29077
+ const alpha = props.disabled ? .4 : 1;
29078
+ return /* @__PURE__ */ jsx(Graphics, {
29079
+ width: size,
29080
+ height: size,
29081
+ dependencies: [
29082
+ props.selected,
29083
+ props.multiple,
29084
+ props.disabled,
29085
+ color,
29086
+ size,
29087
+ strokeWidth
29088
+ ],
29089
+ onDraw: (g) => {
29090
+ g.clear();
29091
+ if (props.multiple) {
29092
+ g.lineStyle(strokeWidth, color, props.selected ? alpha : alpha * .55);
29093
+ g.strokeRoundedRect(strokeWidth / 2, strokeWidth / 2, size - strokeWidth, size - strokeWidth, 3);
29094
+ if (!props.selected) return;
29095
+ } else if (!props.selected) return;
29096
+ g.lineStyle(strokeWidth + 1, color, alpha);
29097
+ g.beginPath();
29098
+ g.moveTo(size * .22, size * .52);
29099
+ g.lineTo(size * .43, size * .72);
29100
+ g.lineTo(size * .8, size * .28);
29101
+ g.strokePath();
28577
29102
  }
28578
29103
  });
28579
29104
  }
@@ -28609,11 +29134,7 @@ function Dropdown(props) {
28609
29134
  const [isOpen, setIsOpen] = useState(false);
28610
29135
  const [internalValue, setInternalValue] = useState(props.defaultValue ?? (props.multiple ? [] : ""));
28611
29136
  const [filterQuery, setFilterQuery] = useState("");
28612
- const [isAnimating, setIsAnimating] = useState(false);
28613
- const shouldIgnoreNextClick = useRef(false);
28614
29137
  const triggerRef = useRef(null);
28615
- const overlayRef = useRef(null);
28616
- const containerRef = useRef(null);
28617
29138
  const scrollViewRef = useRef(null);
28618
29139
  const { applyEffect } = useGameObjectEffect(triggerRef);
28619
29140
  const isControlled = props.value !== void 0;
@@ -28626,6 +29147,13 @@ function Dropdown(props) {
28626
29147
  const animationConfig = props.animationConfig ?? themed.animationConfig ?? "gentle";
28627
29148
  const maxHeight = props.maxHeight ?? overlayTheme.maxHeight ?? 300;
28628
29149
  const arrowConfig = themed.arrow ?? {};
29150
+ const arrowSize = arrowConfig.size ?? 12;
29151
+ const indicatorConfig = themed.selectionIndicator ?? {};
29152
+ const indicatorSize = indicatorConfig.size ?? 16;
29153
+ const indicatorColor = indicatorConfig.color ?? arrowConfig.color;
29154
+ const indicatorStrokeWidth = indicatorConfig.strokeWidth ?? 2;
29155
+ const placement = props.placement ?? "bottom";
29156
+ const popoverPlacement = placement === "top" ? "top-start" : "bottom-start";
28629
29157
  const getSelectedOptions = () => {
28630
29158
  if (props.multiple) {
28631
29159
  const values = currentValue;
@@ -28636,38 +29164,31 @@ function Dropdown(props) {
28636
29164
  }
28637
29165
  };
28638
29166
  const selectedOptions = getSelectedOptions();
28639
- const [overlayHeight, setOverlayHeight] = useSpring(isOpen ? maxHeight : 0, animationConfig, () => setIsAnimating(false));
28640
- useForceRedraw(20, overlayHeight);
28641
29167
  const [arrowRotation, setArrowRotation] = useSpring(isOpen ? Math.PI : 0, animationConfig);
28642
29168
  useForceRedraw(20, arrowRotation);
28643
- const resolvedOverlayHeight = Math.max(0, overlayHeight.value);
28644
- const handleToggle = (event) => {
29169
+ const openDropdown = () => {
28645
29170
  if (props.disabled) return;
28646
- event?.stopPropagation();
28647
- if (isOpen) handleClose();
28648
- else {
28649
- setIsOpen(true);
28650
- setIsAnimating(true);
28651
- setOverlayHeight(maxHeight);
28652
- setArrowRotation(Math.PI);
28653
- setFilterQuery("");
28654
- props.onOpen?.();
28655
- shouldIgnoreNextClick.current = true;
28656
- const resolved = resolveEffect(props, themed);
28657
- applyEffectByName(applyEffect, resolved.effect, resolved.effectConfig);
28658
- }
29171
+ if (isOpen) return;
29172
+ setIsOpen(true);
29173
+ setArrowRotation(Math.PI);
29174
+ setFilterQuery("");
29175
+ props.onOpen?.();
29176
+ const resolved = resolveEffect(props, themed);
29177
+ applyEffectByName(applyEffect, resolved.effect, resolved.effectConfig);
28659
29178
  };
28660
29179
  const handleClose = () => {
28661
- setIsAnimating(true);
29180
+ if (!isOpen) return;
28662
29181
  setIsOpen(false);
28663
- setOverlayHeight(0);
28664
29182
  setArrowRotation(0);
28665
29183
  setFilterQuery("");
28666
29184
  props.onClose?.();
28667
29185
  };
29186
+ const handleOpenChange = (open) => {
29187
+ if (open) openDropdown();
29188
+ else handleClose();
29189
+ };
28668
29190
  const handleSelect = (value, event) => {
28669
29191
  event?.stopPropagation();
28670
- shouldIgnoreNextClick.current = true;
28671
29192
  if (props.multiple) {
28672
29193
  const values = currentValue;
28673
29194
  const newValues = values.includes(value) ? values.filter((v) => v !== value) : [...values, value];
@@ -28680,33 +29201,6 @@ function Dropdown(props) {
28680
29201
  if (props.closeOnSelect ?? true) handleClose();
28681
29202
  }
28682
29203
  };
28683
- const handleOutsideClick = () => {
28684
- if (shouldIgnoreNextClick.current) {
28685
- shouldIgnoreNextClick.current = false;
28686
- return;
28687
- }
28688
- if (isOpen) handleClose();
28689
- };
28690
- const calculateOverlayPosition = () => {
28691
- const triggerContainer = triggerRef.current;
28692
- if (!triggerContainer) return {
28693
- x: 0,
28694
- y: 0,
28695
- width: 0
28696
- };
28697
- const triggerSize = triggerContainer.__getLayoutSize?.() ?? {
28698
- width: 0,
28699
- height: 0
28700
- };
28701
- const triggerX = triggerContainer.x ?? 0;
28702
- const triggerY = triggerContainer.y ?? 0;
28703
- const gap = 4;
28704
- return {
28705
- x: triggerX,
28706
- y: (props.placement ?? "bottom") === "bottom" ? triggerY + triggerSize.height + gap : triggerY - maxHeight - gap,
28707
- width: triggerSize.width
28708
- };
28709
- };
28710
29204
  const renderSelectedValue = () => {
28711
29205
  if (props.renderValue) return props.renderValue(selectedOptions.length > 0 ? selectedOptions : null);
28712
29206
  if (selectedOptions.length === 0) return /* @__PURE__ */ jsx(Text, {
@@ -28734,10 +29228,9 @@ function Dropdown(props) {
28734
29228
  return triggerTheme;
28735
29229
  };
28736
29230
  const triggerStyle = getTriggerStyle();
28737
- const overlayPosition = calculateOverlayPosition();
28738
29231
  const renderedOptions = props.options.map((option, _index) => {
28739
29232
  const isSelected = props.multiple ? currentValue.includes(option.value) : currentValue === option.value;
28740
- const isDisabled = (option.disabled ?? false) || isAnimating && (props.placement ?? "bottom") === "top";
29233
+ const isDisabled = option.disabled ?? false;
28741
29234
  const matchesFilter = props.isFilterable ? option.label.toLowerCase().includes(filterQuery.toLowerCase()) : true;
28742
29235
  const optionStyle = {
28743
29236
  ...optionTheme,
@@ -28758,27 +29251,40 @@ function Dropdown(props) {
28758
29251
  theme: optionNestedTheme,
28759
29252
  ...optionStyle,
28760
29253
  children: props.renderOption ? props.renderOption(option, isSelected) : /* @__PURE__ */ jsxs(Fragment, { children: [
29254
+ /* @__PURE__ */ jsx(DefaultOptionIndicator, {
29255
+ selected: isSelected,
29256
+ multiple: props.multiple === true,
29257
+ disabled: isDisabled,
29258
+ color: indicatorColor ?? 16777215,
29259
+ size: indicatorSize,
29260
+ strokeWidth: indicatorStrokeWidth
29261
+ }),
28761
29262
  option.prefix,
28762
29263
  /* @__PURE__ */ jsx(Text, { text: option.label }),
28763
29264
  option.suffix
28764
29265
  ] })
28765
29266
  }, String(option.value));
28766
29267
  });
28767
- const placement = props.placement ?? "bottom";
28768
29268
  const trigger = /* @__PURE__ */ jsxs(View, {
28769
29269
  ref: triggerRef,
28770
29270
  direction: "row",
28771
29271
  alignItems: "center",
28772
29272
  justifyContent: "space-between",
28773
- enableGestures: !props.disabled,
28774
- onTouch: (data) => handleToggle(data),
28775
29273
  ...triggerStyle,
28776
29274
  children: [/* @__PURE__ */ jsx(View, {
28777
29275
  flex: 1,
28778
29276
  children: renderSelectedValue()
28779
- }), props.arrow ? props.arrow : /* @__PURE__ */ jsx(DefaultArrow, {
28780
- color: arrowConfig.color ?? 16777215,
28781
- size: arrowConfig.size ?? 8
29277
+ }), /* @__PURE__ */ jsx(TransformOriginView, {
29278
+ width: arrowSize,
29279
+ height: arrowSize,
29280
+ rotation: arrowRotation.value,
29281
+ originX: .5,
29282
+ originY: .5,
29283
+ children: props.arrow ? props.arrow : /* @__PURE__ */ jsx(DefaultArrow, {
29284
+ color: arrowConfig.color ?? 16777215,
29285
+ size: arrowSize,
29286
+ strokeWidth: arrowConfig.strokeWidth ?? 2
29287
+ })
28782
29288
  })]
28783
29289
  });
28784
29290
  const filterInput = props.isFilterable && /* @__PURE__ */ jsx(CharTextInput, {
@@ -28796,7 +29302,7 @@ function Dropdown(props) {
28796
29302
  width: "fill",
28797
29303
  children: /* @__PURE__ */ jsx(ScrollView, {
28798
29304
  ref: scrollViewRef,
28799
- showVerticalSlider: isAnimating ? false : "auto",
29305
+ showVerticalSlider: "auto",
28800
29306
  height: "fill",
28801
29307
  width: "100%",
28802
29308
  onTouch: () => {
@@ -28812,33 +29318,53 @@ function Dropdown(props) {
28812
29318
  })
28813
29319
  });
28814
29320
  const overlay = /* @__PURE__ */ jsx(View, {
28815
- height: resolvedOverlayHeight,
28816
- width: overlayPosition.width,
29321
+ direction: "column",
29322
+ width: "fill",
29323
+ height: maxHeight,
28817
29324
  overflow: "hidden",
28818
- children: /* @__PURE__ */ jsx(View, {
28819
- ref: overlayRef,
28820
- direction: "column",
28821
- width: "fill",
28822
- height: "fill",
28823
- visible: isOpen || resolvedOverlayHeight > .1,
28824
- ...overlayTheme,
28825
- children: placement === "top" ? /* @__PURE__ */ jsxs(Fragment, { children: [optionsList, filterInput] }) : /* @__PURE__ */ jsxs(Fragment, { children: [filterInput, optionsList] })
28826
- })
29325
+ theme: nestedTheme,
29326
+ ...overlayTheme,
29327
+ children: placement === "top" ? /* @__PURE__ */ jsxs(Fragment, { children: [optionsList, filterInput] }) : /* @__PURE__ */ jsxs(Fragment, { children: [filterInput, optionsList] })
28827
29328
  });
29329
+ const popoverTheme = {
29330
+ ...nestedTheme,
29331
+ Popover: {
29332
+ backgroundColor: 0,
29333
+ backgroundAlpha: 0,
29334
+ borderWidth: 0,
29335
+ cornerRadius: 0,
29336
+ padding: 0,
29337
+ gap: 0
29338
+ }
29339
+ };
28828
29340
  return /* @__PURE__ */ jsx(View, {
28829
- direction: props.stackLayout ? "stack" : "column",
29341
+ direction: "column",
28830
29342
  width: props.width || "fill",
28831
- height: props.stackLayout ? triggerRef.current?.height : "auto",
29343
+ height: "auto",
28832
29344
  ref: props.ref,
28833
- children: /* @__PURE__ */ jsx(View, {
28834
- ref: containerRef,
28835
- direction: "column",
28836
- width: props.width,
28837
- theme: nestedTheme,
28838
- enableGestures: true,
28839
- onTouchOutside: handleOutsideClick,
28840
- children: placement === "top" ? /* @__PURE__ */ jsxs(Fragment, { children: [overlay, trigger] }) : /* @__PURE__ */ jsxs(Fragment, { children: [trigger, overlay] })
28841
- }, `dropdown-${placement}`)
29345
+ children: /* @__PURE__ */ jsx(Popover, {
29346
+ trigger,
29347
+ isOpen,
29348
+ onOpenChange: handleOpenChange,
29349
+ placement: popoverPlacement,
29350
+ offset: 4,
29351
+ matchTriggerWidth: true,
29352
+ contentHeight: maxHeight,
29353
+ contentProps: {
29354
+ height: maxHeight,
29355
+ backgroundAlpha: 0,
29356
+ borderWidth: 0,
29357
+ padding: 0,
29358
+ gap: 0,
29359
+ cornerRadius: 0
29360
+ },
29361
+ triggerProps: { width: props.width ?? "fill" },
29362
+ closeOnOutside: true,
29363
+ closeOnEscape: true,
29364
+ disabled: props.disabled === true,
29365
+ theme: popoverTheme,
29366
+ children: overlay
29367
+ })
28842
29368
  });
28843
29369
  }
28844
29370
  //#endregion
@@ -30265,14 +30791,21 @@ function interpolateColor(color1, color2, progress) {
30265
30791
  * ```
30266
30792
  */
30267
30793
  function Toggle(props) {
30268
- const { props: themed, nestedTheme } = getThemedProps("Toggle", useTheme(), props.theme ?? {});
30794
+ const localTheme = useTheme();
30795
+ const { props: themed, nestedTheme } = getThemedProps("Toggle", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
30269
30796
  const width = useRef(themed.width ?? 50);
30270
30797
  const height = useRef(themed.height ?? 28);
30271
30798
  const thumbSize = useRef(themed.thumbSize ?? 24);
30272
30799
  const trackColorOff = useRef(themed.trackColorOff ?? 10066329);
30273
30800
  const trackColorOn = useRef(themed.trackColorOn ?? 5025616);
30801
+ const trackBorderColorOff = useRef(themed.trackBorderColorOff ?? 7829367);
30802
+ const trackBorderColorOn = useRef(themed.trackBorderColorOn ?? themed.trackColorOn ?? 5025616);
30803
+ const trackBorderWidth = useRef(themed.trackBorderWidth ?? 1);
30274
30804
  const thumbColor = useRef(themed.thumbColor ?? 16777215);
30805
+ const thumbBorderColor = useRef(themed.thumbBorderColor ?? 13751771);
30806
+ const thumbBorderWidth = useRef(themed.thumbBorderWidth ?? 1);
30275
30807
  const disabledColor = useRef(themed.disabledColor ?? 6710886);
30808
+ const disabledAlpha = useRef(themed.disabledAlpha ?? .5);
30276
30809
  const padding = useRef(themed.padding ?? 2);
30277
30810
  const duration = useRef(themed.duration ?? 200);
30278
30811
  const gap = themed.gap ?? 8;
@@ -30282,8 +30815,14 @@ function Toggle(props) {
30282
30815
  thumbSize.current = themed.thumbSize ?? 24;
30283
30816
  trackColorOff.current = themed.trackColorOff ?? 10066329;
30284
30817
  trackColorOn.current = themed.trackColorOn ?? 5025616;
30818
+ trackBorderColorOff.current = themed.trackBorderColorOff ?? 7829367;
30819
+ trackBorderColorOn.current = themed.trackBorderColorOn ?? themed.trackColorOn ?? 5025616;
30820
+ trackBorderWidth.current = themed.trackBorderWidth ?? 1;
30285
30821
  thumbColor.current = themed.thumbColor ?? 16777215;
30822
+ thumbBorderColor.current = themed.thumbBorderColor ?? 13751771;
30823
+ thumbBorderWidth.current = themed.thumbBorderWidth ?? 1;
30286
30824
  disabledColor.current = themed.disabledColor ?? 6710886;
30825
+ disabledAlpha.current = themed.disabledAlpha ?? .5;
30287
30826
  padding.current = themed.padding ?? 2;
30288
30827
  duration.current = themed.duration ?? 200;
30289
30828
  const thumbRadius = thumbSize.current / 2;
@@ -30297,9 +30836,34 @@ function Toggle(props) {
30297
30836
  const trackRef = useRef(null);
30298
30837
  const thumbRef = useRef(null);
30299
30838
  const containerRef = useRef(null);
30839
+ const drawTrackShape = (g, color, borderColor) => {
30840
+ const borderInset = trackBorderWidth.current / 2;
30841
+ g.clear();
30842
+ g.fillStyle(color, 1);
30843
+ g.fillRoundedRect(0, 0, width.current, height.current, trackRadius);
30844
+ if (trackBorderWidth.current > 0) {
30845
+ g.lineStyle(trackBorderWidth.current, borderColor, 1);
30846
+ g.strokeRoundedRect(borderInset, borderInset, width.current - trackBorderWidth.current, height.current - trackBorderWidth.current, Math.max(0, trackRadius - borderInset));
30847
+ }
30848
+ };
30849
+ const getTrackColor = (targetChecked) => {
30850
+ if (props.disabled && !targetChecked) return disabledColor.current;
30851
+ return targetChecked ? trackColorOn.current : trackColorOff.current;
30852
+ };
30853
+ const getTrackBorderColor = (targetChecked) => {
30854
+ return targetChecked ? trackBorderColorOn.current : trackBorderColorOff.current;
30855
+ };
30300
30856
  useEffect(() => {
30301
30857
  if (props.checked !== void 0 && props.checked !== checked) animateToggle(props.checked);
30302
30858
  }, [props.checked, checked]);
30859
+ useEffect(() => {
30860
+ if (!isAnimating) setThumbX(checked ? thumbOffsetOn : thumbOffsetOff);
30861
+ }, [
30862
+ checked,
30863
+ thumbOffsetOn,
30864
+ thumbOffsetOff,
30865
+ isAnimating
30866
+ ]);
30303
30867
  /**
30304
30868
  * Animate toggle transition
30305
30869
  */
@@ -30334,10 +30898,10 @@ function Toggle(props) {
30334
30898
  duration: duration.current,
30335
30899
  ease: "Cubic.easeOut",
30336
30900
  onUpdate: (tween) => {
30337
- const currentColor = interpolateColor(startColor, endColor, tween.getValue() ?? 0);
30338
- track.clear();
30339
- track.fillStyle(props.disabled ? disabledColor.current : currentColor, 1);
30340
- track.fillRoundedRect(0, 0, width.current, height.current, trackRadius);
30901
+ const progress = tween.getValue() ?? 0;
30902
+ const currentColor = interpolateColor(startColor, endColor, progress);
30903
+ const currentBorderColor = interpolateColor(getTrackBorderColor(checked), getTrackBorderColor(newChecked), progress);
30904
+ drawTrackShape(track, props.disabled && !newChecked ? disabledColor.current : currentColor, currentBorderColor);
30341
30905
  }
30342
30906
  });
30343
30907
  setChecked(newChecked);
@@ -30356,9 +30920,7 @@ function Toggle(props) {
30356
30920
  */
30357
30921
  const drawTrack = (g) => {
30358
30922
  g.clear();
30359
- const color = props.disabled ? disabledColor.current : checked ? trackColorOn.current : trackColorOff.current;
30360
- g.fillStyle(color, 1);
30361
- g.fillRoundedRect(0, 0, width.current, height.current, trackRadius);
30923
+ drawTrackShape(g, getTrackColor(checked), getTrackBorderColor(checked));
30362
30924
  };
30363
30925
  /**
30364
30926
  * Draw thumb graphics
@@ -30367,6 +30929,10 @@ function Toggle(props) {
30367
30929
  g.clear();
30368
30930
  g.fillStyle(thumbColor.current, 1);
30369
30931
  g.fillCircle(0, 0, thumbRadius);
30932
+ if (thumbBorderWidth.current > 0) {
30933
+ g.lineStyle(thumbBorderWidth.current, thumbBorderColor.current, 1);
30934
+ g.strokeCircle(0, 0, Math.max(0, thumbRadius - thumbBorderWidth.current / 2));
30935
+ }
30370
30936
  };
30371
30937
  const toggleElement = /* @__PURE__ */ jsxs(View, {
30372
30938
  direction: "stack",
@@ -30374,11 +30940,24 @@ function Toggle(props) {
30374
30940
  height: height.current,
30375
30941
  enableGestures: !props.disabled,
30376
30942
  onTouch: handleClick,
30377
- alpha: props.disabled ? .5 : 1,
30943
+ alpha: props.disabled ? disabledAlpha.current : 1,
30378
30944
  children: [/* @__PURE__ */ jsx(Graphics, {
30379
30945
  ref: trackRef,
30380
30946
  width: width.current,
30381
30947
  height: height.current,
30948
+ dependencies: [
30949
+ checked,
30950
+ width.current,
30951
+ height.current,
30952
+ trackColorOff.current,
30953
+ trackColorOn.current,
30954
+ trackBorderColorOff.current,
30955
+ trackBorderColorOn.current,
30956
+ trackBorderWidth.current,
30957
+ disabledColor.current,
30958
+ padding.current,
30959
+ props.disabled
30960
+ ],
30382
30961
  onDraw: drawTrack
30383
30962
  }), /* @__PURE__ */ jsx(Graphics, {
30384
30963
  ref: thumbRef,
@@ -30386,12 +30965,19 @@ function Toggle(props) {
30386
30965
  height: thumbSize.current,
30387
30966
  x: thumbX,
30388
30967
  y: height.current / 2,
30968
+ dependencies: [
30969
+ thumbSize.current,
30970
+ thumbColor.current,
30971
+ thumbBorderColor.current,
30972
+ thumbBorderWidth.current
30973
+ ],
30389
30974
  onDraw: drawThumb
30390
30975
  })]
30391
30976
  });
30392
30977
  const labelElement = props.label && labelPosition !== "none" ? /* @__PURE__ */ jsx(Text, {
30393
30978
  text: props.label,
30394
- style: themed.labelStyle
30979
+ style: themed.labelStyle,
30980
+ alpha: props.disabled ? disabledAlpha.current : 1
30395
30981
  }) : null;
30396
30982
  if (!props.label || labelPosition === "none") return /* @__PURE__ */ jsxs(View, {
30397
30983
  ref: containerRef,
@@ -30421,63 +31007,6 @@ function Toggle(props) {
30421
31007
  });
30422
31008
  }
30423
31009
  //#endregion
30424
- //#region src/components/custom/TransformOriginView.tsx
30425
- /**
30426
- * TransformOriginView component - declarative transforms around custom origin point
30427
- *
30428
- * Uses three nested Views to apply rotation/scale around configurable origin:
30429
- * - Outer View: Defines bounding box, receives layout/position props
30430
- * - Middle View: Positioned at origin point, receives transform/visual props
30431
- * - Inner View: Contains content, offset to align with origin
30432
- *
30433
- * @param props - TransformOriginView props
30434
- * @returns JSX element
30435
- */
30436
- function TransformOriginView({ originX = .5, originY = .5, width, height, x = 0, y = 0, children, rotation, scale, scaleX, scaleY, backgroundColor, backgroundAlpha, cornerRadius, borderWidth, borderColor, borderAlpha, ...outerViewProps }) {
30437
- if (typeof width !== "number" || typeof height !== "number") throw new Error("TransformOriginView requires numeric width and height");
30438
- const offsetX = width * originX;
30439
- const offsetY = height * originY;
30440
- const middleViewProps = {};
30441
- if (rotation !== void 0) middleViewProps.rotation = rotation;
30442
- if (scale !== void 0) middleViewProps.scale = scale;
30443
- if (scaleX !== void 0) middleViewProps.scaleX = scaleX;
30444
- if (scaleY !== void 0) middleViewProps.scaleY = scaleY;
30445
- if (backgroundColor !== void 0) middleViewProps.backgroundColor = backgroundColor;
30446
- if (backgroundAlpha !== void 0) middleViewProps.backgroundAlpha = backgroundAlpha;
30447
- if (cornerRadius !== void 0) middleViewProps.cornerRadius = cornerRadius;
30448
- if (borderWidth !== void 0) middleViewProps.borderWidth = borderWidth;
30449
- if (borderColor !== void 0) middleViewProps.borderColor = borderColor;
30450
- if (borderAlpha !== void 0) middleViewProps.borderAlpha = borderAlpha;
30451
- return /* @__PURE__ */ jsx(View, {
30452
- x,
30453
- y,
30454
- width,
30455
- height,
30456
- padding: 0,
30457
- direction: "stack",
30458
- ...outerViewProps,
30459
- children: /* @__PURE__ */ jsx(View, {
30460
- x: offsetX,
30461
- y: offsetY,
30462
- width,
30463
- height,
30464
- padding: 0,
30465
- direction: "stack",
30466
- ...middleViewProps,
30467
- children: /* @__PURE__ */ jsx(View, {
30468
- x: -offsetX,
30469
- y: -offsetY,
30470
- width,
30471
- height,
30472
- padding: 0,
30473
- direction: "stack",
30474
- backgroundAlpha: 0,
30475
- children
30476
- })
30477
- })
30478
- });
30479
- }
30480
- //#endregion
30481
- export { Portal as $, getContrastRatio as $n, normalizeVNodeLike as $t, useSpring as A, withHooks as An, resolveParticlePreset as Ar, createSwingEffect as At, releaseAllSVGTextures as B, applyLightMode as Bn, host as Br, getBadgeSizeConfig as Bt, CharText as C, useRedraw as Cn, particlesPatcher as Cr, createNoneEffect as Ct, Modal as D, useTheme as Dn, getFirstEmitter as Dr, createSlideInEffect as Dt, Dialog as E, useState as En, applyEmitterConfig as Er, createShakeEffect as Et, animatedSignal as F, defaultRadiusTokens as Fn, nineSlicePatcher as Fr, createZoomOutEffect as Ft, svgToTexture as G, midnightPreset as Gn, createTheme as Gt, releaseSVGTextures as H, generateColorScale as Hn, register as Hr, resolveBadgeTextStyle as Ht, isAnimatedSignal as I, defaultSizeTokens as In, imageCreator as Ir, useGameObjectEffect as It, clampProgressValue as J, alpha as Jn, themeRegistry as Jt, registerBuiltins as K, oceanBluePreset as Kn, getThemedProps as Kt, unwrapSignal as L, defaultSpacingTokens as Ln, imagePatcher as Lr, Badge as Lt, DEFAULT_SPRING_CONFIG as M, createDefaultTheme as Mn, buildEmitZone as Mr, createWiggleEffect as Mt, SPRING_PRESETS as N, defaultTheme as Nn, buildEmitZoneFromLayout as Nr, createWobbleEffect as Nt, Accordion as O, useViewportSize as On, isParticleEmitter as Or, createSlideOutEffect as Ot, SpringPhysics as P, createTextStyleTokens as Pn, nineSliceCreator as Pr, createZoomInEffect as Pt, calculateOverlayPosition as Q, ensureContrast as Qn, mountJSX as Qt, KeyboardInputManager as R, defaultTextStyleTokens as Rn, graphicsCreator as Rr, Tag as Rt, CharTextInput as S, useMemo as Sn, particlesCreator as Sr, createJelloEffect as St, WrapText as T, useScene as Tn, applyEmitZone as Tr, createPulseEffect as Tt, useSVGTexture as U, getPreset as Un, viewCreator as Ut, releaseSVGTexture as V, forestGreenPreset as Vn, nodeRegistry as Vr, getBadgeText as Vt, useSVGTextures as W, getPresetWithMode as Wn, viewPatcher as Wt, ContextMenu as X, darken as Xn, getMountStats as Xt, getProgressRatio as Y, createTextStyle as Yn, createElement as Yt, Popover as Z, darkenHex as Zn, mount as Zt, Dropdown as _, useEffect as _n, DebugLogger as _r, createFadeEffect as _t, Tabs as a, View as an, numberToRgb as ar, RadioButton as at, calculateSliderSize as b, useLayoutRect as bn, spriteCreator as br, createFlipOutEffect as bt, Sidebar as c, getCurrent as cn, HexColor as cr, useThemeTokens as ct, NineSlice as d, getLayoutSize$1 as dn, normalizeGap as dr, DEFAULT_EFFECT as dt, patchVNode as en, hex as er, Particles as et, Joystick as f, getWorldLayoutRect as fn, tileSpriteCreator as fr, EFFECT_REGISTRY as ft, Image$1 as g, useCallback as gn, viewportRegistry as gr, createBreatheEffect as gt, useIconPreload as h, useBackgroundGraphics as hn, textPatcher as hr, createBounceEffect as ht, TabPanel as i, portalRegistry as in, numberToHex as ir, RadioGroup as it, useSprings as j, getRenderContext as jn, buildDeathZonesFromLayout as jr, createTadaEffect as jt, computed as k, useWorldLayoutRect as kn, PARTICLE_PRESET_REGISTRY as kr, createSpinEffect as kt, RefOriginView as l, getLayoutProps as ln, normalizeCornerRadius as lr, Checkbox as lt, createIconComponent as m, shouldComponentUpdate as mn, textCreator as mr, resolveEffect as mt, Toggle as n, unmount as nn, lighten as nr, Text as nt, RangeSlider as o, disposeCtx as on, rgbToHsl as or, Graphics as ot, Icon as p, shallowEqual as pn, tileSpritePatcher as pr, applyEffectByName as pt, ProgressBar as q, presets as qn, mergeThemes as qt, Tab as r, unmountJSX as rn, lightenHex as rr, Sprite as rt, Slider as s, getBackgroundGraphics as sn, rgbToNumber as sr, DebugPanel as st, TransformOriginView as t, remountAll as tn, hexToNumber as tr, TileSprite as tt, NineSliceButton as u, getLayoutRect as un, normalizeEdgeInsets as ur, Button as ut, ScrollView as v, useForceRedraw as vn, DevConfig as vr, createFlashEffect as vt, AlertDialog as w, useRef as wn, applyDeathZone as wr, createPressEffect as wt, Divider as x, useLayoutSize as xn, spritePatcher as xr, createFloatEffect as xt, ScrollSlider as y, useLayoutEffect as yn, DevPresets as yr, createFlipInEffect as yt, DOMInputElement as z, applyDarkMode as zn, graphicsPatcher as zr, formatBadgeCount as zt };
31010
+ export { Portal as $, getContrastRatio as $n, normalizeVNodeLike as $t, useSpring as A, withHooks as An, resolveParticlePreset as Ar, createSwingEffect as At, releaseAllSVGTextures as B, applyLightMode as Bn, host as Br, getBadgeSizeConfig as Bt, CharText as C, useRedraw as Cn, particlesPatcher as Cr, createNoneEffect as Ct, Modal as D, useTheme as Dn, getFirstEmitter as Dr, createSlideInEffect as Dt, Dialog as E, useState as En, applyEmitterConfig as Er, createShakeEffect as Et, animatedSignal as F, defaultRadiusTokens as Fn, nineSlicePatcher as Fr, createZoomOutEffect as Ft, svgToTexture as G, midnightPreset as Gn, createTheme as Gt, releaseSVGTextures as H, generateColorScale as Hn, register as Hr, resolveBadgeTextStyle as Ht, isAnimatedSignal as I, defaultSizeTokens as In, imageCreator as Ir, useGameObjectEffect as It, clampProgressValue as J, alpha as Jn, themeRegistry as Jt, registerBuiltins as K, oceanBluePreset as Kn, getThemedProps as Kt, unwrapSignal as L, defaultSpacingTokens as Ln, imagePatcher as Lr, Badge as Lt, DEFAULT_SPRING_CONFIG as M, createDefaultTheme as Mn, buildEmitZone as Mr, createWiggleEffect as Mt, SPRING_PRESETS as N, defaultTheme as Nn, buildEmitZoneFromLayout as Nr, createWobbleEffect as Nt, Accordion as O, useViewportSize as On, isParticleEmitter as Or, createSlideOutEffect as Ot, SpringPhysics as P, createTextStyleTokens as Pn, nineSliceCreator as Pr, createZoomInEffect as Pt, calculateOverlayPosition as Q, ensureContrast as Qn, mountJSX as Qt, KeyboardInputManager as R, defaultTextStyleTokens as Rn, graphicsCreator as Rr, Tag as Rt, CharTextInput as S, useMemo as Sn, particlesCreator as Sr, createJelloEffect as St, WrapText as T, useScene as Tn, applyEmitZone as Tr, createPulseEffect as Tt, useSVGTexture as U, getPreset as Un, viewCreator as Ut, releaseSVGTexture as V, forestGreenPreset as Vn, nodeRegistry as Vr, getBadgeText as Vt, useSVGTextures as W, getPresetWithMode as Wn, viewPatcher as Wt, ContextMenu as X, darken as Xn, getMountStats as Xt, getProgressRatio as Y, createTextStyle as Yn, createElement as Yt, Popover as Z, darkenHex as Zn, mount as Zt, TransformOriginView as _, useEffect as _n, DebugLogger as _r, createFadeEffect as _t, RangeSlider as a, View as an, numberToRgb as ar, RadioButton as at, calculateSliderSize as b, useLayoutRect as bn, spriteCreator as br, createFlipOutEffect as bt, RefOriginView as c, getCurrent as cn, HexColor as cr, useThemeTokens as ct, Joystick as d, getLayoutSize$1 as dn, normalizeGap as dr, DEFAULT_EFFECT as dt, patchVNode as en, hex as er, Particles as et, Icon as f, getWorldLayoutRect as fn, tileSpriteCreator as fr, EFFECT_REGISTRY as ft, Dropdown as g, useCallback as gn, viewportRegistry as gr, createBreatheEffect as gt, Image$1 as h, useBackgroundGraphics as hn, textPatcher as hr, createBounceEffect as ht, Tabs as i, portalRegistry as in, numberToHex as ir, RadioGroup as it, useSprings as j, getRenderContext as jn, buildDeathZonesFromLayout as jr, createTadaEffect as jt, computed as k, useWorldLayoutRect as kn, PARTICLE_PRESET_REGISTRY as kr, createSpinEffect as kt, NineSliceButton as l, getLayoutProps as ln, normalizeCornerRadius as lr, Checkbox as lt, useIconPreload as m, shouldComponentUpdate as mn, textCreator as mr, resolveEffect as mt, Tab as n, unmount as nn, lighten as nr, Text as nt, Slider as o, disposeCtx as on, rgbToHsl as or, Graphics as ot, createIconComponent as p, shallowEqual as pn, tileSpritePatcher as pr, applyEffectByName as pt, ProgressBar as q, presets as qn, mergeThemes as qt, TabPanel as r, unmountJSX as rn, lightenHex as rr, Sprite as rt, Sidebar as s, getBackgroundGraphics as sn, rgbToNumber as sr, DebugPanel as st, Toggle as t, remountAll as tn, hexToNumber as tr, TileSprite as tt, NineSlice as u, getLayoutRect as un, normalizeEdgeInsets as ur, Button as ut, ScrollView as v, useForceRedraw as vn, DevConfig as vr, createFlashEffect as vt, AlertDialog as w, useRef as wn, applyDeathZone as wr, createPressEffect as wt, Divider as x, useLayoutSize as xn, spritePatcher as xr, createFloatEffect as xt, ScrollSlider as y, useLayoutEffect as yn, DevPresets as yr, createFlipInEffect as yt, DOMInputElement as z, applyDarkMode as zn, graphicsPatcher as zr, formatBadgeCount as zt };
30482
31011
 
30483
- //# sourceMappingURL=custom-DMZASXll.js.map
31012
+ //# sourceMappingURL=custom-BPY0TbuS.js.map