@energy8platform/game-engine 0.11.0 → 0.12.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.
- package/README.md +88 -1
- package/dist/index.cjs.js +49 -7
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +49 -7
- package/dist/index.esm.js.map +1 -1
- package/dist/react.cjs.js +667 -215
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.esm.js +667 -215
- package/dist/react.esm.js.map +1 -1
- package/dist/ui.cjs.js +407 -7
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.d.ts +156 -2
- package/dist/ui.esm.js +406 -8
- package/dist/ui.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/react/applyProps.ts +6 -4
- package/src/react/extendAll.ts +2 -0
- package/src/react/jsx.d.ts +28 -1
- package/src/react/reconciler.ts +58 -2
- package/src/ui/FlexContainer.ts +57 -7
- package/src/ui/Slider.ts +241 -0
- package/src/ui/Toggle.ts +201 -0
- package/src/ui/index.ts +5 -1
package/dist/react.esm.js
CHANGED
|
@@ -21,6 +21,8 @@ function extend(components) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const RESERVED = new Set(['children', 'key', 'ref']);
|
|
24
|
+
/** Props handled by the reconciler as flex item config, not forwarded to components */
|
|
25
|
+
const FLEX_ITEM_PROPS$1 = new Set(['flexGrow', 'flexShrink', 'layoutWidth', 'layoutHeight', 'alignSelf', 'flexExclude']);
|
|
24
26
|
// ─── UI Component helpers ────────────────────────────────
|
|
25
27
|
/**
|
|
26
28
|
* Extract a config object from React props.
|
|
@@ -30,7 +32,7 @@ const RESERVED = new Set(['children', 'key', 'ref']);
|
|
|
30
32
|
function extractConfig(props) {
|
|
31
33
|
const config = {};
|
|
32
34
|
for (const key in props) {
|
|
33
|
-
if (RESERVED.has(key) || isEventProp(key))
|
|
35
|
+
if (RESERVED.has(key) || FLEX_ITEM_PROPS$1.has(key) || isEventProp(key))
|
|
34
36
|
continue;
|
|
35
37
|
if (key.includes('-')) {
|
|
36
38
|
const parts = key.split('-');
|
|
@@ -55,7 +57,7 @@ function diffConfig(newProps, oldProps) {
|
|
|
55
57
|
const changed = {};
|
|
56
58
|
// New or changed props
|
|
57
59
|
for (const key in newProps) {
|
|
58
|
-
if (RESERVED.has(key) || isEventProp(key))
|
|
60
|
+
if (RESERVED.has(key) || FLEX_ITEM_PROPS$1.has(key) || isEventProp(key))
|
|
59
61
|
continue;
|
|
60
62
|
if (newProps[key] !== oldProps[key]) {
|
|
61
63
|
if (key.includes('-')) {
|
|
@@ -154,7 +156,7 @@ function setNestedValue(target, path, value) {
|
|
|
154
156
|
function applyProps(instance, newProps, oldProps = {}) {
|
|
155
157
|
// Remove old props not in newProps
|
|
156
158
|
for (const key in oldProps) {
|
|
157
|
-
if (RESERVED.has(key) || key in newProps)
|
|
159
|
+
if (RESERVED.has(key) || FLEX_ITEM_PROPS$1.has(key) || key in newProps)
|
|
158
160
|
continue;
|
|
159
161
|
const pixiEvent = REACT_TO_PIXI_EVENTS[key];
|
|
160
162
|
if (pixiEvent) {
|
|
@@ -173,7 +175,7 @@ function applyProps(instance, newProps, oldProps = {}) {
|
|
|
173
175
|
}
|
|
174
176
|
// Apply new props
|
|
175
177
|
for (const key in newProps) {
|
|
176
|
-
if (RESERVED.has(key))
|
|
178
|
+
if (RESERVED.has(key) || FLEX_ITEM_PROPS$1.has(key))
|
|
177
179
|
continue;
|
|
178
180
|
const value = newProps[key];
|
|
179
181
|
const pixiEvent = REACT_TO_PIXI_EVENTS[key];
|
|
@@ -199,182 +201,6 @@ function applyProps(instance, newProps, oldProps = {}) {
|
|
|
199
201
|
}
|
|
200
202
|
}
|
|
201
203
|
|
|
202
|
-
function toPascalCase(str) {
|
|
203
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
204
|
-
}
|
|
205
|
-
const hostConfig = {
|
|
206
|
-
isPrimaryRenderer: false,
|
|
207
|
-
supportsMutation: true,
|
|
208
|
-
supportsPersistence: false,
|
|
209
|
-
supportsHydration: false,
|
|
210
|
-
createInstance(type, props) {
|
|
211
|
-
const name = toPascalCase(type);
|
|
212
|
-
const Ctor = catalogue[name];
|
|
213
|
-
if (!Ctor) {
|
|
214
|
-
throw new Error(`[PixiReconciler] Unknown element "<${type}>". ` +
|
|
215
|
-
`Call extend({ ${name} }) before rendering.`);
|
|
216
|
-
}
|
|
217
|
-
let instance;
|
|
218
|
-
if (Ctor.prototype.__uiComponent) {
|
|
219
|
-
// Config-based UI component: pass props as constructor config
|
|
220
|
-
const config = extractConfig(props);
|
|
221
|
-
instance = new Ctor(config);
|
|
222
|
-
applyEventProps(instance, props);
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
// Standard PixiJS element
|
|
226
|
-
instance = new Ctor();
|
|
227
|
-
applyProps(instance, props);
|
|
228
|
-
}
|
|
229
|
-
if (hasEventProps(props) && instance.eventMode === 'auto') {
|
|
230
|
-
instance.eventMode = 'static';
|
|
231
|
-
}
|
|
232
|
-
return instance;
|
|
233
|
-
},
|
|
234
|
-
createTextInstance() {
|
|
235
|
-
throw new Error('[PixiReconciler] Text strings are not supported. Use a <text> element.');
|
|
236
|
-
},
|
|
237
|
-
appendInitialChild(parent, child) {
|
|
238
|
-
if (child instanceof Container)
|
|
239
|
-
parent.addChild(child);
|
|
240
|
-
},
|
|
241
|
-
appendChild(parent, child) {
|
|
242
|
-
if (child instanceof Container)
|
|
243
|
-
parent.addChild(child);
|
|
244
|
-
},
|
|
245
|
-
appendChildToContainer(container, child) {
|
|
246
|
-
if (child instanceof Container)
|
|
247
|
-
container.addChild(child);
|
|
248
|
-
},
|
|
249
|
-
removeChild(parent, child) {
|
|
250
|
-
if (child instanceof Container) {
|
|
251
|
-
parent.removeChild(child);
|
|
252
|
-
child.destroy({ children: true });
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
removeChildFromContainer(container, child) {
|
|
256
|
-
if (child instanceof Container) {
|
|
257
|
-
container.removeChild(child);
|
|
258
|
-
child.destroy({ children: true });
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
insertBefore(parent, child, beforeChild) {
|
|
262
|
-
if (child instanceof Container && beforeChild instanceof Container) {
|
|
263
|
-
if (child.parent)
|
|
264
|
-
child.parent.removeChild(child);
|
|
265
|
-
const index = parent.getChildIndex(beforeChild);
|
|
266
|
-
parent.addChildAt(child, index);
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
insertInContainerBefore(container, child, beforeChild) {
|
|
270
|
-
if (child instanceof Container && beforeChild instanceof Container) {
|
|
271
|
-
if (child.parent)
|
|
272
|
-
child.parent.removeChild(child);
|
|
273
|
-
const index = container.getChildIndex(beforeChild);
|
|
274
|
-
container.addChildAt(child, index);
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
commitUpdate(instance, _updatePayload, _type, oldProps, newProps) {
|
|
278
|
-
if (instance.__uiComponent && typeof instance.updateConfig === 'function') {
|
|
279
|
-
const changed = diffConfig(newProps, oldProps);
|
|
280
|
-
if (Object.keys(changed).length > 0) {
|
|
281
|
-
instance.updateConfig(changed);
|
|
282
|
-
}
|
|
283
|
-
applyEventProps(instance, newProps, oldProps);
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
applyProps(instance, newProps, oldProps);
|
|
287
|
-
}
|
|
288
|
-
if (hasEventProps(newProps) && instance.eventMode === 'auto') {
|
|
289
|
-
instance.eventMode = 'static';
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
finalizeInitialChildren() {
|
|
293
|
-
return false;
|
|
294
|
-
},
|
|
295
|
-
prepareUpdate() {
|
|
296
|
-
return true;
|
|
297
|
-
},
|
|
298
|
-
shouldSetTextContent() {
|
|
299
|
-
return false;
|
|
300
|
-
},
|
|
301
|
-
getRootHostContext() {
|
|
302
|
-
return null;
|
|
303
|
-
},
|
|
304
|
-
getChildHostContext(parentHostContext) {
|
|
305
|
-
return parentHostContext;
|
|
306
|
-
},
|
|
307
|
-
getPublicInstance(instance) {
|
|
308
|
-
return instance;
|
|
309
|
-
},
|
|
310
|
-
prepareForCommit() {
|
|
311
|
-
return null;
|
|
312
|
-
},
|
|
313
|
-
resetAfterCommit() { },
|
|
314
|
-
preparePortalMount() { },
|
|
315
|
-
scheduleTimeout: setTimeout,
|
|
316
|
-
cancelTimeout: clearTimeout,
|
|
317
|
-
noTimeout: -1,
|
|
318
|
-
getCurrentEventPriority() {
|
|
319
|
-
return DefaultEventPriority;
|
|
320
|
-
},
|
|
321
|
-
hideInstance(instance) {
|
|
322
|
-
instance.visible = false;
|
|
323
|
-
},
|
|
324
|
-
unhideInstance(instance) {
|
|
325
|
-
instance.visible = true;
|
|
326
|
-
},
|
|
327
|
-
hideTextInstance() { },
|
|
328
|
-
unhideTextInstance() { },
|
|
329
|
-
clearContainer() { },
|
|
330
|
-
detachDeletedInstance() { },
|
|
331
|
-
prepareScopeUpdate() { },
|
|
332
|
-
getInstanceFromNode() { return null; },
|
|
333
|
-
getInstanceFromScope() { return null; },
|
|
334
|
-
beforeActiveInstanceBlur() { },
|
|
335
|
-
afterActiveInstanceBlur() { },
|
|
336
|
-
};
|
|
337
|
-
const reconciler = Reconciler(hostConfig);
|
|
338
|
-
|
|
339
|
-
function createPixiRoot(container) {
|
|
340
|
-
const fiberRoot = reconciler.createContainer(container, // containerInfo
|
|
341
|
-
ConcurrentRoot, // tag
|
|
342
|
-
null, // hydrationCallbacks
|
|
343
|
-
false, // isStrictMode
|
|
344
|
-
null, // concurrentUpdatesByDefaultOverride
|
|
345
|
-
'', // identifierPrefix
|
|
346
|
-
(err) => console.error('[PixiRoot]', err), null);
|
|
347
|
-
return {
|
|
348
|
-
render(element) {
|
|
349
|
-
reconciler.updateContainer(element, fiberRoot, null, () => { });
|
|
350
|
-
},
|
|
351
|
-
unmount() {
|
|
352
|
-
reconciler.updateContainer(null, fiberRoot, null, () => { });
|
|
353
|
-
},
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Resolve a ViewInput to a Container instance.
|
|
359
|
-
*
|
|
360
|
-
* @example
|
|
361
|
-
* ```ts
|
|
362
|
-
* resolveView('btn-idle') // → Sprite.from('btn-idle')
|
|
363
|
-
* resolveView(someTexture) // → new Sprite(someTexture)
|
|
364
|
-
* resolveView(myCustomContainer) // → myCustomContainer (as-is)
|
|
365
|
-
* resolveView(undefined) // → null
|
|
366
|
-
* ```
|
|
367
|
-
*/
|
|
368
|
-
function resolveView(input) {
|
|
369
|
-
if (input == null)
|
|
370
|
-
return null;
|
|
371
|
-
if (typeof input === 'string')
|
|
372
|
-
return Sprite.from(input);
|
|
373
|
-
if (input instanceof Texture)
|
|
374
|
-
return new Sprite(input);
|
|
375
|
-
return input;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
204
|
// ─── Helpers ─────────────────────────────────────────────
|
|
379
205
|
function normalizePadding(p) {
|
|
380
206
|
return typeof p === 'number' ? [p, p, p, p] : p;
|
|
@@ -433,6 +259,37 @@ function layoutLine(items, isRow, mainSize, justify, align, gap, crossOffset, cr
|
|
|
433
259
|
}
|
|
434
260
|
}
|
|
435
261
|
}
|
|
262
|
+
// Shrink: if content overflows and mainSize is finite, shrink eligible items
|
|
263
|
+
if (totalGrow === 0 && mainSize > 0) {
|
|
264
|
+
const overflow = totalFixed + totalGap - mainSize;
|
|
265
|
+
if (overflow > 0) {
|
|
266
|
+
let totalShrinkable = 0;
|
|
267
|
+
for (const item of items) {
|
|
268
|
+
const shrink = item.child._flexConfig?.flexShrink ?? 1;
|
|
269
|
+
if (shrink > 0) {
|
|
270
|
+
totalShrinkable += isRow ? item.w : item.h;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (totalShrinkable > 0) {
|
|
274
|
+
for (const item of items) {
|
|
275
|
+
const shrink = item.child._flexConfig?.flexShrink ?? 1;
|
|
276
|
+
if (shrink > 0) {
|
|
277
|
+
const itemMain = isRow ? item.w : item.h;
|
|
278
|
+
const reduction = overflow * (itemMain / totalShrinkable);
|
|
279
|
+
const newSize = Math.max(0, itemMain - reduction);
|
|
280
|
+
if (isRow) {
|
|
281
|
+
item.w = newSize;
|
|
282
|
+
item.child.width = newSize;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
item.h = newSize;
|
|
286
|
+
item.child.height = newSize;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
436
293
|
// Calculate total main size after flex
|
|
437
294
|
let totalMain = totalGap;
|
|
438
295
|
for (const item of items) {
|
|
@@ -469,9 +326,12 @@ function layoutLine(items, isRow, mainSize, justify, align, gap, crossOffset, cr
|
|
|
469
326
|
for (const item of items) {
|
|
470
327
|
const mainDim = isRow ? item.w : item.h;
|
|
471
328
|
const crossDim = isRow ? item.h : item.w;
|
|
472
|
-
// Cross-axis alignment
|
|
329
|
+
// Cross-axis alignment (alignSelf overrides align)
|
|
330
|
+
const effectiveAlign = (item.child._flexConfig?.alignSelf && item.child._flexConfig.alignSelf !== 'auto')
|
|
331
|
+
? item.child._flexConfig.alignSelf
|
|
332
|
+
: align;
|
|
473
333
|
let crossPos = crossOffset;
|
|
474
|
-
switch (
|
|
334
|
+
switch (effectiveAlign) {
|
|
475
335
|
case 'start':
|
|
476
336
|
break;
|
|
477
337
|
case 'center':
|
|
@@ -655,11 +515,14 @@ class FlexContainer extends Container {
|
|
|
655
515
|
const contentH = this._explicitHeight > 0 ? this._explicitHeight - pt - pb : Infinity;
|
|
656
516
|
const mainLimit = isRow ? contentW : contentH;
|
|
657
517
|
const crossLimit = isRow ? contentH : contentW;
|
|
658
|
-
// Measure children
|
|
659
|
-
const measured =
|
|
518
|
+
// Measure children (skip flexExclude — they position themselves)
|
|
519
|
+
const measured = [];
|
|
520
|
+
for (const child of this._layoutChildren) {
|
|
521
|
+
if (child._flexConfig?.flexExclude)
|
|
522
|
+
continue;
|
|
660
523
|
const { w, h, ox, oy } = measureChild(child);
|
|
661
|
-
|
|
662
|
-
}
|
|
524
|
+
measured.push({ child, w, h, ox, oy });
|
|
525
|
+
}
|
|
663
526
|
// Split into lines (if wrapping)
|
|
664
527
|
const lines = [];
|
|
665
528
|
if (flexWrap && mainLimit < Infinity) {
|
|
@@ -702,7 +565,12 @@ class FlexContainer extends Container {
|
|
|
702
565
|
const mainStart = isRow ? pl : pt;
|
|
703
566
|
// Offset items by padding
|
|
704
567
|
const tempItems = line.map((item) => ({ ...item }));
|
|
705
|
-
|
|
568
|
+
// For single-line layouts, use the full available cross space for alignment;
|
|
569
|
+
// for multi-line (wrapping), each line gets its own measured cross size.
|
|
570
|
+
const effectiveCross = lines.length === 1 && crossLimit < Infinity
|
|
571
|
+
? crossLimit
|
|
572
|
+
: (crossLimit < Infinity ? Math.min(lineCross, crossLimit) : lineCross);
|
|
573
|
+
layoutLine(tempItems, isRow, mainLimit < Infinity ? mainLimit : 0, mainLimit < Infinity ? justifyContent : 'start', alignItems, gap, crossOffset, effectiveCross);
|
|
706
574
|
// Apply main-axis padding offset
|
|
707
575
|
for (const item of tempItems) {
|
|
708
576
|
const origChild = line.find((l) => l.child === item.child);
|
|
@@ -723,36 +591,263 @@ class FlexContainer extends Container {
|
|
|
723
591
|
maxX = Math.max(maxX, child.x + w);
|
|
724
592
|
maxY = Math.max(maxY, child.y + h);
|
|
725
593
|
}
|
|
726
|
-
const [, pr, pb] = this._padding;
|
|
727
|
-
return { width: maxX + pr, height: maxY + pb };
|
|
728
|
-
}
|
|
729
|
-
/** React reconciler update hook — applies changed config props */
|
|
730
|
-
updateConfig(changed) {
|
|
731
|
-
if ('direction' in changed)
|
|
732
|
-
this.setDirection(changed.direction);
|
|
733
|
-
if ('justifyContent' in changed)
|
|
734
|
-
this.setJustifyContent(changed.justifyContent);
|
|
735
|
-
if ('alignItems' in changed)
|
|
736
|
-
this.setAlignItems(changed.alignItems);
|
|
737
|
-
if ('gap' in changed)
|
|
738
|
-
this.setGap(changed.gap);
|
|
739
|
-
if ('padding' in changed)
|
|
740
|
-
this.setPadding(changed.padding);
|
|
741
|
-
if ('flexWrap' in changed) {
|
|
742
|
-
this._config.flexWrap = changed.flexWrap;
|
|
743
|
-
this._layoutDirty = true;
|
|
594
|
+
const [, pr, pb] = this._padding;
|
|
595
|
+
return { width: maxX + pr, height: maxY + pb };
|
|
596
|
+
}
|
|
597
|
+
/** React reconciler update hook — applies changed config props */
|
|
598
|
+
updateConfig(changed) {
|
|
599
|
+
if ('direction' in changed)
|
|
600
|
+
this.setDirection(changed.direction);
|
|
601
|
+
if ('justifyContent' in changed)
|
|
602
|
+
this.setJustifyContent(changed.justifyContent);
|
|
603
|
+
if ('alignItems' in changed)
|
|
604
|
+
this.setAlignItems(changed.alignItems);
|
|
605
|
+
if ('gap' in changed)
|
|
606
|
+
this.setGap(changed.gap);
|
|
607
|
+
if ('padding' in changed)
|
|
608
|
+
this.setPadding(changed.padding);
|
|
609
|
+
if ('flexWrap' in changed) {
|
|
610
|
+
this._config.flexWrap = changed.flexWrap;
|
|
611
|
+
this._layoutDirty = true;
|
|
612
|
+
}
|
|
613
|
+
if ('width' in changed || 'height' in changed) {
|
|
614
|
+
this.resize(changed.width ?? this._explicitWidth, changed.height ?? this._explicitHeight);
|
|
615
|
+
return; // resize calls updateLayout
|
|
616
|
+
}
|
|
617
|
+
if (this._layoutDirty)
|
|
618
|
+
this.updateLayout();
|
|
619
|
+
}
|
|
620
|
+
destroy(options) {
|
|
621
|
+
this._layoutChildren.length = 0;
|
|
622
|
+
super.destroy(options);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Flex item prop names that should be forwarded to _flexConfig on the child */
|
|
627
|
+
const FLEX_ITEM_PROPS = ['flexGrow', 'flexShrink', 'layoutWidth', 'layoutHeight', 'alignSelf', 'flexExclude'];
|
|
628
|
+
/** Extract FlexItemConfig from props if any flex item props are present */
|
|
629
|
+
function extractFlexItemConfig(props) {
|
|
630
|
+
let config;
|
|
631
|
+
for (const key of FLEX_ITEM_PROPS) {
|
|
632
|
+
if (key in props) {
|
|
633
|
+
if (!config)
|
|
634
|
+
config = {};
|
|
635
|
+
config[key] = props[key];
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return config;
|
|
639
|
+
}
|
|
640
|
+
/** Apply flex item config to a child being added to a FlexContainer */
|
|
641
|
+
function addChildToFlex(parent, child) {
|
|
642
|
+
const flexConfig = child._flexConfig;
|
|
643
|
+
if (flexConfig && Object.keys(flexConfig).length > 0) {
|
|
644
|
+
parent.addFlexChild(child, flexConfig);
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
parent.addChild(child);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function toPascalCase(str) {
|
|
651
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
652
|
+
}
|
|
653
|
+
const hostConfig = {
|
|
654
|
+
isPrimaryRenderer: false,
|
|
655
|
+
supportsMutation: true,
|
|
656
|
+
supportsPersistence: false,
|
|
657
|
+
supportsHydration: false,
|
|
658
|
+
createInstance(type, props) {
|
|
659
|
+
const name = toPascalCase(type);
|
|
660
|
+
const Ctor = catalogue[name];
|
|
661
|
+
if (!Ctor) {
|
|
662
|
+
throw new Error(`[PixiReconciler] Unknown element "<${type}>". ` +
|
|
663
|
+
`Call extend({ ${name} }) before rendering.`);
|
|
664
|
+
}
|
|
665
|
+
let instance;
|
|
666
|
+
if (Ctor.prototype.__uiComponent) {
|
|
667
|
+
// Config-based UI component: pass props as constructor config
|
|
668
|
+
const config = extractConfig(props);
|
|
669
|
+
instance = new Ctor(config);
|
|
670
|
+
applyEventProps(instance, props);
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
// Standard PixiJS element
|
|
674
|
+
instance = new Ctor();
|
|
675
|
+
applyProps(instance, props);
|
|
676
|
+
}
|
|
677
|
+
if (hasEventProps(props) && instance.eventMode === 'auto') {
|
|
678
|
+
instance.eventMode = 'static';
|
|
679
|
+
}
|
|
680
|
+
// Store flex item config for when this child is added to a FlexContainer parent
|
|
681
|
+
const flexItemConfig = extractFlexItemConfig(props);
|
|
682
|
+
if (flexItemConfig) {
|
|
683
|
+
instance._flexConfig = { ...instance._flexConfig, ...flexItemConfig };
|
|
684
|
+
}
|
|
685
|
+
return instance;
|
|
686
|
+
},
|
|
687
|
+
createTextInstance() {
|
|
688
|
+
throw new Error('[PixiReconciler] Text strings are not supported. Use a <text> element.');
|
|
689
|
+
},
|
|
690
|
+
appendInitialChild(parent, child) {
|
|
691
|
+
if (child instanceof Container) {
|
|
692
|
+
if (parent instanceof FlexContainer) {
|
|
693
|
+
addChildToFlex(parent, child);
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
parent.addChild(child);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
appendChild(parent, child) {
|
|
701
|
+
if (child instanceof Container) {
|
|
702
|
+
if (parent instanceof FlexContainer) {
|
|
703
|
+
addChildToFlex(parent, child);
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
parent.addChild(child);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
appendChildToContainer(container, child) {
|
|
711
|
+
if (child instanceof Container)
|
|
712
|
+
container.addChild(child);
|
|
713
|
+
},
|
|
714
|
+
removeChild(parent, child) {
|
|
715
|
+
if (child instanceof Container) {
|
|
716
|
+
parent.removeChild(child);
|
|
717
|
+
child.destroy({ children: true });
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
removeChildFromContainer(container, child) {
|
|
721
|
+
if (child instanceof Container) {
|
|
722
|
+
container.removeChild(child);
|
|
723
|
+
child.destroy({ children: true });
|
|
724
|
+
}
|
|
725
|
+
},
|
|
726
|
+
insertBefore(parent, child, beforeChild) {
|
|
727
|
+
if (child instanceof Container && beforeChild instanceof Container) {
|
|
728
|
+
if (child.parent)
|
|
729
|
+
child.parent.removeChild(child);
|
|
730
|
+
const index = parent.getChildIndex(beforeChild);
|
|
731
|
+
parent.addChildAt(child, index);
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
insertInContainerBefore(container, child, beforeChild) {
|
|
735
|
+
if (child instanceof Container && beforeChild instanceof Container) {
|
|
736
|
+
if (child.parent)
|
|
737
|
+
child.parent.removeChild(child);
|
|
738
|
+
const index = container.getChildIndex(beforeChild);
|
|
739
|
+
container.addChildAt(child, index);
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
commitUpdate(instance, _updatePayload, _type, oldProps, newProps) {
|
|
743
|
+
if (instance.__uiComponent && typeof instance.updateConfig === 'function') {
|
|
744
|
+
const changed = diffConfig(newProps, oldProps);
|
|
745
|
+
if (Object.keys(changed).length > 0) {
|
|
746
|
+
instance.updateConfig(changed);
|
|
747
|
+
}
|
|
748
|
+
applyEventProps(instance, newProps, oldProps);
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
applyProps(instance, newProps, oldProps);
|
|
752
|
+
}
|
|
753
|
+
// Update flex item config if parent is FlexContainer
|
|
754
|
+
const newFlexConfig = extractFlexItemConfig(newProps);
|
|
755
|
+
const oldFlexConfig = extractFlexItemConfig(oldProps);
|
|
756
|
+
if (newFlexConfig || oldFlexConfig) {
|
|
757
|
+
instance._flexConfig = { ...instance._flexConfig, ...newFlexConfig };
|
|
758
|
+
// Trigger parent relayout
|
|
759
|
+
if (instance.parent instanceof FlexContainer) {
|
|
760
|
+
instance.parent.updateLayout();
|
|
761
|
+
}
|
|
744
762
|
}
|
|
745
|
-
if (
|
|
746
|
-
|
|
747
|
-
return; // resize calls updateLayout
|
|
763
|
+
if (hasEventProps(newProps) && instance.eventMode === 'auto') {
|
|
764
|
+
instance.eventMode = 'static';
|
|
748
765
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
766
|
+
},
|
|
767
|
+
finalizeInitialChildren() {
|
|
768
|
+
return false;
|
|
769
|
+
},
|
|
770
|
+
prepareUpdate() {
|
|
771
|
+
return true;
|
|
772
|
+
},
|
|
773
|
+
shouldSetTextContent() {
|
|
774
|
+
return false;
|
|
775
|
+
},
|
|
776
|
+
getRootHostContext() {
|
|
777
|
+
return null;
|
|
778
|
+
},
|
|
779
|
+
getChildHostContext(parentHostContext) {
|
|
780
|
+
return parentHostContext;
|
|
781
|
+
},
|
|
782
|
+
getPublicInstance(instance) {
|
|
783
|
+
return instance;
|
|
784
|
+
},
|
|
785
|
+
prepareForCommit() {
|
|
786
|
+
return null;
|
|
787
|
+
},
|
|
788
|
+
resetAfterCommit() { },
|
|
789
|
+
preparePortalMount() { },
|
|
790
|
+
scheduleTimeout: setTimeout,
|
|
791
|
+
cancelTimeout: clearTimeout,
|
|
792
|
+
noTimeout: -1,
|
|
793
|
+
getCurrentEventPriority() {
|
|
794
|
+
return DefaultEventPriority;
|
|
795
|
+
},
|
|
796
|
+
hideInstance(instance) {
|
|
797
|
+
instance.visible = false;
|
|
798
|
+
},
|
|
799
|
+
unhideInstance(instance) {
|
|
800
|
+
instance.visible = true;
|
|
801
|
+
},
|
|
802
|
+
hideTextInstance() { },
|
|
803
|
+
unhideTextInstance() { },
|
|
804
|
+
clearContainer() { },
|
|
805
|
+
detachDeletedInstance() { },
|
|
806
|
+
prepareScopeUpdate() { },
|
|
807
|
+
getInstanceFromNode() { return null; },
|
|
808
|
+
getInstanceFromScope() { return null; },
|
|
809
|
+
beforeActiveInstanceBlur() { },
|
|
810
|
+
afterActiveInstanceBlur() { },
|
|
811
|
+
};
|
|
812
|
+
const reconciler = Reconciler(hostConfig);
|
|
813
|
+
|
|
814
|
+
function createPixiRoot(container) {
|
|
815
|
+
const fiberRoot = reconciler.createContainer(container, // containerInfo
|
|
816
|
+
ConcurrentRoot, // tag
|
|
817
|
+
null, // hydrationCallbacks
|
|
818
|
+
false, // isStrictMode
|
|
819
|
+
null, // concurrentUpdatesByDefaultOverride
|
|
820
|
+
'', // identifierPrefix
|
|
821
|
+
(err) => console.error('[PixiRoot]', err), null);
|
|
822
|
+
return {
|
|
823
|
+
render(element) {
|
|
824
|
+
reconciler.updateContainer(element, fiberRoot, null, () => { });
|
|
825
|
+
},
|
|
826
|
+
unmount() {
|
|
827
|
+
reconciler.updateContainer(null, fiberRoot, null, () => { });
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Resolve a ViewInput to a Container instance.
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* ```ts
|
|
837
|
+
* resolveView('btn-idle') // → Sprite.from('btn-idle')
|
|
838
|
+
* resolveView(someTexture) // → new Sprite(someTexture)
|
|
839
|
+
* resolveView(myCustomContainer) // → myCustomContainer (as-is)
|
|
840
|
+
* resolveView(undefined) // → null
|
|
841
|
+
* ```
|
|
842
|
+
*/
|
|
843
|
+
function resolveView(input) {
|
|
844
|
+
if (input == null)
|
|
845
|
+
return null;
|
|
846
|
+
if (typeof input === 'string')
|
|
847
|
+
return Sprite.from(input);
|
|
848
|
+
if (input instanceof Texture)
|
|
849
|
+
return new Sprite(input);
|
|
850
|
+
return input;
|
|
756
851
|
}
|
|
757
852
|
|
|
758
853
|
/**
|
|
@@ -2604,6 +2699,362 @@ class ScrollContainer extends Container {
|
|
|
2604
2699
|
}
|
|
2605
2700
|
}
|
|
2606
2701
|
|
|
2702
|
+
/**
|
|
2703
|
+
* Draggable slider with customizable track, fill, and handle views.
|
|
2704
|
+
*
|
|
2705
|
+
* @example
|
|
2706
|
+
* ```ts
|
|
2707
|
+
* const volume = new Slider({
|
|
2708
|
+
* min: 0, max: 1, value: 0.5,
|
|
2709
|
+
* width: 200, height: 8,
|
|
2710
|
+
* fillColor: 0xffd700,
|
|
2711
|
+
* onUpdate: (v) => console.log('Volume:', v),
|
|
2712
|
+
* });
|
|
2713
|
+
* ```
|
|
2714
|
+
*/
|
|
2715
|
+
class Slider extends Container {
|
|
2716
|
+
__uiComponent = true;
|
|
2717
|
+
_track;
|
|
2718
|
+
_fill;
|
|
2719
|
+
_fillMask;
|
|
2720
|
+
_handle;
|
|
2721
|
+
_config;
|
|
2722
|
+
_value;
|
|
2723
|
+
_dragging = false;
|
|
2724
|
+
onUpdate = null;
|
|
2725
|
+
onChange = null;
|
|
2726
|
+
constructor(config = {}) {
|
|
2727
|
+
super();
|
|
2728
|
+
this._config = {
|
|
2729
|
+
min: config.min ?? 0,
|
|
2730
|
+
max: config.max ?? 1,
|
|
2731
|
+
step: config.step ?? 0,
|
|
2732
|
+
width: config.width ?? 200,
|
|
2733
|
+
height: config.height ?? 8,
|
|
2734
|
+
borderRadius: config.borderRadius ?? 4,
|
|
2735
|
+
trackColor: config.trackColor ?? 0x333333,
|
|
2736
|
+
fillColor: config.fillColor ?? 0xffd700,
|
|
2737
|
+
handleRadius: config.handleRadius ?? 12,
|
|
2738
|
+
handleColor: config.handleColor ?? 0xffffff,
|
|
2739
|
+
};
|
|
2740
|
+
this._value = config.value ?? this._config.min;
|
|
2741
|
+
this.onUpdate = config.onUpdate ?? null;
|
|
2742
|
+
this.onChange = config.onChange ?? null;
|
|
2743
|
+
const { width, height, borderRadius, trackColor, fillColor, handleRadius, handleColor } = this._config;
|
|
2744
|
+
// Track
|
|
2745
|
+
const customTrack = resolveView(config.trackView);
|
|
2746
|
+
if (customTrack) {
|
|
2747
|
+
customTrack.width = width;
|
|
2748
|
+
customTrack.height = height;
|
|
2749
|
+
this._track = customTrack;
|
|
2750
|
+
}
|
|
2751
|
+
else {
|
|
2752
|
+
const g = new Graphics();
|
|
2753
|
+
g.roundRect(0, 0, width, height, borderRadius).fill(trackColor);
|
|
2754
|
+
this._track = g;
|
|
2755
|
+
}
|
|
2756
|
+
this.addChild(this._track);
|
|
2757
|
+
// Fill
|
|
2758
|
+
const customFill = resolveView(config.fillView);
|
|
2759
|
+
if (customFill) {
|
|
2760
|
+
customFill.width = width;
|
|
2761
|
+
customFill.height = height;
|
|
2762
|
+
this._fill = customFill;
|
|
2763
|
+
}
|
|
2764
|
+
else {
|
|
2765
|
+
const g = new Graphics();
|
|
2766
|
+
g.roundRect(0, 0, width, height, borderRadius).fill(fillColor);
|
|
2767
|
+
this._fill = g;
|
|
2768
|
+
}
|
|
2769
|
+
this.addChild(this._fill);
|
|
2770
|
+
// Fill mask
|
|
2771
|
+
this._fillMask = new Graphics();
|
|
2772
|
+
this.addChild(this._fillMask);
|
|
2773
|
+
this._fill.mask = this._fillMask;
|
|
2774
|
+
// Handle
|
|
2775
|
+
const customHandle = resolveView(config.handleView);
|
|
2776
|
+
if (customHandle) {
|
|
2777
|
+
this._handle = customHandle;
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
const g = new Graphics();
|
|
2781
|
+
g.circle(0, 0, handleRadius).fill(handleColor);
|
|
2782
|
+
this._handle = g;
|
|
2783
|
+
}
|
|
2784
|
+
this._handle.y = height / 2;
|
|
2785
|
+
this.addChild(this._handle);
|
|
2786
|
+
// Interaction
|
|
2787
|
+
this.eventMode = 'static';
|
|
2788
|
+
this.cursor = 'pointer';
|
|
2789
|
+
// Hit area covers track + handle overflow
|
|
2790
|
+
const hitPad = Math.max(handleRadius - height / 2, 0);
|
|
2791
|
+
this.hitArea = { contains: (x, y) => x >= -hitPad && x <= width + hitPad && y >= -hitPad && y <= height + hitPad };
|
|
2792
|
+
this.on('pointerdown', this._onPointerDown, this);
|
|
2793
|
+
this.on('globalpointermove', this._onPointerMove, this);
|
|
2794
|
+
this.on('pointerup', this._onPointerUp, this);
|
|
2795
|
+
this.on('pointerupoutside', this._onPointerUp, this);
|
|
2796
|
+
this._updateVisuals();
|
|
2797
|
+
}
|
|
2798
|
+
/** Current value */
|
|
2799
|
+
get value() {
|
|
2800
|
+
return this._value;
|
|
2801
|
+
}
|
|
2802
|
+
set value(v) {
|
|
2803
|
+
const clamped = this._applyStep(Math.max(this._config.min, Math.min(this._config.max, v)));
|
|
2804
|
+
if (clamped === this._value)
|
|
2805
|
+
return;
|
|
2806
|
+
this._value = clamped;
|
|
2807
|
+
this._updateVisuals();
|
|
2808
|
+
}
|
|
2809
|
+
get min() { return this._config.min; }
|
|
2810
|
+
get max() { return this._config.max; }
|
|
2811
|
+
/** React reconciler update hook */
|
|
2812
|
+
updateConfig(changed) {
|
|
2813
|
+
if ('value' in changed)
|
|
2814
|
+
this.value = changed.value;
|
|
2815
|
+
if ('min' in changed) {
|
|
2816
|
+
this._config.min = changed.min;
|
|
2817
|
+
this._updateVisuals();
|
|
2818
|
+
}
|
|
2819
|
+
if ('max' in changed) {
|
|
2820
|
+
this._config.max = changed.max;
|
|
2821
|
+
this._updateVisuals();
|
|
2822
|
+
}
|
|
2823
|
+
if ('step' in changed)
|
|
2824
|
+
this._config.step = changed.step;
|
|
2825
|
+
if ('onUpdate' in changed)
|
|
2826
|
+
this.onUpdate = changed.onUpdate;
|
|
2827
|
+
if ('onChange' in changed)
|
|
2828
|
+
this.onChange = changed.onChange;
|
|
2829
|
+
}
|
|
2830
|
+
_fraction() {
|
|
2831
|
+
const { min, max } = this._config;
|
|
2832
|
+
return max === min ? 0 : (this._value - min) / (max - min);
|
|
2833
|
+
}
|
|
2834
|
+
_applyStep(v) {
|
|
2835
|
+
const { step, min } = this._config;
|
|
2836
|
+
if (step <= 0)
|
|
2837
|
+
return v;
|
|
2838
|
+
return min + Math.round((v - min) / step) * step;
|
|
2839
|
+
}
|
|
2840
|
+
_updateVisuals() {
|
|
2841
|
+
const frac = this._fraction();
|
|
2842
|
+
const w = this._config.width;
|
|
2843
|
+
const h = this._config.height;
|
|
2844
|
+
// Update fill mask
|
|
2845
|
+
this._fillMask.clear();
|
|
2846
|
+
this._fillMask.rect(0, 0, w * frac, h).fill(0xffffff);
|
|
2847
|
+
// Update handle position
|
|
2848
|
+
this._handle.x = w * frac;
|
|
2849
|
+
}
|
|
2850
|
+
_valueFromPointer(e) {
|
|
2851
|
+
const local = this.toLocal(e.global);
|
|
2852
|
+
const frac = Math.max(0, Math.min(1, local.x / this._config.width));
|
|
2853
|
+
const { min, max } = this._config;
|
|
2854
|
+
return this._applyStep(min + frac * (max - min));
|
|
2855
|
+
}
|
|
2856
|
+
_onPointerDown(e) {
|
|
2857
|
+
this._dragging = true;
|
|
2858
|
+
const newValue = this._valueFromPointer(e);
|
|
2859
|
+
if (newValue !== this._value) {
|
|
2860
|
+
this._value = newValue;
|
|
2861
|
+
this._updateVisuals();
|
|
2862
|
+
this.onUpdate?.(this._value);
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
_onPointerMove(e) {
|
|
2866
|
+
if (!this._dragging)
|
|
2867
|
+
return;
|
|
2868
|
+
const newValue = this._valueFromPointer(e);
|
|
2869
|
+
if (newValue !== this._value) {
|
|
2870
|
+
this._value = newValue;
|
|
2871
|
+
this._updateVisuals();
|
|
2872
|
+
this.onUpdate?.(this._value);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
_onPointerUp(_e) {
|
|
2876
|
+
if (!this._dragging)
|
|
2877
|
+
return;
|
|
2878
|
+
this._dragging = false;
|
|
2879
|
+
this.onChange?.(this._value);
|
|
2880
|
+
}
|
|
2881
|
+
destroy(options) {
|
|
2882
|
+
this.off('pointerdown', this._onPointerDown, this);
|
|
2883
|
+
this.off('globalpointermove', this._onPointerMove, this);
|
|
2884
|
+
this.off('pointerup', this._onPointerUp, this);
|
|
2885
|
+
this.off('pointerupoutside', this._onPointerUp, this);
|
|
2886
|
+
this.onUpdate = null;
|
|
2887
|
+
this.onChange = null;
|
|
2888
|
+
super.destroy(options);
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
/**
|
|
2893
|
+
* Toggle switch with two states.
|
|
2894
|
+
*
|
|
2895
|
+
* Supports custom ON/OFF views or auto-generated Graphics-based toggle.
|
|
2896
|
+
* Click to toggle, or use `forceSwitch(value)` programmatically.
|
|
2897
|
+
*
|
|
2898
|
+
* @example
|
|
2899
|
+
* ```ts
|
|
2900
|
+
* const mute = new Toggle({
|
|
2901
|
+
* value: false,
|
|
2902
|
+
* onColor: 0x22cc22,
|
|
2903
|
+
* onChange: (on) => audioManager.mute(!on),
|
|
2904
|
+
* });
|
|
2905
|
+
* ```
|
|
2906
|
+
*/
|
|
2907
|
+
class Toggle extends Container {
|
|
2908
|
+
__uiComponent = true;
|
|
2909
|
+
_value;
|
|
2910
|
+
_onView = null;
|
|
2911
|
+
_offView = null;
|
|
2912
|
+
_handle = null;
|
|
2913
|
+
_trackGfx = null;
|
|
2914
|
+
_config;
|
|
2915
|
+
_useCustomViews;
|
|
2916
|
+
onChange = null;
|
|
2917
|
+
constructor(config = {}) {
|
|
2918
|
+
super();
|
|
2919
|
+
this._config = {
|
|
2920
|
+
width: config.width ?? 52,
|
|
2921
|
+
height: config.height ?? 28,
|
|
2922
|
+
onColor: config.onColor ?? 0x22cc22,
|
|
2923
|
+
offColor: config.offColor ?? 0x666666,
|
|
2924
|
+
handleColor: config.handleColor ?? 0xffffff,
|
|
2925
|
+
handleRadius: config.handleRadius ?? 0, // 0 = auto
|
|
2926
|
+
animationDuration: config.animationDuration ?? 200,
|
|
2927
|
+
};
|
|
2928
|
+
this._value = config.value ?? false;
|
|
2929
|
+
this.onChange = config.onChange ?? null;
|
|
2930
|
+
const customOn = resolveView(config.onView);
|
|
2931
|
+
const customOff = resolveView(config.offView);
|
|
2932
|
+
this._useCustomViews = !!(customOn || customOff);
|
|
2933
|
+
if (this._useCustomViews) {
|
|
2934
|
+
// Custom view mode: show/hide ON and OFF views
|
|
2935
|
+
if (customOn) {
|
|
2936
|
+
this._onView = customOn;
|
|
2937
|
+
this._onView.visible = this._value;
|
|
2938
|
+
this.addChild(this._onView);
|
|
2939
|
+
}
|
|
2940
|
+
if (customOff) {
|
|
2941
|
+
this._offView = customOff;
|
|
2942
|
+
this._offView.visible = !this._value;
|
|
2943
|
+
this.addChild(this._offView);
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
else {
|
|
2947
|
+
// Graphics mode: track + sliding handle
|
|
2948
|
+
const { width, height, handleColor } = this._config;
|
|
2949
|
+
const handleRadius = this._config.handleRadius || (height / 2 - 3);
|
|
2950
|
+
this._config.handleRadius = handleRadius;
|
|
2951
|
+
this._trackGfx = new Graphics();
|
|
2952
|
+
this.addChild(this._trackGfx);
|
|
2953
|
+
this._drawTrack();
|
|
2954
|
+
const handle = new Graphics();
|
|
2955
|
+
handle.circle(0, 0, handleRadius).fill(handleColor);
|
|
2956
|
+
handle.y = height / 2;
|
|
2957
|
+
handle.x = this._value ? width - handleRadius - 3 : handleRadius + 3;
|
|
2958
|
+
this._handle = handle;
|
|
2959
|
+
this.addChild(handle);
|
|
2960
|
+
}
|
|
2961
|
+
// Interaction
|
|
2962
|
+
this.eventMode = 'static';
|
|
2963
|
+
this.cursor = 'pointer';
|
|
2964
|
+
this.on('pointertap', this._onTap, this);
|
|
2965
|
+
}
|
|
2966
|
+
/** Current toggle state */
|
|
2967
|
+
get value() {
|
|
2968
|
+
return this._value;
|
|
2969
|
+
}
|
|
2970
|
+
set value(v) {
|
|
2971
|
+
if (v === this._value)
|
|
2972
|
+
return;
|
|
2973
|
+
this.forceSwitch(v);
|
|
2974
|
+
}
|
|
2975
|
+
/** Programmatically switch to a specific state with animation */
|
|
2976
|
+
forceSwitch(value) {
|
|
2977
|
+
this._value = value;
|
|
2978
|
+
this._animateToState();
|
|
2979
|
+
}
|
|
2980
|
+
/** React reconciler update hook */
|
|
2981
|
+
updateConfig(changed) {
|
|
2982
|
+
if ('value' in changed)
|
|
2983
|
+
this.value = changed.value;
|
|
2984
|
+
if ('onChange' in changed)
|
|
2985
|
+
this.onChange = changed.onChange;
|
|
2986
|
+
if ('animationDuration' in changed)
|
|
2987
|
+
this._config.animationDuration = changed.animationDuration;
|
|
2988
|
+
}
|
|
2989
|
+
_onTap() {
|
|
2990
|
+
this._value = !this._value;
|
|
2991
|
+
this._animateToState();
|
|
2992
|
+
this.onChange?.(this._value);
|
|
2993
|
+
}
|
|
2994
|
+
_animateToState() {
|
|
2995
|
+
const duration = this._config.animationDuration;
|
|
2996
|
+
if (this._useCustomViews) {
|
|
2997
|
+
// Custom views: crossfade
|
|
2998
|
+
if (this._onView) {
|
|
2999
|
+
Tween.killTweensOf(this._onView);
|
|
3000
|
+
if (this._value) {
|
|
3001
|
+
this._onView.visible = true;
|
|
3002
|
+
Tween.to(this._onView, { alpha: 1 }, duration);
|
|
3003
|
+
}
|
|
3004
|
+
else {
|
|
3005
|
+
Tween.to(this._onView, { alpha: 0 }, duration).then(() => {
|
|
3006
|
+
if (this._onView)
|
|
3007
|
+
this._onView.visible = false;
|
|
3008
|
+
});
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
if (this._offView) {
|
|
3012
|
+
Tween.killTweensOf(this._offView);
|
|
3013
|
+
if (!this._value) {
|
|
3014
|
+
this._offView.visible = true;
|
|
3015
|
+
Tween.to(this._offView, { alpha: 1 }, duration);
|
|
3016
|
+
}
|
|
3017
|
+
else {
|
|
3018
|
+
Tween.to(this._offView, { alpha: 0 }, duration).then(() => {
|
|
3019
|
+
if (this._offView)
|
|
3020
|
+
this._offView.visible = false;
|
|
3021
|
+
});
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
else {
|
|
3026
|
+
// Graphics mode: slide handle + recolor track
|
|
3027
|
+
this._drawTrack();
|
|
3028
|
+
if (this._handle) {
|
|
3029
|
+
const { width } = this._config;
|
|
3030
|
+
const handleRadius = this._config.handleRadius;
|
|
3031
|
+
const targetX = this._value ? width - handleRadius - 3 : handleRadius + 3;
|
|
3032
|
+
Tween.killTweensOf(this._handle);
|
|
3033
|
+
Tween.to(this._handle, { x: targetX }, duration);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
_drawTrack() {
|
|
3038
|
+
if (!this._trackGfx)
|
|
3039
|
+
return;
|
|
3040
|
+
const { width, height, onColor, offColor } = this._config;
|
|
3041
|
+
const radius = height / 2;
|
|
3042
|
+
this._trackGfx.clear();
|
|
3043
|
+
this._trackGfx.roundRect(0, 0, width, height, radius).fill(this._value ? onColor : offColor);
|
|
3044
|
+
}
|
|
3045
|
+
destroy(options) {
|
|
3046
|
+
this.off('pointertap', this._onTap, this);
|
|
3047
|
+
if (this._handle)
|
|
3048
|
+
Tween.killTweensOf(this._handle);
|
|
3049
|
+
if (this._onView)
|
|
3050
|
+
Tween.killTweensOf(this._onView);
|
|
3051
|
+
if (this._offView)
|
|
3052
|
+
Tween.killTweensOf(this._offView);
|
|
3053
|
+
this.onChange = null;
|
|
3054
|
+
super.destroy(options);
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
|
|
2607
3058
|
/**
|
|
2608
3059
|
* Register all standard PixiJS display objects for JSX use.
|
|
2609
3060
|
* Call once at app startup before rendering any React scenes.
|
|
@@ -2644,6 +3095,7 @@ function extendUIElements() {
|
|
|
2644
3095
|
extend({
|
|
2645
3096
|
Button, Label, Panel, FlexContainer, ProgressBar,
|
|
2646
3097
|
ScrollContainer, Modal, Toast, BalanceDisplay, WinDisplay, Layout,
|
|
3098
|
+
Slider, Toggle,
|
|
2647
3099
|
});
|
|
2648
3100
|
}
|
|
2649
3101
|
/**
|