@almadar/ui 5.57.0 → 5.59.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.
@@ -29941,13 +29941,13 @@ var init_MapView = __esm({
29941
29941
  shadowSize: [41, 41]
29942
29942
  });
29943
29943
  L.Marker.prototype.options.icon = defaultIcon;
29944
- const { useEffect: useEffect80, useRef: useRef78, useCallback: useCallback120, useState: useState110 } = React76__namespace.default;
29944
+ const { useEffect: useEffect81, useRef: useRef78, useCallback: useCallback120, useState: useState110 } = React76__namespace.default;
29945
29945
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29946
29946
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29947
29947
  function MapUpdater({ centerLat, centerLng, zoom }) {
29948
29948
  const map = useMap();
29949
29949
  const prevRef = useRef78({ centerLat, centerLng, zoom });
29950
- useEffect80(() => {
29950
+ useEffect81(() => {
29951
29951
  const prev = prevRef.current;
29952
29952
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29953
29953
  map.setView([centerLat, centerLng], zoom);
@@ -29958,7 +29958,7 @@ var init_MapView = __esm({
29958
29958
  }
29959
29959
  function MapClickHandler({ onMapClick }) {
29960
29960
  const map = useMap();
29961
- useEffect80(() => {
29961
+ useEffect81(() => {
29962
29962
  if (!onMapClick) return;
29963
29963
  const handler = (e) => {
29964
29964
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -38419,6 +38419,9 @@ var init_GraphCanvas = __esm({
38419
38419
  actions,
38420
38420
  onNodeClick,
38421
38421
  nodeClickEvent,
38422
+ selectedNodeId,
38423
+ repulsion = 800,
38424
+ linkDistance = 100,
38422
38425
  layout = "force",
38423
38426
  entity,
38424
38427
  isLoading = false,
@@ -38434,6 +38437,35 @@ var init_GraphCanvas = __esm({
38434
38437
  const [hoveredNode, setHoveredNode] = React76.useState(null);
38435
38438
  const nodesRef = React76.useRef([]);
38436
38439
  const [, forceUpdate] = React76.useState(0);
38440
+ const interactionRef = React76.useRef({
38441
+ mode: "none",
38442
+ dragNodeId: null,
38443
+ startMouse: { x: 0, y: 0 },
38444
+ startOffset: { x: 0, y: 0 },
38445
+ downPos: { x: 0, y: 0 }
38446
+ });
38447
+ const toCoords = React76.useCallback(
38448
+ (e) => {
38449
+ const canvas = canvasRef.current;
38450
+ if (!canvas) return null;
38451
+ const rect = canvas.getBoundingClientRect();
38452
+ const screenX = e.clientX - rect.left;
38453
+ const screenY = e.clientY - rect.top;
38454
+ return {
38455
+ screenX,
38456
+ screenY,
38457
+ graphX: (screenX - offset.x) / zoom,
38458
+ graphY: (screenY - offset.y) / zoom
38459
+ };
38460
+ },
38461
+ [offset, zoom]
38462
+ );
38463
+ const nodeAt = React76.useCallback((graphX, graphY) => {
38464
+ return nodesRef.current.find((n) => {
38465
+ const dist = Math.sqrt((n.x - graphX) ** 2 + (n.y - graphY) ** 2);
38466
+ return dist < (n.size || 8) + 4;
38467
+ });
38468
+ }, []);
38437
38469
  const handleAction = React76.useCallback(
38438
38470
  (action) => {
38439
38471
  if (action.event) {
@@ -38499,7 +38531,7 @@ var init_GraphCanvas = __esm({
38499
38531
  const dx = nodes[j].x - nodes[i].x;
38500
38532
  const dy = nodes[j].y - nodes[i].y;
38501
38533
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38502
- const force = 800 / (dist * dist);
38534
+ const force = repulsion / (dist * dist);
38503
38535
  const fx = dx / dist * force;
38504
38536
  const fy = dy / dist * force;
38505
38537
  nodes[i].fx -= fx;
@@ -38515,7 +38547,7 @@ var init_GraphCanvas = __esm({
38515
38547
  const dx = target.x - source.x;
38516
38548
  const dy = target.y - source.y;
38517
38549
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38518
- const force = (dist - 100) * 0.05;
38550
+ const force = (dist - linkDistance) * 0.05;
38519
38551
  const fx = dx / dist * force;
38520
38552
  const fy = dy / dist * force;
38521
38553
  source.fx += fx;
@@ -38549,7 +38581,7 @@ var init_GraphCanvas = __esm({
38549
38581
  return () => {
38550
38582
  cancelAnimationFrame(animRef.current);
38551
38583
  };
38552
- }, [propNodes, propEdges, layout]);
38584
+ }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38553
38585
  React76.useEffect(() => {
38554
38586
  const canvas = canvasRef.current;
38555
38587
  if (!canvas) return;
@@ -38585,18 +38617,25 @@ var init_GraphCanvas = __esm({
38585
38617
  const size = node.size || 8;
38586
38618
  const color = node.color || getGroupColor(node.group, groups);
38587
38619
  const isHovered = hoveredNode === node.id;
38620
+ const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38621
+ const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38588
38622
  ctx.beginPath();
38589
- ctx.arc(node.x, node.y, isHovered ? size + 2 : size, 0, Math.PI * 2);
38623
+ ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
38590
38624
  ctx.fillStyle = color;
38591
38625
  ctx.fill();
38592
- ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
38593
- ctx.lineWidth = isHovered ? 2 : 1;
38626
+ if (isSelected) {
38627
+ ctx.strokeStyle = "var(--color-accent)";
38628
+ ctx.lineWidth = 3;
38629
+ } else {
38630
+ ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
38631
+ ctx.lineWidth = isHovered ? 2 : 1;
38632
+ }
38594
38633
  ctx.stroke();
38595
38634
  if (showLabels && node.label) {
38596
38635
  ctx.fillStyle = "#666666";
38597
- ctx.font = `${isHovered ? "bold " : ""}10px system-ui`;
38636
+ ctx.font = `${isSelected || isHovered ? "bold " : ""}10px system-ui`;
38598
38637
  ctx.textAlign = "center";
38599
- ctx.fillText(node.label, node.x, node.y + size + 12);
38638
+ ctx.fillText(node.label, node.x, node.y + radius + 12);
38600
38639
  }
38601
38640
  }
38602
38641
  ctx.restore();
@@ -38607,6 +38646,97 @@ var init_GraphCanvas = __esm({
38607
38646
  setZoom(1);
38608
38647
  setOffset({ x: 0, y: 0 });
38609
38648
  }, []);
38649
+ const handleWheel = React76.useCallback(
38650
+ (e) => {
38651
+ if (!interactive) return;
38652
+ e.preventDefault();
38653
+ const coords = toCoords(e);
38654
+ if (!coords) return;
38655
+ const oldZoom = zoom;
38656
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
38657
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
38658
+ if (newZoom === oldZoom) return;
38659
+ setOffset((o) => ({
38660
+ x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
38661
+ y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
38662
+ }));
38663
+ setZoom(newZoom);
38664
+ },
38665
+ [interactive, toCoords, zoom]
38666
+ );
38667
+ const handleMouseDown = React76.useCallback(
38668
+ (e) => {
38669
+ const coords = toCoords(e);
38670
+ if (!coords) return;
38671
+ const node = nodeAt(coords.graphX, coords.graphY);
38672
+ const state = interactionRef.current;
38673
+ state.downPos = { x: e.clientX, y: e.clientY };
38674
+ state.startMouse = { x: e.clientX, y: e.clientY };
38675
+ state.startOffset = { ...offset };
38676
+ if (draggable && node) {
38677
+ state.mode = "dragging";
38678
+ state.dragNodeId = node.id;
38679
+ } else if (interactive) {
38680
+ state.mode = "panning";
38681
+ state.dragNodeId = null;
38682
+ } else {
38683
+ state.mode = "none";
38684
+ state.dragNodeId = null;
38685
+ }
38686
+ },
38687
+ [toCoords, nodeAt, draggable, interactive, offset]
38688
+ );
38689
+ const handleMouseMove = React76.useCallback(
38690
+ (e) => {
38691
+ const state = interactionRef.current;
38692
+ if (state.mode === "panning") {
38693
+ const dx = e.clientX - state.startMouse.x;
38694
+ const dy = e.clientY - state.startMouse.y;
38695
+ setOffset({ x: state.startOffset.x + dx, y: state.startOffset.y + dy });
38696
+ return;
38697
+ }
38698
+ if (state.mode === "dragging" && state.dragNodeId) {
38699
+ const coords2 = toCoords(e);
38700
+ if (!coords2) return;
38701
+ const node2 = nodesRef.current.find((n) => n.id === state.dragNodeId);
38702
+ if (node2) {
38703
+ node2.x = coords2.graphX;
38704
+ node2.y = coords2.graphY;
38705
+ node2.vx = 0;
38706
+ node2.vy = 0;
38707
+ forceUpdate((n) => n + 1);
38708
+ }
38709
+ return;
38710
+ }
38711
+ const coords = toCoords(e);
38712
+ if (!coords) return;
38713
+ const node = nodeAt(coords.graphX, coords.graphY);
38714
+ setHoveredNode(node?.id ?? null);
38715
+ },
38716
+ [toCoords, nodeAt]
38717
+ );
38718
+ const handleMouseUp = React76.useCallback(
38719
+ (e) => {
38720
+ const state = interactionRef.current;
38721
+ const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
38722
+ state.mode = "none";
38723
+ state.dragNodeId = null;
38724
+ if (moved < 4) {
38725
+ const coords = toCoords(e);
38726
+ if (!coords) return;
38727
+ const node = nodeAt(coords.graphX, coords.graphY);
38728
+ if (node) {
38729
+ handleNodeClick(node);
38730
+ }
38731
+ }
38732
+ },
38733
+ [toCoords, nodeAt, handleNodeClick]
38734
+ );
38735
+ const handleMouseLeave = React76.useCallback(() => {
38736
+ interactionRef.current.mode = "none";
38737
+ interactionRef.current.dragNodeId = null;
38738
+ setHoveredNode(null);
38739
+ }, []);
38610
38740
  if (isLoading) {
38611
38741
  return /* @__PURE__ */ jsxRuntime.jsx(exports.LoadingState, { message: t("common.loading"), className });
38612
38742
  }
@@ -38668,20 +38798,11 @@ var init_GraphCanvas = __esm({
38668
38798
  height,
38669
38799
  className: "w-full cursor-grab active:cursor-grabbing",
38670
38800
  style: { height },
38671
- onClick: (e) => {
38672
- const canvas = canvasRef.current;
38673
- if (!canvas) return;
38674
- const rect = canvas.getBoundingClientRect();
38675
- const x = (e.clientX - rect.left - offset.x) / zoom;
38676
- const y = (e.clientY - rect.top - offset.y) / zoom;
38677
- const clickedNode = nodesRef.current.find((n) => {
38678
- const dist = Math.sqrt((n.x - x) ** 2 + (n.y - y) ** 2);
38679
- return dist < (n.size || 8) + 4;
38680
- });
38681
- if (clickedNode) {
38682
- handleNodeClick(clickedNode);
38683
- }
38684
- }
38801
+ onWheel: handleWheel,
38802
+ onMouseDown: handleMouseDown,
38803
+ onMouseMove: handleMouseMove,
38804
+ onMouseUp: handleMouseUp,
38805
+ onMouseLeave: handleMouseLeave
38685
38806
  }
38686
38807
  ) }),
38687
38808
  groups.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
@@ -50500,12 +50621,25 @@ function pathToFeatures(path) {
50500
50621
  sprite: `${CDN6}world-map/road_straight.png`
50501
50622
  }));
50502
50623
  }
50624
+ function heroToUnit(hero) {
50625
+ return {
50626
+ id: hero.id,
50627
+ position: { x: hero.x, y: hero.y },
50628
+ name: "Hero",
50629
+ team: "player",
50630
+ sprite: HERO_SPRITE,
50631
+ unitType: "amir",
50632
+ health: 1,
50633
+ maxHealth: 1
50634
+ };
50635
+ }
50503
50636
  function TowerDefenseBoard({
50504
50637
  entity,
50505
50638
  tiles: propTiles,
50506
50639
  path: propPath,
50507
50640
  towers: propTowers,
50508
50641
  creeps: propCreeps,
50642
+ hero: propHero,
50509
50643
  gold: propGold,
50510
50644
  lives: propLives,
50511
50645
  wave: propWave,
@@ -50521,6 +50655,7 @@ function TowerDefenseBoard({
50521
50655
  startWaveEvent,
50522
50656
  playAgainEvent,
50523
50657
  gameEndEvent,
50658
+ moveHeroEvent,
50524
50659
  className
50525
50660
  }) {
50526
50661
  const board = boardEntity(entity) ?? {};
@@ -50532,6 +50667,7 @@ function TowerDefenseBoard({
50532
50667
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
50533
50668
  const towers = propTowers ?? rows(board.towers);
50534
50669
  const creeps = propCreeps ?? rows(board.creeps);
50670
+ const hero = propHero ?? { id: "hero", x: 8, y: 8 };
50535
50671
  const gold = propGold ?? num(board.gold, 100);
50536
50672
  const lives = propLives ?? num(board.lives, 20);
50537
50673
  const wave = propWave ?? num(board.wave, 1);
@@ -50549,6 +50685,31 @@ function TowerDefenseBoard({
50549
50685
  if (result === "none") {
50550
50686
  emittedGameEnd.current = false;
50551
50687
  }
50688
+ const moveHeroEventRef = React76.useRef(moveHeroEvent);
50689
+ moveHeroEventRef.current = moveHeroEvent;
50690
+ const resultRef = React76.useRef(result);
50691
+ resultRef.current = result;
50692
+ React76.useEffect(() => {
50693
+ function onKeyDown(e) {
50694
+ const evt = moveHeroEventRef.current;
50695
+ if (!evt || resultRef.current !== "none") return;
50696
+ let dx = 0;
50697
+ let dy = 0;
50698
+ if (e.key === "ArrowUp") {
50699
+ dy = -1;
50700
+ } else if (e.key === "ArrowDown") {
50701
+ dy = 1;
50702
+ } else if (e.key === "ArrowLeft") {
50703
+ dx = -1;
50704
+ } else if (e.key === "ArrowRight") {
50705
+ dx = 1;
50706
+ } else return;
50707
+ e.preventDefault();
50708
+ eventBus.emit(`UI:${evt}`, { dx, dy });
50709
+ }
50710
+ window.addEventListener("keydown", onKeyDown);
50711
+ return () => window.removeEventListener("keydown", onKeyDown);
50712
+ }, [eventBus]);
50552
50713
  const towerPositions = React76.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
50553
50714
  const pathPositions = React76.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
50554
50715
  const validMoves = React76.useMemo(() => {
@@ -50560,7 +50721,8 @@ function TowerDefenseBoard({
50560
50721
  const isoTiles = React76.useMemo(() => tilesToIso(tiles), [tiles]);
50561
50722
  const towerUnits = React76.useMemo(() => towersToUnits(towers), [towers]);
50562
50723
  const creepUnits = React76.useMemo(() => creepsToUnits(creeps), [creeps]);
50563
- const isoUnits = React76.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
50724
+ const heroUnit = React76.useMemo(() => heroToUnit(hero), [hero]);
50725
+ const isoUnits = React76.useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
50564
50726
  const pathFeatures = React76.useMemo(() => pathToFeatures(path), [path]);
50565
50727
  const handleTileClick = React76.useCallback((x, y) => {
50566
50728
  if (result !== "none") return;
@@ -50659,7 +50821,7 @@ function TowerDefenseBoard({
50659
50821
  ] }) })
50660
50822
  ] });
50661
50823
  }
50662
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
50824
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
50663
50825
  var init_TowerDefenseBoard = __esm({
50664
50826
  "components/game/organisms/TowerDefenseBoard.tsx"() {
50665
50827
  "use client";
@@ -50733,8 +50895,9 @@ var init_TowerDefenseBoard = __esm({
50733
50895
  { x: 13, y: 15 }
50734
50896
  ];
50735
50897
  DEFAULT_TD_TILES = buildDefaultTDTiles();
50736
- TOWER_SPRITE = `${CDN6}units/guardian.png`;
50737
- CREEP_SPRITE = `${CDN6}units/scrapper.png`;
50898
+ TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
50899
+ CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
50900
+ HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
50738
50901
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
50739
50902
  }
50740
50903
  });