From 51502dc24cdacab8539862c7c3bf93c6ea8f5408 Mon Sep 17 00:00:00 2001 From: Simone Mastromattei Date: Mon, 5 Aug 2024 17:57:13 +0200 Subject: [PATCH] 1.3.4 (#30) * trigger a new transition if children height changed while expanding * suppress some demo prop warning * Fix #31 - Update default transition value since Chromium 128 * Tests - Update to match Chrome v128 default transition computation * Pkg - Bump v1.3.4 * Core - Improve comment --- .gitignore | 1 + packages/vue-collapsed/package.json | 2 +- packages/vue-collapsed/src/Collapse.vue | 34 +++++++++++++++------ packages/vue-collapsed/src/utils.ts | 17 ++--------- packages/vue-collapsed/tests/Collapse.cy.ts | 9 ++---- playground/components/AdvancedControl.vue | 2 +- playground/components/DisplayHide.vue | 2 +- playground/components/MountUnmount.vue | 2 +- 8 files changed, 35 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index 8da35a1..e67fa15 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ dist /cypress/screenshots/ .vscode/* !.vscode/extensions.json +.idea *.tgz diff --git a/packages/vue-collapsed/package.json b/packages/vue-collapsed/package.json index 1735648..cb9b76f 100644 --- a/packages/vue-collapsed/package.json +++ b/packages/vue-collapsed/package.json @@ -1,6 +1,6 @@ { "name": "vue-collapsed", - "version": "1.3.3", + "version": "1.3.4", "private": false, "description": "Dynamic CSS height transition from any to auto and vice versa for Vue 3. Accordion ready.", "keywords": [ diff --git a/packages/vue-collapsed/src/Collapse.vue b/packages/vue-collapsed/src/Collapse.vue index 18e4f4a..611ccd1 100644 --- a/packages/vue-collapsed/src/Collapse.vue +++ b/packages/vue-collapsed/src/Collapse.vue @@ -11,13 +11,7 @@ import { } from 'vue' import { SAFE_STYLES, VISUALLY_HIDDEN, AUTO_DUR_VAR, FALLBACK_DURATION } from './constants' -import { - getTransitionProp, - getComputedHeight, - getHeightProp, - getAutoDuration, - isReducedOrDisabled, -} from './utils' +import { getTransitionProp, getComputedHeight, getAutoDuration, isReducedOrDisabled } from './utils' export type TransitionState = 'expanding' | 'expanded' | 'collapsing' | 'collapsed' @@ -104,6 +98,15 @@ const setAutoDuration = (newDuration: number) => (autoDuration.value = newDurati const autoDurationVar = computed(() => ({ [AUTO_DUR_VAR]: `${autoDuration.value}ms` })) +/** + * In some edge cases, Collapse may have children elements that also expand + * their height while expanding. + * + * When this occurs, the 'scrollHeight' obtained on transition start will be lower than + * the same 'scrollHeight' obtained on transition end. + */ +let transitionStartScrollHeight = NaN + function onExpanded() { replaceStyles(SAFE_STYLES) setState('expanded') @@ -139,6 +142,8 @@ onMounted(() => { watch(isExpanded, (isExpanding) => { if (!collapseRef.value) return + transitionStartScrollHeight = NaN + if (isExpanding) { if (isReducedOrDisabled(collapseRef)) return onExpanded() @@ -168,9 +173,11 @@ watch(isExpanded, (isExpanding) => { /** Set height to scrollHeight and trigger the transition. */ + transitionStartScrollHeight = collapseRef.value!.scrollHeight + addStyles({ - ...getHeightProp(collapseRef), ...getTransitionProp(collapseRef), + height: `${transitionStartScrollHeight}px`, willChange: 'height', }) }) @@ -189,7 +196,7 @@ watch(isExpanded, (isExpanding) => { addStyles({ ...autoDurationVar.value, - ...getHeightProp(collapseRef), + height: `${collapseRef.value!.scrollHeight}px`, }) /** Same as for expand, abort transition and force collapse */ @@ -230,6 +237,15 @@ function onTransitionEnd(e: TransitionEvent) { if (isExpanded.value) { if (Math.abs(collapseRef.value.scrollHeight - getComputedHeight(collapseRef)) < 1) { onExpanded() + } else if (transitionStartScrollHeight < collapseRef.value.scrollHeight) { + /** + * A child element expanded its height while Collapse + * is transitioning, update the height and trigger + * the transition again. + */ + addStyles({ + height: `${collapseRef.value.scrollHeight}px`, + }) } } else { if (Math.abs(baseHeight.value - getComputedHeight(collapseRef)) < 1) { diff --git a/packages/vue-collapsed/src/utils.ts b/packages/vue-collapsed/src/utils.ts index b80b6f4..1f63664 100644 --- a/packages/vue-collapsed/src/utils.ts +++ b/packages/vue-collapsed/src/utils.ts @@ -4,32 +4,21 @@ import type { Ref } from 'vue' type RefEl = Ref -const isFirefox = () => typeof navigator !== 'undefined' && navigator.userAgent.includes('Firefox') - export function getComputedHeight(el: RefEl) { if (!el.value) return 0 return parseFloat(getComputedStyle(el.value).height) } -export function getHeightProp(el: RefEl) { - return { - height: `${el.value?.scrollHeight || 0}px`, - } -} - export function getTransitionProp(el: RefEl) { if (!el.value) return {} const { transition } = getComputedStyle(el.value) // If transition is not defined via CSS, return the default one referencing the auto duration - if ( - transition === 'all 0s ease 0s' || - (isFirefox() && - transition === - 'all') /* Since Firefox v124, Gecko returns transition 'all' instead of 'all 0s ease 0s' */ - ) + if (transition === 'all 0s ease 0s' || transition === 'all') { + /* Since Firefox v124 and Chromium v128, their rendering engines compute 'all' instead of 'all 0s ease 0s' as default transition */ return { transition: DEFAULT_TRANSITION } + } return { transition } } diff --git a/packages/vue-collapsed/tests/Collapse.cy.ts b/packages/vue-collapsed/tests/Collapse.cy.ts index b83003d..4624359 100644 --- a/packages/vue-collapsed/tests/Collapse.cy.ts +++ b/packages/vue-collapsed/tests/Collapse.cy.ts @@ -1,4 +1,5 @@ import App from './App.vue' + import { getRandomIntInclusive, isFirefox } from '../cypress/support/component' describe('Collapse', () => { @@ -275,13 +276,7 @@ describe('Collapse', () => { const transition = 'height 0.3s cubic-bezier(0.33, 1, 0.68, 1)' - cy.get('#Collapse').should( - 'have.css', - 'transition', - isFirefox - ? transition // Firefox >= 124 doesn't include '0s' by default anymore - : `${transition} 0s` - ) + cy.get('#Collapse').should('have.css', 'transition', transition) cy.get('#Collapse').and('have.attr', 'style').and('include', '--vc-auto-duration: 300ms') }) diff --git a/playground/components/AdvancedControl.vue b/playground/components/AdvancedControl.vue index fc11951..1157add 100644 --- a/playground/components/AdvancedControl.vue +++ b/playground/components/AdvancedControl.vue @@ -4,7 +4,7 @@ import { Collapse } from 'vue-collapsed' // Used for demo purposes only... const props = withDefaults( defineProps<{ - initialState: boolean + initialState?: boolean }>(), { initialState: true } ) diff --git a/playground/components/DisplayHide.vue b/playground/components/DisplayHide.vue index 866a653..8cfd0bd 100644 --- a/playground/components/DisplayHide.vue +++ b/playground/components/DisplayHide.vue @@ -3,7 +3,7 @@ import { Collapse } from 'vue-collapsed' const props = withDefaults( defineProps<{ - initialState: boolean + initialState?: boolean }>(), { initialState: true } ) diff --git a/playground/components/MountUnmount.vue b/playground/components/MountUnmount.vue index 6af5e72..8df9da3 100644 --- a/playground/components/MountUnmount.vue +++ b/playground/components/MountUnmount.vue @@ -3,7 +3,7 @@ import { Collapse } from 'vue-collapsed' const props = withDefaults( defineProps<{ - initialState: boolean + initialState?: boolean }>(), { initialState: true } )