Skip to content

Commit

Permalink
Components: Tutorial: Make tutorial modal draggable
Browse files Browse the repository at this point in the history
Signed-off-by: Arturo Manzoli <[email protected]>
  • Loading branch information
ArturoManzoli authored and rafaellehmkuhl committed Sep 30, 2024
1 parent c397779 commit 894fd5e
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 5 deletions.
97 changes: 95 additions & 2 deletions src/components/GlassModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
v-if="isVisible"
ref="modal"
class="glass-modal"
:class="[interfaceStore.isOnSmallScreen ? 'rounded-[10px]' : 'rounded-[20px]', selectedOverflow]"
:class="[
interfaceStore.isOnSmallScreen ? 'rounded-[10px]' : 'rounded-[20px]',
selectedOverflow,
{ 'cursor-move': isDraggable },
]"
:style="[modalPositionStyle, interfaceStore.globalGlassMenuStyles]"
@click="bringModalUp"
@mousedown="startDragging"
>
<slot></slot>
</div>
Expand Down Expand Up @@ -33,15 +38,26 @@ const props = defineProps<{
*/
position?: ModalPosition
/**
*
* If true, modal will not close by pressing 'esc' or by an outside click.
*/
isPersistent?: boolean
/**
* The overflow property of the modal.
*/
overflow?: 'auto' | 'hidden' | 'scroll' | 'visible' | 'inherit' | 'initial' | 'unset'
/**
* If true, the modal can be dragged around.
*/
draggable?: boolean
/**
* The storage key to save the modal position in localStorage.
*/
storageKey?: string
}>()
// eslint-disable-next-line
type ModalDraggedPosition = { left: number; top: number }
const emit = defineEmits(['outside-click'])
const isVisible = ref(props.isVisible)
Expand All @@ -51,6 +67,51 @@ const isPersistent = ref(props.isPersistent || false)
const modal = ref<HTMLElement | null>(null)
const zIndexToggle = ref(200)
const isAlwaysOnTop = ref(false)
const isDraggable = ref(props.draggable || false)
const isDragging = ref(false)
const dragStartX = ref(0)
const dragStartY = ref(0)
const modalStartX = ref(0)
const modalStartY = ref(0)
const customPosition = ref<ModalDraggedPosition | null>(null)
const startDragging = (e: MouseEvent): void => {
if (!isDraggable.value) return
isDragging.value = true
dragStartX.value = e.clientX
dragStartY.value = e.clientY
const modalElement = modal.value
if (modalElement) {
const rect = modalElement.getBoundingClientRect()
modalStartX.value = rect.left
modalStartY.value = rect.top
}
document.addEventListener('mousemove', onMouseMove)
document.addEventListener('mouseup', onMouseUp)
}
const onMouseMove = (e: MouseEvent): void => {
if (!isDragging.value) return
const deltaX = e.clientX - dragStartX.value
const deltaY = e.clientY - dragStartY.value
const newLeft = modalStartX.value + deltaX
const newTop = modalStartY.value + deltaY
customPosition.value = { left: newLeft, top: newTop }
if (props.storageKey) {
localStorage.setItem(props.storageKey, JSON.stringify({ left: newLeft, top: newTop }))
}
}
const onMouseUp = (): void => {
isDragging.value = false
document.removeEventListener('mousemove', onMouseMove)
document.removeEventListener('mouseup', onMouseUp)
}
const bringModalUp = (): void => {
zIndexToggle.value = 2000
Expand All @@ -70,12 +131,22 @@ watch(
)
const modalPositionStyle = computed(() => {
if (customPosition.value) {
return {
top: `${customPosition.value.top}px`,
left: `${customPosition.value.left}px`,
transform: 'none',
zIndex: zIndexToggle.value,
}
}
switch (modalPosition.value) {
case 'left':
return {
top: '50%',
left: '0%',
transform: 'translateY(-50%)',
zIndex: zIndexToggle.value,
}
case 'menuitem':
return {
Expand All @@ -84,6 +155,7 @@ const modalPositionStyle = computed(() => {
? `${interfaceStore.mainMenuWidth - 20}px`
: `${interfaceStore.mainMenuWidth + 30}px`,
transform: 'translateY(-50%)',
zIndex: zIndexToggle.value,
}
case 'center':
default:
Expand All @@ -97,6 +169,10 @@ const modalPositionStyle = computed(() => {
})
const closeModal = (): void => {
if (props.storageKey) {
localStorage.removeItem(props.storageKey)
}
customPosition.value = null
emit('outside-click')
}
Expand All @@ -109,6 +185,23 @@ onClickOutside(modal, () => {
}
})
watch(isVisible, (newVal) => {
if (newVal) {
if (props.storageKey) {
const savedPosition = localStorage.getItem(props.storageKey)
if (savedPosition) {
const position = JSON.parse(savedPosition)
customPosition.value = position
} else {
customPosition.value = null
}
}
}
if (!newVal) {
closeModal()
}
})
watch(
() => props.isVisible,
(newVal) => {
Expand Down
15 changes: 12 additions & 3 deletions src/components/Tutorial.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
<template>
<GlassModal :is-visible="showTutorial" class="pa-5 z-[1000000]">
<GlassModal
:is-visible="showTutorial"
class="pa-5 z-[1000000]"
:draggable="true"
storage-key="tutorial-modal"
is-persistent
>
<div class="w-[600px]" :class="tallContent ? 'h-[350px]' : 'h-[280px]'">
<v-window v-model="currentTutorialStep" class="w-full h-full" reverse>
<v-window v-model="currentTutorialStep" class="w-full h-full cursor-move" reverse>
<v-window-item v-for="step in steps" :key="step.id" :value="step.id" class="flex justify-center items-center">
<v-timeline direction="horizontal" class="custom-timeline">
<v-timeline-item
Expand All @@ -10,6 +16,7 @@
fill-dot
size="x-large"
elevation="3"
class="cursor-move"
:dot-color="vehicleStore.isVehicleOnline ? '#4fa483' : '#2c614c'"
>
<div
Expand Down Expand Up @@ -63,7 +70,7 @@
@click="nextTutorialStep"
@keydown.enter="nextTutorialStep"
>
{{ currentTutorialStep === steps.length ? 'Close' : 'Next' }}
{{ currentTutorialStep === steps.length ? 'Close' : currentTutorialStep === 1 ? 'Start' : 'Next' }}
</v-btn>
</div>
</GlassModal>
Expand Down Expand Up @@ -370,6 +377,8 @@ const backTutorialStep = (): void => {
const closeTutorial = (): void => {
showTutorial.value = false
interfaceStore.componentToHighlight = 'none'
currentTutorialStep.value = 1
emits('update:showTutorial', false)
}
Expand Down

0 comments on commit 894fd5e

Please sign in to comment.