ngx-kit 9.0.1 → 9.2.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.
package/README.md CHANGED
@@ -1,3 +1,3 @@
1
- # NgxInput
1
+ # NGX-KIT
2
2
 
3
3
  ## Comming Soon...
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Design goals
4
4
 
5
- The package uses Angular 22 signals and Pointer Events. Dragging and resizing remain transform/pointer based. Drop-list sorting is snapshot-based and driven by `translate3d`, like Angular CDK Drag & Drop.
5
+ The package uses Angular 22 signals and Pointer Events. Dragging and resizing remain transform/pointer based. DOM access, event listeners, computed styles and animation frames are resolved from each host element's own `ownerDocument` / `defaultView`, so same-origin iframe documents can host independent builder canvases. Drop-list sorting is snapshot-based and driven by `translate3d`, like Angular CDK Drag & Drop.
6
6
 
7
7
  ### Professional drop-list behavior
8
8
 
@@ -10,8 +10,8 @@ When a drag starts inside a drop list:
10
10
 
11
11
  - the real item remains in its original DOM position as a hidden layout anchor;
12
12
  - exactly one placeholder preserves the item's layout space;
13
- - a fully styled visual clone is moved to `document.body`, so overflow/scroll containers cannot clip the drag;
14
- - the body preview copies computed styles recursively, including CSS custom properties and descendant styles;
13
+ - a lightweight visual clone is moved to the source element's `ownerDocument.body`, so overflow/scroll containers cannot clip the drag;
14
+ - preview styles are not recursively copied; provide a custom `NgxDragPreview` when the preview needs application-specific styling;
15
15
  - entering, leaving and switching lists always disposes the previous placeholder before creating the next one;
16
16
  - sorting is a pure function of the pointer and a geometry snapshot taken when the drag enters a list, so it is fully reversible (see below);
17
17
  - `previousIndex` is captured from the origin list and `currentIndex` is the final placeholder position;
@@ -22,7 +22,7 @@ When a drag starts inside a drop list:
22
22
 
23
23
  ### How sorting works
24
24
 
25
- Per pointer position the engine answers *"if I release here, where is the item inserted?"*:
25
+ Per pointer position the engine answers _"if I release here, where is the item inserted?"_:
26
26
 
27
27
  1. **Snapshot.** When a drag enters a list, the rectangles of its items (with the dragged item's placeholder in its slot) are measured once. They are kept valid across page/parent/nested scrolling by shifting the pointer by the scroll delta, so pointer moves perform no layout reads.
28
28
  2. **Resolve.** `resolveInsertionIndex(pointer, snapshot)` counts how many siblings precede the pointer. Orientation is derived from the rectangles (row clustering and the order DOM siblings actually land on screen), so column, row, `flex` (incl. `wrap`, `*-reverse`), `grid`, inline/inline-block, floats, RTL and LTR all use one algorithm; `flex-direction`/`direction` are only a fallback for lists of fewer than two items. A small proportional hysteresis (10 % of the sibling's size, max 6 px) prevents flicker on a mid-line.
@@ -38,7 +38,8 @@ The list the drag started in keeps its original slot (a hole) while the pointer
38
38
  - Nested lists: the innermost connected list under the pointer wins; lists inside the dragged element are never targets.
39
39
  - Hit testing only counts the visible (clip-aware) part of a list.
40
40
  - Custom placeholder templates (`NgxPlaceholder`) are not rendered by the engine (pre-existing).
41
- - Scaled (`transform: scale`) ancestors are not compensated.
41
+ - Nested 2D transformed ancestors, especially `transform: scale(...)` and CSS `zoom`, are accounted for when converting viewport pointer deltas, scroll deltas, resize deltas and FLIP/sort offsets into local CSS transform values. Drag vector conversion also handles 2D rotation/skew matrices; resize hit zones remain axis-aligned and should not be used on rotated handles. CSS `perspective` / 3D transform chains are not supported.
42
+ - Each iframe is an independent drag/drop document and coordinate space. The library uses the dragged element's `ownerDocument` and `defaultView`; dragging between documents/iframes (especially cross-origin frames) is not supported.
42
43
 
43
44
  ## Basic drop list
44
45
 
@@ -73,3 +74,39 @@ The emitted event follows the familiar `previousIndex/currentIndex` model, makin
73
74
  ```
74
75
 
75
76
  Resize is pointer based, supports all eight directions, minimum/maximum constraints and optional pixel snapping.
77
+
78
+ ## Page-builder usage (palette in the page, canvas in an `<iframe>`)
79
+
80
+ ```html
81
+ <div NgxDropListGroup>
82
+ <div NgxDropList> <!-- palette (host document) -->
83
+ <div NgxDraggable *ngFor="...">Hero</div>
84
+ </div>
85
+ <!-- inside the iframe's document, rendered by the same Angular app: -->
86
+ <main NgxDropList placeholderFit="stretch" (drop)="onDrop($event)">
87
+ <section NgxDraggable [noDragSelector]="'[data-no-drag], [contenteditable]'">…</section>
88
+ </main>
89
+ </div>
90
+ ```
91
+
92
+ What the engine handles for a same-origin iframe canvas:
93
+
94
+ - pointer capture keeps `pointermove/up` flowing while the pointer is over the iframe; the pointer is converted between the two viewports (iframe offset, border and `transform: scale()` on the `<iframe>` element);
95
+ - scroll and resize listeners are attached to **every** document that hosts a candidate list, so scrolling the canvas (wheel, scrollbar, auto-scroll) re-resolves the insertion point under a still pointer;
96
+ - auto-scroll scrolls the iframe's own scroller using the pointer converted into the iframe's coordinates (and `<html>`/`<body>` scrollers use the window viewport);
97
+ - only the visible part of the iframe counts as "over the list" (a tall canvas does not extend below the iframe), including iframes without `<!doctype>`;
98
+ - the placeholder is sized in the list's own CSS pixels (correct under scaled/zoomed ancestors and scaled iframes). `placeholderFit="stretch"` makes it fill the list's cross axis (a 150px palette tile in a full-width canvas).
99
+
100
+ Editing inside blocks:
101
+
102
+ - `[noDragSelector]` (default `[data-no-drag]`) and `[ignoreInteractive]` keep text caret / buttons / inputs usable;
103
+ - arrow keys only move a draggable/resizable when the element itself has focus (not when typed in an input or `contenteditable` inside it);
104
+ - the `click` the browser fires right after a real drag is swallowed (no accidental select / link follow).
105
+
106
+ Exporting: the directives add `ngx-*` classes, `tabindex="0"`, `aria-grabbed` and `touch-action:none`. Use `getCleanHtml(canvasEl)` (or `stripEditorArtifacts(clone)`) when saving the page.
107
+
108
+ Known limits: cross-origin iframes (no coordinate access), iframes clipped by an `overflow:hidden` ancestor in the host (not clip-tested), `touch-action:none` on draggables blocks touch scrolling that starts on a block (use `dragHandle`), the sorting preview of items with different collapsing margins is approximated until drop, `transform` on a rotated ancestor for resize hit-zones.
109
+
110
+ ## Tests
111
+
112
+ `tests/browser/*.spec.ts` run in real Chromium (Playwright + vitest, set `CHROMIUM_PATH`); `tests/browser/page-builder.spec.ts` covers palette → iframe canvas, iframe scroll/auto-scroll, scaled iframes, editing inside blocks, resizing and export cleanup.