@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.
@@ -32130,13 +32130,13 @@ var init_MapView = __esm({
32130
32130
  shadowSize: [41, 41]
32131
32131
  });
32132
32132
  L.Marker.prototype.options.icon = defaultIcon;
32133
- const { useEffect: useEffect85, useRef: useRef77, useCallback: useCallback125, useState: useState121 } = React90__namespace.default;
32133
+ const { useEffect: useEffect86, useRef: useRef77, useCallback: useCallback125, useState: useState121 } = React90__namespace.default;
32134
32134
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32135
32135
  const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32136
32136
  function MapUpdater({ centerLat, centerLng, zoom }) {
32137
32137
  const map = useMap();
32138
32138
  const prevRef = useRef77({ centerLat, centerLng, zoom });
32139
- useEffect85(() => {
32139
+ useEffect86(() => {
32140
32140
  const prev = prevRef.current;
32141
32141
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32142
32142
  map.setView([centerLat, centerLng], zoom);
@@ -32147,7 +32147,7 @@ var init_MapView = __esm({
32147
32147
  }
32148
32148
  function MapClickHandler({ onMapClick }) {
32149
32149
  const map = useMap();
32150
- useEffect85(() => {
32150
+ useEffect86(() => {
32151
32151
  if (!onMapClick) return;
32152
32152
  const handler = (e) => {
32153
32153
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -40199,6 +40199,9 @@ var init_GraphCanvas = __esm({
40199
40199
  actions,
40200
40200
  onNodeClick,
40201
40201
  nodeClickEvent,
40202
+ selectedNodeId,
40203
+ repulsion = 800,
40204
+ linkDistance = 100,
40202
40205
  layout = "force",
40203
40206
  entity,
40204
40207
  isLoading = false,
@@ -40214,6 +40217,35 @@ var init_GraphCanvas = __esm({
40214
40217
  const [hoveredNode, setHoveredNode] = React90.useState(null);
40215
40218
  const nodesRef = React90.useRef([]);
40216
40219
  const [, forceUpdate] = React90.useState(0);
40220
+ const interactionRef = React90.useRef({
40221
+ mode: "none",
40222
+ dragNodeId: null,
40223
+ startMouse: { x: 0, y: 0 },
40224
+ startOffset: { x: 0, y: 0 },
40225
+ downPos: { x: 0, y: 0 }
40226
+ });
40227
+ const toCoords = React90.useCallback(
40228
+ (e) => {
40229
+ const canvas = canvasRef.current;
40230
+ if (!canvas) return null;
40231
+ const rect = canvas.getBoundingClientRect();
40232
+ const screenX = e.clientX - rect.left;
40233
+ const screenY = e.clientY - rect.top;
40234
+ return {
40235
+ screenX,
40236
+ screenY,
40237
+ graphX: (screenX - offset.x) / zoom,
40238
+ graphY: (screenY - offset.y) / zoom
40239
+ };
40240
+ },
40241
+ [offset, zoom]
40242
+ );
40243
+ const nodeAt = React90.useCallback((graphX, graphY) => {
40244
+ return nodesRef.current.find((n) => {
40245
+ const dist = Math.sqrt((n.x - graphX) ** 2 + (n.y - graphY) ** 2);
40246
+ return dist < (n.size || 8) + 4;
40247
+ });
40248
+ }, []);
40217
40249
  const handleAction = React90.useCallback(
40218
40250
  (action) => {
40219
40251
  if (action.event) {
@@ -40279,7 +40311,7 @@ var init_GraphCanvas = __esm({
40279
40311
  const dx = nodes[j].x - nodes[i].x;
40280
40312
  const dy = nodes[j].y - nodes[i].y;
40281
40313
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
40282
- const force = 800 / (dist * dist);
40314
+ const force = repulsion / (dist * dist);
40283
40315
  const fx = dx / dist * force;
40284
40316
  const fy = dy / dist * force;
40285
40317
  nodes[i].fx -= fx;
@@ -40295,7 +40327,7 @@ var init_GraphCanvas = __esm({
40295
40327
  const dx = target.x - source.x;
40296
40328
  const dy = target.y - source.y;
40297
40329
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
40298
- const force = (dist - 100) * 0.05;
40330
+ const force = (dist - linkDistance) * 0.05;
40299
40331
  const fx = dx / dist * force;
40300
40332
  const fy = dy / dist * force;
40301
40333
  source.fx += fx;
@@ -40329,7 +40361,7 @@ var init_GraphCanvas = __esm({
40329
40361
  return () => {
40330
40362
  cancelAnimationFrame(animRef.current);
40331
40363
  };
40332
- }, [propNodes, propEdges, layout]);
40364
+ }, [propNodes, propEdges, layout, repulsion, linkDistance]);
40333
40365
  React90.useEffect(() => {
40334
40366
  const canvas = canvasRef.current;
40335
40367
  if (!canvas) return;
@@ -40365,18 +40397,25 @@ var init_GraphCanvas = __esm({
40365
40397
  const size = node.size || 8;
40366
40398
  const color = node.color || getGroupColor(node.group, groups);
40367
40399
  const isHovered = hoveredNode === node.id;
40400
+ const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
40401
+ const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
40368
40402
  ctx.beginPath();
40369
- ctx.arc(node.x, node.y, isHovered ? size + 2 : size, 0, Math.PI * 2);
40403
+ ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
40370
40404
  ctx.fillStyle = color;
40371
40405
  ctx.fill();
40372
- ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
40373
- ctx.lineWidth = isHovered ? 2 : 1;
40406
+ if (isSelected) {
40407
+ ctx.strokeStyle = "var(--color-accent)";
40408
+ ctx.lineWidth = 3;
40409
+ } else {
40410
+ ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
40411
+ ctx.lineWidth = isHovered ? 2 : 1;
40412
+ }
40374
40413
  ctx.stroke();
40375
40414
  if (showLabels && node.label) {
40376
40415
  ctx.fillStyle = "#666666";
40377
- ctx.font = `${isHovered ? "bold " : ""}10px system-ui`;
40416
+ ctx.font = `${isSelected || isHovered ? "bold " : ""}10px system-ui`;
40378
40417
  ctx.textAlign = "center";
40379
- ctx.fillText(node.label, node.x, node.y + size + 12);
40418
+ ctx.fillText(node.label, node.x, node.y + radius + 12);
40380
40419
  }
40381
40420
  }
40382
40421
  ctx.restore();
@@ -40387,6 +40426,97 @@ var init_GraphCanvas = __esm({
40387
40426
  setZoom(1);
40388
40427
  setOffset({ x: 0, y: 0 });
40389
40428
  }, []);
40429
+ const handleWheel = React90.useCallback(
40430
+ (e) => {
40431
+ if (!interactive) return;
40432
+ e.preventDefault();
40433
+ const coords = toCoords(e);
40434
+ if (!coords) return;
40435
+ const oldZoom = zoom;
40436
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
40437
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
40438
+ if (newZoom === oldZoom) return;
40439
+ setOffset((o) => ({
40440
+ x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
40441
+ y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
40442
+ }));
40443
+ setZoom(newZoom);
40444
+ },
40445
+ [interactive, toCoords, zoom]
40446
+ );
40447
+ const handleMouseDown = React90.useCallback(
40448
+ (e) => {
40449
+ const coords = toCoords(e);
40450
+ if (!coords) return;
40451
+ const node = nodeAt(coords.graphX, coords.graphY);
40452
+ const state = interactionRef.current;
40453
+ state.downPos = { x: e.clientX, y: e.clientY };
40454
+ state.startMouse = { x: e.clientX, y: e.clientY };
40455
+ state.startOffset = { ...offset };
40456
+ if (draggable && node) {
40457
+ state.mode = "dragging";
40458
+ state.dragNodeId = node.id;
40459
+ } else if (interactive) {
40460
+ state.mode = "panning";
40461
+ state.dragNodeId = null;
40462
+ } else {
40463
+ state.mode = "none";
40464
+ state.dragNodeId = null;
40465
+ }
40466
+ },
40467
+ [toCoords, nodeAt, draggable, interactive, offset]
40468
+ );
40469
+ const handleMouseMove = React90.useCallback(
40470
+ (e) => {
40471
+ const state = interactionRef.current;
40472
+ if (state.mode === "panning") {
40473
+ const dx = e.clientX - state.startMouse.x;
40474
+ const dy = e.clientY - state.startMouse.y;
40475
+ setOffset({ x: state.startOffset.x + dx, y: state.startOffset.y + dy });
40476
+ return;
40477
+ }
40478
+ if (state.mode === "dragging" && state.dragNodeId) {
40479
+ const coords2 = toCoords(e);
40480
+ if (!coords2) return;
40481
+ const node2 = nodesRef.current.find((n) => n.id === state.dragNodeId);
40482
+ if (node2) {
40483
+ node2.x = coords2.graphX;
40484
+ node2.y = coords2.graphY;
40485
+ node2.vx = 0;
40486
+ node2.vy = 0;
40487
+ forceUpdate((n) => n + 1);
40488
+ }
40489
+ return;
40490
+ }
40491
+ const coords = toCoords(e);
40492
+ if (!coords) return;
40493
+ const node = nodeAt(coords.graphX, coords.graphY);
40494
+ setHoveredNode(node?.id ?? null);
40495
+ },
40496
+ [toCoords, nodeAt]
40497
+ );
40498
+ const handleMouseUp = React90.useCallback(
40499
+ (e) => {
40500
+ const state = interactionRef.current;
40501
+ const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
40502
+ state.mode = "none";
40503
+ state.dragNodeId = null;
40504
+ if (moved < 4) {
40505
+ const coords = toCoords(e);
40506
+ if (!coords) return;
40507
+ const node = nodeAt(coords.graphX, coords.graphY);
40508
+ if (node) {
40509
+ handleNodeClick(node);
40510
+ }
40511
+ }
40512
+ },
40513
+ [toCoords, nodeAt, handleNodeClick]
40514
+ );
40515
+ const handleMouseLeave = React90.useCallback(() => {
40516
+ interactionRef.current.mode = "none";
40517
+ interactionRef.current.dragNodeId = null;
40518
+ setHoveredNode(null);
40519
+ }, []);
40390
40520
  if (isLoading) {
40391
40521
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { message: t("common.loading"), className });
40392
40522
  }
@@ -40448,20 +40578,11 @@ var init_GraphCanvas = __esm({
40448
40578
  height,
40449
40579
  className: "w-full cursor-grab active:cursor-grabbing",
40450
40580
  style: { height },
40451
- onClick: (e) => {
40452
- const canvas = canvasRef.current;
40453
- if (!canvas) return;
40454
- const rect = canvas.getBoundingClientRect();
40455
- const x = (e.clientX - rect.left - offset.x) / zoom;
40456
- const y = (e.clientY - rect.top - offset.y) / zoom;
40457
- const clickedNode = nodesRef.current.find((n) => {
40458
- const dist = Math.sqrt((n.x - x) ** 2 + (n.y - y) ** 2);
40459
- return dist < (n.size || 8) + 4;
40460
- });
40461
- if (clickedNode) {
40462
- handleNodeClick(clickedNode);
40463
- }
40464
- }
40581
+ onWheel: handleWheel,
40582
+ onMouseDown: handleMouseDown,
40583
+ onMouseMove: handleMouseMove,
40584
+ onMouseUp: handleMouseUp,
40585
+ onMouseLeave: handleMouseLeave
40465
40586
  }
40466
40587
  ) }),
40467
40588
  groups.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
@@ -51557,12 +51678,25 @@ function pathToFeatures(path) {
51557
51678
  sprite: `${CDN6}world-map/road_straight.png`
51558
51679
  }));
51559
51680
  }
51681
+ function heroToUnit(hero) {
51682
+ return {
51683
+ id: hero.id,
51684
+ position: { x: hero.x, y: hero.y },
51685
+ name: "Hero",
51686
+ team: "player",
51687
+ sprite: HERO_SPRITE,
51688
+ unitType: "amir",
51689
+ health: 1,
51690
+ maxHealth: 1
51691
+ };
51692
+ }
51560
51693
  function TowerDefenseBoard({
51561
51694
  entity,
51562
51695
  tiles: propTiles,
51563
51696
  path: propPath,
51564
51697
  towers: propTowers,
51565
51698
  creeps: propCreeps,
51699
+ hero: propHero,
51566
51700
  gold: propGold,
51567
51701
  lives: propLives,
51568
51702
  wave: propWave,
@@ -51578,6 +51712,7 @@ function TowerDefenseBoard({
51578
51712
  startWaveEvent,
51579
51713
  playAgainEvent,
51580
51714
  gameEndEvent,
51715
+ moveHeroEvent,
51581
51716
  className
51582
51717
  }) {
51583
51718
  const board = boardEntity(entity) ?? {};
@@ -51589,6 +51724,7 @@ function TowerDefenseBoard({
51589
51724
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
51590
51725
  const towers = propTowers ?? rows(board.towers);
51591
51726
  const creeps = propCreeps ?? rows(board.creeps);
51727
+ const hero = propHero ?? { id: "hero", x: 8, y: 8 };
51592
51728
  const gold = propGold ?? num(board.gold, 100);
51593
51729
  const lives = propLives ?? num(board.lives, 20);
51594
51730
  const wave = propWave ?? num(board.wave, 1);
@@ -51606,6 +51742,31 @@ function TowerDefenseBoard({
51606
51742
  if (result === "none") {
51607
51743
  emittedGameEnd.current = false;
51608
51744
  }
51745
+ const moveHeroEventRef = React90.useRef(moveHeroEvent);
51746
+ moveHeroEventRef.current = moveHeroEvent;
51747
+ const resultRef = React90.useRef(result);
51748
+ resultRef.current = result;
51749
+ React90.useEffect(() => {
51750
+ function onKeyDown(e) {
51751
+ const evt = moveHeroEventRef.current;
51752
+ if (!evt || resultRef.current !== "none") return;
51753
+ let dx = 0;
51754
+ let dy = 0;
51755
+ if (e.key === "ArrowUp") {
51756
+ dy = -1;
51757
+ } else if (e.key === "ArrowDown") {
51758
+ dy = 1;
51759
+ } else if (e.key === "ArrowLeft") {
51760
+ dx = -1;
51761
+ } else if (e.key === "ArrowRight") {
51762
+ dx = 1;
51763
+ } else return;
51764
+ e.preventDefault();
51765
+ eventBus.emit(`UI:${evt}`, { dx, dy });
51766
+ }
51767
+ window.addEventListener("keydown", onKeyDown);
51768
+ return () => window.removeEventListener("keydown", onKeyDown);
51769
+ }, [eventBus]);
51609
51770
  const towerPositions = React90.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
51610
51771
  const pathPositions = React90.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
51611
51772
  const validMoves = React90.useMemo(() => {
@@ -51617,7 +51778,8 @@ function TowerDefenseBoard({
51617
51778
  const isoTiles = React90.useMemo(() => tilesToIso(tiles), [tiles]);
51618
51779
  const towerUnits = React90.useMemo(() => towersToUnits(towers), [towers]);
51619
51780
  const creepUnits = React90.useMemo(() => creepsToUnits(creeps), [creeps]);
51620
- const isoUnits = React90.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
51781
+ const heroUnit = React90.useMemo(() => heroToUnit(hero), [hero]);
51782
+ const isoUnits = React90.useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
51621
51783
  const pathFeatures = React90.useMemo(() => pathToFeatures(path), [path]);
51622
51784
  const handleTileClick = React90.useCallback((x, y) => {
51623
51785
  if (result !== "none") return;
@@ -51716,7 +51878,7 @@ function TowerDefenseBoard({
51716
51878
  ] }) })
51717
51879
  ] });
51718
51880
  }
51719
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
51881
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
51720
51882
  var init_TowerDefenseBoard = __esm({
51721
51883
  "components/game/organisms/TowerDefenseBoard.tsx"() {
51722
51884
  "use client";
@@ -51790,8 +51952,9 @@ var init_TowerDefenseBoard = __esm({
51790
51952
  { x: 13, y: 15 }
51791
51953
  ];
51792
51954
  DEFAULT_TD_TILES = buildDefaultTDTiles();
51793
- TOWER_SPRITE = `${CDN6}units/guardian.png`;
51794
- CREEP_SPRITE = `${CDN6}units/scrapper.png`;
51955
+ TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
51956
+ CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
51957
+ HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
51795
51958
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
51796
51959
  }
51797
51960
  });