@knotx/viselect
Version:
[Forked] Simple, lightweight and modern library library for making visual DOM Selections.
4 lines (3 loc) • 15 kB
JavaScript
/*! @viselect/vanilla v3.9.0 MIT | https://github.com/simonwep/viselect/tree/master/packages/vanilla */
(function(w,E){typeof exports=="object"&&typeof module<"u"?module.exports=E():typeof define=="function"&&define.amd?define(E):(w=typeof globalThis<"u"?globalThis:w||self,w.SelectionArea=E())})(this,function(){"use strict";class w{constructor(){this._listeners=new Map,this.on=this.addEventListener,this.off=this.removeEventListener,this.emit=this.dispatchEvent}addEventListener(e,t){const s=this._listeners.get(e)??new Set;return this._listeners.set(e,s),s.add(t),this}removeEventListener(e,t){var s;return(s=this._listeners.get(e))==null||s.delete(t),this}dispatchEvent(e,...t){let s=!0;for(const o of this._listeners.get(e)??[])s=o(...t)!==!1&&s;return s}unbindAllListeners(){this._listeners.clear()}}const E=(l,e="px")=>typeof l=="number"?l+e:l,S=({style:l},e,t)=>{if(typeof e=="object")for(const[s,o]of Object.entries(e))o!==void 0&&(l[s]=E(o));else t!==void 0&&(l[e]=E(t))},k=(l=0,e=0,t=0,s=0)=>{const o={x:l,y:e,width:t,height:s,top:e,left:l,right:l+t,bottom:e+s};return{...o,toJSON:()=>JSON.stringify(o)}},X=l=>{let e,t=-1,s=!1;return{next:(...o)=>{e=o,s||(s=!0,t=requestAnimationFrame(()=>{l(...e),s=!1}))},cancel:()=>{cancelAnimationFrame(t),s=!1}}},B=(l,e,t="touch")=>{switch(t){case"center":{const s=e.left+e.width/2,o=e.top+e.height/2;return s>=l.left&&s<=l.right&&o>=l.top&&o<=l.bottom}case"cover":return e.left>=l.left&&e.top>=l.top&&e.right<=l.right&&e.bottom<=l.bottom;case"touch":return l.right>=e.left&&l.left<=e.right&&l.bottom>=e.top&&l.top<=e.bottom}},Y=()=>matchMedia("(hover: none), (pointer: coarse)").matches,K=()=>"safari"in window,P=l=>Array.isArray(l)?l:[l],R=l=>(e,t,s,o={})=>{(e instanceof HTMLCollection||e instanceof NodeList)&&(e=Array.from(e)),t=P(t),e=P(e);for(const i of e)if(i)for(const n of t)i[l](n,s,{capture:!1,...o})},y=R("addEventListener"),m=R("removeEventListener"),C=l=>{var o;const{clientX:e,clientY:t,target:s}=((o=l.touches)==null?void 0:o[0])??l;return{x:e,y:t,target:s}},x=(l,e=document)=>P(l).map(t=>typeof t=="string"?Array.from(e.querySelectorAll(t)):t instanceof Element?t:null).flat().filter(Boolean),N=(l,e)=>e.some(t=>typeof t=="number"?l.button===t:typeof t=="object"?t.button!==l.button?!1:t.modifiers.every(s=>{switch(s){case"alt":return l.altKey;case"ctrl":return l.ctrlKey||l.metaKey;case"shift":return l.shiftKey}}):!1),{abs:v,max:z,min:D,ceil:O}=Math,j=(l=[])=>({stored:l,selected:[],touched:[],changed:{added:[],removed:[]}}),A={getScrollPosition:l=>({x:l.scrollLeft,y:l.scrollTop}),setScrollPosition:(l,e)=>{e.x!==void 0&&(l.scrollLeft=e.x),e.y!==void 0&&(l.scrollTop=e.y)},getScrollSize:l=>({width:l.scrollWidth,height:l.scrollHeight}),getClientSize:l=>({width:l.clientWidth,height:l.clientHeight}),alwaysScroll:!1},M=class M extends w{constructor(e){var i,n,r,c,d,_,u,h,a,f;super(),this._selection=j(),this._targetBoundaryScrolled=!0,this._selectables=[],this._areaLocation={y1:0,x2:0,y2:0,x1:0},this._areaRect=k(),this._singleClick=!0,this._scrollAvailable=!0,this._scrollingActive=!1,this._scrollSpeed={x:0,y:0},this._scrollDelta={x:0,y:0},this._lastMousePosition={x:0,y:0},this.enable=this._toggleStartEvents,this.disable=this._toggleStartEvents.bind(this,!1),this._options={selectionAreaClass:"selection-area",selectionContainerClass:void 0,selectables:[],document:window.document,startAreas:["html"],boundaries:["html"],container:"body",...e,behaviour:{overlap:"invert",intersect:"touch",triggers:[0],...e.behaviour,startThreshold:(i=e.behaviour)!=null&&i.startThreshold?typeof e.behaviour.startThreshold=="number"?e.behaviour.startThreshold:{x:10,y:10,...e.behaviour.startThreshold}:{x:10,y:10},scrolling:{speedDivider:10,manualSpeed:750,...(n=e.behaviour)==null?void 0:n.scrolling,startScrollMargins:{x:0,y:0,...(c=(r=e.behaviour)==null?void 0:r.scrolling)==null?void 0:c.startScrollMargins}}},features:{range:!0,touch:!0,deselectOnBlur:!1,...e.features,singleTap:{allow:!0,intersect:"native",...(d=e.features)==null?void 0:d.singleTap}}},this._scrollController={getScrollPosition:((_=e.scrollController)==null?void 0:_.getScrollPosition)||A.getScrollPosition,setScrollPosition:((u=e.scrollController)==null?void 0:u.setScrollPosition)||A.setScrollPosition,getScrollSize:((h=e.scrollController)==null?void 0:h.getScrollSize)||A.getScrollSize,getClientSize:((a=e.scrollController)==null?void 0:a.getClientSize)||A.getClientSize,alwaysScroll:((f=e.scrollController)==null?void 0:f.alwaysScroll)||A.alwaysScroll};for(const p of Object.getOwnPropertyNames(Object.getPrototypeOf(this)))typeof this[p]=="function"&&(this[p]=this[p].bind(this));const{document:t,selectionAreaClass:s,selectionContainerClass:o}=this._options;this._area=t.createElement("div"),this._clippingElement=t.createElement("div"),this._clippingElement.appendChild(this._area),this._area.classList.add(s),o&&this._clippingElement.classList.add(o),S(this._area,{willChange:"top, left, bottom, right, width, height",top:0,left:0,position:"fixed"}),S(this._clippingElement,{overflow:"hidden",position:"fixed",transform:"translate3d(0, 0, 0)",pointerEvents:"none",zIndex:"1"}),this._frame=X(p=>{this._recalculateSelectionAreaRect(),this._updateElementSelection(),this._emitEvent("move",p),this._redrawSelectionArea()}),this.enable()}_toggleStartEvents(e=!0){const{document:t,features:s}=this._options,o=e?y:m;o(t,"mousedown",this._onTapStart),s.touch&&o(t,"touchstart",this._onTapStart,{passive:!1})}_onTapStart(e,t=!1){const{x:s,y:o,target:i}=C(e),{document:n,startAreas:r,boundaries:c,features:d,behaviour:_}=this._options,u=i.getBoundingClientRect();if(e instanceof MouseEvent&&!N(e,_.triggers))return;const h=x(r,n),a=x(c,n);this._targetElement=a.find(b=>B(b.getBoundingClientRect(),u));const f=e.composedPath(),p=h.find(b=>f.includes(b));if(this._targetBoundary=a.find(b=>f.includes(b)),!this._targetElement||!p||!this._targetBoundary||!t&&this._emitEvent("beforestart",e)===!1)return;this._areaLocation={x1:s,y1:o,x2:0,y2:0};const g=n.scrollingElement??n.body,T=this._scrollController.getScrollPosition(g);this._scrollDelta={x:T.x,y:T.y},this._singleClick=!0,this.clearSelection(!1,!0),y(n,["touchmove","mousemove"],this._delayedTapMove,{passive:!1}),y(n,["mouseup","touchcancel","touchend"],this._onTapStop),y(n,"scroll",this._onScroll),d.deselectOnBlur&&(this._targetBoundaryScrolled=!1,y(this._targetBoundary,"scroll",this._onStartAreaScroll))}_onSingleTap(e){const{singleTap:{intersect:t},range:s}=this._options.features,o=C(e);let i;if(t==="native")i=o.target;else if(t==="touch"){this.resolveSelectables();const{x:r,y:c}=o;i=this._selectables.find(d=>{const{right:_,left:u,top:h,bottom:a}=d.getBoundingClientRect();return r<_&&r>u&&c<a&&c>h})}if(!i)return;for(this.resolveSelectables();!this._selectables.includes(i);)if(i.parentElement)i=i.parentElement;else{this._targetBoundaryScrolled||this.clearSelection();return}const{stored:n}=this._selection;if(this._emitEvent("start",e),e.shiftKey&&s&&this._latestElement){const r=this._latestElement,[c,d]=r.compareDocumentPosition(i)&4?[i,r]:[r,i],_=[...this._selectables.filter(u=>u.compareDocumentPosition(c)&4&&u.compareDocumentPosition(d)&2),c,d];this.select(_),this._latestElement=r}else n.includes(i)&&(n.length===1||e.ctrlKey||n.every(r=>this._selection.stored.includes(r)))?this.deselect(i):(this.select(i),this._latestElement=i)}_delayedTapMove(e){const{container:t,document:s,behaviour:{startThreshold:o}}=this._options,{x1:i,y1:n}=this._areaLocation,{x:r,y:c}=C(e);if(typeof o=="number"&&v(r+c-(i+n))>=o||typeof o=="object"&&v(r-i)>=o.x||v(c-n)>=o.y){if(m(s,["mousemove","touchmove"],this._delayedTapMove,{passive:!1}),this._emitEvent("beforedrag",e)===!1){m(s,["mouseup","touchcancel","touchend"],this._onTapStop);return}y(s,["mousemove","touchmove"],this._onTapMove,{passive:!1}),S(this._area,"display","block"),x(t,s)[0].appendChild(this._clippingElement),this.resolveSelectables(),this._singleClick=!1,this._targetRect=this._targetElement.getBoundingClientRect();const d=this._targetElement,_=this._scrollController.getScrollSize(d),u=this._scrollController.getClientSize(d);this._scrollAvailable=_.height!==u.height||_.width!==u.width,this._scrollAvailable&&(y(this._targetElement,"wheel",this._wheelScroll,{passive:!1}),y(this._options.document,"keydown",this._keyboardScroll,{passive:!1}),this._selectables=this._selectables.filter(h=>this._targetElement.contains(h))),this._setupSelectionArea(),this._emitEvent("start",e),this._onTapMove(e)}this._handleMoveEvent(e)}_setupSelectionArea(){const{_clippingElement:e,_targetElement:t,_area:s}=this,o=this._targetRect=t.getBoundingClientRect();this._scrollAvailable?(S(e,{top:o.top,left:o.left,width:o.width,height:o.height}),S(s,{marginTop:-o.top,marginLeft:-o.left})):(S(e,{top:0,left:0,width:"100%",height:"100%"}),S(s,{marginTop:0,marginLeft:0}))}_onTapMove(e){const{_scrollSpeed:t,_areaLocation:s,_options:o,_frame:i}=this,{speedDivider:n}=o.behaviour.scrolling,r=this._targetElement,{x:c,y:d}=C(e);if(s.x2=c,s.y2=d,this._lastMousePosition.x=c,this._lastMousePosition.y=d,this._scrollAvailable&&!this._scrollingActive&&(t.y||t.x)){this._scrollingActive=!0;const _=()=>{if(!t.x&&!t.y){this._scrollingActive=!1;return}const{x:u,y:h}=this._scrollController.getScrollPosition(r),a={};if(t.y){a.y=h+O(t.y/n),this._scrollController.setScrollPosition(r,a);const f=this._scrollController.getScrollPosition(r);s.y1-=f.y-h}if(t.x){a.x=u+O(t.x/n),this._scrollController.setScrollPosition(r,a);const f=this._scrollController.getScrollPosition(r);s.x1-=f.x-u}i.next(e),requestAnimationFrame(_)};requestAnimationFrame(_)}else i.next(e);this._handleMoveEvent(e)}_handleMoveEvent(e){const{features:t}=this._options;(t.touch&&Y()||this._scrollAvailable&&K())&&e.preventDefault()}_onScroll(){const{_scrollDelta:e,_options:{document:t}}=this,s=t.scrollingElement??t.body,o=this._scrollController.getScrollPosition(s);this._areaLocation.x1+=e.x-o.x,this._areaLocation.y1+=e.y-o.y,e.x=o.x,e.y=o.y,this._setupSelectionArea(),this._frame.next(null)}_onStartAreaScroll(){this._targetBoundaryScrolled=!0,m(this._targetElement,"scroll",this._onStartAreaScroll)}_wheelScroll(e){const{manualSpeed:t}=this._options.behaviour.scrolling,s=e.deltaY?e.deltaY>0?1:-1:0,o=e.deltaX?e.deltaX>0?1:-1:0;this._scrollSpeed.y+=s*t,this._scrollSpeed.x+=o*t,this._onTapMove(e),e.preventDefault()}_keyboardScroll(e){const{manualSpeed:t}=this._options.behaviour.scrolling,s=e.key==="ArrowLeft"?-1:e.key==="ArrowRight"?1:0,o=e.key==="ArrowUp"?-1:e.key==="ArrowDown"?1:0;this._scrollSpeed.x+=Math.sign(s)*t,this._scrollSpeed.y+=Math.sign(o)*t,e.preventDefault(),this._onTapMove({clientX:this._lastMousePosition.x,clientY:this._lastMousePosition.y,preventDefault:()=>{}})}_recalculateSelectionAreaRect(){const{_scrollSpeed:e,_areaLocation:t,_targetElement:s,_options:o}=this,i=this._targetRect,n=this._scrollController.getScrollPosition(s),r=this._scrollController.getClientSize(s),c=this._scrollController.getScrollSize(s),d=this._scrollController.alwaysScroll,{x1:_,y1:u}=t;let{x2:h,y2:a}=t;const{behaviour:{scrolling:{startScrollMargins:f}}}=o;h<i.left+f.x?(e.x=n.x||d?-v(i.left-h+f.x):0,h=h<i.left?i.left:h):h>i.right-f.x?(e.x=c.width-n.x-r.width||d?v(i.left+i.width-h-f.x):0,h=h>i.right?i.right:h):e.x=0,a<i.top+f.y?(e.y=n.y||d?-v(i.top-a+f.y):0,a=a<i.top?i.top:a):a>i.bottom-f.y?(e.y=c.height-n.y-r.height||d?v(i.top+i.height-a-f.y):0,a=a>i.bottom?i.bottom:a):e.y=0;const p=D(_,h),g=D(u,a),T=z(_,h),b=z(u,a);this._areaRect=k(p,g,T-p,b-g)}_redrawSelectionArea(){const{x:e,y:t,width:s,height:o}=this._areaRect,{style:i}=this._area;i.left=`${e}px`,i.top=`${t}px`,i.width=`${s}px`,i.height=`${o}px`}_onTapStop(e,t){var n;const{document:s,features:o}=this._options,{_singleClick:i}=this;m(this._targetElement,"scroll",this._onStartAreaScroll),m(s,["mousemove","touchmove"],this._delayedTapMove),m(s,["touchmove","mousemove"],this._onTapMove),m(s,["mouseup","touchcancel","touchend"],this._onTapStop),m(s,"scroll",this._onScroll),this._keepSelection(),e&&i&&o.singleTap.allow?this._onSingleTap(e):!i&&!t&&(this._updateElementSelection(),this._emitEvent("stop",e)),this._scrollSpeed.x=0,this._scrollSpeed.y=0,m(this._targetElement,"wheel",this._wheelScroll,{passive:!0}),m(this._options.document,"keydown",this._keyboardScroll,{passive:!0}),this._clippingElement.remove(),(n=this._frame)==null||n.cancel(),S(this._area,"display","none")}_updateElementSelection(){const{_selectables:e,_options:t,_selection:s,_areaRect:o}=this,{stored:i,selected:n,touched:r}=s,{intersect:c,overlap:d}=t.behaviour,_=d==="invert",u=[],h=[],a=[];for(let p=0;p<e.length;p++){const g=e[p];if(B(o,g.getBoundingClientRect(),c)){if(n.includes(g))i.includes(g)&&!r.includes(g)&&r.push(g);else if(_&&i.includes(g)){a.push(g);continue}else h.push(g);u.push(g)}}_&&h.push(...i.filter(p=>!n.includes(p)));const f=d==="keep";for(let p=0;p<n.length;p++){const g=n[p];!u.includes(g)&&!(f&&i.includes(g))&&a.push(g)}s.selected=u,s.changed={added:h,removed:a},this._latestElement=void 0}_emitEvent(e,t){return this.emit(e,{event:t,store:this._selection,selection:this})}_keepSelection(){const{_options:e,_selection:t}=this,{selected:s,changed:o,touched:i,stored:n}=t,r=s.filter(c=>!n.includes(c));switch(e.behaviour.overlap){case"drop":{t.stored=[...r,...n.filter(c=>!i.includes(c))];break}case"invert":{t.stored=[...r,...n.filter(c=>!o.removed.includes(c))];break}case"keep":{t.stored=[...n,...s.filter(c=>!n.includes(c))];break}}}trigger(e,t=!0){this._onTapStart(e,t)}resolveSelectables(){this._selectables=x(this._options.selectables,this._options.document)}clearSelection(e=!0,t=!1){const{selected:s,stored:o,changed:i}=this._selection;i.added=[],i.removed.push(...s,...e?o:[]),t||(this._emitEvent("move",null),this._emitEvent("stop",null)),this._selection=j(e?[]:o)}getSelection(){return this._selection.stored}getSelectionArea(){return this._area}getSelectables(){return this._selectables}setAreaLocation(e){Object.assign(this._areaLocation,e),this._redrawSelectionArea()}getAreaLocation(){return this._areaLocation}cancel(e=!1){this._onTapStop(null,!e)}destroy(){this.cancel(),this.disable(),this._clippingElement.remove(),super.unbindAllListeners()}select(e,t=!1){const{changed:s,selected:o,stored:i}=this._selection,n=x(e,this._options.document).filter(r=>!o.includes(r)&&!i.includes(r));return i.push(...n),o.push(...n),s.added.push(...n),s.removed=[],this._latestElement=void 0,t||(this._emitEvent("move",null),this._emitEvent("stop",null)),n}deselect(e,t=!1){const{selected:s,stored:o,changed:i}=this._selection,n=x(e,this._options.document).filter(r=>s.includes(r)||o.includes(r));this._selection.stored=o.filter(r=>!n.includes(r)),this._selection.selected=s.filter(r=>!n.includes(r)),this._selection.changed.added=[],this._selection.changed.removed.push(...n.filter(r=>!i.removed.includes(r))),this._latestElement=void 0,t||(this._emitEvent("move",null),this._emitEvent("stop",null))}};M.version="3.9.0";let L=M;return L});
//# sourceMappingURL=viselect.umd.js.map