@almadar/ui 4.50.4 → 4.50.6

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.
@@ -21109,12 +21109,17 @@ function useDataDnd(args) {
21109
21109
  React80__namespace.default.useEffect(() => {
21110
21110
  setLocalOrder(null);
21111
21111
  }, [items]);
21112
+ const itemIdsSignature = orderedItems.map((it, idx) => {
21113
+ const raw = it[dndItemIdField];
21114
+ return String(raw ?? `__idx_${idx}`);
21115
+ }).join("|");
21112
21116
  const itemIds = React80__namespace.default.useMemo(
21113
21117
  () => orderedItems.map((it, idx) => {
21114
21118
  const raw = it[dndItemIdField];
21115
21119
  return raw ?? `__idx_${idx}`;
21116
21120
  }),
21117
- [orderedItems, dndItemIdField]
21121
+ // eslint-disable-next-line react-hooks/exhaustive-deps
21122
+ [itemIdsSignature]
21118
21123
  );
21119
21124
  const zonesRef = React80__namespace.default.useRef(/* @__PURE__ */ new Map());
21120
21125
  const registerZone = React80__namespace.default.useCallback((zoneId2, meta2) => {
@@ -21123,10 +21128,6 @@ function useDataDnd(args) {
21123
21128
  const unregisterZone = React80__namespace.default.useCallback((zoneId2) => {
21124
21129
  zonesRef.current.delete(zoneId2);
21125
21130
  }, []);
21126
- const [overlayNode, setOverlayNode] = React80__namespace.default.useState(null);
21127
- const setOverlay = React80__namespace.default.useCallback((node) => {
21128
- setOverlayNode(node);
21129
- }, []);
21130
21131
  const zoneId = React80__namespace.default.useId();
21131
21132
  const ownGroup = dragGroup ?? accepts ?? zoneId;
21132
21133
  const meta = React80__namespace.default.useMemo(
@@ -21137,10 +21138,18 @@ function useDataDnd(args) {
21137
21138
  const target = isRoot ? null : parentRoot;
21138
21139
  if (!target) {
21139
21140
  zonesRef.current.set(zoneId, meta);
21140
- return () => zonesRef.current.delete(zoneId);
21141
+ dndLog.debug("zone:register:self", { zoneId, group: meta.group, itemCount: meta.itemIds.length, isRoot });
21142
+ return () => {
21143
+ zonesRef.current.delete(zoneId);
21144
+ dndLog.debug("zone:unregister:self", { zoneId, group: meta.group });
21145
+ };
21141
21146
  }
21142
21147
  target.registerZone(zoneId, meta);
21143
- return () => target.unregisterZone(zoneId);
21148
+ dndLog.debug("zone:register", { zoneId, group: meta.group, itemCount: meta.itemIds.length, dropEvent: meta.dropEvent, reorderEvent: meta.reorderEvent });
21149
+ return () => {
21150
+ target.unregisterZone(zoneId);
21151
+ dndLog.debug("zone:unregister", { zoneId, group: meta.group });
21152
+ };
21144
21153
  }, [parentRoot, isRoot, zoneId, meta]);
21145
21154
  const sensors = core$1.useSensors(
21146
21155
  core$1.useSensor(core$1.PointerSensor, { activationConstraint: { distance: 5 } }),
@@ -21148,10 +21157,18 @@ function useDataDnd(args) {
21148
21157
  );
21149
21158
  const collisionDetection = React80__namespace.default.useCallback((args2) => {
21150
21159
  const pointerCollisions = core$1.pointerWithin(args2);
21151
- if (pointerCollisions.length > 0) return pointerCollisions;
21160
+ if (pointerCollisions.length > 0) {
21161
+ dndLog.debug("collision:pointerWithin", { count: pointerCollisions.length, ids: pointerCollisions.map((c) => c.id) });
21162
+ return pointerCollisions;
21163
+ }
21152
21164
  const rectCollisions = core$1.rectIntersection(args2);
21153
- if (rectCollisions.length > 0) return rectCollisions;
21154
- return core$1.closestCorners(args2);
21165
+ if (rectCollisions.length > 0) {
21166
+ dndLog.debug("collision:rectIntersection", { count: rectCollisions.length, ids: rectCollisions.map((c) => c.id) });
21167
+ return rectCollisions;
21168
+ }
21169
+ const cornerCollisions = core$1.closestCorners(args2);
21170
+ dndLog.debug("collision:closestCorners", { count: cornerCollisions.length, ids: cornerCollisions.map((c) => c.id) });
21171
+ return cornerCollisions;
21155
21172
  }, []);
21156
21173
  const findZoneByItem = React80__namespace.default.useCallback(
21157
21174
  (id) => {
@@ -21174,22 +21191,52 @@ function useDataDnd(args) {
21174
21191
  const handleDragEnd = React80__namespace.default.useCallback(
21175
21192
  (event) => {
21176
21193
  const { active, over } = event;
21177
- if (!over) return;
21194
+ const allZones = Array.from(zonesRef.current.entries()).map(([id, m]) => ({ id, group: m.group, items: m.itemIds.length }));
21195
+ dndLog.debug("dragEnd:received", {
21196
+ activeId: active.id,
21197
+ overId: over?.id,
21198
+ overData: over?.data?.current,
21199
+ zones: allZones
21200
+ });
21201
+ if (!over) {
21202
+ dndLog.warn("dragEnd:abort:no-over", { activeId: active.id, reason: "no droppable under pointer at drop time \u2192 item snaps back" });
21203
+ return;
21204
+ }
21178
21205
  const sourceZone = findZoneByItem(active.id);
21179
21206
  const overData = over.data?.current;
21180
21207
  const targetGroup = overData?.dndGroup;
21181
- if (!sourceZone || !targetGroup) return;
21208
+ dndLog.debug("dragEnd:resolved", { sourceGroup: sourceZone?.group, targetGroup, overDataKeys: overData ? Object.keys(overData) : null });
21209
+ if (!sourceZone) {
21210
+ dndLog.warn("dragEnd:abort:no-source-zone", { activeId: active.id });
21211
+ return;
21212
+ }
21213
+ if (!targetGroup) {
21214
+ dndLog.warn("dragEnd:abort:no-target-group", { overId: over.id, overData });
21215
+ return;
21216
+ }
21182
21217
  const targetZone = findZoneByGroup(targetGroup);
21183
- if (!targetZone) return;
21218
+ if (!targetZone) {
21219
+ dndLog.warn("dragEnd:abort:target-zone-not-registered", { targetGroup });
21220
+ return;
21221
+ }
21184
21222
  if (sourceZone.group !== targetZone.group) {
21185
21223
  if (targetZone.dropEvent) {
21186
21224
  const newIndex2 = targetZone.itemIds.indexOf(over.id);
21225
+ dndLog.info("dragEnd:cross-container:emit", {
21226
+ event: targetZone.dropEvent,
21227
+ id: String(active.id),
21228
+ sourceGroup: sourceZone.group,
21229
+ targetGroup: targetZone.group,
21230
+ newIndex: newIndex2 === -1 ? targetZone.itemIds.length : newIndex2
21231
+ });
21187
21232
  eventBus.emit(targetZone.dropEvent, {
21188
21233
  id: String(active.id),
21189
21234
  sourceGroup: sourceZone.group,
21190
21235
  targetGroup: targetZone.group,
21191
21236
  newIndex: newIndex2 === -1 ? targetZone.itemIds.length : newIndex2
21192
21237
  });
21238
+ } else {
21239
+ dndLog.warn("dragEnd:cross-container:no-dropEvent-on-target", { targetGroup: targetZone.group });
21193
21240
  }
21194
21241
  return;
21195
21242
  }
@@ -21201,16 +21248,24 @@ function useDataDnd(args) {
21201
21248
  setLocalOrder(reordered);
21202
21249
  }
21203
21250
  if (sourceZone.reorderEvent) {
21251
+ dndLog.info("dragEnd:reorder:emit", {
21252
+ event: sourceZone.reorderEvent,
21253
+ id: String(active.id),
21254
+ oldIndex,
21255
+ newIndex
21256
+ });
21204
21257
  eventBus.emit(sourceZone.reorderEvent, {
21205
21258
  id: String(active.id),
21206
21259
  oldIndex,
21207
21260
  newIndex
21208
21261
  });
21262
+ } else {
21263
+ dndLog.debug("dragEnd:reorder:no-reorderEvent", { sourceGroup: sourceZone.group });
21209
21264
  }
21210
21265
  },
21211
21266
  [orderedItems, ownGroup, findZoneByItem, findZoneByGroup, eventBus]
21212
21267
  );
21213
- const overlaySink = isRoot ? setOverlay : parentRoot?.setOverlay;
21268
+ const sortableData = React80__namespace.default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
21214
21269
  const SortableItem = React80__namespace.default.useCallback(
21215
21270
  ({ id, children }) => {
21216
21271
  const {
@@ -21220,19 +21275,16 @@ function useDataDnd(args) {
21220
21275
  transform,
21221
21276
  transition,
21222
21277
  isDragging
21223
- } = sortable.useSortable({ id, data: { dndGroup: ownGroup } });
21224
- const childrenRef = React80__namespace.default.useRef(children);
21225
- childrenRef.current = children;
21226
- React80__namespace.default.useEffect(() => {
21227
- if (!isDragging || !overlaySink) return;
21228
- overlaySink(childrenRef.current);
21229
- return () => overlaySink(null);
21230
- }, [isDragging]);
21278
+ } = sortable.useSortable({ id, data: sortableData });
21231
21279
  const style = {
21232
21280
  transform: utilities.CSS.Transform.toString(transform),
21233
21281
  transition,
21234
21282
  opacity: isDragging ? 0.4 : 1,
21235
- cursor: enabled ? "grab" : void 0
21283
+ cursor: enabled ? "grab" : void 0,
21284
+ // Lift the dragged element above siblings so its visual movement
21285
+ // isn't hidden behind other cards / column boundaries.
21286
+ zIndex: isDragging ? 50 : void 0,
21287
+ position: isDragging ? "relative" : void 0
21236
21288
  };
21237
21289
  return /* @__PURE__ */ jsxRuntime.jsx(
21238
21290
  Box,
@@ -21246,73 +21298,91 @@ function useDataDnd(args) {
21246
21298
  }
21247
21299
  );
21248
21300
  },
21249
- [ownGroup, enabled, overlaySink]
21301
+ [sortableData, enabled]
21250
21302
  );
21251
21303
  const DropZoneShell = ({ children }) => {
21304
+ const droppableId = `dnd-zone-${zoneId}`;
21252
21305
  const { setNodeRef, isOver } = core$1.useDroppable({
21253
- id: `dnd-zone-${zoneId}`,
21254
- data: { dndGroup: ownGroup }
21306
+ id: droppableId,
21307
+ data: sortableData
21255
21308
  });
21309
+ React80__namespace.default.useEffect(() => {
21310
+ dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver });
21311
+ }, [droppableId, isOver]);
21256
21312
  return /* @__PURE__ */ jsxRuntime.jsx(
21257
21313
  Box,
21258
21314
  {
21259
21315
  ref: setNodeRef,
21260
21316
  "data-dnd-zone": ownGroup,
21317
+ "data-dnd-is-over": isOver ? "true" : "false",
21261
21318
  className: isOver ? "ring-2 ring-primary ring-offset-2 rounded-lg transition-all min-h-[3rem]" : "min-h-[3rem] rounded-lg transition-all",
21262
21319
  children
21263
21320
  }
21264
21321
  );
21265
21322
  };
21266
21323
  const rootContextValue = React80__namespace.default.useMemo(
21267
- () => ({ registerZone, unregisterZone, setOverlay }),
21268
- [registerZone, unregisterZone, setOverlay]
21324
+ () => ({ registerZone, unregisterZone }),
21325
+ [registerZone, unregisterZone]
21269
21326
  );
21327
+ const handleDragStart = React80__namespace.default.useCallback((event) => {
21328
+ const sourceZone = findZoneByItem(event.active.id);
21329
+ dndLog.info("dragStart", {
21330
+ activeId: event.active.id,
21331
+ activeData: event.active.data?.current,
21332
+ sourceGroup: sourceZone?.group,
21333
+ zoneCount: zonesRef.current.size
21334
+ });
21335
+ }, [findZoneByItem]);
21336
+ const handleDragOver = React80__namespace.default.useCallback((event) => {
21337
+ dndLog.debug("dragOver", {
21338
+ activeId: event.active.id,
21339
+ overId: event.over?.id,
21340
+ overData: event.over?.data?.current
21341
+ });
21342
+ }, []);
21343
+ const handleDragCancel = React80__namespace.default.useCallback((event) => {
21344
+ dndLog.warn("dragCancel", {
21345
+ activeId: event.active.id,
21346
+ reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
21347
+ });
21348
+ }, []);
21270
21349
  const wrapContainer = React80__namespace.default.useCallback(
21271
21350
  (children) => {
21272
21351
  if (!enabled) return children;
21273
21352
  const strategy = layout === "grid" ? sortable.rectSortingStrategy : sortable.verticalListSortingStrategy;
21274
- const clearOverlay = () => setOverlay(null);
21275
21353
  if (!isZone) {
21276
21354
  if (!isRoot) return children;
21277
- return /* @__PURE__ */ jsxRuntime.jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
21355
+ return /* @__PURE__ */ jsxRuntime.jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
21278
21356
  core$1.DndContext,
21279
21357
  {
21280
21358
  sensors,
21281
21359
  collisionDetection,
21282
- onDragEnd: (e) => {
21283
- handleDragEnd(e);
21284
- clearOverlay();
21285
- },
21286
- onDragCancel: clearOverlay,
21287
- children: [
21288
- children,
21289
- /* @__PURE__ */ jsxRuntime.jsx(core$1.DragOverlay, { children: overlayNode })
21290
- ]
21360
+ onDragStart: handleDragStart,
21361
+ onDragOver: handleDragOver,
21362
+ onDragEnd: handleDragEnd,
21363
+ onDragCancel: handleDragCancel,
21364
+ children
21291
21365
  }
21292
21366
  ) });
21293
21367
  }
21294
21368
  const inner = /* @__PURE__ */ jsxRuntime.jsx(DropZoneShell, { children: /* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items: itemIds, strategy, children }) });
21295
21369
  if (isRoot) {
21296
- return /* @__PURE__ */ jsxRuntime.jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
21370
+ return /* @__PURE__ */ jsxRuntime.jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
21297
21371
  core$1.DndContext,
21298
21372
  {
21299
21373
  sensors,
21300
21374
  collisionDetection,
21301
- onDragEnd: (e) => {
21302
- handleDragEnd(e);
21303
- clearOverlay();
21304
- },
21305
- onDragCancel: clearOverlay,
21306
- children: [
21307
- inner,
21308
- /* @__PURE__ */ jsxRuntime.jsx(core$1.DragOverlay, { children: overlayNode })
21309
- ]
21375
+ onDragStart: handleDragStart,
21376
+ onDragOver: handleDragOver,
21377
+ onDragEnd: handleDragEnd,
21378
+ onDragCancel: handleDragCancel,
21379
+ children: inner
21310
21380
  }
21311
21381
  ) });
21312
21382
  }
21313
21383
  return inner;
21314
21384
  },
21315
- [enabled, isZone, layout, sensors, collisionDetection, handleDragEnd, itemIds, isRoot, rootContextValue, overlayNode, setOverlay]
21385
+ [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEnd, handleDragCancel, itemIds, isRoot, rootContextValue]
21316
21386
  );
21317
21387
  return {
21318
21388
  enabled,
@@ -21322,12 +21392,13 @@ function useDataDnd(args) {
21322
21392
  orderedItems
21323
21393
  };
21324
21394
  }
21325
- var RootCtx;
21395
+ var dndLog, RootCtx;
21326
21396
  var init_useDataDnd = __esm({
21327
21397
  "components/molecules/useDataDnd.tsx"() {
21328
21398
  "use client";
21329
21399
  init_useEventBus();
21330
21400
  init_Box();
21401
+ dndLog = logger.createLogger("almadar:ui:dnd");
21331
21402
  RootCtx = React80__namespace.default.createContext(null);
21332
21403
  }
21333
21404
  });
@@ -36,7 +36,7 @@ import langToml from 'react-syntax-highlighter/dist/esm/languages/prism/toml.js'
36
36
  import langGo from 'react-syntax-highlighter/dist/esm/languages/prism/go.js';
37
37
  import langGraphql from 'react-syntax-highlighter/dist/esm/languages/prism/graphql.js';
38
38
  import { isCircuitEvent, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, isEntityCall, isInlineTrait } from '@almadar/core';
39
- import { useSensors, useSensor, PointerSensor, KeyboardSensor, pointerWithin, rectIntersection, closestCorners, DndContext, DragOverlay, useDroppable } from '@dnd-kit/core';
39
+ import { useSensors, useSensor, PointerSensor, KeyboardSensor, pointerWithin, rectIntersection, closestCorners, DndContext, useDroppable } from '@dnd-kit/core';
40
40
  import { sortableKeyboardCoordinates, arrayMove, useSortable, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
41
41
  import { CSS } from '@dnd-kit/utilities';
42
42
  import { Handle, Position } from '@xyflow/react';
@@ -21063,12 +21063,17 @@ function useDataDnd(args) {
21063
21063
  React80__default.useEffect(() => {
21064
21064
  setLocalOrder(null);
21065
21065
  }, [items]);
21066
+ const itemIdsSignature = orderedItems.map((it, idx) => {
21067
+ const raw = it[dndItemIdField];
21068
+ return String(raw ?? `__idx_${idx}`);
21069
+ }).join("|");
21066
21070
  const itemIds = React80__default.useMemo(
21067
21071
  () => orderedItems.map((it, idx) => {
21068
21072
  const raw = it[dndItemIdField];
21069
21073
  return raw ?? `__idx_${idx}`;
21070
21074
  }),
21071
- [orderedItems, dndItemIdField]
21075
+ // eslint-disable-next-line react-hooks/exhaustive-deps
21076
+ [itemIdsSignature]
21072
21077
  );
21073
21078
  const zonesRef = React80__default.useRef(/* @__PURE__ */ new Map());
21074
21079
  const registerZone = React80__default.useCallback((zoneId2, meta2) => {
@@ -21077,10 +21082,6 @@ function useDataDnd(args) {
21077
21082
  const unregisterZone = React80__default.useCallback((zoneId2) => {
21078
21083
  zonesRef.current.delete(zoneId2);
21079
21084
  }, []);
21080
- const [overlayNode, setOverlayNode] = React80__default.useState(null);
21081
- const setOverlay = React80__default.useCallback((node) => {
21082
- setOverlayNode(node);
21083
- }, []);
21084
21085
  const zoneId = React80__default.useId();
21085
21086
  const ownGroup = dragGroup ?? accepts ?? zoneId;
21086
21087
  const meta = React80__default.useMemo(
@@ -21091,10 +21092,18 @@ function useDataDnd(args) {
21091
21092
  const target = isRoot ? null : parentRoot;
21092
21093
  if (!target) {
21093
21094
  zonesRef.current.set(zoneId, meta);
21094
- return () => zonesRef.current.delete(zoneId);
21095
+ dndLog.debug("zone:register:self", { zoneId, group: meta.group, itemCount: meta.itemIds.length, isRoot });
21096
+ return () => {
21097
+ zonesRef.current.delete(zoneId);
21098
+ dndLog.debug("zone:unregister:self", { zoneId, group: meta.group });
21099
+ };
21095
21100
  }
21096
21101
  target.registerZone(zoneId, meta);
21097
- return () => target.unregisterZone(zoneId);
21102
+ dndLog.debug("zone:register", { zoneId, group: meta.group, itemCount: meta.itemIds.length, dropEvent: meta.dropEvent, reorderEvent: meta.reorderEvent });
21103
+ return () => {
21104
+ target.unregisterZone(zoneId);
21105
+ dndLog.debug("zone:unregister", { zoneId, group: meta.group });
21106
+ };
21098
21107
  }, [parentRoot, isRoot, zoneId, meta]);
21099
21108
  const sensors = useSensors(
21100
21109
  useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
@@ -21102,10 +21111,18 @@ function useDataDnd(args) {
21102
21111
  );
21103
21112
  const collisionDetection = React80__default.useCallback((args2) => {
21104
21113
  const pointerCollisions = pointerWithin(args2);
21105
- if (pointerCollisions.length > 0) return pointerCollisions;
21114
+ if (pointerCollisions.length > 0) {
21115
+ dndLog.debug("collision:pointerWithin", { count: pointerCollisions.length, ids: pointerCollisions.map((c) => c.id) });
21116
+ return pointerCollisions;
21117
+ }
21106
21118
  const rectCollisions = rectIntersection(args2);
21107
- if (rectCollisions.length > 0) return rectCollisions;
21108
- return closestCorners(args2);
21119
+ if (rectCollisions.length > 0) {
21120
+ dndLog.debug("collision:rectIntersection", { count: rectCollisions.length, ids: rectCollisions.map((c) => c.id) });
21121
+ return rectCollisions;
21122
+ }
21123
+ const cornerCollisions = closestCorners(args2);
21124
+ dndLog.debug("collision:closestCorners", { count: cornerCollisions.length, ids: cornerCollisions.map((c) => c.id) });
21125
+ return cornerCollisions;
21109
21126
  }, []);
21110
21127
  const findZoneByItem = React80__default.useCallback(
21111
21128
  (id) => {
@@ -21128,22 +21145,52 @@ function useDataDnd(args) {
21128
21145
  const handleDragEnd = React80__default.useCallback(
21129
21146
  (event) => {
21130
21147
  const { active, over } = event;
21131
- if (!over) return;
21148
+ const allZones = Array.from(zonesRef.current.entries()).map(([id, m]) => ({ id, group: m.group, items: m.itemIds.length }));
21149
+ dndLog.debug("dragEnd:received", {
21150
+ activeId: active.id,
21151
+ overId: over?.id,
21152
+ overData: over?.data?.current,
21153
+ zones: allZones
21154
+ });
21155
+ if (!over) {
21156
+ dndLog.warn("dragEnd:abort:no-over", { activeId: active.id, reason: "no droppable under pointer at drop time \u2192 item snaps back" });
21157
+ return;
21158
+ }
21132
21159
  const sourceZone = findZoneByItem(active.id);
21133
21160
  const overData = over.data?.current;
21134
21161
  const targetGroup = overData?.dndGroup;
21135
- if (!sourceZone || !targetGroup) return;
21162
+ dndLog.debug("dragEnd:resolved", { sourceGroup: sourceZone?.group, targetGroup, overDataKeys: overData ? Object.keys(overData) : null });
21163
+ if (!sourceZone) {
21164
+ dndLog.warn("dragEnd:abort:no-source-zone", { activeId: active.id });
21165
+ return;
21166
+ }
21167
+ if (!targetGroup) {
21168
+ dndLog.warn("dragEnd:abort:no-target-group", { overId: over.id, overData });
21169
+ return;
21170
+ }
21136
21171
  const targetZone = findZoneByGroup(targetGroup);
21137
- if (!targetZone) return;
21172
+ if (!targetZone) {
21173
+ dndLog.warn("dragEnd:abort:target-zone-not-registered", { targetGroup });
21174
+ return;
21175
+ }
21138
21176
  if (sourceZone.group !== targetZone.group) {
21139
21177
  if (targetZone.dropEvent) {
21140
21178
  const newIndex2 = targetZone.itemIds.indexOf(over.id);
21179
+ dndLog.info("dragEnd:cross-container:emit", {
21180
+ event: targetZone.dropEvent,
21181
+ id: String(active.id),
21182
+ sourceGroup: sourceZone.group,
21183
+ targetGroup: targetZone.group,
21184
+ newIndex: newIndex2 === -1 ? targetZone.itemIds.length : newIndex2
21185
+ });
21141
21186
  eventBus.emit(targetZone.dropEvent, {
21142
21187
  id: String(active.id),
21143
21188
  sourceGroup: sourceZone.group,
21144
21189
  targetGroup: targetZone.group,
21145
21190
  newIndex: newIndex2 === -1 ? targetZone.itemIds.length : newIndex2
21146
21191
  });
21192
+ } else {
21193
+ dndLog.warn("dragEnd:cross-container:no-dropEvent-on-target", { targetGroup: targetZone.group });
21147
21194
  }
21148
21195
  return;
21149
21196
  }
@@ -21155,16 +21202,24 @@ function useDataDnd(args) {
21155
21202
  setLocalOrder(reordered);
21156
21203
  }
21157
21204
  if (sourceZone.reorderEvent) {
21205
+ dndLog.info("dragEnd:reorder:emit", {
21206
+ event: sourceZone.reorderEvent,
21207
+ id: String(active.id),
21208
+ oldIndex,
21209
+ newIndex
21210
+ });
21158
21211
  eventBus.emit(sourceZone.reorderEvent, {
21159
21212
  id: String(active.id),
21160
21213
  oldIndex,
21161
21214
  newIndex
21162
21215
  });
21216
+ } else {
21217
+ dndLog.debug("dragEnd:reorder:no-reorderEvent", { sourceGroup: sourceZone.group });
21163
21218
  }
21164
21219
  },
21165
21220
  [orderedItems, ownGroup, findZoneByItem, findZoneByGroup, eventBus]
21166
21221
  );
21167
- const overlaySink = isRoot ? setOverlay : parentRoot?.setOverlay;
21222
+ const sortableData = React80__default.useMemo(() => ({ dndGroup: ownGroup }), [ownGroup]);
21168
21223
  const SortableItem = React80__default.useCallback(
21169
21224
  ({ id, children }) => {
21170
21225
  const {
@@ -21174,19 +21229,16 @@ function useDataDnd(args) {
21174
21229
  transform,
21175
21230
  transition,
21176
21231
  isDragging
21177
- } = useSortable({ id, data: { dndGroup: ownGroup } });
21178
- const childrenRef = React80__default.useRef(children);
21179
- childrenRef.current = children;
21180
- React80__default.useEffect(() => {
21181
- if (!isDragging || !overlaySink) return;
21182
- overlaySink(childrenRef.current);
21183
- return () => overlaySink(null);
21184
- }, [isDragging]);
21232
+ } = useSortable({ id, data: sortableData });
21185
21233
  const style = {
21186
21234
  transform: CSS.Transform.toString(transform),
21187
21235
  transition,
21188
21236
  opacity: isDragging ? 0.4 : 1,
21189
- cursor: enabled ? "grab" : void 0
21237
+ cursor: enabled ? "grab" : void 0,
21238
+ // Lift the dragged element above siblings so its visual movement
21239
+ // isn't hidden behind other cards / column boundaries.
21240
+ zIndex: isDragging ? 50 : void 0,
21241
+ position: isDragging ? "relative" : void 0
21190
21242
  };
21191
21243
  return /* @__PURE__ */ jsx(
21192
21244
  Box,
@@ -21200,73 +21252,91 @@ function useDataDnd(args) {
21200
21252
  }
21201
21253
  );
21202
21254
  },
21203
- [ownGroup, enabled, overlaySink]
21255
+ [sortableData, enabled]
21204
21256
  );
21205
21257
  const DropZoneShell = ({ children }) => {
21258
+ const droppableId = `dnd-zone-${zoneId}`;
21206
21259
  const { setNodeRef, isOver } = useDroppable({
21207
- id: `dnd-zone-${zoneId}`,
21208
- data: { dndGroup: ownGroup }
21260
+ id: droppableId,
21261
+ data: sortableData
21209
21262
  });
21263
+ React80__default.useEffect(() => {
21264
+ dndLog.debug("dropzone:isOver:change", { droppableId, group: ownGroup, isOver });
21265
+ }, [droppableId, isOver]);
21210
21266
  return /* @__PURE__ */ jsx(
21211
21267
  Box,
21212
21268
  {
21213
21269
  ref: setNodeRef,
21214
21270
  "data-dnd-zone": ownGroup,
21271
+ "data-dnd-is-over": isOver ? "true" : "false",
21215
21272
  className: isOver ? "ring-2 ring-primary ring-offset-2 rounded-lg transition-all min-h-[3rem]" : "min-h-[3rem] rounded-lg transition-all",
21216
21273
  children
21217
21274
  }
21218
21275
  );
21219
21276
  };
21220
21277
  const rootContextValue = React80__default.useMemo(
21221
- () => ({ registerZone, unregisterZone, setOverlay }),
21222
- [registerZone, unregisterZone, setOverlay]
21278
+ () => ({ registerZone, unregisterZone }),
21279
+ [registerZone, unregisterZone]
21223
21280
  );
21281
+ const handleDragStart = React80__default.useCallback((event) => {
21282
+ const sourceZone = findZoneByItem(event.active.id);
21283
+ dndLog.info("dragStart", {
21284
+ activeId: event.active.id,
21285
+ activeData: event.active.data?.current,
21286
+ sourceGroup: sourceZone?.group,
21287
+ zoneCount: zonesRef.current.size
21288
+ });
21289
+ }, [findZoneByItem]);
21290
+ const handleDragOver = React80__default.useCallback((event) => {
21291
+ dndLog.debug("dragOver", {
21292
+ activeId: event.active.id,
21293
+ overId: event.over?.id,
21294
+ overData: event.over?.data?.current
21295
+ });
21296
+ }, []);
21297
+ const handleDragCancel = React80__default.useCallback((event) => {
21298
+ dndLog.warn("dragCancel", {
21299
+ activeId: event.active.id,
21300
+ reason: "dnd-kit cancelled the drag (escape key, pointer interrupted, or external)"
21301
+ });
21302
+ }, []);
21224
21303
  const wrapContainer = React80__default.useCallback(
21225
21304
  (children) => {
21226
21305
  if (!enabled) return children;
21227
21306
  const strategy = layout === "grid" ? rectSortingStrategy : verticalListSortingStrategy;
21228
- const clearOverlay = () => setOverlay(null);
21229
21307
  if (!isZone) {
21230
21308
  if (!isRoot) return children;
21231
- return /* @__PURE__ */ jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxs(
21309
+ return /* @__PURE__ */ jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsx(
21232
21310
  DndContext,
21233
21311
  {
21234
21312
  sensors,
21235
21313
  collisionDetection,
21236
- onDragEnd: (e) => {
21237
- handleDragEnd(e);
21238
- clearOverlay();
21239
- },
21240
- onDragCancel: clearOverlay,
21241
- children: [
21242
- children,
21243
- /* @__PURE__ */ jsx(DragOverlay, { children: overlayNode })
21244
- ]
21314
+ onDragStart: handleDragStart,
21315
+ onDragOver: handleDragOver,
21316
+ onDragEnd: handleDragEnd,
21317
+ onDragCancel: handleDragCancel,
21318
+ children
21245
21319
  }
21246
21320
  ) });
21247
21321
  }
21248
21322
  const inner = /* @__PURE__ */ jsx(DropZoneShell, { children: /* @__PURE__ */ jsx(SortableContext, { items: itemIds, strategy, children }) });
21249
21323
  if (isRoot) {
21250
- return /* @__PURE__ */ jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsxs(
21324
+ return /* @__PURE__ */ jsx(RootCtx.Provider, { value: rootContextValue, children: /* @__PURE__ */ jsx(
21251
21325
  DndContext,
21252
21326
  {
21253
21327
  sensors,
21254
21328
  collisionDetection,
21255
- onDragEnd: (e) => {
21256
- handleDragEnd(e);
21257
- clearOverlay();
21258
- },
21259
- onDragCancel: clearOverlay,
21260
- children: [
21261
- inner,
21262
- /* @__PURE__ */ jsx(DragOverlay, { children: overlayNode })
21263
- ]
21329
+ onDragStart: handleDragStart,
21330
+ onDragOver: handleDragOver,
21331
+ onDragEnd: handleDragEnd,
21332
+ onDragCancel: handleDragCancel,
21333
+ children: inner
21264
21334
  }
21265
21335
  ) });
21266
21336
  }
21267
21337
  return inner;
21268
21338
  },
21269
- [enabled, isZone, layout, sensors, collisionDetection, handleDragEnd, itemIds, isRoot, rootContextValue, overlayNode, setOverlay]
21339
+ [enabled, isZone, layout, sensors, collisionDetection, handleDragStart, handleDragOver, handleDragEnd, handleDragCancel, itemIds, isRoot, rootContextValue]
21270
21340
  );
21271
21341
  return {
21272
21342
  enabled,
@@ -21276,12 +21346,13 @@ function useDataDnd(args) {
21276
21346
  orderedItems
21277
21347
  };
21278
21348
  }
21279
- var RootCtx;
21349
+ var dndLog, RootCtx;
21280
21350
  var init_useDataDnd = __esm({
21281
21351
  "components/molecules/useDataDnd.tsx"() {
21282
21352
  "use client";
21283
21353
  init_useEventBus();
21284
21354
  init_Box();
21355
+ dndLog = createLogger("almadar:ui:dnd");
21285
21356
  RootCtx = React80__default.createContext(null);
21286
21357
  }
21287
21358
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.50.4",
3
+ "version": "4.50.6",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [