A photo manager for VRChat.
1import { emit } from '@tauri-apps/api/event'; 2import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; 3import anime from 'animejs'; 4import { Show, onMount } from 'solid-js'; 5import { ViewState } from './Managers/ViewManager'; 6 7const appWindow = getCurrentWebviewWindow(); 8 9let NavBar = () => { 10 let dropdownVisible = false; 11 let inAnimation = false; 12 let dropdown: HTMLElement; 13 14 onMount(() => { 15 anime.set(dropdown, { opacity: 0, translateX: -10 }); 16 dropdown.style.display = 'none'; 17 }) 18 19 let setDropdownVisibility = ( visible: boolean ) => { 20 if(inAnimation)return; 21 22 if(dropdownVisible !== visible){ 23 dropdownVisible = visible; 24 inAnimation = true; 25 26 if(visible){ 27 dropdown.style.display = 'block'; 28 29 anime({ 30 targets: dropdown, 31 opacity: 1, 32 translateX: 0, 33 easing: 'easeInOutQuad', 34 duration: 250, 35 complete: () => { 36 inAnimation = false; 37 } 38 }) 39 } else{ 40 anime({ 41 targets: dropdown, 42 opacity: 0, 43 translateX: -10, 44 easing: 'easeInOutQuad', 45 duration: 250, 46 complete: () => { 47 inAnimation = false; 48 dropdown.style.display = 'none'; 49 } 50 }) 51 } 52 } 53 } 54 55 window.CloseAllPopups.push(() => setDropdownVisibility(false)); 56 57 return ( 58 <> 59 <div class="navbar" data-tauri-drag-region> 60 <div class="tabs" data-tauri-drag-region> 61 <div class="nav-tab" onClick={() => { 62 window.ViewManager.ChangeState(ViewState.PHOTO_LIST); 63 anime( 64 { 65 targets: '.settings', 66 opacity: 0, 67 translateX: '500px', 68 easing: 'easeInOutQuad', 69 duration: 250, 70 complete: () => { 71 anime.set('.settings', { display: 'none' }); 72 } 73 }) 74 }}>Photos</div> 75 </div> 76 <div class="nav-tab" style={{ width: '200px', "text-align": 'center', background: 'transparent' }} data-tauri-drag-region> 77 <Show when={window.SyncManager.IsSyncing()}> 78 <Show when={ window.SyncManager.SyncError() == "" } fallback={ "Error: " + window.SyncManager.SyncError() }> 79 <div style={{ width: '100%', "text-align": 'center', 'font-size': '14px' }}> 80 { window.SyncManager.SyncType() }ing: { window.SyncManager.SyncPhotoTransfers () } / { window.SyncManager.SyncPhotoTotal() }<br /> 81 <div style={{ width: '80%', height: '2px', margin: 'auto', "margin-top": '5px', background: '#111' }}> 82 <div style={{ height: '2px', width: (window.SyncManager.SyncPhotoTransfers() / window.SyncManager.SyncPhotoTotal()) * 100 + '%', background: '#00ccff' }}></div> 83 </div> 84 </div> 85 </Show> 86 </Show> 87 </div> 88 <div class="account" onClick={() => setDropdownVisibility(!dropdownVisible)}> 89 <div class="icon"> 90 <img draggable="false" width="24" height="24" src="/icon/caret-down-solid.svg"></img> 91 </div> 92 </div> 93 <div class="control-lights"> 94 <div class="light" onClick={() => appWindow.minimize()}> 95 <img draggable="false" width="24" height="24" src="/icon/minus-solid.svg"></img> 96 </div> 97 <div class="light" onClick={() => appWindow.toggleMaximize()}> 98 <img draggable="false" width="24" height="24" src="/icon/square-regular.svg"></img> 99 </div> 100 <div class="light" onClick={() => { appWindow.hide(); emit('hide-window'); } }> 101 <img draggable="false" width="24" height="24" src="/icon/x-solid.svg"></img> 102 </div> 103 </div> 104 </div> 105 106 <div class="dropdown" ref={( el ) => dropdown = el}> 107 <div class="dropdown-button" onClick={async () => { 108 anime.set('.settings', { display: 'block' }); 109 anime({ 110 targets: '.settings', 111 opacity: 1, 112 translateX: '0px', 113 easing: 'easeInOutQuad', 114 duration: 250 115 }) 116 117 window.ViewManager.ChangeState(ViewState.SETTINGS); 118 setDropdownVisibility(false); 119 }}>Settings</div> 120 </div> 121 </> 122 ) 123} 124 125export default NavBar;