215 lines
7.8 KiB
JavaScript
215 lines
7.8 KiB
JavaScript
import { ref, watch, computed, nextTick, defineComponent, createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
|
|
import { pick, extend, unitToPx, truthProp, isSameValue, makeArrayProp, preventDefault, makeStringProp, makeNumericProp, BORDER_UNSET_TOP_BOTTOM } from "../utils/index.mjs";
|
|
import { bem, name, isOptionExist, getColumnsType, findOptionByValue, assignDefaultFields, formatCascadeColumns, getFirstEnabledOption } from "./utils.mjs";
|
|
import { useChildren, useEventListener, useParent } from "@vant/use";
|
|
import { useExpose } from "../composables/use-expose.mjs";
|
|
import { Loading } from "../loading/index.mjs";
|
|
import Column, { PICKER_KEY } from "./PickerColumn.mjs";
|
|
import Toolbar, { pickerToolbarPropKeys, pickerToolbarProps, pickerToolbarSlots } from "./PickerToolbar.mjs";
|
|
import { PICKER_GROUP_KEY } from "../picker-group/PickerGroup.mjs";
|
|
const pickerSharedProps = extend({
|
|
loading: Boolean,
|
|
readonly: Boolean,
|
|
allowHtml: Boolean,
|
|
optionHeight: makeNumericProp(44),
|
|
showToolbar: truthProp,
|
|
swipeDuration: makeNumericProp(1e3),
|
|
visibleOptionNum: makeNumericProp(6)
|
|
}, pickerToolbarProps);
|
|
const pickerProps = extend({}, pickerSharedProps, {
|
|
columns: makeArrayProp(),
|
|
modelValue: makeArrayProp(),
|
|
toolbarPosition: makeStringProp("top"),
|
|
columnsFieldNames: Object
|
|
});
|
|
var stdin_default = defineComponent({
|
|
name,
|
|
props: pickerProps,
|
|
emits: ["confirm", "cancel", "change", "scrollInto", "clickOption", "update:modelValue"],
|
|
setup(props, {
|
|
emit,
|
|
slots
|
|
}) {
|
|
const columnsRef = ref();
|
|
const selectedValues = ref(props.modelValue.slice(0));
|
|
const {
|
|
parent
|
|
} = useParent(PICKER_GROUP_KEY);
|
|
const {
|
|
children,
|
|
linkChildren
|
|
} = useChildren(PICKER_KEY);
|
|
linkChildren();
|
|
const fields = computed(() => assignDefaultFields(props.columnsFieldNames));
|
|
const optionHeight = computed(() => unitToPx(props.optionHeight));
|
|
const columnsType = computed(() => getColumnsType(props.columns, fields.value));
|
|
const currentColumns = computed(() => {
|
|
const {
|
|
columns
|
|
} = props;
|
|
switch (columnsType.value) {
|
|
case "multiple":
|
|
return columns;
|
|
case "cascade":
|
|
return formatCascadeColumns(columns, fields.value, selectedValues);
|
|
default:
|
|
return [columns];
|
|
}
|
|
});
|
|
const hasOptions = computed(() => currentColumns.value.some((options) => options.length));
|
|
const selectedOptions = computed(() => currentColumns.value.map((options, index) => findOptionByValue(options, selectedValues.value[index], fields.value)));
|
|
const selectedIndexes = computed(() => currentColumns.value.map((options, index) => options.findIndex((option) => option[fields.value.value] === selectedValues.value[index])));
|
|
const setValue = (index, value) => {
|
|
if (selectedValues.value[index] !== value) {
|
|
const newValues = selectedValues.value.slice(0);
|
|
newValues[index] = value;
|
|
selectedValues.value = newValues;
|
|
}
|
|
};
|
|
const getEventParams = () => ({
|
|
selectedValues: selectedValues.value.slice(0),
|
|
selectedOptions: selectedOptions.value,
|
|
selectedIndexes: selectedIndexes.value
|
|
});
|
|
const onChange = (value, columnIndex) => {
|
|
setValue(columnIndex, value);
|
|
if (columnsType.value === "cascade") {
|
|
selectedValues.value.forEach((value2, index) => {
|
|
const options = currentColumns.value[index];
|
|
if (!isOptionExist(options, value2, fields.value)) {
|
|
setValue(index, options.length ? options[0][fields.value.value] : void 0);
|
|
}
|
|
});
|
|
}
|
|
nextTick(() => {
|
|
emit("change", extend({
|
|
columnIndex
|
|
}, getEventParams()));
|
|
});
|
|
};
|
|
const onClickOption = (currentOption, columnIndex) => {
|
|
const params = {
|
|
columnIndex,
|
|
currentOption
|
|
};
|
|
emit("clickOption", extend(getEventParams(), params));
|
|
emit("scrollInto", params);
|
|
};
|
|
const confirm = () => {
|
|
children.forEach((child) => child.stopMomentum());
|
|
const params = getEventParams();
|
|
nextTick(() => {
|
|
emit("confirm", params);
|
|
});
|
|
return params;
|
|
};
|
|
const cancel = () => emit("cancel", getEventParams());
|
|
const renderColumnItems = () => currentColumns.value.map((options, columnIndex) => _createVNode(Column, {
|
|
"value": selectedValues.value[columnIndex],
|
|
"fields": fields.value,
|
|
"options": options,
|
|
"readonly": props.readonly,
|
|
"allowHtml": props.allowHtml,
|
|
"optionHeight": optionHeight.value,
|
|
"swipeDuration": props.swipeDuration,
|
|
"visibleOptionNum": props.visibleOptionNum,
|
|
"onChange": (value) => onChange(value, columnIndex),
|
|
"onClickOption": (option) => onClickOption(option, columnIndex),
|
|
"onScrollInto": (option) => {
|
|
emit("scrollInto", {
|
|
currentOption: option,
|
|
columnIndex
|
|
});
|
|
}
|
|
}, {
|
|
option: slots.option
|
|
}));
|
|
const renderMask = (wrapHeight) => {
|
|
if (hasOptions.value) {
|
|
const frameStyle = {
|
|
height: `${optionHeight.value}px`
|
|
};
|
|
const maskStyle = {
|
|
backgroundSize: `100% ${(wrapHeight - optionHeight.value) / 2}px`
|
|
};
|
|
return [_createVNode("div", {
|
|
"class": bem("mask"),
|
|
"style": maskStyle
|
|
}, null), _createVNode("div", {
|
|
"class": [BORDER_UNSET_TOP_BOTTOM, bem("frame")],
|
|
"style": frameStyle
|
|
}, null)];
|
|
}
|
|
};
|
|
const renderColumns = () => {
|
|
const wrapHeight = optionHeight.value * +props.visibleOptionNum;
|
|
const columnsStyle = {
|
|
height: `${wrapHeight}px`
|
|
};
|
|
if (!props.loading && !hasOptions.value && slots.empty) {
|
|
return slots.empty();
|
|
}
|
|
return _createVNode("div", {
|
|
"ref": columnsRef,
|
|
"class": bem("columns"),
|
|
"style": columnsStyle
|
|
}, [renderColumnItems(), renderMask(wrapHeight)]);
|
|
};
|
|
const renderToolbar = () => {
|
|
if (props.showToolbar && !parent) {
|
|
return _createVNode(Toolbar, _mergeProps(pick(props, pickerToolbarPropKeys), {
|
|
"onConfirm": confirm,
|
|
"onCancel": cancel
|
|
}), pick(slots, pickerToolbarSlots));
|
|
}
|
|
};
|
|
watch(currentColumns, (columns) => {
|
|
columns.forEach((options, index) => {
|
|
if (options.length && !isOptionExist(options, selectedValues.value[index], fields.value)) {
|
|
setValue(index, getFirstEnabledOption(options)[fields.value.value]);
|
|
}
|
|
});
|
|
}, {
|
|
immediate: true
|
|
});
|
|
let lastEmittedModelValue;
|
|
watch(() => props.modelValue, (newValues) => {
|
|
if (!isSameValue(newValues, selectedValues.value) && !isSameValue(newValues, lastEmittedModelValue)) {
|
|
selectedValues.value = newValues.slice(0);
|
|
lastEmittedModelValue = newValues.slice(0);
|
|
}
|
|
}, {
|
|
deep: true
|
|
});
|
|
watch(selectedValues, (newValues) => {
|
|
if (!isSameValue(newValues, props.modelValue)) {
|
|
lastEmittedModelValue = newValues.slice(0);
|
|
emit("update:modelValue", lastEmittedModelValue);
|
|
}
|
|
}, {
|
|
immediate: true
|
|
});
|
|
useEventListener("touchmove", preventDefault, {
|
|
target: columnsRef
|
|
});
|
|
const getSelectedOptions = () => selectedOptions.value;
|
|
useExpose({
|
|
confirm,
|
|
getSelectedOptions
|
|
});
|
|
return () => {
|
|
var _a, _b;
|
|
return _createVNode("div", {
|
|
"class": bem()
|
|
}, [props.toolbarPosition === "top" ? renderToolbar() : null, props.loading ? _createVNode(Loading, {
|
|
"class": bem("loading")
|
|
}, null) : null, (_a = slots["columns-top"]) == null ? void 0 : _a.call(slots), renderColumns(), (_b = slots["columns-bottom"]) == null ? void 0 : _b.call(slots), props.toolbarPosition === "bottom" ? renderToolbar() : null]);
|
|
};
|
|
}
|
|
});
|
|
export {
|
|
stdin_default as default,
|
|
pickerProps,
|
|
pickerSharedProps
|
|
};
|