Skip to content

Commit

Permalink
refactor(maz-ui): MazTabsBar - improve animations
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisMazel committed Mar 13, 2024
1 parent 2d62220 commit dc1b06e
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 47 deletions.
32 changes: 16 additions & 16 deletions packages/docs/docs/components/maz-tabs.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ description: MazTabs is a standalone component to display content in tabs with a
const tabs: MazTabsBarItem[] = [
{ label: 'First Tab', disabled: false },
{ label: 'Second Tab', disabled: false, badge: { color: 'danger', value: 1, roundedSize: 'full' } },
{ label: 'Second Tab', disabled: false, badge: { color: 'danger', content: 1, roundedSize: 'full' } },
{ label: 'Third Tab', disabled: true },
]
</script>
Expand Down Expand Up @@ -144,20 +144,6 @@ You can choose the name of this query parameter with the props `query-param` `@d
</MazTabsContent>
</MazTabs>

<script lang="ts" setup>
import { ref } from 'vue'

const currentTab = ref(2)

const tabs: MazTabsBarItem[] = [
{ label: 'First Tab', disabled: false },
{ label: 'Second Tab', disabled: false, badge: { color: 'danger', value: '1', roundedSize: 'full' } },
{ label: 'Third Tab', disabled: true },
]

const tabs2: MazTabsBarItem[] = ['First Tab', 'Second Tab', 'Third Tab']
</script>

## Custom tabs with slot

<MazTabs>
Expand Down Expand Up @@ -193,6 +179,20 @@ You can choose the name of this query parameter with the props `query-param` `@d
</MazTabs>
```

<script lang="ts" setup>
import { ref } from 'vue'

const currentTab = ref(2)

const tabs: MazTabsBarItem[] = [
{ label: 'First Tab', disabled: false },
{ label: 'Second Tab', disabled: false, badge: { color: 'danger', content: 1, roundedSize: 'full' } },
{ label: 'Third Tab', disabled: true },
]

const tabs2: MazTabsBarItem[] = ['First Tab', 'Second Tab', 'Third Tab', 'Fourth Tab', 'Fifth Tab', 'Sixth Tab', 'Seventh Tab', 'Eighth Tab', 'Ninth Tab', 'Tenth Tab']
</script>

## Types

```ts
Expand All @@ -201,7 +201,7 @@ type MazTabsBarItem =
label: string
disabled?: boolean
badge?: {
value: string | number
content: string | number
color?: BadgeColor
pastel?: boolean
outline?: boolean
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/components/MazBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

<style lang="postcss" scoped>
.m-badge {
@apply maz-inline-flex maz-items-center maz-justify-center maz-border maz-font-medium;
@apply maz-inline-flex maz-items-center maz-justify-center maz-border maz-font-semibold;
padding: 0.25em 0.5em;
line-height: 1.4em;
Expand Down
76 changes: 51 additions & 25 deletions packages/lib/components/MazTabsBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
'--elevation': !noElevation,
}"
>
<div class="m-tabs-bar__indicator" :style="[tabsIndicatorState]"></div>
<div
class="m-tabs-bar__indicator"
:class="{ '--animated': tabsBarHasScrollAnimation }"
:style="[tabsIndicatorState]"
></div>
<template v-for="(item, index) in normalizedItems" :key="index">
<button
:ref="(mazBtn) => addElementToItemRefs({ mazBtn, index })"
Expand All @@ -32,10 +36,16 @@
:pastel="item.badge.pastel"
:outline="item.badge.outline"
:rounded-size="item.badge.roundedSize"
:size="item.badge.size"
nowrap
:size="item.badge.size ?? '0.7rem'"
class="m-tabs-bar__item__badge"
>
{{ item.badge.value }}
<!--
@slot badge-content - Content in the badge
@binding {string | number | boolean} content - content of the badge provided in item
-->
<slot name="badge-content" :content="item.badge.content">
{{ item.badge.content }}
</slot>
</MazBadge>
</slot>
</button>
Expand All @@ -47,19 +57,18 @@
import {
ref,
computed,
watchEffect,
onBeforeMount,
nextTick,
onMounted,
defineAsyncComponent,
type StyleValue,
type ComponentPublicInstance,
watch,
} from 'vue'
import type { MazTabsProvide } from './MazTabs.vue'
import { injectStrict } from './../modules/helpers/inject-strict'
import { sleep } from './../modules/helpers/sleep'
import { type BadgeRoundedSize, type BadgeColor } from './MazBadge.vue'
import { sleep } from '../modules'
const MazBadge = defineAsyncComponent(() => import('./MazBadge.vue'))
Expand All @@ -68,7 +77,7 @@
label: string
disabled?: boolean
badge?: {
value: string | number
content: string | number | boolean
color?: BadgeColor
pastel?: boolean
outline?: boolean
Expand Down Expand Up @@ -138,53 +147,53 @@
)
const tabsIndicatorState = ref<StyleValue>()
const tabsBarHasScrollAnimation = ref(false)
watchEffect(async () => {
async function setIndicatorAndScroll() {
if (!props.autoScroll) {
return
}
const tabsBar = tabsBarRef.value
await sleep(150)
const tabsBar = tabsBarRef.value
const activeTab = itemRefs.value[currentTab.value - 1]
if (!tabsBar || !activeTab) {
return
}
await nextTick()
await sleep(150)
const scrollOffset = 50
if (
activeTab.offsetLeft < tabsBar.scrollLeft ||
activeTab.offsetLeft - scrollOffset < tabsBar.scrollLeft ||
activeTab.offsetLeft + activeTab.offsetWidth > tabsBar.scrollLeft + tabsBar.clientWidth
) {
const tabBarPaddingLeft = window.getComputedStyle(tabsBar, 'padding-left').paddingLeft
const tabsBarPaddingOffset = Number(tabBarPaddingLeft.slice(0, -2))
tabsBar.scrollTo({
left: activeTab.offsetLeft - tabsBarPaddingOffset,
behavior: 'smooth', // Ajoutez le défilement fluide
left: activeTab.offsetLeft - tabsBarPaddingOffset - scrollOffset,
behavior: tabsBarHasScrollAnimation.value ? 'smooth' : 'instant',
})
await sleep(150)
}
if (typeof currentTab.value !== 'number') {
return {}
return
}
const tabItemActive: HTMLButtonElement | undefined = itemRefs.value[currentTab.value - 1]
const indicatorWidth = tabItemActive?.offsetWidth ?? 0
const indicatorHeight = tabItemActive?.offsetHeight ?? 0
const translateXValue = tabItemActive?.offsetLeft ?? 0
const indicatorWidth = activeTab?.offsetWidth ?? 0
const indicatorHeight = activeTab?.offsetHeight ?? 0
const translateXValue = activeTab?.offsetLeft ?? 0
tabsIndicatorState.value = {
transform: `translateX(${translateXValue}px)`,
width: `${indicatorWidth}px`,
height: `${indicatorHeight}px`,
}
})
tabsBarHasScrollAnimation.value = true
}
function getTabStyle(index: number, disabled: boolean): StyleValue {
if (disabled) {
Expand All @@ -203,6 +212,19 @@
}
})
onMounted(() => {
if (props.persistent) {
setIndicatorAndScroll()
}
})
watch(
() => [currentTab.value, normalizedItems.value],
() => {
setIndicatorAndScroll()
},
)
function getQueryParamTab() {
const urlActuelle = new URL(window.location.href)
return Number(urlActuelle.searchParams.get(props.queryParam))
Expand Down Expand Up @@ -248,7 +270,11 @@
}
&__indicator {
@apply maz-absolute maz-left-0 maz-rounded maz-bg-color-light maz-text-center maz-transition-all maz-duration-500 maz-ease-in-out;
@apply maz-absolute maz-left-0 maz-rounded maz-bg-color-light maz-text-center;
&.--animated {
@apply maz-transition-all maz-duration-300 maz-ease-in-out;
}
}
}
</style>
20 changes: 15 additions & 5 deletions packages/playground/src/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
<div>
<!-- Start Developping Area - You should not commit anything here to keep this place clean for all others -->

<MazPhoneNumberInput :id="id" v-model="phone" label="Phone number" />

<!-- End Developping Area -->
</div>

Expand All @@ -16,18 +14,30 @@

<script lang="ts" setup>
import { sleep } from 'maz-ui'
import type { MazTabsBarItem } from 'maz-ui/components/MazTabsBar.vue'
const toast = useToast()
const wait = useWait()
const id = useId()
const phone = ref<string>()
const tabs = ref<MazTabsBarItem[]>([
{ label: 'First Tab', disabled: false },
{ label: 'Third Tab', disabled: false },
{ label: 'Fourth Tab', disabled: false },
{ label: 'Fifth Tab', disabled: false },
{ label: 'Sixth Tab', disabled: false },
{ label: 'Seven Tab', disabled: false },
{ label: 'Height Tab', disabled: false },
{ label: 'Nine Tab', disabled: false },
])
onMounted(async () => {
wait.start('APP_LOADING')
await sleep(500)
wait.stop('APP_LOADING')
setTimeout(() => {
tabs.value.splice(1, 0, { label: 'Second Tab', disabled: false, badge: { content: 5 } })
}, 1000)
})
toast.message('Votre mot de passe a été mis à jour', {
Expand Down

0 comments on commit dc1b06e

Please sign in to comment.