@gyenno/nutui-taro
Version:
京东风格的轻量级移动端 Vue2、Vue3 组件库(支持小程序开发)
717 lines (716 loc) • 23 kB
JavaScript
import { c as createComponent, b as preventDefault, d as clamp } from "./component-25dcca32.js";
import { reactive, ref, computed, watch, toRefs, onMounted, openBlock, createElementBlock, createElementVNode, normalizeStyle, Fragment, renderList, normalizeClass, toDisplayString, createCommentVNode, createTextVNode, resolveComponent, renderSlot, mergeProps, createVNode } from "vue";
import { p as pxCheck } from "./pxCheck-c6b9f6b7.js";
import { u as useTouch } from "./index-7a7385e4.js";
import { _ as _export_sfc } from "./_plugin-vue_export-helper-cc2b3d55.js";
import Taro from "@tarojs/taro";
import "../locale/lang";
const { componentName } = createComponent("picker");
const usePicker = (props, emit) => {
const state = reactive({
formattedColumns: props.columns
});
let defaultValues = ref([]);
const pickerColumn = ref([]);
const swipeRef = (el) => {
if (el && pickerColumn.value.length < columnsList.value.length) {
pickerColumn.value.push(el);
}
};
const classes = computed(() => {
const prefixCls = componentName;
return {
[prefixCls]: true
};
});
const selectedOptions = computed(() => {
let optins = [];
columnsList.value.map((column2, index) => {
let currOptions = [];
currOptions = column2.filter((item) => item.value == defaultValues.value[index]);
optins.push(currOptions[0]);
});
return optins;
});
const columnsType = computed(() => {
const firstColumn = state.formattedColumns[0];
if (firstColumn) {
if (Array.isArray(firstColumn)) {
return "multiple";
}
if ("children" in firstColumn) {
return "cascade";
}
}
return "single";
});
const columnsList = computed(() => {
switch (columnsType.value) {
case "multiple":
return state.formattedColumns;
case "cascade":
return formatCascade(state.formattedColumns, defaultValues.value ? defaultValues.value : []);
default:
return [state.formattedColumns];
}
});
const formatCascade = (columns, defaultValues2) => {
const formatted = [];
let cursor = {
text: "",
value: "",
children: columns
};
let columnIndex = 0;
while (cursor && cursor.children) {
const options = cursor.children;
const value = defaultValues2[columnIndex];
let index = options.findIndex((columnItem) => columnItem.value == value);
if (index == -1)
index = 0;
cursor = cursor.children[index];
columnIndex++;
formatted.push(options);
}
return formatted;
};
const cancel = () => {
emit("cancel", {
selectedValue: defaultValues.value,
selectedOptions: selectedOptions.value
});
};
const changeHandler = (columnIndex, option) => {
if (option && Object.keys(option).length) {
defaultValues.value = defaultValues.value ? defaultValues.value : [];
if (columnsType.value === "cascade") {
defaultValues.value[columnIndex] = option.value ? option.value : "";
let index = columnIndex;
let cursor = option;
while (cursor && cursor.children && cursor.children[0]) {
defaultValues.value[index + 1] = cursor.children[0].value;
index++;
cursor = cursor.children[0];
}
if (cursor && cursor.children && cursor.children.length == 0) {
defaultValues.value = defaultValues.value.slice(0, index + 1);
}
} else {
defaultValues.value[columnIndex] = option.hasOwnProperty("value") ? option.value : "";
}
emit("change", {
columnIndex,
selectedValue: defaultValues.value,
selectedOptions: selectedOptions.value
});
}
};
const confirm = () => {
if (defaultValues.value && !defaultValues.value.length) {
columnsList.value.forEach((columns) => {
defaultValues.value.push(columns[0].value);
});
}
emit("confirm", {
selectedValue: defaultValues.value,
selectedOptions: selectedOptions.value
});
};
const isSameValue = (valA, valB) => JSON.stringify(valA) === JSON.stringify(valB);
watch(
() => props.modelValue,
(newValues) => {
if (!isSameValue(newValues, defaultValues.value)) {
defaultValues.value = newValues;
}
},
{ deep: true, immediate: true }
);
watch(
defaultValues,
(newValues) => {
if (!isSameValue(newValues, props.modelValue)) {
emit("update:modelValue", newValues);
}
},
{ deep: true }
);
watch(
() => props.columns,
(val) => {
if (val.length)
state.formattedColumns = val;
}
);
return {
classes,
...toRefs(state),
columnsType,
columnsList,
cancel,
changeHandler,
confirm,
defaultValues,
pickerColumn,
swipeRef,
selectedOptions,
isSameValue
};
};
const { create: create$1 } = createComponent("picker-column");
const _sfc_main$1 = create$1({
props: {
// 当前选中项
value: [String, Number],
columnsType: String,
column: {
type: Array,
default: () => []
},
// 是否开启3D效果
threeDimensional: {
type: Boolean,
default: true
},
swipeDuration: {
type: [Number, String],
default: 1e3
},
visibleOptionNum: {
type: [Number, String],
default: 7
},
optionHeight: {
type: [Number, String],
default: 36
}
},
emits: ["click", "change"],
setup(props, { emit }) {
const touch = useTouch();
const state = reactive({
touchParams: {
startY: 0,
endY: 0,
startTime: 0,
endTime: 0,
lastY: 0,
lastTime: 0
},
currIndex: 1,
transformY: 0,
scrollDistance: 0,
rotation: 20
});
const roller = ref(null);
const moving = ref(false);
const touchDeg = ref(0);
const touchTime = ref(0);
const DEFAULT_DURATION = 200;
const INERTIA_TIME = 300;
const INERTIA_DISTANCE = 15;
const touchRollerStyle = computed(() => {
return {
transition: `transform ${touchTime.value}ms cubic-bezier(0.17, 0.89, 0.45, 1)`,
transform: `rotate3d(1, 0, 0, ${touchDeg.value})`,
top: `calc(50% - ${+props.optionHeight / 2}px)`
};
});
const touchTileStyle = computed(() => {
const { optionHeight } = props;
return {
transition: `transform ${touchTime.value}ms cubic-bezier(0.17, 0.89, 0.45, 1)`,
transform: `translate3d(0, ${state.scrollDistance}px, 0)`,
top: `calc(50% - ${+optionHeight / 2}px)`,
height: `${optionHeight}px`
};
});
const setRollerStyle = (index) => {
return `transform: rotate3d(1, 0, 0, ${-state.rotation * index}deg) translate3d(0px, 0px, 104px)`;
};
const maskStyles = computed(() => {
return {
backgroundSize: `100% ${(+props.visibleOptionNum - 1) * +props.optionHeight / 2}px`
};
});
const onTouchStart = (event) => {
touch.start(event);
if (moving.value) {
let dom = roller.value;
const { transform } = window.getComputedStyle(dom);
if (props.threeDimensional) {
const circle = Math.floor(parseInt(touchDeg.value) / 360);
const cos = +transform.split(", ")[5];
const sin = +transform.split(", ")[6] < 0 ? 180 : 0;
const endDeg = circle * 360 + Math.acos(cos) / Math.PI * 180 + sin;
state.scrollDistance = -Math.abs((endDeg / state.rotation - 1) * +props.optionHeight);
} else {
state.scrollDistance = +transform.slice(7, transform.length - 1).split(", ")[5];
}
}
preventDefault(event, true);
state.touchParams.startY = touch.deltaY.value;
state.touchParams.startTime = Date.now();
state.transformY = state.scrollDistance;
};
const onTouchMove = (event) => {
touch.move(event);
if (touch.isVertical()) {
moving.value = true;
preventDefault(event, true);
}
state.touchParams.lastY = touch.deltaY.value;
let move = state.touchParams.lastY - state.touchParams.startY;
setMove(move);
};
const onTouchEnd = () => {
state.touchParams.lastY = touch.deltaY.value;
state.touchParams.lastTime = Date.now();
let move = state.touchParams.lastY - state.touchParams.startY;
let moveTime = state.touchParams.lastTime - state.touchParams.startTime;
if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) {
const distance = momentum(move, moveTime);
setMove(distance, "end", +props.swipeDuration);
return;
} else {
setMove(move, "end");
}
setTimeout(() => {
touch.reset();
moving.value = false;
}, 0);
};
const momentum = (distance, duration) => {
const speed = Math.abs(distance / duration);
distance = speed / 3e-3 * (distance < 0 ? -1 : 1);
return distance;
};
const isHidden = (index) => {
if (index >= state.currIndex + 8 || index <= state.currIndex - 8) {
return true;
} else {
return false;
}
};
const setTransform = (translateY = 0, type, time = DEFAULT_DURATION, deg) => {
if (type === "end") {
touchTime.value = time;
} else {
touchTime.value = 0;
}
touchDeg.value = deg;
state.scrollDistance = translateY;
};
const setMove = (move, type, time) => {
const { optionHeight } = props;
let updateMove = move + state.transformY;
if (type === "end") {
if (updateMove > 0) {
updateMove = 0;
}
if (updateMove < -(props.column.length - 1) * +optionHeight) {
updateMove = -(props.column.length - 1) * +optionHeight;
}
let endMove = Math.round(updateMove / +optionHeight) * +optionHeight;
let deg = `${(Math.abs(Math.round(endMove / +optionHeight)) + 1) * state.rotation}deg`;
setTransform(endMove, type, time, deg);
state.currIndex = Math.abs(Math.round(endMove / +optionHeight)) + 1;
} else {
let deg = 0;
let currentDeg = (-updateMove / +optionHeight + 1) * state.rotation;
const maxDeg = (props.column.length + 1) * state.rotation;
const minDeg = 0;
deg = clamp(currentDeg, minDeg, maxDeg);
if (minDeg < deg && deg < maxDeg) {
setTransform(updateMove, null, void 0, deg + "deg");
state.currIndex = Math.abs(Math.round(updateMove / +optionHeight)) + 1;
}
}
};
const setChooseValue = () => {
emit("change", props.column[state.currIndex - 1]);
};
const modifyStatus = (type) => {
const { column: column2 } = props;
let index = column2.findIndex((columnItem) => columnItem.value == props.value);
state.currIndex = index === -1 ? 1 : index + 1;
let move = index === -1 ? 0 : index * +props.optionHeight;
type && setChooseValue();
setMove(-move);
};
const stopMomentum = () => {
moving.value = false;
touchTime.value = 0;
setChooseValue();
};
watch(
() => props.column,
(val) => {
if (props.column && props.column.length > 0) {
state.transformY = 0;
modifyStatus(false);
}
},
{
deep: true
}
);
watch(
() => props.value,
(val) => {
state.transformY = 0;
modifyStatus(false);
},
{
deep: true
}
);
onMounted(() => {
modifyStatus(true);
});
return {
...toRefs(state),
...toRefs(props),
setRollerStyle,
isHidden,
roller,
onTouchStart,
onTouchMove,
onTouchEnd,
touchRollerStyle,
touchTileStyle,
setMove,
stopMomentum,
pxCheck,
maskStyles
};
}
});
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("view", {
class: "nut-picker__list",
onTouchstart: _cache[1] || (_cache[1] = (...args) => _ctx.onTouchStart && _ctx.onTouchStart(...args)),
onTouchmove: _cache[2] || (_cache[2] = (...args) => _ctx.onTouchMove && _ctx.onTouchMove(...args)),
onTouchend: _cache[3] || (_cache[3] = (...args) => _ctx.onTouchEnd && _ctx.onTouchEnd(...args))
}, [
createElementVNode("view", {
class: "nut-picker-roller",
ref: "roller",
style: normalizeStyle(_ctx.threeDimensional ? _ctx.touchRollerStyle : _ctx.touchTileStyle),
onTransitionend: _cache[0] || (_cache[0] = (...args) => _ctx.stopMomentum && _ctx.stopMomentum(...args))
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.column, (item, index) => {
return openBlock(), createElementBlock(Fragment, {
key: item.value ? item.value : index
}, [
item && item.text && _ctx.threeDimensional ? (openBlock(), createElementBlock("view", {
key: 0,
class: normalizeClass(["nut-picker-roller-item", { "nut-picker-roller-item-hidden": _ctx.isHidden(index + 1) }]),
style: normalizeStyle(_ctx.setRollerStyle(index + 1))
}, toDisplayString(item.text), 7)) : createCommentVNode("", true),
createTextVNode(),
item && item.text && !_ctx.threeDimensional ? (openBlock(), createElementBlock("view", {
key: 1,
class: "nut-picker-roller-item-tile",
style: normalizeStyle({ height: _ctx.pxCheck(_ctx.optionHeight), lineHeight: _ctx.pxCheck(_ctx.optionHeight) })
}, toDisplayString(item.text), 5)) : createCommentVNode("", true)
], 64);
}), 128))
], 36),
createTextVNode(),
createElementVNode("view", {
class: "nut-picker-roller-mask",
style: normalizeStyle(_ctx.maskStyles)
}, null, 4)
], 32);
}
const column = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1]]);
const baseProps = {
modelValue: {
type: Array,
default: () => []
},
title: {
type: String,
default: ""
},
cancelText: {
type: String,
default: ""
},
okText: {
type: String,
default: ""
},
columns: {
type: Array,
default: () => {
return [];
}
},
threeDimensional: {
type: Boolean,
default: true
},
swipeDuration: {
type: [Number, String],
default: 1e3
},
showToolbar: {
type: Boolean,
default: true
},
visibleOptionNum: {
type: [Number, String],
default: 7
},
optionHeight: {
type: [Number, String],
default: 36
}
};
const { translate } = createComponent("picker");
const componentWeb = {
components: {
[column.name]: column
},
props: baseProps,
emits: ["cancel", "change", "confirm", "update:modelValue"],
setup(props, { emit }) {
const { changeHandler, confirm, defaultValues, columnsList, selectedOptions, columnsType, classes, cancel } = usePicker(props, emit);
const state = reactive({
ENV: Taro.getEnv(),
ENV_TYPE: Taro.ENV_TYPE
});
const pickerColumn = ref([]);
const swipeRef = (el) => {
if (el && pickerColumn.value.length < columnsList.value.length) {
pickerColumn.value.push(el);
}
};
const confirmHandler = () => {
pickerColumn.value.length > 0 && pickerColumn.value.forEach((column2) => {
column2.stopMomentum();
});
confirm();
};
const columnStyle = computed(() => {
const styles = {};
styles.height = `${+props.visibleOptionNum * +props.optionHeight}px`;
styles["--lineHeight"] = `${+props.optionHeight}px`;
return styles;
});
return {
classes,
...toRefs(state),
column,
columnsType,
columnsList,
cancel,
changeHandler,
confirmHandler,
defaultValues,
pickerColumn,
swipeRef,
translate,
columnStyle
};
}
};
const componentWeapp = {
components: {
[column.name]: column
},
props: baseProps,
emits: ["cancel", "change", "confirm", "update:modelValue"],
setup(props, { emit }) {
const { changeHandler, confirm, defaultValues, columnsList, isSameValue, columnsType, classes, cancel } = usePicker(
props,
emit
);
const state = reactive({
show: false,
picking: false,
ENV: Taro.getEnv(),
ENV_TYPE: Taro.ENV_TYPE
});
let defaultIndexes = ref([]);
const pickerViewStyles = computed(() => {
const styles = {};
styles.height = `${+props.visibleOptionNum * +props.optionHeight}px`;
styles["--lineHeight"] = `${+props.optionHeight}px`;
return styles;
});
const defaultValuesConvert = () => {
let defaultIndexs = [];
if (defaultValues.value.length > 0) {
defaultValues.value.forEach((value, index) => {
for (let i = 0; i < columnsList.value[index].length; i++) {
if (columnsList.value[index][i].value == value) {
defaultIndexs.push(i);
break;
}
}
});
} else {
if (columnsList && columnsList.value.length > 0) {
columnsList.value.forEach((item) => {
defaultIndexs.push(0);
item.length > 0 && defaultValues.value.push(item[0].value);
});
}
}
return defaultIndexs;
};
const tileChange = (data) => {
const prevDefaultValue = defaultIndexes.value;
let changeIndex = 0;
data.detail.value.forEach((col, index) => {
if (prevDefaultValue[index] != col)
changeIndex = index;
});
changeHandler(changeIndex, columnsList.value[changeIndex][data.detail.value[changeIndex]]);
defaultIndexes.value = defaultValuesConvert();
};
const confirmHandler = () => {
if (state.picking) {
setTimeout(() => {
confirm();
}, 0);
} else {
confirm();
}
};
const handlePickstart = () => {
state.picking = true;
};
const handlePickend = () => {
state.picking = false;
};
onMounted(() => {
if (defaultValues.value.length > 0) {
defaultIndexes.value = defaultValuesConvert();
}
});
watch(
() => props.modelValue,
(newValues) => {
if (!isSameValue(newValues, defaultValues.value)) {
setTimeout(() => {
defaultIndexes.value = defaultValuesConvert();
}, 100);
}
},
{ deep: true }
);
return {
classes,
...toRefs(state),
column,
columnsType,
columnsList,
cancel,
changeHandler,
confirmHandler,
defaultValues,
defaultIndexes,
tileChange,
handlePickstart,
translate,
handlePickend,
pickerViewStyles,
pxCheck
};
}
};
const { create } = createComponent("picker");
const component = Taro.getEnv() == Taro.ENV_TYPE.WEB ? componentWeb : componentWeapp;
const _sfc_main = create(component);
const _hoisted_1 = {
key: 0,
class: "nut-picker__bar"
};
const _hoisted_2 = { class: "nut-picker__title" };
const _hoisted_3 = ["indicatorStyle", "value"];
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_nut_picker_column = resolveComponent("nut-picker-column");
return openBlock(), createElementBlock("view", {
class: normalizeClass(_ctx.classes)
}, [
_ctx.showToolbar ? (openBlock(), createElementBlock("view", _hoisted_1, [
createElementVNode("view", {
class: "nut-picker__cancel nut-picker__left nut-picker__button",
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.cancel && _ctx.cancel(...args))
}, toDisplayString(_ctx.cancelText || _ctx.translate("cancel")), 1),
createTextVNode(),
createElementVNode("view", _hoisted_2, toDisplayString(_ctx.title), 1),
createTextVNode(),
createElementVNode("view", {
class: "nut-picker__confirm nut-picker__right nut-picker__button",
onClick: _cache[1] || (_cache[1] = ($event) => _ctx.confirmHandler())
}, toDisplayString(_ctx.okText || _ctx.translate("confirm")), 1)
])) : createCommentVNode("", true),
createTextVNode(),
renderSlot(_ctx.$slots, "top"),
createTextVNode(),
_ctx.ENV != _ctx.ENV_TYPE.WEB ? (openBlock(), createElementBlock("picker-view", mergeProps({
key: 1,
indicatorStyle: `height:${_ctx.optionHeight}px`,
value: _ctx.defaultIndexes,
style: _ctx.pickerViewStyles
}, _ctx.$attrs, {
immediateChange: true,
onChange: _cache[2] || (_cache[2] = (...args) => _ctx.tileChange && _ctx.tileChange(...args)),
onPickstart: _cache[3] || (_cache[3] = (...args) => _ctx.handlePickstart && _ctx.handlePickstart(...args)),
onPickend: _cache[4] || (_cache[4] = (...args) => _ctx.handlePickend && _ctx.handlePickend(...args))
}), [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.columnsList, (column2, columnIndex) => {
return openBlock(), createElementBlock("picker-view-column", { key: columnIndex }, [
(openBlock(true), createElementBlock(Fragment, null, renderList(column2, (item, index) => {
return openBlock(), createElementBlock("view", {
class: "nut-picker-roller-item-tarotile",
style: normalizeStyle({
lineHeight: _ctx.pxCheck(_ctx.optionHeight)
}),
key: item.value ? item.value : index
}, toDisplayString(item.text), 5);
}), 128))
]);
}), 128))
], 16, _hoisted_3)) : createCommentVNode("", true),
createTextVNode(),
_ctx.ENV == _ctx.ENV_TYPE.WEB ? (openBlock(), createElementBlock("view", {
key: 2,
class: "nut-picker__column",
style: normalizeStyle(_ctx.columnStyle)
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.columnsList, (column2, columnIndex) => {
return openBlock(), createElementBlock("view", {
class: "nut-picker__columnitem",
key: columnIndex
}, [
createVNode(_component_nut_picker_column, {
ref_for: true,
ref: _ctx.swipeRef,
column: column2,
columnsType: _ctx.columnsType,
value: _ctx.defaultValues[columnIndex],
threeDimensional: false,
swipeDuration: _ctx.swipeDuration,
visibleOptionNum: _ctx.visibleOptionNum,
optionHeight: _ctx.optionHeight,
onChange: (option) => {
_ctx.changeHandler(columnIndex, option);
}
}, null, 8, ["column", "columnsType", "value", "swipeDuration", "visibleOptionNum", "optionHeight", "onChange"])
]);
}), 128))
], 4)) : createCommentVNode("", true),
createTextVNode(),
renderSlot(_ctx.$slots, "default")
], 2);
}
const Picker = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export {
Picker as default
};