Skip to content

Commit 88d62ad

Browse files
authored
refactor(toolbar): sliding toolbar (#33)
1 parent 6386afd commit 88d62ad

10 files changed

Lines changed: 391 additions & 135 deletions

File tree

frontend/components.d.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ declare module "vue" {
3131
"./src/components/settings/DeviceSettingsTab.vue",
3232
)["default"];
3333
EmojiPicker: typeof import("./src/components/EmojiPicker.vue")["default"];
34-
FloatingControls: typeof import(
35-
"./src/components/FloatingControls.vue",
36-
)["default"];
3734
FloatingReactions: typeof import(
3835
"./src/components/FloatingReactions.vue",
3936
)["default"];
@@ -44,6 +41,9 @@ declare module "vue" {
4441
KickParticipantDialog: typeof import(
4542
"./src/components/KickParticipantDialog.vue",
4643
)["default"];
44+
LayoutSettingsTab: typeof import(
45+
"./src/components/settings/LayoutSettingsTab.vue",
46+
)["default"];
4747
LobbyOverlay: typeof import("./src/components/LobbyOverlay.vue")["default"];
4848
LucideAlertCircle: typeof import("~icons/lucide/alert-circle")["default"];
4949
LucideAlertTriangle: typeof import(
@@ -101,6 +101,9 @@ declare module "vue" {
101101
MeetingPreview: typeof import(
102102
"./src/components/MeetingPreview.vue",
103103
)["default"];
104+
MeetingToolbar: typeof import(
105+
"./src/components/MeetingToolbar.vue",
106+
)["default"];
104107
NamePill: typeof import("./src/components/NamePill.vue")["default"];
105108
NotificationSettingsTab: typeof import(
106109
"./src/components/settings/NotificationSettingsTab.vue",
@@ -118,6 +121,9 @@ declare module "vue" {
118121
PeopleWaitingSection: typeof import(
119122
"./src/components/PeopleWaitingSection.vue",
120123
)["default"];
124+
PreviewToolbar: typeof import(
125+
"./src/components/PreviewToolbar.vue",
126+
)["default"];
121127
ReactionPicker: typeof import(
122128
"./src/components/ReactionPicker.vue",
123129
)["default"];

frontend/src/components/MeetingPreview.vue

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,9 @@
4848
</div>
4949
</div>
5050

51-
<FloatingControls
52-
:isPreview="true"
53-
:isChatOpen="false"
51+
<PreviewToolbar
5452
:isMicOn="isMicOn"
5553
:isCameraOn="isCameraOn"
56-
:isScreenSharing="false"
5754
:cameraPermissionGranted="cameraPermissionGranted"
5855
:microphonePermissionGranted="microphonePermissionGranted"
5956
@toggle-microphone="$emit('toggle-microphone')"
@@ -135,8 +132,8 @@
135132
<script setup>
136133
import { Button, FormControl, createResource, toast } from "frappe-ui";
137134
import { computed, inject, nextTick, onMounted, ref, watch } from "vue";
138-
import FloatingControls from "../components/FloatingControls.vue";
139135
import ParticipantAvatarGroup from "../components/ParticipantAvatarGroup.vue";
136+
import PreviewToolbar from "../components/PreviewToolbar.vue";
140137
import { useMeetingPreviewPresence } from "../composables/useMeetingPreviewPresence";
141138
import { session } from "../data/session";
142139
import FrappeMeetingLogo from "../icons/FrappeMeetingLogo.vue";

frontend/src/components/FloatingControls.vue renamed to frontend/src/components/MeetingToolbar.vue

Lines changed: 36 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,14 @@
11
<template>
2-
<Transition
3-
enter-active-class="transition-all duration-300 ease-out"
4-
enter-from-class="opacity-0 transform translate-y-4"
5-
enter-to-class="opacity-100 transform translate-y-0"
6-
:leave-active-class="
7-
instantHide
8-
? 'duration-0'
9-
: 'transition-all duration-300 ease-in'
10-
"
11-
leave-from-class="opacity-100 transform translate-y-0"
12-
:leave-to-class="
13-
instantHide ? 'opacity-0' : 'opacity-0 transform translate-y-4'
14-
"
2+
<div
3+
class="w-full overflow-hidden shrink-0 transition-[height,margin] duration-300 ease-in-out"
4+
:style="{ height: toolbarHeight }"
155
>
166
<div
17-
v-show="isVisible"
18-
:class="[
19-
'z-5 pointer-events-none w-auto max-w-3xl px-4 md:px-0 bottom-4 left-1/2 transform -translate-x-1/2',
20-
isPreview ? 'absolute' : 'fixed',
21-
]"
7+
class="flex justify-center px-4 transition-transform duration-300 ease-in-out"
8+
:class="isVisible ? 'translate-y-0' : 'translate-y-full'"
229
>
2310
<div
24-
class="flex items-center gap-3 px-6 py-3 bg-black/80 backdrop-blur-md rounded-full border border-white/10 shadow-xl pointer-events-auto transition-all duration-300 mx-auto"
11+
class="flex items-center gap-3 px-6 py-3 bg-black/80 backdrop-blur-md rounded-full border border-white/10 shadow-xl pointer-events-auto transition-all duration-300"
2512
@mouseenter="onMouseEnter"
2613
@mouseleave="onMouseLeave"
2714
>
@@ -62,7 +49,7 @@
6249

6350
<!-- Screen Share -->
6451
<Button
65-
v-if="!isPreview && canScreenShare()"
52+
v-if="canScreenShare()"
6653
@click="$emit('toggle-screen-share')"
6754
variant="solid"
6855
:theme="isScreenSharing ? 'orange' : 'gray'"
@@ -89,7 +76,6 @@
8976
>
9077
<template #trigger>
9178
<Button
92-
v-if="!isPreview"
9379
variant="solid"
9480
theme="gray"
9581
size="2xl"
@@ -106,11 +92,8 @@
10692
</template>
10793
</ReactionPicker>
10894

109-
<!-- Raise Hand -->
110-
111-
11295
<!-- Chat -->
113-
<div v-if="!isPreview && !isMobile" class="relative">
96+
<div v-if="!isMobile" class="relative">
11497
<Button
11598
@click="$emit('toggle-chat')"
11699
variant="solid"
@@ -139,7 +122,7 @@
139122
</div>
140123

141124
<!-- People -->
142-
<div class="relative" v-if="!isPreview && !isMobile">
125+
<div class="relative" v-if="!isMobile">
143126
<Button
144127
@click="$emit('toggle-people')"
145128
variant="solid"
@@ -162,24 +145,8 @@
162145
/>
163146
</div>
164147

165-
<!-- Settings -->
166-
<Button
167-
v-if="isPreview && (cameraPermissionGranted || microphonePermissionGranted)"
168-
@click="showSettingsDialog = true"
169-
variant="solid"
170-
theme="gray"
171-
size="2xl"
172-
class="!rounded-full p-0 !bg-opacity-90 hover:!bg-opacity-100 transition-all duration-200 hover:scale-105 active:scale-95"
173-
title="Settings"
174-
>
175-
<template #icon>
176-
<lucide-settings class="w-5 h-5 text-white" />
177-
</template>
178-
</Button>
179-
180148
<!-- More Options -->
181149
<div
182-
v-if="!isPreview"
183150
class="relative"
184151
ref="dropdownContainer"
185152
@click="handleDropdownClick"
@@ -203,7 +170,6 @@
203170

204171
<!-- End Call -->
205172
<Button
206-
v-if="!isPreview"
207173
@click="$emit('end-call')"
208174
variant="solid"
209175
theme="red"
@@ -217,28 +183,28 @@
217183
</Button>
218184
</div>
219185
</div>
220-
</Transition>
186+
</div>
221187

222188
<MeetingInfoDialog
223189
v-model="showMeetingInfoDialog"
224190
:meetingId="meetingId"
225191
:meetingTitle="meetingTitle"
226192
/>
227193

228-
229194
<SettingsDialog
230195
v-model="showSettingsDialog"
231196
:meetingId="meetingId"
232-
:isPreview="isPreview"
197+
:isPreview="false"
233198
@device-changed="$emit('device-changed', $event)"
234199
/>
235200
</template>
236201

237202
<script setup>
238203
import { Button, Dropdown } from "frappe-ui";
239-
import { computed, onMounted, onUnmounted, ref, toRefs } from "vue";
204+
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
240205
import { useMeetingDoc } from "../composables/useMeetingDoc";
241206
import { useResponsiveGrid } from "../composables/useResponsiveGrid";
207+
import { autoHideToolbar } from "../data/mediaPreferences";
242208
import { canScreenShare } from "../utils/device";
243209
import MeetingInfoDialog from "./MeetingInfoDialog.vue";
244210
import ReactionPicker from "./ReactionPicker.vue";
@@ -281,10 +247,6 @@ const props = defineProps({
281247
type: Boolean,
282248
default: false,
283249
},
284-
isPreview: {
285-
type: Boolean,
286-
default: false,
287-
},
288250
meetingId: {
289251
type: String,
290252
default: "",
@@ -313,8 +275,6 @@ if (props.meetingId) {
313275
getMeetingDoc(props.meetingId);
314276
}
315277
316-
const { isPreview } = toRefs(props);
317-
318278
const emit = defineEmits([
319279
"toggle-chat",
320280
"toggle-people",
@@ -335,10 +295,6 @@ const moreOptions = computed(() => [
335295
{
336296
icon: "settings",
337297
label: "Settings",
338-
condition: () =>
339-
props.cameraPermissionGranted ||
340-
props.microphonePermissionGranted ||
341-
!isPreview.value,
342298
onClick: () => {
343299
showSettingsDialog.value = true;
344300
resetHideTimer();
@@ -359,38 +315,34 @@ const moreOptions = computed(() => [
359315
label: "People",
360316
onClick: () => {
361317
emit("toggle-people");
362-
instantHide.value = true;
363318
isVisible.value = false;
364-
setTimeout(() => {
365-
instantHide.value = false;
366-
}, 300);
367319
},
368320
},
369321
{
370322
icon: "message-square",
371323
label: "Chat",
372324
onClick: () => {
373325
emit("toggle-chat");
374-
instantHide.value = true;
375326
isVisible.value = false;
376-
setTimeout(() => {
377-
instantHide.value = false;
378-
}, 300);
379327
},
380328
},
381329
]
382330
: []),
383331
]);
384332
385333
const isVisible = ref(true);
386-
const instantHide = ref(false);
387334
const isHovering = ref(false);
388335
const isDropdownOpen = ref(false);
389336
const dropdownContainer = ref(null);
390337
const showMeetingInfoDialog = ref(false);
391338
const showSettingsDialog = ref(false);
392339
let hideTimeout = null;
393340
341+
const TOOLBAR_VISIBLE_HEIGHT = "5.5rem";
342+
const toolbarHeight = computed(() =>
343+
isVisible.value ? TOOLBAR_VISIBLE_HEIGHT : "0px",
344+
);
345+
394346
const showControls = () => {
395347
isVisible.value = true;
396348
resetHideTimer();
@@ -399,7 +351,13 @@ const showControls = () => {
399351
const resetHideTimer = (force = false) => {
400352
if (hideTimeout) {
401353
clearTimeout(hideTimeout);
354+
hideTimeout = null;
355+
}
356+
357+
if (!autoHideToolbar.value) {
358+
return;
402359
}
360+
403361
if (
404362
!force &&
405363
(isDropdownOpen.value || isHovering.value || props.isReactionPickerOpen)
@@ -440,7 +398,6 @@ const handleShortcut = (event) => {
440398
};
441399
442400
const handleDropdownClick = (event) => {
443-
// for not hiding controls when clicked on dropdown
444401
isDropdownOpen.value = !isDropdownOpen.value;
445402
446403
if (isDropdownOpen.value) {
@@ -478,6 +435,18 @@ const updateReactionPickerOpen = (value) => {
478435
emit("update:isReactionPickerOpen", value);
479436
};
480437
438+
watch(autoHideToolbar, (shouldAutoHide) => {
439+
if (!shouldAutoHide) {
440+
if (hideTimeout) {
441+
clearTimeout(hideTimeout);
442+
hideTimeout = null;
443+
}
444+
isVisible.value = true;
445+
} else {
446+
resetHideTimer();
447+
}
448+
});
449+
481450
onMounted(() => {
482451
resetHideTimer();
483452

0 commit comments

Comments
 (0)