Skip to content

Commit 8d86ded

Browse files
committed
fix: quasar scrolling layout
1 parent 914393e commit 8d86ded

2 files changed

Lines changed: 221 additions & 66 deletions

File tree

examples/vue-quasar/src/components/Application.vue

Lines changed: 154 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script setup lang="ts">
2+
import { ref } from 'vue';
23
import { usePdfiumEngine } from '@embedpdf/engines/vue';
34
import { EmbedPDF } from '@embedpdf/core/vue';
45
import { createPluginRegistration, PluginRegistry } from '@embedpdf/core';
@@ -32,34 +33,35 @@ import {
3233
import type { AnnotationTool } from '@embedpdf/plugin-annotation/vue';
3334
import { PdfAnnotationSubtype } from '@embedpdf/models';
3435
import type { PdfStampAnnoObject } from '@embedpdf/models';
35-
3636
import Toolbar from './Toolbar.vue';
37-
import DrawerProvider from './drawer-system/DrawerProvider.vue';
38-
import Drawer from './drawer-system/Drawer.vue';
39-
import Search from './Search.vue';
40-
import Sidebar from './Sidebar.vue';
4137
import PageControls from './PageControls.vue';
4238
import RedactionSelectionMenu from './RedactionSelectionMenu.vue';
4339
import AnnotationSelectionMenu from './AnnotationSelectionMenu.vue';
4440
45-
// Define drawer components
46-
const drawerComponents = [
47-
{
48-
id: 'search',
49-
component: Search,
50-
icon: 'mdi-magnify',
51-
label: 'Search',
52-
position: 'right' as const,
53-
},
54-
{
55-
id: 'sidebar',
56-
component: Sidebar,
57-
icon: 'mdi-dock-left',
58-
label: 'Sidebar',
59-
position: 'left' as const,
60-
},
61-
];
41+
import Search from './Search.vue';
42+
import Sidebar from './Sidebar.vue';
43+
44+
const leftDrawerOpen = ref(false);
45+
const rightDrawerOpen = ref(false);
6246
47+
const toggleLeftDrawer = () => {
48+
leftDrawerOpen.value = !leftDrawerOpen.value;
49+
if (leftDrawerOpen.value && rightDrawerOpen.value) {
50+
rightDrawerOpen.value = false;
51+
}
52+
};
53+
54+
const toggleRightDrawer = () => {
55+
rightDrawerOpen.value = !rightDrawerOpen.value;
56+
if (leftDrawerOpen.value && rightDrawerOpen.value) {
57+
leftDrawerOpen.value = false;
58+
}
59+
};
60+
61+
const closeDrawers = () => {
62+
leftDrawerOpen.value = false;
63+
rightDrawerOpen.value = false;
64+
};
6365
const { engine, isLoading: engineLoading, error: engineError } = usePdfiumEngine();
6466
6567
const handleInitialized = async (registry: PluginRegistry) => {
@@ -99,11 +101,12 @@ const handleInitialized = async (registry: PluginRegistry) => {
99101
</div>
100102

101103
<!-- Main application -->
102-
<div v-else-if="engine" class="fit">
103-
<EmbedPDF
104-
:engine="engine"
105-
:on-initialized="handleInitialized"
106-
:plugins="[
104+
<div v-else-if="engine" class="application__root">
105+
<div class="application__embed">
106+
<EmbedPDF
107+
:engine="engine"
108+
:on-initialized="handleInitialized"
109+
:plugins="[
107110
createPluginRegistration(LoaderPluginPackage, {
108111
loadingOptions: {
109112
type: 'url',
@@ -149,17 +152,27 @@ const handleInitialized = async (registry: PluginRegistry) => {
149152
createPluginRegistration(RedactionPluginPackage),
150153
createPluginRegistration(AnnotationPluginPackage),
151154
]"
152-
>
153-
<template #default="{ pluginsReady }">
154-
<DrawerProvider :components="drawerComponents">
155-
<q-layout view="hHh Lpr fFf" class="application__layout">
156-
<Toolbar />
155+
>
156+
<template #default="{ pluginsReady }">
157+
<div class="application__shell">
158+
<Toolbar
159+
:left-drawer-open="leftDrawerOpen"
160+
:right-drawer-open="rightDrawerOpen"
161+
@toggle-left-drawer="toggleLeftDrawer"
162+
@toggle-right-drawer="toggleRightDrawer"
163+
/>
157164

158-
<Drawer position="left" />
165+
<div class="application__body">
166+
<aside
167+
class="application__drawer application__drawer--left"
168+
:class="{ 'is-open': leftDrawerOpen }"
169+
aria-hidden="true"
170+
>
171+
<Sidebar />
172+
</aside>
159173

160-
<q-page-container class="application__page-container">
161-
<q-page class="application__page q-pa-none">
162-
<GlobalPointerProvider>
174+
<div class="application__main">
175+
<GlobalPointerProvider class="application__pointer-provider">
163176
<Viewport class="application__viewport">
164177
<div
165178
v-if="!pluginsReady"
@@ -219,7 +232,9 @@ const handleInitialized = async (registry: PluginRegistry) => {
219232
:scale="page.scale"
220233
:rotation="page.rotation"
221234
>
222-
<template #selection-menu="{ item, selected, menuWrapperProps, rect }">
235+
<template
236+
#selection-menu="{ item, selected, menuWrapperProps, rect }"
237+
>
223238
<RedactionSelectionMenu
224239
v-if="selected"
225240
:item="item"
@@ -237,14 +252,28 @@ const handleInitialized = async (registry: PluginRegistry) => {
237252
<PageControls />
238253
</Viewport>
239254
</GlobalPointerProvider>
240-
</q-page>
241-
</q-page-container>
242-
243-
<Drawer position="right" />
244-
</q-layout>
245-
</DrawerProvider>
246-
</template>
247-
</EmbedPDF>
255+
</div>
256+
257+
<aside
258+
class="application__drawer application__drawer--right"
259+
:class="{ 'is-open': rightDrawerOpen }"
260+
aria-hidden="true"
261+
>
262+
<Search />
263+
</aside>
264+
265+
<button
266+
v-if="leftDrawerOpen || rightDrawerOpen"
267+
type="button"
268+
class="application__drawer-overlay"
269+
aria-label="Close drawers"
270+
@click="closeDrawers"
271+
/>
272+
</div>
273+
</div>
274+
</template>
275+
</EmbedPDF>
276+
</div>
248277
</div>
249278

250279
<!-- Engine not ready state -->
@@ -257,20 +286,95 @@ const handleInitialized = async (registry: PluginRegistry) => {
257286
<style scoped>
258287
.application {
259288
user-select: none;
289+
height: 100vh;
290+
display: flex;
291+
flex-direction: column;
292+
min-height: 0;
260293
}
261294
262-
.application__layout {
263-
background-color: #f5f5f5;
295+
.application__root {
296+
flex: 1 1 auto;
297+
min-height: 0;
298+
display: flex;
299+
height: 100%;
264300
}
265301
266-
.application__page-container,
267-
.application__page {
302+
.application__embed {
303+
flex: 1 1 auto;
304+
min-height: 0;
305+
display: flex;
268306
height: 100%;
269307
}
270308
271-
.application__viewport {
309+
.application__shell {
310+
flex: 1 1 auto;
311+
min-height: 0;
312+
display: flex;
313+
flex-direction: column;
272314
height: 100%;
273315
background-color: #f5f5f5;
316+
}
317+
318+
.application__body {
319+
flex: 1 1 auto;
320+
min-height: 0;
321+
display: flex;
322+
position: relative;
323+
overflow: hidden;
324+
}
325+
326+
.application__drawer {
327+
width: 300px;
328+
flex: 0 0 auto;
329+
background-color: #ffffff;
330+
border-right: 1px solid var(--q-color-grey-4);
331+
overflow: auto;
332+
transform: translateX(-100%);
333+
transition: transform 0.2s ease;
334+
z-index: 2;
335+
}
336+
337+
.application__drawer--right {
338+
border-right: none;
339+
border-left: 1px solid var(--q-color-grey-4);
340+
transform: translateX(100%);
341+
}
342+
343+
.application__drawer.is-open {
344+
transform: translateX(0);
345+
}
346+
347+
.application__drawer-overlay {
348+
position: absolute;
349+
inset: 0;
350+
background: rgba(0, 0, 0, 0.25);
351+
border: none;
352+
padding: 0;
353+
margin: 0;
354+
cursor: pointer;
355+
z-index: 1;
356+
}
357+
358+
.application__drawer-overlay:focus {
359+
outline: 2px solid var(--q-color-primary);
360+
}
361+
362+
.application__main {
363+
flex: 1 1 auto;
364+
min-height: 0;
365+
display: flex;
366+
}
367+
368+
.application__pointer-provider {
369+
flex: 1 1 auto;
370+
min-height: 0;
371+
display: flex;
372+
}
373+
374+
.application__viewport {
375+
flex: 1 1 auto;
376+
min-height: 0;
377+
background-color: #f5f5f5;
274378
overflow: auto;
275379
position: relative;
276380
}

examples/vue-quasar/src/components/Toolbar.vue

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,19 @@ import { useSpread, SpreadMode } from '@embedpdf/plugin-spread/vue';
99
import { useInteractionManager } from '@embedpdf/plugin-interaction-manager/vue';
1010
import PrintDialog from './PrintDialog.vue';
1111
import ZoomControls from './ZoomControls.vue';
12-
import DrawerToggleButton from './drawer-system/DrawerToggleButton.vue';
1312
import AnnotationToolbar from './AnnotationToolbar.vue';
1413
import RedactToolbar from './RedactToolbar.vue';
1514
15+
const props = defineProps<{
16+
leftDrawerOpen: boolean;
17+
rightDrawerOpen: boolean;
18+
}>();
19+
20+
const emit = defineEmits<{
21+
(event: 'toggle-left-drawer'): void;
22+
(event: 'toggle-right-drawer'): void;
23+
}>();
24+
1625
const { provides: fullscreenProvider, state: fullscreenState } = useFullscreen();
1726
const { provides: panProvider, isPanning } = usePan();
1827
const { provides: rotateProvider } = useRotateCapability();
@@ -77,8 +86,8 @@ const handlePrintDialogClose = () => {
7786
</script>
7887

7988
<template>
80-
<q-header elevated class="toolbar bg-white text-dark">
81-
<q-toolbar class="q-px-md q-gutter-sm">
89+
<div class="toolbar bg-white text-dark">
90+
<div class="toolbar__inner row items-center no-wrap q-px-md q-gutter-sm">
8291
<!-- Main Menu -->
8392
<q-btn
8493
flat
@@ -124,7 +133,16 @@ const handlePrintDialogClose = () => {
124133
<q-separator vertical spaced />
125134

126135
<!-- Sidebar Toggle -->
127-
<DrawerToggleButton component-id="sidebar" />
136+
<q-btn
137+
flat
138+
round
139+
dense
140+
icon="mdi-dock-left"
141+
:color="props.leftDrawerOpen ? 'primary' : undefined"
142+
@click="emit('toggle-left-drawer')"
143+
>
144+
<q-tooltip>Sidebar</q-tooltip>
145+
</q-btn>
128146

129147
<!-- Page Settings Menu -->
130148
<q-btn
@@ -254,27 +272,60 @@ const handlePrintDialogClose = () => {
254272
<q-space />
255273

256274
<!-- Search Toggle -->
257-
<DrawerToggleButton component-id="search" />
258-
</q-toolbar>
259-
<AnnotationToolbar v-if="mode === 'annotate'" />
260-
<RedactToolbar v-if="mode === 'redact'" />
261-
</q-header>
262-
263-
<!-- Print Dialog -->
264-
<PrintDialog :open="printDialogOpen" @close="handlePrintDialogClose" />
275+
<q-btn
276+
flat
277+
round
278+
dense
279+
icon="mdi-magnify"
280+
:color="props.rightDrawerOpen ? 'primary' : undefined"
281+
@click="emit('toggle-right-drawer')"
282+
>
283+
<q-tooltip>Search</q-tooltip>
284+
</q-btn>
285+
</div>
286+
<div v-if="mode === 'annotate'" class="toolbar__secondary">
287+
<div class="toolbar__secondary-inner">
288+
<AnnotationToolbar />
289+
</div>
290+
</div>
291+
<div v-else-if="mode === 'redact'" class="toolbar__secondary">
292+
<div class="toolbar__secondary-inner">
293+
<RedactToolbar />
294+
</div>
295+
</div>
296+
<PrintDialog :open="printDialogOpen" @close="handlePrintDialogClose" />
297+
</div>
265298
</template>
266299

267300
<style scoped>
268301
.toolbar {
269-
border-bottom: 1px solid #cfd4da;
302+
display: flex;
303+
flex-direction: column;
304+
border-bottom: 1px solid var(--q-color-grey-4);
305+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
270306
}
271307
272-
.mode-tabs-container {
308+
.toolbar__inner {
273309
display: flex;
274310
align-items: center;
311+
gap: 8px;
312+
min-height: 56px;
313+
flex-wrap: nowrap;
275314
}
276315
277-
.mode-tabs :deep(.q-tab__label) {
278-
font-weight: 500;
316+
.toolbar__secondary {
317+
border-top: 1px solid var(--q-color-grey-4);
318+
padding: 8px 16px;
319+
}
320+
321+
.toolbar__secondary-inner {
322+
display: flex;
323+
align-items: center;
324+
gap: 8px;
325+
}
326+
327+
.mode-tabs-container {
328+
display: flex;
329+
align-items: center;
279330
}
280331
</style>

0 commit comments

Comments
 (0)