@@ -19,6 +19,15 @@ export 'reorder_events.dart';
1919/// marked with `reorderPlaceholder` attribute as a placeholder when moving
2020/// items.
2121///
22+ /// `reorderItem` s should be direct children of the `reorder-list` and, by
23+ /// default, the entire element is a handle and may be dragged and dropped
24+ /// to perfom reorder operations.
25+ ///
26+ /// If a `reorderItem` component implements [ReorderHandleProvider] , the child
27+ /// element marked with `reorderHandle` will be draggable for reorder
28+ /// operations. This allows complex components to be reordered by a handle and
29+ /// not have the entire component be listening to drag and drop events.
30+ ///
2231/// __Attention:__ This component is not an implementation of the Material List
2332/// reorder control from the Material Spec. There is currently no support for
2433/// interactions on a mobile device.
@@ -77,8 +86,8 @@ class ReorderListComponent implements OnDestroy {
7786 @Input ()
7887 bool multiSelect = false ;
7988
80- // Reorderable items in the list
81- List < ReorderItemDirective > _items;
89+ // Reorderable items in the list; keys are the handles, values are the items
90+ Map < HtmlElement , HtmlElement > _items;
8291 // Map of active drag&drop event subscriptions.
8392 Map <HtmlElement , List <StreamSubscription >> _subscriptions;
8493 // Map of active onDrag subscriptions,
@@ -111,12 +120,13 @@ class ReorderListComponent implements OnDestroy {
111120
112121 @ContentChildren (ReorderItemDirective )
113122 set items (List <ReorderItemDirective > value) {
114- _items = value;
123+ _items = new Map .fromIterable (value,
124+ key: (e) => e.handleElement, value: (e) => e.element);
115125 _refreshItems ();
116126 }
117127
118128 void _refreshItems () {
119- Set < HtmlElement > newElements = _items. map ((e) => e.element) .toSet ();
129+ final newElements = _handleElements .toSet ();
120130 Set <HtmlElement > currentlyTracked = new Set .from (_subscriptions.keys);
121131 for (HtmlElement tracked in currentlyTracked) {
122132 if (! newElements.contains (tracked)) {
@@ -204,22 +214,23 @@ class ReorderListComponent implements OnDestroy {
204214 }
205215 }
206216
217+ final draggedElement = _items[_dragSourceElement];
207218 if (verticalItems) {
208219 placeholder.style
209- ..height = "${_dragSourceElement .borderEdge .height }px"
210- ..width = "${_dragSourceElement .borderEdge .width }px"
220+ ..height = "${draggedElement .borderEdge .height }px"
221+ ..width = "${draggedElement .borderEdge .width }px"
211222 ..top = "${upperStackSize }px" ;
212223 } else {
213224 HtmlElement e = contents[toIndex];
214225 // If e move right, take its left as placeholder's left. Else, take
215226 // its right minus drag source width as placeholder's left.
216227 var left = moveRight
217228 ? e.offset.left
218- : e.offset.right - _dragSourceElement .borderEdge.width;
229+ : e.offset.right - draggedElement .borderEdge.width;
219230
220231 placeholder.style
221- ..height = "${_dragSourceElement .borderEdge .height }px"
222- ..width = "${_dragSourceElement .borderEdge .width }px"
232+ ..height = "${draggedElement .borderEdge .height }px"
233+ ..width = "${draggedElement .borderEdge .width }px"
223234 ..top = "${e .offset .top }px"
224235 ..left = "${left }px" ;
225236 }
@@ -297,7 +308,8 @@ class ReorderListComponent implements OnDestroy {
297308 _dragSubscriptions.remove (element);
298309 }
299310
300- get _reorderElements => _items.map ((x) => x.element).toList ();
311+ List <HtmlElement > get _handleElements => _items.keys.toList ();
312+ List <HtmlElement > get _reorderElements => _items.values.toList ();
301313
302314 void _onDragStart (MouseEvent e) {
303315 // If multiSelect is enabled, clear the selection and replace with the
@@ -315,7 +327,7 @@ class ReorderListComponent implements OnDestroy {
315327 // Initialize all transforms.
316328 var contents = _reorderElements;
317329 int childCount = contents.length;
318- _moveSourceIndex = contents .indexOf (_dragSourceElement);
330+ _moveSourceIndex = _handleElements .indexOf (_dragSourceElement);
319331 _curTransformY = new List <int >.filled (childCount, 0 );
320332 _itemSizes = new List <int >(childCount);
321333 for (int i = 0 ; i < childCount; i++ ) {
@@ -550,7 +562,7 @@ class ReorderListComponent implements OnDestroy {
550562 }
551563
552564 int _getIndex (HtmlElement element) {
553- List contents = _reorderElements ;
565+ List contents = _handleElements ;
554566 int childCount = contents.length;
555567 for (int i = 0 ; i < childCount; i++ ) {
556568 if (element == contents[i]) {
@@ -601,23 +613,59 @@ class ReorderListComponent implements OnDestroy {
601613
602614typedef void ReorderListHandler (int sourceIndex, int destIndex);
603615
604- /// Indicates that a child will participate in reorder operation inside a
605- /// reorder-list component. See ReorderListComponent for usage.
606- // TODO(google): Add back in host value once attribute bug is fixed.
616+ /// Indicates that the element is a list item in the containing `reorder-list`
617+ /// component. See [ReorderListComponent] for usage.
607618@Directive (
608619 selector: '[reorderItem]' ,
609620)
610621class ReorderItemDirective {
611622 @HostBinding ('attr.draggable' )
612- static const hostDraggable = 'true' ;
623+ String get hostDraggable => _reorderHandle == null ? 'true' : null ;
613624
614625 @HostBinding ('attr.role' )
615626 static const hostRole = 'listitem' ;
616627
617628 @HostBinding ('tabIndex' )
618629 static const hostTabIndex = 0 ;
619630
631+ final HtmlElement element;
632+ final ReorderHandleProvider _handleProvider;
633+ HtmlElement _handleElement;
634+
635+ HtmlElement get _reorderHandle =>
636+ _handleElement ?? _handleProvider? .reorderHandle? .element;
637+
638+ /// The [HtmlElement] to be used as the drag handle.
639+ ///
640+ /// Optional. If not specified, the host element for this directive will also
641+ /// be the handle.
642+ @Input ()
643+ set reorderHandle (HtmlElement element) {
644+ _handleElement = element;
645+ }
646+
647+ HtmlElement get handleElement => _reorderHandle ?? element;
648+
649+ ReorderItemDirective (this .element, @Optional () this ._handleProvider);
650+ }
651+
652+ /// Interface that will return a [ReorderHandleDirective] for use as a handle of
653+ /// a component marked as a `reorderItem` inside a reorder-list.
654+ abstract class ReorderHandleProvider {
655+ ReorderHandleDirective get reorderHandle;
656+ }
657+
658+ /// Indicates the element with this directive is the drag handle for the
659+ /// containing [reorderItem] .
660+ @Directive (
661+ selector: '[reorderHandle]' ,
662+ exportAs: 'handle' ,
663+ )
664+ class ReorderHandleDirective {
665+ @HostBinding ('attr.draggable' )
666+ static const hostDraggable = 'true' ;
667+
620668 final HtmlElement element;
621669
622- ReorderItemDirective (this .element);
670+ ReorderHandleDirective (this .element);
623671}
0 commit comments