IndexBar.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. var __defProp = Object.defineProperty;
  2. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  3. var __getOwnPropNames = Object.getOwnPropertyNames;
  4. var __hasOwnProp = Object.prototype.hasOwnProperty;
  5. var __export = (target, all) => {
  6. for (var name2 in all)
  7. __defProp(target, name2, { get: all[name2], enumerable: true });
  8. };
  9. var __copyProps = (to, from, except, desc) => {
  10. if (from && typeof from === "object" || typeof from === "function") {
  11. for (let key of __getOwnPropNames(from))
  12. if (!__hasOwnProp.call(to, key) && key !== except)
  13. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  14. }
  15. return to;
  16. };
  17. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  18. var stdin_exports = {};
  19. __export(stdin_exports, {
  20. INDEX_BAR_KEY: () => INDEX_BAR_KEY,
  21. default: () => stdin_default,
  22. indexBarProps: () => indexBarProps
  23. });
  24. module.exports = __toCommonJS(stdin_exports);
  25. var import_vue = require("vue");
  26. var import_vue2 = require("vue");
  27. var import_utils = require("../utils");
  28. var import_use = require("@vant/use");
  29. var import_use_touch = require("../composables/use-touch");
  30. var import_use_expose = require("../composables/use-expose");
  31. function genAlphabet() {
  32. const charCodeOfA = "A".charCodeAt(0);
  33. const indexList = Array(26).fill("").map((_, i) => String.fromCharCode(charCodeOfA + i));
  34. return indexList;
  35. }
  36. const [name, bem] = (0, import_utils.createNamespace)("index-bar");
  37. const indexBarProps = {
  38. sticky: import_utils.truthProp,
  39. zIndex: import_utils.numericProp,
  40. teleport: [String, Object],
  41. highlightColor: String,
  42. stickyOffsetTop: (0, import_utils.makeNumberProp)(0),
  43. indexList: {
  44. type: Array,
  45. default: genAlphabet
  46. }
  47. };
  48. const INDEX_BAR_KEY = Symbol(name);
  49. var stdin_default = (0, import_vue2.defineComponent)({
  50. name,
  51. props: indexBarProps,
  52. emits: ["select", "change"],
  53. setup(props, {
  54. emit,
  55. slots
  56. }) {
  57. const root = (0, import_vue2.ref)();
  58. const sidebar = (0, import_vue2.ref)();
  59. const activeAnchor = (0, import_vue2.ref)("");
  60. const touch = (0, import_use_touch.useTouch)();
  61. const scrollParent = (0, import_use.useScrollParent)(root);
  62. const {
  63. children,
  64. linkChildren
  65. } = (0, import_use.useChildren)(INDEX_BAR_KEY);
  66. let selectActiveIndex;
  67. linkChildren({
  68. props
  69. });
  70. const sidebarStyle = (0, import_vue2.computed)(() => {
  71. if ((0, import_utils.isDef)(props.zIndex)) {
  72. return {
  73. zIndex: +props.zIndex + 1
  74. };
  75. }
  76. });
  77. const highlightStyle = (0, import_vue2.computed)(() => {
  78. if (props.highlightColor) {
  79. return {
  80. color: props.highlightColor
  81. };
  82. }
  83. });
  84. const getActiveAnchor = (scrollTop, rects) => {
  85. for (let i = children.length - 1; i >= 0; i--) {
  86. const prevHeight = i > 0 ? rects[i - 1].height : 0;
  87. const reachTop = props.sticky ? prevHeight + props.stickyOffsetTop : 0;
  88. if (scrollTop + reachTop >= rects[i].top) {
  89. return i;
  90. }
  91. }
  92. return -1;
  93. };
  94. const getMatchAnchor = (index) => children.find((item) => String(item.index) === index);
  95. const onScroll = () => {
  96. if ((0, import_utils.isHidden)(root)) {
  97. return;
  98. }
  99. const {
  100. sticky,
  101. indexList
  102. } = props;
  103. const scrollTop = (0, import_utils.getScrollTop)(scrollParent.value);
  104. const scrollParentRect = (0, import_use.useRect)(scrollParent);
  105. const rects = children.map((item) => item.getRect(scrollParent.value, scrollParentRect));
  106. let active = -1;
  107. if (selectActiveIndex) {
  108. const match = getMatchAnchor(selectActiveIndex);
  109. if (match) {
  110. const rect = match.getRect(scrollParent.value, scrollParentRect);
  111. active = getActiveAnchor(rect.top, rects);
  112. }
  113. } else {
  114. active = getActiveAnchor(scrollTop, rects);
  115. }
  116. activeAnchor.value = indexList[active];
  117. if (sticky) {
  118. children.forEach((item, index) => {
  119. const {
  120. state,
  121. $el
  122. } = item;
  123. if (index === active || index === active - 1) {
  124. const rect = $el.getBoundingClientRect();
  125. state.left = rect.left;
  126. state.width = rect.width;
  127. } else {
  128. state.left = null;
  129. state.width = null;
  130. }
  131. if (index === active) {
  132. state.active = true;
  133. state.top = Math.max(props.stickyOffsetTop, rects[index].top - scrollTop) + scrollParentRect.top;
  134. } else if (index === active - 1 && selectActiveIndex === "") {
  135. const activeItemTop = rects[active].top - scrollTop;
  136. state.active = activeItemTop > 0;
  137. state.top = activeItemTop + scrollParentRect.top - rects[index].height;
  138. } else {
  139. state.active = false;
  140. }
  141. });
  142. }
  143. selectActiveIndex = "";
  144. };
  145. const init = () => {
  146. (0, import_vue2.nextTick)(onScroll);
  147. };
  148. (0, import_use.useEventListener)("scroll", onScroll, {
  149. target: scrollParent,
  150. passive: true
  151. });
  152. (0, import_vue2.onMounted)(init);
  153. (0, import_vue2.watch)(() => props.indexList, init);
  154. (0, import_vue2.watch)(activeAnchor, (value) => {
  155. if (value) {
  156. emit("change", value);
  157. }
  158. });
  159. const renderIndexes = () => props.indexList.map((index) => {
  160. const active = index === activeAnchor.value;
  161. return (0, import_vue.createVNode)("span", {
  162. "class": bem("index", {
  163. active
  164. }),
  165. "style": active ? highlightStyle.value : void 0,
  166. "data-index": index
  167. }, [index]);
  168. });
  169. const scrollTo = (index) => {
  170. selectActiveIndex = String(index);
  171. const match = getMatchAnchor(selectActiveIndex);
  172. if (match) {
  173. const scrollTop = (0, import_utils.getScrollTop)(scrollParent.value);
  174. const scrollParentRect = (0, import_use.useRect)(scrollParent);
  175. const {
  176. offsetHeight
  177. } = document.documentElement;
  178. match.$el.scrollIntoView();
  179. if (scrollTop === offsetHeight - scrollParentRect.height) {
  180. onScroll();
  181. return;
  182. }
  183. if (props.sticky && props.stickyOffsetTop) {
  184. (0, import_utils.setRootScrollTop)((0, import_utils.getRootScrollTop)() - props.stickyOffsetTop);
  185. }
  186. emit("select", match.index);
  187. }
  188. };
  189. const scrollToElement = (element) => {
  190. const {
  191. index
  192. } = element.dataset;
  193. if (index) {
  194. scrollTo(index);
  195. }
  196. };
  197. const onClickSidebar = (event) => {
  198. scrollToElement(event.target);
  199. };
  200. let touchActiveIndex;
  201. const onTouchMove = (event) => {
  202. touch.move(event);
  203. if (touch.isVertical()) {
  204. (0, import_utils.preventDefault)(event);
  205. const {
  206. clientX,
  207. clientY
  208. } = event.touches[0];
  209. const target = document.elementFromPoint(clientX, clientY);
  210. if (target) {
  211. const {
  212. index
  213. } = target.dataset;
  214. if (index && touchActiveIndex !== index) {
  215. touchActiveIndex = index;
  216. scrollToElement(target);
  217. }
  218. }
  219. }
  220. };
  221. const renderSidebar = () => (0, import_vue.createVNode)("div", {
  222. "ref": sidebar,
  223. "class": bem("sidebar"),
  224. "style": sidebarStyle.value,
  225. "onClick": onClickSidebar,
  226. "onTouchstartPassive": touch.start
  227. }, [renderIndexes()]);
  228. (0, import_use_expose.useExpose)({
  229. scrollTo
  230. });
  231. (0, import_use.useEventListener)("touchmove", onTouchMove, {
  232. target: sidebar
  233. });
  234. return () => {
  235. var _a;
  236. return (0, import_vue.createVNode)("div", {
  237. "ref": root,
  238. "class": bem()
  239. }, [props.teleport ? (0, import_vue.createVNode)(import_vue2.Teleport, {
  240. "to": props.teleport
  241. }, {
  242. default: () => [renderSidebar()]
  243. }) : renderSidebar(), (_a = slots.default) == null ? void 0 : _a.call(slots)]);
  244. };
  245. }
  246. });