@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.
@@ -29220,13 +29220,13 @@ var init_MapView = __esm({
29220
29220
  shadowSize: [41, 41]
29221
29221
  });
29222
29222
  L.Marker.prototype.options.icon = defaultIcon;
29223
- const { useEffect: useEffect81, useRef: useRef75, useCallback: useCallback119, useState: useState113 } = React81__namespace.default;
29223
+ const { useEffect: useEffect82, useRef: useRef75, useCallback: useCallback119, useState: useState113 } = React81__namespace.default;
29224
29224
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29225
29225
  const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29226
29226
  function MapUpdater({ centerLat, centerLng, zoom }) {
29227
29227
  const map = useMap();
29228
29228
  const prevRef = useRef75({ centerLat, centerLng, zoom });
29229
- useEffect81(() => {
29229
+ useEffect82(() => {
29230
29230
  const prev = prevRef.current;
29231
29231
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29232
29232
  map.setView([centerLat, centerLng], zoom);
@@ -29237,7 +29237,7 @@ var init_MapView = __esm({
29237
29237
  }
29238
29238
  function MapClickHandler({ onMapClick }) {
29239
29239
  const map = useMap();
29240
- useEffect81(() => {
29240
+ useEffect82(() => {
29241
29241
  if (!onMapClick) return;
29242
29242
  const handler = (e) => {
29243
29243
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -37698,6 +37698,9 @@ var init_GraphCanvas = __esm({
37698
37698
  actions,
37699
37699
  onNodeClick,
37700
37700
  nodeClickEvent,
37701
+ selectedNodeId,
37702
+ repulsion = 800,
37703
+ linkDistance = 100,
37701
37704
  layout = "force",
37702
37705
  entity,
37703
37706
  isLoading = false,
@@ -37713,6 +37716,35 @@ var init_GraphCanvas = __esm({
37713
37716
  const [hoveredNode, setHoveredNode] = React81.useState(null);
37714
37717
  const nodesRef = React81.useRef([]);
37715
37718
  const [, forceUpdate] = React81.useState(0);
37719
+ const interactionRef = React81.useRef({
37720
+ mode: "none",
37721
+ dragNodeId: null,
37722
+ startMouse: { x: 0, y: 0 },
37723
+ startOffset: { x: 0, y: 0 },
37724
+ downPos: { x: 0, y: 0 }
37725
+ });
37726
+ const toCoords = React81.useCallback(
37727
+ (e) => {
37728
+ const canvas = canvasRef.current;
37729
+ if (!canvas) return null;
37730
+ const rect = canvas.getBoundingClientRect();
37731
+ const screenX = e.clientX - rect.left;
37732
+ const screenY = e.clientY - rect.top;
37733
+ return {
37734
+ screenX,
37735
+ screenY,
37736
+ graphX: (screenX - offset.x) / zoom,
37737
+ graphY: (screenY - offset.y) / zoom
37738
+ };
37739
+ },
37740
+ [offset, zoom]
37741
+ );
37742
+ const nodeAt = React81.useCallback((graphX, graphY) => {
37743
+ return nodesRef.current.find((n) => {
37744
+ const dist = Math.sqrt((n.x - graphX) ** 2 + (n.y - graphY) ** 2);
37745
+ return dist < (n.size || 8) + 4;
37746
+ });
37747
+ }, []);
37716
37748
  const handleAction = React81.useCallback(
37717
37749
  (action) => {
37718
37750
  if (action.event) {
@@ -37778,7 +37810,7 @@ var init_GraphCanvas = __esm({
37778
37810
  const dx = nodes[j].x - nodes[i].x;
37779
37811
  const dy = nodes[j].y - nodes[i].y;
37780
37812
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
37781
- const force = 800 / (dist * dist);
37813
+ const force = repulsion / (dist * dist);
37782
37814
  const fx = dx / dist * force;
37783
37815
  const fy = dy / dist * force;
37784
37816
  nodes[i].fx -= fx;
@@ -37794,7 +37826,7 @@ var init_GraphCanvas = __esm({
37794
37826
  const dx = target.x - source.x;
37795
37827
  const dy = target.y - source.y;
37796
37828
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
37797
- const force = (dist - 100) * 0.05;
37829
+ const force = (dist - linkDistance) * 0.05;
37798
37830
  const fx = dx / dist * force;
37799
37831
  const fy = dy / dist * force;
37800
37832
  source.fx += fx;
@@ -37828,7 +37860,7 @@ var init_GraphCanvas = __esm({
37828
37860
  return () => {
37829
37861
  cancelAnimationFrame(animRef.current);
37830
37862
  };
37831
- }, [propNodes, propEdges, layout]);
37863
+ }, [propNodes, propEdges, layout, repulsion, linkDistance]);
37832
37864
  React81.useEffect(() => {
37833
37865
  const canvas = canvasRef.current;
37834
37866
  if (!canvas) return;
@@ -37864,18 +37896,25 @@ var init_GraphCanvas = __esm({
37864
37896
  const size = node.size || 8;
37865
37897
  const color = node.color || getGroupColor(node.group, groups);
37866
37898
  const isHovered = hoveredNode === node.id;
37899
+ const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
37900
+ const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
37867
37901
  ctx.beginPath();
37868
- ctx.arc(node.x, node.y, isHovered ? size + 2 : size, 0, Math.PI * 2);
37902
+ ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
37869
37903
  ctx.fillStyle = color;
37870
37904
  ctx.fill();
37871
- ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
37872
- ctx.lineWidth = isHovered ? 2 : 1;
37905
+ if (isSelected) {
37906
+ ctx.strokeStyle = "var(--color-accent)";
37907
+ ctx.lineWidth = 3;
37908
+ } else {
37909
+ ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
37910
+ ctx.lineWidth = isHovered ? 2 : 1;
37911
+ }
37873
37912
  ctx.stroke();
37874
37913
  if (showLabels && node.label) {
37875
37914
  ctx.fillStyle = "#666666";
37876
- ctx.font = `${isHovered ? "bold " : ""}10px system-ui`;
37915
+ ctx.font = `${isSelected || isHovered ? "bold " : ""}10px system-ui`;
37877
37916
  ctx.textAlign = "center";
37878
- ctx.fillText(node.label, node.x, node.y + size + 12);
37917
+ ctx.fillText(node.label, node.x, node.y + radius + 12);
37879
37918
  }
37880
37919
  }
37881
37920
  ctx.restore();
@@ -37886,6 +37925,97 @@ var init_GraphCanvas = __esm({
37886
37925
  setZoom(1);
37887
37926
  setOffset({ x: 0, y: 0 });
37888
37927
  }, []);
37928
+ const handleWheel = React81.useCallback(
37929
+ (e) => {
37930
+ if (!interactive) return;
37931
+ e.preventDefault();
37932
+ const coords = toCoords(e);
37933
+ if (!coords) return;
37934
+ const oldZoom = zoom;
37935
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
37936
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
37937
+ if (newZoom === oldZoom) return;
37938
+ setOffset((o) => ({
37939
+ x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
37940
+ y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
37941
+ }));
37942
+ setZoom(newZoom);
37943
+ },
37944
+ [interactive, toCoords, zoom]
37945
+ );
37946
+ const handleMouseDown = React81.useCallback(
37947
+ (e) => {
37948
+ const coords = toCoords(e);
37949
+ if (!coords) return;
37950
+ const node = nodeAt(coords.graphX, coords.graphY);
37951
+ const state = interactionRef.current;
37952
+ state.downPos = { x: e.clientX, y: e.clientY };
37953
+ state.startMouse = { x: e.clientX, y: e.clientY };
37954
+ state.startOffset = { ...offset };
37955
+ if (draggable && node) {
37956
+ state.mode = "dragging";
37957
+ state.dragNodeId = node.id;
37958
+ } else if (interactive) {
37959
+ state.mode = "panning";
37960
+ state.dragNodeId = null;
37961
+ } else {
37962
+ state.mode = "none";
37963
+ state.dragNodeId = null;
37964
+ }
37965
+ },
37966
+ [toCoords, nodeAt, draggable, interactive, offset]
37967
+ );
37968
+ const handleMouseMove = React81.useCallback(
37969
+ (e) => {
37970
+ const state = interactionRef.current;
37971
+ if (state.mode === "panning") {
37972
+ const dx = e.clientX - state.startMouse.x;
37973
+ const dy = e.clientY - state.startMouse.y;
37974
+ setOffset({ x: state.startOffset.x + dx, y: state.startOffset.y + dy });
37975
+ return;
37976
+ }
37977
+ if (state.mode === "dragging" && state.dragNodeId) {
37978
+ const coords2 = toCoords(e);
37979
+ if (!coords2) return;
37980
+ const node2 = nodesRef.current.find((n) => n.id === state.dragNodeId);
37981
+ if (node2) {
37982
+ node2.x = coords2.graphX;
37983
+ node2.y = coords2.graphY;
37984
+ node2.vx = 0;
37985
+ node2.vy = 0;
37986
+ forceUpdate((n) => n + 1);
37987
+ }
37988
+ return;
37989
+ }
37990
+ const coords = toCoords(e);
37991
+ if (!coords) return;
37992
+ const node = nodeAt(coords.graphX, coords.graphY);
37993
+ setHoveredNode(node?.id ?? null);
37994
+ },
37995
+ [toCoords, nodeAt]
37996
+ );
37997
+ const handleMouseUp = React81.useCallback(
37998
+ (e) => {
37999
+ const state = interactionRef.current;
38000
+ const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
38001
+ state.mode = "none";
38002
+ state.dragNodeId = null;
38003
+ if (moved < 4) {
38004
+ const coords = toCoords(e);
38005
+ if (!coords) return;
38006
+ const node = nodeAt(coords.graphX, coords.graphY);
38007
+ if (node) {
38008
+ handleNodeClick(node);
38009
+ }
38010
+ }
38011
+ },
38012
+ [toCoords, nodeAt, handleNodeClick]
38013
+ );
38014
+ const handleMouseLeave = React81.useCallback(() => {
38015
+ interactionRef.current.mode = "none";
38016
+ interactionRef.current.dragNodeId = null;
38017
+ setHoveredNode(null);
38018
+ }, []);
37889
38019
  if (isLoading) {
37890
38020
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { message: t("common.loading"), className });
37891
38021
  }
@@ -37947,20 +38077,11 @@ var init_GraphCanvas = __esm({
37947
38077
  height,
37948
38078
  className: "w-full cursor-grab active:cursor-grabbing",
37949
38079
  style: { height },
37950
- onClick: (e) => {
37951
- const canvas = canvasRef.current;
37952
- if (!canvas) return;
37953
- const rect = canvas.getBoundingClientRect();
37954
- const x = (e.clientX - rect.left - offset.x) / zoom;
37955
- const y = (e.clientY - rect.top - offset.y) / zoom;
37956
- const clickedNode = nodesRef.current.find((n) => {
37957
- const dist = Math.sqrt((n.x - x) ** 2 + (n.y - y) ** 2);
37958
- return dist < (n.size || 8) + 4;
37959
- });
37960
- if (clickedNode) {
37961
- handleNodeClick(clickedNode);
37962
- }
37963
- }
38080
+ onWheel: handleWheel,
38081
+ onMouseDown: handleMouseDown,
38082
+ onMouseMove: handleMouseMove,
38083
+ onMouseUp: handleMouseUp,
38084
+ onMouseLeave: handleMouseLeave
37964
38085
  }
37965
38086
  ) }),
37966
38087
  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: [
@@ -49056,12 +49177,25 @@ function pathToFeatures(path) {
49056
49177
  sprite: `${CDN6}world-map/road_straight.png`
49057
49178
  }));
49058
49179
  }
49180
+ function heroToUnit(hero) {
49181
+ return {
49182
+ id: hero.id,
49183
+ position: { x: hero.x, y: hero.y },
49184
+ name: "Hero",
49185
+ team: "player",
49186
+ sprite: HERO_SPRITE,
49187
+ unitType: "amir",
49188
+ health: 1,
49189
+ maxHealth: 1
49190
+ };
49191
+ }
49059
49192
  function TowerDefenseBoard({
49060
49193
  entity,
49061
49194
  tiles: propTiles,
49062
49195
  path: propPath,
49063
49196
  towers: propTowers,
49064
49197
  creeps: propCreeps,
49198
+ hero: propHero,
49065
49199
  gold: propGold,
49066
49200
  lives: propLives,
49067
49201
  wave: propWave,
@@ -49077,6 +49211,7 @@ function TowerDefenseBoard({
49077
49211
  startWaveEvent,
49078
49212
  playAgainEvent,
49079
49213
  gameEndEvent,
49214
+ moveHeroEvent,
49080
49215
  className
49081
49216
  }) {
49082
49217
  const board = boardEntity(entity) ?? {};
@@ -49088,6 +49223,7 @@ function TowerDefenseBoard({
49088
49223
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
49089
49224
  const towers = propTowers ?? rows(board.towers);
49090
49225
  const creeps = propCreeps ?? rows(board.creeps);
49226
+ const hero = propHero ?? { id: "hero", x: 8, y: 8 };
49091
49227
  const gold = propGold ?? num(board.gold, 100);
49092
49228
  const lives = propLives ?? num(board.lives, 20);
49093
49229
  const wave = propWave ?? num(board.wave, 1);
@@ -49105,6 +49241,31 @@ function TowerDefenseBoard({
49105
49241
  if (result === "none") {
49106
49242
  emittedGameEnd.current = false;
49107
49243
  }
49244
+ const moveHeroEventRef = React81.useRef(moveHeroEvent);
49245
+ moveHeroEventRef.current = moveHeroEvent;
49246
+ const resultRef = React81.useRef(result);
49247
+ resultRef.current = result;
49248
+ React81.useEffect(() => {
49249
+ function onKeyDown(e) {
49250
+ const evt = moveHeroEventRef.current;
49251
+ if (!evt || resultRef.current !== "none") return;
49252
+ let dx = 0;
49253
+ let dy = 0;
49254
+ if (e.key === "ArrowUp") {
49255
+ dy = -1;
49256
+ } else if (e.key === "ArrowDown") {
49257
+ dy = 1;
49258
+ } else if (e.key === "ArrowLeft") {
49259
+ dx = -1;
49260
+ } else if (e.key === "ArrowRight") {
49261
+ dx = 1;
49262
+ } else return;
49263
+ e.preventDefault();
49264
+ eventBus.emit(`UI:${evt}`, { dx, dy });
49265
+ }
49266
+ window.addEventListener("keydown", onKeyDown);
49267
+ return () => window.removeEventListener("keydown", onKeyDown);
49268
+ }, [eventBus]);
49108
49269
  const towerPositions = React81.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
49109
49270
  const pathPositions = React81.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
49110
49271
  const validMoves = React81.useMemo(() => {
@@ -49116,7 +49277,8 @@ function TowerDefenseBoard({
49116
49277
  const isoTiles = React81.useMemo(() => tilesToIso(tiles), [tiles]);
49117
49278
  const towerUnits = React81.useMemo(() => towersToUnits(towers), [towers]);
49118
49279
  const creepUnits = React81.useMemo(() => creepsToUnits(creeps), [creeps]);
49119
- const isoUnits = React81.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
49280
+ const heroUnit = React81.useMemo(() => heroToUnit(hero), [hero]);
49281
+ const isoUnits = React81.useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
49120
49282
  const pathFeatures = React81.useMemo(() => pathToFeatures(path), [path]);
49121
49283
  const handleTileClick = React81.useCallback((x, y) => {
49122
49284
  if (result !== "none") return;
@@ -49215,7 +49377,7 @@ function TowerDefenseBoard({
49215
49377
  ] }) })
49216
49378
  ] });
49217
49379
  }
49218
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
49380
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
49219
49381
  var init_TowerDefenseBoard = __esm({
49220
49382
  "components/game/organisms/TowerDefenseBoard.tsx"() {
49221
49383
  "use client";
@@ -49289,8 +49451,9 @@ var init_TowerDefenseBoard = __esm({
49289
49451
  { x: 13, y: 15 }
49290
49452
  ];
49291
49453
  DEFAULT_TD_TILES = buildDefaultTDTiles();
49292
- TOWER_SPRITE = `${CDN6}units/guardian.png`;
49293
- CREEP_SPRITE = `${CDN6}units/scrapper.png`;
49454
+ TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
49455
+ CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
49456
+ HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
49294
49457
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
49295
49458
  }
49296
49459
  });