IndexBar.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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. });
  23. module.exports = __toCommonJS(stdin_exports);
  24. var import_vue = require("vue");
  25. var import_vue2 = 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_vue2.defineComponent)({
  49. name,
  50. props: indexBarProps,
  51. emits: ["select", "change"],
  52. setup(props, {
  53. emit,
  54. slots
  55. }) {
  56. const root = (0, import_vue2.ref)();
  57. const sidebar = (0, import_vue2.ref)();
  58. const activeAnchor = (0, import_vue2.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_vue2.computed)(() => {
  70. if ((0, import_utils.isDef)(props.zIndex)) {
  71. return {
  72. zIndex: +props.zIndex + 1
  73. };
  74. }
  75. });
  76. const highlightStyle = (0, import_vue2.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. active = getActiveAnchor(rect.top, rects);
  111. }
  112. } else {
  113. active = getActiveAnchor(scrollTop, rects);
  114. }
  115. activeAnchor.value = indexList[active];
  116. if (sticky) {
  117. children.forEach((item, index) => {
  118. const {
  119. state,
  120. $el
  121. } = item;
  122. if (index === active || index === active - 1) {
  123. const rect = $el.getBoundingClientRect();
  124. state.left = rect.left;
  125. state.width = rect.width;
  126. } else {
  127. state.left = null;
  128. state.width = null;
  129. }
  130. if (index === active) {
  131. state.active = true;
  132. state.top = Math.max(props.stickyOffsetTop, rects[index].top - scrollTop) + scrollParentRect.top;
  133. } else if (index === active - 1 && selectActiveIndex === "") {
  134. const activeItemTop = rects[active].top - scrollTop;
  135. state.active = activeItemTop > 0;
  136. state.top = activeItemTop + scrollParentRect.top - rects[index].height;
  137. } else {
  138. state.active = false;
  139. }
  140. });
  141. }
  142. selectActiveIndex = "";
  143. };
  144. const init = () => {
  145. (0, import_vue2.nextTick)(onScroll);
  146. };
  147. (0, import_use.useEventListener)("scroll", onScroll, {
  148. target: scrollParent,
  149. passive: true
  150. });
  151. (0, import_vue2.onMounted)(init);
  152. (0, import_vue2.watch)(() => props.indexList, init);
  153. (0, import_vue2.watch)(activeAnchor, (value) => {
  154. if (value) {
  155. emit("change", value);
  156. }
  157. });
  158. const renderIndexes = () => props.indexList.map((index) => {
  159. const active = index === activeAnchor.value;
  160. return (0, import_vue.createVNode)("span", {
  161. "class": bem("index", {
  162. active
  163. }),
  164. "style": active ? highlightStyle.value : void 0,
  165. "data-index": index
  166. }, [index]);
  167. });
  168. const scrollTo = (index) => {
  169. selectActiveIndex = String(index);
  170. const match = getMatchAnchor(selectActiveIndex);
  171. if (match) {
  172. const scrollTop = (0, import_utils.getScrollTop)(scrollParent.value);
  173. const scrollParentRect = (0, import_use.useRect)(scrollParent);
  174. const {
  175. offsetHeight
  176. } = document.documentElement;
  177. match.$el.scrollIntoView();
  178. if (scrollTop === offsetHeight - scrollParentRect.height) {
  179. onScroll();
  180. return;
  181. }
  182. if (props.sticky && props.stickyOffsetTop) {
  183. (0, import_utils.setRootScrollTop)((0, import_utils.getRootScrollTop)() - props.stickyOffsetTop);
  184. }
  185. emit("select", match.index);
  186. }
  187. };
  188. const scrollToElement = (element) => {
  189. const {
  190. index
  191. } = element.dataset;
  192. if (index) {
  193. scrollTo(index);
  194. }
  195. };
  196. const onClickSidebar = (event) => {
  197. scrollToElement(event.target);
  198. };
  199. let touchActiveIndex;
  200. const onTouchMove = (event) => {
  201. touch.move(event);
  202. if (touch.isVertical()) {
  203. (0, import_utils.preventDefault)(event);
  204. const {
  205. clientX,
  206. clientY
  207. } = event.touches[0];
  208. const target = document.elementFromPoint(clientX, clientY);
  209. if (target) {
  210. const {
  211. index
  212. } = target.dataset;
  213. if (index && touchActiveIndex !== index) {
  214. touchActiveIndex = index;
  215. scrollToElement(target);
  216. }
  217. }
  218. }
  219. };
  220. const renderSidebar = () => (0, import_vue.createVNode)("div", {
  221. "ref": sidebar,
  222. "class": bem("sidebar"),
  223. "style": sidebarStyle.value,
  224. "onClick": onClickSidebar,
  225. "onTouchstartPassive": touch.start
  226. }, [renderIndexes()]);
  227. (0, import_use_expose.useExpose)({
  228. scrollTo
  229. });
  230. (0, import_use.useEventListener)("touchmove", onTouchMove, {
  231. target: sidebar
  232. });
  233. return () => {
  234. var _a;
  235. return (0, import_vue.createVNode)("div", {
  236. "ref": root,
  237. "class": bem()
  238. }, [props.teleport ? (0, import_vue.createVNode)(import_vue2.Teleport, {
  239. "to": props.teleport
  240. }, {
  241. default: () => [renderSidebar()]
  242. }) : renderSidebar(), (_a = slots.default) == null ? void 0 : _a.call(slots)]);
  243. };
  244. }
  245. });