UNPKG

13.6 kBJavaScriptView Raw
1var __create = Object.create;
2var __defProp = Object.defineProperty;
3var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4var __getOwnPropNames = Object.getOwnPropertyNames;
5var __getProtoOf = Object.getPrototypeOf;
6var __hasOwnProp = Object.prototype.hasOwnProperty;
7var __export = (target, all) => {
8 for (var name2 in all)
9 __defProp(target, name2, { get: all[name2], enumerable: true });
10};
11var __copyProps = (to, from, except, desc) => {
12 if (from && typeof from === "object" || typeof from === "function") {
13 for (let key of __getOwnPropNames(from))
14 if (!__hasOwnProp.call(to, key) && key !== except)
15 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16 }
17 return to;
18};
19var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20 // If the importer is in node compatibility mode or this is not an ESM
21 // file that has been converted to a CommonJS file using a Babel-
22 // compatible transform (i.e. "__esModule" has not been set), then set
23 // "default" to the CommonJS "module.exports" for node compatibility.
24 isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25 mod
26));
27var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28var stdin_exports = {};
29__export(stdin_exports, {
30 TABS_KEY: () => TABS_KEY,
31 default: () => stdin_default,
32 tabsProps: () => tabsProps
33});
34module.exports = __toCommonJS(stdin_exports);
35var import_vue = require("vue");
36var import_utils = require("../utils");
37var import_utils2 = require("./utils");
38var import_use = require("@vant/use");
39var import_use_id = require("../composables/use-id");
40var import_use_route = require("../composables/use-route");
41var import_use_refs = require("../composables/use-refs");
42var import_use_expose = require("../composables/use-expose");
43var import_on_popup_reopen = require("../composables/on-popup-reopen");
44var import_use_visibility_change = require("../composables/use-visibility-change");
45var import_sticky = require("../sticky");
46var import_TabsContent = __toESM(require("./TabsContent"));
47const [name, bem] = (0, import_utils.createNamespace)("tabs");
48const tabsProps = {
49 type: (0, import_utils.makeStringProp)("line"),
50 color: String,
51 border: Boolean,
52 sticky: Boolean,
53 shrink: Boolean,
54 active: (0, import_utils.makeNumericProp)(0),
55 duration: (0, import_utils.makeNumericProp)(0.3),
56 animated: Boolean,
57 ellipsis: import_utils.truthProp,
58 swipeable: Boolean,
59 scrollspy: Boolean,
60 offsetTop: (0, import_utils.makeNumericProp)(0),
61 background: String,
62 lazyRender: import_utils.truthProp,
63 showHeader: import_utils.truthProp,
64 lineWidth: import_utils.numericProp,
65 lineHeight: import_utils.numericProp,
66 beforeChange: Function,
67 swipeThreshold: (0, import_utils.makeNumericProp)(5),
68 titleActiveColor: String,
69 titleInactiveColor: String
70};
71const TABS_KEY = Symbol(name);
72var stdin_default = (0, import_vue.defineComponent)({
73 name,
74 props: tabsProps,
75 emits: ["change", "scroll", "rendered", "clickTab", "update:active"],
76 setup(props, {
77 emit,
78 slots
79 }) {
80 let tabHeight;
81 let lockScroll;
82 let stickyFixed;
83 let cancelScrollLeftToRaf;
84 let cancelScrollTopToRaf;
85 const root = (0, import_vue.ref)();
86 const navRef = (0, import_vue.ref)();
87 const wrapRef = (0, import_vue.ref)();
88 const contentRef = (0, import_vue.ref)();
89 const id = (0, import_use_id.useId)();
90 const scroller = (0, import_use.useScrollParent)(root);
91 const [titleRefs, setTitleRefs] = (0, import_use_refs.useRefs)();
92 const {
93 children,
94 linkChildren
95 } = (0, import_use.useChildren)(TABS_KEY);
96 const state = (0, import_vue.reactive)({
97 inited: false,
98 position: "",
99 lineStyle: {},
100 currentIndex: -1
101 });
102 const scrollable = (0, import_vue.computed)(() => children.length > +props.swipeThreshold || !props.ellipsis || props.shrink);
103 const navStyle = (0, import_vue.computed)(() => ({
104 borderColor: props.color,
105 background: props.background
106 }));
107 const getTabName = (tab, index) => {
108 var _a;
109 return (_a = tab.name) != null ? _a : index;
110 };
111 const currentName = (0, import_vue.computed)(() => {
112 const activeTab = children[state.currentIndex];
113 if (activeTab) {
114 return getTabName(activeTab, state.currentIndex);
115 }
116 });
117 const offsetTopPx = (0, import_vue.computed)(() => (0, import_utils.unitToPx)(props.offsetTop));
118 const scrollOffset = (0, import_vue.computed)(() => {
119 if (props.sticky) {
120 return offsetTopPx.value + tabHeight;
121 }
122 return 0;
123 });
124 const scrollIntoView = (immediate) => {
125 const nav = navRef.value;
126 const titles = titleRefs.value;
127 if (!scrollable.value || !nav || !titles || !titles[state.currentIndex]) {
128 return;
129 }
130 const title = titles[state.currentIndex].$el;
131 const to = title.offsetLeft - (nav.offsetWidth - title.offsetWidth) / 2;
132 if (cancelScrollLeftToRaf) cancelScrollLeftToRaf();
133 cancelScrollLeftToRaf = (0, import_utils2.scrollLeftTo)(nav, to, immediate ? 0 : +props.duration);
134 };
135 const setLine = () => {
136 const shouldAnimate = state.inited;
137 (0, import_vue.nextTick)(() => {
138 const titles = titleRefs.value;
139 if (!titles || !titles[state.currentIndex] || props.type !== "line" || (0, import_utils.isHidden)(root.value)) {
140 return;
141 }
142 const title = titles[state.currentIndex].$el;
143 const {
144 lineWidth,
145 lineHeight
146 } = props;
147 const left = title.offsetLeft + title.offsetWidth / 2;
148 const lineStyle = {
149 width: (0, import_utils.addUnit)(lineWidth),
150 backgroundColor: props.color,
151 transform: `translateX(${left}px) translateX(-50%)`
152 };
153 if (shouldAnimate) {
154 lineStyle.transitionDuration = `${props.duration}s`;
155 }
156 if ((0, import_utils.isDef)(lineHeight)) {
157 const height = (0, import_utils.addUnit)(lineHeight);
158 lineStyle.height = height;
159 lineStyle.borderRadius = height;
160 }
161 state.lineStyle = lineStyle;
162 });
163 };
164 const findAvailableTab = (index) => {
165 const diff = index < state.currentIndex ? -1 : 1;
166 while (index >= 0 && index < children.length) {
167 if (!children[index].disabled) {
168 return index;
169 }
170 index += diff;
171 }
172 };
173 const setCurrentIndex = (currentIndex, skipScrollIntoView) => {
174 const newIndex = findAvailableTab(currentIndex);
175 if (!(0, import_utils.isDef)(newIndex)) {
176 return;
177 }
178 const newTab = children[newIndex];
179 const newName = getTabName(newTab, newIndex);
180 const shouldEmitChange = state.currentIndex !== null;
181 if (state.currentIndex !== newIndex) {
182 state.currentIndex = newIndex;
183 if (!skipScrollIntoView) {
184 scrollIntoView();
185 }
186 setLine();
187 }
188 if (newName !== props.active) {
189 emit("update:active", newName);
190 if (shouldEmitChange) {
191 emit("change", newName, newTab.title);
192 }
193 }
194 if (stickyFixed && !props.scrollspy) {
195 (0, import_utils.setRootScrollTop)(Math.ceil((0, import_utils.getElementTop)(root.value) - offsetTopPx.value));
196 }
197 };
198 const setCurrentIndexByName = (name2, skipScrollIntoView) => {
199 const matched = children.find((tab, index2) => getTabName(tab, index2) === name2);
200 const index = matched ? children.indexOf(matched) : 0;
201 setCurrentIndex(index, skipScrollIntoView);
202 };
203 const scrollToCurrentContent = (immediate = false) => {
204 if (props.scrollspy) {
205 const target = children[state.currentIndex].$el;
206 if (target && scroller.value) {
207 const to = (0, import_utils.getElementTop)(target, scroller.value) - scrollOffset.value;
208 lockScroll = true;
209 if (cancelScrollTopToRaf) cancelScrollTopToRaf();
210 cancelScrollTopToRaf = (0, import_utils2.scrollTopTo)(scroller.value, to, immediate ? 0 : +props.duration, () => {
211 lockScroll = false;
212 });
213 }
214 }
215 };
216 const onClickTab = (item, index, event) => {
217 const {
218 title,
219 disabled
220 } = children[index];
221 const name2 = getTabName(children[index], index);
222 if (!disabled) {
223 (0, import_utils.callInterceptor)(props.beforeChange, {
224 args: [name2],
225 done: () => {
226 setCurrentIndex(index);
227 scrollToCurrentContent();
228 }
229 });
230 (0, import_use_route.route)(item);
231 }
232 emit("clickTab", {
233 name: name2,
234 title,
235 event,
236 disabled
237 });
238 };
239 const onStickyScroll = (params) => {
240 stickyFixed = params.isFixed;
241 emit("scroll", params);
242 };
243 const scrollTo = (name2) => {
244 (0, import_vue.nextTick)(() => {
245 setCurrentIndexByName(name2);
246 scrollToCurrentContent(true);
247 });
248 };
249 const getCurrentIndexOnScroll = () => {
250 for (let index = 0; index < children.length; index++) {
251 const {
252 top
253 } = (0, import_use.useRect)(children[index].$el);
254 if (top > scrollOffset.value) {
255 return index === 0 ? 0 : index - 1;
256 }
257 }
258 return children.length - 1;
259 };
260 const onScroll = () => {
261 if (props.scrollspy && !lockScroll) {
262 const index = getCurrentIndexOnScroll();
263 setCurrentIndex(index);
264 }
265 };
266 const renderLine = () => {
267 if (props.type === "line" && children.length) {
268 return (0, import_vue.createVNode)("div", {
269 "class": bem("line"),
270 "style": state.lineStyle
271 }, null);
272 }
273 };
274 const renderHeader = () => {
275 var _a, _b, _c;
276 const {
277 type,
278 border,
279 sticky
280 } = props;
281 const Header = [(0, import_vue.createVNode)("div", {
282 "ref": sticky ? void 0 : wrapRef,
283 "class": [bem("wrap"), {
284 [import_utils.BORDER_TOP_BOTTOM]: type === "line" && border
285 }]
286 }, [(0, import_vue.createVNode)("div", {
287 "ref": navRef,
288 "role": "tablist",
289 "class": bem("nav", [type, {
290 shrink: props.shrink,
291 complete: scrollable.value
292 }]),
293 "style": navStyle.value,
294 "aria-orientation": "horizontal"
295 }, [(_a = slots["nav-left"]) == null ? void 0 : _a.call(slots), children.map((item) => item.renderTitle(onClickTab)), renderLine(), (_b = slots["nav-right"]) == null ? void 0 : _b.call(slots)])]), (_c = slots["nav-bottom"]) == null ? void 0 : _c.call(slots)];
296 if (sticky) {
297 return (0, import_vue.createVNode)("div", {
298 "ref": wrapRef
299 }, [Header]);
300 }
301 return Header;
302 };
303 const resize = () => {
304 setLine();
305 (0, import_vue.nextTick)(() => {
306 var _a, _b;
307 scrollIntoView(true);
308 (_b = (_a = contentRef.value) == null ? void 0 : _a.swipeRef.value) == null ? void 0 : _b.resize();
309 });
310 };
311 (0, import_vue.watch)(() => [props.color, props.duration, props.lineWidth, props.lineHeight], setLine);
312 (0, import_vue.watch)(import_utils.windowWidth, resize);
313 (0, import_vue.watch)(() => props.active, (value) => {
314 if (value !== currentName.value) {
315 setCurrentIndexByName(value);
316 }
317 });
318 (0, import_vue.watch)(() => children.length, () => {
319 if (state.inited) {
320 setCurrentIndexByName(props.active);
321 setLine();
322 (0, import_vue.nextTick)(() => {
323 scrollIntoView(true);
324 });
325 }
326 });
327 const init = () => {
328 setCurrentIndexByName(props.active, true);
329 (0, import_vue.nextTick)(() => {
330 state.inited = true;
331 if (wrapRef.value) {
332 tabHeight = (0, import_use.useRect)(wrapRef.value).height;
333 }
334 scrollIntoView(true);
335 });
336 };
337 const onRendered = (name2, title) => emit("rendered", name2, title);
338 (0, import_use_expose.useExpose)({
339 resize,
340 scrollTo
341 });
342 (0, import_vue.onActivated)(setLine);
343 (0, import_on_popup_reopen.onPopupReopen)(setLine);
344 (0, import_use.onMountedOrActivated)(init);
345 (0, import_use_visibility_change.useVisibilityChange)(root, setLine);
346 (0, import_use.useEventListener)("scroll", onScroll, {
347 target: scroller,
348 passive: true
349 });
350 linkChildren({
351 id,
352 props,
353 setLine,
354 scrollable,
355 onRendered,
356 currentName,
357 setTitleRefs,
358 scrollIntoView
359 });
360 return () => (0, import_vue.createVNode)("div", {
361 "ref": root,
362 "class": bem([props.type])
363 }, [props.showHeader ? props.sticky ? (0, import_vue.createVNode)(import_sticky.Sticky, {
364 "container": root.value,
365 "offsetTop": offsetTopPx.value,
366 "onScroll": onStickyScroll
367 }, {
368 default: () => [renderHeader()]
369 }) : renderHeader() : null, (0, import_vue.createVNode)(import_TabsContent.default, {
370 "ref": contentRef,
371 "count": children.length,
372 "inited": state.inited,
373 "animated": props.animated,
374 "duration": props.duration,
375 "swipeable": props.swipeable,
376 "lazyRender": props.lazyRender,
377 "currentIndex": state.currentIndex,
378 "onChange": setCurrentIndex
379 }, {
380 default: () => {
381 var _a;
382 return [(_a = slots.default) == null ? void 0 : _a.call(slots)];
383 }
384 })]);
385 }
386});