Skip to content
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
31 changes: 10 additions & 21 deletions packages/renderless/src/tabs-mf/vue-nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,32 @@ export const renderless = (props, { reactive, inject, computed, onMounted, onBef
navItems: computed(() => tabs.state.items),
currentNav: computed(() => tabs.state.navs[state.currentIndex]),
currentIndex: computed(() =>
tabs.state.navs.findIndex((item) => tabs.state.currentItem && tabs.state.currentItem.name === item.name)
tabs.state.navs.findIndex((item) => tabs.state.cacheCurrentItem && tabs.state.cacheCurrentItem.name === item.name)
),
currentWidth: 0,
currentPosition: 0
})

let rafId, observer
let observer

onMounted(() => {
observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes') {
if (rafId) {
cancelAnimationFrame(rafId)
}

rafId = requestAnimationFrame(() => {
const nav = state.currentNav

state.currentWidth = (nav && nav.$el.offsetWidth) || 0
state.currentPosition = (nav && nav.$el.offsetLeft) || 0
})
}
}
const nav = state.currentNav
state.currentWidth = (nav && nav.$el.offsetWidth) || 0
state.currentPosition = (nav && nav.$el.offsetLeft) || 0
})

observer.observe(vm.$el, { attributes: true, subtree: true })
})

onBeforeUnmount(() => {
if (rafId) {
cancelAnimationFrame(rafId)
}

observer.disconnect()
observer = null
})

return { state }
Object.assign(api, {
state
})

return api
}
25 changes: 25 additions & 0 deletions packages/renderless/src/tabs-mf/vue-slider-bar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const api = ['state']

export const renderless = (props, { reactive, inject, computed, onMounted, onUnmounted, onUpdated }, { vm, emit }) => {
const tabs = inject('tabs', null)

const state = reactive({
currentNav: computed(() => props.currentNav || null),
currentWidth: computed(() => props.currentWidth + 'px'),
currentPosition: computed(() => props.currentPosition + 'px')
})

const handleTransitionEnd = () => {
tabs.state.currentItem = tabs.state.cacheCurrentItem
}

onMounted(() => {
vm.$el.addEventListener('transitionend', handleTransitionEnd)
})

onUnmounted(() => {
vm.$el.removeEventListener('transitionend', handleTransitionEnd)
})

return { state }
}
4 changes: 3 additions & 1 deletion packages/renderless/src/tabs-mf/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export const renderless = (props, hooks, { vm, emit, nextTick }) => {
const state = reactive({
items: [],
navs: [],
currentItem: computed(() => state.items.find((item) => item.selected)),
currentItem: null,
cacheCurrentItem: computed(() => state.items.find((item) => item.selected)),
key: computed(() => (state.currentItem ? state.currentItem.name : random())),
separator: props.separator,
swipeable: computed(() => api.computedSwipeable()),
Expand Down Expand Up @@ -93,6 +94,7 @@ export const renderless = (props, hooks, { vm, emit, nextTick }) => {
onMounted(() => {
// 在 vue2 类似 tabSwipe0 这些动态的 ref 只能在 nextTick 中拿到
nextTick(() => api.observeTabSwipeSize())
state.currentItem = state.cacheCurrentItem
props.activeName && api.scrollTo(props.activeName)
props.modelValue && api.scrollTo(props.modelValue)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/vue/src/tabs/src/mobile-first.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default defineComponent({
vnodes = []

state.items.forEach((item) => {
if (item !== state.currentItem) {
if (!state.currentItem || item.name !== state.currentItem.name) {
return
}

Expand Down
6 changes: 4 additions & 2 deletions packages/vue/src/tabs/src/mobile-first/tab-bar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { renderless, api } from '@opentiny/vue-renderless/tabs-mf/vue-bar'
import { props, setup, defineComponent, h } from '@opentiny/vue-common'
import { $props, setup, defineComponent, h } from '@opentiny/vue-common'
import { IconPopup, IconPlus, IconChevronLeft } from '@opentiny/vue-icon'
import Dropdown from '@opentiny/vue-dropdown'
import DropdownMenu from '@opentiny/vue-dropdown-menu'
Expand All @@ -9,7 +9,9 @@ import TabNav from './tab-nav.vue'
import type { NavItem } from './type'

export default defineComponent({
props: [...props],
props: {
...$props
},
setup(props: any, context: any) {
return setup({ props, context, renderless, api, mono: true })
},
Expand Down
15 changes: 7 additions & 8 deletions packages/vue/src/tabs/src/mobile-first/tab-nav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { renderless, api } from '@opentiny/vue-renderless/tabs-mf/vue-nav'
import { $props, setup, defineComponent, h } from '@opentiny/vue-common'
import TabNavItem from './tab-nav-item.vue'
import tabSliderBar from './tab-slider-bar.vue'
import type { NavItem } from './type'

export default defineComponent({
Expand All @@ -27,14 +28,12 @@ export default defineComponent({
})
: null
),
h('div', {
class: m(
'absolute bg-color-brand transition-all duration-300 z-20 rounded-full bottom-px h-px py-px box-content',
{
'hidden': !state.currentNav
}
),
style: { width: state.currentWidth + 'px', left: state.currentPosition + 'px' }
h(tabSliderBar, {
props: {
currentWidth: state.currentWidth,
currentPosition: state.currentPosition,
currentNav: state.currentNav
}
})
])
}
Expand Down
38 changes: 38 additions & 0 deletions packages/vue/src/tabs/src/mobile-first/tab-slider-bar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts">
import { renderless, api } from '@opentiny/vue-renderless/tabs-mf/vue-slider-bar'
import { $props, setup, defineComponent, h } from '@opentiny/vue-common'
export default defineComponent({
props: {
...$props,
currentWidth: {
type: Number,
default: 0
},
currentPosition: {
type: Number,
default: 0
},
currentNav: {
type: Object,
default: () => {}
}
},
setup(props: any, context: any) {
return setup({ props, context, renderless, api, mono: true })
},
render() {
const { state, m } = this
return h('div', {
class: m(
'absolute bg-color-brand transition-all duration-100 z-20 rounded-full bottom-px h-px py-px box-content',
{
'hidden': !state.currentNav
}
),
style: { width: state.currentWidth, left: state.currentPosition }
})
}
})
</script>
Loading