Popover.mjs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import { ref, watch, nextTick, onMounted, watchEffect, onBeforeUnmount, defineComponent, createVNode as _createVNode, Fragment as _Fragment, mergeProps as _mergeProps } from "vue";
  2. import { createPopper, offsetModifier } from "@vant/popperjs";
  3. import { pick, extend, inBrowser, truthProp, numericProp, unknownProp, BORDER_RIGHT, BORDER_BOTTOM, makeArrayProp, makeStringProp, createNamespace } from "../utils/index.mjs";
  4. import { useClickAway } from "@vant/use";
  5. import { useScopeId } from "../composables/use-scope-id.mjs";
  6. import { useSyncPropRef } from "../composables/use-sync-prop-ref.mjs";
  7. import { Icon } from "../icon/index.mjs";
  8. import { Popup } from "../popup/index.mjs";
  9. const [name, bem] = createNamespace("popover");
  10. const popupProps = ["overlay", "duration", "teleport", "overlayStyle", "overlayClass", "closeOnClickOverlay"];
  11. const popoverProps = {
  12. show: Boolean,
  13. theme: makeStringProp("light"),
  14. overlay: Boolean,
  15. actions: makeArrayProp(),
  16. actionsDirection: makeStringProp("vertical"),
  17. trigger: makeStringProp("click"),
  18. duration: numericProp,
  19. showArrow: truthProp,
  20. placement: makeStringProp("bottom"),
  21. iconPrefix: String,
  22. overlayClass: unknownProp,
  23. overlayStyle: Object,
  24. closeOnClickAction: truthProp,
  25. closeOnClickOverlay: truthProp,
  26. closeOnClickOutside: truthProp,
  27. offset: {
  28. type: Array,
  29. default: () => [0, 8]
  30. },
  31. teleport: {
  32. type: [String, Object],
  33. default: "body"
  34. }
  35. };
  36. var stdin_default = defineComponent({
  37. name,
  38. props: popoverProps,
  39. emits: ["select", "touchstart", "update:show"],
  40. setup(props, {
  41. emit,
  42. slots,
  43. attrs
  44. }) {
  45. let popper;
  46. const popupRef = ref();
  47. const wrapperRef = ref();
  48. const popoverRef = ref();
  49. const show = useSyncPropRef(() => props.show, (value) => emit("update:show", value));
  50. const getPopoverOptions = () => ({
  51. placement: props.placement,
  52. modifiers: [{
  53. name: "computeStyles",
  54. options: {
  55. adaptive: false,
  56. gpuAcceleration: false
  57. }
  58. }, extend({}, offsetModifier, {
  59. options: {
  60. offset: props.offset
  61. }
  62. })]
  63. });
  64. const createPopperInstance = () => {
  65. if (wrapperRef.value && popoverRef.value) {
  66. return createPopper(wrapperRef.value, popoverRef.value.popupRef.value, getPopoverOptions());
  67. }
  68. return null;
  69. };
  70. const updateLocation = () => {
  71. nextTick(() => {
  72. if (!show.value) {
  73. return;
  74. }
  75. if (!popper) {
  76. popper = createPopperInstance();
  77. if (inBrowser) {
  78. window.addEventListener("animationend", updateLocation);
  79. window.addEventListener("transitionend", updateLocation);
  80. }
  81. } else {
  82. popper.setOptions(getPopoverOptions());
  83. }
  84. });
  85. };
  86. const updateShow = (value) => {
  87. show.value = value;
  88. };
  89. const onClickWrapper = () => {
  90. if (props.trigger === "click") {
  91. show.value = !show.value;
  92. }
  93. };
  94. const onClickAction = (action, index) => {
  95. if (action.disabled) {
  96. return;
  97. }
  98. emit("select", action, index);
  99. if (props.closeOnClickAction) {
  100. show.value = false;
  101. }
  102. };
  103. const onClickAway = () => {
  104. if (show.value && props.closeOnClickOutside && (!props.overlay || props.closeOnClickOverlay)) {
  105. show.value = false;
  106. }
  107. };
  108. const renderActionContent = (action, index) => {
  109. if (slots.action) {
  110. return slots.action({
  111. action,
  112. index
  113. });
  114. }
  115. return [action.icon && _createVNode(Icon, {
  116. "name": action.icon,
  117. "classPrefix": props.iconPrefix,
  118. "class": bem("action-icon")
  119. }, null), _createVNode("div", {
  120. "class": [bem("action-text"), {
  121. [BORDER_BOTTOM]: props.actionsDirection === "vertical"
  122. }]
  123. }, [action.text])];
  124. };
  125. const renderAction = (action, index) => {
  126. const {
  127. icon,
  128. color,
  129. disabled,
  130. className
  131. } = action;
  132. return _createVNode("div", {
  133. "role": "menuitem",
  134. "class": [bem("action", {
  135. disabled,
  136. "with-icon": icon
  137. }), {
  138. [BORDER_RIGHT]: props.actionsDirection === "horizontal"
  139. }, className],
  140. "style": {
  141. color
  142. },
  143. "tabindex": disabled ? void 0 : 0,
  144. "aria-disabled": disabled || void 0,
  145. "onClick": () => onClickAction(action, index)
  146. }, [renderActionContent(action, index)]);
  147. };
  148. onMounted(() => {
  149. updateLocation();
  150. watchEffect(() => {
  151. var _a;
  152. popupRef.value = (_a = popoverRef.value) == null ? void 0 : _a.popupRef.value;
  153. });
  154. });
  155. onBeforeUnmount(() => {
  156. if (popper) {
  157. if (inBrowser) {
  158. window.removeEventListener("animationend", updateLocation);
  159. window.removeEventListener("transitionend", updateLocation);
  160. }
  161. popper.destroy();
  162. popper = null;
  163. }
  164. });
  165. watch(() => [show.value, props.offset, props.placement], updateLocation);
  166. useClickAway([wrapperRef, popupRef], onClickAway, {
  167. eventName: "touchstart"
  168. });
  169. return () => {
  170. var _a;
  171. return _createVNode(_Fragment, null, [_createVNode("span", {
  172. "ref": wrapperRef,
  173. "class": bem("wrapper"),
  174. "onClick": onClickWrapper
  175. }, [(_a = slots.reference) == null ? void 0 : _a.call(slots)]), _createVNode(Popup, _mergeProps({
  176. "ref": popoverRef,
  177. "show": show.value,
  178. "class": bem([props.theme]),
  179. "position": "",
  180. "transition": "van-popover-zoom",
  181. "lockScroll": false,
  182. "onUpdate:show": updateShow
  183. }, attrs, useScopeId(), pick(props, popupProps)), {
  184. default: () => [props.showArrow && _createVNode("div", {
  185. "class": bem("arrow")
  186. }, null), _createVNode("div", {
  187. "role": "menu",
  188. "class": bem("content", props.actionsDirection)
  189. }, [slots.default ? slots.default() : props.actions.map(renderAction)])]
  190. })]);
  191. };
  192. }
  193. });
  194. export {
  195. stdin_default as default,
  196. popoverProps
  197. };