UNPKG

8.27 kBJavaScriptView Raw
1var __defProp = Object.defineProperty;
2var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3var __getOwnPropNames = Object.getOwnPropertyNames;
4var __hasOwnProp = Object.prototype.hasOwnProperty;
5var __export = (target, all) => {
6 for (var name2 in all)
7 __defProp(target, name2, { get: all[name2], enumerable: true });
8};
9var __copyProps = (to, from, except, desc) => {
10 if (from && typeof from === "object" || typeof from === "function") {
11 for (let key of __getOwnPropNames(from))
12 if (!__hasOwnProp.call(to, key) && key !== except)
13 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14 }
15 return to;
16};
17var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18var stdin_exports = {};
19__export(stdin_exports, {
20 PICKER_KEY: () => PICKER_KEY,
21 default: () => stdin_default
22});
23module.exports = __toCommonJS(stdin_exports);
24var import_vue = require("vue");
25var import_utils = require("../utils");
26var import_utils2 = require("./utils");
27var import_use = require("@vant/use");
28var import_use_touch = require("../composables/use-touch");
29var import_use_expose = require("../composables/use-expose");
30const DEFAULT_DURATION = 200;
31const MOMENTUM_TIME = 300;
32const MOMENTUM_DISTANCE = 15;
33const [name, bem] = (0, import_utils.createNamespace)("picker-column");
34const PICKER_KEY = Symbol(name);
35var stdin_default = (0, import_vue.defineComponent)({
36 name,
37 props: {
38 value: import_utils.numericProp,
39 fields: (0, import_utils.makeRequiredProp)(Object),
40 options: (0, import_utils.makeArrayProp)(),
41 readonly: Boolean,
42 allowHtml: Boolean,
43 optionHeight: (0, import_utils.makeRequiredProp)(Number),
44 swipeDuration: (0, import_utils.makeRequiredProp)(import_utils.numericProp),
45 visibleOptionNum: (0, import_utils.makeRequiredProp)(import_utils.numericProp)
46 },
47 emits: ["change", "clickOption", "scrollInto"],
48 setup(props, {
49 emit,
50 slots
51 }) {
52 let moving;
53 let startOffset;
54 let touchStartTime;
55 let momentumOffset;
56 let transitionEndTrigger;
57 const root = (0, import_vue.ref)();
58 const wrapper = (0, import_vue.ref)();
59 const currentOffset = (0, import_vue.ref)(0);
60 const currentDuration = (0, import_vue.ref)(0);
61 const touch = (0, import_use_touch.useTouch)();
62 const count = () => props.options.length;
63 const baseOffset = () => props.optionHeight * (+props.visibleOptionNum - 1) / 2;
64 const updateValueByIndex = (index) => {
65 let enabledIndex = (0, import_utils2.findIndexOfEnabledOption)(props.options, index);
66 const offset = -enabledIndex * props.optionHeight;
67 const trigger = () => {
68 if (enabledIndex > count() - 1) {
69 enabledIndex = (0, import_utils2.findIndexOfEnabledOption)(props.options, index);
70 }
71 const value = props.options[enabledIndex][props.fields.value];
72 if (value !== props.value) {
73 emit("change", value);
74 }
75 };
76 if (moving && offset !== currentOffset.value) {
77 transitionEndTrigger = trigger;
78 } else {
79 trigger();
80 }
81 currentOffset.value = offset;
82 };
83 const isReadonly = () => props.readonly || !props.options.length;
84 const onClickOption = (index) => {
85 if (moving || isReadonly()) {
86 return;
87 }
88 transitionEndTrigger = null;
89 currentDuration.value = DEFAULT_DURATION;
90 updateValueByIndex(index);
91 emit("clickOption", props.options[index]);
92 };
93 const getIndexByOffset = (offset) => (0, import_utils.clamp)(Math.round(-offset / props.optionHeight), 0, count() - 1);
94 const currentIndex = (0, import_vue.computed)(() => getIndexByOffset(currentOffset.value));
95 const momentum = (distance, duration) => {
96 const speed = Math.abs(distance / duration);
97 distance = currentOffset.value + speed / 3e-3 * (distance < 0 ? -1 : 1);
98 const index = getIndexByOffset(distance);
99 currentDuration.value = +props.swipeDuration;
100 updateValueByIndex(index);
101 };
102 const stopMomentum = () => {
103 moving = false;
104 currentDuration.value = 0;
105 if (transitionEndTrigger) {
106 transitionEndTrigger();
107 transitionEndTrigger = null;
108 }
109 };
110 const onTouchStart = (event) => {
111 if (isReadonly()) {
112 return;
113 }
114 touch.start(event);
115 if (moving) {
116 const translateY = (0, import_utils2.getElementTranslateY)(wrapper.value);
117 currentOffset.value = Math.min(0, translateY - baseOffset());
118 }
119 currentDuration.value = 0;
120 startOffset = currentOffset.value;
121 touchStartTime = Date.now();
122 momentumOffset = startOffset;
123 transitionEndTrigger = null;
124 };
125 const onTouchMove = (event) => {
126 if (isReadonly()) {
127 return;
128 }
129 touch.move(event);
130 if (touch.isVertical()) {
131 moving = true;
132 (0, import_utils.preventDefault)(event, true);
133 }
134 const newOffset = (0, import_utils.clamp)(startOffset + touch.deltaY.value, -(count() * props.optionHeight), props.optionHeight);
135 const newIndex = getIndexByOffset(newOffset);
136 if (newIndex !== currentIndex.value) {
137 emit("scrollInto", props.options[newIndex]);
138 }
139 currentOffset.value = newOffset;
140 const now = Date.now();
141 if (now - touchStartTime > MOMENTUM_TIME) {
142 touchStartTime = now;
143 momentumOffset = newOffset;
144 }
145 };
146 const onTouchEnd = () => {
147 if (isReadonly()) {
148 return;
149 }
150 const distance = currentOffset.value - momentumOffset;
151 const duration = Date.now() - touchStartTime;
152 const startMomentum = duration < MOMENTUM_TIME && Math.abs(distance) > MOMENTUM_DISTANCE;
153 if (startMomentum) {
154 momentum(distance, duration);
155 return;
156 }
157 const index = getIndexByOffset(currentOffset.value);
158 currentDuration.value = DEFAULT_DURATION;
159 updateValueByIndex(index);
160 setTimeout(() => {
161 moving = false;
162 }, 0);
163 };
164 const renderOptions = () => {
165 const optionStyle = {
166 height: `${props.optionHeight}px`
167 };
168 return props.options.map((option, index) => {
169 const text = option[props.fields.text];
170 const {
171 disabled
172 } = option;
173 const value = option[props.fields.value];
174 const data = {
175 role: "button",
176 style: optionStyle,
177 tabindex: disabled ? -1 : 0,
178 class: [bem("item", {
179 disabled,
180 selected: value === props.value
181 }), option.className],
182 onClick: () => onClickOption(index)
183 };
184 const childData = {
185 class: "van-ellipsis",
186 [props.allowHtml ? "innerHTML" : "textContent"]: text
187 };
188 return (0, import_vue.createVNode)("li", data, [slots.option ? slots.option(option, index) : (0, import_vue.createVNode)("div", childData, null)]);
189 });
190 };
191 (0, import_use.useParent)(PICKER_KEY);
192 (0, import_use_expose.useExpose)({
193 stopMomentum
194 });
195 (0, import_vue.watchEffect)(() => {
196 const index = moving ? Math.floor(-currentOffset.value / props.optionHeight) : props.options.findIndex((option) => option[props.fields.value] === props.value);
197 const enabledIndex = (0, import_utils2.findIndexOfEnabledOption)(props.options, index);
198 const offset = -enabledIndex * props.optionHeight;
199 if (moving && enabledIndex < index) stopMomentum();
200 currentOffset.value = offset;
201 });
202 (0, import_use.useEventListener)("touchmove", onTouchMove, {
203 target: root
204 });
205 return () => (0, import_vue.createVNode)("div", {
206 "ref": root,
207 "class": bem(),
208 "onTouchstartPassive": onTouchStart,
209 "onTouchend": onTouchEnd,
210 "onTouchcancel": onTouchEnd
211 }, [(0, import_vue.createVNode)("ul", {
212 "ref": wrapper,
213 "style": {
214 transform: `translate3d(0, ${currentOffset.value + baseOffset()}px, 0)`,
215 transitionDuration: `${currentDuration.value}ms`,
216 transitionProperty: currentDuration.value ? "all" : "none"
217 },
218 "class": bem("wrapper"),
219 "onTransitionend": stopMomentum
220 }, [renderOptions()])]);
221 }
222});