vue-scroll-picker
Advanced tools
Comparing version
@@ -0,1 +1,2 @@ | ||
/// <reference types="node" /> | ||
import { PropType } from 'vue'; | ||
@@ -8,2 +9,3 @@ type MouseWheelEvent = MouseEvent & { | ||
value: any; | ||
disabled?: boolean; | ||
} | ||
@@ -42,5 +44,5 @@ export type ScrollPickerOptionable = string | number | boolean | ScrollPickerOption; | ||
internalValue: any; | ||
pivots: number[]; | ||
pivotsMin: number; | ||
pivotsMax: number; | ||
bounds: number[]; | ||
boundMin: number; | ||
boundMax: number; | ||
scroll: number | null; | ||
@@ -50,4 +52,3 @@ scrollOffsetTop: number; | ||
scrollMax: number; | ||
transitioning: boolean; | ||
transitionTimer: any; | ||
transitionTimeout: NodeJS.Timeout | null; | ||
start: [scroll: number, clientY: number] | null; | ||
@@ -60,3 +61,3 @@ isDragging: boolean; | ||
resize(): void; | ||
calculatePivots(): void; | ||
calculateBounds(): void; | ||
sanitizeInternalIndex(index: number): number; | ||
@@ -73,4 +74,14 @@ findIndexFromScroll(scroll: number): number; | ||
onClick(event: TouchEvent | MouseEvent): void; | ||
correction(index: number): void; | ||
}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{ | ||
correction(scroll: number): void; | ||
scrollTo(scroll: number, onComplete?: () => void): void; | ||
emitModalValue(value: unknown): void; | ||
}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, { | ||
'update:modelValue': (value: any) => boolean; | ||
start: () => boolean; | ||
move: (value: any) => boolean; | ||
end: (value: any) => boolean; | ||
cancel: () => boolean; | ||
wheel: (value: any) => boolean; | ||
click: (newValue: any, oldValue: any) => boolean; | ||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{ | ||
modelValue: null; | ||
@@ -101,3 +112,11 @@ options: { | ||
}; | ||
}>>, { | ||
}>> & { | ||
"onUpdate:modelValue"?: ((value: any) => any) | undefined; | ||
onStart?: (() => any) | undefined; | ||
onMove?: ((value: any) => any) | undefined; | ||
onEnd?: ((value: any) => any) | undefined; | ||
onCancel?: (() => any) | undefined; | ||
onWheel?: ((value: any) => any) | undefined; | ||
onClick?: ((newValue: any, oldValue: any) => any) | undefined; | ||
}, { | ||
options: ScrollPickerOptionable[]; | ||
@@ -104,0 +123,0 @@ dragSensitivity: number; |
import { App, Plugin } from 'vue'; | ||
import VueScrollPicker from './components/picker'; | ||
import VueScrollPicker, { type ScrollPickerOption, type ScrollPickerOptionable } from './components/picker'; | ||
export declare function install(app: App): void; | ||
declare const plugin: Plugin; | ||
export default plugin; | ||
export interface VueScrollPickerOption { | ||
name: string; | ||
value: any; | ||
} | ||
export { VueScrollPicker, }; | ||
export type VueScrollPickerOption = ScrollPickerOption; | ||
export type VueScrollPickerOptionable = ScrollPickerOptionable; |
@@ -1,2 +0,2 @@ | ||
(function(a,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],l):(a=typeof globalThis<"u"?globalThis:a||self,l(a.VueScrollPicker={},a.Vue))})(this,function(a,l){"use strict";const g="";function p(t,e=83){let i=null;return function(){i&&(clearTimeout(i),i=null);const n=this,s=arguments;i=setTimeout(()=>t.apply(n,s),e)}}function v(t){const{top:e,bottom:i}=t.getBoundingClientRect();return(e+i)/2}function d(t){return t.map(e=>{switch(typeof e){case"string":return{value:e,name:e};case"number":case"boolean":return{value:e,name:`${e}`}}return e})}function f(t){return t.changedTouches||t.touches}function c(t){return f(t)?t.changedTouches[0]||t.touches[0]:t}const m=l.defineComponent({props:{modelValue:null,options:{type:Array,default:()=>[]},dragSensitivity:{type:Number,default:1.7},touchSensitivity:{type:Number,default:1.7},scrollSensitivity:{type:Number,default:1},empty:{type:String,default:"No Items"},placeholder:{type:String,default:null}},data(){var n,s;const t=d(this.options);let e=t.findIndex(o=>o.value==this.modelValue);e===-1&&!this.placeholder&&!this.$slots.placeholder&&this.options.length>0&&(e=0);const i=(s=(n=t[e])==null?void 0:n.value)!=null?s:null;return{refItems:[],internalOptions:t,internalIndex:e,internalValue:i,pivots:[],pivotsMin:0,pivotsMax:0,scroll:null,scrollOffsetTop:0,scrollMin:0,scrollMax:0,transitioning:!1,transitionTimer:null,start:null,isDragging:!1}},computed:{hasPlaceholder(){return!!(this.placeholder||this.$slots.placeholder)}},watch:{modelValue(t){if(t==null&&this.hasPlaceholder){this.correction(-1);return}const e=this.internalOptions.findIndex(i=>i.value==t);if(e===-1){this.$emit("update:modelValue",this.internalValue);return}this.internalIndex!==e&&this.correction(e)},options:{handler(t){var s,o;const e=this.internalOptions=d(t);let i=e.findIndex(r=>r.value==this.modelValue);i===-1&&!this.hasPlaceholder&&this.options.length>0&&(i=0);const n=(o=(s=e[i])==null?void 0:s.value)!=null?o:null;this.$nextTick(()=>{this.calculatePivots(),this.scroll=this.findScrollByIndex(i),this.internalIndex=i,this.internalValue!==n&&this.$emit("update:modelValue",this.internalValue=n)})},deep:!0}},beforeUpdate(){this.refItems=[]},mounted(){this.calculatePivots(),this.scroll=this.findScrollByIndex(this.internalIndex),this.internalValue!==this.modelValue&&this.$emit("update:modelValue",this.internalValue);const t=this.$el;t.addEventListener("touchstart",this.onStart),t.addEventListener("touchmove",this.onMove),t.addEventListener("touchend",this.onEnd),t.addEventListener("touchcancel",this.onCancel),"onwheel"in t?t.addEventListener("wheel",this.onWheel):"onmousewheel"in t?t.addEventListener("mousewheel",this.onWheel):"onDOMMouseScroll"in t&&t.addEventListener("DOMMouseScroll",this.onWheel),t.addEventListener("mousedown",this.onStart),document.addEventListener("mousemove",this.onMove),document.addEventListener("mouseup",this.onEnd),document.addEventListener("mouseout",this.onDocumentMouseOut)},beforeUnmount(){const t=this.$el;t.removeEventListener("touchstart",this.onStart),t.removeEventListener("touchmove",this.onMove),t.removeEventListener("touchend",this.onEnd),t.removeEventListener("touchcancel",this.onCancel),"onwheel"in t?t.removeEventListener("wheel",this.onWheel):"onmousewheel"in t?t.removeEventListener("mousewheel",this.onWheel):"onDOMMouseScroll"in t&&t.removeEventListener("DOMMouseScroll",this.onWheel),t.removeEventListener("mousedown",this.onStart),document.removeEventListener("mousemove",this.onMove),document.removeEventListener("mouseup",this.onEnd),document.removeEventListener("mouseout",this.onDocumentMouseOut)},methods:{setRefItem(t){this.refItems.push(t)},resize(){this.$nextTick(()=>{this.calculatePivots(),this.scroll=this.findScrollByIndex(this.internalIndex)})},calculatePivots(){const t=this.$refs.rotator,e=this.$refs.layerSelection,i=t.getBoundingClientRect().top,n=this.pivots=this.refItems.map(h=>v(h)-i).sort((h,I)=>h-I),s=this.pivotsMin=Math.min(...n),o=this.pivotsMax=Math.max(...n),r=this.scrollOffsetTop=e.offsetTop+e.offsetHeight/2;this.scrollMin=r-s,this.scrollMax=r-o},sanitizeInternalIndex(t){return Math.min(Math.max(t,this.hasPlaceholder?-1:0),this.internalOptions.length-1)},findIndexFromScroll(t){let e=null,i=0;return this.pivots.forEach((n,s)=>{const o=n+t-this.scrollOffsetTop;(e===null||Math.abs(e)>Math.abs(o))&&(i=s,e=o)}),this.hasPlaceholder||this.options.length===0?i-1:i},findScrollByIndex(t){let e=t;return(this.hasPlaceholder||this.options.length===0)&&e++,t>-1&&e in this.pivots?this.scrollOffsetTop-this.pivots[e]:t>=this.pivots.length?this.scrollOffsetTop-this.pivotsMax:this.scrollOffsetTop-this.pivotsMin},onWheel(t){var r,h;if(this.scroll>=this.scrollMin&&t.deltaY<0||this.scroll<=this.scrollMax&&t.deltaY>0||this.pivots.length===1)return;t.preventDefault();const e=this.sanitizeInternalIndex(this.internalIndex+(t.deltaY>0?1:-1)),i=t.deltaY>0?this.findScrollByIndex(e-1)-this.findScrollByIndex(e):this.findScrollByIndex(e)-this.findScrollByIndex(e+1),n=Math.max(Math.min(t.deltaY,i),i*-1);this.scroll=Math.min(Math.max(this.scroll-n*this.scrollSensitivity,this.scrollMax),this.scrollMin);const s=this.sanitizeInternalIndex(this.findIndexFromScroll(this.scroll)),o=(h=(r=this.internalOptions[s])==null?void 0:r.value)!=null?h:null;this.internalIndex=s,this.internalValue!==o&&this.$emit("update:modelValue",this.internalValue=o),this.onAfterWheel(()=>{this.correction(this.findIndexFromScroll(this.scroll))})},onAfterWheel:p(t=>{t()},200),onStart(t){t.cancelable&&t.preventDefault();const{clientY:e}=c(t);this.start=[this.scroll,e],this.isDragging=!1},onMove(t){if(!this.start)return;t.cancelable&&t.preventDefault();const{clientY:e}=c(t),i=e-this.start[1];Math.abs(i)>1.5&&(this.isDragging=!0),this.scroll=this.start[0]+i*(f(t)?this.touchSensitivity:this.dragSensitivity)},onEnd(t){!this.start||(t.cancelable&&t.preventDefault(),this.isDragging?this.correction(this.findIndexFromScroll(this.scroll)):this.onClick(t),this.start=null,this.isDragging=!1)},onDocumentMouseOut(t){var e;(t.relatedTarget===null||((e=t.relatedTarget)==null?void 0:e.nodeName)==="HTML")&&this.onCancel(t)},onCancel(t){t.cancelable&&t.preventDefault(),this.correction(this.internalIndex),this.start=null,this.isDragging=!1},onClick(t){const e=this.$refs.layerTop,i=this.$refs.layerBottom,{clientX:n,clientY:s}=c(t),o=e.getBoundingClientRect(),r=i.getBoundingClientRect();o.left<=n&&n<=o.right&&o.top<=s&&s<=o.bottom?this.correction(this.internalIndex-1):r.left<=n&&n<=r.right&&r.top<=s&&s<=r.bottom&&this.correction(this.internalIndex+1)},correction(t){var n,s;const e=this.sanitizeInternalIndex(t),i=(s=(n=this.internalOptions[e])==null?void 0:n.value)!=null?s:null;this.scroll=this.findScrollByIndex(e),this.transitioning=!0,this.transitionTimer&&(clearTimeout(this.transitionTimer),this.transitionTimer=null),this.transitionTimer=setTimeout(()=>{this.transitioning=!1,this.transitionTimer=null,this.internalIndex=e,this.internalValue!==i&&this.$emit("update:modelValue",this.internalValue=i)},100)}},render(){let t=[];return this.hasPlaceholder?t.push(l.h("div",{class:["vue-scroll-picker-item","vue-scroll-picker-item-placeholder",{"vue-scroll-picker-item-selected":this.internalIndex===-1}],ref:e=>e&&this.setRefItem(e)},l.renderSlot(this.$slots,"placeholder",{text:this.placeholder},()=>[this.placeholder]))):this.internalOptions.length===0&&t.push(l.h("div",{class:["vue-scroll-picker-item","vue-scroll-picker-item-empty","vue-scroll-picker-item-selected"],ref:e=>e&&this.setRefItem(e)},l.renderSlot(this.$slots,"empty",{text:this.empty},()=>[this.empty]))),t=t.concat(this.internalOptions.map((e,i)=>l.h("div",{class:["vue-scroll-picker-item",{"vue-scroll-picker-item-selected":this.internalIndex===i}],key:e.value,ref:n=>n&&this.setRefItem(n)},l.renderSlot(this.$slots,"default",{option:e},()=>[e.name])))),l.h("div",{class:["vue-scroll-picker"]},[l.h("div",{ref:"rotator",class:["vue-scroll-picker-rotator",{"vue-scroll-picker-rotator-transition":this.transitioning}],style:typeof this.scroll=="number"?{top:`${this.scroll}px`}:{}},t),l.h("div",{class:["vue-scroll-picker-layer"]},[l.h("div",{class:["vue-scroll-picker-layer-top"],ref:"layerTop"}),l.h("div",{class:["vue-scroll-picker-layer-selection"],ref:"layerSelection"}),l.h("div",{class:["vue-scroll-picker-layer-bottom"],ref:"layerBottom"})])])}});function u(t){t.component("VueScrollPicker",m)}typeof window<"u"&&window.Vue&&u(window.Vue);const x={install:u};a.VueScrollPicker=m,a.default=x,a.install=u,Object.defineProperties(a,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
(function(h,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],o):(h=typeof globalThis<"u"?globalThis:h||self,o(h.VueScrollPicker={},h.Vue))})(this,function(h,o){"use strict";const y="";function v(e,t=83){let i=null;return function(){i&&(clearTimeout(i),i=null);const n=this,l=arguments;i=setTimeout(()=>e.apply(n,l),t)}}function I(e){const{top:t,bottom:i}=e.getBoundingClientRect();return(t+i)/2}function f(e){return e.map(t=>{switch(typeof t){case"string":return{value:t,name:t};case"number":case"boolean":return{value:t,name:`${t}`}}return t})}function m(e){return e.changedTouches||e.touches}function c(e){return m(e)?e.changedTouches[0]||e.touches[0]:e}const p=o.defineComponent({props:{modelValue:null,options:{type:Array,default:()=>[]},dragSensitivity:{type:Number,default:1.7},touchSensitivity:{type:Number,default:1.7},scrollSensitivity:{type:Number,default:1},empty:{type:String,default:"No Items"},placeholder:{type:String,default:null}},emits:{"update:modelValue":e=>!0,start:()=>!0,move:e=>!0,end:e=>!0,cancel:()=>!0,wheel:e=>!0,click:(e,t)=>!0},data(){var n;const e=f(this.options);let t=e.findIndex(l=>l.value==this.modelValue);t===-1&&!this.placeholder&&!this.$slots.placeholder&&this.options.length>0&&(t=0);const i=((n=e[t])==null?void 0:n.value)??null;return{refItems:[],internalOptions:e,internalIndex:t,internalValue:i,bounds:[],boundMin:0,boundMax:0,scroll:null,scrollOffsetTop:0,scrollMin:0,scrollMax:0,transitionTimeout:null,start:null,isDragging:!1}},computed:{hasPlaceholder(){return!!(this.placeholder||this.$slots.placeholder)}},watch:{modelValue(e){if(e==null&&this.hasPlaceholder){this.scrollTo(this.findScrollByIndex(-1));return}const t=this.internalOptions.findIndex(i=>i.value==e);if(t===-1){this.$emit("update:modelValue",this.internalValue);return}this.internalIndex!==t&&this.scrollTo(this.findScrollByIndex(t))},options:{handler(e){var l;const t=this.internalOptions=f(e);let i=t.findIndex(s=>s.value==this.modelValue);i===-1&&!this.hasPlaceholder&&this.options.length>0&&(i=0);const n=((l=t[i])==null?void 0:l.value)??null;this.$nextTick(()=>{this.calculateBounds(),this.scroll=this.findScrollByIndex(i),this.internalIndex=i,this.internalValue!==n&&this.$emit("update:modelValue",this.internalValue=n)})},deep:!0}},beforeUpdate(){this.refItems=[]},mounted(){this.calculateBounds(),this.scroll=this.findScrollByIndex(this.internalIndex),this.internalValue!==this.modelValue&&this.$emit("update:modelValue",this.internalValue);const e=this.$el;e.addEventListener("touchstart",this.onStart),e.addEventListener("touchmove",this.onMove),e.addEventListener("touchend",this.onEnd),e.addEventListener("touchcancel",this.onCancel),"onwheel"in e?e.addEventListener("wheel",this.onWheel):"onmousewheel"in e?e.addEventListener("mousewheel",this.onWheel):"onDOMMouseScroll"in e&&e.addEventListener("DOMMouseScroll",this.onWheel),e.addEventListener("mousedown",this.onStart),document.addEventListener("mousemove",this.onMove),document.addEventListener("mouseup",this.onEnd),document.addEventListener("mouseout",this.onDocumentMouseOut)},beforeUnmount(){const e=this.$el;e.removeEventListener("touchstart",this.onStart),e.removeEventListener("touchmove",this.onMove),e.removeEventListener("touchend",this.onEnd),e.removeEventListener("touchcancel",this.onCancel),"onwheel"in e?e.removeEventListener("wheel",this.onWheel):"onmousewheel"in e?e.removeEventListener("mousewheel",this.onWheel):"onDOMMouseScroll"in e&&e.removeEventListener("DOMMouseScroll",this.onWheel),e.removeEventListener("mousedown",this.onStart),document.removeEventListener("mousemove",this.onMove),document.removeEventListener("mouseup",this.onEnd),document.removeEventListener("mouseout",this.onDocumentMouseOut)},methods:{setRefItem(e){this.refItems.push(e)},resize(){this.$nextTick(()=>{this.calculateBounds(),this.scroll=this.findScrollByIndex(this.internalIndex)})},calculateBounds(){const e=this.$refs.rotator,t=this.$refs.layerSelection,i=e.getBoundingClientRect().top,n=this.bounds=this.refItems.map(a=>I(a)-i).sort((a,d)=>a-d),l=this.boundMin=Math.min(...n),s=this.boundMax=Math.max(...n),r=this.scrollOffsetTop=t.offsetTop+t.offsetHeight/2;this.scrollMin=r-l,this.scrollMax=r-s},sanitizeInternalIndex(e){return Math.min(Math.max(e,this.hasPlaceholder?-1:0),this.internalOptions.length-1)},findIndexFromScroll(e){let t=null,i=0;return this.bounds.forEach((n,l)=>{const s=n+e-this.scrollOffsetTop;(t===null||Math.abs(t)>Math.abs(s))&&(i=l,t=s)}),this.hasPlaceholder||this.options.length===0?i-1:i},findScrollByIndex(e){let t=e;return(this.hasPlaceholder||this.options.length===0)&&t++,e>-1&&t in this.bounds?this.scrollOffsetTop-this.bounds[t]:e>=this.bounds.length?this.scrollOffsetTop-this.boundMax:this.scrollOffsetTop-this.boundMin},onWheel(e){if(this.scroll>=this.scrollMin&&e.deltaY<0||this.scroll<=this.scrollMax&&e.deltaY>0||this.bounds.length===1)return;e.preventDefault();const t=this.sanitizeInternalIndex(this.internalIndex+(e.deltaY>0?1:-1)),i=e.deltaY>0?this.findScrollByIndex(t-1)-this.findScrollByIndex(t):this.findScrollByIndex(t)-this.findScrollByIndex(t+1),n=Math.max(Math.min(e.deltaY,i),i*-1);this.scroll=Math.min(Math.max(this.scroll-n*this.scrollSensitivity,this.scrollMax),this.scrollMin);const l=this.sanitizeInternalIndex(this.findIndexFromScroll(this.scroll)),s=this.internalOptions[l],r=(s==null?void 0:s.value)??null;this.internalIndex=l,this.$emit("wheel",r),this.internalValue!==r&&!(s!=null&&s.disabled)&&this.$emit("update:modelValue",this.internalValue=r),this.onAfterWheel(()=>{this.correction(this.scroll)})},onAfterWheel:v(e=>{e()},200),onStart(e){e.cancelable&&e.preventDefault();const{clientY:t}=c(e);this.start=[this.scroll,t],this.isDragging=!1,this.$emit("start")},onMove(e){var s;if(!this.start)return;e.cancelable&&e.preventDefault();const{clientY:t}=c(e),i=t-this.start[1];Math.abs(i)>1.5&&(this.isDragging=!0),this.scroll=this.start[0]+i*(m(e)?this.touchSensitivity:this.dragSensitivity);const n=this.sanitizeInternalIndex(this.findIndexFromScroll(this.scroll)),l=((s=this.internalOptions[n])==null?void 0:s.value)??null;this.$emit("move",l)},onEnd(e){this.start&&(e.cancelable&&e.preventDefault(),this.isDragging?this.correction(this.scroll):this.onClick(e),this.start=null,this.isDragging=!1,this.$emit("end",this.internalValue))},onDocumentMouseOut(e){var t;(e.relatedTarget===null||((t=e.relatedTarget)==null?void 0:t.nodeName)==="HTML")&&this.onCancel(e)},onCancel(e){e.cancelable&&e.preventDefault(),this.scrollTo(this.findScrollByIndex(this.internalIndex)),this.start=null,this.isDragging=!1,this.$emit("cancel")},onClick(e){const t=this.$refs.layerTop,i=this.$refs.layerBottom,{clientX:n,clientY:l}=c(e),s=t.getBoundingClientRect(),r=i.getBoundingClientRect();let a=this.internalIndex;if(s.left<=n&&n<=s.right&&s.top<=l&&l<=s.bottom){if(this.internalIndex===(this.hasPlaceholder?-1:0))return;for(a--;this.internalOptions[a]&&this.internalOptions[a].disabled;)a--}else if(r.left<=n&&n<=r.right&&r.top<=l&&l<=r.bottom){if(this.internalIndex===this.internalOptions.length-1)return;for(a++;this.internalOptions[a]&&this.internalOptions[a].disabled;)a++}if(this.internalIndex!==a&&this.internalOptions[a]){const d=this.internalValue,x=this.internalOptions[a].value;this.scrollTo(this.findScrollByIndex(a),()=>{this.internalIndex=a,this.emitModalValue(x)}),this.$emit("click",x,d)}},correction(e){var l;const t=this.hasPlaceholder||this.options.length===0?1:0,i=this.bounds.map((s,r)=>[r-t,s+e-this.scrollOffsetTop]).sort((s,r)=>Math.abs(s[1])-Math.abs(r[1])).map(([s])=>s);console.log(JSON.stringify(i));let n=0;for(;i[n]!=null&&this.internalOptions[i[n]]&&this.internalOptions[i[n]].disabled;)console.log("skip",i[n]),n++;if(i[n]===-1||i[n]!=null&&this.internalOptions[i[n]]){const s=i[n],r=((l=this.internalOptions[s])==null?void 0:l.value)??null;this.scrollTo(this.findScrollByIndex(s),()=>{this.internalIndex=s,this.emitModalValue(r)})}else this.scrollTo(this.findScrollByIndex(this.internalIndex))},scrollTo(e,t){this.scroll=e,this.transitionTimeout&&clearTimeout(this.transitionTimeout),this.transitionTimeout=setTimeout(()=>{this.transitionTimeout=null,t==null||t()},100)},emitModalValue(e){this.internalValue!==e&&this.$emit("update:modelValue",this.internalValue=e)}},render(){let e=[];return this.hasPlaceholder?e.push(o.h("div",{class:["vue-scroll-picker-item","vue-scroll-picker-item-placeholder",{"vue-scroll-picker-item-selected":this.internalIndex===-1}],ref:t=>t&&this.setRefItem(t)},o.renderSlot(this.$slots,"placeholder",{text:this.placeholder},()=>[this.placeholder]))):this.internalOptions.length===0&&e.push(o.h("div",{class:["vue-scroll-picker-item","vue-scroll-picker-item-empty","vue-scroll-picker-item-selected"],ref:t=>t&&this.setRefItem(t)},o.renderSlot(this.$slots,"empty",{text:this.empty},()=>[this.empty]))),e=e.concat(this.internalOptions.map((t,i)=>o.h("div",{class:["vue-scroll-picker-item",{"vue-scroll-picker-item-selected":this.internalIndex===i,"vue-scroll-picker-item-disabled":t.disabled}],key:t.value,ref:n=>n&&this.setRefItem(n)},o.renderSlot(this.$slots,"default",{option:t},()=>[t.name])))),o.h("div",{class:["vue-scroll-picker"]},[o.h("div",{ref:"rotator",class:["vue-scroll-picker-rotator",{"vue-scroll-picker-rotator-transition":this.transitionTimeout}],style:typeof this.scroll=="number"?{top:`${this.scroll}px`}:{}},e),o.h("div",{class:["vue-scroll-picker-layer"]},[o.h("div",{class:["vue-scroll-picker-layer-top"],ref:"layerTop"}),o.h("div",{class:["vue-scroll-picker-layer-selection"],ref:"layerSelection"}),o.h("div",{class:["vue-scroll-picker-layer-bottom"],ref:"layerBottom"})])])}});function u(e){e.component("VueScrollPicker",p)}typeof window<"u"&&window.Vue&&u(window.Vue);const g={install:u};h.VueScrollPicker=p,h.default=g,h.install=u,Object.defineProperties(h,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
//# sourceMappingURL=vue-scroll-picker.umd.js.map |
{ | ||
"name": "vue-scroll-picker", | ||
"version": "1.1.4", | ||
"version": "1.2.0", | ||
"description": "iOS Style Scroll Picker Component for Vue 3. Support All Gestures of Mouse(also MouseWheel) and Touch.", | ||
@@ -28,13 +28,13 @@ "author": "Changwan Jun", | ||
"@commitlint/config-conventional": "13.2.0", | ||
"@types/node": "18.19.14", | ||
"@types/node": "18.19.31", | ||
"@types/simple-icons": "5.21.1", | ||
"@typescript-eslint/eslint-plugin": "5.62.0", | ||
"@typescript-eslint/parser": "5.62.0", | ||
"@vitejs/plugin-vue": "3.2.0", | ||
"@vue/compiler-sfc": "3.4.15", | ||
"@vitejs/plugin-vue": "^4.6.2", | ||
"@vue/compiler-sfc": "3.4.21", | ||
"@vue/eslint-config-typescript": "11.0.3", | ||
"eslint": "8.56.0", | ||
"eslint": "8.57.0", | ||
"eslint-config-prettier": "8.10.0", | ||
"eslint-plugin-prettier": "4.2.1", | ||
"eslint-plugin-vue": "9.21.1", | ||
"eslint-plugin-vue": "9.24.1", | ||
"gh-pages": "3.2.3", | ||
@@ -44,6 +44,6 @@ "husky": "7.0.4", | ||
"simple-icons": "7.21.0", | ||
"typescript": "4.9.5", | ||
"vite": "3.2.8", | ||
"vue": "3.4.15" | ||
"typescript": "^4.9.5", | ||
"vite": "^4.5.2", | ||
"vue": "3.4.21" | ||
} | ||
} |
@@ -72,3 +72,3 @@ # Vue Scroll Picker | ||
| empty | `string` | `'No Items'` | | | ||
| options | `string[]`<br /> `{ name: string, value: any }[]` | `[]` | `["10KG", "20KG", "30KG"]`<br /> `[{value: 10, name: "10KG"}, {value: 20, name: "20KG"}]` | | ||
| options | `string[]`<br /> `{ name: string, value: any, disabled: boolean }[]` | `[]` | `["10KG", "20KG", "30KG"]`<br /> `[{value: 10, name: "10KG"}, {value: 20, name: "20KG"}]` | | ||
| dragSensitivity | `number` | `1.7` | | | ||
@@ -80,5 +80,11 @@ | touchSensitivity | `number` | `1.7` | | | ||
| Name | Type | | ||
| ----------------- | :---- | | ||
| update:modelValue | `any` | | ||
| Name | Type | | ||
| ----------------- | :------------------------------------ | | ||
| update:modelValue | `(value: any) => void` | | ||
| start | `() => void` | | ||
| move | `(value: any) => void` | | ||
| end | `(value: any) => void` | | ||
| cancel | `() => void` | | ||
| wheel | `(value: any) => void` | | ||
| click | `(value: any, oldValue: any) => void` | | ||
@@ -85,0 +91,0 @@ ### Slots |
@@ -53,2 +53,3 @@ import { defineComponent, PropType, h, renderSlot, VNode } from 'vue' | ||
value: any | ||
disabled?: boolean | ||
} | ||
@@ -86,2 +87,16 @@ | ||
}, | ||
emits: { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any | ||
'update:modelValue': (value: any) => true, | ||
'start': () => true, | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any | ||
'move': (value: any) => true, | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any | ||
'end': (value: any) => true, | ||
'cancel': () => true, | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any | ||
'wheel': (value: any) => true, | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any | ||
'click': (newValue: any, oldValue: any) => true, | ||
}, | ||
data() { | ||
@@ -103,5 +118,5 @@ const internalOptions = normalizeOptions(this.options) | ||
pivots: [] as number[], | ||
pivotsMin: 0, | ||
pivotsMax: 0, | ||
bounds: [] as number[], | ||
boundMin: 0, | ||
boundMax: 0, | ||
@@ -113,4 +128,3 @@ scroll: null as number | null, | ||
transitioning: false, | ||
transitionTimer: null as any | null, | ||
transitionTimeout: null as ReturnType<typeof setTimeout> | null, | ||
@@ -130,3 +144,3 @@ start: null as [scroll: number, clientY: number] | null, | ||
if ((value === null || value === undefined) && this.hasPlaceholder) { | ||
this.correction(-1) | ||
this.scrollTo(this.findScrollByIndex(-1)) | ||
return | ||
@@ -142,3 +156,3 @@ } | ||
if (this.internalIndex !== nextInternalIndex) { | ||
this.correction(nextInternalIndex) | ||
this.scrollTo(this.findScrollByIndex(nextInternalIndex)) | ||
} | ||
@@ -157,3 +171,3 @@ }, | ||
this.$nextTick(() => { | ||
this.calculatePivots() | ||
this.calculateBounds() | ||
this.scroll = this.findScrollByIndex(internalIndex) | ||
@@ -173,3 +187,3 @@ this.internalIndex = internalIndex | ||
mounted() { | ||
this.calculatePivots() | ||
this.calculateBounds() | ||
this.scroll = this.findScrollByIndex(this.internalIndex) | ||
@@ -227,7 +241,7 @@ if (this.internalValue !== this.modelValue) { | ||
this.$nextTick(() => { | ||
this.calculatePivots() | ||
this.calculateBounds() | ||
this.scroll = this.findScrollByIndex(this.internalIndex) | ||
}) | ||
}, | ||
calculatePivots() { | ||
calculateBounds() { | ||
const $rotator = this.$refs.rotator as HTMLDivElement | ||
@@ -237,10 +251,10 @@ const $layerSelection = this.$refs.layerSelection as HTMLDivElement | ||
const rotatorTop = $rotator.getBoundingClientRect().top | ||
const pivots = this.pivots = this.refItems.map((item) => getBoundingClientCenterY(item) - rotatorTop).sort((a, b) => a - b) | ||
const pivotsMin = this.pivotsMin = Math.min(...pivots) | ||
const pivotsMax = this.pivotsMax = Math.max(...pivots) | ||
const bounds = this.bounds = this.refItems.map((item) => getBoundingClientCenterY(item) - rotatorTop).sort((a, b) => a - b) | ||
const boundMin = this.boundMin = Math.min(...bounds) | ||
const boundMax = this.boundMax = Math.max(...bounds) | ||
const scrollOffsetTop = this.scrollOffsetTop = $layerSelection.offsetTop + $layerSelection.offsetHeight / 2 | ||
this.scrollMin = scrollOffsetTop - pivotsMin | ||
this.scrollMax = scrollOffsetTop - pivotsMax | ||
this.scrollMin = scrollOffsetTop - boundMin | ||
this.scrollMax = scrollOffsetTop - boundMax | ||
}, | ||
@@ -252,7 +266,7 @@ sanitizeInternalIndex(index: number): number { | ||
let prevDiff = null as number | null | ||
let pivotIndex = 0 | ||
this.pivots.forEach((pivot, i) => { | ||
const diff = pivot + scroll - this.scrollOffsetTop | ||
let boundIndex = 0 | ||
this.bounds.forEach((bound, i) => { | ||
const diff = bound + scroll - this.scrollOffsetTop | ||
if (prevDiff === null || Math.abs(prevDiff) > Math.abs(diff)) { | ||
pivotIndex = i | ||
boundIndex = i | ||
prevDiff = diff | ||
@@ -262,18 +276,18 @@ } | ||
if (this.hasPlaceholder || this.options.length === 0) { | ||
return pivotIndex - 1 | ||
return boundIndex - 1 | ||
} | ||
return pivotIndex | ||
return boundIndex | ||
}, | ||
findScrollByIndex(index: number): number { | ||
let pivotIndex = index | ||
let boundIndex = index | ||
if (this.hasPlaceholder || this.options.length === 0) { | ||
pivotIndex++ | ||
boundIndex++ | ||
} | ||
if (index > -1 && pivotIndex in this.pivots) { | ||
return this.scrollOffsetTop - this.pivots[pivotIndex] | ||
if (index > -1 && boundIndex in this.bounds) { | ||
return this.scrollOffsetTop - this.bounds[boundIndex] | ||
} | ||
if (index >= this.pivots.length) { | ||
return this.scrollOffsetTop - this.pivotsMax | ||
if (index >= this.bounds.length) { | ||
return this.scrollOffsetTop - this.boundMax | ||
} | ||
return this.scrollOffsetTop - this.pivotsMin | ||
return this.scrollOffsetTop - this.boundMin | ||
@@ -284,3 +298,3 @@ }, | ||
if (this.scroll! <= this.scrollMax && event.deltaY > 0) { return } | ||
if (this.pivots.length === 1) { return } | ||
if (this.bounds.length === 1) { return } | ||
@@ -299,6 +313,8 @@ event.preventDefault() | ||
const nextInternalIndex = this.sanitizeInternalIndex(this.findIndexFromScroll(this.scroll)) | ||
const nextInternalValue = this.internalOptions[nextInternalIndex]?.value ?? null | ||
const nextOption = this.internalOptions[nextInternalIndex] | ||
const nextInternalValue = nextOption?.value ?? null | ||
this.internalIndex = nextInternalIndex | ||
if (this.internalValue !== nextInternalValue) { | ||
this.$emit('wheel', nextInternalValue) | ||
if (this.internalValue !== nextInternalValue && !nextOption?.disabled) { | ||
this.$emit('update:modelValue', this.internalValue = nextInternalValue) | ||
@@ -308,3 +324,3 @@ } | ||
this.onAfterWheel(() => { | ||
this.correction(this.findIndexFromScroll(this.scroll!)) | ||
this.correction(this.scroll!) | ||
}) | ||
@@ -323,2 +339,3 @@ }, | ||
this.isDragging = false | ||
this.$emit('start') | ||
}, | ||
@@ -338,2 +355,6 @@ onMove(event: TouchEvent | MouseEvent) { | ||
this.scroll = this.start[0] + diff * (isTouchEvent(event) ? this.touchSensitivity : this.dragSensitivity) | ||
const nextInternalIndex = this.sanitizeInternalIndex(this.findIndexFromScroll(this.scroll)) | ||
const nextInternalValue = this.internalOptions[nextInternalIndex]?.value ?? null | ||
this.$emit('move', nextInternalValue) | ||
}, | ||
@@ -348,3 +369,3 @@ onEnd(event: TouchEvent | MouseEvent) { | ||
if (this.isDragging) { | ||
this.correction(this.findIndexFromScroll(this.scroll!)) | ||
this.correction(this.scroll!) | ||
} else { | ||
@@ -355,2 +376,3 @@ this.onClick(event) | ||
this.isDragging = false | ||
this.$emit('end', this.internalValue) | ||
}, | ||
@@ -366,5 +388,6 @@ onDocumentMouseOut(event: MouseEvent) { | ||
} | ||
this.correction(this.internalIndex) // cancel (rollback) | ||
this.scrollTo(this.findScrollByIndex(this.internalIndex)) | ||
this.start = null | ||
this.isDragging = false | ||
this.$emit('cancel') | ||
}, | ||
@@ -378,29 +401,77 @@ onClick(event: TouchEvent | MouseEvent) { | ||
let nextIndex = this.internalIndex | ||
if (topRect.left <= x && x <= topRect.right && topRect.top <= y && y <= topRect.bottom) { | ||
this.correction(this.internalIndex - 1) | ||
if (this.internalIndex === (this.hasPlaceholder ? -1 : 0)) { | ||
return // top | ||
} | ||
nextIndex-- | ||
while (this.internalOptions[nextIndex] && this.internalOptions[nextIndex].disabled) { | ||
nextIndex-- | ||
} | ||
} else if (bottomRect.left <= x && x <= bottomRect.right && bottomRect.top <= y && y <= bottomRect.bottom) { | ||
this.correction(this.internalIndex + 1) | ||
if (this.internalIndex === this.internalOptions.length - 1) { | ||
return // bottom | ||
} | ||
nextIndex++ | ||
while (this.internalOptions[nextIndex] && this.internalOptions[nextIndex].disabled) { | ||
nextIndex++ | ||
} | ||
} | ||
if (this.internalIndex !== nextIndex && this.internalOptions[nextIndex]) { | ||
const value = this.internalValue | ||
const nextValue = this.internalOptions[nextIndex].value | ||
this.scrollTo(this.findScrollByIndex(nextIndex), () => { | ||
this.internalIndex = nextIndex | ||
this.emitModalValue(nextValue) | ||
}) | ||
this.$emit('click', nextValue, value) | ||
} | ||
}, | ||
correction(index: number) { | ||
const nextInternalIndex = this.sanitizeInternalIndex(index) | ||
const nextInternalValue = this.internalOptions[nextInternalIndex]?.value ?? null | ||
this.scroll = this.findScrollByIndex(nextInternalIndex) | ||
correction(scroll: number) { | ||
const indexOffset = this.hasPlaceholder || this.options.length === 0 ? 1 : 0 | ||
const indexes = this.bounds | ||
.map((bound, i) => [i - indexOffset, bound + scroll - this.scrollOffsetTop]) // [index, diff] | ||
.sort((a, b) => Math.abs(a[1]) - Math.abs(b[1])) // nearest diff | ||
.map(([i]) => i) // index | ||
this.transitioning = true | ||
if (this.transitionTimer) { | ||
clearTimeout(this.transitionTimer) | ||
this.transitionTimer = null | ||
console.log(JSON.stringify(indexes)) | ||
let indexCursor = 0 | ||
while ( | ||
indexes[indexCursor] != null | ||
&& this.internalOptions[indexes[indexCursor]] | ||
&& this.internalOptions[indexes[indexCursor]].disabled | ||
) { | ||
console.log('skip', indexes[indexCursor]) | ||
indexCursor++ | ||
} | ||
this.transitionTimer = setTimeout(() => { | ||
this.transitioning = false | ||
this.transitionTimer = null | ||
this.internalIndex = nextInternalIndex | ||
if (this.internalValue !== nextInternalValue) { | ||
this.$emit('update:modelValue', this.internalValue = nextInternalValue) | ||
} | ||
if ( | ||
indexes[indexCursor] === -1 | ||
|| indexes[indexCursor] != null && this.internalOptions[indexes[indexCursor]] | ||
) { | ||
const nextIndex = indexes[indexCursor] | ||
const nextValue = this.internalOptions[nextIndex]?.value ?? null | ||
this.scrollTo(this.findScrollByIndex(nextIndex), () => { | ||
this.internalIndex = nextIndex | ||
this.emitModalValue(nextValue) | ||
}) | ||
} else { | ||
this.scrollTo(this.findScrollByIndex(this.internalIndex)) // cancel | ||
} | ||
}, | ||
scrollTo(scroll: number, onComplete?: () => void) { | ||
this.scroll = scroll | ||
if (this.transitionTimeout) { | ||
clearTimeout(this.transitionTimeout) | ||
} | ||
this.transitionTimeout = setTimeout(() => { | ||
this.transitionTimeout = null | ||
onComplete?.() | ||
}, 100) | ||
}, | ||
emitModalValue(value: unknown) { | ||
if (this.internalValue !== value) { | ||
this.$emit('update:modelValue', this.internalValue = value) | ||
} | ||
}, | ||
}, | ||
@@ -441,2 +512,3 @@ render() { | ||
'vue-scroll-picker-item-selected': this.internalIndex === index, | ||
'vue-scroll-picker-item-disabled': option.disabled, | ||
}, | ||
@@ -460,3 +532,3 @@ ], | ||
{ | ||
'vue-scroll-picker-rotator-transition': this.transitioning, | ||
'vue-scroll-picker-rotator-transition': this.transitionTimeout, | ||
}, | ||
@@ -463,0 +535,0 @@ ], |
import { App, Plugin } from 'vue' | ||
import VueScrollPicker from './components/picker' | ||
import VueScrollPicker, { type ScrollPickerOption, type ScrollPickerOptionable } from './components/picker' | ||
export function install(app: App) { | ||
@@ -20,10 +19,7 @@ app.component('VueScrollPicker', VueScrollPicker) | ||
// re-define: https://github.com/vitejs/vite/issues/2117 | ||
export interface VueScrollPickerOption { | ||
name: string | ||
value: any | ||
} | ||
export { | ||
VueScrollPicker, | ||
} | ||
export type VueScrollPickerOption = ScrollPickerOption | ||
export type VueScrollPickerOptionable = ScrollPickerOptionable |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
115684
18.42%1063
15.42%96
6.67%1
Infinity%