From d349dd8b4b0889b1f137d94ec68c52d44a2bbf84 Mon Sep 17 00:00:00 2001 From: Sven Date: Wed, 6 Sep 2023 13:58:29 +0200 Subject: [PATCH] tabs animation change --- packages/ui/src/composites/wui-tabs/index.ts | 67 +++++++++++++++---- packages/ui/src/composites/wui-tabs/styles.ts | 44 +++--------- 2 files changed, 66 insertions(+), 45 deletions(-) diff --git a/packages/ui/src/composites/wui-tabs/index.ts b/packages/ui/src/composites/wui-tabs/index.ts index 477dd2715f..2ce9a1e952 100644 --- a/packages/ui/src/composites/wui-tabs/index.ts +++ b/packages/ui/src/composites/wui-tabs/index.ts @@ -3,6 +3,7 @@ import { customElement, property, state } from 'lit/decorators.js' import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' import type { IconType } from '../../utils/TypesUtil.js' import styles from './styles.js' +import { animate } from 'motion' @customElement('wui-tabs') export class WuiTabs extends LitElement { @@ -13,47 +14,89 @@ export class WuiTabs extends LitElement { @property() public onTabChange: (index: number) => void = () => null + @property({ type: Array }) public buttons: HTMLButtonElement[] = [] + @state() public activeTab = 0 + @state() public localTabWidth = '100px' + + @state() public isDense = false + // -- Render -------------------------------------------- // public override render() { - const isDense = this.tabs.length > 3 + this.isDense = this.tabs.length > 3 this.style.cssText = ` --local-tab: ${this.activeTab}; - --local-tab-width: 100px; + --local-tab-width: ${this.localTabWidth}; --local-dense-tab-width: max-content; ` - this.dataset['type'] = isDense ? 'flex' : 'block' + this.dataset['type'] = this.isDense ? 'flex' : 'block' return this.tabs.map((tab, index) => { const isActive = index === this.activeTab return html` - ` }) } + override firstUpdated() { + if (this.shadowRoot && this.isDense) { + this.buttons = [...this.shadowRoot.querySelectorAll('button')] + setTimeout(() => { + this.animateTabs(0, true) + }, 1) + } + } + // -- Private ------------------------------------------- // private onTabClick(index: number) { + if (this.buttons) { + this.animateTabs(index, false) + } this.activeTab = index this.onTabChange(index) } - private showLabel(tab: { icon: IconType; label: string }, isDense: boolean, isActive: boolean) { - if (!isDense) { - return html` ${tab.label}` - } - if (isDense && isActive) { - return html` ${tab.label}` + private animateTabs(index: number, initialAnimation: boolean) { + const passiveBtn = this.buttons[this.activeTab] + const activeBtn = this.buttons[index] + + const passiveBtnText = passiveBtn?.querySelector('wui-text') + const activeBtnText = activeBtn?.querySelector('wui-text') + + const activeBtnBounds = activeBtn?.getBoundingClientRect() + const activeBtnTextBounds = activeBtnText?.getBoundingClientRect() + + if (passiveBtn && passiveBtnText && !initialAnimation) { + animate(passiveBtnText, { opacity: 0 }, { duration: 0.25 }) + animate(passiveBtn, { width: `20px` }, { duration: 0.25, delay: 0.05 }) } - return null + if (activeBtn && activeBtnBounds && activeBtnTextBounds && activeBtnText) { + this.localTabWidth = `${Math.round( + activeBtnBounds.width + activeBtnTextBounds.width + 6 + 12 + )}px` + + animate( + activeBtn, + { width: `${activeBtnBounds.width + activeBtnTextBounds.width + 6}px` }, + { duration: 0.5, delay: 0.1 } + ) + + animate(activeBtnText, { opacity: 1 }, { duration: 0.25, delay: 0.15 }) + } } } diff --git a/packages/ui/src/composites/wui-tabs/styles.ts b/packages/ui/src/composites/wui-tabs/styles.ts index ec5a023523..7a243ef803 100644 --- a/packages/ui/src/composites/wui-tabs/styles.ts +++ b/packages/ui/src/composites/wui-tabs/styles.ts @@ -28,17 +28,19 @@ export default css` } :host([data-type='flex'])::before { - transform: translateX(calc(var(--local-tab) * var(--local-tab-width) / 2.02)); + transform: translateX(calc(var(--local-tab) * var(--local-tab-width) / 1.85)); } :host([data-type='flex']) { display: flex; - padding: 0px 4px; + padding: 0px 12px; + gap: 28px; } - :host([data-type='flex']) > button[data-active='true'] > wui-text { - animation-name: fadein; - animation-duration: 0.5s; + :host([data-type='flex']) > button > wui-text { + position: absolute; + left: 18px; + opacity: 0; } button[data-active='true'] > wui-icon, @@ -62,12 +64,10 @@ export default css` } :host([data-type='flex']) > button { - width: var(--local-dense-tab-width); - padding: 0px 16px; - } - - :host([data-type='flex']) > button[data-active='true'] { - min-width: 105px; + width: 20px; + position: relative; + display: flex; + justify-content: flex-start; } button:hover:enabled, @@ -84,26 +84,4 @@ export default css` button:active > wui-text { color: var(--wui-color-fg-125); } - - @keyframes fadein { - from { - opacity: 0; - transform: translateX(10px); - } - to { - opacity: 1; - transform: translateX(0px); - } - } - - @keyframes fadeout { - from { - opacity: 1; - transform: translateX(0px); - } - to { - opacity: 0; - transform: translateX(-10px); - } - } `