@progress/kendo-react-sortable
Version:
React Sortable provides a sortable drag-and-drop functionality to elements within a list. KendoReact Sortable package
9 lines (8 loc) • 11.8 kB
JavaScript
/**
* @license
*-------------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the package root for more information
*-------------------------------------------------------------------------------------------
*/
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("react"),g=require("prop-types"),m=require("@progress/kendo-react-common"),P=require("./events/SortableOnDragStartEvent.js"),U=require("./events/SortableOnDragOverEvent.js"),k=require("./events/SortableOnDragEndEvent.js"),F=require("./events/SortableOnNavigateEvent.js"),O=require("@progress/kendo-react-intl"),R=require("./messages/index.js"),v=require("./utils/utils.js"),x=require("./package-metadata.js");function B(b){const c=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(b){for(const t in b)if(t!=="default"){const e=Object.getOwnPropertyDescriptor(b,t);Object.defineProperty(c,t,e.get?e:{enumerable:!0,get:()=>b[t]})}}return c.default=b,Object.freeze(c)}const f=B(M),A=200,D="data-sortable-id",C="data-sortable-component",T="[data-sortable-id]:not(.k-disabled)",q={[C]:!0},E=class E extends f.Component{constructor(c){super(c),this.state={clientX:0,clientY:0,isDragging:!1,activeId:"",dragCueWidth:0,dragCueHeight:0},this.isRtl=!1,this.itemRefsMap={},this.oldSizesMap={},this.animatingItemMap={},this.draggableRef=null,this.isUnmounted=!1,this.focusActiveId=!1,this.isKeyboardNavigated=!1,this.isDragPrevented=!1,this.showLicenseWatermark=!1,this.windowTimeout=t=>{window.setTimeout(()=>this.animatingItemMap[t]=!1,A)},this.swapItems=(t,e,i)=>{const s=t[e];return t[e]=t[i],t[i]=s,e=i,e},this.generateNewState=(t,e)=>{const{data:i}=this.props,s=[...i];if(t>e)for(let n=t-1;n>=e;n--){const o=i[n];this.isItemDisabled(o)||(t=this.swapItems(s,t,n))}else for(let n=t+1;n<=e;n++){const o=i[n];this.isItemDisabled(o)||(t=this.swapItems(s,t,n))}return s},this.closestSortableItem=t=>{let e=t;for(;e;){const i=e.getAttribute(D);if(i&&this.itemRefsMap[i]===e)return{id:i,element:e};e=e.parentElement}return{id:"",element:null}},this.isSortable=t=>!!t.hasAttribute(C),this.closestSortable=t=>{let e=t;for(;e;){if(this.isSortable(e))return e;e=e.parentElement}return null},this.isSameSortable=t=>this.closestSortable(t)===this.container,this.idComparer=(t,e)=>t+""==e+"",this.findItem=t=>{const{data:e,idField:i}=this.props;if(!(t+""))return;const s=m.getter(i);return v.find(e,a=>this.idComparer(s(a),t))},this.findIndex=t=>{const{data:e,idField:i}=this.props;return t+""?v.findIndex(e,s=>this.idComparer(s[i],t)):-1},this.isItemDisabled=t=>t&&t[this.props.disabledField||""]===!0,this.shouldResetActive=()=>{const t=m.getActiveElement(document);return t instanceof HTMLElement?!this.closestSortableItem(t).element:!1},this.widgetTarget=t=>{const e=v.closest(t,i=>v.hasClasses(i,"k-widget")||this.isSortable(i));return e&&!this.isSortable(e)},this.allowDrag=t=>t.hasAttribute(D)||!(v.isFocusable(t)||this.widgetTarget(t)),this.onDragStart=t=>{const{event:e}=t,{onDragStart:i}=this.props,s=document.elementFromPoint(e.clientX,e.clientY),{id:a,element:n}=this.closestSortableItem(s),o=this.findItem(a);if(!a||o&&this.isItemDisabled(o)||!this.allowDrag(s)||!this.isSameSortable(s)){this.isDragPrevented=!0;return}e.isTouch&&e.originalEvent.preventDefault();const r=new P.SortableOnDragStartEvent(this,this.findIndex(a),s);i&&i.call(void 0,r),this.isDragPrevented=r.isDefaultPrevented(),this.isDragPrevented?e.originalEvent.preventDefault():(this.offsetParent=v.relativeContextElement(this.container),this.setState({activeId:a,dragCueWidth:n&&n.clientWidth||0,dragCueHeight:n&&n.clientHeight||0}))},this.onDragOver=t=>{const{event:e}=t,{onDragOver:i,data:s}=this.props;if(this.isDragPrevented)return;e.originalEvent.preventDefault();const a=this.findIndex(this.state.activeId);if(a===-1){this.resetState();return}const n=document.elementFromPoint(e.clientX,e.clientY),o=this.closestSortableItem(n),r=this.findIndex(o.id),l=s[r];if(i&&r>-1&&a!==r&&!this.isItemDisabled(l)&&!this.animatingItemMap[o.id]&&this.shouldReorder(o.element,e.clientX,e.clientY)){const h=new U.SortableOnDragOverEvent(this,a,r,this.generateNewState(a,r));i.call(void 0,h)}const d=this.parentOffset();this.setState({clientX:e.clientX-d.left,clientY:e.clientY-d.top,isDragging:!0})},this.onDragEnd=t=>{const{event:e}=t,i=this.shouldResetActive();if(this.isDragPrevented)return;const{onDragEnd:s,data:a}=this.props,n=document.elementFromPoint(e.clientX,e.clientY),o=this.closestSortableItem(n);let r=this.findIndex(o.id),l=this.findIndex(this.state.activeId);const d=this.isItemDisabled(a[r]);if((r===-1||d)&&(r=l),s){let h=this.generateNewState(l,r);if(!d){const u=this.thresholdRect(o.element);if(u&&(e.clientX<u.left||e.clientX>u.right||e.clientY<u.top||e.clientY>u.bottom)){const I=l;l=r,r=I,h=this.props.data.slice()}}const p=new k.SortableOnDragEndEvent(this,l,r,h);s.call(void 0,p)}this.resetState(i)},this.shouldReorder=(t,e,i)=>{const s=this.thresholdRect(t);return s&&e>s.left&&e<s.right&&i>s.top&&i<s.bottom},this.thresholdRect=t=>{const e=this.state.activeId,i=this.container,a=(i?Array.from(i.childNodes):[]).find(u=>u instanceof HTMLElement&&u.getAttribute(D)===e);if(!t||!a)return null;const{width:n,height:o}=a.getBoundingClientRect(),r=t.getBoundingClientRect(),l=r.top+r.height/2-o/2,d=r.left+r.width/2-n/2,h=l+o,p=d+n;return{top:l,left:d,bottom:h,right:p}},this.onItemBlur=()=>{window.setTimeout(()=>{this.isUnmounted||this.shouldResetActive()&&!this.state.isDragging&&this.setState({activeId:""})})},this.onItemFocus=t=>{const{id:e,element:i}=this.closestSortableItem(t.currentTarget);!this.idComparer(e,this.state.activeId)&&this.isSameSortable(t.target)&&i===t.target&&this.setState({activeId:e})},this.resetState=t=>{this.isDragPrevented=!1,this.setState({clientX:0,clientY:0,isDragging:!1,dragCueWidth:0,dragCueHeight:0,activeId:t?"":this.state.activeId})},this.renderData=()=>{const{data:t,itemUI:e,idField:i,tabIndex:s,navigatable:a}=this.props;return t.map(n=>{const r=m.getter(i)(n),l=this.isItemDisabled(n),d=this.idComparer(this.state.activeId,r),h=a?d?0:-1:s;return f.createElement(e,{key:r,forwardRef:p=>this.refAssign(p,r),dataItem:n,isDisabled:l,isActive:d,isDragged:d&&this.state.isDragging,isDragCue:!1,attributes:{[D]:r,"aria-disabled":l,"aria-grabbed":d&&this.state.isDragging&&!this.isDragPrevented,"aria-dropeffect":l?"none":"move",tabIndex:m.getTabIndex(h,l),onFocus:this.onItemFocus,onBlur:this.onItemBlur},style:{cursor:l?"auto":"move",MozUserSelect:"none",msUserSelect:"none",WebkitUserSelect:"none",userSelect:"none"}})})},this.renderNoData=()=>{const{emptyItemUI:t}=this.props,i=O.provideLocalizationService(this).toLanguageString(R.noData,R.messages[R.noData]);if(t)return f.createElement(t,{message:i})},this.renderDragCue=()=>{const{itemUI:t}=this.props,{isDragging:e,activeId:i,clientX:s,clientY:a}=this.state,n=this.findItem(i);if(!(!e||!n))return f.createElement(t,{dataItem:n,isDisabled:!1,isActive:!0,isDragged:!0,isDragCue:!0,style:{position:"fixed",top:a+10,left:s+10,width:this.state.dragCueWidth,height:this.state.dragCueHeight},attributes:{}})},this.refAssign=(t,e)=>{t?this.itemRefsMap[e]=t:delete this.itemRefsMap[e]},this.draggableRefAssign=t=>{this.draggableRef=t},this.onKeyDown=t=>{var r,l,d;const{data:e,idField:i}=this.props,{activeId:s}=this.state,a=e.filter(h=>!this.isItemDisabled(h)),n=v.findIndex(a,h=>this.idComparer(h[i],s)),o=n<0?0:n;t.key===m.KEYS.tab&&m.getActiveElement(document)!==((r=this.draggableRef)==null?void 0:r.element)&&(t.preventDefault(),t.stopPropagation()),this.navigation&&((l=this.draggableRef)!=null&&l.element)&&this.navigation.triggerKeyboardEvent({target:(d=this.draggableRef)==null?void 0:d.element.querySelectorAll(T)[o],key:t.key,nativeEvent:{type:t.type},originalEvent:t})},this.handleNext=(t,e,i)=>{var s;m.getActiveElement(document)!==((s=this.draggableRef)==null?void 0:s.element)&&(i.originalEvent.metaKey?this.moveItem(t,e,i,"next"):e.focusNext(t))},this.handlePrev=(t,e,i)=>{var s;m.getActiveElement(document)!==((s=this.draggableRef)==null?void 0:s.element)&&(i.originalEvent.metaKey?this.moveItem(t,e,i,"prev"):e.focusPrevious(t))},this.moveItem=(t,e,i,s)=>{var a;if(m.getActiveElement(document)!==((a=this.draggableRef)==null?void 0:a.element)){const{onNavigate:n,data:o,idField:r}=this.props,l=this.findIndex(this.state.activeId);if(n){let d,h;s==="next"?(d=o[l+1],h=d&&d.disabled?o[l+2]:o[l+1]):(d=o[l-1],h=d&&d.disabled?o[l-2]:o[l-1]);const p=h&&h[r],u=o[l],I=u?u[r]:"",w=this.findIndex(I),y=this.findIndex(p||I),N=new F.SortableOnNavigateEvent(this,w,y,this.generateNewState(w,y));this.isKeyboardNavigated=!0,n.call(void 0,N)}}},this.showLicenseWatermark=!m.validatePackage(x.packageMetadata,{component:"Sortable"}),this.licenseMessage=m.getLicenseMessage(x.packageMetadata),this.onKeyDown=this.onKeyDown.bind(this)}get container(){return this.draggableRef&&this.draggableRef.element}getSnapshotBeforeUpdate(){const{idField:c,animation:t}=this.props;return this.oldSizesMap={},t&&this.props.data.forEach(e=>{const i=e[c],s=this.itemRefsMap[i];s&&(this.oldSizesMap[i]=s.getBoundingClientRect())}),null}componentDidUpdate(c){const{idField:t,animation:e}=this.props;this.focusActiveId&&(this.focusActiveId=!1,this.itemRefsMap[this.state.activeId].focus()),!(!e||!this.state.isDragging&&!this.isKeyboardNavigated)&&(this.isKeyboardNavigated=!1,c.data.forEach(i=>{const s=i[t],a=this.itemRefsMap[s];if(!a)return;const n=a.getBoundingClientRect(),o=this.oldSizesMap[s],r=o.left-n.left,l=o.top-n.top;r===0&&l===0||requestAnimationFrame(()=>{this.animatingItemMap[s]=!0,a.style.transform=`translate(${r}px, ${l}px)`,a.style.transition="transform 0s",requestAnimationFrame(()=>{a.style.transform="",a.style.transition=`transform ${A}ms cubic-bezier(0.2, 0, 0, 1) 0s`,this.windowTimeout(s)})})}))}componentDidMount(){var c,t;(c=this.draggableRef)!=null&&c.element&&(this.navigation=new m.Navigation({tabIndex:0,root:{current:(t=this.draggableRef)==null?void 0:t.element},rovingTabIndex:!0,selectors:[T],keyboardEvents:{keydown:{ArrowDown:this.handleNext,ArrowRight:this.handleNext,ArrowUp:this.handlePrev,ArrowLeft:this.handlePrev,Enter:(e,i,s)=>{i.focusElement(i.first,e)},Escape:(e,i,s)=>{var a,n;e.setAttribute("tabindex","-1"),(n=(a=this.draggableRef)==null?void 0:a.element)==null||n.focus(),this.setState({activeId:""})}}}})),this.isRtl=this.container&&getComputedStyle(this.container).direction==="rtl"||!1}componentWillUnmount(){this.isUnmounted=!0}parentOffset(){const c=this.offsetParent;if(c&&c.ownerDocument&&c!==c.ownerDocument.body){const t=c.getBoundingClientRect();return{left:t.left-c.scrollLeft,top:t.top-c.scrollTop}}return{left:0,top:0}}render(){const{data:c,style:t,className:e,itemsWrapUI:i,tabIndex:s,navigatable:a}=this.props,n=i||"div";return f.createElement(m.Draggable,{onDragStart:this.onDragStart,onDrag:this.onDragOver,onDragEnd:this.onDragEnd,ref:this.draggableRefAssign},f.createElement(n,{onKeyDown:o=>a&&this.onKeyDown(o),...q,className:e,style:{position:"relative",touchAction:"none",...t},tabIndex:a?s||0:void 0},c&&c.length?this.renderData():this.renderNoData(),this.renderDragCue(),this.showLicenseWatermark&&f.createElement(m.WatermarkOverlay,{message:this.licenseMessage})))}};E.defaultProps={navigation:!0,animation:!0,emptyItemUI:c=>f.createElement("div",null,c.message)},E.propTypes={idField:g.string.isRequired,disabledField:g.string,data:g.array.isRequired,tabIndex:g.number,navigation:g.bool,animation:g.bool,itemsWrapUI:g.any,itemUI:g.func.isRequired,emptyItemUI:g.func,style:g.object,className:g.string,onDragStart:g.func,onDragOver:g.func,onDragEnd:g.func,onNavigate:g.func};let S=E;O.registerForLocalization(S);exports.Sortable=S;