Skip to content
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

1.3.4 #30

Merged
merged 6 commits into from
Aug 5, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ dist
/cypress/screenshots/
.vscode/*
!.vscode/extensions.json
.idea
*.tgz
2 changes: 1 addition & 1 deletion packages/vue-collapsed/package.json
Original file line number Diff line number Diff line change
@@ -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": [
Expand Down
34 changes: 25 additions & 9 deletions packages/vue-collapsed/src/Collapse.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -139,6 +142,8 @@ onMounted(() => {
watch(isExpanded, (isExpanding) => {
if (!collapseRef.value) return

transitionStartScrollHeight = NaN

if (isExpanding) {
if (isReducedOrDisabled(collapseRef)) return onExpanded()

Expand Down Expand Up @@ -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',
})
})
Expand All @@ -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 */
Expand Down Expand Up @@ -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) {
Expand Down
17 changes: 3 additions & 14 deletions packages/vue-collapsed/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,21 @@ import type { Ref } from 'vue'

type RefEl = Ref<HTMLElement | null>

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 }
}
Expand Down
9 changes: 2 additions & 7 deletions packages/vue-collapsed/tests/Collapse.cy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import App from './App.vue'

import { getRandomIntInclusive, isFirefox } from '../cypress/support/component'

describe('Collapse', () => {
Expand Down Expand Up @@ -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')
})
Expand Down
2 changes: 1 addition & 1 deletion playground/components/AdvancedControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Collapse } from 'vue-collapsed'
// Used for demo purposes only...
const props = withDefaults(
defineProps<{
initialState: boolean
initialState?: boolean
}>(),
{ initialState: true }
)
Expand Down
2 changes: 1 addition & 1 deletion playground/components/DisplayHide.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Collapse } from 'vue-collapsed'

const props = withDefaults(
defineProps<{
initialState: boolean
initialState?: boolean
}>(),
{ initialState: true }
)
Expand Down
2 changes: 1 addition & 1 deletion playground/components/MountUnmount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Collapse } from 'vue-collapsed'

const props = withDefaults(
defineProps<{
initialState: boolean
initialState?: boolean
}>(),
{ initialState: true }
)
Expand Down
Loading