@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
@@ -16743,6 +16743,23 @@ function hasRenderableBackground(props) {
16743
16743
  const hasBorder = (props.borderWidth ?? 0) > 0 && props.borderColor !== void 0;
16744
16744
  return hasBackground || hasBorder;
16745
16745
  }
16746
+ function hasRoundedCorners(radius) {
16747
+ if (typeof radius === "number") return radius !== 0;
16748
+ return (radius.tl ?? 0) !== 0 || (radius.tr ?? 0) !== 0 || (radius.bl ?? 0) !== 0 || (radius.br ?? 0) !== 0;
16749
+ }
16750
+ function insetCornerRadius(radius, inset) {
16751
+ const insetSingleRadius = (value) => {
16752
+ const resolved = value ?? 0;
16753
+ return (resolved < 0 ? -1 : 1) * Math.max(0, Math.abs(resolved) - inset);
16754
+ };
16755
+ if (typeof radius === "number") return insetSingleRadius(radius);
16756
+ return {
16757
+ tl: insetSingleRadius(radius.tl),
16758
+ tr: insetSingleRadius(radius.tr),
16759
+ bl: insetSingleRadius(radius.bl),
16760
+ br: insetSingleRadius(radius.br)
16761
+ };
16762
+ }
16746
16763
  function drawBackground(graphics, props, width, height) {
16747
16764
  const bgColor = props.backgroundColor;
16748
16765
  const bgAlpha = props.backgroundAlpha ?? 1;
@@ -16751,14 +16768,21 @@ function drawBackground(graphics, props, width, height) {
16751
16768
  const borderWidth = props.borderWidth ?? 0;
16752
16769
  const borderAlpha = props.borderAlpha ?? 1;
16753
16770
  const hasBorder = borderWidth > 0 && borderColor !== void 0;
16771
+ const rounded = hasRoundedCorners(cornerRadius);
16754
16772
  if (bgColor !== void 0) graphics.fillStyle(bgColor, bgAlpha);
16755
16773
  if (hasBorder) graphics.lineStyle(borderWidth, borderColor, borderAlpha);
16756
- if (cornerRadius !== 0) {
16774
+ if (rounded) {
16757
16775
  if (bgColor !== void 0) graphics.fillRoundedRect(0, 0, width, height, cornerRadius);
16758
- if (hasBorder) graphics.strokeRoundedRect(0, 0, width, height, cornerRadius);
16776
+ if (hasBorder) {
16777
+ const inset = borderWidth / 2;
16778
+ graphics.strokeRoundedRect(inset, inset, Math.max(0, width - borderWidth), Math.max(0, height - borderWidth), insetCornerRadius(cornerRadius, inset));
16779
+ }
16759
16780
  } else {
16760
16781
  if (bgColor !== void 0) graphics.fillRect(0, 0, width, height);
16761
- if (hasBorder) graphics.strokeRect(0, 0, width, height);
16782
+ if (hasBorder) {
16783
+ const inset = borderWidth / 2;
16784
+ graphics.strokeRect(inset, inset, Math.max(0, width - borderWidth), Math.max(0, height - borderWidth));
16785
+ }
16762
16786
  }
16763
16787
  }
16764
16788
  function getSceneBackgroundTextureCache(scene) {
@@ -18278,6 +18302,12 @@ function getMargin(child) {
18278
18302
  };
18279
18303
  return margin ?? {};
18280
18304
  }
18305
+ function getHorizontalMargin$1(margin) {
18306
+ return (margin.left ?? 0) + (margin.right ?? 0);
18307
+ }
18308
+ function getVerticalMargin$1(margin) {
18309
+ return (margin.top ?? 0) + (margin.bottom ?? 0);
18310
+ }
18281
18311
  /**
18282
18312
  * Get effective size of a child
18283
18313
  * Calls __getLayoutSize if available, otherwise falls back to layoutProps or default
@@ -18291,8 +18321,13 @@ function getChildSize(child, parentSize, parentPadding) {
18291
18321
  if (child.__layoutProps && (child.__layoutProps.width !== void 0 || child.__layoutProps.height !== void 0)) {
18292
18322
  const layoutWidth = child.__layoutProps.width;
18293
18323
  const layoutHeight = child.__layoutProps.height;
18294
- let width = resolveSize(parseSize(layoutWidth), parentSize?.width, child.width ?? 100, parentPadding?.horizontal);
18295
- let height = resolveSize(parseSize(layoutHeight), parentSize?.height, child.height ?? 20, parentPadding?.vertical);
18324
+ const margin = getMargin(child);
18325
+ const parsedWidth = parseSize(layoutWidth);
18326
+ let width = resolveSize(parsedWidth, parentSize?.width, child.width ?? 100, parentPadding?.horizontal);
18327
+ if (parsedWidth.type === "fill") width = Math.max(0, width - getHorizontalMargin$1(margin));
18328
+ const parsedHeight = parseSize(layoutHeight);
18329
+ let height = resolveSize(parsedHeight, parentSize?.height, child.height ?? 20, parentPadding?.vertical);
18330
+ if (parsedHeight.type === "fill") height = Math.max(0, height - getVerticalMargin$1(margin));
18296
18331
  const { minWidth, maxWidth, minHeight, maxHeight } = child.__layoutProps;
18297
18332
  width = clampSize(width, minWidth, maxWidth, parentSize?.width, child.width, parentPadding?.horizontal);
18298
18333
  height = clampSize(height, minHeight, maxHeight, parentSize?.height, child.height, parentPadding?.vertical);
@@ -18340,6 +18375,21 @@ function processNestedContainer(child, calculateLayoutFn, parentSize, parentPadd
18340
18375
  }
18341
18376
  //#endregion
18342
18377
  //#region src/layout/utils/dimension-calculator.ts
18378
+ function normalizeMargin(margin) {
18379
+ if (typeof margin === "number") return {
18380
+ left: margin,
18381
+ top: margin,
18382
+ right: margin,
18383
+ bottom: margin
18384
+ };
18385
+ return margin ?? {};
18386
+ }
18387
+ function getHorizontalMargin(margin) {
18388
+ return (margin.left ?? 0) + (margin.right ?? 0);
18389
+ }
18390
+ function getVerticalMargin(margin) {
18391
+ return (margin.top ?? 0) + (margin.bottom ?? 0);
18392
+ }
18343
18393
  /**
18344
18394
  * Calculate final container dimensions
18345
18395
  * @param props - Layout properties
@@ -18360,8 +18410,13 @@ function calculateContainerSize(props, metrics, padding, direction, gap, childCo
18360
18410
  }
18361
18411
  const contentWidth = direction === "row" ? totalMainSizeWithGaps + padding.left + padding.right : metrics.maxWidth + padding.left + padding.right;
18362
18412
  const contentHeight = direction === "row" ? metrics.maxHeight + padding.top + padding.bottom : totalMainSizeWithGaps + padding.top + padding.bottom;
18363
- let width = resolveSize(parseSize(props.width), parentSize?.width, contentWidth, parentPadding?.horizontal);
18364
- let height = resolveSize(parseSize(props.height), parentSize?.height, contentHeight, parentPadding?.vertical);
18413
+ const parsedWidth = parseSize(props.width);
18414
+ let width = resolveSize(parsedWidth, parentSize?.width, contentWidth, parentPadding?.horizontal);
18415
+ const margin = normalizeMargin(props.margin);
18416
+ if (parsedWidth.type === "fill") width = Math.max(0, width - getHorizontalMargin(margin));
18417
+ const parsedHeight = parseSize(props.height);
18418
+ let height = resolveSize(parsedHeight, parentSize?.height, contentHeight, parentPadding?.vertical);
18419
+ if (parsedHeight.type === "fill") height = Math.max(0, height - getVerticalMargin(margin));
18365
18420
  width = clampSize(width, props.minWidth, props.maxWidth, parentSize?.width, contentWidth, void 0);
18366
18421
  height = clampSize(height, props.minHeight, props.maxHeight, parentSize?.height, contentHeight, void 0);
18367
18422
  return {
@@ -19980,10 +20035,13 @@ function buildDefaultTheme(colors) {
19980
20035
  },
19981
20036
  RadioButton: {
19982
20037
  selectedColor: colors.primary.DEFAULT.toNumber(),
19983
- color: colors.border.medium.toNumber(),
19984
- gap: 10,
19985
- size: 16,
19986
- innerSize: 16,
20038
+ color: colors.border.dark.toNumber(),
20039
+ borderWidth: 2,
20040
+ gap: 8,
20041
+ size: 18,
20042
+ innerSize: 8,
20043
+ disabledAlpha: .5,
20044
+ labelPosition: "right",
19987
20045
  labelStyle: {
19988
20046
  color: colors.text.DEFAULT.toString(),
19989
20047
  fontSize: "14px"
@@ -20008,7 +20066,7 @@ function buildDefaultTheme(colors) {
20008
20066
  size: "medium",
20009
20067
  maxCount: 99,
20010
20068
  disabledAlpha: .5,
20011
- textStyle: { fontStyle: "bold" }
20069
+ textStyle: { fontFamily: "Arial" }
20012
20070
  },
20013
20071
  Tag: {
20014
20072
  tone: "neutral",
@@ -20016,7 +20074,7 @@ function buildDefaultTheme(colors) {
20016
20074
  size: "medium",
20017
20075
  closeSize: 16,
20018
20076
  disabledAlpha: .5,
20019
- textStyle: { fontStyle: "bold" }
20077
+ textStyle: { fontFamily: "Arial" }
20020
20078
  },
20021
20079
  Popover: {
20022
20080
  placement: "bottom",
@@ -20025,6 +20083,8 @@ function buildDefaultTheme(colors) {
20025
20083
  closeOnOutside: true,
20026
20084
  closeOnEscape: true,
20027
20085
  viewportPadding: 8,
20086
+ openDuration: 120,
20087
+ closeDuration: 100,
20028
20088
  backgroundColor: colors.surface.dark.toNumber(),
20029
20089
  borderColor: colors.border.medium.toNumber(),
20030
20090
  borderWidth: 1,
@@ -20075,65 +20135,226 @@ function buildDefaultTheme(colors) {
20075
20135
  }
20076
20136
  },
20077
20137
  ScrollSlider: {
20078
- borderColor: colors.border.dark.toNumber(),
20138
+ borderColor: colors.border.medium.toNumber(),
20079
20139
  trackColor: colors.surface.dark.toNumber(),
20080
- thumbColor: colors.primary.dark.toNumber(),
20081
- borderWidth: 2,
20140
+ trackCornerRadius: 0,
20141
+ thumbColor: colors.primary.DEFAULT.toNumber(),
20142
+ thumbActiveColor: colors.primary.dark.toNumber(),
20143
+ thumbBorderColor: colors.primary.light.toNumber(),
20144
+ thumbBorderWidth: 1,
20145
+ thumbCornerRadius: 0,
20146
+ borderWidth: 1,
20147
+ cornerRadius: 0,
20082
20148
  minThumbSize: 30,
20083
20149
  size: 24
20084
20150
  },
20085
20151
  Button: {
20086
- disabledColor: colors.background.DEFAULT.toNumber(),
20087
- iconSize: 24,
20088
- backgroundColor: colors.primary.DEFAULT.toNumber(),
20152
+ variant: "primary",
20153
+ size: "medium",
20154
+ disabledColor: colors.surface.medium.toNumber(),
20155
+ disabledAlpha: .48,
20156
+ iconSize: 18,
20157
+ minWidth: 92,
20158
+ height: 40,
20159
+ backgroundColor: colors.primary.medium.toNumber(),
20089
20160
  backgroundAlpha: 1,
20090
20161
  borderColor: colors.primary.dark.toNumber(),
20091
20162
  borderWidth: 1,
20092
- cornerRadius: 6,
20093
- padding: 8,
20163
+ cornerRadius: 8,
20164
+ padding: {
20165
+ top: 8,
20166
+ bottom: 8,
20167
+ left: 16,
20168
+ right: 16
20169
+ },
20094
20170
  gap: 8,
20095
20171
  justifyContent: "center",
20096
20172
  alignItems: "center",
20173
+ effect: "press",
20174
+ effectConfig: {
20175
+ intensity: .94,
20176
+ time: 140
20177
+ },
20178
+ textStyle: {
20179
+ ...textStyles.medium,
20180
+ color: "#ffffff",
20181
+ fontStyle: "bold"
20182
+ },
20183
+ Text: { style: {
20184
+ ...textStyles.medium,
20185
+ color: "#ffffff",
20186
+ fontStyle: "bold"
20187
+ } },
20188
+ Icon: {
20189
+ size: 18,
20190
+ tint: 16777215
20191
+ },
20097
20192
  primary: {
20098
20193
  backgroundColor: colors.primary.medium.toNumber(),
20099
- borderColor: colors.primary.dark.toNumber()
20194
+ borderColor: colors.primary.dark.toNumber(),
20195
+ textStyle: {
20196
+ color: "#ffffff",
20197
+ fontStyle: "bold"
20198
+ },
20199
+ Icon: { tint: 16777215 }
20100
20200
  },
20101
20201
  secondary: {
20102
- backgroundColor: colors.secondary.DEFAULT.toNumber(),
20103
- borderColor: colors.secondary.dark.toNumber(),
20104
- effect: "press",
20105
- effectConfig: {
20106
- intensity: .9,
20107
- time: 200
20108
- }
20202
+ backgroundColor: colors.surface.lightest.toNumber(),
20203
+ backgroundAlpha: .92,
20204
+ borderColor: colors.border.light.toNumber(),
20205
+ borderWidth: 1,
20206
+ textStyle: {
20207
+ color: colors.text.dark.toString(),
20208
+ fontStyle: "bold"
20209
+ },
20210
+ Icon: { tint: colors.text.dark.toNumber() }
20109
20211
  },
20110
20212
  outline: {
20111
- backgroundColor: 0,
20112
- backgroundAlpha: 0,
20113
- borderColor: colors.accent.DEFAULT.toNumber(),
20213
+ backgroundColor: colors.primary.dark.toNumber(),
20214
+ backgroundAlpha: .08,
20215
+ borderColor: colors.primary.light.toNumber(),
20114
20216
  borderWidth: 2,
20115
- effect: "flash",
20116
- effectConfig: {
20117
- intensity: 1.15,
20118
- time: 200
20217
+ textStyle: {
20218
+ color: colors.primary.light.toString(),
20219
+ fontStyle: "bold"
20220
+ },
20221
+ Icon: { tint: colors.primary.light.toNumber() }
20222
+ },
20223
+ ghost: {
20224
+ backgroundColor: colors.surface.light.toNumber(),
20225
+ backgroundAlpha: .18,
20226
+ borderColor: colors.border.medium.toNumber(),
20227
+ borderAlpha: .35,
20228
+ borderWidth: 1,
20229
+ textStyle: {
20230
+ color: colors.text.light.toString(),
20231
+ fontStyle: "bold"
20232
+ },
20233
+ Icon: { tint: colors.text.light.toNumber() }
20234
+ },
20235
+ danger: {
20236
+ backgroundColor: colors.error.medium.toNumber(),
20237
+ borderColor: colors.error.dark.toNumber(),
20238
+ textStyle: {
20239
+ color: "#ffffff",
20240
+ fontStyle: "bold"
20241
+ },
20242
+ Icon: { tint: 16777215 }
20243
+ },
20244
+ variants: {
20245
+ primary: {
20246
+ backgroundColor: colors.primary.medium.toNumber(),
20247
+ borderColor: colors.primary.dark.toNumber()
20248
+ },
20249
+ secondary: {
20250
+ backgroundColor: colors.surface.lightest.toNumber(),
20251
+ backgroundAlpha: .92,
20252
+ borderColor: colors.border.light.toNumber(),
20253
+ borderWidth: 1
20254
+ },
20255
+ outline: {
20256
+ backgroundColor: colors.primary.dark.toNumber(),
20257
+ backgroundAlpha: .08,
20258
+ borderColor: colors.primary.light.toNumber(),
20259
+ borderWidth: 2
20260
+ },
20261
+ ghost: {
20262
+ backgroundColor: colors.surface.light.toNumber(),
20263
+ backgroundAlpha: .18,
20264
+ borderColor: colors.border.medium.toNumber(),
20265
+ borderAlpha: .35,
20266
+ borderWidth: 1
20267
+ },
20268
+ danger: {
20269
+ backgroundColor: colors.error.medium.toNumber(),
20270
+ borderColor: colors.error.dark.toNumber()
20119
20271
  }
20120
20272
  },
20121
20273
  small: {
20122
- padding: 6,
20123
- cornerRadius: 4
20274
+ minWidth: 72,
20275
+ height: 32,
20276
+ padding: {
20277
+ top: 6,
20278
+ bottom: 6,
20279
+ left: 12,
20280
+ right: 12
20281
+ },
20282
+ cornerRadius: 7,
20283
+ gap: 6,
20284
+ iconSize: 15,
20285
+ textStyle: { fontSize: "12px" },
20286
+ Icon: { size: 15 }
20124
20287
  },
20125
20288
  medium: {
20126
- padding: 8,
20127
- cornerRadius: 6
20289
+ minWidth: 92,
20290
+ height: 40,
20291
+ padding: {
20292
+ top: 8,
20293
+ bottom: 8,
20294
+ left: 16,
20295
+ right: 16
20296
+ },
20297
+ cornerRadius: 8,
20298
+ gap: 8,
20299
+ iconSize: 18,
20300
+ Icon: { size: 18 }
20128
20301
  },
20129
20302
  large: {
20303
+ minWidth: 116,
20304
+ height: 48,
20130
20305
  padding: {
20131
- top: 12,
20132
- bottom: 12,
20133
- left: 8,
20134
- right: 8
20306
+ top: 10,
20307
+ bottom: 10,
20308
+ left: 20,
20309
+ right: 20
20135
20310
  },
20136
- cornerRadius: 8
20311
+ cornerRadius: 10,
20312
+ gap: 10,
20313
+ iconSize: 22,
20314
+ textStyle: { fontSize: "18px" },
20315
+ Icon: { size: 22 }
20316
+ },
20317
+ sizes: {
20318
+ small: {
20319
+ minWidth: 72,
20320
+ height: 32,
20321
+ padding: {
20322
+ top: 6,
20323
+ bottom: 6,
20324
+ left: 12,
20325
+ right: 12
20326
+ },
20327
+ cornerRadius: 7,
20328
+ gap: 6,
20329
+ iconSize: 15
20330
+ },
20331
+ medium: {
20332
+ minWidth: 92,
20333
+ height: 40,
20334
+ padding: {
20335
+ top: 8,
20336
+ bottom: 8,
20337
+ left: 16,
20338
+ right: 16
20339
+ },
20340
+ cornerRadius: 8,
20341
+ gap: 8,
20342
+ iconSize: 18
20343
+ },
20344
+ large: {
20345
+ minWidth: 116,
20346
+ height: 48,
20347
+ padding: {
20348
+ top: 10,
20349
+ bottom: 10,
20350
+ left: 20,
20351
+ right: 20
20352
+ },
20353
+ cornerRadius: 10,
20354
+ gap: 10,
20355
+ iconSize: 22,
20356
+ textStyle: { fontSize: "18px" }
20357
+ }
20137
20358
  }
20138
20359
  },
20139
20360
  Sidebar: {
@@ -20365,64 +20586,110 @@ function buildDefaultTheme(colors) {
20365
20586
  lineHeight: 1.2,
20366
20587
  wordWrap: false,
20367
20588
  disabledColor: colors.border.dark.toNumber(),
20368
- focusedBorderColor: colors.accent.lightest.toNumber(),
20369
- backgroundColor: colors.surface.medium.toNumber(),
20589
+ disabledBackgroundColor: colors.surface.medium.toNumber(),
20590
+ disabledBorderColor: colors.border.light.toNumber(),
20591
+ disabledAlpha: .6,
20592
+ focusedBorderColor: colors.primary.DEFAULT.toNumber(),
20593
+ backgroundColor: colors.surface.lightest.toNumber(),
20370
20594
  borderColor: colors.border.medium.toNumber(),
20371
20595
  borderWidth: 1,
20372
- padding: 8,
20373
- textStyle: textStyles.DEFAULT
20596
+ cornerRadius: 7,
20597
+ padding: {
20598
+ left: 10,
20599
+ right: 10,
20600
+ top: 8,
20601
+ bottom: 8
20602
+ },
20603
+ textStyle: {
20604
+ ...textStyles.DEFAULT,
20605
+ color: colors.text.dark.toString()
20606
+ },
20607
+ placeholderStyle: {
20608
+ ...textStyles.DEFAULT,
20609
+ color: colors.text.light.toString()
20610
+ }
20374
20611
  },
20375
20612
  Dropdown: {
20376
20613
  trigger: {
20377
- backgroundColor: colors.surface.medium.toNumber(),
20614
+ backgroundColor: colors.surface.light.toNumber(),
20378
20615
  borderColor: colors.border.medium.toNumber(),
20379
20616
  borderWidth: 1,
20380
- cornerRadius: 4,
20617
+ cornerRadius: 7,
20381
20618
  padding: {
20382
20619
  left: 12,
20383
- right: 12,
20384
- top: 8,
20385
- bottom: 8
20620
+ right: 10,
20621
+ top: 9,
20622
+ bottom: 9
20386
20623
  },
20387
- width: "fill"
20624
+ width: "fill",
20625
+ gap: 8
20388
20626
  },
20389
20627
  triggerHover: { borderColor: colors.primary.DEFAULT.toNumber() },
20390
20628
  triggerOpen: {
20391
20629
  borderColor: colors.primary.DEFAULT.toNumber(),
20392
- backgroundColor: colors.surface.dark.toNumber()
20630
+ borderWidth: 2,
20631
+ backgroundColor: colors.surface.lightest.toNumber()
20393
20632
  },
20394
20633
  triggerDisabled: {
20395
20634
  backgroundColor: colors.surface.light.toNumber(),
20396
20635
  alpha: .5
20397
20636
  },
20398
20637
  arrow: {
20399
- color: colors.text.DEFAULT.toNumber(),
20400
- size: 12
20638
+ color: colors.text.dark.toNumber(),
20639
+ size: 12,
20640
+ strokeWidth: 2
20641
+ },
20642
+ selectionIndicator: {
20643
+ color: colors.primary.dark.toNumber(),
20644
+ size: 16,
20645
+ strokeWidth: 2
20401
20646
  },
20402
20647
  overlay: {
20403
- backgroundColor: colors.surface.medium.toNumber(),
20648
+ backgroundColor: colors.surface.lightest.toNumber(),
20404
20649
  borderColor: colors.border.medium.toNumber(),
20405
20650
  borderWidth: 1,
20406
- cornerRadius: 4,
20651
+ cornerRadius: 7,
20407
20652
  maxHeight: 300,
20408
- padding: 4
20653
+ padding: 6
20654
+ },
20655
+ option: {
20656
+ padding: {
20657
+ left: 10,
20658
+ right: 10,
20659
+ top: 8,
20660
+ bottom: 8
20661
+ },
20662
+ cornerRadius: 5,
20663
+ gap: 8
20409
20664
  },
20410
- option: { padding: {
20411
- left: 12,
20412
- right: 12,
20413
- top: 8,
20414
- bottom: 8
20415
- } },
20416
20665
  optionSelected: {
20417
- backgroundColor: colors.primary.light.toNumber(),
20418
- Text: { style: textStyles.DEFAULT }
20666
+ backgroundColor: colors.primary.lightest.toNumber(),
20667
+ borderColor: colors.primary.light.toNumber(),
20668
+ borderWidth: 1,
20669
+ backgroundAlpha: .9,
20670
+ Text: { style: {
20671
+ ...textStyles.DEFAULT,
20672
+ color: colors.text.dark.toString(),
20673
+ fontStyle: "bold"
20674
+ } }
20419
20675
  },
20420
20676
  optionDisabled: { alpha: .3 },
20421
20677
  textStyle: textStyles.DEFAULT,
20422
- placeholderStyle: textStyles.DEFAULT,
20678
+ placeholderStyle: {
20679
+ ...textStyles.DEFAULT,
20680
+ color: colors.text.light.toString()
20681
+ },
20423
20682
  filterInput: {
20424
20683
  backgroundColor: colors.surface.lightest.toNumber(),
20425
- borderColor: colors.border.medium.toNumber()
20684
+ borderColor: colors.border.medium.toNumber(),
20685
+ borderWidth: 1,
20686
+ cornerRadius: 6,
20687
+ padding: {
20688
+ left: 10,
20689
+ right: 10,
20690
+ top: 6,
20691
+ bottom: 6
20692
+ }
20426
20693
  },
20427
20694
  animationConfig: "stiff",
20428
20695
  optionGap: 2
@@ -20473,9 +20740,15 @@ function buildDefaultTheme(colors) {
20473
20740
  height: 28,
20474
20741
  thumbSize: 24,
20475
20742
  trackColorOff: colors.surface.dark.toNumber(),
20476
- trackColorOn: colors.success.DEFAULT.toNumber(),
20743
+ trackColorOn: colors.primary.DEFAULT.toNumber(),
20744
+ trackBorderColorOff: colors.border.medium.toNumber(),
20745
+ trackBorderColorOn: colors.primary.dark.toNumber(),
20746
+ trackBorderWidth: 1,
20477
20747
  thumbColor: colors.surface.lightest.toNumber(),
20748
+ thumbBorderColor: colors.border.light.toNumber(),
20749
+ thumbBorderWidth: 1,
20478
20750
  disabledColor: colors.border.medium.toNumber(),
20751
+ disabledAlpha: .5,
20479
20752
  padding: 2,
20480
20753
  duration: 200,
20481
20754
  gap: 8,
@@ -22067,13 +22340,16 @@ function patchVNode(parent, oldV, newV) {
22067
22340
  }
22068
22341
  if (newV.props !== void 0) ctx.componentVNode.props = newV.props;
22069
22342
  if (newV.children !== void 0) ctx.componentVNode.children = newV.children;
22343
+ const previousTheme = ctx.theme;
22070
22344
  if (newV.__theme !== void 0) ctx.theme = newV.__theme;
22071
22345
  const newVWithCtx = setVNodePropSafe(newV, "__ctx", ctx);
22072
22346
  const propsWithChildren = ctx.componentVNode.children?.length ? {
22073
22347
  ...ctx.componentVNode.props ?? {},
22074
22348
  children: ctx.componentVNode.children
22075
22349
  } : ctx.componentVNode.props;
22076
- if (!shouldComponentUpdate(ctx, propsWithChildren)) return;
22350
+ const themeChanged = !Object.is(previousTheme, ctx.theme);
22351
+ const propsChanged = shouldComponentUpdate(ctx, propsWithChildren);
22352
+ if (!themeChanged && !propsChanged) return;
22077
22353
  const renderedNext = normalizeVNodeLike(withHooks(ctx, () => newVWithCtx.type(propsWithChildren)));
22078
22354
  if (!renderedNext) {
22079
22355
  ctx.vnode = renderedNext;
@@ -22378,6 +22654,7 @@ var ThemeRegistry = class {
22378
22654
  ...styles
22379
22655
  });
22380
22656
  }
22657
+ this.notifyListeners();
22381
22658
  }
22382
22659
  /**
22383
22660
  * Set the entire global theme (replaces current theme)
@@ -22571,12 +22848,21 @@ function deepMergeDefined(base, override) {
22571
22848
  function extractNestedThemes(theme) {
22572
22849
  const ownProps = { ...theme };
22573
22850
  const nestedThemes = {};
22851
+ const internalPrimitiveNames = new Set([
22852
+ "view",
22853
+ "text",
22854
+ "nineslice",
22855
+ "sprite",
22856
+ "image",
22857
+ "graphics",
22858
+ "tilesprite",
22859
+ "particles"
22860
+ ]);
22574
22861
  const allComponentNames = new Set([
22575
- "View",
22576
- "Text",
22577
- "NineSlice",
22862
+ ...Object.keys(defaultTheme),
22863
+ ...Object.keys(themeRegistry.getGlobalTheme()),
22578
22864
  ...Array.from(themeRegistry.getCustomComponentNames())
22579
- ]);
22865
+ ].filter((key) => key !== "__colorPreset" && !internalPrimitiveNames.has(key)));
22580
22866
  for (const key in ownProps) if (allComponentNames.has(key)) {
22581
22867
  nestedThemes[key] = ownProps[key];
22582
22868
  delete ownProps[key];
@@ -23189,41 +23475,41 @@ var DEFAULT_TONES = {
23189
23475
  };
23190
23476
  var DEFAULT_SIZES = {
23191
23477
  small: {
23192
- height: 20,
23478
+ height: 22,
23193
23479
  padding: {
23194
- left: 7,
23195
- right: 7,
23196
- top: 2,
23197
- bottom: 2
23480
+ left: 8,
23481
+ right: 8,
23482
+ top: 3,
23483
+ bottom: 3
23198
23484
  },
23199
- fontSize: 11,
23200
- cornerRadius: 10,
23485
+ fontSize: 12,
23486
+ cornerRadius: 11,
23201
23487
  gap: 5,
23202
23488
  dotSize: 8
23203
23489
  },
23204
23490
  medium: {
23205
- height: 24,
23491
+ height: 26,
23206
23492
  padding: {
23207
- left: 9,
23208
- right: 9,
23209
- top: 3,
23210
- bottom: 3
23493
+ left: 10,
23494
+ right: 10,
23495
+ top: 4,
23496
+ bottom: 4
23211
23497
  },
23212
- fontSize: 13,
23213
- cornerRadius: 12,
23498
+ fontSize: 14,
23499
+ cornerRadius: 13,
23214
23500
  gap: 6,
23215
23501
  dotSize: 10
23216
23502
  },
23217
23503
  large: {
23218
- height: 30,
23504
+ height: 32,
23219
23505
  padding: {
23220
- left: 12,
23221
- right: 12,
23222
- top: 4,
23223
- bottom: 4
23506
+ left: 13,
23507
+ right: 13,
23508
+ top: 5,
23509
+ bottom: 5
23224
23510
  },
23225
23511
  fontSize: 18,
23226
- cornerRadius: 15,
23512
+ cornerRadius: 16,
23227
23513
  gap: 8,
23228
23514
  dotSize: 12
23229
23515
  }
@@ -23277,7 +23563,8 @@ function resolveBadgeTextStyle(options) {
23277
23563
  }
23278
23564
  function Badge(props) {
23279
23565
  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;
23280
- const { props: themed, nestedTheme } = getThemedProps("Badge", useTheme(), theme ?? {});
23566
+ const localTheme = useTheme();
23567
+ const { props: themed, nestedTheme } = getThemedProps("Badge", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
23281
23568
  const tone = explicitTone ?? themed.tone ?? "neutral";
23282
23569
  const variant = explicitVariant ?? themed.variant ?? "solid";
23283
23570
  const size = explicitSize ?? themed.size ?? "medium";
@@ -23333,13 +23620,16 @@ function Badge(props) {
23333
23620
  }
23334
23621
  function Tag(props) {
23335
23622
  const { selected = false, onRemove, closeLabel = "x", tone, variant, size, theme, children, label, textStyle, ...badgeProps } = props;
23336
- const { props: themed, nestedTheme } = getThemedProps("Tag", useTheme(), theme ?? {});
23623
+ const localTheme = useTheme();
23624
+ const { props: themed, nestedTheme } = getThemedProps("Tag", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
23337
23625
  const resolvedTone = tone ?? themed.tone ?? (selected ? "primary" : "neutral");
23338
23626
  const resolvedVariant = variant ?? themed.variant ?? (selected ? "solid" : "soft");
23339
23627
  const resolvedSize = size ?? themed.size ?? "medium";
23628
+ const colors = resolveBadgeColors(resolvedTone, resolvedVariant);
23629
+ const closeSize = themed.closeSize ?? 16;
23340
23630
  const resolvedTextStyle = resolveBadgeTextStyle({
23341
23631
  size: resolvedSize,
23342
- textColor: resolveBadgeColors(resolvedTone, resolvedVariant).textColor,
23632
+ textColor: colors.textColor,
23343
23633
  themedTextStyle: themed.textStyle,
23344
23634
  explicitTextStyle: textStyle
23345
23635
  });
@@ -23358,16 +23648,15 @@ function Tag(props) {
23358
23648
  }),
23359
23649
  children,
23360
23650
  onRemove && /* @__PURE__ */ require_jsx_runtime.jsx(Button, {
23361
- width: themed.closeSize ?? 16,
23362
- height: themed.closeSize ?? 16,
23651
+ width: closeSize,
23652
+ height: closeSize,
23653
+ minWidth: closeSize,
23363
23654
  padding: 0,
23364
- cornerRadius: (themed.closeSize ?? 16) / 2,
23655
+ cornerRadius: closeSize / 2,
23365
23656
  onClick: onRemove,
23366
23657
  theme: nestedTheme,
23367
- children: /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
23368
- text: closeLabel,
23369
- style: resolvedTextStyle
23370
- })
23658
+ label: closeLabel,
23659
+ textStyle: resolvedTextStyle
23371
23660
  })
23372
23661
  ]
23373
23662
  });
@@ -24290,6 +24579,37 @@ function resolveEffect(props, theme) {
24290
24579
  }
24291
24580
  //#endregion
24292
24581
  //#region src/components/custom/Button.tsx
24582
+ function mergeButtonTheme(base, override) {
24583
+ return override ? {
24584
+ ...base,
24585
+ ...override
24586
+ } : base;
24587
+ }
24588
+ function resolveButtonSlotTheme(theme, group, name) {
24589
+ if (!name) return void 0;
24590
+ return {
24591
+ ...theme[group]?.[name] ?? {},
24592
+ ...theme[name] ?? {}
24593
+ };
24594
+ }
24595
+ function buildButtonContentTheme(theme) {
24596
+ const textTheme = theme.Text ?? {};
24597
+ const iconTheme = theme.Icon ?? {};
24598
+ const mergedTextStyle = theme.textStyle || textTheme.style ? {
24599
+ ...textTheme.style ?? {},
24600
+ ...theme.textStyle ?? {}
24601
+ } : void 0;
24602
+ return {
24603
+ Text: mergedTextStyle ? {
24604
+ ...textTheme,
24605
+ style: mergedTextStyle
24606
+ } : textTheme,
24607
+ Icon: {
24608
+ ...iconTheme,
24609
+ ...theme.iconSize !== void 0 ? { size: theme.iconSize } : {}
24610
+ }
24611
+ };
24612
+ }
24293
24613
  /**
24294
24614
  * Generic Button component
24295
24615
  * Provides variant, size, and disabled state support
@@ -24308,50 +24628,61 @@ function resolveEffect(props, theme) {
24308
24628
  * ```
24309
24629
  */
24310
24630
  function Button(props) {
24311
- const { children, onClick, disabled, variant, size, width, height, ...restProps } = props;
24312
- const { props: themed } = getThemedProps("Button", void 0, {});
24631
+ const { children, label, text, onClick, disabled = false, variant, size, width, height, textStyle, iconSize, disabledAlpha, alpha, theme, onTouch, ...restProps } = props;
24632
+ const localTheme = useTheme();
24633
+ const { props: themed, nestedTheme } = getThemedProps("Button", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
24313
24634
  const ref = useRef(null);
24314
24635
  const { applyEffect } = useGameObjectEffect(ref);
24315
24636
  const themedButton = themed;
24316
- let variantTheme = { ...themedButton };
24317
- if (variant && themedButton[variant]) {
24318
- const variantOverrides = themedButton[variant];
24319
- variantTheme = {
24320
- ...variantTheme,
24321
- ...variantOverrides
24322
- };
24323
- }
24324
- let sizeTheme = { ...variantTheme };
24325
- if (size && themedButton[size]) {
24326
- const sizeOverrides = themedButton[size];
24327
- sizeTheme = {
24328
- ...sizeTheme,
24329
- ...sizeOverrides
24330
- };
24331
- }
24637
+ const resolvedVariant = variant ?? themedButton.variant ?? "primary";
24638
+ const resolvedSize = size ?? themedButton.size ?? "medium";
24639
+ const sizeTheme = mergeButtonTheme(mergeButtonTheme({ ...themedButton }, resolveButtonSlotTheme(themedButton, "variants", resolvedVariant)), resolveButtonSlotTheme(themedButton, "sizes", resolvedSize));
24640
+ const resolvedTextStyle = sizeTheme.textStyle || textStyle ? {
24641
+ ...sizeTheme.textStyle ?? {},
24642
+ ...textStyle ?? {}
24643
+ } : void 0;
24644
+ const resolvedIconSize = iconSize ?? sizeTheme.iconSize;
24645
+ const resolvedDisabledAlpha = disabledAlpha ?? sizeTheme.disabledAlpha ?? .5;
24646
+ const contentStyleProps = {
24647
+ ...resolvedTextStyle ? { textStyle: resolvedTextStyle } : {},
24648
+ ...resolvedIconSize !== void 0 ? { iconSize: resolvedIconSize } : {}
24649
+ };
24332
24650
  const effectiveTheme = disabled ? {
24333
24651
  ...sizeTheme,
24334
- backgroundColor: themedButton.disabledColor ?? sizeTheme?.backgroundColor,
24335
- alpha: .5
24336
- } : sizeTheme;
24337
- const handleTouch = !disabled ? () => {
24338
- const resolved = resolveEffect(props, themed);
24652
+ backgroundColor: sizeTheme.disabledColor ?? themedButton.disabledColor ?? sizeTheme.backgroundColor,
24653
+ alpha: alpha ?? resolvedDisabledAlpha,
24654
+ ...contentStyleProps
24655
+ } : {
24656
+ ...sizeTheme,
24657
+ ...alpha !== void 0 ? { alpha } : {},
24658
+ ...contentStyleProps
24659
+ };
24660
+ const handleTouch = !disabled ? (event) => {
24661
+ const resolved = resolveEffect(props, effectiveTheme);
24339
24662
  applyEffectByName(applyEffect, resolved.effect, resolved.effectConfig);
24663
+ onTouch?.(event);
24340
24664
  onClick?.();
24341
24665
  } : void 0;
24342
- const { disabledColor: _disabledColor, effect: _effect, effectConfig: _effectConfig, primary: _primary, secondary: _secondary, outline: _outline, small: _small, medium: _medium, large: _large, ...viewThemeProps } = effectiveTheme;
24666
+ const generatedText = label ?? text;
24667
+ const content = children ?? (generatedText !== void 0 ? /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
24668
+ text: `${generatedText}`,
24669
+ ...resolvedTextStyle ? { style: resolvedTextStyle } : {}
24670
+ }) : null);
24671
+ const contentTheme = mergeThemes(nestedTheme, buildButtonContentTheme(effectiveTheme));
24672
+ 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;
24343
24673
  return /* @__PURE__ */ require_jsx_runtime.jsx(View, {
24344
24674
  ref,
24345
- width,
24346
- height,
24347
24675
  enableGestures: !disabled,
24348
24676
  direction: "row",
24349
24677
  alignItems: "center",
24350
24678
  justifyContent: "center",
24351
- ...handleTouch && { onTouch: handleTouch },
24352
24679
  ...viewThemeProps,
24680
+ width: width ?? effectiveTheme.width,
24681
+ height: height ?? effectiveTheme.height,
24353
24682
  ...restProps,
24354
- children
24683
+ ...handleTouch && { onTouch: handleTouch },
24684
+ theme: contentTheme,
24685
+ children: content
24355
24686
  });
24356
24687
  }
24357
24688
  //#endregion
@@ -24391,7 +24722,8 @@ function drawDefaultIndicator(graphics, indicatorProps) {
24391
24722
  }
24392
24723
  }
24393
24724
  function Checkbox(props) {
24394
- const { props: themed, nestedTheme } = getThemedProps("Checkbox", useTheme(), props.theme ?? {});
24725
+ const localTheme = useTheme();
24726
+ const { props: themed, nestedTheme } = getThemedProps("Checkbox", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
24395
24727
  const size = themed.size ?? 20;
24396
24728
  const gap = themed.gap ?? 8;
24397
24729
  const labelPosition = props.labelPosition ?? themed.labelPosition ?? "right";
@@ -24628,7 +24960,7 @@ function collectSnapshot(scene) {
24628
24960
  frameMs,
24629
24961
  phaserVersion: phaser.VERSION,
24630
24962
  renderer: resolveRendererName(scene),
24631
- viewport: `${scene.scale.width}x${scene.scale.height}`,
24963
+ viewport: `${scene.scale.width.toFixed(0)}x${scene.scale.height.toFixed(0)}`,
24632
24964
  textureCount: resolveTextureCount(scene),
24633
24965
  mountsTotal: mountStats.totalMounts,
24634
24966
  mountsByType: summarizeMap(mountStats.byType),
@@ -24743,38 +25075,61 @@ function Graphics(props) {
24743
25075
  * @returns RadioButton JSX element
24744
25076
  */
24745
25077
  function RadioButton(props) {
24746
- const { props: themed, nestedTheme } = getThemedProps("RadioButton", void 0, {});
24747
- const size = themed.size ?? 16;
24748
- const innerSize = themed.innerSize ?? size * .75;
25078
+ const localTheme = useTheme();
25079
+ const { props: themed, nestedTheme } = getThemedProps("RadioButton", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
25080
+ const size = themed.size ?? 18;
25081
+ const innerSize = themed.innerSize ?? Math.max(6, Math.round(size * .45));
24749
25082
  const innerRadius = innerSize * .5;
24750
25083
  const outerRadius = size * .5;
25084
+ const borderWidth = themed.borderWidth ?? Math.max(2, Math.round(size * .11));
25085
+ const selected = props.selected ?? false;
25086
+ const disabled = props.disabled ?? false;
25087
+ const disabledAlpha = themed.disabledAlpha ?? .5;
25088
+ const labelPosition = props.labelPosition ?? themed.labelPosition ?? "right";
25089
+ const selectedColor = themed.selectedColor ?? 2201331;
25090
+ const idleColor = themed.color ?? 7697781;
25091
+ const activeColor = selected ? selectedColor : idleColor;
25092
+ const indicatorFillColor = themed.backgroundColor;
25093
+ const gap = themed.gap ?? 8;
25094
+ const label = labelPosition !== "none" ? /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
25095
+ text: props.label,
25096
+ style: themed.labelStyle,
25097
+ alpha: disabled ? disabledAlpha : 1
25098
+ }) : null;
25099
+ const handleClick = () => {
25100
+ if (disabled) return;
25101
+ props.onClick?.();
25102
+ };
24751
25103
  return /* @__PURE__ */ require_jsx_runtime.jsxs(View, {
24752
25104
  direction: "row",
24753
25105
  alignItems: "center",
24754
- enableGestures: true,
24755
- onTouch: () => props.onClick?.(),
25106
+ enableGestures: !disabled,
25107
+ onTouch: handleClick,
24756
25108
  theme: nestedTheme,
24757
- gap: themed.gap,
24758
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(View, {
24759
- width: size,
24760
- height: size,
24761
- backgroundColor: themed.color,
24762
- alignItems: "center",
24763
- justifyContent: "center",
24764
- backgroundAlpha: 1,
24765
- padding: 0,
24766
- cornerRadius: outerRadius,
24767
- children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
24768
- width: innerSize,
24769
- height: innerSize,
24770
- backgroundColor: themed.selectedColor,
24771
- visible: props.selected ?? false,
24772
- cornerRadius: innerRadius
24773
- })
24774
- }), /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
24775
- text: props.label,
24776
- style: themed.labelStyle
24777
- })]
25109
+ gap,
25110
+ alpha: disabled ? disabledAlpha : 1,
25111
+ children: [
25112
+ labelPosition === "left" && label,
25113
+ /* @__PURE__ */ require_jsx_runtime.jsx(View, {
25114
+ width: size,
25115
+ height: size,
25116
+ ...indicatorFillColor !== void 0 && { backgroundColor: indicatorFillColor },
25117
+ borderColor: activeColor,
25118
+ borderWidth,
25119
+ alignItems: "center",
25120
+ justifyContent: "center",
25121
+ padding: 0,
25122
+ cornerRadius: outerRadius,
25123
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
25124
+ width: innerSize,
25125
+ height: innerSize,
25126
+ backgroundColor: selectedColor,
25127
+ visible: selected,
25128
+ cornerRadius: innerRadius
25129
+ })
25130
+ }),
25131
+ labelPosition !== "left" && label
25132
+ ]
24778
25133
  }, props.key);
24779
25134
  }
24780
25135
  //#endregion
@@ -24789,10 +25144,12 @@ function RadioButton(props) {
24789
25144
  * @returns RadioGroup JSX element
24790
25145
  */
24791
25146
  function RadioGroup(props) {
24792
- const { props: themed, nestedTheme } = getThemedProps("RadioButton", void 0, {});
24793
- const [selected, setSelected] = useState(props.value ?? "");
25147
+ const { props: themed, nestedTheme } = getThemedProps("RadioButton", useTheme(), {});
25148
+ const [internalSelected, setInternalSelected] = useState(props.value ?? props.defaultValue ?? "");
25149
+ const selected = props.value ?? internalSelected;
24794
25150
  const handleSelect = (value) => {
24795
- setSelected(value);
25151
+ if (props.disabled) return;
25152
+ if (props.value === void 0) setInternalSelected(value);
24796
25153
  props.onChange?.(value);
24797
25154
  };
24798
25155
  return /* @__PURE__ */ require_jsx_runtime.jsx(View, {
@@ -24801,36 +25158,11 @@ function RadioGroup(props) {
24801
25158
  gap: themed.gap,
24802
25159
  children: props.options.map((option) => {
24803
25160
  const isSelected = selected === option.value;
24804
- const size = themed.size ?? 16;
24805
- const innerSize = themed.innerSize ?? size * .75;
24806
- const innerRadius = innerSize * .5;
24807
- const outerRadius = size * .5;
24808
- return /* @__PURE__ */ require_jsx_runtime.jsxs(View, {
24809
- direction: "row",
24810
- gap: themed.gap,
24811
- alignItems: "center",
24812
- enableGestures: true,
24813
- onTouch: () => handleSelect(option.value),
24814
- children: [/* @__PURE__ */ require_jsx_runtime.jsx(View, {
24815
- width: size,
24816
- height: size,
24817
- backgroundColor: themed.color,
24818
- alignItems: "center",
24819
- justifyContent: "center",
24820
- backgroundAlpha: 1,
24821
- padding: 0,
24822
- cornerRadius: outerRadius,
24823
- children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
24824
- width: innerSize,
24825
- height: innerSize,
24826
- backgroundColor: themed.selectedColor,
24827
- visible: isSelected,
24828
- cornerRadius: innerRadius
24829
- })
24830
- }), /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
24831
- text: option.label,
24832
- style: themed.labelStyle
24833
- })]
25161
+ return /* @__PURE__ */ require_jsx_runtime.jsx(RadioButton, {
25162
+ label: option.label,
25163
+ selected: isSelected,
25164
+ disabled: props.disabled ?? false,
25165
+ onClick: () => handleSelect(option.value)
24834
25166
  }, option.value);
24835
25167
  })
24836
25168
  });
@@ -25052,6 +25384,31 @@ function Portal(props) {
25052
25384
  return null;
25053
25385
  }
25054
25386
  //#endregion
25387
+ //#region src/components/custom/useOverlayPresence.ts
25388
+ function useOverlayPresence(show) {
25389
+ const [isPresent, setIsPresent] = useState(show);
25390
+ const [phase, setPhase] = useState(show ? "entered" : "exited");
25391
+ const previousShow = useRef(show);
25392
+ useEffect(() => {
25393
+ if (show) {
25394
+ setIsPresent(true);
25395
+ if (!previousShow.current) setPhase("entering");
25396
+ } else if (previousShow.current) setPhase("exiting");
25397
+ previousShow.current = show;
25398
+ }, [show]);
25399
+ return {
25400
+ isPresent,
25401
+ phase,
25402
+ finishEnter: useCallback(() => {
25403
+ setPhase((current) => current === "entering" ? "entered" : current);
25404
+ }, []),
25405
+ finishExit: useCallback(() => {
25406
+ setIsPresent(false);
25407
+ setPhase("exited");
25408
+ }, [])
25409
+ };
25410
+ }
25411
+ //#endregion
25055
25412
  //#region src/components/custom/Popover.tsx
25056
25413
  function getMainPlacement(placement) {
25057
25414
  return placement.split("-")[0];
@@ -25135,7 +25492,8 @@ function assignRef(ref, value) {
25135
25492
  ref.current = value;
25136
25493
  }
25137
25494
  function Popover(props) {
25138
- const { props: themed, nestedTheme } = getThemedProps("Popover", useTheme(), props.theme ?? {});
25495
+ const localTheme = useTheme();
25496
+ const { props: themed, nestedTheme } = getThemedProps("Popover", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
25139
25497
  const scene = useScene();
25140
25498
  const triggerRef = useRef(null);
25141
25499
  const contentRef = useRef(null);
@@ -25143,12 +25501,18 @@ function Popover(props) {
25143
25501
  const [internalOpen, setInternalOpen] = useState(props.defaultOpen ?? false);
25144
25502
  const isControlled = props.isOpen !== void 0;
25145
25503
  const isOpen = isControlled ? props.isOpen === true : internalOpen;
25504
+ const presence = useOverlayPresence(isOpen);
25505
+ const { applyEffect: contentAnimation, stopEffects: stopContentEffects } = useGameObjectEffect(contentRef);
25146
25506
  const placement = props.placement ?? themed.placement ?? "bottom";
25147
25507
  const offset = props.offset ?? themed.offset ?? 8;
25148
25508
  const depth = props.depth ?? themed.depth ?? 1100;
25149
25509
  const closeOnOutside = props.closeOnOutside ?? themed.closeOnOutside ?? true;
25150
25510
  const closeOnEscape = props.closeOnEscape ?? themed.closeOnEscape ?? true;
25151
25511
  const viewportPadding = props.viewportPadding ?? themed.viewportPadding ?? 8;
25512
+ const openEffect = props.openEffect ?? themed.openEffect ?? createFadeInEffect;
25513
+ const closeEffect = props.closeEffect ?? themed.closeEffect ?? createFadeOutEffect;
25514
+ const openDuration = props.openDuration ?? themed.openDuration ?? 120;
25515
+ const closeDuration = props.closeDuration ?? themed.closeDuration ?? 100;
25152
25516
  const explicitContentWidth = props.contentWidth ?? themed.contentWidth;
25153
25517
  const explicitContentHeight = props.contentHeight ?? themed.contentHeight;
25154
25518
  const viewport = portalRegistry.getViewportSize(scene);
@@ -25197,7 +25561,7 @@ function Popover(props) {
25197
25561
  return () => window.removeEventListener("keydown", handleKeyDown);
25198
25562
  }, [closeOnEscape, isOpen]);
25199
25563
  useEffect(() => {
25200
- if (!isOpen) return;
25564
+ if (!presence.isPresent) return;
25201
25565
  DeferredLayoutQueue.defer(() => {
25202
25566
  const size = getLayoutSize(contentRef.current);
25203
25567
  if (size.width <= 0 || size.height <= 0) return;
@@ -25210,18 +25574,47 @@ function Popover(props) {
25210
25574
  });
25211
25575
  });
25212
25576
  }, [
25213
- isOpen,
25577
+ presence.isPresent,
25214
25578
  props.children,
25215
25579
  explicitContentWidth,
25216
25580
  explicitContentHeight
25217
25581
  ]);
25582
+ useEffect(() => {
25583
+ const content = contentRef.current;
25584
+ if (!content || !presence.isPresent) return;
25585
+ if (presence.phase === "entering") {
25586
+ if (!isPositionReady) return;
25587
+ stopContentEffects();
25588
+ content.setVisible(true);
25589
+ contentAnimation(openEffect, {
25590
+ time: openDuration,
25591
+ onComplete: presence.finishEnter
25592
+ });
25593
+ } else if (presence.phase === "exiting") {
25594
+ stopContentEffects();
25595
+ contentAnimation(closeEffect, {
25596
+ time: closeDuration,
25597
+ onComplete: () => {
25598
+ contentRef.current?.setVisible(false);
25599
+ setMeasuredContentSize(null);
25600
+ presence.finishExit();
25601
+ }
25602
+ });
25603
+ }
25604
+ }, [
25605
+ presence.phase,
25606
+ presence.isPresent,
25607
+ isPositionReady,
25608
+ openDuration,
25609
+ closeDuration
25610
+ ]);
25218
25611
  return /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsx(View, {
25219
25612
  ...props.triggerProps ?? {},
25220
25613
  ref: triggerRef,
25221
25614
  enableGestures: !props.disabled,
25222
25615
  onTouch: toggleOpen,
25223
25616
  children: props.trigger
25224
- }), isOpen && /* @__PURE__ */ require_jsx_runtime.jsxs(Portal, {
25617
+ }), presence.isPresent && /* @__PURE__ */ require_jsx_runtime.jsxs(Portal, {
25225
25618
  depth,
25226
25619
  blockEvents: false,
25227
25620
  children: [closeOnOutside && /* @__PURE__ */ require_jsx_runtime.jsx(View, {
@@ -25232,11 +25625,6 @@ function Popover(props) {
25232
25625
  onTouch: close,
25233
25626
  onTouchMove: (event) => event.stopPropagation()
25234
25627
  }), /* @__PURE__ */ require_jsx_runtime.jsx(View, {
25235
- ...contentProps,
25236
- ref: handleContentRef,
25237
- x: overlayPosition.x,
25238
- y: overlayPosition.y,
25239
- ...props.matchTriggerWidth || explicitContentWidth !== void 0 ? { width: contentWidth } : {},
25240
25628
  direction: "column",
25241
25629
  backgroundColor: themed.backgroundColor ?? 1120295,
25242
25630
  borderColor: themed.borderColor ?? 3359061,
@@ -25244,6 +25632,11 @@ function Popover(props) {
25244
25632
  cornerRadius: themed.cornerRadius ?? 8,
25245
25633
  padding: themed.padding ?? 10,
25246
25634
  gap: themed.gap ?? 8,
25635
+ ...contentProps,
25636
+ ref: handleContentRef,
25637
+ x: overlayPosition.x,
25638
+ y: overlayPosition.y,
25639
+ ...props.matchTriggerWidth || explicitContentWidth !== void 0 ? { width: contentWidth } : {},
25247
25640
  ...isPositionReady ? contentPropsAlpha !== void 0 ? { alpha: contentPropsAlpha } : {} : { alpha: 0 },
25248
25641
  onTouch: (event) => event.stopPropagation(),
25249
25642
  theme: nestedTheme,
@@ -25253,7 +25646,8 @@ function Popover(props) {
25253
25646
  }
25254
25647
  function ContextMenu(props) {
25255
25648
  const { items, width: explicitWidth, onSelect, isOpen: explicitOpen, defaultOpen, onOpenChange, ...popoverProps } = props;
25256
- const { props: themed, nestedTheme } = getThemedProps("ContextMenu", useTheme(), props.theme ?? {});
25649
+ const localTheme = useTheme();
25650
+ const { props: themed, nestedTheme } = getThemedProps("ContextMenu", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
25257
25651
  const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
25258
25652
  const isControlled = explicitOpen !== void 0;
25259
25653
  const isOpen = isControlled ? explicitOpen === true : internalOpen;
@@ -25345,7 +25739,8 @@ function formatDefaultValue(props) {
25345
25739
  }
25346
25740
  function ProgressBar(props) {
25347
25741
  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;
25348
- const { props: themed, nestedTheme } = getThemedProps("ProgressBar", useTheme(), theme ?? {});
25742
+ const localTheme = useTheme();
25743
+ const { props: themed, nestedTheme } = getThemedProps("ProgressBar", theme ? mergeThemes(localTheme ?? {}, theme) : localTheme, {});
25349
25744
  const orientation = explicitOrientation ?? themed.orientation ?? "horizontal";
25350
25745
  const ratio = getProgressRatio(value, min, max);
25351
25746
  const percent = ratio * 100;
@@ -27706,18 +28101,24 @@ function CharText(props) {
27706
28101
  * - Scroll-to-cursor behavior on input
27707
28102
  */
27708
28103
  function CharTextInput(props) {
28104
+ const localTheme = useTheme();
28105
+ const { props: themed, nestedTheme } = getThemedProps("CharTextInput", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
28106
+ const inputProps = {
28107
+ ...themed,
28108
+ ...props
28109
+ };
27709
28110
  const containerRef = useRef(null);
27710
28111
  const inputManagerRef = useRef(null);
27711
28112
  const charTextApiRef = useRef(null);
27712
- const [internalValue, setInternalValue] = useState(props.value ?? "");
28113
+ const [internalValue, setInternalValue] = useState(inputProps.value ?? "");
27713
28114
  const [cursorPosition, setCursorPosition] = useState(0);
27714
28115
  const [selectionAnchor, setSelectionAnchor] = useState(-1);
27715
28116
  const [isFocused, setIsFocused] = useState(false);
27716
- const refCurrentValue = useRef(props.value ?? "");
28117
+ const refCurrentValue = useRef(inputProps.value ?? "");
27717
28118
  const refCursorPosition = useRef(0);
27718
28119
  const refSelectionAnchor = useRef(-1);
27719
- const currentValue = props.value !== void 0 ? props.value : internalValue;
27720
- const isControlled = props.value !== void 0;
28120
+ const currentValue = inputProps.value !== void 0 ? inputProps.value : internalValue;
28121
+ const isControlled = inputProps.value !== void 0;
27721
28122
  refCurrentValue.current = currentValue;
27722
28123
  refCursorPosition.current = cursorPosition;
27723
28124
  refSelectionAnchor.current = selectionAnchor;
@@ -27735,14 +28136,14 @@ function CharTextInput(props) {
27735
28136
  if (!containerRef.current) return;
27736
28137
  const container = containerRef.current;
27737
28138
  inputManagerRef.current = new KeyboardInputManager(container, {
27738
- ...props.maxLength !== void 0 && { maxLength: props.maxLength },
27739
- ...props.disabled !== void 0 && { disabled: props.disabled },
27740
- debug: props.debugHtmlInput ?? false,
28139
+ ...inputProps.maxLength !== void 0 && { maxLength: inputProps.maxLength },
28140
+ ...inputProps.disabled !== void 0 && { disabled: inputProps.disabled },
28141
+ debug: inputProps.debugHtmlInput ?? false,
27741
28142
  onInput: (_value, _event) => {},
27742
28143
  onKeyDown: (event) => {
27743
- if (event.key === "Enter") if (!props.multiline) {
28144
+ if (event.key === "Enter") if (!inputProps.multiline) {
27744
28145
  event.preventDefault();
27745
- props.onSubmit?.(refCurrentValue.current);
28146
+ inputProps.onSubmit?.(refCurrentValue.current);
27746
28147
  } else {
27747
28148
  event.preventDefault();
27748
28149
  handleCharacterInput("\n");
@@ -27766,13 +28167,13 @@ function CharTextInput(props) {
27766
28167
  setIsFocused(true);
27767
28168
  inputManagerRef.current?.setPointerEvents(false);
27768
28169
  setCursorPosition(currentValue.length);
27769
- props.onFocus?.();
28170
+ inputProps.onFocus?.();
27770
28171
  },
27771
28172
  onBlur: () => {
27772
28173
  setIsFocused(false);
27773
28174
  setSelectionAnchor(-1);
27774
28175
  inputManagerRef.current?.setPointerEvents(true);
27775
- props.onBlur?.();
28176
+ inputProps.onBlur?.();
27776
28177
  }
27777
28178
  });
27778
28179
  return () => {
@@ -27781,9 +28182,9 @@ function CharTextInput(props) {
27781
28182
  };
27782
28183
  }, [
27783
28184
  containerRef.current,
27784
- props.maxLength,
27785
- props.disabled,
27786
- props.multiline,
28185
+ inputProps.maxLength,
28186
+ inputProps.disabled,
28187
+ inputProps.multiline,
27787
28188
  isControlled
27788
28189
  ]);
27789
28190
  /**
@@ -27812,11 +28213,11 @@ function CharTextInput(props) {
27812
28213
  newValue = currentValue.slice(0, cursorPosition) + char + currentValue.slice(cursorPosition);
27813
28214
  newCursorPos = cursorPosition + char.length;
27814
28215
  }
27815
- if (props.maxLength !== void 0 && newValue.length > props.maxLength) return;
27816
- if (props.multiline && char === "\n" && props.maxLines !== void 0) {
27817
- if (newValue.split("\n").length > props.maxLines) return;
28216
+ if (inputProps.maxLength !== void 0 && newValue.length > inputProps.maxLength) return;
28217
+ if (inputProps.multiline && char === "\n" && inputProps.maxLines !== void 0) {
28218
+ if (newValue.split("\n").length > inputProps.maxLines) return;
27818
28219
  }
27819
- if (!props.multiline && charTextApiRef.current) {
28220
+ if (!inputProps.multiline && charTextApiRef.current) {
27820
28221
  const insertPosition = anchor >= 0 ? Math.min(anchor, cursorPosition) : cursorPosition;
27821
28222
  if (!charTextApiRef.current.canFitChar(char, insertPosition)) return;
27822
28223
  }
@@ -27934,10 +28335,10 @@ function CharTextInput(props) {
27934
28335
  * Update value (controlled or uncontrolled)
27935
28336
  */
27936
28337
  const updateValue = (newValue) => {
27937
- if (isControlled) props.onChange?.(newValue);
28338
+ if (isControlled) inputProps.onChange?.(newValue);
27938
28339
  else {
27939
28340
  setInternalValue(newValue);
27940
- props.onChange?.(newValue);
28341
+ inputProps.onChange?.(newValue);
27941
28342
  }
27942
28343
  };
27943
28344
  /**
@@ -27956,12 +28357,31 @@ function CharTextInput(props) {
27956
28357
  setCursorPosition(end);
27957
28358
  } else setSelectionAnchor(-1);
27958
28359
  };
27959
- const { value: _value, placeholder: _placeholder, onChange: _onChange, onFocus: _onFocus, onBlur: _onBlur, onSubmit: _onSubmit, ...viewProps } = props;
27960
- const displayText = currentValue || (props.placeholder && !isFocused ? props.placeholder : "");
28360
+ const { value: _value, placeholder: _placeholder, placeholderStyle, focusedBorderColor, disabledBackgroundColor, disabledBorderColor, disabledAlpha, onChange: _onChange, onFocus: _onFocus, onBlur: _onBlur, onSubmit: _onSubmit, ...viewProps } = inputProps;
28361
+ const isPlaceholderVisible = currentValue.length === 0 && !!inputProps.placeholder && !isFocused;
28362
+ const displayText = currentValue || (isPlaceholderVisible ? inputProps.placeholder ?? "" : "");
28363
+ const textStyle = isPlaceholderVisible ? {
28364
+ ...inputProps.textStyle ?? {},
28365
+ ...placeholderStyle ?? {}
28366
+ } : inputProps.textStyle;
28367
+ const disabled = inputProps.disabled ?? false;
28368
+ const focusedViewProps = isFocused ? {
28369
+ borderColor: focusedBorderColor ?? inputProps.borderColor,
28370
+ borderWidth: Math.max(2, inputProps.borderWidth ?? 1)
28371
+ } : {};
28372
+ const disabledViewProps = disabled ? {
28373
+ backgroundColor: disabledBackgroundColor ?? inputProps.backgroundColor,
28374
+ borderColor: disabledBorderColor ?? inputProps.borderColor,
28375
+ alpha: inputProps.alpha ?? disabledAlpha ?? .6
28376
+ } : {};
27961
28377
  return /* @__PURE__ */ require_jsx_runtime.jsx(CharText, {
27962
28378
  forwardRef: (r) => containerRef.current = r,
27963
28379
  ...viewProps,
28380
+ ...focusedViewProps,
28381
+ ...disabledViewProps,
28382
+ theme: nestedTheme,
27964
28383
  text: displayText,
28384
+ ...textStyle !== void 0 && { textStyle },
27965
28385
  showCursor: isFocused,
27966
28386
  cursorPosition,
27967
28387
  selectionStart,
@@ -27996,10 +28416,11 @@ function Divider(props) {
27996
28416
  /**
27997
28417
  * Calculate slider dimensions based on size variant and theme
27998
28418
  * @param size - Size variant
28419
+ * @param theme - Optional resolved ScrollSlider theme values
27999
28420
  * @returns Calculated dimensions
28000
28421
  */
28001
- function calculateSliderSize(size) {
28002
- const { props: themed } = getThemedProps("ScrollSlider", void 0, {});
28422
+ function calculateSliderSize(size, theme) {
28423
+ const themed = theme ?? getThemedProps("ScrollSlider", void 0, {}).props;
28003
28424
  const sizeFactor = size === "large" ? 1.25 : size === "small" ? .75 : size === "tiny" ? .5 : size === "micro" ? .25 : size === "nano" ? .125 : 1;
28004
28425
  const border = (themed.borderWidth ?? 1) * sizeFactor;
28005
28426
  const outer = (themed.size ?? 24) * sizeFactor;
@@ -28016,15 +28437,21 @@ function calculateSliderSize(size) {
28016
28437
  */
28017
28438
  function ScrollSlider(props) {
28018
28439
  const { direction, scrollPosition, viewportSize, contentSize, onScroll, momentum = true, onMomentumEnd } = props;
28019
- const { props: themed } = getThemedProps("ScrollSlider", void 0, {});
28440
+ const { props: themed } = getThemedProps("ScrollSlider", useTheme(), {});
28020
28441
  const sliderRef = useRef(null);
28021
28442
  const isDraggingRef = useRef(false);
28443
+ const [isDragging, setIsDragging] = useState(false);
28022
28444
  const trackContainerRef = useRef(null);
28023
28445
  const velocityRef = useRef(0);
28024
28446
  const lastTimeRef = useRef(0);
28025
28447
  const tweenRef = useRef(null);
28026
28448
  const isVertical = direction === "vertical";
28027
- const { border, outer, dimension } = calculateSliderSize(props.size);
28449
+ const { border, outer, dimension } = calculateSliderSize(props.size, themed);
28450
+ const sizeFactor = outer / (themed.size ?? 24);
28451
+ const thumbBorderWidth = Math.min((themed.thumbBorderWidth ?? 1) * sizeFactor, Math.max(0, dimension / 3));
28452
+ const outerRadius = themed.cornerRadius ?? outer / 2;
28453
+ const trackRadius = themed.trackCornerRadius ?? dimension / 2;
28454
+ const thumbRadius = themed.thumbCornerRadius ?? dimension / 2;
28028
28455
  const containerWithLayout = trackContainerRef.current;
28029
28456
  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;
28030
28457
  const minThumbSize = themed.minThumbSize ?? 20;
@@ -28037,6 +28464,7 @@ function ScrollSlider(props) {
28037
28464
  data.stopPropagation();
28038
28465
  if (data.state === "start") {
28039
28466
  isDraggingRef.current = true;
28467
+ setIsDragging(true);
28040
28468
  velocityRef.current = 0;
28041
28469
  lastTimeRef.current = Date.now();
28042
28470
  if (tweenRef.current) {
@@ -28047,6 +28475,7 @@ function ScrollSlider(props) {
28047
28475
  }
28048
28476
  if (data.state === "end") {
28049
28477
  isDraggingRef.current = false;
28478
+ setIsDragging(false);
28050
28479
  if (momentum && Math.abs(velocityRef.current) > .1) startMomentum(scrollPosition);
28051
28480
  else if (onMomentumEnd) onMomentumEnd();
28052
28481
  return;
@@ -28094,12 +28523,14 @@ function ScrollSlider(props) {
28094
28523
  width: isVertical ? outer : "100%",
28095
28524
  height: isVertical ? "100%" : outer,
28096
28525
  backgroundColor: themed.borderColor ?? 0,
28526
+ cornerRadius: outerRadius,
28097
28527
  padding: border,
28098
28528
  children: /* @__PURE__ */ require_jsx_runtime.jsxs(View, {
28099
28529
  ref: sliderRef,
28100
28530
  width: isVertical ? dimension : "100%",
28101
28531
  height: isVertical ? "100%" : dimension,
28102
28532
  backgroundColor: themed.trackColor ?? 14540253,
28533
+ cornerRadius: trackRadius,
28103
28534
  direction: "stack",
28104
28535
  padding: 0,
28105
28536
  children: [/* @__PURE__ */ require_jsx_runtime.jsx(View, {
@@ -28108,6 +28539,7 @@ function ScrollSlider(props) {
28108
28539
  x: 0,
28109
28540
  y: 0,
28110
28541
  backgroundColor: themed.trackColor ?? 11184810,
28542
+ cornerRadius: trackRadius,
28111
28543
  enableGestures: true,
28112
28544
  onTouch: handleBackgroundTouch
28113
28545
  }), /* @__PURE__ */ require_jsx_runtime.jsx(View, {
@@ -28115,7 +28547,10 @@ function ScrollSlider(props) {
28115
28547
  height: isVertical ? thumbSize : dimension,
28116
28548
  x: isVertical ? 0 : thumbPosition,
28117
28549
  y: isVertical ? thumbPosition : 0,
28118
- backgroundColor: themed.thumbColor ?? 15658683,
28550
+ backgroundColor: isDragging ? themed.thumbActiveColor ?? themed.thumbColor ?? 15658683 : themed.thumbColor ?? 15658683,
28551
+ borderColor: themed.thumbBorderColor,
28552
+ borderWidth: thumbBorderWidth,
28553
+ cornerRadius: thumbRadius,
28119
28554
  enableGestures: true,
28120
28555
  onTouchMove: handleThumbTouchMove
28121
28556
  })]
@@ -28134,7 +28569,8 @@ function ScrollSlider(props) {
28134
28569
  * @returns JSX element
28135
28570
  */
28136
28571
  function ScrollView(props) {
28137
- const { children, showVerticalSlider = "auto", showHorizontalSlider = "auto", scroll: initialScroll, onScrollInfoChange, snap = false, snapAlignment = "start", snapThreshold = 20, momentum = true, onSnap, ...viewProps } = props;
28572
+ const { children, showVerticalSlider = "auto", showHorizontalSlider = "auto", scroll: initialScroll, onScrollInfoChange, snap = false, snapAlignment = "start", snapThreshold = 20, momentum = true, onSnap, onSliderSize, sliderSize: sliderSizeVariant, ...viewProps } = props;
28573
+ const { props: sliderTheme } = getThemedProps("ScrollSlider", useTheme(), {});
28138
28574
  const [scroll, setScroll] = useState({
28139
28575
  dx: initialScroll?.dx ?? 0,
28140
28576
  dy: initialScroll?.dy ?? 0
@@ -28168,9 +28604,9 @@ function ScrollView(props) {
28168
28604
  const needsHorizontalScroll = effectiveContentWidth > viewportWidth + epsilon;
28169
28605
  const showVerticalSliderActual = showVerticalSlider === true || needsVerticalScroll && showVerticalSlider === "auto";
28170
28606
  const showHorizontalSliderActual = showHorizontalSlider === true || needsHorizontalScroll && showHorizontalSlider === "auto";
28171
- const { outer: sliderSize } = calculateSliderSize(props.sliderSize);
28172
- props.onSliderSize?.({
28173
- width: showVerticalSlider ? sliderSize : 0,
28607
+ const { outer: sliderSize } = calculateSliderSize(sliderSizeVariant, sliderTheme);
28608
+ onSliderSize?.({
28609
+ width: showVerticalSliderActual ? sliderSize : 0,
28174
28610
  height: showHorizontalSliderActual ? sliderSize : 0
28175
28611
  });
28176
28612
  const maxScrollY = Math.max(0, effectiveContentHeight - viewportHeight);
@@ -28633,7 +29069,7 @@ function ScrollView(props) {
28633
29069
  visible: showHorizontalSliderActual ? true : "none",
28634
29070
  children: /* @__PURE__ */ require_jsx_runtime.jsx(ScrollSlider, {
28635
29071
  direction: "horizontal",
28636
- size: props.sliderSize,
29072
+ size: sliderSizeVariant,
28637
29073
  scrollPosition: scroll.dx,
28638
29074
  viewportSize: viewportWidth,
28639
29075
  contentSize: contentWidth,
@@ -28650,7 +29086,7 @@ function ScrollView(props) {
28650
29086
  flex: 1,
28651
29087
  children: /* @__PURE__ */ require_jsx_runtime.jsx(ScrollSlider, {
28652
29088
  direction: "vertical",
28653
- size: props.sliderSize,
29089
+ size: sliderSizeVariant,
28654
29090
  scrollPosition: scroll.dy,
28655
29091
  viewportSize: viewportHeight,
28656
29092
  contentSize: effectiveContentHeight,
@@ -28667,25 +29103,114 @@ function ScrollView(props) {
28667
29103
  });
28668
29104
  }
28669
29105
  //#endregion
29106
+ //#region src/components/custom/TransformOriginView.tsx
29107
+ /**
29108
+ * TransformOriginView component - declarative transforms around custom origin point
29109
+ *
29110
+ * Uses three nested Views to apply rotation/scale around configurable origin:
29111
+ * - Outer View: Defines bounding box, receives layout/position props
29112
+ * - Middle View: Positioned at origin point, receives transform/visual props
29113
+ * - Inner View: Contains content, offset to align with origin
29114
+ *
29115
+ * @param props - TransformOriginView props
29116
+ * @returns JSX element
29117
+ */
29118
+ function TransformOriginView({ originX = .5, originY = .5, width, height, x = 0, y = 0, children, rotation, scale, scaleX, scaleY, backgroundColor, backgroundAlpha, cornerRadius, borderWidth, borderColor, borderAlpha, ...outerViewProps }) {
29119
+ if (typeof width !== "number" || typeof height !== "number") throw new Error("TransformOriginView requires numeric width and height");
29120
+ const offsetX = width * originX;
29121
+ const offsetY = height * originY;
29122
+ const middleViewProps = {};
29123
+ if (rotation !== void 0) middleViewProps.rotation = rotation;
29124
+ if (scale !== void 0) middleViewProps.scale = scale;
29125
+ if (scaleX !== void 0) middleViewProps.scaleX = scaleX;
29126
+ if (scaleY !== void 0) middleViewProps.scaleY = scaleY;
29127
+ if (backgroundColor !== void 0) middleViewProps.backgroundColor = backgroundColor;
29128
+ if (backgroundAlpha !== void 0) middleViewProps.backgroundAlpha = backgroundAlpha;
29129
+ if (cornerRadius !== void 0) middleViewProps.cornerRadius = cornerRadius;
29130
+ if (borderWidth !== void 0) middleViewProps.borderWidth = borderWidth;
29131
+ if (borderColor !== void 0) middleViewProps.borderColor = borderColor;
29132
+ if (borderAlpha !== void 0) middleViewProps.borderAlpha = borderAlpha;
29133
+ return /* @__PURE__ */ require_jsx_runtime.jsx(View, {
29134
+ x,
29135
+ y,
29136
+ width,
29137
+ height,
29138
+ padding: 0,
29139
+ direction: "stack",
29140
+ ...outerViewProps,
29141
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
29142
+ x: offsetX,
29143
+ y: offsetY,
29144
+ width,
29145
+ height,
29146
+ padding: 0,
29147
+ direction: "stack",
29148
+ ...middleViewProps,
29149
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
29150
+ x: -offsetX,
29151
+ y: -offsetY,
29152
+ width,
29153
+ height,
29154
+ padding: 0,
29155
+ direction: "stack",
29156
+ backgroundAlpha: 0,
29157
+ children
29158
+ })
29159
+ })
29160
+ });
29161
+ }
29162
+ //#endregion
28670
29163
  //#region src/components/custom/Dropdown.tsx
28671
29164
  /**
28672
- * Default arrow component - simple Graphics triangle
29165
+ * Default arrow component - chevron indicator.
28673
29166
  */
28674
29167
  function DefaultArrow(props) {
28675
29168
  const color = props.color ?? 16777215;
28676
29169
  const size = props.size ?? 8;
29170
+ const strokeWidth = props.strokeWidth ?? Math.max(2, Math.round(size * .16));
28677
29171
  return /* @__PURE__ */ require_jsx_runtime.jsx(Graphics, {
28678
29172
  width: size,
28679
29173
  height: size,
28680
29174
  onDraw: (g) => {
28681
29175
  g.clear();
28682
- g.fillStyle(color, 1);
29176
+ g.lineStyle(strokeWidth, color, 1);
28683
29177
  g.beginPath();
28684
- g.moveTo(0, 0);
28685
- g.lineTo(size, 0);
28686
- g.lineTo(size / 2, size);
28687
- g.closePath();
28688
- g.fillPath();
29178
+ g.moveTo(size * .18, size * .35);
29179
+ g.lineTo(size * .5, size * .68);
29180
+ g.lineTo(size * .82, size * .35);
29181
+ g.strokePath();
29182
+ }
29183
+ });
29184
+ }
29185
+ function DefaultOptionIndicator(props) {
29186
+ const size = props.size ?? 16;
29187
+ const color = props.color ?? 16777215;
29188
+ const strokeWidth = props.strokeWidth ?? 2;
29189
+ const alpha = props.disabled ? .4 : 1;
29190
+ return /* @__PURE__ */ require_jsx_runtime.jsx(Graphics, {
29191
+ width: size,
29192
+ height: size,
29193
+ dependencies: [
29194
+ props.selected,
29195
+ props.multiple,
29196
+ props.disabled,
29197
+ color,
29198
+ size,
29199
+ strokeWidth
29200
+ ],
29201
+ onDraw: (g) => {
29202
+ g.clear();
29203
+ if (props.multiple) {
29204
+ g.lineStyle(strokeWidth, color, props.selected ? alpha : alpha * .55);
29205
+ g.strokeRoundedRect(strokeWidth / 2, strokeWidth / 2, size - strokeWidth, size - strokeWidth, 3);
29206
+ if (!props.selected) return;
29207
+ } else if (!props.selected) return;
29208
+ g.lineStyle(strokeWidth + 1, color, alpha);
29209
+ g.beginPath();
29210
+ g.moveTo(size * .22, size * .52);
29211
+ g.lineTo(size * .43, size * .72);
29212
+ g.lineTo(size * .8, size * .28);
29213
+ g.strokePath();
28689
29214
  }
28690
29215
  });
28691
29216
  }
@@ -28721,11 +29246,7 @@ function Dropdown(props) {
28721
29246
  const [isOpen, setIsOpen] = useState(false);
28722
29247
  const [internalValue, setInternalValue] = useState(props.defaultValue ?? (props.multiple ? [] : ""));
28723
29248
  const [filterQuery, setFilterQuery] = useState("");
28724
- const [isAnimating, setIsAnimating] = useState(false);
28725
- const shouldIgnoreNextClick = useRef(false);
28726
29249
  const triggerRef = useRef(null);
28727
- const overlayRef = useRef(null);
28728
- const containerRef = useRef(null);
28729
29250
  const scrollViewRef = useRef(null);
28730
29251
  const { applyEffect } = useGameObjectEffect(triggerRef);
28731
29252
  const isControlled = props.value !== void 0;
@@ -28738,6 +29259,13 @@ function Dropdown(props) {
28738
29259
  const animationConfig = props.animationConfig ?? themed.animationConfig ?? "gentle";
28739
29260
  const maxHeight = props.maxHeight ?? overlayTheme.maxHeight ?? 300;
28740
29261
  const arrowConfig = themed.arrow ?? {};
29262
+ const arrowSize = arrowConfig.size ?? 12;
29263
+ const indicatorConfig = themed.selectionIndicator ?? {};
29264
+ const indicatorSize = indicatorConfig.size ?? 16;
29265
+ const indicatorColor = indicatorConfig.color ?? arrowConfig.color;
29266
+ const indicatorStrokeWidth = indicatorConfig.strokeWidth ?? 2;
29267
+ const placement = props.placement ?? "bottom";
29268
+ const popoverPlacement = placement === "top" ? "top-start" : "bottom-start";
28741
29269
  const getSelectedOptions = () => {
28742
29270
  if (props.multiple) {
28743
29271
  const values = currentValue;
@@ -28748,38 +29276,31 @@ function Dropdown(props) {
28748
29276
  }
28749
29277
  };
28750
29278
  const selectedOptions = getSelectedOptions();
28751
- const [overlayHeight, setOverlayHeight] = useSpring(isOpen ? maxHeight : 0, animationConfig, () => setIsAnimating(false));
28752
- useForceRedraw(20, overlayHeight);
28753
29279
  const [arrowRotation, setArrowRotation] = useSpring(isOpen ? Math.PI : 0, animationConfig);
28754
29280
  useForceRedraw(20, arrowRotation);
28755
- const resolvedOverlayHeight = Math.max(0, overlayHeight.value);
28756
- const handleToggle = (event) => {
29281
+ const openDropdown = () => {
28757
29282
  if (props.disabled) return;
28758
- event?.stopPropagation();
28759
- if (isOpen) handleClose();
28760
- else {
28761
- setIsOpen(true);
28762
- setIsAnimating(true);
28763
- setOverlayHeight(maxHeight);
28764
- setArrowRotation(Math.PI);
28765
- setFilterQuery("");
28766
- props.onOpen?.();
28767
- shouldIgnoreNextClick.current = true;
28768
- const resolved = resolveEffect(props, themed);
28769
- applyEffectByName(applyEffect, resolved.effect, resolved.effectConfig);
28770
- }
29283
+ if (isOpen) return;
29284
+ setIsOpen(true);
29285
+ setArrowRotation(Math.PI);
29286
+ setFilterQuery("");
29287
+ props.onOpen?.();
29288
+ const resolved = resolveEffect(props, themed);
29289
+ applyEffectByName(applyEffect, resolved.effect, resolved.effectConfig);
28771
29290
  };
28772
29291
  const handleClose = () => {
28773
- setIsAnimating(true);
29292
+ if (!isOpen) return;
28774
29293
  setIsOpen(false);
28775
- setOverlayHeight(0);
28776
29294
  setArrowRotation(0);
28777
29295
  setFilterQuery("");
28778
29296
  props.onClose?.();
28779
29297
  };
29298
+ const handleOpenChange = (open) => {
29299
+ if (open) openDropdown();
29300
+ else handleClose();
29301
+ };
28780
29302
  const handleSelect = (value, event) => {
28781
29303
  event?.stopPropagation();
28782
- shouldIgnoreNextClick.current = true;
28783
29304
  if (props.multiple) {
28784
29305
  const values = currentValue;
28785
29306
  const newValues = values.includes(value) ? values.filter((v) => v !== value) : [...values, value];
@@ -28792,33 +29313,6 @@ function Dropdown(props) {
28792
29313
  if (props.closeOnSelect ?? true) handleClose();
28793
29314
  }
28794
29315
  };
28795
- const handleOutsideClick = () => {
28796
- if (shouldIgnoreNextClick.current) {
28797
- shouldIgnoreNextClick.current = false;
28798
- return;
28799
- }
28800
- if (isOpen) handleClose();
28801
- };
28802
- const calculateOverlayPosition = () => {
28803
- const triggerContainer = triggerRef.current;
28804
- if (!triggerContainer) return {
28805
- x: 0,
28806
- y: 0,
28807
- width: 0
28808
- };
28809
- const triggerSize = triggerContainer.__getLayoutSize?.() ?? {
28810
- width: 0,
28811
- height: 0
28812
- };
28813
- const triggerX = triggerContainer.x ?? 0;
28814
- const triggerY = triggerContainer.y ?? 0;
28815
- const gap = 4;
28816
- return {
28817
- x: triggerX,
28818
- y: (props.placement ?? "bottom") === "bottom" ? triggerY + triggerSize.height + gap : triggerY - maxHeight - gap,
28819
- width: triggerSize.width
28820
- };
28821
- };
28822
29316
  const renderSelectedValue = () => {
28823
29317
  if (props.renderValue) return props.renderValue(selectedOptions.length > 0 ? selectedOptions : null);
28824
29318
  if (selectedOptions.length === 0) return /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
@@ -28846,10 +29340,9 @@ function Dropdown(props) {
28846
29340
  return triggerTheme;
28847
29341
  };
28848
29342
  const triggerStyle = getTriggerStyle();
28849
- const overlayPosition = calculateOverlayPosition();
28850
29343
  const renderedOptions = props.options.map((option, _index) => {
28851
29344
  const isSelected = props.multiple ? currentValue.includes(option.value) : currentValue === option.value;
28852
- const isDisabled = (option.disabled ?? false) || isAnimating && (props.placement ?? "bottom") === "top";
29345
+ const isDisabled = option.disabled ?? false;
28853
29346
  const matchesFilter = props.isFilterable ? option.label.toLowerCase().includes(filterQuery.toLowerCase()) : true;
28854
29347
  const optionStyle = {
28855
29348
  ...optionTheme,
@@ -28870,27 +29363,40 @@ function Dropdown(props) {
28870
29363
  theme: optionNestedTheme,
28871
29364
  ...optionStyle,
28872
29365
  children: props.renderOption ? props.renderOption(option, isSelected) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
29366
+ /* @__PURE__ */ require_jsx_runtime.jsx(DefaultOptionIndicator, {
29367
+ selected: isSelected,
29368
+ multiple: props.multiple === true,
29369
+ disabled: isDisabled,
29370
+ color: indicatorColor ?? 16777215,
29371
+ size: indicatorSize,
29372
+ strokeWidth: indicatorStrokeWidth
29373
+ }),
28873
29374
  option.prefix,
28874
29375
  /* @__PURE__ */ require_jsx_runtime.jsx(Text, { text: option.label }),
28875
29376
  option.suffix
28876
29377
  ] })
28877
29378
  }, String(option.value));
28878
29379
  });
28879
- const placement = props.placement ?? "bottom";
28880
29380
  const trigger = /* @__PURE__ */ require_jsx_runtime.jsxs(View, {
28881
29381
  ref: triggerRef,
28882
29382
  direction: "row",
28883
29383
  alignItems: "center",
28884
29384
  justifyContent: "space-between",
28885
- enableGestures: !props.disabled,
28886
- onTouch: (data) => handleToggle(data),
28887
29385
  ...triggerStyle,
28888
29386
  children: [/* @__PURE__ */ require_jsx_runtime.jsx(View, {
28889
29387
  flex: 1,
28890
29388
  children: renderSelectedValue()
28891
- }), props.arrow ? props.arrow : /* @__PURE__ */ require_jsx_runtime.jsx(DefaultArrow, {
28892
- color: arrowConfig.color ?? 16777215,
28893
- size: arrowConfig.size ?? 8
29389
+ }), /* @__PURE__ */ require_jsx_runtime.jsx(TransformOriginView, {
29390
+ width: arrowSize,
29391
+ height: arrowSize,
29392
+ rotation: arrowRotation.value,
29393
+ originX: .5,
29394
+ originY: .5,
29395
+ children: props.arrow ? props.arrow : /* @__PURE__ */ require_jsx_runtime.jsx(DefaultArrow, {
29396
+ color: arrowConfig.color ?? 16777215,
29397
+ size: arrowSize,
29398
+ strokeWidth: arrowConfig.strokeWidth ?? 2
29399
+ })
28894
29400
  })]
28895
29401
  });
28896
29402
  const filterInput = props.isFilterable && /* @__PURE__ */ require_jsx_runtime.jsx(CharTextInput, {
@@ -28908,7 +29414,7 @@ function Dropdown(props) {
28908
29414
  width: "fill",
28909
29415
  children: /* @__PURE__ */ require_jsx_runtime.jsx(ScrollView, {
28910
29416
  ref: scrollViewRef,
28911
- showVerticalSlider: isAnimating ? false : "auto",
29417
+ showVerticalSlider: "auto",
28912
29418
  height: "fill",
28913
29419
  width: "100%",
28914
29420
  onTouch: () => {
@@ -28924,33 +29430,53 @@ function Dropdown(props) {
28924
29430
  })
28925
29431
  });
28926
29432
  const overlay = /* @__PURE__ */ require_jsx_runtime.jsx(View, {
28927
- height: resolvedOverlayHeight,
28928
- width: overlayPosition.width,
29433
+ direction: "column",
29434
+ width: "fill",
29435
+ height: maxHeight,
28929
29436
  overflow: "hidden",
28930
- children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
28931
- ref: overlayRef,
28932
- direction: "column",
28933
- width: "fill",
28934
- height: "fill",
28935
- visible: isOpen || resolvedOverlayHeight > .1,
28936
- ...overlayTheme,
28937
- children: placement === "top" ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [optionsList, filterInput] }) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [filterInput, optionsList] })
28938
- })
29437
+ theme: nestedTheme,
29438
+ ...overlayTheme,
29439
+ children: placement === "top" ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [optionsList, filterInput] }) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [filterInput, optionsList] })
28939
29440
  });
29441
+ const popoverTheme = {
29442
+ ...nestedTheme,
29443
+ Popover: {
29444
+ backgroundColor: 0,
29445
+ backgroundAlpha: 0,
29446
+ borderWidth: 0,
29447
+ cornerRadius: 0,
29448
+ padding: 0,
29449
+ gap: 0
29450
+ }
29451
+ };
28940
29452
  return /* @__PURE__ */ require_jsx_runtime.jsx(View, {
28941
- direction: props.stackLayout ? "stack" : "column",
29453
+ direction: "column",
28942
29454
  width: props.width || "fill",
28943
- height: props.stackLayout ? triggerRef.current?.height : "auto",
29455
+ height: "auto",
28944
29456
  ref: props.ref,
28945
- children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
28946
- ref: containerRef,
28947
- direction: "column",
28948
- width: props.width,
28949
- theme: nestedTheme,
28950
- enableGestures: true,
28951
- onTouchOutside: handleOutsideClick,
28952
- children: placement === "top" ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [overlay, trigger] }) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [trigger, overlay] })
28953
- }, `dropdown-${placement}`)
29457
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(Popover, {
29458
+ trigger,
29459
+ isOpen,
29460
+ onOpenChange: handleOpenChange,
29461
+ placement: popoverPlacement,
29462
+ offset: 4,
29463
+ matchTriggerWidth: true,
29464
+ contentHeight: maxHeight,
29465
+ contentProps: {
29466
+ height: maxHeight,
29467
+ backgroundAlpha: 0,
29468
+ borderWidth: 0,
29469
+ padding: 0,
29470
+ gap: 0,
29471
+ cornerRadius: 0
29472
+ },
29473
+ triggerProps: { width: props.width ?? "fill" },
29474
+ closeOnOutside: true,
29475
+ closeOnEscape: true,
29476
+ disabled: props.disabled === true,
29477
+ theme: popoverTheme,
29478
+ children: overlay
29479
+ })
28954
29480
  });
28955
29481
  }
28956
29482
  //#endregion
@@ -30377,14 +30903,21 @@ function interpolateColor(color1, color2, progress) {
30377
30903
  * ```
30378
30904
  */
30379
30905
  function Toggle(props) {
30380
- const { props: themed, nestedTheme } = getThemedProps("Toggle", useTheme(), props.theme ?? {});
30906
+ const localTheme = useTheme();
30907
+ const { props: themed, nestedTheme } = getThemedProps("Toggle", props.theme ? mergeThemes(localTheme ?? {}, props.theme) : localTheme, {});
30381
30908
  const width = useRef(themed.width ?? 50);
30382
30909
  const height = useRef(themed.height ?? 28);
30383
30910
  const thumbSize = useRef(themed.thumbSize ?? 24);
30384
30911
  const trackColorOff = useRef(themed.trackColorOff ?? 10066329);
30385
30912
  const trackColorOn = useRef(themed.trackColorOn ?? 5025616);
30913
+ const trackBorderColorOff = useRef(themed.trackBorderColorOff ?? 7829367);
30914
+ const trackBorderColorOn = useRef(themed.trackBorderColorOn ?? themed.trackColorOn ?? 5025616);
30915
+ const trackBorderWidth = useRef(themed.trackBorderWidth ?? 1);
30386
30916
  const thumbColor = useRef(themed.thumbColor ?? 16777215);
30917
+ const thumbBorderColor = useRef(themed.thumbBorderColor ?? 13751771);
30918
+ const thumbBorderWidth = useRef(themed.thumbBorderWidth ?? 1);
30387
30919
  const disabledColor = useRef(themed.disabledColor ?? 6710886);
30920
+ const disabledAlpha = useRef(themed.disabledAlpha ?? .5);
30388
30921
  const padding = useRef(themed.padding ?? 2);
30389
30922
  const duration = useRef(themed.duration ?? 200);
30390
30923
  const gap = themed.gap ?? 8;
@@ -30394,8 +30927,14 @@ function Toggle(props) {
30394
30927
  thumbSize.current = themed.thumbSize ?? 24;
30395
30928
  trackColorOff.current = themed.trackColorOff ?? 10066329;
30396
30929
  trackColorOn.current = themed.trackColorOn ?? 5025616;
30930
+ trackBorderColorOff.current = themed.trackBorderColorOff ?? 7829367;
30931
+ trackBorderColorOn.current = themed.trackBorderColorOn ?? themed.trackColorOn ?? 5025616;
30932
+ trackBorderWidth.current = themed.trackBorderWidth ?? 1;
30397
30933
  thumbColor.current = themed.thumbColor ?? 16777215;
30934
+ thumbBorderColor.current = themed.thumbBorderColor ?? 13751771;
30935
+ thumbBorderWidth.current = themed.thumbBorderWidth ?? 1;
30398
30936
  disabledColor.current = themed.disabledColor ?? 6710886;
30937
+ disabledAlpha.current = themed.disabledAlpha ?? .5;
30399
30938
  padding.current = themed.padding ?? 2;
30400
30939
  duration.current = themed.duration ?? 200;
30401
30940
  const thumbRadius = thumbSize.current / 2;
@@ -30409,9 +30948,34 @@ function Toggle(props) {
30409
30948
  const trackRef = useRef(null);
30410
30949
  const thumbRef = useRef(null);
30411
30950
  const containerRef = useRef(null);
30951
+ const drawTrackShape = (g, color, borderColor) => {
30952
+ const borderInset = trackBorderWidth.current / 2;
30953
+ g.clear();
30954
+ g.fillStyle(color, 1);
30955
+ g.fillRoundedRect(0, 0, width.current, height.current, trackRadius);
30956
+ if (trackBorderWidth.current > 0) {
30957
+ g.lineStyle(trackBorderWidth.current, borderColor, 1);
30958
+ g.strokeRoundedRect(borderInset, borderInset, width.current - trackBorderWidth.current, height.current - trackBorderWidth.current, Math.max(0, trackRadius - borderInset));
30959
+ }
30960
+ };
30961
+ const getTrackColor = (targetChecked) => {
30962
+ if (props.disabled && !targetChecked) return disabledColor.current;
30963
+ return targetChecked ? trackColorOn.current : trackColorOff.current;
30964
+ };
30965
+ const getTrackBorderColor = (targetChecked) => {
30966
+ return targetChecked ? trackBorderColorOn.current : trackBorderColorOff.current;
30967
+ };
30412
30968
  useEffect(() => {
30413
30969
  if (props.checked !== void 0 && props.checked !== checked) animateToggle(props.checked);
30414
30970
  }, [props.checked, checked]);
30971
+ useEffect(() => {
30972
+ if (!isAnimating) setThumbX(checked ? thumbOffsetOn : thumbOffsetOff);
30973
+ }, [
30974
+ checked,
30975
+ thumbOffsetOn,
30976
+ thumbOffsetOff,
30977
+ isAnimating
30978
+ ]);
30415
30979
  /**
30416
30980
  * Animate toggle transition
30417
30981
  */
@@ -30446,10 +31010,10 @@ function Toggle(props) {
30446
31010
  duration: duration.current,
30447
31011
  ease: "Cubic.easeOut",
30448
31012
  onUpdate: (tween) => {
30449
- const currentColor = interpolateColor(startColor, endColor, tween.getValue() ?? 0);
30450
- track.clear();
30451
- track.fillStyle(props.disabled ? disabledColor.current : currentColor, 1);
30452
- track.fillRoundedRect(0, 0, width.current, height.current, trackRadius);
31013
+ const progress = tween.getValue() ?? 0;
31014
+ const currentColor = interpolateColor(startColor, endColor, progress);
31015
+ const currentBorderColor = interpolateColor(getTrackBorderColor(checked), getTrackBorderColor(newChecked), progress);
31016
+ drawTrackShape(track, props.disabled && !newChecked ? disabledColor.current : currentColor, currentBorderColor);
30453
31017
  }
30454
31018
  });
30455
31019
  setChecked(newChecked);
@@ -30468,9 +31032,7 @@ function Toggle(props) {
30468
31032
  */
30469
31033
  const drawTrack = (g) => {
30470
31034
  g.clear();
30471
- const color = props.disabled ? disabledColor.current : checked ? trackColorOn.current : trackColorOff.current;
30472
- g.fillStyle(color, 1);
30473
- g.fillRoundedRect(0, 0, width.current, height.current, trackRadius);
31035
+ drawTrackShape(g, getTrackColor(checked), getTrackBorderColor(checked));
30474
31036
  };
30475
31037
  /**
30476
31038
  * Draw thumb graphics
@@ -30479,6 +31041,10 @@ function Toggle(props) {
30479
31041
  g.clear();
30480
31042
  g.fillStyle(thumbColor.current, 1);
30481
31043
  g.fillCircle(0, 0, thumbRadius);
31044
+ if (thumbBorderWidth.current > 0) {
31045
+ g.lineStyle(thumbBorderWidth.current, thumbBorderColor.current, 1);
31046
+ g.strokeCircle(0, 0, Math.max(0, thumbRadius - thumbBorderWidth.current / 2));
31047
+ }
30482
31048
  };
30483
31049
  const toggleElement = /* @__PURE__ */ require_jsx_runtime.jsxs(View, {
30484
31050
  direction: "stack",
@@ -30486,11 +31052,24 @@ function Toggle(props) {
30486
31052
  height: height.current,
30487
31053
  enableGestures: !props.disabled,
30488
31054
  onTouch: handleClick,
30489
- alpha: props.disabled ? .5 : 1,
31055
+ alpha: props.disabled ? disabledAlpha.current : 1,
30490
31056
  children: [/* @__PURE__ */ require_jsx_runtime.jsx(Graphics, {
30491
31057
  ref: trackRef,
30492
31058
  width: width.current,
30493
31059
  height: height.current,
31060
+ dependencies: [
31061
+ checked,
31062
+ width.current,
31063
+ height.current,
31064
+ trackColorOff.current,
31065
+ trackColorOn.current,
31066
+ trackBorderColorOff.current,
31067
+ trackBorderColorOn.current,
31068
+ trackBorderWidth.current,
31069
+ disabledColor.current,
31070
+ padding.current,
31071
+ props.disabled
31072
+ ],
30494
31073
  onDraw: drawTrack
30495
31074
  }), /* @__PURE__ */ require_jsx_runtime.jsx(Graphics, {
30496
31075
  ref: thumbRef,
@@ -30498,12 +31077,19 @@ function Toggle(props) {
30498
31077
  height: thumbSize.current,
30499
31078
  x: thumbX,
30500
31079
  y: height.current / 2,
31080
+ dependencies: [
31081
+ thumbSize.current,
31082
+ thumbColor.current,
31083
+ thumbBorderColor.current,
31084
+ thumbBorderWidth.current
31085
+ ],
30501
31086
  onDraw: drawThumb
30502
31087
  })]
30503
31088
  });
30504
31089
  const labelElement = props.label && labelPosition !== "none" ? /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
30505
31090
  text: props.label,
30506
- style: themed.labelStyle
31091
+ style: themed.labelStyle,
31092
+ alpha: props.disabled ? disabledAlpha.current : 1
30507
31093
  }) : null;
30508
31094
  if (!props.label || labelPosition === "none") return /* @__PURE__ */ require_jsx_runtime.jsxs(View, {
30509
31095
  ref: containerRef,
@@ -30533,63 +31119,6 @@ function Toggle(props) {
30533
31119
  });
30534
31120
  }
30535
31121
  //#endregion
30536
- //#region src/components/custom/TransformOriginView.tsx
30537
- /**
30538
- * TransformOriginView component - declarative transforms around custom origin point
30539
- *
30540
- * Uses three nested Views to apply rotation/scale around configurable origin:
30541
- * - Outer View: Defines bounding box, receives layout/position props
30542
- * - Middle View: Positioned at origin point, receives transform/visual props
30543
- * - Inner View: Contains content, offset to align with origin
30544
- *
30545
- * @param props - TransformOriginView props
30546
- * @returns JSX element
30547
- */
30548
- function TransformOriginView({ originX = .5, originY = .5, width, height, x = 0, y = 0, children, rotation, scale, scaleX, scaleY, backgroundColor, backgroundAlpha, cornerRadius, borderWidth, borderColor, borderAlpha, ...outerViewProps }) {
30549
- if (typeof width !== "number" || typeof height !== "number") throw new Error("TransformOriginView requires numeric width and height");
30550
- const offsetX = width * originX;
30551
- const offsetY = height * originY;
30552
- const middleViewProps = {};
30553
- if (rotation !== void 0) middleViewProps.rotation = rotation;
30554
- if (scale !== void 0) middleViewProps.scale = scale;
30555
- if (scaleX !== void 0) middleViewProps.scaleX = scaleX;
30556
- if (scaleY !== void 0) middleViewProps.scaleY = scaleY;
30557
- if (backgroundColor !== void 0) middleViewProps.backgroundColor = backgroundColor;
30558
- if (backgroundAlpha !== void 0) middleViewProps.backgroundAlpha = backgroundAlpha;
30559
- if (cornerRadius !== void 0) middleViewProps.cornerRadius = cornerRadius;
30560
- if (borderWidth !== void 0) middleViewProps.borderWidth = borderWidth;
30561
- if (borderColor !== void 0) middleViewProps.borderColor = borderColor;
30562
- if (borderAlpha !== void 0) middleViewProps.borderAlpha = borderAlpha;
30563
- return /* @__PURE__ */ require_jsx_runtime.jsx(View, {
30564
- x,
30565
- y,
30566
- width,
30567
- height,
30568
- padding: 0,
30569
- direction: "stack",
30570
- ...outerViewProps,
30571
- children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
30572
- x: offsetX,
30573
- y: offsetY,
30574
- width,
30575
- height,
30576
- padding: 0,
30577
- direction: "stack",
30578
- ...middleViewProps,
30579
- children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
30580
- x: -offsetX,
30581
- y: -offsetY,
30582
- width,
30583
- height,
30584
- padding: 0,
30585
- direction: "stack",
30586
- backgroundAlpha: 0,
30587
- children
30588
- })
30589
- })
30590
- });
30591
- }
30592
- //#endregion
30593
31122
  Object.defineProperty(exports, "Accordion", {
30594
31123
  enumerable: true,
30595
31124
  get: function() {
@@ -31809,4 +32338,4 @@ Object.defineProperty(exports, "withHooks", {
31809
32338
  }
31810
32339
  });
31811
32340
 
31812
- //# sourceMappingURL=custom-37gL0VZG.cjs.map
32341
+ //# sourceMappingURL=custom-Bnit70lx.cjs.map