cf9982577b567e1ef14a3f0c4558d8a80c10a7e9499cbd17a6fd57f0ea64db053a6d4445e3900f1941890e65d79f06a244aa7950038d579b222fd80fd87eab 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { defineComponent, computed, getCurrentInstance, ref, watch, nextTick, provide, createVNode, renderSlot } from 'vue';
  2. import { omit } from 'lodash-unified';
  3. import { ElIcon } from '../../icon/index.mjs';
  4. import { Plus } from '@element-plus/icons-vue';
  5. import { tabsRootContextKey } from './constants.mjs';
  6. import TabNav from './tab-nav.mjs';
  7. import { buildProps, definePropType } from '../../../utils/vue/props/runtime.mjs';
  8. import { UPDATE_MODEL_EVENT } from '../../../constants/event.mjs';
  9. import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
  10. import { useOrderedChildren } from '../../../hooks/use-ordered-children/index.mjs';
  11. import { EVENT_CODE } from '../../../constants/aria.mjs';
  12. import { isString } from '@vue/shared';
  13. import { isNumber, isUndefined } from '../../../utils/types.mjs';
  14. const tabsProps = buildProps({
  15. type: {
  16. type: String,
  17. values: ["card", "border-card", ""],
  18. default: ""
  19. },
  20. closable: Boolean,
  21. addable: Boolean,
  22. modelValue: {
  23. type: [String, Number]
  24. },
  25. editable: Boolean,
  26. tabPosition: {
  27. type: String,
  28. values: ["top", "right", "bottom", "left"],
  29. default: "top"
  30. },
  31. beforeLeave: {
  32. type: definePropType(Function),
  33. default: () => true
  34. },
  35. stretch: Boolean
  36. });
  37. const isPaneName = (value) => isString(value) || isNumber(value);
  38. const tabsEmits = {
  39. [UPDATE_MODEL_EVENT]: (name) => isPaneName(name),
  40. tabClick: (pane, ev) => ev instanceof Event,
  41. tabChange: (name) => isPaneName(name),
  42. edit: (paneName, action) => ["remove", "add"].includes(action),
  43. tabRemove: (name) => isPaneName(name),
  44. tabAdd: () => true
  45. };
  46. const Tabs = defineComponent({
  47. name: "ElTabs",
  48. props: tabsProps,
  49. emits: tabsEmits,
  50. setup(props, {
  51. emit,
  52. slots,
  53. expose
  54. }) {
  55. var _a;
  56. const ns = useNamespace("tabs");
  57. const isVertical = computed(() => ["left", "right"].includes(props.tabPosition));
  58. const {
  59. children: panes,
  60. addChild: registerPane,
  61. removeChild: unregisterPane,
  62. ChildrenSorter: PanesSorter
  63. } = useOrderedChildren(getCurrentInstance(), "ElTabPane");
  64. const nav$ = ref();
  65. const currentName = ref((_a = props.modelValue) != null ? _a : "0");
  66. const setCurrentName = async (value, trigger = false) => {
  67. var _a2, _b, _c, _d;
  68. if (currentName.value === value || isUndefined(value))
  69. return;
  70. try {
  71. let canLeave;
  72. if (props.beforeLeave) {
  73. const result = props.beforeLeave(value, currentName.value);
  74. canLeave = result instanceof Promise ? await result : result;
  75. } else {
  76. canLeave = true;
  77. }
  78. if (canLeave !== false) {
  79. const isFocusInsidePane = (_a2 = panes.value.find((item) => item.paneName === currentName.value)) == null ? void 0 : _a2.isFocusInsidePane();
  80. currentName.value = value;
  81. if (trigger) {
  82. emit(UPDATE_MODEL_EVENT, value);
  83. emit("tabChange", value);
  84. }
  85. (_c = (_b = nav$.value) == null ? void 0 : _b.removeFocus) == null ? void 0 : _c.call(_b);
  86. if (isFocusInsidePane) {
  87. (_d = nav$.value) == null ? void 0 : _d.focusActiveTab();
  88. }
  89. }
  90. } catch (e) {
  91. }
  92. };
  93. const handleTabClick = (tab, tabName, event) => {
  94. if (tab.props.disabled)
  95. return;
  96. emit("tabClick", tab, event);
  97. setCurrentName(tabName, true);
  98. };
  99. const handleTabRemove = (pane, ev) => {
  100. if (pane.props.disabled || isUndefined(pane.props.name))
  101. return;
  102. ev.stopPropagation();
  103. emit("edit", pane.props.name, "remove");
  104. emit("tabRemove", pane.props.name);
  105. };
  106. const handleTabAdd = () => {
  107. emit("edit", void 0, "add");
  108. emit("tabAdd");
  109. };
  110. const swapChildren = (vnode) => {
  111. const actualFirstChild = vnode.el.firstChild;
  112. const firstChild = ["bottom", "right"].includes(props.tabPosition) ? vnode.children[0].el : vnode.children[1].el;
  113. if (actualFirstChild !== firstChild) {
  114. actualFirstChild.before(firstChild);
  115. }
  116. };
  117. watch(() => props.modelValue, (modelValue) => setCurrentName(modelValue));
  118. watch(currentName, async () => {
  119. var _a2;
  120. await nextTick();
  121. (_a2 = nav$.value) == null ? void 0 : _a2.scrollToActiveTab();
  122. });
  123. provide(tabsRootContextKey, {
  124. props,
  125. currentName,
  126. registerPane,
  127. unregisterPane,
  128. nav$
  129. });
  130. expose({
  131. currentName,
  132. get tabNavRef() {
  133. return omit(nav$.value, ["scheduleRender"]);
  134. }
  135. });
  136. return () => {
  137. const addSlot = slots["add-icon"];
  138. const newButton = props.editable || props.addable ? createVNode("div", {
  139. "class": [ns.e("new-tab"), isVertical.value && ns.e("new-tab-vertical")],
  140. "tabindex": "0",
  141. "onClick": handleTabAdd,
  142. "onKeydown": (ev) => {
  143. if ([EVENT_CODE.enter, EVENT_CODE.numpadEnter].includes(ev.code))
  144. handleTabAdd();
  145. }
  146. }, [addSlot ? renderSlot(slots, "add-icon") : createVNode(ElIcon, {
  147. "class": ns.is("icon-plus")
  148. }, {
  149. default: () => [createVNode(Plus, null, null)]
  150. })]) : null;
  151. const tabNav = () => createVNode(TabNav, {
  152. "ref": nav$,
  153. "currentName": currentName.value,
  154. "editable": props.editable,
  155. "type": props.type,
  156. "panes": panes.value,
  157. "stretch": props.stretch,
  158. "onTabClick": handleTabClick,
  159. "onTabRemove": handleTabRemove
  160. }, null);
  161. const header = createVNode("div", {
  162. "class": [ns.e("header"), isVertical.value && ns.e("header-vertical"), ns.is(props.tabPosition)]
  163. }, [createVNode(PanesSorter, null, {
  164. default: tabNav,
  165. $stable: true
  166. }), newButton]);
  167. const panels = createVNode("div", {
  168. "class": ns.e("content")
  169. }, [renderSlot(slots, "default")]);
  170. return createVNode("div", {
  171. "class": [ns.b(), ns.m(props.tabPosition), {
  172. [ns.m("card")]: props.type === "card",
  173. [ns.m("border-card")]: props.type === "border-card"
  174. }],
  175. "onVnodeMounted": swapChildren,
  176. "onVnodeUpdated": swapChildren
  177. }, [panels, header]);
  178. };
  179. }
  180. });
  181. var Tabs$1 = Tabs;
  182. export { Tabs$1 as default, tabsEmits, tabsProps };
  183. //# sourceMappingURL=tabs.mjs.map