IndexBar.js 8.3 KB

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