Tour.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  3. import { Fragment as _Fragment, createVNode as _createVNode } from "vue";
  4. var __rest = this && this.__rest || function (s, e) {
  5. var t = {};
  6. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  7. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  8. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  9. }
  10. return t;
  11. };
  12. import { ref, computed, watch, watchEffect, defineComponent, toRefs, shallowRef } from 'vue';
  13. import Trigger, { triggerProps } from '../vc-trigger';
  14. import classNames from '../_util/classNames';
  15. import useMergedState from '../_util/hooks/useMergedState';
  16. import useTarget from './hooks/useTarget';
  17. import TourStep from './TourStep';
  18. import Mask from './Mask';
  19. import { getPlacements } from './placements';
  20. import { initDefaultProps } from '../_util/props-util';
  21. import { someType, stringType, arrayType, objectType, functionType, booleanType } from '../_util/type';
  22. import Portal from '../_util/PortalWrapper';
  23. const CENTER_PLACEHOLDER = {
  24. left: '50%',
  25. top: '50%',
  26. width: '1px',
  27. height: '1px'
  28. };
  29. export const tourProps = () => {
  30. const {
  31. builtinPlacements,
  32. popupAlign
  33. } = triggerProps();
  34. return {
  35. builtinPlacements,
  36. popupAlign,
  37. steps: arrayType(),
  38. open: booleanType(),
  39. defaultCurrent: {
  40. type: Number
  41. },
  42. current: {
  43. type: Number
  44. },
  45. onChange: functionType(),
  46. onClose: functionType(),
  47. onFinish: functionType(),
  48. mask: someType([Boolean, Object], true),
  49. arrow: someType([Boolean, Object], true),
  50. rootClassName: {
  51. type: String
  52. },
  53. placement: stringType('bottom'),
  54. prefixCls: {
  55. type: String,
  56. default: 'rc-tour'
  57. },
  58. renderPanel: functionType(),
  59. gap: objectType(),
  60. animated: someType([Boolean, Object]),
  61. scrollIntoViewOptions: someType([Boolean, Object], true),
  62. zIndex: {
  63. type: Number,
  64. default: 1001
  65. }
  66. };
  67. };
  68. const Tour = defineComponent({
  69. name: 'Tour',
  70. inheritAttrs: false,
  71. props: initDefaultProps(tourProps(), {}),
  72. setup(props) {
  73. const {
  74. defaultCurrent,
  75. placement,
  76. mask,
  77. scrollIntoViewOptions,
  78. open,
  79. gap,
  80. arrow
  81. } = toRefs(props);
  82. const triggerRef = ref();
  83. const [mergedCurrent, setMergedCurrent] = useMergedState(0, {
  84. value: computed(() => props.current),
  85. defaultValue: defaultCurrent.value
  86. });
  87. const [mergedOpen, setMergedOpen] = useMergedState(undefined, {
  88. value: computed(() => props.open),
  89. postState: origin => mergedCurrent.value < 0 || mergedCurrent.value >= props.steps.length ? false : origin !== null && origin !== void 0 ? origin : true
  90. });
  91. const openRef = shallowRef(mergedOpen.value);
  92. watchEffect(() => {
  93. if (mergedOpen.value && !openRef.value) {
  94. setMergedCurrent(0);
  95. }
  96. openRef.value = mergedOpen.value;
  97. });
  98. const curStep = computed(() => props.steps[mergedCurrent.value] || {});
  99. const mergedPlacement = computed(() => {
  100. var _a;
  101. return (_a = curStep.value.placement) !== null && _a !== void 0 ? _a : placement.value;
  102. });
  103. const mergedMask = computed(() => {
  104. var _a;
  105. return mergedOpen.value && ((_a = curStep.value.mask) !== null && _a !== void 0 ? _a : mask.value);
  106. });
  107. const mergedScrollIntoViewOptions = computed(() => {
  108. var _a;
  109. return (_a = curStep.value.scrollIntoViewOptions) !== null && _a !== void 0 ? _a : scrollIntoViewOptions.value;
  110. });
  111. const [posInfo, targetElement] = useTarget(computed(() => curStep.value.target), open, gap, mergedScrollIntoViewOptions);
  112. // ========================= arrow =========================
  113. const mergedArrow = computed(() => targetElement.value ? typeof curStep.value.arrow === 'undefined' ? arrow.value : curStep.value.arrow : false);
  114. const arrowPointAtCenter = computed(() => typeof mergedArrow.value === 'object' ? mergedArrow.value.pointAtCenter : false);
  115. watch(arrowPointAtCenter, () => {
  116. var _a;
  117. (_a = triggerRef.value) === null || _a === void 0 ? void 0 : _a.forcePopupAlign();
  118. });
  119. watch(mergedCurrent, () => {
  120. var _a;
  121. (_a = triggerRef.value) === null || _a === void 0 ? void 0 : _a.forcePopupAlign();
  122. });
  123. // ========================= Change =========================
  124. const onInternalChange = nextCurrent => {
  125. var _a;
  126. setMergedCurrent(nextCurrent);
  127. (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, nextCurrent);
  128. };
  129. return () => {
  130. var _a;
  131. const {
  132. prefixCls,
  133. steps,
  134. onClose,
  135. onFinish,
  136. rootClassName,
  137. renderPanel,
  138. animated,
  139. zIndex
  140. } = props,
  141. restProps = __rest(props, ["prefixCls", "steps", "onClose", "onFinish", "rootClassName", "renderPanel", "animated", "zIndex"]);
  142. // ========================= Render =========================
  143. // Skip if not init yet
  144. if (targetElement.value === undefined) {
  145. return null;
  146. }
  147. const handleClose = () => {
  148. setMergedOpen(false);
  149. onClose === null || onClose === void 0 ? void 0 : onClose(mergedCurrent.value);
  150. };
  151. const mergedShowMask = typeof mergedMask.value === 'boolean' ? mergedMask.value : !!mergedMask.value;
  152. const mergedMaskStyle = typeof mergedMask.value === 'boolean' ? undefined : mergedMask.value;
  153. // when targetElement is not exist, use body as triggerDOMNode
  154. const getTriggerDOMNode = () => {
  155. return targetElement.value || document.body;
  156. };
  157. const getPopupElement = () => _createVNode(TourStep, _objectSpread({
  158. "arrow": mergedArrow.value,
  159. "key": "content",
  160. "prefixCls": prefixCls,
  161. "total": steps.length,
  162. "renderPanel": renderPanel,
  163. "onPrev": () => {
  164. onInternalChange(mergedCurrent.value - 1);
  165. },
  166. "onNext": () => {
  167. onInternalChange(mergedCurrent.value + 1);
  168. },
  169. "onClose": handleClose,
  170. "current": mergedCurrent.value,
  171. "onFinish": () => {
  172. handleClose();
  173. onFinish === null || onFinish === void 0 ? void 0 : onFinish();
  174. }
  175. }, curStep.value), null);
  176. const posInfoStyle = computed(() => {
  177. const info = posInfo.value || CENTER_PLACEHOLDER;
  178. // 如果info[key] 是number,添加 px
  179. const style = {};
  180. Object.keys(info).forEach(key => {
  181. if (typeof info[key] === 'number') {
  182. style[key] = `${info[key]}px`;
  183. } else {
  184. style[key] = info[key];
  185. }
  186. });
  187. return style;
  188. });
  189. return mergedOpen.value ? _createVNode(_Fragment, null, [_createVNode(Mask, {
  190. "zIndex": zIndex,
  191. "prefixCls": prefixCls,
  192. "pos": posInfo.value,
  193. "showMask": mergedShowMask,
  194. "style": mergedMaskStyle === null || mergedMaskStyle === void 0 ? void 0 : mergedMaskStyle.style,
  195. "fill": mergedMaskStyle === null || mergedMaskStyle === void 0 ? void 0 : mergedMaskStyle.color,
  196. "open": mergedOpen.value,
  197. "animated": animated,
  198. "rootClassName": rootClassName
  199. }, null), _createVNode(Trigger, _objectSpread(_objectSpread({}, restProps), {}, {
  200. "arrow": !!restProps.arrow,
  201. "builtinPlacements": !curStep.value.target ? undefined : (_a = restProps.builtinPlacements) !== null && _a !== void 0 ? _a : getPlacements(arrowPointAtCenter.value),
  202. "ref": triggerRef,
  203. "popupStyle": !curStep.value.target ? _extends(_extends({}, curStep.value.style), {
  204. position: 'fixed',
  205. left: CENTER_PLACEHOLDER.left,
  206. top: CENTER_PLACEHOLDER.top,
  207. transform: 'translate(-50%, -50%)'
  208. }) : curStep.value.style,
  209. "popupPlacement": mergedPlacement.value,
  210. "popupVisible": mergedOpen.value,
  211. "popupClassName": classNames(rootClassName, curStep.value.className),
  212. "prefixCls": prefixCls,
  213. "popup": getPopupElement,
  214. "forceRender": false,
  215. "destroyPopupOnHide": true,
  216. "zIndex": zIndex,
  217. "mask": false,
  218. "getTriggerDOMNode": getTriggerDOMNode
  219. }), {
  220. default: () => [_createVNode(Portal, {
  221. "visible": mergedOpen.value,
  222. "autoLock": true
  223. }, {
  224. default: () => [_createVNode("div", {
  225. "class": classNames(rootClassName, `${prefixCls}-target-placeholder`),
  226. "style": _extends(_extends({}, posInfoStyle.value), {
  227. position: 'fixed',
  228. pointerEvents: 'none'
  229. })
  230. }, null)]
  231. })]
  232. })]) : null;
  233. };
  234. }
  235. });
  236. export default Tour;