Mooeypoo has uploaded a new change for review. https://gerrit.wikimedia.org/r/172325
Change subject: [wip] Adding DraggableGroupElement and DraggableElement mixins ...................................................................... [wip] Adding DraggableGroupElement and DraggableElement mixins Adding a GroupDragElement and DragElement to ooui for a drag/drop usability. Change-Id: I859ff276ea97628fde28327d200ed059d018c178 --- M build/modules.json A src/elements/DraggableElement.js A src/elements/DraggableGroupElement.js M src/styles/core.less A src/styles/elements/DraggableElement.less A src/styles/elements/DraggableGroupElement.less 6 files changed, 339 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/oojs/ui refs/changes/25/172325/1 diff --git a/build/modules.json b/build/modules.json index 6422127..b76b247 100644 --- a/build/modules.json +++ b/build/modules.json @@ -21,6 +21,8 @@ "src/elements/ButtonElement.js", "src/elements/GroupElement.js", + "src/elements/DraggableElement.js", + "src/elements/DraggableGroupElement.js", "src/elements/IconElement.js", "src/elements/IndicatorElement.js", "src/elements/LabelElement.js", diff --git a/src/elements/DraggableElement.js b/src/elements/DraggableElement.js new file mode 100644 index 0000000..7bab9cf --- /dev/null +++ b/src/elements/DraggableElement.js @@ -0,0 +1,104 @@ +/** + * A mixin for an element that can be dragged and dropped. + * Use in conjunction with DragGroupWidget + * + * @abstract + * @class + * + * @constructor + * @param {Object} [config] Configuration options + */ +OO.ui.DraggableElement = function OoUiDraggableElement( config ) { + // Configuration + config = config || {}; + + // Properties + this.index = null; + + // Initialize and events + this.$element + .attr( 'draggable', true ) + .addClass( 'oo-ui-DraggableElement' ) + // This is only here until I figure out the CSS issue + // TODO: Remove this display rule - it should be in the css file!!!! + .css( 'display', 'inline-block' ) + .on( { + dragstart: this.onDragStart.bind( this ), + dragover: this.onDragOver.bind( this ), + dragend: this.onDragEnd.bind( this ), + drop: this.onDrop.bind( this ) + } ); +}; + +/** + * Respond to dragstart event. + * @param {jQuery.Event} event jQuery event + * @return {boolean} True + * @fires dragstart + */ +OO.ui.DraggableElement.prototype.onDragStart = function ( event ) { + // Define drop effect + event.originalEvent.dataTransfer.dropEffect = 'move'; + event.originalEvent.dataTransfer.effectAllowed = 'move'; + + // Add dragging class + this.$element.addClass( 'oo-ui-DraggableElement-dragging' ); + + // Emit event + this.emit( 'dragstart', this ); + return true; +}; + +/** + * Respond to dragend event. + * @param {jQuery.Event} event jQuery event + * @return {boolean} False + * @fires dragend + */ +OO.ui.DraggableElement.prototype.onDragEnd = function () { + this.$element.removeClass( 'oo-ui-DraggableElement-dragging' ); + + this.emit( 'dragend' ); + // Return false and prevent propogation + return false; +}; + +/** + * Handle drop event. + * @param {jQuery.Event} event jQuery event + * @fires drop + */ +OO.ui.DraggableElement.prototype.onDrop = function () { + this.emit( 'drop', this ); +}; + +/** + * In order for drag/drop to work, the dragover event must + * return false and stop propogation. + * @return {boolean} False + * @fires dragover + */ +OO.ui.DraggableElement.prototype.onDragOver = function () { + this.emit( 'dragover', this.index ); + return false; +}; + +/** + * Set item index. + * Store it in the dom so we can access from the widget drag event + * @param {number} Item index + */ +OO.ui.DraggableElement.prototype.setIndex = function ( index ) { + if ( this.index !== index ) { + this.index = index; + this.$element.data( 'index', index ); + } +}; + +/** + * Get item index + * @return {number} Item index + */ +OO.ui.DraggableElement.prototype.getIndex = function () { + return this.index; +}; diff --git a/src/elements/DraggableGroupElement.js b/src/elements/DraggableGroupElement.js new file mode 100644 index 0000000..6db334f --- /dev/null +++ b/src/elements/DraggableGroupElement.js @@ -0,0 +1,213 @@ +/** + * Element containing a sequence of child elements that can be dragged + * and dropped. + * + * @abstract + * @class + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {jQuery} [$group] Container node, assigned to #$group, omit to use a generated `<div>` + */ +OO.ui.DraggableGroupElement = function OoUiDraggableGroupElement( config ) { + // Configuration intialization + config = config || {}; + + // Parent constructor + OO.ui.GroupElement.call( this, config ); + + // Properties + this.dragItem = null; + this.itemKeys = {}; + this.sideInsertion = ''; + + // Aggregate drag drop events in items + this.aggregate( { + dragstart: 'itemDragStart', + dragend: 'itemDragEnd', + drop: 'itemDrop' + } ); + + // Item events + this.connect( this, { + itemDragStart: 'onItemDragStart', + itemDrop: 'onItemDrop', + itemDragEnd: 'onItemDragEnd' + } ); + + // Group events + this.$element.on( { + drag: $.proxy( this.onDrag, this ), + dragover: $.proxy( this.onDragOver, this ) + } ); + + // Add items + if ( $.isArray( config.items ) ) { + this.addItems( config.items ); + } + + // Initialize + this.$placeholder = $( '<div>' ) + .addClass( 'oo-ui-draggableGroupElement-placeholder' ); + this.$element + .addClass( 'oo-ui-draggableGroupElement' ) + .prepend( this.$placeholder ); +}; + +/* Setup */ + +OO.inheritClass( OO.ui.DraggableGroupElement, OO.ui.GroupElement ); + +/* Methods */ + +/** + * Respond to item drag start event + * @param {OO.ui.DraggableElement} item Dragged item + */ +OO.ui.DraggableGroupElement.prototype.onItemDragStart = function ( item ) { + // Set the height of the indicator + this.$placeholder.css( 'height', this.items[0].$element.outerHeight() || 20 ); + this.setDragItem( item ); +}; + +/** + * Respond to item drag end event + */ +OO.ui.DraggableGroupElement.prototype.onItemDragEnd = function () { + this.unsetDragItem(); +}; + +/** + * Handle drop event and switch the order of the items accordingly + * @param {OO.ui.DraggableElement} item Dropped item + */ +OO.ui.DraggableGroupElement.prototype.onItemDrop = function ( item ) { + this.placeItemAtIndex( this.getDragItem(), item.getIndex() ); +}; + +/** + * Switch the place of two items + * @param {OO.ui.DraggableElement} fromIndex [description] + * @param {number} toIndex [description] + */ +OO.ui.DraggableGroupElement.prototype.placeItemAtIndex = function ( item, toIndex ) { + // If the insertion point is 'after', the insertion index + // is shifted to the right + if ( this.sideInsertion === 'after' ) { + toIndex++; + } + + // Change the item position + this.addItems( [ item ], toIndex ); +}; + +/** + * Respond to mouse move event + * @param {jQuery.Event} event Event details + */ +OO.ui.DraggableGroupElement.prototype.onDrag = function ( event ) { + var dragOverObj, $optionWidget, itemOffset, itemWidth, itemMidpoint, + dragPosition, itemIndex, sidePosition, + pageX = event.originalEvent.pageX, + pageY = event.originalEvent.pageY, + widgetOffset = this.$element.offset(); + + // Get the OptionWidget item we are dragging over + dragOverObj = this.getElementDocument().elementFromPoint( pageX, pageY ); + $optionWidget = $( dragOverObj ).closest( '.oo-ui-draggableElement' ); + itemOffset = $optionWidget.offset(); + itemIndex = $optionWidget.data( 'index' ); + + if ( + itemOffset && + this.isDragging() && + itemIndex !== this.getDragItem().getIndex() + ) { + // Calculate where the mouse is relative to the item + itemWidth = $optionWidget.outerWidth(); + itemMidpoint = itemOffset.left + itemWidth / 2; + dragPosition = pageX - widgetOffset.left; + + // Which side of the item we hover over will dictate + // where the placeholder will appear, on the left or + // on the right + sidePosition = dragPosition < itemMidpoint ? itemOffset.left : itemOffset.left + itemWidth; + // Store whether we are before or after an item to rearrange + // Also account for RTL, as this is flipped + if ( this.$element.css( 'direction' ) === 'rtl' ) { + this.sideInsertion = dragPosition < itemMidpoint ? 'after' : 'before'; + } else { + this.sideInsertion = dragPosition < itemMidpoint ? 'before' : 'after'; + } + + // Add drop indicator between objects + if ( this.sideInsertion ) { + this.$placeholder + .css( { + left: sidePosition, + top: itemOffset.top + } ) + .show(); + } else { + this.$placeholder + .css( { + left: 0, + top: itemOffset.top + } ) + .hide(); + } + } else { + // This means the item was dragged outside the widget + this.$placeholder + .css( 'left', 0 ) + .hide(); + } + +}; + +/** + * Set a dragged item + * @param {OO.ui.DraggableElement} item Dragged item + */ +OO.ui.DraggableGroupElement.prototype.setDragItem = function ( item ) { + this.dragItem = item; +}; + +/** + * Unset the current dragged item + */ +OO.ui.DraggableGroupElement.prototype.unsetDragItem = function () { + this.dragItem = null; + this.$placeholder.hide(); + this.sideInsertion = ''; +}; + +/** + * Get the current dragged item + * @return {OO.ui.DraggableElement|null} item Dragged item or null if no item is dragged + */ +OO.ui.DraggableGroupElement.prototype.getDragItem = function () { + return this.dragItem; +}; + +/** + * Check if there's an item being dragged. + * @return {Boolean} Item is being dragged + */ +OO.ui.DraggableGroupElement.prototype.isDragging = function () { + return this.getDragItem() !== null; +}; + +/** + * Expand the addItems method to store item indeces + */ +OO.ui.DraggableGroupElement.prototype.addItems = function ( items, index ) { + var i; + // Parent + OO.ui.GroupElement.prototype.addItems.call( this, items, index ); + + // Map the index of each object + for ( i = 0; i < this.items.length; i++ ) { + this.items[i].setIndex( i ); + } +}; diff --git a/src/styles/core.less b/src/styles/core.less index 139384c..89d4745 100644 --- a/src/styles/core.less +++ b/src/styles/core.less @@ -19,6 +19,8 @@ @import 'elements/ButtonElement.less'; @import 'elements/ClippableElement.less'; @import 'elements/FlaggedElement.less'; +@import 'elements/DraggableElement.less'; +@import 'elements/DraggableGroupElement.less'; @import 'elements/GroupElement.less'; @import 'elements/IconElement.less'; @import 'elements/IndicatorElement.less'; diff --git a/src/styles/elements/DraggableElement.less b/src/styles/elements/DraggableElement.less new file mode 100644 index 0000000..6485580 --- /dev/null +++ b/src/styles/elements/DraggableElement.less @@ -0,0 +1,10 @@ +@import '../common'; + +.oo-ui-draggableElement { + display: inline-block; +} + +.oo-ui-draggableElement-dragging { + background: #666666; + opacity: 0.4; +} diff --git a/src/styles/elements/DraggableGroupElement.less b/src/styles/elements/DraggableGroupElement.less new file mode 100644 index 0000000..7a5860f --- /dev/null +++ b/src/styles/elements/DraggableGroupElement.less @@ -0,0 +1,8 @@ +@import '../common'; + +.oo-ui-draggableGroupElement-placeholder { + position: absolute; + display: block; + width: 2px; + background-color: #2947C2; +} -- To view, visit https://gerrit.wikimedia.org/r/172325 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I859ff276ea97628fde28327d200ed059d018c178 Gerrit-PatchSet: 1 Gerrit-Project: oojs/ui Gerrit-Branch: master Gerrit-Owner: Mooeypoo <mor...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits