hongluan-ui
Version:
Hongluan Component Library for Vue 3
1 lines • 11.5 kB
Source Map (JSON)
{"version":3,"file":"tabs.mjs","sources":["../../../../../../packages/components/tabs/src/tabs.tsx"],"sourcesContent":["import {\n defineComponent,\n createVNode,\n getCurrentInstance,\n provide,\n ref,\n renderSlot,\n watch,\n computed,\n nextTick,\n} from 'vue'\nimport {\n buildProps,\n definePropType,\n isFunction,\n isNumber,\n isString,\n isUndefined,\n} from '@hongluan-ui/utils'\nimport {\n EVENT_CODE,\n INPUT_EVENT,\n UPDATE_MODEL_EVENT,\n} from '@hongluan-ui/constants'\nimport HlIcon from '@hongluan-ui/components/icon'\nimport { SystemAdd } from '@hongluan-ui/components/system-icon'\nimport { tabsRootContextKey } from '@hongluan-ui/tokens'\nimport { useDeprecated, useNamespace, useOrderedChildren } from '@hongluan-ui/hooks'\nimport TabNav from './tab-nav'\nimport type { TabNavInstance } from './tab-nav'\nimport type { TabsPaneContext } from '@hongluan-ui/tokens'\n\nimport type { ExtractPropTypes, FunctionalComponent, VNode } from 'vue'\nimport type { Awaitable } from '@hongluan-ui/utils'\n\nexport type TabPaneName = string | number\n\nexport const tabsProps = buildProps({\n /**\n * @description type of Tab\n */\n type: {\n type: String,\n values: ['line', 'button', ''],\n default: '',\n },\n activeName: {\n type: [String, Number],\n },\n /**\n * @description whether Tab is closable\n */\n closable: Boolean,\n /**\n * @description whether Tab is addable\n */\n addable: Boolean,\n /**\n * @description binding value, name of the selected tab\n */\n modelValue: {\n type: [String, Number],\n },\n /**\n * @description whether Tab is addable and closable\n */\n editable: Boolean,\n /**\n * @description position of tabs\n */\n tabPosition: {\n type: String,\n values: ['top', 'right', 'bottom', 'left'],\n default: 'top',\n },\n /**\n * @description hook function before switching tab. If `false` is returned or a `Promise` is returned and then is rejected, switching will be prevented\n */\n beforeLeave: {\n type: definePropType<\n (\n newName: TabPaneName,\n oldName: TabPaneName\n ) => Awaitable<void | boolean>\n >(Function),\n default: () => true,\n },\n showPane: {\n type: Boolean,\n default: true,\n },\n paneHeight: {\n type: String,\n },\n navHeight: {\n type: String,\n },\n navPadding: {\n type: String,\n },\n gap: {\n type: String,\n },\n /**\n * @description whether width of tab automatically fits its container\n */\n stretch: Boolean,\n} as const)\nexport type TabsProps = ExtractPropTypes<typeof tabsProps>\n\nconst isPanelName = (value: unknown): value is string | number =>\n isString(value) || isNumber(value)\n\nexport const tabsEmits = {\n [UPDATE_MODEL_EVENT]: (name: TabPaneName) => isPanelName(name),\n /** @deprecated use `tab-change` instead */\n [INPUT_EVENT]: (name: TabPaneName) => isPanelName(name),\n tabClick: (pane: TabsPaneContext, ev: Event) => ev instanceof Event,\n tabChange: (name: TabPaneName) => isPanelName(name),\n edit: (paneName: TabPaneName | undefined, action: 'remove' | 'add') =>\n ['remove', 'add'].includes(action),\n tabRemove: (name: TabPaneName) => isPanelName(name),\n tabAdd: () => true,\n}\nexport type TabsEmits = typeof tabsEmits\nexport type TabsPanes = Record<number, TabsPaneContext>\n\nconst Tabs = defineComponent({\n name: 'Tabs',\n\n props: tabsProps,\n emits: tabsEmits,\n\n setup(props, { emit, slots, expose }) {\n const isVertical = computed(() =>\n ['left', 'right'].includes(props.tabPosition),\n )\n const {\n children: panes,\n addChild: sortPane,\n removeChild: unregisterPane,\n } = useOrderedChildren<TabsPaneContext>(getCurrentInstance()!, 'TabPane')\n\n const instance = getCurrentInstance()!\n const nav$ = ref<TabNavInstance>()\n const currentName = ref<TabPaneName>(\n props.modelValue ?? props.activeName ?? '0',\n )\n\n useDeprecated(\n {\n scope: 'Tabs',\n type: 'Event',\n from: 'input',\n replacement: 'tab-change',\n version: '2.5.0',\n ref: 'https://hongluan-ui.github.io/hongluan-ui/2x/#/zh-CN/component/tabs.html#tabs-events',\n },\n computed(() => isFunction(instance.vnode.props?.onInput)),\n )\n\n useDeprecated(\n {\n from: '\"activeName\"',\n replacement: '\"model-value\" or \"v-model\"',\n scope: 'Tabs',\n version: '2.5.0',\n ref: 'https://hongluan-ui.github.io/hongluan-ui/2x/#/zh-CN/component/tabs.html#attributes',\n type: 'Attribute',\n },\n computed(() => !!props.activeName),\n )\n\n const { namespace } = useNamespace('tabs')\n\n const setCurrentName = async (value?: TabPaneName, trigger = false) => {\n // should do nothing.\n if (currentName.value === value || isUndefined(value)) return\n\n try {\n const canLeave = await props.beforeLeave?.(value, currentName.value)\n if (canLeave !== false) {\n currentName.value = value\n if (trigger) {\n emit(UPDATE_MODEL_EVENT, value)\n emit('tabChange', value)\n }\n nav$.value?.removeFocus?.()\n }\n } catch {}\n }\n\n const handleTabClick = (\n tab: TabsPaneContext,\n tabName: TabPaneName,\n event: Event,\n ) => {\n if (tab.props.disabled) return\n setCurrentName(tabName, true)\n emit('tabClick', tab, event)\n }\n\n const handleTabRemove = (pane: TabsPaneContext, ev: Event) => {\n if (pane.props.disabled || isUndefined(pane.props.name)) return\n ev.stopPropagation()\n emit('edit', pane.props.name, 'remove')\n emit('tabRemove', pane.props.name)\n }\n\n const handleTabAdd = () => {\n emit('edit', undefined, 'add')\n emit('tabAdd')\n }\n\n watch(\n () => props.activeName,\n modelValue => setCurrentName(modelValue),\n )\n\n watch(\n () => props.modelValue,\n modelValue => setCurrentName(modelValue),\n )\n\n watch(currentName, async () => {\n await nextTick()\n nav$.value?.scrollToActiveTab()\n })\n\n provide(tabsRootContextKey, {\n props,\n currentName,\n registerPane: (pane: TabsPaneContext) => {\n panes.value.push(pane)\n },\n sortPane,\n unregisterPane,\n })\n\n expose({\n currentName,\n })\n\n const TabNavRenderer: FunctionalComponent<{ render: () => VNode; }> = ({\n render,\n }) => {\n return render()\n }\n\n return () => {\n const addSlot = slots['add-icon']\n const newButton =\n props.editable || props.addable ? (\n <div\n class={['tab-new', isVertical.value && 'new-tab-vertical']}\n tabindex=\"0\"\n onClick={handleTabAdd}\n onKeydown={(ev: KeyboardEvent) => {\n if (ev.code === EVENT_CODE.enter) handleTabAdd()\n }}\n >\n {addSlot ? (\n renderSlot(slots, 'add-icon')\n ) : (\n <HlIcon>\n <SystemAdd />\n </HlIcon>\n )}\n </div>\n ) : null\n\n const header = (\n <div class={['tabs-header', `at-${props.tabPosition}`, isVertical.value && 'header-vertical']}>\n <TabNavRenderer\n render={() => {\n const hasLabelSlot = panes.value.some(pane => pane.slots.label)\n return createVNode(\n TabNav,\n {\n ref: nav$,\n currentName: currentName.value,\n editable: props.editable,\n type: props.type,\n panes: panes.value,\n stretch: props.stretch,\n onTabClick: handleTabClick,\n onTabRemove: handleTabRemove,\n },\n { $stable: !hasLabelSlot },\n )\n }}\n />\n {newButton}\n </div>\n )\n\n const panels = (\n <div class={'tabs-content'}>{renderSlot(slots, 'default')}</div>\n )\n\n return (\n <div\n class={{\n [namespace.value]: true,\n 'tabs-line': props.type === 'line',\n 'tabs-button': props.type === 'button',\n 'no-pane': !props.showPane,\n [`tabs-${props.tabPosition}`]: true,\n }}\n style={[\n props.paneHeight ? `--tabs-pane-height:${props.paneHeight}` : '',\n props.navHeight ? `--tabs-height:${props.navHeight}` : '',\n props.navPadding ? `--tabs-padding:${props.navPadding}` : '',\n props.gap ? `--tabs-gap:${props.gap}` : '',\n ]}\n >\n {...props.tabPosition !== 'bottom'\n ? [header, panels]\n : [panels, header]}\n </div>\n )\n }\n },\n})\n\nexport type TabsInstance = InstanceType<typeof Tabs> & {\n currentName: TabPaneName\n}\n\nexport default Tabs\n"],"names":["tabsProps","buildProps","type","values","default","activeName","String","closable","addable","modelValue","editable","tabPosition","beforeLeave","Function","showPane","Boolean","paneHeight","navHeight","navPadding","gap","stretch","isPanelName","tabsEmits","tabClick","tabChange","edit","tabRemove","tabAdd","Tabs","name","props","emits","setup","emit","slots","expose","isVertical","children","addChild","removeChild","currentName","useDeprecated","scope","from","replacement","version","ref","namespace","value","nav$","handleTabClick","tab","setCurrentName","disabled","ev","watch","_createVNode","provide","tabsRootContextKey","registerPane","panes","pane","sortPane","unregisterPane","render"],"mappings":";;;;;;;;;;;;;;;;;;;MAqCaA,SAAS,GAAGC,UAAU,CAAC;AAClC;AACF;AACA;AACEC;AACEA;AACAC,YAAQ;AACRC,IAAAA,OAAO;AAHH,GAJ4B;AASlCC,EAAAA,UAAU;AACRH,WAAOI;AADG;;AAGZ;AACF;AACA;AACEC,UAAQ;;AACR;AACF;AACA;AACEC;;AACA;AACF;AACA;AACEC;AACEP;AADU;;AAGZ;AACF;AACA;AACEQ;;AACA;AACF;AACA;AACEC;AACET;AACAC,kBAAQ;AACRC;AAHW;;AAKb;AACF;AACA;AACEQ,EAAAA,WAAW;AACTV,QAAI,iBAKFW;AACFT,WAAO,EAAE;AAPE;AASbU,EAAAA;UACQC;AACNX;AAFQ;AAIVY,EAAAA;AACEd,QAAI;AADM;AAGZe;AACEf,IAAAA,MAAMI;AADG,GAzDuB;AA4DlCY,YAAU,EAAE;AACVhB,IAAAA,MAAMI;AADI;AAGZa;AACEjB;AADG;;AAGL;AACF;AACA;AACEkB;AArEkC;;AAyEpC,MAAMC;;AAGN,aAAaC,SAAS;AACpB;;AACA;AACA,yBAAsCD;AACtCE;AACAC,iBAAW;AACXC,MAAI;AAEJC,iBAAW;AACXC;AATuB;AAczB,MAAMC;AACJC,MAAI;AAEJC,OAAK;AACLC;;AAEAC,QAAMF;AAASG,IAAAA;AAAMC;AAAOC;AAAf;AACX,UAAMC;AAGN;AACEC;AACAC;AACAC;AAHI;AAMN;AACA;AACA,UAAMC,iBAAiB,CACrBV,KAAK,qBAAqBzB;AAG5BoC;AAEIC;AACAxC;AACAyC,MAAAA;AACAC,+BAJF;AAKEC,MAAAA,OAAO;AACPC;AANF;AAWFL;AAEIE,MAAAA;AACAC;AACAF,MAAAA;AACAG,eAAS;AACTC,MAAAA,GAAG;AACH5C,MAAAA,IAAI;AANN,sBAQe,EAAE4B,KAAK,WAAd,CATG;AAYb;AAAQiB;AAAF,2BAA4B;;AAElC;AACE;AACA,UAAIP,WAAW,CAACQ,UAAUA;;AAE1B,UAAI;AACF,sBAAc;;AACd;AACER,8BAAoBQ;;AACpB;AACEf;AACAA,8BAAkBe;AACnB;;AACDC,oBAAA;AACD;AACF;AACF;;AAED,UAAMC,cAAc,GAAG,CACrBC;AAIA;AACAC;AACAnB,MAAAA,eAAI;AACL,KARD;;AAUA,4BAAwB;AACtB,qBAAeoB,QAAX,0BAAmC;AACvCC,QAAE;AACFrB,uBAAiB;AACjBA;AACD;;AAED;AACEA;AACAA,UAAI,UAAJ;AACD;;AAEDsB,qBACaC,YACX/C;AAGF8C,kCAEE9C,UAAU;AAGZ8C,SAAK,CAACf,YAAa;AACjB;AACAS,UAAI,MAAJ;AACD;AAEDQ,YAAQC;AACN5B;AACAU;AACAmB,kBAAY,MAAE,IAA2B;AACvCC,mBAAA,KAAA,CAAiBC;AAClB;AACDC;AACAC;AAP0B;AAU5B5B;AACEK;AADK;;AAIP;AACEwB;AADqE,UAEjE;AACJ,aAAOA;AACR;;AAED;AACE;AACA,qBAAe;AACb,iBAEW,4BAAY;AAFvB;AAAA;AAAA;AAMM;AACD;AAPL;;;;"}