@almadar/ui 4.50.22 → 4.51.1

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.
@@ -35,8 +35,8 @@ import langToml from 'react-syntax-highlighter/dist/esm/languages/prism/toml.js'
35
35
  import langGo from 'react-syntax-highlighter/dist/esm/languages/prism/go.js';
36
36
  import langGraphql from 'react-syntax-highlighter/dist/esm/languages/prism/graphql.js';
37
37
  import { isInlineTrait } from '@almadar/core';
38
- import { useSensors, useSensor, PointerSensor, KeyboardSensor, pointerWithin, rectIntersection, closestCorners, DndContext, useDroppable } from '@dnd-kit/core';
39
- import { sortableKeyboardCoordinates, useSortable, arrayMove, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
38
+ import { DndContext, useSensors, useSensor, PointerSensor, KeyboardSensor, useDroppable, pointerWithin, rectIntersection, closestCorners } from '@dnd-kit/core';
39
+ import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
40
40
  import { CSS } from '@dnd-kit/utilities';
41
41
  import { Handle, Position } from '@xyflow/react';
42
42
  import { useUISlots } from '@almadar/ui/context';
@@ -20027,6 +20027,31 @@ var init_DashboardLayout = __esm({
20027
20027
  NavLink.displayName = "NavLink";
20028
20028
  }
20029
20029
  });
20030
+ function useAlmadarDndSensors(withSortableKeyboard = true) {
20031
+ return useSensors(
20032
+ useSensor(PointerSensor, {
20033
+ activationConstraint: { distance: ALMADAR_DND_ACTIVATION_DISTANCE }
20034
+ }),
20035
+ useSensor(
20036
+ KeyboardSensor,
20037
+ withSortableKeyboard ? { coordinateGetter: sortableKeyboardCoordinates } : void 0
20038
+ )
20039
+ );
20040
+ }
20041
+ var ALMADAR_DND_ACTIVATION_DISTANCE, almadarDndCollisionDetection;
20042
+ var init_useAlmadarDndCollision = __esm({
20043
+ "hooks/useAlmadarDndCollision.ts"() {
20044
+ "use client";
20045
+ ALMADAR_DND_ACTIVATION_DISTANCE = 5;
20046
+ almadarDndCollisionDetection = (args) => {
20047
+ const pw = pointerWithin(args);
20048
+ if (pw.length > 0) return pw;
20049
+ const ri = rectIntersection(args);
20050
+ if (ri.length > 0) return ri;
20051
+ return closestCorners(args);
20052
+ };
20053
+ }
20054
+ });
20030
20055
  function useDataDnd(args) {
20031
20056
  const {
20032
20057
  dragGroup,
@@ -20122,25 +20147,8 @@ function useDataDnd(args) {
20122
20147
  dndLog.debug("zone:unregister", { zoneId, group: meta.group });
20123
20148
  };
20124
20149
  }, [parentRoot, isRoot, zoneId, meta]);
20125
- const sensors = useSensors(
20126
- useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
20127
- useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
20128
- );
20129
- const collisionDetection = React75__default.useCallback((args2) => {
20130
- const pointerCollisions = pointerWithin(args2);
20131
- if (pointerCollisions.length > 0) {
20132
- dndLog.debug("collision:pointerWithin", { count: pointerCollisions.length, ids: pointerCollisions.map((c) => c.id) });
20133
- return pointerCollisions;
20134
- }
20135
- const rectCollisions = rectIntersection(args2);
20136
- if (rectCollisions.length > 0) {
20137
- dndLog.debug("collision:rectIntersection", { count: rectCollisions.length, ids: rectCollisions.map((c) => c.id) });
20138
- return rectCollisions;
20139
- }
20140
- const cornerCollisions = closestCorners(args2);
20141
- dndLog.debug("collision:closestCorners", { count: cornerCollisions.length, ids: cornerCollisions.map((c) => c.id) });
20142
- return cornerCollisions;
20143
- }, []);
20150
+ const sensors = useAlmadarDndSensors(true);
20151
+ const collisionDetection = almadarDndCollisionDetection;
20144
20152
  const findZoneByItem = React75__default.useCallback(
20145
20153
  (id) => {
20146
20154
  for (const z of zonesRef.current.values()) {
@@ -20477,6 +20485,7 @@ var init_useDataDnd = __esm({
20477
20485
  "components/molecules/useDataDnd.tsx"() {
20478
20486
  "use client";
20479
20487
  init_useEventBus();
20488
+ init_useAlmadarDndCollision();
20480
20489
  init_Box();
20481
20490
  dndLog = createLogger("almadar:ui:dnd");
20482
20491
  RootCtx = React75__default.createContext(null);
@@ -0,0 +1,155 @@
1
+ /**
2
+ * useCanvasDnd — drag/drop primitives for the AVL canvas surface.
3
+ *
4
+ * Shares the sensor stack + collision waterfall with `useDataDnd` (both
5
+ * import from `hooks/useAlmadarDndCollision`); diverges on drop semantics
6
+ * because palette → canvas is a cursor-resolved tree insert, not a
7
+ * sortable-list reorder.
8
+ *
9
+ * Pointer-sensor based so it works inside React Flow nodes (the native
10
+ * HTML5 DnD path was swallowed by React Flow's pan/zoom handlers).
11
+ *
12
+ * Event contract (defaults — overridable via `CanvasDndProvider.onDrop`):
13
+ * - On drag start: emits `UI:DRAG_START` { kind, data }
14
+ * - On drag end: emits `UI:DRAG_END` { kind, data }
15
+ * - On 'pattern' drop: emits `UI:PATTERN_DROP` { patternType, containerNode, parentPath?, index? }
16
+ * - On 'behavior' drop: emits `UI:BEHAVIOR_DROP` { behaviorName, containerNode }
17
+ *
18
+ * The `onDrop` callback lets consumers route OTHER payload kinds (e.g.
19
+ * `'pattern-instance'` for in-canvas reorder of an existing pattern) to
20
+ * their own bus events or schema mutations. Reorder semantics belong in
21
+ * the consumer because they touch the SExpr tree directly — this primitive
22
+ * only resolves payload + target + cursor and hands them off.
23
+ */
24
+ import React from 'react';
25
+ import { useDraggable as dndKitUseDraggable } from '@dnd-kit/core';
26
+ import type { EventPayload } from '@almadar/core';
27
+ /**
28
+ * Drag kinds the canvas understands. Open-ended `string` so consumers can
29
+ * introduce new kinds (e.g. `'pattern-instance'` for in-canvas reorder of an
30
+ * existing pattern) without touching this file — the provider's defaults
31
+ * cover `'pattern'` and `'behavior'`; everything else routes through the
32
+ * consumer's `onDrop`.
33
+ */
34
+ export type CanvasDragKind = 'pattern' | 'behavior' | (string & {});
35
+ /**
36
+ * Payload carried by a draggable. For `'pattern'` tiles `data` is
37
+ * `{ type: string }`, for `'behavior'` tiles `{ name: string }`. For consumer
38
+ * kinds (e.g. `'pattern-instance'`) the shape is whatever the consumer
39
+ * agrees on with its own `onDrop`.
40
+ */
41
+ export interface CanvasDragPayload {
42
+ kind: CanvasDragKind;
43
+ data: EventPayload;
44
+ }
45
+ /**
46
+ * Orbital/trait/transition context a drop will mutate.
47
+ *
48
+ * Has an explicit `[key: string]: string | undefined` index signature so it
49
+ * structurally matches `EventPayload` — required because we pass the whole
50
+ * object as a field on the `UI:PATTERN_DROP` payload.
51
+ */
52
+ export interface CanvasContainerNode {
53
+ orbitalName?: string;
54
+ traitName?: string;
55
+ transitionEvent?: string;
56
+ [key: string]: string | undefined;
57
+ }
58
+ /** Drop-target metadata stored on each droppable's `data` field. */
59
+ export interface CanvasDropTarget {
60
+ /**
61
+ * `l1` = outer orbital frame (overview level).
62
+ * `l2` = render-ui slot inside an expanded orbital.
63
+ * `wrapper` = page-level catch-all fired when nothing inner caught the drop.
64
+ */
65
+ level: 'l1' | 'l2' | 'wrapper' | (string & {});
66
+ /** Full or partial container context this drop will mutate. */
67
+ containerNode: CanvasContainerNode;
68
+ /**
69
+ * Optional resolver called at drop time to derive `parentPath` + `index`
70
+ * from the pointer's final client position. L2 slots use this to walk the
71
+ * DOM under their `contentRef` and find the nearest `data-accepts-children`
72
+ * container plus the cursor-relative insertion index.
73
+ */
74
+ resolvePath?: (cursor: {
75
+ x: number;
76
+ y: number;
77
+ }) => {
78
+ parentPath: string;
79
+ index: number;
80
+ } | null;
81
+ }
82
+ export interface CanvasDropEvent {
83
+ payload: CanvasDragPayload;
84
+ target: CanvasDropTarget;
85
+ /** Final pointer client position, when dnd-kit could compute it. */
86
+ cursor: {
87
+ x: number;
88
+ y: number;
89
+ } | null;
90
+ /**
91
+ * Resolved insertion path/index from `target.resolvePath(cursor)`. Null
92
+ * when the target has no resolver or the cursor was unavailable.
93
+ */
94
+ resolved: {
95
+ parentPath: string;
96
+ index: number;
97
+ } | null;
98
+ }
99
+ export interface UseCanvasDraggableArgs {
100
+ /** Unique id (per-tile). dnd-kit uses this to track the active drag. */
101
+ id: string;
102
+ payload: CanvasDragPayload;
103
+ disabled?: boolean;
104
+ }
105
+ export interface UseCanvasDraggableResult {
106
+ setNodeRef: (node: HTMLElement | null) => void;
107
+ attributes: ReturnType<typeof dndKitUseDraggable>['attributes'];
108
+ listeners: ReturnType<typeof dndKitUseDraggable>['listeners'];
109
+ isDragging: boolean;
110
+ /** Spread on the tile — live transform + grab cursor + touch-action. */
111
+ style: React.CSSProperties;
112
+ }
113
+ export declare function useCanvasDraggable({ id, payload, disabled, }: UseCanvasDraggableArgs): UseCanvasDraggableResult;
114
+ export interface UseCanvasDroppableArgs {
115
+ id: string;
116
+ target: CanvasDropTarget;
117
+ /** Which drag kinds this zone accepts. Defaults to ['pattern','behavior']. */
118
+ accepts?: readonly CanvasDragKind[];
119
+ disabled?: boolean;
120
+ }
121
+ export interface UseCanvasDroppableResult {
122
+ setNodeRef: (node: HTMLElement | null) => void;
123
+ isOver: boolean;
124
+ }
125
+ export declare function useCanvasDroppable({ id, target, accepts, disabled, }: UseCanvasDroppableArgs): UseCanvasDroppableResult;
126
+ export interface CanvasDndProviderProps {
127
+ children: React.ReactNode;
128
+ /**
129
+ * Override the default drop behavior. The default emits `UI:PATTERN_DROP`
130
+ * / `UI:BEHAVIOR_DROP` based on payload kind. Pass an `onDrop` to route
131
+ * additional kinds (e.g. `'pattern-instance'` reorder) elsewhere, or to
132
+ * mutate schema directly without going through the bus.
133
+ *
134
+ * Return `true` from `onDrop` to suppress the default emit; return
135
+ * `false`/`undefined` to fall through to defaults after running your code.
136
+ */
137
+ onDrop?: (drop: CanvasDropEvent) => boolean | void;
138
+ /**
139
+ * Renders the floating preview that follows the cursor during a drag.
140
+ * @dnd-kit moves the source DOM node via CSS transform, which gets clipped
141
+ * by any ancestor with `overflow: hidden|auto|scroll` (e.g. a scrollable
142
+ * palette column). Returning a node here mounts a portal-attached overlay
143
+ * outside the clip so the user always sees what they're dragging.
144
+ *
145
+ * Receives the active payload (or null between drags) and returns the
146
+ * preview element — typically the same JSX the tile renders inline.
147
+ */
148
+ renderOverlay?: (payload: CanvasDragPayload | null) => React.ReactNode;
149
+ }
150
+ /**
151
+ * Wraps a canvas subtree in one DndContext + sensors + collision waterfall.
152
+ * Every `useCanvasDraggable` / `useCanvasDroppable` inside this provider
153
+ * participates in the same drag session.
154
+ */
155
+ export declare function CanvasDndProvider({ children, onDrop, renderOverlay, }: CanvasDndProviderProps): React.ReactElement;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Shared DnD primitives — sensor config + collision waterfall.
3
+ *
4
+ * Both the row-reorder primitive (`useDataDnd` in DataList/DataGrid) and the
5
+ * canvas-insert primitive (`useCanvasDnd` in AVL OrbPreviewNode) need
6
+ * identical pointer activation behavior and the same multi-container
7
+ * collision strategy. Centralizing them here keeps the two hooks from
8
+ * drifting and gives a single tunable spot when DnD ergonomics need a tweak.
9
+ *
10
+ * Activation: `distance: 5` on the PointerSensor so a small accidental
11
+ * tremor doesn't fire a drag — matches the threshold dnd-kit's own examples
12
+ * use for grab-and-drag interactions.
13
+ *
14
+ * Collision waterfall (run in order, first non-empty wins):
15
+ * 1. pointerWithin — catches the empty zones + clean hover-over-zone case
16
+ * 2. rectIntersection — falls back when the pointer is in margin/padding
17
+ * 3. closestCorners — last resort over the items in whichever zone won
18
+ */
19
+ import { type CollisionDetection, type SensorDescriptor, type SensorOptions } from '@dnd-kit/core';
20
+ export declare const ALMADAR_DND_ACTIVATION_DISTANCE = 5;
21
+ /**
22
+ * Returns the canonical sensor stack used by every Almadar DnD primitive.
23
+ *
24
+ * @param withSortableKeyboard - when true, the KeyboardSensor uses
25
+ * `sortableKeyboardCoordinates` (correct for sortable lists). Defaults to
26
+ * true so `useDataDnd` keeps its current behavior; `useCanvasDnd` doesn't
27
+ * need it (no sortable rows) so it can pass false.
28
+ */
29
+ export declare function useAlmadarDndSensors(withSortableKeyboard?: boolean): SensorDescriptor<SensorOptions>[];
30
+ /**
31
+ * Multi-container collision waterfall.
32
+ * pointerWithin → rectIntersection → closestCorners.
33
+ */
34
+ export declare const almadarDndCollisionDetection: CollisionDetection;
@@ -21318,6 +21318,31 @@ var init_DashboardLayout = __esm({
21318
21318
  NavLink.displayName = "NavLink";
21319
21319
  }
21320
21320
  });
21321
+ function useAlmadarDndSensors(withSortableKeyboard = true) {
21322
+ return core$1.useSensors(
21323
+ core$1.useSensor(core$1.PointerSensor, {
21324
+ activationConstraint: { distance: ALMADAR_DND_ACTIVATION_DISTANCE }
21325
+ }),
21326
+ core$1.useSensor(
21327
+ core$1.KeyboardSensor,
21328
+ withSortableKeyboard ? { coordinateGetter: sortable.sortableKeyboardCoordinates } : void 0
21329
+ )
21330
+ );
21331
+ }
21332
+ var ALMADAR_DND_ACTIVATION_DISTANCE, almadarDndCollisionDetection;
21333
+ var init_useAlmadarDndCollision = __esm({
21334
+ "hooks/useAlmadarDndCollision.ts"() {
21335
+ "use client";
21336
+ ALMADAR_DND_ACTIVATION_DISTANCE = 5;
21337
+ almadarDndCollisionDetection = (args) => {
21338
+ const pw = core$1.pointerWithin(args);
21339
+ if (pw.length > 0) return pw;
21340
+ const ri = core$1.rectIntersection(args);
21341
+ if (ri.length > 0) return ri;
21342
+ return core$1.closestCorners(args);
21343
+ };
21344
+ }
21345
+ });
21321
21346
  function useDataDnd(args) {
21322
21347
  const {
21323
21348
  dragGroup,
@@ -21413,25 +21438,8 @@ function useDataDnd(args) {
21413
21438
  dndLog.debug("zone:unregister", { zoneId, group: meta.group });
21414
21439
  };
21415
21440
  }, [parentRoot, isRoot, zoneId, meta]);
21416
- const sensors = core$1.useSensors(
21417
- core$1.useSensor(core$1.PointerSensor, { activationConstraint: { distance: 5 } }),
21418
- core$1.useSensor(core$1.KeyboardSensor, { coordinateGetter: sortable.sortableKeyboardCoordinates })
21419
- );
21420
- const collisionDetection = React81__namespace.default.useCallback((args2) => {
21421
- const pointerCollisions = core$1.pointerWithin(args2);
21422
- if (pointerCollisions.length > 0) {
21423
- dndLog.debug("collision:pointerWithin", { count: pointerCollisions.length, ids: pointerCollisions.map((c) => c.id) });
21424
- return pointerCollisions;
21425
- }
21426
- const rectCollisions = core$1.rectIntersection(args2);
21427
- if (rectCollisions.length > 0) {
21428
- dndLog.debug("collision:rectIntersection", { count: rectCollisions.length, ids: rectCollisions.map((c) => c.id) });
21429
- return rectCollisions;
21430
- }
21431
- const cornerCollisions = core$1.closestCorners(args2);
21432
- dndLog.debug("collision:closestCorners", { count: cornerCollisions.length, ids: cornerCollisions.map((c) => c.id) });
21433
- return cornerCollisions;
21434
- }, []);
21441
+ const sensors = useAlmadarDndSensors(true);
21442
+ const collisionDetection = almadarDndCollisionDetection;
21435
21443
  const findZoneByItem = React81__namespace.default.useCallback(
21436
21444
  (id) => {
21437
21445
  for (const z of zonesRef.current.values()) {
@@ -21768,6 +21776,7 @@ var init_useDataDnd = __esm({
21768
21776
  "components/molecules/useDataDnd.tsx"() {
21769
21777
  "use client";
21770
21778
  init_useEventBus();
21779
+ init_useAlmadarDndCollision();
21771
21780
  init_Box();
21772
21781
  dndLog = logger.createLogger("almadar:ui:dnd");
21773
21782
  RootCtx = React81__namespace.default.createContext(null);
@@ -36,8 +36,8 @@ 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 { isInlineTrait } from '@almadar/core';
39
- import { useSensors, useSensor, PointerSensor, KeyboardSensor, pointerWithin, rectIntersection, closestCorners, DndContext, useDroppable } from '@dnd-kit/core';
40
- import { sortableKeyboardCoordinates, useSortable, arrayMove, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
39
+ import { DndContext, pointerWithin, rectIntersection, closestCorners, useSensors, useSensor, PointerSensor, KeyboardSensor, useDroppable } from '@dnd-kit/core';
40
+ import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
41
41
  import { CSS } from '@dnd-kit/utilities';
42
42
  import { Handle, Position } from '@xyflow/react';
43
43
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -21272,6 +21272,31 @@ var init_DashboardLayout = __esm({
21272
21272
  NavLink.displayName = "NavLink";
21273
21273
  }
21274
21274
  });
21275
+ function useAlmadarDndSensors(withSortableKeyboard = true) {
21276
+ return useSensors(
21277
+ useSensor(PointerSensor, {
21278
+ activationConstraint: { distance: ALMADAR_DND_ACTIVATION_DISTANCE }
21279
+ }),
21280
+ useSensor(
21281
+ KeyboardSensor,
21282
+ withSortableKeyboard ? { coordinateGetter: sortableKeyboardCoordinates } : void 0
21283
+ )
21284
+ );
21285
+ }
21286
+ var ALMADAR_DND_ACTIVATION_DISTANCE, almadarDndCollisionDetection;
21287
+ var init_useAlmadarDndCollision = __esm({
21288
+ "hooks/useAlmadarDndCollision.ts"() {
21289
+ "use client";
21290
+ ALMADAR_DND_ACTIVATION_DISTANCE = 5;
21291
+ almadarDndCollisionDetection = (args) => {
21292
+ const pw = pointerWithin(args);
21293
+ if (pw.length > 0) return pw;
21294
+ const ri = rectIntersection(args);
21295
+ if (ri.length > 0) return ri;
21296
+ return closestCorners(args);
21297
+ };
21298
+ }
21299
+ });
21275
21300
  function useDataDnd(args) {
21276
21301
  const {
21277
21302
  dragGroup,
@@ -21367,25 +21392,8 @@ function useDataDnd(args) {
21367
21392
  dndLog.debug("zone:unregister", { zoneId, group: meta.group });
21368
21393
  };
21369
21394
  }, [parentRoot, isRoot, zoneId, meta]);
21370
- const sensors = useSensors(
21371
- useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
21372
- useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
21373
- );
21374
- const collisionDetection = React81__default.useCallback((args2) => {
21375
- const pointerCollisions = pointerWithin(args2);
21376
- if (pointerCollisions.length > 0) {
21377
- dndLog.debug("collision:pointerWithin", { count: pointerCollisions.length, ids: pointerCollisions.map((c) => c.id) });
21378
- return pointerCollisions;
21379
- }
21380
- const rectCollisions = rectIntersection(args2);
21381
- if (rectCollisions.length > 0) {
21382
- dndLog.debug("collision:rectIntersection", { count: rectCollisions.length, ids: rectCollisions.map((c) => c.id) });
21383
- return rectCollisions;
21384
- }
21385
- const cornerCollisions = closestCorners(args2);
21386
- dndLog.debug("collision:closestCorners", { count: cornerCollisions.length, ids: cornerCollisions.map((c) => c.id) });
21387
- return cornerCollisions;
21388
- }, []);
21395
+ const sensors = useAlmadarDndSensors(true);
21396
+ const collisionDetection = almadarDndCollisionDetection;
21389
21397
  const findZoneByItem = React81__default.useCallback(
21390
21398
  (id) => {
21391
21399
  for (const z of zonesRef.current.values()) {
@@ -21722,6 +21730,7 @@ var init_useDataDnd = __esm({
21722
21730
  "components/molecules/useDataDnd.tsx"() {
21723
21731
  "use client";
21724
21732
  init_useEventBus();
21733
+ init_useAlmadarDndCollision();
21725
21734
  init_Box();
21726
21735
  dndLog = createLogger("almadar:ui:dnd");
21727
21736
  RootCtx = React81__default.createContext(null);
@@ -21087,6 +21087,31 @@ var init_DashboardLayout = __esm({
21087
21087
  NavLink.displayName = "NavLink";
21088
21088
  }
21089
21089
  });
21090
+ function useAlmadarDndSensors(withSortableKeyboard = true) {
21091
+ return core$1.useSensors(
21092
+ core$1.useSensor(core$1.PointerSensor, {
21093
+ activationConstraint: { distance: ALMADAR_DND_ACTIVATION_DISTANCE }
21094
+ }),
21095
+ core$1.useSensor(
21096
+ core$1.KeyboardSensor,
21097
+ withSortableKeyboard ? { coordinateGetter: sortable.sortableKeyboardCoordinates } : void 0
21098
+ )
21099
+ );
21100
+ }
21101
+ var ALMADAR_DND_ACTIVATION_DISTANCE, almadarDndCollisionDetection;
21102
+ var init_useAlmadarDndCollision = __esm({
21103
+ "hooks/useAlmadarDndCollision.ts"() {
21104
+ "use client";
21105
+ ALMADAR_DND_ACTIVATION_DISTANCE = 5;
21106
+ almadarDndCollisionDetection = (args) => {
21107
+ const pw = core$1.pointerWithin(args);
21108
+ if (pw.length > 0) return pw;
21109
+ const ri = core$1.rectIntersection(args);
21110
+ if (ri.length > 0) return ri;
21111
+ return core$1.closestCorners(args);
21112
+ };
21113
+ }
21114
+ });
21090
21115
  function useDataDnd(args) {
21091
21116
  const {
21092
21117
  dragGroup,
@@ -21182,25 +21207,8 @@ function useDataDnd(args) {
21182
21207
  dndLog.debug("zone:unregister", { zoneId, group: meta.group });
21183
21208
  };
21184
21209
  }, [parentRoot, isRoot, zoneId, meta]);
21185
- const sensors = core$1.useSensors(
21186
- core$1.useSensor(core$1.PointerSensor, { activationConstraint: { distance: 5 } }),
21187
- core$1.useSensor(core$1.KeyboardSensor, { coordinateGetter: sortable.sortableKeyboardCoordinates })
21188
- );
21189
- const collisionDetection = React80__namespace.default.useCallback((args2) => {
21190
- const pointerCollisions = core$1.pointerWithin(args2);
21191
- if (pointerCollisions.length > 0) {
21192
- dndLog.debug("collision:pointerWithin", { count: pointerCollisions.length, ids: pointerCollisions.map((c) => c.id) });
21193
- return pointerCollisions;
21194
- }
21195
- const rectCollisions = core$1.rectIntersection(args2);
21196
- if (rectCollisions.length > 0) {
21197
- dndLog.debug("collision:rectIntersection", { count: rectCollisions.length, ids: rectCollisions.map((c) => c.id) });
21198
- return rectCollisions;
21199
- }
21200
- const cornerCollisions = core$1.closestCorners(args2);
21201
- dndLog.debug("collision:closestCorners", { count: cornerCollisions.length, ids: cornerCollisions.map((c) => c.id) });
21202
- return cornerCollisions;
21203
- }, []);
21210
+ const sensors = useAlmadarDndSensors(true);
21211
+ const collisionDetection = almadarDndCollisionDetection;
21204
21212
  const findZoneByItem = React80__namespace.default.useCallback(
21205
21213
  (id) => {
21206
21214
  for (const z of zonesRef.current.values()) {
@@ -21537,6 +21545,7 @@ var init_useDataDnd = __esm({
21537
21545
  "components/molecules/useDataDnd.tsx"() {
21538
21546
  "use client";
21539
21547
  init_useEventBus();
21548
+ init_useAlmadarDndCollision();
21540
21549
  init_Box();
21541
21550
  dndLog = logger.createLogger("almadar:ui:dnd");
21542
21551
  RootCtx = React80__namespace.default.createContext(null);
@@ -36,8 +36,8 @@ 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, useDroppable } from '@dnd-kit/core';
40
- import { sortableKeyboardCoordinates, useSortable, arrayMove, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
39
+ import { DndContext, pointerWithin, rectIntersection, closestCorners, useSensors, useSensor, PointerSensor, KeyboardSensor, useDroppable } from '@dnd-kit/core';
40
+ import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
41
41
  import { CSS } from '@dnd-kit/utilities';
42
42
  import { Handle, Position } from '@xyflow/react';
43
43
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -21041,6 +21041,31 @@ var init_DashboardLayout = __esm({
21041
21041
  NavLink.displayName = "NavLink";
21042
21042
  }
21043
21043
  });
21044
+ function useAlmadarDndSensors(withSortableKeyboard = true) {
21045
+ return useSensors(
21046
+ useSensor(PointerSensor, {
21047
+ activationConstraint: { distance: ALMADAR_DND_ACTIVATION_DISTANCE }
21048
+ }),
21049
+ useSensor(
21050
+ KeyboardSensor,
21051
+ withSortableKeyboard ? { coordinateGetter: sortableKeyboardCoordinates } : void 0
21052
+ )
21053
+ );
21054
+ }
21055
+ var ALMADAR_DND_ACTIVATION_DISTANCE, almadarDndCollisionDetection;
21056
+ var init_useAlmadarDndCollision = __esm({
21057
+ "hooks/useAlmadarDndCollision.ts"() {
21058
+ "use client";
21059
+ ALMADAR_DND_ACTIVATION_DISTANCE = 5;
21060
+ almadarDndCollisionDetection = (args) => {
21061
+ const pw = pointerWithin(args);
21062
+ if (pw.length > 0) return pw;
21063
+ const ri = rectIntersection(args);
21064
+ if (ri.length > 0) return ri;
21065
+ return closestCorners(args);
21066
+ };
21067
+ }
21068
+ });
21044
21069
  function useDataDnd(args) {
21045
21070
  const {
21046
21071
  dragGroup,
@@ -21136,25 +21161,8 @@ function useDataDnd(args) {
21136
21161
  dndLog.debug("zone:unregister", { zoneId, group: meta.group });
21137
21162
  };
21138
21163
  }, [parentRoot, isRoot, zoneId, meta]);
21139
- const sensors = useSensors(
21140
- useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
21141
- useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
21142
- );
21143
- const collisionDetection = React80__default.useCallback((args2) => {
21144
- const pointerCollisions = pointerWithin(args2);
21145
- if (pointerCollisions.length > 0) {
21146
- dndLog.debug("collision:pointerWithin", { count: pointerCollisions.length, ids: pointerCollisions.map((c) => c.id) });
21147
- return pointerCollisions;
21148
- }
21149
- const rectCollisions = rectIntersection(args2);
21150
- if (rectCollisions.length > 0) {
21151
- dndLog.debug("collision:rectIntersection", { count: rectCollisions.length, ids: rectCollisions.map((c) => c.id) });
21152
- return rectCollisions;
21153
- }
21154
- const cornerCollisions = closestCorners(args2);
21155
- dndLog.debug("collision:closestCorners", { count: cornerCollisions.length, ids: cornerCollisions.map((c) => c.id) });
21156
- return cornerCollisions;
21157
- }, []);
21164
+ const sensors = useAlmadarDndSensors(true);
21165
+ const collisionDetection = almadarDndCollisionDetection;
21158
21166
  const findZoneByItem = React80__default.useCallback(
21159
21167
  (id) => {
21160
21168
  for (const z of zonesRef.current.values()) {
@@ -21491,6 +21499,7 @@ var init_useDataDnd = __esm({
21491
21499
  "components/molecules/useDataDnd.tsx"() {
21492
21500
  "use client";
21493
21501
  init_useEventBus();
21502
+ init_useAlmadarDndCollision();
21494
21503
  init_Box();
21495
21504
  dndLog = createLogger("almadar:ui:dnd");
21496
21505
  RootCtx = React80__default.createContext(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.50.22",
3
+ "version": "4.51.1",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [