feat: add animated CSS loader during preview reload
Displays a spinning loader in the top-right corner of the iframe while PagedJS is rendering. The loader automatically adjusts position when the editor panel is open. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b584a539fe
commit
9e02813d19
1 changed files with 44 additions and 5 deletions
49
src/App.vue
49
src/App.vue
|
|
@ -16,7 +16,7 @@ provide('activeTab', activeTab);
|
||||||
|
|
||||||
let savedScrollPercentage = 0;
|
let savedScrollPercentage = 0;
|
||||||
const currentFrameIndex = ref(1); // 1 or 2, which iframe is currently visible
|
const currentFrameIndex = ref(1); // 1 or 2, which iframe is currently visible
|
||||||
let isTransitioning = false;
|
const isTransitioning = ref(false);
|
||||||
|
|
||||||
const activeFrame = computed(() => {
|
const activeFrame = computed(() => {
|
||||||
return currentFrameIndex.value === 1
|
return currentFrameIndex.value === 1
|
||||||
|
|
@ -25,8 +25,8 @@ const activeFrame = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderPreview = async (shouldReloadFromFile = false) => {
|
const renderPreview = async (shouldReloadFromFile = false) => {
|
||||||
if (isTransitioning) return;
|
if (isTransitioning.value) return;
|
||||||
isTransitioning = true;
|
isTransitioning.value = true;
|
||||||
|
|
||||||
// Determine which iframe is currently visible and which to render to
|
// Determine which iframe is currently visible and which to render to
|
||||||
const visibleFrame =
|
const visibleFrame =
|
||||||
|
|
@ -35,7 +35,7 @@ const renderPreview = async (shouldReloadFromFile = false) => {
|
||||||
currentFrameIndex.value === 1 ? previewFrame2.value : previewFrame1.value;
|
currentFrameIndex.value === 1 ? previewFrame2.value : previewFrame1.value;
|
||||||
|
|
||||||
if (!hiddenFrame) {
|
if (!hiddenFrame) {
|
||||||
isTransitioning = false;
|
isTransitioning.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +112,7 @@ const renderPreview = async (shouldReloadFromFile = false) => {
|
||||||
|
|
||||||
// Swap current frame
|
// Swap current frame
|
||||||
currentFrameIndex.value = currentFrameIndex.value === 1 ? 2 : 1;
|
currentFrameIndex.value = currentFrameIndex.value === 1 ? 2 : 1;
|
||||||
isTransitioning = false;
|
isTransitioning.value = false;
|
||||||
}, 200); // Match CSS transition duration
|
}, 200); // Match CSS transition duration
|
||||||
}, 50); // Small delay to ensure scroll is set
|
}, 50); // Small delay to ensure scroll is set
|
||||||
}, 200); // Wait for PagedJS
|
}, 200); // Wait for PagedJS
|
||||||
|
|
@ -147,6 +147,10 @@ onMounted(() => renderPreview(true));
|
||||||
:class="{ shifted: activeTab.length > 0 }"
|
:class="{ shifted: activeTab.length > 0 }"
|
||||||
></iframe>
|
></iframe>
|
||||||
|
|
||||||
|
<div v-if="isTransitioning" class="preview-loader" :class="{ shifted: activeTab.length > 0 }">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ElementPopup ref="elementPopup" :iframeRef="activeFrame" />
|
<ElementPopup ref="elementPopup" :iframeRef="activeFrame" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -179,4 +183,39 @@ onMounted(() => renderPreview(true));
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-loader {
|
||||||
|
position: fixed;
|
||||||
|
top: 2rem;
|
||||||
|
right: 2rem;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: all 0.2s ease-in-out var(--curve);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-loader.shifted {
|
||||||
|
right: calc(2rem + 19rem * 0.7);
|
||||||
|
top: calc(2rem - 30vh * 0.7);
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
border-top: 3px solid #fff;
|
||||||
|
border-right: 3px solid transparent;
|
||||||
|
box-sizing: border-box;
|
||||||
|
animation: rotation 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotation {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue