Modal-ize the nav search bar.

This commit is contained in:
Graphictoria 2021-11-09 22:33:09 -05:00
parent a98017b0d7
commit 3320857c12
7 changed files with 217 additions and 66 deletions

12
web/resources/js/components/Loader.js vendored Normal file
View File

@ -0,0 +1,12 @@
// © XlXi 2021
// Graphictoria 5
import React from 'react';
const Loader = () => {
return (
<div className="gtoria-loader"><div></div><div></div><div></div><div></div></div>
);
};
export default Loader;

View File

@ -1,31 +1,11 @@
// © XlXi 2021
// Graphictoria 5
import React, { useState } from 'react';
import React from 'react';
import { Link, NavLink } from 'react-router-dom';
const dropdownLinks = [
{
area: 'Games',
urlbase: '/games/search/'
},
{
area: 'Catalog',
urlbase: '/catalog/search/'
},
{
area: 'Users',
urlbase: '/users/search/'
},
{
area: 'Groups',
urlbase: '/groups/search/'
}
];
import SearchBar from './SearchBar.js';
const Navbar = (props) => {
const [searchQuery, setSearchQuery] = useState('');
return (
<>
<nav className="navbar graphictoria-navbar fixed-top navbar-expand-md shadow-sm">
@ -65,33 +45,13 @@ const Navbar = (props) => {
</li>
}
</ul>
{
!props.maintenanceEnabled ?
<input type="text" className="form-control d-lg-flex graphictoria-search" placeholder="Search" aria-label="Search" aria-describedby="graphictoria-nav-search-button" onChange={ changeEvent => setSearchQuery(changeEvent.target.value) } value={ searchQuery }/>
:
null
}
{
searchQuery.length !== 0 ?
<div id="graphictoria-search-dropdown">
<ul className="dropdown-menu show" area-labelledby="graphictoria-search-dropdown">
{
dropdownLinks.map(({ area, urlbase }, index) =>
<li key={index}>
<Link className="dropdown-item py-2" onClick={ () => setSearchQuery('') } to={urlbase + encodeURIComponent(searchQuery)}>Search <b className="text-truncate graphictoria-search-dropdown-truncate">{searchQuery}</b> in {area}</Link>
</li>
)
}
</ul>
</div>
:
null
}
{
!props.maintenanceEnabled ?
<Link className="btn btn-success" to="/login">Login / Sign up</Link>
<>
<SearchBar />
<Link className="btn btn-success" to="/login">Login / Sign up</Link>
</>
:
null
}

View File

@ -0,0 +1,58 @@
// © XlXi 2021
// Graphictoria 5
import { useState, useRef } from 'react';
import { Link } from 'react-router-dom';
import { useOnClickOutside } from '../helpers/utils.js';
const dropdownLinks = [
{
area: 'Games',
urlbase: '/games/search/'
},
{
area: 'Catalog',
urlbase: '/catalog/search/'
},
{
area: 'Users',
urlbase: '/users/search/'
},
{
area: 'Groups',
urlbase: '/groups/search/'
}
];
const SearchBar = () => {
const inputRef = useRef();
const dropdownRef = useRef();
const [searchQuery, setSearchQuery] = useState('');
useOnClickOutside([inputRef, dropdownRef], () => setSearchQuery(''));
return (
<>
<input type="text" ref={inputRef} className="form-control d-lg-flex graphictoria-search" placeholder="Search" aria-label="Search" aria-describedby="graphictoria-nav-search-button" onChange={ changeEvent => setSearchQuery(changeEvent.target.value) } value={ searchQuery }/>
{
searchQuery.length !== 0 ?
<div ref={dropdownRef} id="graphictoria-search-dropdown">
<ul className="dropdown-menu show" area-labelledby="graphictoria-search-dropdown">
{
dropdownLinks.map(({ area, urlbase }, index) =>
<li key={index}>
<Link className="dropdown-item py-2" onClick={ () => setSearchQuery('') } to={ urlbase + encodeURIComponent(searchQuery) }>Search <b className="text-truncate graphictoria-search-dropdown-truncate">{searchQuery}</b> in {area}</Link>
</li>
)
}
</ul>
</div>
:
null
}
</>
);
};
export default SearchBar;

42
web/resources/js/helpers/utils.js vendored Normal file
View File

@ -0,0 +1,42 @@
// © XlXi 2021
// Graphictoria 5
import { useEffect } from 'react';
function useOnClickOutside(refs, handler) {
useEffect(
() => {
const listener = (event) => {
var shouldReturn = false;
refs.map(
function(val)
{
if (shouldReturn == false && (!val.current || val.current.contains(event.target)))
{
shouldReturn = true;
}
}
);
if(shouldReturn)
{
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
},
[refs, handler]
);
}
export {
useOnClickOutside
};

View File

@ -1,5 +1,5 @@
import axios from 'axios';
import React, { useState } from 'react';
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { PageTransition } from '@steveeeie/react-page-transition';

View File

@ -2,13 +2,15 @@
// Graphictoria 5
import axios from 'axios';
import React from "react";
import React, { useState } from "react";
import { Link, useHistory } from "react-router-dom";
import Config from '../config.js';
import SetTitle from "../Helpers/Title.js";
import Loader from '../Components/Loader.js';
import { GenericErrorModal } from './Errors.js';
var url = Config.BaseUrl.replace('http://', '');
@ -17,7 +19,10 @@ var protocol = Config.Protocol;
class Games extends React.Component {
constructor(props) {
super(props);
this.state = {offline: false};
this.state = {
offline: false,
loading: true
};
}
componentDidMount()
@ -25,29 +30,30 @@ class Games extends React.Component {
var app = this;
SetTitle('Games');
function updateBanners()
{
axios.get(protocol + 'api.' + url + '/games/metadata').then((response) => {
app.setState({offline: !response.data.available});
});
}
updateBanners();
axios.get(protocol + 'api.' + url + '/games/metadata').then((response) => {
app.setState({loading: !(response.data.available == false), offline: !response.data.available});
});
}
render()
{
{
return (
this.state.offline
this.state.loading
?
<GenericErrorModal title="Games Offline">
<img src="/images/symbols/warning.png" width="100" className="mb-3" />
<br />
Seems like XlXi tripped over the game server's power cord again. Games are temporarily unavailable and administrators have been notified of the issue. Sorry for the inconvenience!
</GenericErrorModal>
<Loader />
:
<></>
(
this.state.offline
?
<GenericErrorModal title="Games Offline">
<img src="/images/symbols/warning.png" width="100" className="mb-3" />
<br />
Seems like XlXi tripped over the game server's power cord again. Games are temporarily unavailable and administrators have been notified of the issue. Sorry for the inconvenience!
</GenericErrorModal>
:
<></>
)
);
}
}

View File

@ -21,6 +21,79 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,w
border-width: 0 1px $width 1px;
}
// Loader
.gtoria-loader {
display: inline-block;
position: relative;
margin: auto auto 0 auto;
width: 80px;
height: 80px;
}
.gtoria-loader div {
position: absolute;
top: 33px;
width: 13px;
height: 13px;
border-radius: 50%;
html.gtoria-light & {
background-color: $dark;
}
html.gtoria-dark & {
background-color: $light;
}
}
.gtoria-loader div:nth-child(1) {
left: 8px;
animation: gtoria-ld-p1 0.5s infinite;
}
.gtoria-loader div:nth-child(2) {
left: 8px;
animation: gtoria-ld-p2 0.5s infinite;
}
.gtoria-loader div:nth-child(3) {
left: 32px;
animation: gtoria-ld-p2 0.5s infinite;
}
.gtoria-loader div:nth-child(4) {
left: 56px;
animation: gtoria-ld-p3 0.5s infinite;
}
@keyframes gtoria-ld-p1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes gtoria-ld-p3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes gtoria-ld-p2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(24px, 0);
}
}
// Background
html, body, #gtoria-root {