@almadar/ui 4.50.21 → 4.51.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.
@@ -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);
@@ -34144,6 +34153,7 @@ var init_Form = __esm({
34144
34153
  submitLabel,
34145
34154
  cancelLabel,
34146
34155
  showCancel,
34156
+ showSubmit = true,
34147
34157
  title,
34148
34158
  submitEvent = "SAVE",
34149
34159
  cancelEvent = "CANCEL",
@@ -34630,8 +34640,8 @@ var init_Form = __esm({
34630
34640
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
34631
34641
  schemaFields,
34632
34642
  children,
34633
- (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "pt-4", children: [
34634
- /* @__PURE__ */ jsx(
34643
+ (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && (showSubmit || shouldShowCancel) && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "pt-4", children: [
34644
+ showSubmit && /* @__PURE__ */ jsx(
34635
34645
  Button,
34636
34646
  {
34637
34647
  type: "submit",
@@ -0,0 +1,144 @@
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
+ /**
140
+ * Wraps a canvas subtree in one DndContext + sensors + collision waterfall.
141
+ * Every `useCanvasDraggable` / `useCanvasDroppable` inside this provider
142
+ * participates in the same drag session.
143
+ */
144
+ export declare function CanvasDndProvider({ children, onDrop }: CanvasDndProviderProps): React.ReactElement;
@@ -176,6 +176,8 @@ export interface FormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement
176
176
  cancelLabel?: string;
177
177
  /** Show cancel button (defaults to true for schema forms) */
178
178
  showCancel?: boolean;
179
+ /** Show submit button (defaults to true for schema forms). Set false when a parent atom owns the submit action externally (e.g. wizard footers). */
180
+ showSubmit?: boolean;
179
181
  /** Form title (used by ModalSlot to extract title) */
180
182
  title?: string;
181
183
  /** Event to dispatch on successful submit (defaults to 'SAVE') */
@@ -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);
@@ -34950,6 +34959,7 @@ var init_Form = __esm({
34950
34959
  submitLabel,
34951
34960
  cancelLabel,
34952
34961
  showCancel,
34962
+ showSubmit = true,
34953
34963
  title,
34954
34964
  submitEvent = "SAVE",
34955
34965
  cancelEvent = "CANCEL",
@@ -35436,8 +35446,8 @@ var init_Form = __esm({
35436
35446
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
35437
35447
  schemaFields,
35438
35448
  children,
35439
- (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "pt-4", children: [
35440
- /* @__PURE__ */ jsxRuntime.jsx(
35449
+ (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && (showSubmit || shouldShowCancel) && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "pt-4", children: [
35450
+ showSubmit && /* @__PURE__ */ jsxRuntime.jsx(
35441
35451
  Button,
35442
35452
  {
35443
35453
  type: "submit",
@@ -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);
@@ -34904,6 +34913,7 @@ var init_Form = __esm({
34904
34913
  submitLabel,
34905
34914
  cancelLabel,
34906
34915
  showCancel,
34916
+ showSubmit = true,
34907
34917
  title,
34908
34918
  submitEvent = "SAVE",
34909
34919
  cancelEvent = "CANCEL",
@@ -35390,8 +35400,8 @@ var init_Form = __esm({
35390
35400
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
35391
35401
  schemaFields,
35392
35402
  children,
35393
- (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "pt-4", children: [
35394
- /* @__PURE__ */ jsx(
35403
+ (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && (showSubmit || shouldShowCancel) && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "pt-4", children: [
35404
+ showSubmit && /* @__PURE__ */ jsx(
35395
35405
  Button,
35396
35406
  {
35397
35407
  type: "submit",
@@ -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);
@@ -34517,6 +34526,7 @@ var init_Form = __esm({
34517
34526
  submitLabel,
34518
34527
  cancelLabel,
34519
34528
  showCancel,
34529
+ showSubmit = true,
34520
34530
  title,
34521
34531
  submitEvent = "SAVE",
34522
34532
  cancelEvent = "CANCEL",
@@ -35003,8 +35013,8 @@ var init_Form = __esm({
35003
35013
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
35004
35014
  schemaFields,
35005
35015
  children,
35006
- (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "pt-4", children: [
35007
- /* @__PURE__ */ jsxRuntime.jsx(
35016
+ (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && (showSubmit || shouldShowCancel) && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "pt-4", children: [
35017
+ showSubmit && /* @__PURE__ */ jsxRuntime.jsx(
35008
35018
  Button,
35009
35019
  {
35010
35020
  type: "submit",
@@ -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);
@@ -34471,6 +34480,7 @@ var init_Form = __esm({
34471
34480
  submitLabel,
34472
34481
  cancelLabel,
34473
34482
  showCancel,
34483
+ showSubmit = true,
34474
34484
  title,
34475
34485
  submitEvent = "SAVE",
34476
34486
  cancelEvent = "CANCEL",
@@ -34957,8 +34967,8 @@ var init_Form = __esm({
34957
34967
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
34958
34968
  schemaFields,
34959
34969
  children,
34960
- (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "pt-4", children: [
34961
- /* @__PURE__ */ jsx(
34970
+ (schemaFields && schemaFields.length > 0 || sectionElements && sectionElements.length > 0) && (showSubmit || shouldShowCancel) && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "pt-4", children: [
34971
+ showSubmit && /* @__PURE__ */ jsx(
34962
34972
  Button,
34963
34973
  {
34964
34974
  type: "submit",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.50.21",
3
+ "version": "4.51.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [