[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: stabilise modal chart render after transition (#1034)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

authored by

Alec Lloyd Probert
coderabbitai[bot]
and committed by
GitHub
32d1d0a6 348b9670

+66 -7
+19
app/components/Modal.client.vue
··· 5 5 6 6 const dialogRef = useTemplateRef('dialogRef') 7 7 8 + const emit = defineEmits<{ 9 + (e: 'transitioned'): void 10 + }>() 11 + 8 12 const modalTitleId = computed(() => { 9 13 const id = getCurrentInstance()?.attrs.id 10 14 return id ? `${id}-title` : undefined ··· 14 18 dialogRef.value?.close() 15 19 } 16 20 21 + /** 22 + * Emits `transitioned` once the dialog has finished its open opacity transition. 23 + * This is used by consumers that need to run layout-sensitive logic (for example 24 + * dispatching a resize) only after the modal is fully displayed. 25 + */ 26 + function onDialogTransitionEnd(event: TransitionEvent) { 27 + const el = dialogRef.value 28 + if (!el) return 29 + if (!el.open) return 30 + if (event.target !== el) return 31 + if (event.propertyName !== 'opacity') return 32 + emit('transitioned') 33 + } 34 + 17 35 defineExpose({ 18 36 showModal: () => dialogRef.value?.showModal(), 19 37 close: () => dialogRef.value?.close(), ··· 28 46 class="w-full bg-bg border border-border rounded-lg shadow-xl max-h-[90vh] overflow-y-auto overscroll-contain m-0 m-auto p-6 text-fg focus-visible:outline focus-visible:outline-accent/70" 29 47 :aria-labelledby="modalTitleId" 30 48 v-bind="$attrs" 49 + @transitionend="onDialogTransitionEnd" 31 50 > 32 51 <!-- Modal top header section --> 33 52 <div class="flex items-center justify-between mb-6">
+47 -7
app/components/Package/WeeklyDownloadStats.vue
··· 9 9 }>() 10 10 11 11 const chartModal = useModal('chart-modal') 12 - 12 + const hasChartModalTransitioned = shallowRef(false) 13 13 const isChartModalOpen = shallowRef(false) 14 + 14 15 async function openChartModal() { 15 16 isChartModalOpen.value = true 17 + hasChartModalTransitioned.value = false 16 18 // ensure the component renders before opening the dialog 17 19 await nextTick() 18 20 await nextTick() 19 21 chartModal.open() 22 + } 23 + 24 + function handleModalClose() { 25 + isChartModalOpen.value = false 26 + hasChartModalTransitioned.value = false 27 + } 28 + 29 + function handleModalTransitioned() { 30 + hasChartModalTransitioned.value = true 20 31 } 21 32 22 33 const { fetchPackageDownloadEvolution } = useCharts() ··· 249 260 </CollapsibleSection> 250 261 </div> 251 262 252 - <PackageChartModal v-if="isChartModalOpen" @close="isChartModalOpen = false"> 253 - <PackageDownloadAnalytics 254 - :weeklyDownloads="weeklyDownloads" 255 - :inModal="true" 256 - :packageName="props.packageName" 257 - :createdIso="createdIso" 263 + <PackageChartModal @close="handleModalClose" @transitioned="handleModalTransitioned"> 264 + <!-- The Chart is mounted after the dialog has transitioned --> 265 + <!-- This avoids flaky behavior that hides the chart's minimap half of the time --> 266 + <Transition name="opacity" mode="out-in"> 267 + <PackageDownloadAnalytics 268 + v-if="hasChartModalTransitioned" 269 + :weeklyDownloads="weeklyDownloads" 270 + :inModal="true" 271 + :packageName="props.packageName" 272 + :createdIso="createdIso" 273 + /> 274 + </Transition> 275 + 276 + <!-- This placeholder bears the same dimensions as the PackageDownloadAnalytics component --> 277 + <!-- Avoids CLS when the dialog has transitioned --> 278 + <div 279 + v-if="!hasChartModalTransitioned" 280 + class="w-full aspect-[390/634.5] sm:aspect-[718/622.797]" 258 281 /> 259 282 </PackageChartModal> 260 283 </template> 284 + 285 + <style scoped> 286 + .opacity-enter-active, 287 + .opacity-leave-active { 288 + transition: opacity 200ms ease; 289 + } 290 + 291 + .opacity-enter-from, 292 + .opacity-leave-to { 293 + opacity: 0; 294 + } 295 + 296 + .opacity-enter-to, 297 + .opacity-leave-from { 298 + opacity: 1; 299 + } 300 + </style> 261 301 262 302 <style> 263 303 /** Overrides */