canvasengine 2.0.0-beta.12 → 2.0.0-beta.14

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/dist/index.js CHANGED
@@ -347,8 +347,10 @@ var KeyboardControls = class extends Directive {
347
347
  };
348
348
  }
349
349
  onInit(element) {
350
+ const value = element.props.controls.value ?? element.props.controls;
351
+ if (!value) return;
350
352
  this.setupListeners();
351
- this.setInputs(element.props.controls.value);
353
+ this.setInputs(value);
352
354
  this.interval = setInterval(() => {
353
355
  this.preStep();
354
356
  }, fps2ms(this.serverFps ?? 60));
@@ -957,158 +959,11 @@ var Scheduler = class extends Directive {
957
959
  };
958
960
  registerDirective("tick", Scheduler);
959
961
 
960
- // src/directives/ViewportFollow.ts
961
- var ViewportFollow = class extends Directive {
962
- onInit(element) {
963
- }
964
- onMount(element) {
965
- const { viewportFollow } = element.props;
966
- const { viewport } = element.props.context;
967
- if (!viewport) {
968
- throw error("ViewportFollow directive requires a Viewport component to be mounted in the same context");
969
- }
970
- if (viewportFollow) viewport.follow(element.componentInstance);
971
- }
972
- onUpdate(props) {
973
- }
974
- onDestroy() {
975
- }
976
- };
977
- registerDirective("viewportFollow", ViewportFollow);
978
-
979
- // src/directives/Sound.ts
980
- import { effect } from "@signe/reactive";
981
- import { Howl } from "howler";
982
- var EVENTS = ["load", "loaderror", "playerror", "play", "end", "pause", "stop", "mute", "volume", "rate", "seek", "fade", "unlock"];
983
- var Sound = class extends Directive {
984
- constructor() {
985
- super(...arguments);
986
- this.eventsFn = [];
987
- this.maxVolume = 1;
988
- this.maxDistance = 100;
989
- }
990
- onInit(element) {
991
- }
992
- onMount(element) {
993
- const { props } = element;
994
- const tick2 = props.context.tick;
995
- const { src, autoplay, loop: loop2, volume, spatial } = props.sound;
996
- this.sound = new Howl({
997
- src,
998
- autoplay,
999
- loop: loop2,
1000
- volume
1001
- });
1002
- for (let event of EVENTS) {
1003
- if (!props.sound[event]) continue;
1004
- const fn = props.sound[event];
1005
- this.eventsFn.push(fn);
1006
- this.sound.on(event, fn);
1007
- }
1008
- if (spatial) {
1009
- const { soundListenerPosition } = props.context;
1010
- if (!soundListenerPosition) {
1011
- throw new error("SoundListenerPosition directive is required for spatial sound in component parent");
1012
- }
1013
- const { x: listenerX, y: listenerY } = soundListenerPosition;
1014
- this.tickSubscription = effect(() => {
1015
- tick2();
1016
- const { x, y } = element.componentInstance;
1017
- const distance = calculateDistance(x, y, listenerX(), listenerY());
1018
- const volume2 = Math.max(this.maxVolume - distance / this.maxDistance, 0);
1019
- this.sound.volume(volume2);
1020
- }).subscription;
1021
- }
1022
- }
1023
- onUpdate(props) {
1024
- const { volume, loop: loop2, mute, seek, playing, rate, spatial } = props;
1025
- if (volume != void 0) this.sound.volume(volume);
1026
- if (loop2 != void 0) this.sound.loop(loop2);
1027
- if (mute != void 0) this.sound.mute(mute);
1028
- if (seek != void 0) this.sound.seek(seek);
1029
- if (playing != void 0) {
1030
- if (playing) this.sound.play();
1031
- else this.sound.pause();
1032
- }
1033
- if (spatial) {
1034
- this.maxVolume = spatial.maxVolume ?? this.maxVolume;
1035
- this.maxDistance = spatial.maxDistance ?? this.maxDistance;
1036
- }
1037
- if (rate != void 0) this.sound.rate(rate);
1038
- }
1039
- onDestroy() {
1040
- this.sound.stop();
1041
- this.tickSubscription?.unsubscribe();
1042
- for (let event of EVENTS) {
1043
- if (this.eventsFn[event]) {
1044
- this.sound.off(event, this.eventsFn[event]);
1045
- }
1046
- }
1047
- }
1048
- };
1049
- var SoundListenerPosition = class extends Directive {
1050
- onMount(element) {
1051
- element.props.context.soundListenerPosition = element.propObservables?.soundListenerPosition;
1052
- }
1053
- onInit(element) {
1054
- }
1055
- onUpdate(props) {
1056
- }
1057
- onDestroy() {
1058
- }
1059
- };
1060
- registerDirective("sound", Sound);
1061
- registerDirective("soundListenerPosition", SoundListenerPosition);
1062
-
1063
- // src/directives/Drag.ts
1064
- import { effect as effect2, isSignal } from "@signe/reactive";
1065
- import { Rectangle } from "pixi.js";
1066
- import { snap } from "popmotion";
1067
-
1068
- // src/directives/Transition.ts
1069
- import { DisplacementFilter, Sprite, Texture, WRAP_MODES } from "pixi.js";
1070
- import { animate } from "popmotion";
1071
- var Transition = class extends Directive {
1072
- onInit(element) {
1073
- }
1074
- onMount(element) {
1075
- const { image } = element.props.transition;
1076
- const displacementSprite = new Sprite(Texture.from(image));
1077
- displacementSprite.texture.baseTexture.wrapMode = WRAP_MODES.REPEAT;
1078
- const displacementFilter = new DisplacementFilter(displacementSprite);
1079
- const instance = element.componentInstance;
1080
- instance.filters = [displacementFilter];
1081
- instance.addChild(displacementSprite);
1082
- setTimeout(() => {
1083
- animate({
1084
- from: 0,
1085
- to: 1,
1086
- duration: 500,
1087
- onUpdate: (progress) => {
1088
- displacementFilter.scale.x = progress;
1089
- displacementFilter.scale.y = progress;
1090
- }
1091
- });
1092
- }, 5e3);
1093
- }
1094
- onUpdate(props) {
1095
- }
1096
- onDestroy() {
1097
- }
1098
- };
1099
- registerDirective("transition", Transition);
1100
-
1101
- // src/index.ts
1102
- export * from "@signe/reactive";
1103
- import { Howler } from "howler";
1104
-
1105
- // src/components/Canvas.ts
1106
- import { effect as effect4, signal as signal3 } from "@signe/reactive";
1107
- import { Container as Container3, autoDetectRenderer } from "pixi.js";
1108
- import { loadYoga } from "yoga-layout";
962
+ // src/hooks/useProps.ts
963
+ import { isSignal as isSignal2, signal as signal2 } from "@signe/reactive";
1109
964
 
1110
965
  // src/engine/reactive.ts
1111
- import { isSignal as isSignal2 } from "@signe/reactive";
966
+ import { isComputed, isSignal, signal } from "@signe/reactive";
1112
967
  import {
1113
968
  Observable,
1114
969
  Subject,
@@ -1137,11 +992,11 @@ function destroyElement(element) {
1137
992
  }
1138
993
  element.propSubscriptions.forEach((sub) => sub.unsubscribe());
1139
994
  element.effectSubscriptions.forEach((sub) => sub.unsubscribe());
995
+ element.effectUnmounts.forEach((fn) => fn?.());
1140
996
  for (let name in element.directives) {
1141
- element.directives[name].onDestroy?.();
997
+ element.directives[name].onDestroy?.(element);
1142
998
  }
1143
999
  element.componentInstance.onDestroy?.(element.parent);
1144
- element.effectUnmounts.forEach((fn) => fn?.());
1145
1000
  }
1146
1001
  function createComponent(tag, props) {
1147
1002
  if (!components[tag]) {
@@ -1174,7 +1029,7 @@ function createComponent(tag, props) {
1174
1029
  set(element.props, path2 + "." + key, value);
1175
1030
  };
1176
1031
  Object.entries(props2).forEach(([key, value]) => {
1177
- if (isSignal2(value)) {
1032
+ if (isSignal(value)) {
1178
1033
  const _value = value;
1179
1034
  if ("dependencies" in _value && _value.dependencies.size == 0) {
1180
1035
  _set(path, key, _value());
@@ -1184,7 +1039,7 @@ function createComponent(tag, props) {
1184
1039
  _value.observable.subscribe((value2) => {
1185
1040
  _set(path, key, value2);
1186
1041
  if (element.directives[key]) {
1187
- element.directives[key].onUpdate?.(value2);
1042
+ element.directives[key].onUpdate?.(value2, element);
1188
1043
  }
1189
1044
  if (key == "tick") {
1190
1045
  return;
@@ -1235,7 +1090,7 @@ function createComponent(tag, props) {
1235
1090
  ;
1236
1091
  async function propagateContext(element2) {
1237
1092
  if (element2.props.attach) {
1238
- const isReactiveAttach = isSignal2(element2.propObservables?.attach);
1093
+ const isReactiveAttach = isSignal(element2.propObservables?.attach);
1239
1094
  if (!isReactiveAttach) {
1240
1095
  element2.props.children.push(element2.props.attach);
1241
1096
  } else {
@@ -1306,11 +1161,16 @@ function createComponent(tag, props) {
1306
1161
  return element;
1307
1162
  }
1308
1163
  function loop(itemsSubject, createElementFn) {
1164
+ if (isComputed(itemsSubject) && itemsSubject.dependencies.size == 0) {
1165
+ itemsSubject = signal(itemsSubject());
1166
+ } else if (!isSignal(itemsSubject)) {
1167
+ itemsSubject = signal(itemsSubject);
1168
+ }
1309
1169
  return defer(() => {
1310
1170
  let elements = [];
1311
1171
  let elementMap = /* @__PURE__ */ new Map();
1312
1172
  let isFirstSubscription = true;
1313
- const isArraySignal = (signal7) => Array.isArray(signal7());
1173
+ const isArraySignal = (signal9) => Array.isArray(signal9());
1314
1174
  return new Observable((subscriber) => {
1315
1175
  const subscription = isArraySignal(itemsSubject) ? itemsSubject.observable.subscribe((change) => {
1316
1176
  if (isFirstSubscription) {
@@ -1439,7 +1299,7 @@ function loop(itemsSubject, createElementFn) {
1439
1299
  }
1440
1300
  function cond(condition, createElementFn) {
1441
1301
  let element = null;
1442
- if (isSignal2(condition)) {
1302
+ if (isSignal(condition)) {
1443
1303
  const signalCondition = condition;
1444
1304
  return new Observable((subscriber) => {
1445
1305
  return signalCondition.observable.subscribe((bool) => {
@@ -1495,26 +1355,25 @@ function cond(condition, createElementFn) {
1495
1355
  }
1496
1356
 
1497
1357
  // src/hooks/useProps.ts
1498
- import { isSignal as isSignal3, signal } from "@signe/reactive";
1499
1358
  var useProps = (props, defaults = {}) => {
1500
- if (isSignal3(props)) {
1359
+ if (isSignal2(props)) {
1501
1360
  return props();
1502
1361
  }
1503
1362
  const obj = {};
1504
1363
  for (let key in props) {
1505
1364
  const value = props[key];
1506
- obj[key] = isPrimitive(value) ? signal(value) : value;
1365
+ obj[key] = isPrimitive(value) ? signal2(value) : value;
1507
1366
  }
1508
1367
  for (let key in defaults) {
1509
1368
  if (!(key in obj)) {
1510
- obj[key] = signal(defaults[key]);
1369
+ obj[key] = isPrimitive(defaults[key]) ? signal2(defaults[key]) : defaults[key];
1511
1370
  }
1512
1371
  }
1513
1372
  return obj;
1514
1373
  };
1515
1374
  var useDefineProps = (props) => {
1516
1375
  return (schema) => {
1517
- const rawProps = isSignal3(props) ? props() : props;
1376
+ const rawProps = isSignal2(props) ? props() : props;
1518
1377
  const validatedProps = {};
1519
1378
  for (const key in schema) {
1520
1379
  const propConfig = schema[key];
@@ -1543,7 +1402,7 @@ var useDefineProps = (props) => {
1543
1402
  validatedValue = value;
1544
1403
  }
1545
1404
  }
1546
- validatedProps[key] = isSignal3(validatedValue) ? validatedValue : signal(validatedValue);
1405
+ validatedProps[key] = isSignal2(validatedValue) ? validatedValue : signal2(validatedValue);
1547
1406
  }
1548
1407
  return {
1549
1408
  ...useProps(rawProps),
@@ -1553,7 +1412,7 @@ var useDefineProps = (props) => {
1553
1412
  };
1554
1413
  function validateType(key, value, types) {
1555
1414
  if (value === void 0 || value === null) return;
1556
- const valueToCheck = isSignal3(value) ? value() : value;
1415
+ const valueToCheck = isSignal2(value) ? value() : value;
1557
1416
  const valid = types.some((type) => {
1558
1417
  if (type === Number) return typeof valueToCheck === "number";
1559
1418
  if (type === String) return typeof valueToCheck === "string";
@@ -1571,8 +1430,420 @@ function validateType(key, value, types) {
1571
1430
  }
1572
1431
  }
1573
1432
 
1433
+ // src/directives/ViewportFollow.ts
1434
+ var ViewportFollow = class extends Directive {
1435
+ onInit(element) {
1436
+ }
1437
+ onMount(element) {
1438
+ this.onUpdate(element.props.viewportFollow, element);
1439
+ }
1440
+ onUpdate(viewportFollow, element) {
1441
+ const { viewport } = element.props.context;
1442
+ if (!viewport) {
1443
+ throw error("ViewportFollow directive requires a Viewport component to be mounted in the same context");
1444
+ }
1445
+ if (viewportFollow) {
1446
+ if (viewportFollow === true) {
1447
+ viewport.follow(element.componentInstance);
1448
+ } else {
1449
+ const options = useProps(viewportFollow, {
1450
+ speed: void 0,
1451
+ acceleration: void 0,
1452
+ radius: void 0
1453
+ });
1454
+ viewport.follow(element.componentInstance, {
1455
+ speed: options.speed(),
1456
+ acceleration: options.acceleration(),
1457
+ radius: options.radius()
1458
+ });
1459
+ }
1460
+ } else {
1461
+ viewport.plugins.remove("follow");
1462
+ }
1463
+ }
1464
+ onDestroy(element) {
1465
+ const { viewportFollow } = element.props;
1466
+ const { viewport } = element.props.context;
1467
+ if (viewportFollow) viewport.plugins.remove("follow");
1468
+ }
1469
+ };
1470
+ registerDirective("viewportFollow", ViewportFollow);
1471
+
1472
+ // src/directives/Sound.ts
1473
+ import { effect } from "@signe/reactive";
1474
+ import { Howl } from "howler";
1475
+ var EVENTS = ["load", "loaderror", "playerror", "play", "end", "pause", "stop", "mute", "volume", "rate", "seek", "fade", "unlock"];
1476
+ var Sound = class extends Directive {
1477
+ constructor() {
1478
+ super(...arguments);
1479
+ this.eventsFn = [];
1480
+ this.maxVolume = 1;
1481
+ this.maxDistance = 100;
1482
+ }
1483
+ onInit(element) {
1484
+ }
1485
+ onMount(element) {
1486
+ const { props } = element;
1487
+ const tick2 = props.context.tick;
1488
+ const { src, autoplay, loop: loop2, volume, spatial } = props.sound;
1489
+ this.sound = new Howl({
1490
+ src,
1491
+ autoplay,
1492
+ loop: loop2,
1493
+ volume
1494
+ });
1495
+ for (let event of EVENTS) {
1496
+ if (!props.sound[event]) continue;
1497
+ const fn = props.sound[event];
1498
+ this.eventsFn.push(fn);
1499
+ this.sound.on(event, fn);
1500
+ }
1501
+ if (spatial) {
1502
+ const { soundListenerPosition } = props.context;
1503
+ if (!soundListenerPosition) {
1504
+ throw new error("SoundListenerPosition directive is required for spatial sound in component parent");
1505
+ }
1506
+ const { x: listenerX, y: listenerY } = soundListenerPosition;
1507
+ this.tickSubscription = effect(() => {
1508
+ tick2();
1509
+ const { x, y } = element.componentInstance;
1510
+ const distance = calculateDistance(x, y, listenerX(), listenerY());
1511
+ const volume2 = Math.max(this.maxVolume - distance / this.maxDistance, 0);
1512
+ this.sound.volume(volume2);
1513
+ }).subscription;
1514
+ }
1515
+ }
1516
+ onUpdate(props) {
1517
+ const { volume, loop: loop2, mute, seek, playing, rate, spatial } = props;
1518
+ if (volume != void 0) this.sound.volume(volume);
1519
+ if (loop2 != void 0) this.sound.loop(loop2);
1520
+ if (mute != void 0) this.sound.mute(mute);
1521
+ if (seek != void 0) this.sound.seek(seek);
1522
+ if (playing != void 0) {
1523
+ if (playing) this.sound.play();
1524
+ else this.sound.pause();
1525
+ }
1526
+ if (spatial) {
1527
+ this.maxVolume = spatial.maxVolume ?? this.maxVolume;
1528
+ this.maxDistance = spatial.maxDistance ?? this.maxDistance;
1529
+ }
1530
+ if (rate != void 0) this.sound.rate(rate);
1531
+ }
1532
+ onDestroy() {
1533
+ this.sound.stop();
1534
+ this.tickSubscription?.unsubscribe();
1535
+ for (let event of EVENTS) {
1536
+ if (this.eventsFn[event]) {
1537
+ this.sound.off(event, this.eventsFn[event]);
1538
+ }
1539
+ }
1540
+ }
1541
+ };
1542
+ var SoundListenerPosition = class extends Directive {
1543
+ onMount(element) {
1544
+ element.props.context.soundListenerPosition = element.propObservables?.soundListenerPosition;
1545
+ }
1546
+ onInit(element) {
1547
+ }
1548
+ onUpdate(props) {
1549
+ }
1550
+ onDestroy() {
1551
+ }
1552
+ };
1553
+ registerDirective("sound", Sound);
1554
+ registerDirective("soundListenerPosition", SoundListenerPosition);
1555
+
1556
+ // src/directives/Drag.ts
1557
+ import { effect as effect2, isComputed as isComputed2, isSignal as isSignal3 } from "@signe/reactive";
1558
+ import { Rectangle, Point } from "pixi.js";
1559
+ import { snap } from "popmotion";
1560
+
1561
+ // src/hooks/addContext.ts
1562
+ var addContext = (element, key, value) => {
1563
+ element.props.context = {
1564
+ ...element.props.context ?? {},
1565
+ [key]: value
1566
+ };
1567
+ };
1568
+
1569
+ // src/directives/Drag.ts
1570
+ var Drop = class extends Directive {
1571
+ constructor() {
1572
+ super(...arguments);
1573
+ this.elementRef = null;
1574
+ }
1575
+ onInit(element) {
1576
+ this.elementRef = element;
1577
+ }
1578
+ onMount(element) {
1579
+ addContext(element, "drop", element);
1580
+ }
1581
+ onUpdate() {
1582
+ }
1583
+ onDestroy() {
1584
+ this.elementRef = null;
1585
+ }
1586
+ };
1587
+ var Drag = class extends Directive {
1588
+ constructor() {
1589
+ super(...arguments);
1590
+ this.elementRef = null;
1591
+ this.stageRef = null;
1592
+ this.offsetInParent = new Point();
1593
+ this.isDragging = false;
1594
+ this.viewport = null;
1595
+ this.animationFrameId = null;
1596
+ this.lastPointerPosition = new Point();
1597
+ this.onDragMoveHandler = () => {
1598
+ };
1599
+ this.onDragEndHandler = () => {
1600
+ };
1601
+ this.onDragStartHandler = () => {
1602
+ };
1603
+ this.subscriptions = [];
1604
+ }
1605
+ onInit(element) {
1606
+ this.elementRef = element;
1607
+ this.onDragMoveHandler = this.onDragMove.bind(this);
1608
+ this.onDragEndHandler = this.onDragEnd.bind(this);
1609
+ this.onDragStartHandler = this.onPointerDown.bind(this);
1610
+ }
1611
+ onMount(element) {
1612
+ const { rootElement, canvasSize, viewport, tick: tick2 } = element.props.context;
1613
+ const instance = element.componentInstance;
1614
+ const dragProps = this.dragProps;
1615
+ const haveNotProps = Object.keys(dragProps).length === 0;
1616
+ if (haveNotProps) {
1617
+ this.onDestroy();
1618
+ return;
1619
+ }
1620
+ if (!instance) return;
1621
+ this.stageRef = rootElement.componentInstance;
1622
+ if (!this.stageRef) return;
1623
+ this.viewport = viewport;
1624
+ instance.eventMode = "static";
1625
+ this.stageRef.eventMode = "static";
1626
+ const _effect = effect2(() => {
1627
+ if (this.stageRef) {
1628
+ this.stageRef.hitArea = new Rectangle(0, 0, canvasSize().width, canvasSize().height);
1629
+ }
1630
+ });
1631
+ instance.on("pointerdown", this.onDragStartHandler);
1632
+ this.stageRef.on("pointerup", this.onDragEndHandler);
1633
+ this.stageRef.on("pointerupoutside", this.onDragEndHandler);
1634
+ this.subscriptions = [
1635
+ tick2.observable.subscribe(() => {
1636
+ if (this.isDragging && this.viewport) {
1637
+ this.updateViewportPosition(this.lastPointerPosition);
1638
+ }
1639
+ }),
1640
+ _effect.subscription
1641
+ ];
1642
+ }
1643
+ get dragProps() {
1644
+ const drag = this.elementRef?.props.drag;
1645
+ const options = useProps(drag?.value ?? drag, {
1646
+ snap: 0,
1647
+ viewport: {},
1648
+ direction: "all"
1649
+ });
1650
+ options.viewport = useProps(options.viewport, {
1651
+ edgeThreshold: 300,
1652
+ maxSpeed: 40
1653
+ });
1654
+ return options;
1655
+ }
1656
+ get axis() {
1657
+ const direction = this.dragProps.direction();
1658
+ const axis = {
1659
+ x: true,
1660
+ y: true
1661
+ };
1662
+ if (direction === "x") {
1663
+ axis.y = false;
1664
+ }
1665
+ if (direction === "y") {
1666
+ axis.x = false;
1667
+ }
1668
+ return axis;
1669
+ }
1670
+ /**
1671
+ * Updates element position when dragging and starts continuous viewport movement
1672
+ * @param event The pointer event that triggered the drag move
1673
+ */
1674
+ onDragMove(event) {
1675
+ if (!this.isDragging || !this.elementRef?.componentInstance || !this.elementRef.componentInstance.parent) return;
1676
+ const instance = this.elementRef.componentInstance;
1677
+ const parent = instance.parent;
1678
+ const dragProps = this.dragProps;
1679
+ const propObservables = this.elementRef.propObservables;
1680
+ const snapTo = snap(dragProps?.snap() ?? 0);
1681
+ dragProps?.move?.(event);
1682
+ const currentParentLocalPointer = parent.toLocal(event.global);
1683
+ const newX = currentParentLocalPointer.x - this.offsetInParent.x;
1684
+ const newY = currentParentLocalPointer.y - this.offsetInParent.y;
1685
+ if (dragProps?.snap()) {
1686
+ instance.position.x = snapTo(newX);
1687
+ instance.position.y = snapTo(newY);
1688
+ } else {
1689
+ if (this.axis.x) instance.position.x = newX;
1690
+ if (this.axis.y) instance.position.y = newY;
1691
+ }
1692
+ this.lastPointerPosition.copyFrom(event.global);
1693
+ const { x: xProp, y: yProp } = propObservables;
1694
+ const updatePosition = (prop, value) => {
1695
+ if (isComputed2(prop)) {
1696
+ prop.dependencies.forEach((dependency) => {
1697
+ dependency.set(value);
1698
+ });
1699
+ } else if (isSignal3(prop)) {
1700
+ prop.set(value);
1701
+ }
1702
+ };
1703
+ if (xProp !== void 0) updatePosition(xProp, instance.position.x);
1704
+ if (yProp !== void 0) updatePosition(yProp, instance.position.y);
1705
+ }
1706
+ /**
1707
+ * Moves the viewport if the dragged element is near screen edges
1708
+ * @param globalPosition The global pointer position
1709
+ */
1710
+ updateViewportPosition(globalPosition) {
1711
+ if (!this.viewport || !this.elementRef) return;
1712
+ const dragProps = this.dragProps;
1713
+ const edgeThreshold = dragProps?.viewport?.edgeThreshold();
1714
+ const maxSpeed = dragProps?.viewport?.maxSpeed();
1715
+ const screenLeft = 0;
1716
+ const screenRight = this.viewport.screenWidth;
1717
+ const screenTop = 0;
1718
+ const screenBottom = this.viewport.screenHeight;
1719
+ const instance = this.elementRef.componentInstance;
1720
+ const distanceFromLeft = globalPosition.x - screenLeft;
1721
+ const distanceFromRight = screenRight - globalPosition.x;
1722
+ const distanceFromTop = globalPosition.y - screenTop;
1723
+ const distanceFromBottom = screenBottom - globalPosition.y;
1724
+ let moveX = 0;
1725
+ let moveY = 0;
1726
+ if (distanceFromLeft < edgeThreshold) {
1727
+ const velocity = maxSpeed * (1 - distanceFromLeft / edgeThreshold);
1728
+ moveX = -velocity;
1729
+ } else if (distanceFromRight < edgeThreshold) {
1730
+ const velocity = maxSpeed * (1 - distanceFromRight / edgeThreshold);
1731
+ moveX = velocity;
1732
+ }
1733
+ if (distanceFromTop < edgeThreshold) {
1734
+ const velocity = maxSpeed * (1 - distanceFromTop / edgeThreshold);
1735
+ moveY = -velocity;
1736
+ } else if (distanceFromBottom < edgeThreshold) {
1737
+ const velocity = maxSpeed * (1 - distanceFromBottom / edgeThreshold);
1738
+ moveY = velocity;
1739
+ }
1740
+ if (moveX !== 0 || moveY !== 0) {
1741
+ const lastViewValue = this.viewport.center;
1742
+ this.viewport.moveCenter(
1743
+ this.viewport.center.x + moveX,
1744
+ this.viewport.center.y + moveY
1745
+ );
1746
+ if (this.axis.x && lastViewValue.x !== this.viewport.center.x) {
1747
+ instance.position.x += moveX;
1748
+ }
1749
+ if (this.axis.y && lastViewValue.y !== this.viewport.center.y) {
1750
+ instance.position.y += moveY;
1751
+ }
1752
+ }
1753
+ }
1754
+ /**
1755
+ * Handles drag end event and stops viewport movement
1756
+ */
1757
+ onDragEnd() {
1758
+ if (!this.isDragging || !this.elementRef) return;
1759
+ const dragProps = this.dragProps;
1760
+ this.isDragging = false;
1761
+ dragProps?.end?.();
1762
+ if (this.stageRef) {
1763
+ this.stageRef.off("pointermove", this.onDragMoveHandler);
1764
+ }
1765
+ }
1766
+ onPointerDown(event) {
1767
+ if (!this.elementRef?.componentInstance || !this.stageRef || !this.elementRef.componentInstance.parent) return;
1768
+ const instance = this.elementRef.componentInstance;
1769
+ const parent = instance.parent;
1770
+ const dragProps = this.dragProps;
1771
+ const parentLocalPointer = parent.toLocal(event.global);
1772
+ this.offsetInParent.x = parentLocalPointer.x - instance.position.x;
1773
+ this.offsetInParent.y = parentLocalPointer.y - instance.position.y;
1774
+ this.isDragging = true;
1775
+ this.lastPointerPosition.copyFrom(event.global);
1776
+ dragProps?.start?.();
1777
+ this.stageRef.on("pointermove", this.onDragMoveHandler);
1778
+ }
1779
+ onUpdate(props) {
1780
+ if (props.type && props.type === "reset") {
1781
+ this.onDestroy();
1782
+ this.onMount(this.elementRef);
1783
+ }
1784
+ }
1785
+ onDestroy() {
1786
+ this.subscriptions.forEach((subscription) => subscription.unsubscribe());
1787
+ const instance = this.elementRef?.componentInstance;
1788
+ if (instance) {
1789
+ instance.off("pointerdown", this.onDragStartHandler);
1790
+ }
1791
+ if (this.stageRef) {
1792
+ this.stageRef.off("pointermove", this.onDragMoveHandler);
1793
+ this.stageRef.off("pointerup", this.onDragEndHandler);
1794
+ this.stageRef.off("pointerupoutside", this.onDragEndHandler);
1795
+ }
1796
+ this.stageRef = null;
1797
+ this.viewport = null;
1798
+ }
1799
+ };
1800
+ registerDirective("drag", Drag);
1801
+ registerDirective("drop", Drop);
1802
+
1803
+ // src/directives/Transition.ts
1804
+ import { DisplacementFilter, Sprite, Texture, WRAP_MODES } from "pixi.js";
1805
+ import { animate } from "popmotion";
1806
+ var Transition = class extends Directive {
1807
+ onInit(element) {
1808
+ }
1809
+ onMount(element) {
1810
+ const { image } = element.props.transition;
1811
+ const displacementSprite = new Sprite(Texture.from(image));
1812
+ displacementSprite.texture.baseTexture.wrapMode = WRAP_MODES.REPEAT;
1813
+ const displacementFilter = new DisplacementFilter(displacementSprite);
1814
+ const instance = element.componentInstance;
1815
+ instance.filters = [displacementFilter];
1816
+ instance.addChild(displacementSprite);
1817
+ setTimeout(() => {
1818
+ animate({
1819
+ from: 0,
1820
+ to: 1,
1821
+ duration: 500,
1822
+ onUpdate: (progress) => {
1823
+ displacementFilter.scale.x = progress;
1824
+ displacementFilter.scale.y = progress;
1825
+ }
1826
+ });
1827
+ }, 5e3);
1828
+ }
1829
+ onUpdate(props) {
1830
+ }
1831
+ onDestroy() {
1832
+ }
1833
+ };
1834
+ registerDirective("transition", Transition);
1835
+
1836
+ // src/index.ts
1837
+ export * from "@signe/reactive";
1838
+ import { Howler } from "howler";
1839
+
1840
+ // src/components/Canvas.ts
1841
+ import { effect as effect4, signal as signal5 } from "@signe/reactive";
1842
+ import { Container as Container3, autoDetectRenderer } from "pixi.js";
1843
+ import { loadYoga } from "yoga-layout";
1844
+
1574
1845
  // src/components/DisplayObject.ts
1575
- import { effect as effect3, signal as signal2 } from "@signe/reactive";
1846
+ import { effect as effect3, signal as signal4 } from "@signe/reactive";
1576
1847
  import { DropShadowFilter } from "pixi-filters";
1577
1848
  import { BlurFilter, ObservablePoint } from "pixi.js";
1578
1849
  var EVENTS2 = [
@@ -1662,8 +1933,8 @@ function DisplayObject(extendClass) {
1662
1933
  0
1663
1934
  );
1664
1935
  this.isCustomAnchor = false;
1665
- this.displayWidth = signal2(0);
1666
- this.displayHeight = signal2(0);
1936
+ this.displayWidth = signal4(0);
1937
+ this.displayHeight = signal4(0);
1667
1938
  this.overrideProps = [];
1668
1939
  }
1669
1940
  get yoga() {
@@ -1959,14 +2230,14 @@ registerComponent("Canvas", class Canvas extends DisplayObject(Container3) {
1959
2230
  var Canvas2 = async (props = {}) => {
1960
2231
  let { cursorStyles, width, height, class: className } = useProps(props);
1961
2232
  const Yoga = await loadYoga();
1962
- if (!props.width) width = signal3(800);
1963
- if (!props.height) height = signal3(600);
2233
+ if (!props.width) width = signal5(800);
2234
+ if (!props.height) height = signal5(600);
1964
2235
  const renderer = await autoDetectRenderer({
1965
2236
  ...props,
1966
2237
  width: width?.(),
1967
2238
  height: height?.()
1968
2239
  });
1969
- const canvasSize = signal3({
2240
+ const canvasSize = signal5({
1970
2241
  width: renderer.width,
1971
2242
  height: renderer.height
1972
2243
  });
@@ -1982,7 +2253,7 @@ var Canvas2 = async (props = {}) => {
1982
2253
  height: height?.()
1983
2254
  };
1984
2255
  if (!props.tick) {
1985
- options.context.tick = options.tick = signal3({
2256
+ options.context.tick = options.tick = signal5({
1986
2257
  timestamp: 0,
1987
2258
  deltaTime: 0,
1988
2259
  frame: 0,
@@ -2079,12 +2350,16 @@ var CanvasGraphics = class extends DisplayObject(PixiGraphics) {
2079
2350
  onInit(props) {
2080
2351
  super.onInit(props);
2081
2352
  if (props.draw) {
2082
- effect5(() => {
2353
+ this.clearEffect = effect5(() => {
2083
2354
  this.clear();
2084
2355
  props.draw?.(this);
2085
2356
  });
2086
2357
  }
2087
2358
  }
2359
+ onDestroy() {
2360
+ this.clearEffect.subscription.unsubscribe();
2361
+ super.onDestroy();
2362
+ }
2088
2363
  };
2089
2364
  registerComponent("Graphics", CanvasGraphics);
2090
2365
  function Graphics(props) {
@@ -2268,10 +2543,10 @@ import {
2268
2543
  } from "pixi.js";
2269
2544
 
2270
2545
  // src/engine/animation.ts
2271
- import { effect as effect6, signal as signal4 } from "@signe/reactive";
2546
+ import { effect as effect6, signal as signal6 } from "@signe/reactive";
2272
2547
  import { animate as animatePopmotion } from "popmotion";
2273
- function isAnimatedSignal(signal7) {
2274
- return signal7.animatedState !== void 0;
2548
+ function isAnimatedSignal(signal9) {
2549
+ return signal9.animatedState !== void 0;
2275
2550
  }
2276
2551
  function animatedSignal(initialValue, options = {}) {
2277
2552
  const state = {
@@ -2280,8 +2555,8 @@ function animatedSignal(initialValue, options = {}) {
2280
2555
  end: initialValue
2281
2556
  };
2282
2557
  let animation;
2283
- const publicSignal = signal4(initialValue);
2284
- const privateSignal = signal4(state);
2558
+ const publicSignal = signal6(initialValue);
2559
+ const privateSignal = signal6(state);
2285
2560
  effect6(() => {
2286
2561
  const currentState = privateSignal();
2287
2562
  publicSignal.set(currentState.current);
@@ -2663,7 +2938,7 @@ var Sprite2 = (props) => {
2663
2938
  };
2664
2939
 
2665
2940
  // src/components/Video.ts
2666
- import { effect as effect8, signal as signal5 } from "@signe/reactive";
2941
+ import { effect as effect8, signal as signal7 } from "@signe/reactive";
2667
2942
  function Video(props) {
2668
2943
  const eventsMap = {
2669
2944
  audioprocess: null,
@@ -2688,7 +2963,7 @@ function Video(props) {
2688
2963
  volumechange: null,
2689
2964
  waiting: null
2690
2965
  };
2691
- const video = signal5(null);
2966
+ const video = signal7(null);
2692
2967
  const defineProps = useDefineProps(props);
2693
2968
  const { play, loop: loop2, muted } = defineProps({
2694
2969
  play: {
@@ -2758,12 +3033,12 @@ function Video(props) {
2758
3033
  import { Text as PixiText } from "pixi.js";
2759
3034
 
2760
3035
  // src/engine/trigger.ts
2761
- import { effect as effect9, signal as signal6 } from "@signe/reactive";
3036
+ import { effect as effect9, signal as signal8 } from "@signe/reactive";
2762
3037
  function isTrigger(arg) {
2763
3038
  return arg?.start && arg?.listen;
2764
3039
  }
2765
3040
  function trigger(globalConfig) {
2766
- const _signal = signal6({
3041
+ const _signal = signal8({
2767
3042
  config: globalConfig,
2768
3043
  value: 0,
2769
3044
  resolve: (value) => void 0
@@ -2981,10 +3256,7 @@ var CanvasViewport = class extends DisplayObject(PixiViewport) {
2981
3256
  onInit(props) {
2982
3257
  super.onInit(props);
2983
3258
  for (let event of EVENTS3) {
2984
- const camelCaseEvent = event.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
2985
- if (props[camelCaseEvent]) {
2986
- this.on(event, props[camelCaseEvent]);
2987
- }
3259
+ if (props[event]) this.on(event, props[event]);
2988
3260
  }
2989
3261
  }
2990
3262
  onMount(element) {
@@ -3023,6 +3295,9 @@ var CanvasViewport = class extends DisplayObject(PixiViewport) {
3023
3295
  if (props.worldHeight !== void 0) {
3024
3296
  this.worldHeight = props.worldHeight;
3025
3297
  }
3298
+ if (props.drag) {
3299
+ this.drag(props.drag);
3300
+ }
3026
3301
  if (props.clamp) {
3027
3302
  this.clamp(props.clamp.value ?? props.clamp);
3028
3303
  }