SwipeCell.mjs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import { ref, reactive, computed, defineComponent, createVNode as _createVNode } from "vue";
  2. import { clamp, isDef, numericProp, preventDefault, callInterceptor, createNamespace, makeNumericProp } from "../utils/index.mjs";
  3. import { useRect, useClickAway, useEventListener } from "@vant/use";
  4. import { useTouch } from "../composables/use-touch.mjs";
  5. import { useExpose } from "../composables/use-expose.mjs";
  6. const [name, bem] = createNamespace("swipe-cell");
  7. const swipeCellProps = {
  8. name: makeNumericProp(""),
  9. disabled: Boolean,
  10. leftWidth: numericProp,
  11. rightWidth: numericProp,
  12. beforeClose: Function,
  13. stopPropagation: Boolean
  14. };
  15. var stdin_default = defineComponent({
  16. name,
  17. props: swipeCellProps,
  18. emits: ["open", "close", "click"],
  19. setup(props, {
  20. emit,
  21. slots
  22. }) {
  23. let opened;
  24. let lockClick;
  25. let startOffset;
  26. let isInBeforeClosing;
  27. const root = ref();
  28. const leftRef = ref();
  29. const rightRef = ref();
  30. const state = reactive({
  31. offset: 0,
  32. dragging: false
  33. });
  34. const touch = useTouch();
  35. const getWidthByRef = (ref2) => ref2.value ? useRect(ref2).width : 0;
  36. const leftWidth = computed(() => isDef(props.leftWidth) ? +props.leftWidth : getWidthByRef(leftRef));
  37. const rightWidth = computed(() => isDef(props.rightWidth) ? +props.rightWidth : getWidthByRef(rightRef));
  38. const open = (side) => {
  39. state.offset = side === "left" ? leftWidth.value : -rightWidth.value;
  40. if (!opened) {
  41. opened = true;
  42. emit("open", {
  43. name: props.name,
  44. position: side
  45. });
  46. }
  47. };
  48. const close = (position) => {
  49. state.offset = 0;
  50. if (opened) {
  51. opened = false;
  52. emit("close", {
  53. name: props.name,
  54. position
  55. });
  56. }
  57. };
  58. const toggle = (side) => {
  59. const offset = Math.abs(state.offset);
  60. const THRESHOLD = 0.15;
  61. const threshold = opened ? 1 - THRESHOLD : THRESHOLD;
  62. const width = side === "left" ? leftWidth.value : rightWidth.value;
  63. if (width && offset > width * threshold) {
  64. open(side);
  65. } else {
  66. close(side);
  67. }
  68. };
  69. const onTouchStart = (event) => {
  70. if (!props.disabled) {
  71. startOffset = state.offset;
  72. touch.start(event);
  73. }
  74. };
  75. const onTouchMove = (event) => {
  76. if (props.disabled) {
  77. return;
  78. }
  79. const {
  80. deltaX
  81. } = touch;
  82. touch.move(event);
  83. if (touch.isHorizontal()) {
  84. lockClick = true;
  85. state.dragging = true;
  86. const isEdge = !opened || deltaX.value * startOffset < 0;
  87. if (isEdge) {
  88. preventDefault(event, props.stopPropagation);
  89. }
  90. state.offset = clamp(deltaX.value + startOffset, -rightWidth.value, leftWidth.value);
  91. }
  92. };
  93. const onTouchEnd = () => {
  94. if (state.dragging) {
  95. state.dragging = false;
  96. toggle(state.offset > 0 ? "left" : "right");
  97. setTimeout(() => {
  98. lockClick = false;
  99. }, 0);
  100. }
  101. };
  102. const onClick = (position = "outside", event) => {
  103. if (isInBeforeClosing) return;
  104. emit("click", position);
  105. if (opened && !lockClick) {
  106. isInBeforeClosing = true;
  107. callInterceptor(props.beforeClose, {
  108. args: [{
  109. event,
  110. name: props.name,
  111. position
  112. }],
  113. done: () => {
  114. isInBeforeClosing = false;
  115. close(position);
  116. },
  117. canceled: () => isInBeforeClosing = false,
  118. error: () => isInBeforeClosing = false
  119. });
  120. }
  121. };
  122. const getClickHandler = (position, stop) => (event) => {
  123. if (stop) {
  124. event.stopPropagation();
  125. }
  126. if (lockClick) {
  127. return;
  128. }
  129. onClick(position, event);
  130. };
  131. const renderSideContent = (side, ref2) => {
  132. const contentSlot = slots[side];
  133. if (contentSlot) {
  134. return _createVNode("div", {
  135. "ref": ref2,
  136. "class": bem(side),
  137. "onClick": getClickHandler(side, true)
  138. }, [contentSlot()]);
  139. }
  140. };
  141. useExpose({
  142. open,
  143. close
  144. });
  145. useClickAway(root, (event) => onClick("outside", event), {
  146. eventName: "touchstart"
  147. });
  148. useEventListener("touchmove", onTouchMove, {
  149. target: root
  150. });
  151. return () => {
  152. var _a;
  153. const wrapperStyle = {
  154. transform: `translate3d(${state.offset}px, 0, 0)`,
  155. transitionDuration: state.dragging ? "0s" : ".6s"
  156. };
  157. return _createVNode("div", {
  158. "ref": root,
  159. "class": bem(),
  160. "onClick": getClickHandler("cell", lockClick),
  161. "onTouchstartPassive": onTouchStart,
  162. "onTouchend": onTouchEnd,
  163. "onTouchcancel": onTouchEnd
  164. }, [_createVNode("div", {
  165. "class": bem("wrapper"),
  166. "style": wrapperStyle
  167. }, [renderSideContent("left", leftRef), (_a = slots.default) == null ? void 0 : _a.call(slots), renderSideContent("right", rightRef)])]);
  168. };
  169. }
  170. });
  171. export {
  172. stdin_default as default,
  173. swipeCellProps
  174. };