Created
January 19, 2023 18:41
-
-
Save lorado/fc6af5a7b249144877514073812252a7 to your computer and use it in GitHub Desktop.
SlideUpDown component for Vue3/Nuxt3, fork of https://github.com/craigrileyuk/vue3-slide-up-down
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script setup lang="ts"> | |
const emit = defineEmits<{ | |
(e: "open-start"): void; | |
(e: "close-start"): void; | |
(e: "open-end"): void; | |
(e: "close-end"): void; | |
}>(); | |
const props = withDefaults( | |
defineProps<{ | |
modelValue: boolean; | |
duration?: number; | |
timingFunction?: string; | |
tag?: string; | |
}>(), | |
{ | |
modelValue: false, | |
duration: 500, | |
timingFunction: "ease-in-out", | |
tag: "div", | |
} | |
); | |
// Template ref for the slide-up-down container | |
const containerRef = ref<HTMLElement>(); | |
// Whether to apply the overflow-y: hidden style to the component | |
const shouldHideOverflow = ref(false); | |
// The measured height of the contents of the container | |
const contentHeight = ref(0); | |
// The currently rendered height of the container | |
const currentHeight = ref<number | null>(null); | |
// Is the slide animation currently playing | |
const isTransitioning = ref(false); | |
/** | |
* Wait until the synchronous loop is cleared before executing action | |
* | |
* @param {Function} fn - The callback function to execute. | |
*/ | |
const delayAction = (fn: any) => { | |
setTimeout(() => { | |
window.requestAnimationFrame(fn); | |
}, 0); | |
}; | |
/** | |
* Update the contentHeight value to the current height of the contents inside the container | |
*/ | |
const updateContainerHeight = () => { | |
if (containerRef.value) { | |
contentHeight.value = containerRef.value.scrollHeight; | |
} | |
}; | |
/** | |
* Perform the animations between open, close and shift states | |
*/ | |
const updateDisplay = () => { | |
updateContainerHeight(); | |
if (isTransitioning.value) { | |
if (!props.modelValue) { | |
currentHeight.value = 0; | |
} | |
return transitionEnd({ target: containerRef.value }); | |
} | |
currentHeight.value = contentHeight.value; | |
if (!props.modelValue) { | |
shouldHideOverflow.value = true; | |
emit("close-start"); | |
delayAction(() => { | |
currentHeight.value = 0; | |
}); | |
} else { | |
emit("open-start"); | |
} | |
isTransitioning.value = true; | |
}; | |
/** | |
* @constant { computed<object> } generatedBaseStyles - Computed style object for the container | |
*/ | |
const generatedBaseStyles = computed(() => ({ | |
transition: `height ${props.duration}ms ${props.timingFunction}`, | |
height: currentHeight.value !== null ? `${currentHeight.value}px` : null, | |
overflowY: shouldHideOverflow.value ? "hidden" : null, | |
// "--content-height": contentHeight.value, | |
})); | |
/** | |
* @constant { computed<object> } generatedBaseAttributes - Computed attributes object for the container | |
*/ | |
const generatedBaseAttributes = computed(() => ({ | |
"aria-hidden": !props.modelValue, | |
tabindex: !props.modelValue ? "-1" : null, | |
})); | |
/** | |
* Called when the CSS transition animation is complete. | |
* | |
* @param {Event} event - ontransitionend JS event. | |
*/ | |
const transitionEnd = (event: any) => { | |
if (event.target !== containerRef.value) { | |
return; | |
} | |
if (props.modelValue) { | |
currentHeight.value = null; | |
shouldHideOverflow.value = false; | |
emit("open-end"); | |
} else { | |
emit("close-end"); | |
} | |
isTransitioning.value = false; | |
}; | |
// init state | |
if (!props.modelValue) { | |
currentHeight.value = 0; | |
shouldHideOverflow.value = true; | |
} | |
onMounted(() => { | |
updateContainerHeight(); | |
}); | |
watch( | |
() => props.modelValue, | |
(v) => { | |
updateDisplay(); | |
} | |
); | |
const attrs = useAttrs(); | |
const slots = useSlots(); | |
const SlideUpDownRender = () => | |
h( | |
props.tag, | |
{ | |
...Object.assign({}, attrs, { style: generatedBaseStyles.value }), | |
class: "slide-up-down__container", | |
onTransitionend: transitionEnd, | |
...generatedBaseAttributes.value, | |
ref: containerRef, | |
}, | |
slots.default!() | |
); | |
</script> | |
<template> | |
<SlideUpDownRender /> | |
</template> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment