import { IButtonStyles , IconButton } from 'office-ui-fabric-react/lib/Button' ; import { Selection } from 'office-ui-fabric-react/lib/DetailsList' ; import { Label } from 'office-ui-fabric-react/lib/Label' ; import { DragDropHelper } from 'office-ui-fabric-react/lib/utilities/dragdrop' ; import { IDragDropContext } from 'office-ui-fabric-react/lib/utilities/dragdrop/interfaces' ; import * as React from 'react' ; import * as telemetry from '../../common/telemetry' ; import { IPropertyFieldOrderHostProps , IPropertyFieldOrderHostState } from './IPropertyFieldOrderHost' ; import styles from './PropertyFieldOrderHost.module.scss' ; import { isEqual } from '@microsoft/sp-lodash-subset' ; import { EventGroup } from 'office-ui-fabric-react/lib/Utilities' ; export default class PropertyFieldOrderHost extends React . Component < IPropertyFieldOrderHostProps , IPropertyFieldOrderHostState > { private _draggedItem : any ; private _selection : Selection ; private _ddHelper : DragDropHelper ; private _refs : Array < HTMLElement > ; private _ddSubs : Array < any > ; private _lastBox : HTMLElement ; constructor ( props , state ) { super ( props ) ; telemetry . track ( 'PropertyFieldOrder' , { disabled : props . disabled } ) ; this . _selection = null ; this . _ddHelper = new DragDropHelper ( { selection : this . _selection } ) ; this . _refs = new Array < HTMLElement > ( ) ; this . _ddSubs = new Array < any > ( ) ; this . _draggedItem = null ; this . state = { items : [ ] } ; } public render ( ) { const { items } = this . state ; return ( < div className = { styles . propertyFieldOrder } > { this . props . label && < Label > { this . props . label } < /Label>} < ul style = { { maxHeight : this . props . maxHeight ? this . props . maxHeight + 'px' : '100%' } } className = { ! this . props . disabled ? styles . enabled : styles . disabled } > { ( items && items . length > 0 ) && ( items . map ( ( value , index ) => { return ( < li ref = { this . registerRef } key = { index } draggable = { ! this . props . disableDragAndDrop && ! this . props . disabled } style = { { cursor : ! this . props . disableDragAndDrop && ! this . props . disabled ? 'pointer' : 'default' } } > { this . renderItem ( value , index ) } < /li>); } ) ) } { ( items && items . length > 0 ) && < div className = { styles . lastBox } ref = { ( ref ) => { this . _lastBox = ref ; } } />} < /ul> < /div>); } private renderItem ( item , index ) { return ( < div > < div className = { styles . itemBox } > { this . renderDisplayValue ( item , index ) } < /div> { ! this . props . removeArrows && < div > { this . renderArrows ( index ) } < /div>} < /div>); } private renderDisplayValue ( item , index ) { if ( typeof this . props . onRenderItem === "function" ) { return this . props . onRenderItem ( item , index ) ; } else { return ( < span > { this . props . textProperty ? item [ this . props . textProperty ] : item . toString ( ) } < /span>); } } private renderArrows ( index ) { let arrowButtonStyles = { root : { width : '14px' , height : '100%' , display : 'inline-block' } , rootDisabled : { backgroundColor : 'transparent' } , icon : { fontSize : "10px" } } ; return ( < div > < IconButton disabled = { this . props . disabled || index === 0 } iconProps = { { iconName : this . props . moveUpIconName } } onClick = { ( ) => { this . onMoveUpClick ( index ) ; } } styles = { arrowButtonStyles } / > < IconButton disabled = { this . props . disabled || index === this . props . items . length - 1 } iconProps = { { iconName : this . props . moveDownIconName } } onClick = { ( ) => { this . onMoveDownClick ( index ) ; } } styles = { arrowButtonStyles } / > < /div>); } public componentWillMount ( ) { this . setState ( { items : this . props . items || [ ] } ) ; } public componentDidMount ( ) { this . setupSubscriptions ( ) ; } public componentWillUpdate ( nextProps ) { if ( ! isEqual ( nextProps . items , this . state . items ) ) { this . setState ( { items : this . props . items || [ ] } ) ; } } public componentDidUpdate ( ) { this . cleanupSubscriptions ( ) ; this . setupSubscriptions ( ) ; } public componentWillUnmount ( ) { this . cleanupSubscriptions ( ) ; } private registerRef = ( ref ) : void => { this . _refs . push ( ref ) ; } ; private setupSubscriptions = ( ) : void => { if ( ! this . props . disableDragAndDrop && ! this . props . disabled ) { this . _refs . forEach ( ( value , index ) => { this . _ddSubs . push ( this . _ddHelper . subscribe ( value , new EventGroup ( value ) , { eventMap : [ { callback : ( context , event ? ) => { this . _draggedItem = context . data ; } , eventName : 'dragstart' } ] , selectionIndex : index , context : { data : this . state . items [ index ] , index : index } , updateDropState : ( isDropping , event ) => { if ( isDropping ) { value . classList . add ( styles . dragEnter ) ; } else { value . classList . remove ( styles . dragEnter ) ; } } , canDrop : ( dropContext ? , dragContext ? ) => { return true ; } , canDrag : ( item ? ) => { return true ; } , onDrop : ( item ? , event ? ) => { if ( this . _draggedItem ) { this . insertBeforeItem ( item ) ; } } , onDragEnd : ( item ? , event ? ) => { this . _draggedItem = null ; } } ) ) ; } ) ; if ( this . _refs . length && typeof this . _lastBox !== "undefined" ) { this . _ddSubs . push ( this . _ddHelper . subscribe ( this . _lastBox , new EventGroup ( this . _lastBox ) , { selectionIndex : this . _refs . length , context : { data : { } , index : this . _refs . length } , updateDropState : ( isDropping , event ) => { if ( isDropping ) { this . _refs [ this . _refs . length - 1 ] . classList . add ( styles . dragLast ) ; } else { this . _refs [ this . _refs . length - 1 ] . classList . remove ( styles . dragLast ) ; } } , canDrop : ( dropContext ? , dragContext ? ) => { return true ; } , onDrop : ( item ? , event ? ) => { if ( this . _draggedItem ) { let itemIndex = this . state . items . indexOf ( this . _draggedItem ) ; this . moveItemAtIndexToTargetIndex ( itemIndex , this . state . items . length - 1 ) ; } } } ) ) ; } } } ; private cleanupSubscriptions = ( ) : void => { while ( this . _ddSubs . length ) { let sub = this . _ddSubs . pop ( ) ; sub . dispose ( ) ; } } ; private insertBeforeItem = ( item ) => { let itemIndex = this . state . items . indexOf ( this . _draggedItem ) ; let targetIndex = this . state . items . indexOf ( item ) ; if ( itemIndex < targetIndex ) { targetIndex -= 1 ; } this . moveItemAtIndexToTargetIndex ( itemIndex , targetIndex ) ; } ; private onMoveUpClick = ( itemIndex ) : void => { if ( itemIndex > 0 ) { this . moveItemAtIndexToTargetIndex ( itemIndex , itemIndex - 1 ) ; } } ; private onMoveDownClick = ( itemIndex ) : void => { if ( itemIndex < this . state . items . length - 1 ) { this . moveItemAtIndexToTargetIndex ( itemIndex , itemIndex + 1 ) ; } } ; private moveItemAtIndexToTargetIndex = ( itemIndex , targetIndex ) : void => { if ( itemIndex !== targetIndex && itemIndex > - 1 && targetIndex > - 1 && itemIndex < this . state . items . length && targetIndex < this . state . items . length ) { let items = this . state . items ; items . splice ( targetIndex , 0 , ... items . splice ( itemIndex , 1 ) ) ; this . setState ( { items : items } ) ; this . props . valueChanged ( items ) ; } } ; }