Picker.mjs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { mergeProps as _mergeProps, createVNode as _createVNode } from "vue";
  2. import { ref, watch, computed, nextTick, defineComponent } from "vue";
  3. import { pick, extend, unitToPx, truthProp, isSameValue, makeArrayProp, preventDefault, makeStringProp, makeNumericProp, BORDER_UNSET_TOP_BOTTOM } from "../utils/index.mjs";
  4. import { bem, name, isOptionExist, getColumnsType, findOptionByValue, assignDefaultFields, formatCascadeColumns, getFirstEnabledOption } from "./utils.mjs";
  5. import { useChildren, useEventListener, useParent } from "@vant/use";
  6. import { useExpose } from "../composables/use-expose.mjs";
  7. import { Loading } from "../loading/index.mjs";
  8. import Column, { PICKER_KEY } from "./PickerColumn.mjs";
  9. import Toolbar, { pickerToolbarPropKeys, pickerToolbarProps, pickerToolbarSlots } from "./PickerToolbar.mjs";
  10. import { PICKER_GROUP_KEY } from "../picker-group/PickerGroup.mjs";
  11. const pickerSharedProps = extend({
  12. loading: Boolean,
  13. readonly: Boolean,
  14. allowHtml: Boolean,
  15. optionHeight: makeNumericProp(44),
  16. showToolbar: truthProp,
  17. swipeDuration: makeNumericProp(1e3),
  18. visibleOptionNum: makeNumericProp(6)
  19. }, pickerToolbarProps);
  20. const pickerProps = extend({}, pickerSharedProps, {
  21. columns: makeArrayProp(),
  22. modelValue: makeArrayProp(),
  23. toolbarPosition: makeStringProp("top"),
  24. columnsFieldNames: Object
  25. });
  26. var stdin_default = defineComponent({
  27. name,
  28. props: pickerProps,
  29. emits: ["confirm", "cancel", "change", "clickOption", "update:modelValue"],
  30. setup(props, {
  31. emit,
  32. slots
  33. }) {
  34. const columnsRef = ref();
  35. const selectedValues = ref(props.modelValue.slice(0));
  36. const {
  37. parent
  38. } = useParent(PICKER_GROUP_KEY);
  39. const {
  40. children,
  41. linkChildren
  42. } = useChildren(PICKER_KEY);
  43. linkChildren();
  44. const fields = computed(() => assignDefaultFields(props.columnsFieldNames));
  45. const optionHeight = computed(() => unitToPx(props.optionHeight));
  46. const columnsType = computed(() => getColumnsType(props.columns, fields.value));
  47. const currentColumns = computed(() => {
  48. const {
  49. columns
  50. } = props;
  51. switch (columnsType.value) {
  52. case "multiple":
  53. return columns;
  54. case "cascade":
  55. return formatCascadeColumns(columns, fields.value, selectedValues);
  56. default:
  57. return [columns];
  58. }
  59. });
  60. const hasOptions = computed(() => currentColumns.value.some((options) => options.length));
  61. const selectedOptions = computed(() => currentColumns.value.map((options, index) => findOptionByValue(options, selectedValues.value[index], fields.value)));
  62. const selectedIndexes = computed(() => currentColumns.value.map((options, index) => options.findIndex((option) => option[fields.value.value] === selectedValues.value[index])));
  63. const setValue = (index, value) => {
  64. if (selectedValues.value[index] !== value) {
  65. const newValues = selectedValues.value.slice(0);
  66. newValues[index] = value;
  67. selectedValues.value = newValues;
  68. }
  69. };
  70. const getEventParams = () => ({
  71. selectedValues: selectedValues.value.slice(0),
  72. selectedOptions: selectedOptions.value,
  73. selectedIndexes: selectedIndexes.value
  74. });
  75. const onChange = (value, columnIndex) => {
  76. setValue(columnIndex, value);
  77. if (columnsType.value === "cascade") {
  78. selectedValues.value.forEach((value2, index) => {
  79. const options = currentColumns.value[index];
  80. if (!isOptionExist(options, value2, fields.value)) {
  81. setValue(index, options.length ? options[0][fields.value.value] : void 0);
  82. }
  83. });
  84. }
  85. emit("change", extend({
  86. columnIndex
  87. }, getEventParams()));
  88. };
  89. const onClickOption = (currentOption, columnIndex) => emit("clickOption", extend({
  90. columnIndex,
  91. currentOption
  92. }, getEventParams()));
  93. const confirm = () => {
  94. children.forEach((child) => child.stopMomentum());
  95. const params = getEventParams();
  96. nextTick(() => {
  97. emit("confirm", params);
  98. });
  99. return params;
  100. };
  101. const cancel = () => emit("cancel", getEventParams());
  102. const renderColumnItems = () => currentColumns.value.map((options, columnIndex) => _createVNode(Column, {
  103. "value": selectedValues.value[columnIndex],
  104. "fields": fields.value,
  105. "options": options,
  106. "readonly": props.readonly,
  107. "allowHtml": props.allowHtml,
  108. "optionHeight": optionHeight.value,
  109. "swipeDuration": props.swipeDuration,
  110. "visibleOptionNum": props.visibleOptionNum,
  111. "onChange": (value) => onChange(value, columnIndex),
  112. "onClickOption": (option) => onClickOption(option, columnIndex)
  113. }, {
  114. option: slots.option
  115. }));
  116. const renderMask = (wrapHeight) => {
  117. if (hasOptions.value) {
  118. const frameStyle = {
  119. height: `${optionHeight.value}px`
  120. };
  121. const maskStyle = {
  122. backgroundSize: `100% ${(wrapHeight - optionHeight.value) / 2}px`
  123. };
  124. return [_createVNode("div", {
  125. "class": bem("mask"),
  126. "style": maskStyle
  127. }, null), _createVNode("div", {
  128. "class": [BORDER_UNSET_TOP_BOTTOM, bem("frame")],
  129. "style": frameStyle
  130. }, null)];
  131. }
  132. };
  133. const renderColumns = () => {
  134. const wrapHeight = optionHeight.value * +props.visibleOptionNum;
  135. const columnsStyle = {
  136. height: `${wrapHeight}px`
  137. };
  138. return _createVNode("div", {
  139. "ref": columnsRef,
  140. "class": bem("columns"),
  141. "style": columnsStyle
  142. }, [renderColumnItems(), renderMask(wrapHeight)]);
  143. };
  144. const renderToolbar = () => {
  145. if (props.showToolbar && !parent) {
  146. return _createVNode(Toolbar, _mergeProps(pick(props, pickerToolbarPropKeys), {
  147. "onConfirm": confirm,
  148. "onCancel": cancel
  149. }), pick(slots, pickerToolbarSlots));
  150. }
  151. };
  152. watch(currentColumns, (columns) => {
  153. columns.forEach((options, index) => {
  154. if (options.length && !isOptionExist(options, selectedValues.value[index], fields.value)) {
  155. setValue(index, getFirstEnabledOption(options)[fields.value.value]);
  156. }
  157. });
  158. }, {
  159. immediate: true
  160. });
  161. let lastEmittedModelValue;
  162. watch(() => props.modelValue, (newValues) => {
  163. if (!isSameValue(newValues, selectedValues.value) && !isSameValue(newValues, lastEmittedModelValue)) {
  164. selectedValues.value = newValues.slice(0);
  165. }
  166. }, {
  167. deep: true
  168. });
  169. watch(selectedValues, (newValues) => {
  170. if (!isSameValue(newValues, props.modelValue)) {
  171. lastEmittedModelValue = newValues.slice(0);
  172. emit("update:modelValue", lastEmittedModelValue);
  173. }
  174. }, {
  175. immediate: true
  176. });
  177. useEventListener("touchmove", preventDefault, {
  178. target: columnsRef
  179. });
  180. const getSelectedOptions = () => selectedOptions.value;
  181. useExpose({
  182. confirm,
  183. getSelectedOptions
  184. });
  185. return () => {
  186. var _a, _b;
  187. return _createVNode("div", {
  188. "class": bem()
  189. }, [props.toolbarPosition === "top" ? renderToolbar() : null, props.loading ? _createVNode(Loading, {
  190. "class": bem("loading")
  191. }, 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]);
  192. };
  193. }
  194. });
  195. export {
  196. stdin_default as default,
  197. pickerProps,
  198. pickerSharedProps
  199. };