diff --git a/README.md b/README.md index 1035b6f..20f5076 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -![npm](https://img.shields.io/npm/v/vue-collapsed?color=46c119) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/smastrom/vue-collapsed/tests.yml?branch=main&label=tests) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/vue-collapsed?color=success) -![dependency-count](https://img.shields.io/badge/dependency%20count-0-success) +![npm](https://img.shields.io/npm/v/vue-collapsed?color=46c119) ![dependencies](https://img.shields.io/badge/dependencies-0-success) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/vue-collapsed?color=success) ![downloads](https://img.shields.io/npm/dm/vue-collapsed) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/smastrom/vue-collapsed/tests.yml?branch=main&label=tests) # Vue Collapsed @@ -126,6 +125,18 @@ To transition other properties use the attribute `data-collapse`: } ``` +Or to use different easings/durations for expand and collapse: + +```css +.v-collapse[data-collapse='expanding'] { + transition: height 600ms ease-in-out; +} + +.v-collapse[data-collapse='collapsing'] { + transition: height 300ms ease-out; +} +``` + Above values can also be accessed using `v-slot`: ```vue @@ -188,33 +199,6 @@ function handleAccordion(selectedIndex) { ``` -## Example - Callbacks - -```vue - - - - - - {{ question.title }} - - scrollIntoView(index)"> - - {{ question.answer }} - - - - -``` - ## Accessibility `vue-collapsed` automatically detects if users prefer reduced motion and will disable transitions accordingly while keeping the same API behavior (emitting events and post-transition styles). @@ -255,7 +239,7 @@ function handleCollapse() { ``` -## Manually disable transitions +## Manually disabling transitions ```vue diff --git a/demo/NestedCollapse.vue b/demo/NestedCollapse.vue index b6afdbc..aba0622 100644 --- a/demo/NestedCollapse.vue +++ b/demo/NestedCollapse.vue @@ -7,7 +7,6 @@ const nested = reactive({ first: true, // Initial value second: false, third: false, - fourth: false, }) const innerText = ` diff --git a/package.json b/package.json index ab43712..4c412a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-collapsed", - "version": "1.2.5", + "version": "1.2.6", "private": false, "description": "Dynamic CSS height transition from any to auto and vice versa for Vue 3. Accordion ready.", "keywords": [ @@ -28,6 +28,13 @@ "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, "files": [ "dist/*" ], @@ -51,7 +58,7 @@ "@rollup/plugin-terser": "^0.4.3", "@types/node": "^18.17.1", "@vitejs/plugin-vue": "^4.2.3", - "cypress": "^12.17.2", + "cypress": "^12.17.3", "cypress-wait-frames": "^0.9.4", "husky": "^8.0.3", "lint-staged": "^13.2.3", @@ -59,7 +66,7 @@ "prettier": "^2.8.8", "rimraf": "^4.4.1", "typescript": "^4.9.5", - "vite": "^4.4.7", + "vite": "^4.4.8", "vite-plugin-dts": "^1.7.3", "vue": "^3.3.4", "vue-tsc": "^1.8.8" diff --git a/src/Collapse.vue b/src/Collapse.vue index 7cf4379..0fb0252 100644 --- a/src/Collapse.vue +++ b/src/Collapse.vue @@ -18,10 +18,10 @@ import { type CSSProperties as CSS, } from 'vue' -import { SAFE_STYLES as safeStyles, AUTO_DUR_VAR } from './constants' +import { SAFE_STYLES as safeStyles, VISUALLY_HIDDEN, AUTO_DUR_VAR } from './constants' import { getTransition, getHeight, getAutoDuration, isReducedOrDisaled } from './utils' -type TransitionState = 'expanding' | 'expanded' | 'collapsing' | 'collapsed' +export type TransitionState = 'expanding' | 'expanded' | 'collapsing' | 'collapsed' const props = withDefaults( defineProps<{ @@ -61,19 +61,11 @@ const collapsedStyles = computed(() => ({ const collapseRef = ref(null) const state = ref(isExpanded.value ? 'expanded' : 'collapsed') -const style = shallowRef(isExpanded.value ? safeStyles : collapsedStyles.value) +const style = shallowRef({}) -const autoDuration = ref(0) +const autoDuration = ref(300) const autoDurationVar = computed(() => ({ [AUTO_DUR_VAR]: `${autoDuration.value}ms` })) -function setAutoDuration() { - if (!collapseRef.value) return - - if (isExpanded.value || baseHeight.value > 0) { - autoDuration.value = getAutoDuration(collapseRef.value.scrollHeight - baseHeight.value) - } -} - function onExpanded() { style.value = safeStyles state.value = 'expanded' @@ -88,7 +80,13 @@ function onCollapsed() { // Lifecycle / Watchers -onMounted(setAutoDuration) +onMounted(() => { + if (!collapseRef.value) return + if (!isExpanded.value && baseHeight.value === 0) style.value = VISUALLY_HIDDEN + + autoDuration.value = getAutoDuration(collapseRef.value.scrollHeight - baseHeight.value) + style.value = isExpanded.value ? safeStyles : collapsedStyles.value +}) watch(isExpanded, (isExpanding) => { if (isExpanding) { @@ -104,28 +102,23 @@ watch(isExpanded, (isExpanding) => { * We set the height to baseHeight as it is the 'current' height * we are transitioning from. */ + state.value = 'expanding' + emit('expand') + style.value = { ...safeStyles, ...baseHeightStyles.value, + ...autoDurationVar.value, willChange: 'height', } requestAnimationFrame(() => { - /** Get auto duration in once scrollHeight is available. */ - setAutoDuration() - style.value = { ...style.value, ...autoDurationVar.value } - - requestAnimationFrame(() => { - /** Set height to scrollHeight and trigger the transition. */ - style.value = { - ...style.value, - ...getHeight(collapseRef.value), - ...getTransition(collapseRef.value), - } - - state.value = 'expanding' - emit('expand') - }) + /** Set height to scrollHeight and trigger the transition. */ + style.value = { + ...style.value, + ...getHeight(collapseRef.value), + ...getTransition(collapseRef.value), + } }) } else { if (isReducedOrDisaled(collapseRef.value)) return onCollapsed() @@ -136,31 +129,24 @@ watch(isExpanded, (isExpanding) => { * * Since the element is visible we get the 'current' * expanded height (scrollHeight) and set it as height. - * - * We could use only one raf call, but we must use the - * same number of raf calls of the previous condition (2) to make - * sure accordion transitions (collapse/expand at the same time) - * are perfectly in sync. */ + state.value = 'collapsing' + emit('collapse') + + style.value = { + ...style.value, + ...autoDurationVar.value, + ...getHeight(collapseRef.value), + willChange: 'height', + } + requestAnimationFrame(() => { + /** Set height to baseHeight and trigger the transition. */ style.value = { ...style.value, - ...autoDurationVar.value, - ...getHeight(collapseRef.value), - willChange: 'height', + ...baseHeightStyles.value, + ...getTransition(collapseRef.value), } - - requestAnimationFrame(() => { - /** Set height to baseHeight and trigger the transition. */ - style.value = { - ...style.value, - ...baseHeightStyles.value, - ...getTransition(collapseRef.value), - } - - state.value = 'collapsing' - emit('collapse') - }) }) } }) @@ -175,13 +161,14 @@ watch(baseHeight, (newBaseHeight) => { * * Below styles are going to be replaced on next expand. */ - transitionDuration: '0s', - height: `${newBaseHeight}px`, + ...(newBaseHeight === 0 + ? { display: 'none' } + : { transition: 'none', height: `${newBaseHeight}px` }), } } }) -// Callbacks +// Event handlers function onTransitionEnd(event: TransitionEvent) { if (event.target === collapseRef.value && event.propertyName === 'height') { diff --git a/src/constants.ts b/src/constants.ts index 516a673..d7ad107 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,4 +2,16 @@ export const AUTO_DUR_VAR = '--vc-auto-duration' export const DEFAULT_TRANSITION = `height var(${AUTO_DUR_VAR}) cubic-bezier(0.33, 1, 0.68, 1)` -export const SAFE_STYLES = { padding: 0, border: 0, margin: 0 } as const +export const SAFE_STYLES = { padding: 0 } as const + +export const VISUALLY_HIDDEN = { + position: 'absolute', + width: '1px', + height: '1px', + padding: '0', + margin: '-1px', + overflow: 'hidden', + clip: 'rect(0, 0, 0, 0)', + whiteSpace: 'nowrap', + border: '0', +} as const
- {{ question.answer }} -