This navigation overrides the main navigation, making it act as a modal. This is complicated, so let's not try to do it again anywhere else besides these options menus :P Well actually not that complicated, but it is a bit of duplicated work and feels a little hacky so I'm not entirely comfortable with it. Maybe there's a DRYer way to get it working though?
168 lines
4.4 KiB
Svelte
168 lines
4.4 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from 'svelte'
|
|
import Router, { location, pop } from 'svelte-spa-router'
|
|
|
|
import SoftKey from './components/SoftKey.svelte'
|
|
|
|
import Home from './routes/Home.svelte'
|
|
import Login from './routes/Login.svelte'
|
|
import Messages from './routes/Messages.svelte'
|
|
import Contacts from './routes/Contacts.svelte'
|
|
import Chat from './routes/Chat.svelte'
|
|
import Redirect from './routes/Redirect.svelte'
|
|
|
|
import {
|
|
titleStore,
|
|
softkeysStore,
|
|
} from './stores.ts'
|
|
|
|
let title
|
|
titleStore.subscribe(value => title = value)
|
|
|
|
let softkeys
|
|
softkeysStore.subscribe(value => softkeys = value)
|
|
|
|
// Navigation
|
|
const routes = {
|
|
'/': Home,
|
|
'/login': Login,
|
|
'/messages': Messages,
|
|
'/contacts': Contacts,
|
|
'/chat/:chatID': Chat,
|
|
'*': Redirect,
|
|
}
|
|
|
|
// Go back
|
|
function goBack() {
|
|
// exit if we're on the main screen
|
|
if ($location === '/') {
|
|
window.close()
|
|
}
|
|
// otherwise, go back
|
|
pop()
|
|
}
|
|
|
|
// Account creation (or not)
|
|
function createAccount() {
|
|
alert('Unfortunately, you cannot create an account yet :(')
|
|
}
|
|
|
|
// For D-pad navigation
|
|
function nav(move) {
|
|
let currentIndex = document.activeElement.tabIndex
|
|
let next = currentIndex + move
|
|
let items = document.querySelectorAll('.focusable')
|
|
|
|
// Loop around if at end
|
|
if (next < 0) next = items.length - 1
|
|
if (next > items.length - 1) next = 0
|
|
let targetElement = items[next]
|
|
targetElement.focus()
|
|
}
|
|
|
|
/**
|
|
* Okay, so here's a slightly complicated function.
|
|
* What we're trying to do is scroll down (in the case
|
|
* of a long element that overflows the screen), or, if
|
|
* we've got to the end of that element, then to move on
|
|
* to the next one. This may not be the neatest way to
|
|
* do it but let's start with this and see how it goes...
|
|
*/
|
|
function navDown() {
|
|
// list.scrollTop > dong.offsetTop + dong.offsetHeight - (list.offsetHeight) // when this turns true, it means we've gone past
|
|
|
|
// Check if the parent scrolls
|
|
if (document.activeElement?.parentElement?.scrollTop > 0) {
|
|
|
|
// Yes it does! Okay, we've got to handle this carefully
|
|
let el = document.activeElement
|
|
let scrollEl = el.parentElement
|
|
|
|
// If we're already maxed out, time to navigate on
|
|
if (scrollEl.scrollTop == scrollEl.scrollTopMax) {
|
|
nav(1)
|
|
return
|
|
}
|
|
|
|
// First, scroll down a bit
|
|
scrollEl.scrollTop += scrollEl.offsetHeight * 0.5
|
|
|
|
// Now, check if we're still in view
|
|
if (scrollEl.scrollTop > // current scroll greater than
|
|
el.offsetTop // place where element starts
|
|
+ el.offsetHeight // plus height of element
|
|
- (scrollEl.offsetHeight * 0.4) // minus a wee bit
|
|
) {
|
|
// Now the previous thing's scrolled reasonably away
|
|
// so we can safely focus on the next one
|
|
nav(1)
|
|
|
|
// But let's just scroll back up a bit to make sure
|
|
// everything's still in view
|
|
scrollEl.scrollTop -= scrollEl.offsetHeight * 0.5
|
|
}
|
|
} else {
|
|
// Viewport not scrollable; just navigate without
|
|
// any funky stuff
|
|
nav(1)
|
|
}
|
|
}
|
|
|
|
// Key-down handler
|
|
function handleKeydown(e) {
|
|
switch(e.key) {
|
|
case 'ArrowUp':
|
|
nav(-1)
|
|
break
|
|
case 'ArrowDown':
|
|
navDown()
|
|
break
|
|
case 'SoftLeft':
|
|
case '[':
|
|
softkeys.left.callback()
|
|
break
|
|
case 'Enter':
|
|
softkeys.center.callback()
|
|
break
|
|
case 'SoftRight':
|
|
case ']':
|
|
softkeys.right.callback()
|
|
break
|
|
case 'Backspace':
|
|
goBack()
|
|
e.preventDefault()
|
|
break
|
|
}
|
|
}
|
|
|
|
// Listen for keys
|
|
document
|
|
.addEventListener('keydown', handleKeydown)
|
|
|
|
// Get rid of the splash screen
|
|
onMount(() => {
|
|
let splash = document.getElementById('splash')
|
|
if (splash) splash.remove()
|
|
})
|
|
</script>
|
|
|
|
<main id="app">
|
|
<div id="header">
|
|
<div name="header">{title}</div>
|
|
</div>
|
|
<div id="content">
|
|
<Router {routes}/>
|
|
</div>
|
|
<div class="softkeys">
|
|
{#if softkeys.left && softkeys.left.label}
|
|
<SoftKey side="left">{softkeys.left.label}</SoftKey>
|
|
{/if}
|
|
{#if softkeys.center && softkeys.center.label}
|
|
<SoftKey side="center">{softkeys.center.label}</SoftKey>
|
|
{/if}
|
|
{#if softkeys.right && softkeys.right.label}
|
|
<SoftKey side="right">{softkeys.right.label}</SoftKey>
|
|
{/if}
|
|
</div>
|
|
</main>
|