Skip to content

refactor(Tabs): refactor Tabs #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions packages/devui-vue/devui/tabs/src/tab.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
import { defineComponent, inject } from 'vue'
import { Tabs } from './tabs'
import { defineComponent, inject } from 'vue';
import { Tabs } from './tabs';

export default defineComponent({
name: 'DTab',
props: {
title: {
default: null,
type: [String, Number]
type: [String, Number],
},
id: {
default: null,
type: String
type: String,
},
disabled: {
type: Boolean,
default: false
}
default: false,
},
},
setup(props, { slots }) {
const tabs = inject<Tabs>('tabs')
tabs.state.slots.push(slots.dTabTitle)
tabs.state.data.push(props)
const tabs = inject<Tabs>('tabs');
tabs.state.slots.push(slots.title);
tabs.state.data.push(props);
return () => {
const { id } = props
const { id } = props;
const content =
tabs.state.showContent && tabs.state.active === id ? (
<div class='devui-tab-content'>
<div role='tabpanel' class='devui-tab-pane in active'>
{slots.default()}
</div>
</div>
) : null
return content
}
}
})
) : null;
return content;
};
},
});
166 changes: 66 additions & 100 deletions packages/devui-vue/devui/tabs/src/tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,192 +1,158 @@
import {
defineComponent,
onBeforeMount,
onMounted,
onUpdated,
PropType,
provide,
reactive,
ref,
Slot
} from 'vue'
import './tabs.scss'
import { defineComponent, onBeforeMount, onMounted, onUpdated, PropType, provide, reactive, ref, Slot } from 'vue';
import './tabs.scss';

export type Active = string | number | null
export type TabsType = 'tabs' | 'pills' | 'options' | 'wrapped' | 'slider'
export type Active = string | number | null;
export type TabsType = 'tabs' | 'pills' | 'options' | 'wrapped' | 'slider';
export interface Tabs {
state: TabsState
state: TabsState;
}
interface TabsState {
data?: any[]
showContent: boolean
active: string
slots: Slot[]
data?: any[];
showContent: boolean;
active: string;
slots: Slot[];
}
export default defineComponent({
name: 'DTabs',
props: {
modelValue: {
type: [String, Number],
default: null
default: null,
},

type: {
type: String as () => TabsType,
default: 'tabs'
default: 'tabs',
},
showContent: {
type: Boolean,
default: true
default: true,
},
vertical: {
type: Boolean,
default: false
default: false,
},
reactivable: {
type: Boolean,
default: true
default: true,
},
customWidth: {
type: String,
default: ''
default: '',
},
cssClass: {
type: String,
default: ''
default: '',
},
beforeChange: {
type: Function as PropType<(id: Active) => boolean>,
default: null
}
default: null,
},
},

emits: ['update:modelValue', 'activeTabChange'],
emits: ['update:modelValue', 'active-tab-change'],
setup(props, { emit, slots }) {
const tabsEle = ref(null)
const data = reactive({ offsetLeft: 0, offsetWidth: 0, id: null })
const tabsEle = ref(null);
const data = reactive({ offsetLeft: 0, offsetWidth: 0, id: null });
const state: TabsState = reactive({
data: [],
active: props.modelValue,
showContent: props.showContent,
slots: []
})
slots: [],
});
provide<Tabs>('tabs', {
state
})
state,
});

const canChange = function (currentTab: Active) {
let changeResult = Promise.resolve(true)
let changeResult = Promise.resolve(true);
if (typeof props.beforeChange === 'function') {
const result: any = props.beforeChange(currentTab)
const result: any = props.beforeChange(currentTab);
if (typeof result !== 'undefined') {
if (result.then) {
changeResult = result
changeResult = result;
} else {
console.log(result)
changeResult = Promise.resolve(result)
changeResult = Promise.resolve(result);
}
}
}

return changeResult
}
return changeResult;
};
const activeClick = function (item, tabEl?) {
if (!props.reactivable && props.modelValue === item.id) {
return
return;
}
canChange(item.id).then((change) => {
if (!change) {
return
return;
}
const tab = state.data.find((itemOption) => itemOption.id === item.id)
const tab = state.data.find((itemOption) => itemOption.id === item.id);
if (tab && !tab.disabled) {
state.active = item.id
emit('update:modelValue', tab.id)
state.active = item.id;
emit('update:modelValue', tab.id);
if (props.type === 'slider' && tabEl && tabsEle) {
this.offsetLeft =
tabEl.getBoundingClientRect().left -
this.tabsEle.nativeElement.getBoundingClientRect().left
this.offsetWidth = tabEl.getBoundingClientRect().width
this.offsetLeft = tabEl.getBoundingClientRect().left - this.tabsEle.nativeElement.getBoundingClientRect().left;
this.offsetWidth = tabEl.getBoundingClientRect().width;
}
emit('activeTabChange', tab.id)
emit('active-tab-change', tab.id);
}
})
}
const ulClass: string[] = [props.type]
props.cssClass && ulClass.push(props.cssClass)
props.vertical && ulClass.push('devui-nav-stacked')
});
};
const ulClass: string[] = [props.type];
props.cssClass && ulClass.push(props.cssClass);
props.vertical && ulClass.push('devui-nav-stacked');
onUpdated(() => {
if (props.type === 'slider') {
// 延时等待active样式切换至正确的tab
setTimeout(() => {
const tabEle = tabsEle.value.querySelector('#' + props.modelValue + '.active')
const tabEle = tabsEle.value.querySelector('#' + props.modelValue + '.active');
if (tabEle) {
data.offsetLeft =
tabEle.getBoundingClientRect().left - tabsEle.value.getBoundingClientRect().left
data.offsetWidth = tabEle.getBoundingClientRect().width
data.offsetLeft = tabEle.getBoundingClientRect().left - tabsEle.value.getBoundingClientRect().left;
data.offsetWidth = tabEle.getBoundingClientRect().width;
}
})
});
}
})
});
onBeforeMount(() => {
if (props.type !== 'slider' && props.modelValue === undefined && state.data.length > 0) {
activeClick(state.data[0])
activeClick(state.data[0]);
}
})
});
onMounted(() => {
if (
props.type === 'slider' &&
props.modelValue === undefined &&
state.data.length > 0 &&
state.data[0]
) {
activeClick(state.data[0].tabsEle.value.getElementById(state.data[0].tabId))
if (props.type === 'slider' && props.modelValue === undefined && state.data.length > 0 && state.data[0]) {
activeClick(state.data[0].tabsEle.value.getElementById(state.data[0].tabId));
}
})
});
return () => {
return (
<div>
<ul
ref={tabsEle}
role='tablist'
class={`devui-nav devui-nav-${ulClass.join(' ')}`}
id='devuiTabs11'
>
<ul ref={tabsEle} role='tablist' class={`devui-nav devui-nav-${ulClass.join(' ')}`} id='devuiTabs11'>
{state.data.map((item, i) => {
return (
<li
role='presentation'
onClick={() => {
activeClick(item)
activeClick(item);
}}
class={
(props.modelValue === (item.id || item.tabId) ? 'active' : '') +
' ' +
(item.disabled ? 'disabled' : '')
}
id={item.id || item.tabId}
>
<a
role='tab'
data-toggle={item.id}
aria-expanded={props.modelValue === (item.id || item.tabId)}
>
class={(props.modelValue === (item.id || item.tabId) ? 'active' : '') + ' ' + (item.disabled ? 'disabled' : '')}
id={item.id || item.tabId}>
<a role='tab' data-toggle={item.id} aria-expanded={props.modelValue === (item.id || item.tabId)}>
{state.slots[i] ? state.slots[i]() : <span>{item.title}</span>}
</a>
</li>
)
);
})}
<div
class={`devui-nav-${props.type}-animation`}
style={{
left: data.offsetLeft + 'px',
width: data.offsetWidth + 'px'
}}
></div>
width: data.offsetWidth + 'px',
}}></div>
</ul>
{slots.default()}
</div>
)
}
}
})
);
};
},
});
Loading