github.com/Theta-Dev/Talon@v0.0.0-20211018130634-ff179e19fa9a/ui/menu/src/components/Menu.svelte (about)

     1  <script lang="ts">
     2  	import {openModal} from "svelte-modals"
     3  
     4  	import Icon from "./Icon.svelte"
     5  	import MenuItem from "./MenuItem.svelte"
     6  	import MenuItemPage from "./MenuItemPage.svelte"
     7  	import InfoModal from "./InfoModal.svelte"
     8  	import FloatingButton from "./FloatingButton.svelte"
     9  
    10  	import type {Focusable, TalonPage} from "../util/types"
    11  	import {TalonVisibility} from "../util/types"
    12  	import PageIcon from "./PageIcon.svelte"
    13  	import MenuItemInput from "./MenuItemInput.svelte"
    14  	import {currentPage, currentPageId, pages, rootPath} from "../util/talonData"
    15  
    16  	function showSidebar(): void {
    17  		sidebarShown = true
    18  	}
    19  
    20  	function hideSidebar(): void {
    21  		sidebarShown = false
    22  	}
    23  
    24  	function isMobile(): boolean {
    25  		return window.innerWidth < 768
    26  	}
    27  
    28  	function openSearch(): void {
    29  		searchOpen = true
    30  		searchInput.focus()
    31  	}
    32  
    33  	function closeSearch() {
    34  		searchOpen = false
    35  		searchInput.blur()
    36  
    37  		if (displayedPages.length === 0) searchText = ""
    38  	}
    39  
    40  	function clearSearch() {
    41  		searchText = ""
    42  		closeSearch()
    43  	}
    44  
    45  	function searchKeypress(e: KeyboardEvent) {
    46  		switch (e.key) {
    47  			case "Enter":
    48  				if (!searchText) {
    49  					closeSearch()
    50  				} else if (displayedPages.length) {
    51  					window.location.href = rootPath + displayedPages[0].path
    52  				} else {
    53  					closeSearch()
    54  				}
    55  				break
    56  			case "Escape":
    57  				clearSearch()
    58  				break
    59  		}
    60  	}
    61  
    62  	function openInfo() {
    63  		openModal(InfoModal)
    64  	}
    65  
    66  	let sidebarShown = !isMobile()
    67  	let searchInput: Focusable
    68  	let searchOpen = false
    69  	let searchText = ""
    70  
    71  	let displayedPages: TalonPage[]
    72  	$: displayedPages = Object.entries(pages)
    73  		.filter(([id, page]) => {
    74  			if (id === currentPageId) return false
    75  
    76  			if (searchText) {
    77  				return (
    78  					page.visibility !== TalonVisibility.HIDDEN &&
    79  					page.name.toLowerCase().includes(searchText.toLowerCase())
    80  				)
    81  			}
    82  			return page.visibility === TalonVisibility.FEATURED
    83  		})
    84  		.map(([, page]) => page)
    85  
    86  </script>
    87  
    88  <style lang="sass">
    89  	@use "../style/values"
    90  	@use "../style/mixin"
    91  
    92  	nav
    93  		position: fixed
    94  		top: 0
    95  		right: 0
    96  		height: 100%
    97  
    98  		padding: 1em 0.4em
    99  
   100  		display: flex
   101  		flex-direction: column
   102  		justify-content: space-between
   103  		overflow: hidden
   104  		box-sizing: border-box
   105  
   106  		&.hide
   107  			display: none
   108  
   109  		> div
   110  			flex: 2 1 auto
   111  			overflow-x: hidden
   112  			overflow-y: auto
   113  
   114  			&:first-child, &:last-child
   115  				flex: 0 0 auto
   116  
   117  			+mixin.hideScrollbar
   118  </style>
   119  
   120  <nav class:hide={!sidebarShown}>
   121  	<div>
   122  		<MenuItemInput
   123  			active={searchOpen || Boolean(searchText).valueOf()}
   124  			on:click={openSearch}
   125  			on:focusout={closeSearch}
   126  			on:keyup={searchKeypress}
   127  			bind:input={searchInput}
   128  			bind:text={searchText} />
   129  	</div>
   130  	<div>
   131  		{#each displayedPages as page, i}
   132  			<MenuItemPage
   133  				{page}
   134  				{rootPath}
   135  				active={searchOpen && searchText && i === 0} />
   136  		{/each}
   137  	</div>
   138  	<div>
   139  		{#if currentPage && currentPage.source}
   140  			<MenuItem
   141  				text="View source"
   142  				link={currentPage.source.url}
   143  				newTab={true}
   144  				privacy={true}>
   145  				<Icon iconName={currentPage.source.type} size={40} scale={0.6} />
   146  			</MenuItem>
   147  		{/if}
   148  		<MenuItem text="Info" on:click={openInfo}>
   149  			<PageIcon page={currentPage} />
   150  		</MenuItem>
   151  		<MenuItem text="Hide sidebar" on:click={hideSidebar}>
   152  			<Icon iconName="arrowRight" size={40} scale={0.6} />
   153  		</MenuItem>
   154  	</div>
   155  </nav>
   156  
   157  <FloatingButton hide={sidebarShown} on:click={showSidebar} />