tabs.mjs 5.6 KB

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