forty-cdk 0.2.0 → 0.3.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/accordion/README.md +122 -0
- package/aspect-ratio/README.md +76 -0
- package/avatar/README.md +100 -0
- package/breadcrumbs/README.md +49 -0
- package/breakpoints/README.md +81 -0
- package/button/README.md +49 -0
- package/calendar/README.md +458 -0
- package/carousel/README.md +358 -0
- package/checkbox/README.md +146 -0
- package/combobox/README.md +535 -0
- package/context-menu/README.md +139 -0
- package/date-field/README.md +184 -0
- package/date-picker/README.md +338 -0
- package/dialog/README.md +388 -0
- package/disclosure/README.md +114 -0
- package/drag-drop/README.md +359 -0
- package/drawer/README.md +560 -0
- package/dropdown-menu/README.md +176 -0
- package/fesm2022/forty-cdk-accordion.mjs +348 -0
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
- package/fesm2022/forty-cdk-avatar.mjs +308 -0
- package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
- package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
- package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
- package/fesm2022/forty-cdk-button.mjs +134 -0
- package/fesm2022/forty-cdk-button.mjs.map +1 -0
- package/fesm2022/forty-cdk-calendar.mjs +2034 -0
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
- package/fesm2022/forty-cdk-carousel.mjs +968 -0
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
- package/fesm2022/forty-cdk-checkbox.mjs +226 -0
- package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-combobox.mjs +2596 -0
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
- package/fesm2022/forty-cdk-context-menu.mjs +413 -0
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-core.mjs +9022 -0
- package/fesm2022/forty-cdk-core.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-field.mjs +744 -0
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-dialog.mjs +707 -0
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
- package/fesm2022/forty-cdk-disclosure.mjs +190 -0
- package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
- package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
- package/fesm2022/forty-cdk-drawer.mjs +1641 -0
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-field.mjs +425 -0
- package/fesm2022/forty-cdk-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-fieldset.mjs +164 -0
- package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
- package/fesm2022/forty-cdk-file-upload.mjs +221 -0
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
- package/fesm2022/forty-cdk-hover-card.mjs +496 -0
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
- package/fesm2022/forty-cdk-input.mjs +274 -0
- package/fesm2022/forty-cdk-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +1279 -0
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-menu.mjs +1439 -0
- package/fesm2022/forty-cdk-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-menubar.mjs +787 -0
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
- package/fesm2022/forty-cdk-meter.mjs +211 -0
- package/fesm2022/forty-cdk-meter.mjs.map +1 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-number-input.mjs +559 -0
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-otp-input.mjs +527 -0
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-pagination.mjs +323 -0
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
- package/fesm2022/forty-cdk-popover.mjs +698 -0
- package/fesm2022/forty-cdk-popover.mjs.map +1 -0
- package/fesm2022/forty-cdk-progress.mjs +226 -0
- package/fesm2022/forty-cdk-progress.mjs.map +1 -0
- package/fesm2022/forty-cdk-radio-group.mjs +378 -0
- package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
- package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
- package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
- package/fesm2022/forty-cdk-search.mjs +205 -0
- package/fesm2022/forty-cdk-search.mjs.map +1 -0
- package/fesm2022/forty-cdk-select.mjs +1661 -0
- package/fesm2022/forty-cdk-select.mjs.map +1 -0
- package/fesm2022/forty-cdk-separator.mjs +82 -0
- package/fesm2022/forty-cdk-separator.mjs.map +1 -0
- package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
- package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
- package/fesm2022/forty-cdk-slider.mjs +803 -0
- package/fesm2022/forty-cdk-slider.mjs.map +1 -0
- package/fesm2022/forty-cdk-stepper.mjs +886 -0
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
- package/fesm2022/forty-cdk-switch.mjs +137 -0
- package/fesm2022/forty-cdk-switch.mjs.map +1 -0
- package/fesm2022/forty-cdk-table.mjs +1518 -0
- package/fesm2022/forty-cdk-table.mjs.map +1 -0
- package/fesm2022/forty-cdk-tabs.mjs +400 -0
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-field.mjs +593 -0
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-toast.mjs +1153 -0
- package/fesm2022/forty-cdk-toast.mjs.map +1 -0
- package/fesm2022/forty-cdk-toggle.mjs +516 -0
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
- package/fesm2022/forty-cdk-toolbar.mjs +374 -0
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
- package/fesm2022/forty-cdk-tooltip.mjs +672 -0
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
- package/fesm2022/forty-cdk-tree.mjs +2007 -0
- package/fesm2022/forty-cdk-tree.mjs.map +1 -0
- package/fesm2022/forty-cdk-virtualization.mjs +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk.mjs +0 -43310
- package/fesm2022/forty-cdk.mjs.map +1 -1
- package/field/README.md +97 -0
- package/fieldset/README.md +86 -0
- package/file-upload/README.md +73 -0
- package/hover-card/README.md +171 -0
- package/input/README.md +156 -0
- package/listbox/README.md +424 -0
- package/menu/README.md +181 -0
- package/menubar/README.md +140 -0
- package/meter/README.md +128 -0
- package/navigation-menu/README.md +253 -0
- package/number-input/README.md +171 -0
- package/otp-input/README.md +198 -0
- package/package.json +213 -1
- package/pagination/README.md +61 -0
- package/pane-resizer/README.md +136 -0
- package/popover/README.md +262 -0
- package/progress/README.md +115 -0
- package/radio-group/README.md +129 -0
- package/scroll-area/README.md +184 -0
- package/search/README.md +42 -0
- package/select/README.md +488 -0
- package/separator/README.md +84 -0
- package/signal-forms/README.md +72 -0
- package/slider/README.md +152 -0
- package/stepper/README.md +292 -0
- package/switch/README.md +116 -0
- package/table/README.md +769 -0
- package/tabs/README.md +130 -0
- package/time-field/README.md +157 -0
- package/time-picker/README.md +172 -0
- package/toast/README.md +398 -0
- package/toggle/README.md +224 -0
- package/toolbar/README.md +109 -0
- package/tooltip/README.md +274 -0
- package/tree/README.md +708 -0
- package/types/forty-cdk-accordion.d.ts +242 -0
- package/types/forty-cdk-aspect-ratio.d.ts +59 -0
- package/types/forty-cdk-avatar.d.ts +133 -0
- package/types/forty-cdk-breadcrumbs.d.ts +92 -0
- package/types/forty-cdk-breakpoints.d.ts +141 -0
- package/types/forty-cdk-button.d.ts +80 -0
- package/types/forty-cdk-calendar.d.ts +914 -0
- package/types/forty-cdk-carousel.d.ts +530 -0
- package/types/forty-cdk-checkbox.d.ts +141 -0
- package/types/forty-cdk-combobox.d.ts +1259 -0
- package/types/forty-cdk-context-menu.d.ts +313 -0
- package/types/forty-cdk-core.d.ts +5774 -0
- package/types/forty-cdk-date-field.d.ts +307 -0
- package/types/forty-cdk-date-picker.d.ts +622 -0
- package/types/forty-cdk-dialog.d.ts +546 -0
- package/types/forty-cdk-disclosure.d.ts +127 -0
- package/types/forty-cdk-drag-drop.d.ts +456 -0
- package/types/forty-cdk-drawer.d.ts +871 -0
- package/types/forty-cdk-dropdown-menu.d.ts +242 -0
- package/types/forty-cdk-field.d.ts +236 -0
- package/types/forty-cdk-fieldset.d.ts +119 -0
- package/types/forty-cdk-file-upload.d.ts +124 -0
- package/types/forty-cdk-hover-card.d.ts +320 -0
- package/types/forty-cdk-input.d.ts +169 -0
- package/types/forty-cdk-internationalized-date.d.ts +1 -1
- package/types/forty-cdk-listbox.d.ts +513 -0
- package/types/forty-cdk-menu.d.ts +629 -0
- package/types/forty-cdk-menubar.d.ts +451 -0
- package/types/forty-cdk-meter.d.ts +122 -0
- package/types/forty-cdk-navigation-menu.d.ts +514 -0
- package/types/forty-cdk-number-input.d.ts +319 -0
- package/types/forty-cdk-otp-input.d.ts +248 -0
- package/types/forty-cdk-pagination.d.ts +214 -0
- package/types/forty-cdk-pane-resizer.d.ts +145 -0
- package/types/forty-cdk-popover.d.ts +509 -0
- package/types/forty-cdk-progress.d.ts +143 -0
- package/types/forty-cdk-radio-group.d.ts +222 -0
- package/types/forty-cdk-scroll-area.d.ts +258 -0
- package/types/forty-cdk-search.d.ts +142 -0
- package/types/forty-cdk-select.d.ts +899 -0
- package/types/forty-cdk-separator.d.ts +59 -0
- package/types/forty-cdk-signal-forms.d.ts +58 -0
- package/types/forty-cdk-slider.d.ts +379 -0
- package/types/forty-cdk-stepper.d.ts +650 -0
- package/types/forty-cdk-switch.d.ts +87 -0
- package/types/forty-cdk-table.d.ts +723 -0
- package/types/forty-cdk-tabs.d.ts +235 -0
- package/types/forty-cdk-time-field.d.ts +307 -0
- package/types/forty-cdk-time-picker.d.ts +578 -0
- package/types/forty-cdk-toast.d.ts +598 -0
- package/types/forty-cdk-toggle.d.ts +310 -0
- package/types/forty-cdk-toolbar.d.ts +217 -0
- package/types/forty-cdk-tooltip.d.ts +436 -0
- package/types/forty-cdk-tree.d.ts +688 -0
- package/types/forty-cdk.d.ts +1 -19743
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# Drag & Drop
|
|
2
|
+
|
|
3
|
+
Headless accessible drag-and-drop for sortable lists and cross-list item
|
|
4
|
+
transfers. Supports both keyboard and pointer (mouse / touch / pen) dragging.
|
|
5
|
+
|
|
6
|
+
For repositioning an arbitrary element (no list, no reorder) — e.g. dragging a
|
|
7
|
+
whole dialog around by its header — see [`[forFreeDrag]`](#free-drag-forfreedrag).
|
|
8
|
+
|
|
9
|
+
## Keyboard model
|
|
10
|
+
|
|
11
|
+
| State | Key | Action |
|
|
12
|
+
| ------ | ----------- | ------------------------------------------ |
|
|
13
|
+
| Idle | Arrow keys | Move roving focus between items |
|
|
14
|
+
| Idle | Home / End | Jump to first / last item |
|
|
15
|
+
| Idle | Space/Enter | **Lift** the focused item |
|
|
16
|
+
| Lifted | Arrow keys | Step the logical drop position |
|
|
17
|
+
| Lifted | Space/Enter | **Drop** (commits and emits `(dragDrop)`) |
|
|
18
|
+
| Lifted | Escape | **Cancel** (no event, focus stays on item) |
|
|
19
|
+
|
|
20
|
+
Arrow direction follows the list's `orientation` and respects RTL via `dir`. In
|
|
21
|
+
`orientation="mixed"` every arrow key steps the lifted item linearly in DOM order.
|
|
22
|
+
|
|
23
|
+
## Pointer dragging
|
|
24
|
+
|
|
25
|
+
When a pointer drag starts, the dragged item stays in place and reflects
|
|
26
|
+
`data-dragging` (style it as a dimmed gap). A fixed-position clone of the item
|
|
27
|
+
follows the pointer as the default preview. The clone carries
|
|
28
|
+
`data-for-drag-preview` and `aria-hidden="true"`.
|
|
29
|
+
|
|
30
|
+
### Auto-scroll
|
|
31
|
+
|
|
32
|
+
While a pointer drag is in flight, `[forDropList]` automatically scrolls the
|
|
33
|
+
nearest scrollable container (the list itself, a scrollable ancestor, or the
|
|
34
|
+
viewport) toward whichever edge the pointer approaches. Speed scales with
|
|
35
|
+
proximity — the closer the pointer is to the edge, the faster the scroll.
|
|
36
|
+
|
|
37
|
+
The feature is **on by default**. Opt out with `[autoScroll]="false"`:
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<ul forDropList [autoScroll]="false" (dragDrop)="onDrop($event)">
|
|
41
|
+
…
|
|
42
|
+
</ul>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Configure the edge zone and max speed via `provideForDragDropDefaults`:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
providers: [
|
|
49
|
+
provideForDragDropDefaults({
|
|
50
|
+
autoScrollEdgeSize: 80,
|
|
51
|
+
autoScrollMaxSpeed: 24,
|
|
52
|
+
}),
|
|
53
|
+
];
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Keyboard dragging is unaffected. SSR-safe — no-op when there is no browser window.
|
|
57
|
+
|
|
58
|
+
### Optional drag handle
|
|
59
|
+
|
|
60
|
+
Apply `[forDragHandle]` on a child element of `[forDraggable]` to restrict
|
|
61
|
+
where a pointer gesture may start. Once any handle is present on an item,
|
|
62
|
+
pointer drags may only begin from within that handle — keyboard dragging is
|
|
63
|
+
unaffected.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<li forDraggable [dragData]="item">
|
|
67
|
+
<span forDragHandle aria-hidden="true">::</span>
|
|
68
|
+
{{ item.label }}
|
|
69
|
+
</li>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Custom preview & placeholder
|
|
73
|
+
|
|
74
|
+
Place `<ng-template forDragPreview>` and/or `<ng-template forDragPlaceholder>` as direct
|
|
75
|
+
children of `[forDraggable]` to override the default drag visuals during a pointer drag.
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<li forDraggable [dragData]="item">
|
|
79
|
+
{{ item.label }}
|
|
80
|
+
<ng-template forDragPreview>
|
|
81
|
+
<div class="my-preview">{{ item.label }}</div>
|
|
82
|
+
</ng-template>
|
|
83
|
+
<ng-template forDragPlaceholder>
|
|
84
|
+
<div class="my-placeholder"></div>
|
|
85
|
+
</ng-template>
|
|
86
|
+
</li>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- **`[forDragPreview]`** replaces the default cloned floating element that follows the pointer
|
|
90
|
+
during a pointer drag. Applies to pointer drags only; keyboard dragging is unaffected.
|
|
91
|
+
- **`[forDragPlaceholder]`** renders in the dragged item's slot while a pointer drag is in flight.
|
|
92
|
+
The dragged item's host is hidden (`display: none`) and the placeholder template occupies its
|
|
93
|
+
space, preserving the gap. When the drag ends (drop or cancel) the placeholder is removed and
|
|
94
|
+
the item is revealed again. Keyboard dragging keeps the default behaviour — the lifted item
|
|
95
|
+
stays in place reflecting `data-dragging`, and no placeholder is rendered.
|
|
96
|
+
|
|
97
|
+
### Live-sort placeholder
|
|
98
|
+
|
|
99
|
+
Add `[liveSort]="true"` to `[forDropList]` to make the `[forDragPlaceholder]` follow the live
|
|
100
|
+
resolved drop index during a pointer drag — within the list and across connected lists — so
|
|
101
|
+
siblings part to reveal where the item will land. When `false` (the default), the placeholder
|
|
102
|
+
stays in the dragged item's source slot.
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<ul forDropList [liveSort]="true" (dragDrop)="onDrop($event)">
|
|
106
|
+
@for (item of items(); track item.id) {
|
|
107
|
+
<li forDraggable [dragData]="item">
|
|
108
|
+
{{ item.label }}
|
|
109
|
+
<ng-template forDragPlaceholder>
|
|
110
|
+
<div class="my-placeholder"></div>
|
|
111
|
+
</ng-template>
|
|
112
|
+
</li>
|
|
113
|
+
}
|
|
114
|
+
</ul>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`[liveSort]` has no visible effect without a `[forDragPlaceholder]` template, and has no effect
|
|
118
|
+
on keyboard dragging.
|
|
119
|
+
|
|
120
|
+
### Boundary & axis lock
|
|
121
|
+
|
|
122
|
+
`[forDropList]` supports two opt-in visual constraints on the pointer-drag preview. Both are
|
|
123
|
+
`null` by default and have **no effect on keyboard dragging** (which has no floating preview).
|
|
124
|
+
Neither changes the resolved drop index — they constrain the visual preview only.
|
|
125
|
+
|
|
126
|
+
**`[boundary]`** — confine the preview within a boundary element. Accepts an `HTMLElement` or
|
|
127
|
+
a CSS selector string resolved via `closest()` from the list host. The preview box is clamped so
|
|
128
|
+
it stays fully inside the boundary. When the boundary is smaller than the preview on an axis,
|
|
129
|
+
the preview is pinned to the boundary's start edge on that axis.
|
|
130
|
+
|
|
131
|
+
**`[lockAxis]`** — constrain movement to one axis. `'x'` keeps the preview at its lift-time `y`
|
|
132
|
+
(horizontal-only movement); `'y'` keeps it at its lift-time `x` (vertical-only movement).
|
|
133
|
+
|
|
134
|
+
Both inputs may be combined:
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<div class="container" #container>
|
|
138
|
+
<ul forDropList [boundary]="container" lockAxis="x" (dragDrop)="onDrop($event)">
|
|
139
|
+
@for (item of items(); track item.id) {
|
|
140
|
+
<li forDraggable [dragData]="item">{{ item.label }}</li>
|
|
141
|
+
}
|
|
142
|
+
</ul>
|
|
143
|
+
</div>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
String selector form (resolved via `closest()` on the list host):
|
|
147
|
+
|
|
148
|
+
```html
|
|
149
|
+
<ul forDropList [boundary]="'.my-scroller'" (dragDrop)="onDrop($event)">
|
|
150
|
+
…
|
|
151
|
+
</ul>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Reorder & settle animations
|
|
155
|
+
|
|
156
|
+
Add `[animateReorder]="true"` to `[forDropList]` to animate committed drops. When enabled:
|
|
157
|
+
|
|
158
|
+
- **FLIP reorder** — displaced sibling items transition smoothly from their old positions to their
|
|
159
|
+
new ones instead of snapping.
|
|
160
|
+
- **Drop-settle** — on a pointer drag, the floating preview transitions from its release position
|
|
161
|
+
into the final item slot before it is removed.
|
|
162
|
+
|
|
163
|
+
Both are opt-in and fully skipped under `prefers-reduced-motion: reduce`. They work for both
|
|
164
|
+
keyboard and pointer drags. The library publishes the styling hooks below — duration / easing are
|
|
165
|
+
always provided by the consumer via CSS; the library imposes none.
|
|
166
|
+
|
|
167
|
+
```html
|
|
168
|
+
<ul forDropList [animateReorder]="true" (dragDrop)="onDrop($event)">
|
|
169
|
+
…
|
|
170
|
+
</ul>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```css
|
|
174
|
+
[forDraggable][data-drag-animating] {
|
|
175
|
+
transition: transform 200ms ease;
|
|
176
|
+
}
|
|
177
|
+
[data-for-drag-preview][data-settling] {
|
|
178
|
+
transition: transform 200ms ease;
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
With no such CSS, `animateReorder` is a graceful no-op — transforms clear instantly and the
|
|
183
|
+
preview is destroyed promptly.
|
|
184
|
+
|
|
185
|
+
## Orientation
|
|
186
|
+
|
|
187
|
+
`[forDropList]` resolves the live drop index along its `orientation`:
|
|
188
|
+
|
|
189
|
+
- **`"vertical"`** (default) — a stacked column; the index is resolved by the pointer's `y`.
|
|
190
|
+
- **`"horizontal"`** — a single row; the index is resolved by the pointer's `x` (RTL-aware).
|
|
191
|
+
- **`"mixed"`** — a wrapping grid (`flex-wrap` / CSS grid) of **uniformly-sized** items. The index
|
|
192
|
+
is resolved in 2D, so an item dragged across a wrapped row lands in the slot under the pointer's
|
|
193
|
+
row **and** column instead of mis-resolving to the nearest single-axis slot. A `"mixed"` list that
|
|
194
|
+
happens to render as a single row or single column resolves identically to `"horizontal"` /
|
|
195
|
+
`"vertical"`.
|
|
196
|
+
|
|
197
|
+
```html
|
|
198
|
+
<ul class="grid" forDropList orientation="mixed" (dragDrop)="onDrop($event)">
|
|
199
|
+
@for (item of items(); track item.id) {
|
|
200
|
+
<li forDraggable [dragData]="item">{{ item.label }}</li>
|
|
201
|
+
}
|
|
202
|
+
</ul>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
```css
|
|
206
|
+
.grid {
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-wrap: wrap;
|
|
209
|
+
gap: 8px;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
A wrapper can default the orientation for every `[forDropList]` in its scope via the
|
|
214
|
+
`FOR_DROP_LIST_DEFAULT_ORIENTATION` token (the same mechanism `ForTableColumnReorder` uses for
|
|
215
|
+
`"horizontal"`):
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
providers: [{ provide: FOR_DROP_LIST_DEFAULT_ORIENTATION, useValue: 'mixed' }];
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`"mixed"` targets regular grids of uniformly-sized items; variable-size / masonry layouts are out of
|
|
222
|
+
scope. `[animateReorder]` (FLIP) reflows by DOM order and needs no change in mixed mode.
|
|
223
|
+
|
|
224
|
+
## Free drag (`[forFreeDrag]`)
|
|
225
|
+
|
|
226
|
+
`[forFreeDrag]` repositions its host element (or a resolved `rootElement`) by pointer drag, with
|
|
227
|
+
**no `[forDropList]` required** — it never commits a reorder, it just moves the element around via a
|
|
228
|
+
CSS `transform: translate(...)`. It is the standalone counterpart to a sortable list item: a
|
|
229
|
+
pointer-driven way to move an element freely, with optional root-element retargeting, a confinement
|
|
230
|
+
boundary, axis locking, and a controllable position (see the inputs below).
|
|
231
|
+
|
|
232
|
+
```html
|
|
233
|
+
<!-- move the whole dialog by its header -->
|
|
234
|
+
<div class="dialog">
|
|
235
|
+
<header forFreeDrag rootElement=".dialog" boundary=".viewport">Drag me</header>
|
|
236
|
+
…
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<!-- or with an explicit handle and a controllable position -->
|
|
240
|
+
<div forFreeDrag [(position)]="pos" boundary=".viewport">
|
|
241
|
+
<span forDragHandle aria-hidden="true">⠿</span>
|
|
242
|
+
…
|
|
243
|
+
</div>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Input | Type | Default | Meaning |
|
|
247
|
+
| ------------- | ------------------------------- | ------------- | -------------------------------------------------------------------------------------------------- |
|
|
248
|
+
| `disabled` | `boolean` | `false` | When true, the element can't be dragged (it stays focusable; the transform doesn't change). |
|
|
249
|
+
| `rootElement` | `HTMLElement \| string \| null` | `null` | The element actually moved. A `closest()` selector resolves an ancestor. `null` moves the host. |
|
|
250
|
+
| `boundary` | `HTMLElement \| string \| null` | `null` | Confine the moved element fully inside this element (or `closest()` selector). `null` = unbounded. |
|
|
251
|
+
| `lockAxis` | `'x' \| 'y' \| null` | `null` | Constrain movement to one axis. `'x'` pins lift-time `y`, `'y'` pins lift-time `x`. |
|
|
252
|
+
| `position` | `model<{ x; y }>` | `{ x:0,y:0 }` | Two-way translate offset (px) from the element's natural position. Controllable / restorable. |
|
|
253
|
+
|
|
254
|
+
| Output | Payload | Fires |
|
|
255
|
+
| ----------- | ---------- | ---------------------------------------------------------------------------------------------- |
|
|
256
|
+
| `dragStart` | `{ x; y }` | When a pointer drag starts (the lift-time position). |
|
|
257
|
+
| `dragMove` | `{ x; y }` | On every armed move, with the live position. |
|
|
258
|
+
| `dragEnd` | `{ x; y }` | When the drag ends (commit keeps the position; cancel/Escape restores the lift-time snapshot). |
|
|
259
|
+
|
|
260
|
+
`[forDragHandle]` works inside `[forFreeDrag]` exactly as it does inside `[forDraggable]`: once any
|
|
261
|
+
handle is present, a pointer drag may only start from within a handle.
|
|
262
|
+
|
|
263
|
+
**Accessibility.** Free-drag is **pointer-only** — there is no WAI-ARIA pattern for "reposition an
|
|
264
|
+
element", so it owns no role or ARIA state. The consumer is responsible
|
|
265
|
+
for keeping the moved element fully usable at its default position (e.g. a repositionable dialog must
|
|
266
|
+
still be operable by keyboard); dragging is a pointer convenience, not the only way to use it.
|
|
267
|
+
|
|
268
|
+
## Data attributes
|
|
269
|
+
|
|
270
|
+
| Attribute | Element | Meaning |
|
|
271
|
+
| ----------------------- | ----------------- | ------------------------------------------------------------------------------------ |
|
|
272
|
+
| `data-orientation` | `[forDropList]` | `"vertical"`, `"horizontal"`, or `"mixed"` |
|
|
273
|
+
| `data-disabled` | both | Present when the item or list is disabled |
|
|
274
|
+
| `data-disabled` | `[forFreeDrag]` | Present when the free-drag element is disabled |
|
|
275
|
+
| `data-dragging` | `[forDropList]` | Present while a drag originates here |
|
|
276
|
+
| `data-dragging` | `[forDraggable]` | Present while this item is lifted |
|
|
277
|
+
| `data-dragging` | `[forFreeDrag]` | Present while a free-drag pointer gesture is armed |
|
|
278
|
+
| `data-drag-over` | `[forDropList]` | Present while this list is the drop target |
|
|
279
|
+
| `data-drag-handle` | `[forDragHandle]` | Present on every registered drag handle |
|
|
280
|
+
| `data-for-drag-preview` | preview element | Present on the default clone preview **or** the `[forDragPreview]` template wrapper |
|
|
281
|
+
| `data-drag-animating` | `[forDraggable]` | Present while the item's FLIP reorder transition plays (requires `[animateReorder]`) |
|
|
282
|
+
| `data-settling` | preview element | Present while the drop-settle transition plays (requires `[animateReorder]`) |
|
|
283
|
+
|
|
284
|
+
## Sortable list
|
|
285
|
+
|
|
286
|
+
```html
|
|
287
|
+
<ul forDropList (dragDrop)="onDrop($event)">
|
|
288
|
+
@for (item of items(); track item.id) {
|
|
289
|
+
<li forDraggable [dragData]="item">{{ item.label }}</li>
|
|
290
|
+
}
|
|
291
|
+
</ul>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
onDrop(event: ForDragDropEvent<MyItem>): void {
|
|
296
|
+
this.items.set(
|
|
297
|
+
moveItemInArray(this.items(), event.previousIndex, event.currentIndex),
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Two-list transfer with `forDropListGroup`
|
|
303
|
+
|
|
304
|
+
```html
|
|
305
|
+
<div forDropListGroup>
|
|
306
|
+
<ul forDropList (dragDrop)="onDrop($event)">
|
|
307
|
+
@for (item of active(); track item.id) {
|
|
308
|
+
<li forDraggable [dragData]="item">{{ item.label }}</li>
|
|
309
|
+
}
|
|
310
|
+
</ul>
|
|
311
|
+
<ul forDropList (dragDrop)="onDrop($event)">
|
|
312
|
+
@for (item of archived(); track item.id) {
|
|
313
|
+
<li forDraggable [dragData]="item">{{ item.label }}</li>
|
|
314
|
+
}
|
|
315
|
+
</ul>
|
|
316
|
+
</div>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
onDrop(event: ForDragDropEvent<MyItem>): void {
|
|
321
|
+
if (event.previousContainer === event.container) {
|
|
322
|
+
this.updateList(event.container, (arr) =>
|
|
323
|
+
moveItemInArray(arr, event.previousIndex, event.currentIndex),
|
|
324
|
+
);
|
|
325
|
+
} else {
|
|
326
|
+
const result = transferArrayItem(
|
|
327
|
+
event.previousContainer === this.activeCtx ? this.active() : this.archived(),
|
|
328
|
+
event.container === this.activeCtx ? this.active() : this.archived(),
|
|
329
|
+
event.previousIndex,
|
|
330
|
+
event.currentIndex,
|
|
331
|
+
);
|
|
332
|
+
this.active.set(result.from);
|
|
333
|
+
this.archived.set(result.to);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Two-list transfer with explicit `[connectedTo]`
|
|
339
|
+
|
|
340
|
+
```html
|
|
341
|
+
<ul forDropList #listA="forDropList" [connectedTo]="[listB]" (dragDrop)="onDrop($event)">
|
|
342
|
+
...
|
|
343
|
+
</ul>
|
|
344
|
+
<ul forDropList #listB="forDropList" [connectedTo]="[listA]" (dragDrop)="onDrop($event)">
|
|
345
|
+
...
|
|
346
|
+
</ul>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Announcement customisation
|
|
350
|
+
|
|
351
|
+
Override the default ARIA live-region messages at any injector scope:
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
providers: [
|
|
355
|
+
provideForDragDropDefaults({
|
|
356
|
+
announceLift: (label, index, total) => `Grabbed ${label}, position ${index} of ${total}.`,
|
|
357
|
+
}),
|
|
358
|
+
];
|
|
359
|
+
```
|