Skip to content

Commit

Permalink
add drawer and dismiss gesture to modals
Browse files Browse the repository at this point in the history
  • Loading branch information
ticruz38 committed Nov 28, 2024
1 parent c35f459 commit 78fb0e1
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/app/MenuDesktop.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
$: userDisplay = deriveProfileDisplay($pubkey)
</script>

<div class="fixed bottom-0 left-0 top-0 z-nav w-72 bg-tinted-700 transition-colors">
<div class="fixed bottom-0 left-0 top-0 z-modal w-72 bg-tinted-700 transition-colors">
<Anchor external class="mb-4 mt-4 flex items-center gap-2 px-6" href="https://coracle.tools">
<img
alt="App Logo"
Expand Down
2 changes: 1 addition & 1 deletion src/app/Routes.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@

{#each reverse($modals).filter(m => !m.virtual) as m, i (router.getKey(m) + i)}
{@const {component} = router.getMatch(m.path).route}
<Modal mini={m.mini} virtual={false} canClose={!m.noEscape}>
<Modal mini={m.mini} drawer={m.drawer} virtual={false} canClose={!m.noEscape}>
<svelte:component this={component} {...router.getProps(m)} />
</Modal>
{/each}
2 changes: 1 addition & 1 deletion src/app/shared/Note.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
}
}
const showPerson = () => router.at("people").of(event.pubkey).open()
const showPerson = () => router.at("people").of(event.pubkey).open({drawer: false})
const goToDetail = () =>
router
Expand Down
63 changes: 56 additions & 7 deletions src/partials/Modal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
import cx from "classnames"
import {randomId} from "hurdak"
import {onMount} from "svelte"
import {tweened} from "svelte/motion"
import {fly, fade} from "src/util/transition"
import AltColor from "src/partials/AltColor.svelte"
import {router} from "src/app/util/router"
import {swipe} from "src/util/swipe"
import {cubicOut} from "svelte/easing"
export let mini = false
export let drawer = false
export let virtual = true
export let onEscape = null
export let canClose = true
Expand Down Expand Up @@ -70,6 +74,22 @@
router.remove(key)
}
})
const translateY = tweened(0, {easing: cubicOut, duration: 150})
function handleSwipe(e: CustomEvent) {
if (e.detail.isTop) {
translateY.set(e.detail.deltaY, {duration: 0})
}
}
function handleSwipeEnd(e) {
if ($translateY > 200) {
tryClose()
} else {
$translateY = 0
}
}
</script>

<svelte:body
Expand All @@ -89,14 +109,22 @@
class="modal-content h-full overflow-auto"
class:overflow-hidden={mini}
class:pointer-events-none={mini}
transition:fly={{y: 1000}}
use:swipe
on:swipe={handleSwipe}
on:end={handleSwipeEnd}
transition:fly={drawer ? {x: -1000} : {y: 1000}}
style="margin-top: {$translateY}px;"
bind:this={content}>
<div
class="pointer-events-auto mt-12 min-h-full transition transition-all duration-500"
class="pointer-events-auto mt-12 min-h-full flex-row-reverse justify-end transition-all duration-500"
class:flex={drawer}
class:mt-0={drawer}
class:mt-[55vh]={mini}>
{#if canClose}
<div
class="pointer-events-none sticky top-0 z-popover flex w-full flex-col items-end gap-2 p-2">
class="pointer-events-none sticky top-0 z-popover flex w-full flex-col items-end gap-2 p-2"
class:w-auto={drawer}
class:fixed={drawer}>
<div
class="pointer-events-auto flex h-10 w-10 cursor-pointer items-center justify-center rounded-full
border border-solid border-accent bg-accent text-white transition-colors hover:bg-accent"
Expand All @@ -108,20 +136,41 @@
class:hidden={!isNested || !canCloseAll}
class="clear-modals pointer-events-auto flex h-10 w-10 cursor-pointer items-center justify-center
rounded-full border border-solid border-tinted-700 bg-neutral-600 text-neutral-100 transition-colors hover:bg-neutral-600">
<i class="fa fa-angles-down fa-lg" />
<i
class={cx(
{"fa-angles-left": drawer, "fa-angles-down": !drawer},
"fa fa-angles-left fa-lg",
)} />
</div>
</div>
{/if}
<AltColor background class="absolute mt-12 h-full w-full" />
<div on:click|stopPropagation>
{#if !drawer}
<AltColor background class="absolute mt-12 h-full w-full" />
{/if}
<div
on:click|stopPropagation
class:w-full={drawer}
class:border-l={drawer}
class:max-w-screen-md={drawer}>
<AltColor
background
class="relative h-full w-full cursor-auto rounded-t-2xl pb-20 pt-2">
class={cx(
{
"rounded-t-none": drawer,
"rounded-r-2xl": drawer,
"h-screen": drawer,
"overflow-scroll": drawer,
},
"relative w-full cursor-auto rounded-t-2xl pb-20 pt-2",
)}>
<div class="modal-content-inner m-auto flex max-w-2xl flex-col gap-4 p-2">
<slot />
</div>
</AltColor>
</div>
{#if drawer}
<div class="hidden w-72 shrink-0 sm:block" />
{/if}
</div>
</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {NetContext} from "@welshman/net"
import type {AppContext} from "@welshman/app"
import type {SwipeCustomEvent} from "src/util/swipe"

declare module "fuse.js/dist/fuse.min.js"

Expand All @@ -9,3 +10,9 @@ declare module "@welshman/lib" {
app: AppContext
}
}

declare namespace svelteHTML {
interface HTMLAttributes<T> {
"on:swipe"?: (event: SwipeCustomEvent) => any
}
}
3 changes: 2 additions & 1 deletion src/util/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export type HistoryItem = {
path: string
key?: string
mini?: boolean
drawer?: boolean
modal?: boolean
virtual?: boolean
noEscape?: boolean
Expand Down Expand Up @@ -238,7 +239,7 @@ class RouterExtension {

push = (config = {}) => this.go(config)

open = (config = {}) => this.go({...config, modal: true})
open = (config = {}) => this.go({modal: true, drawer: true, ...config})

pushModal = (config = {}) => this.go({...config, modal: true})

Expand Down
117 changes: 117 additions & 0 deletions src/util/swipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
export type SwipeCustomEvent = {
isTop: boolean
deltaX: number
deltaY: number
startX: number
startY: number
currentX: number
currentY: number
}

export function swipe(
node: HTMLElement,
options: {
thresholdX?: number
thresholdY?: number
direction?: "left" | "right" | "top" | "bottom"
} = {
direction: "top",
},
) {
const {direction} = options
let startX: number | null
let startY: number | null

function handleTouchStart(event: TouchEvent) {
const touch = event.touches[0]
startX = touch.clientX
startY = touch.clientY
}

function handleTouchMove(event: TouchEvent) {
if (!startX || !startY) return

const touch = event.touches[0]
const deltaX = touch.clientX - startX
const deltaY = touch.clientY - startY

const lateral = deltaX > 0 ? "right" : "left"
const vertical = deltaY > 0 ? "top" : "bottom"

const swipeX = lateral == direction
const swipeY = vertical == direction

if (!swipeX && !swipeY) return

// for an horizontla swipe, make sure deltaX is 2x deltaY
if (swipeX && Math.abs(deltaX) < Math.abs(2 * deltaY)) return

// for an vertical swipe, make sure deltaY is 2x deltaX
if (swipeY && Math.abs(deltaY) < Math.abs(2 * deltaX)) return

node.dispatchEvent(
new CustomEvent("swipe", {
detail: {
isTop: node.scrollTop == 0,
startX,
startY,
deltaX,
deltaY,
currentX: touch.clientX,
currentY: touch.clientY,
},
}),
)
}

function handleTouchEnd(event: TouchEvent) {
if (!startX || !startY) return

const touch = event.changedTouches[0]
const deltaX = touch.clientX - startX
const deltaY = touch.clientY - startY

const lateral = deltaX > 0 ? "right" : "left"
const vertical = deltaY > 0 ? "top" : "bottom"

const swipeX = lateral == direction
const swipeY = vertical == direction

if (swipeX && swipeY) return

// for an horizontla swipe, make sure deltaX is 2x deltaY
if (swipeX && Math.abs(deltaX) < Math.abs(2 * deltaY)) return

// for an vertical swipe, make sure deltaY is 2x deltaX
if (swipeY && Math.abs(deltaY) < Math.abs(2 * deltaX)) return

node.dispatchEvent(
new CustomEvent("end", {
detail: {
isTop: node.scrollTop == 0,
startX,
startY,
deltaX,
deltaY,
currentX: touch.clientX,
currentY: touch.clientY,
},
}),
)

startX = null
startY = null
}

node.addEventListener("touchstart", handleTouchStart)
node.addEventListener("touchmove", handleTouchMove)
node.addEventListener("touchend", handleTouchEnd)

return {
destroy() {
node.removeEventListener("touchstart", handleTouchStart)
node.removeEventListener("touchmove", handleTouchMove)
node.removeEventListener("touchend", handleTouchEnd)
},
}
}

0 comments on commit 78fb0e1

Please sign in to comment.