1 | var __defProp = Object.defineProperty;
|
2 | var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
3 | var __getOwnPropNames = Object.getOwnPropertyNames;
|
4 | var __hasOwnProp = Object.prototype.hasOwnProperty;
|
5 | var __export = (target, all) => {
|
6 | for (var name2 in all)
|
7 | __defProp(target, name2, { get: all[name2], enumerable: true });
|
8 | };
|
9 | var __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 | };
|
17 | var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
18 | var stdin_exports = {};
|
19 | __export(stdin_exports, {
|
20 | PICKER_KEY: () => PICKER_KEY,
|
21 | default: () => stdin_default
|
22 | });
|
23 | module.exports = __toCommonJS(stdin_exports);
|
24 | var import_vue = require("vue");
|
25 | var import_utils = require("../utils");
|
26 | var import_utils2 = require("./utils");
|
27 | var import_use = require("@vant/use");
|
28 | var import_use_touch = require("../composables/use-touch");
|
29 | var import_use_expose = require("../composables/use-expose");
|
30 | const DEFAULT_DURATION = 200;
|
31 | const MOMENTUM_TIME = 300;
|
32 | const MOMENTUM_DISTANCE = 15;
|
33 | const [name, bem] = (0, import_utils.createNamespace)("picker-column");
|
34 | const PICKER_KEY = Symbol(name);
|
35 | var 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 | });
|