Modal-ize the nav search bar.
This commit is contained in:
parent
a98017b0d7
commit
3320857c12
|
|
@ -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;
|
||||||
|
|
@ -1,31 +1,11 @@
|
||||||
// © XlXi 2021
|
// © XlXi 2021
|
||||||
// Graphictoria 5
|
// Graphictoria 5
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
|
import SearchBar from './SearchBar.js';
|
||||||
const dropdownLinks = [
|
|
||||||
{
|
|
||||||
area: 'Games',
|
|
||||||
urlbase: '/games/search/'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area: 'Catalog',
|
|
||||||
urlbase: '/catalog/search/'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area: 'Users',
|
|
||||||
urlbase: '/users/search/'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
area: 'Groups',
|
|
||||||
urlbase: '/groups/search/'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const Navbar = (props) => {
|
const Navbar = (props) => {
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<nav className="navbar graphictoria-navbar fixed-top navbar-expand-md shadow-sm">
|
<nav className="navbar graphictoria-navbar fixed-top navbar-expand-md shadow-sm">
|
||||||
|
|
@ -65,33 +45,13 @@ const Navbar = (props) => {
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</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 ?
|
!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
|
null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
|
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
|
||||||
import { PageTransition } from '@steveeeie/react-page-transition';
|
import { PageTransition } from '@steveeeie/react-page-transition';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@
|
||||||
// Graphictoria 5
|
// Graphictoria 5
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { Link, useHistory } from "react-router-dom";
|
import { Link, useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import Config from '../config.js';
|
import Config from '../config.js';
|
||||||
|
|
||||||
import SetTitle from "../Helpers/Title.js";
|
import SetTitle from "../Helpers/Title.js";
|
||||||
|
|
||||||
|
import Loader from '../Components/Loader.js';
|
||||||
|
|
||||||
import { GenericErrorModal } from './Errors.js';
|
import { GenericErrorModal } from './Errors.js';
|
||||||
|
|
||||||
var url = Config.BaseUrl.replace('http://', '');
|
var url = Config.BaseUrl.replace('http://', '');
|
||||||
|
|
@ -17,7 +19,10 @@ var protocol = Config.Protocol;
|
||||||
class Games extends React.Component {
|
class Games extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {offline: false};
|
this.state = {
|
||||||
|
offline: false,
|
||||||
|
loading: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount()
|
componentDidMount()
|
||||||
|
|
@ -25,29 +30,30 @@ class Games extends React.Component {
|
||||||
var app = this;
|
var app = this;
|
||||||
|
|
||||||
SetTitle('Games');
|
SetTitle('Games');
|
||||||
|
|
||||||
function updateBanners()
|
axios.get(protocol + 'api.' + url + '/games/metadata').then((response) => {
|
||||||
{
|
app.setState({loading: !(response.data.available == false), offline: !response.data.available});
|
||||||
axios.get(protocol + 'api.' + url + '/games/metadata').then((response) => {
|
});
|
||||||
app.setState({offline: !response.data.available});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBanners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
this.state.offline
|
this.state.loading
|
||||||
?
|
?
|
||||||
<GenericErrorModal title="Games Offline">
|
<Loader />
|
||||||
<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>
|
|
||||||
:
|
:
|
||||||
<></>
|
(
|
||||||
|
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>
|
||||||
|
:
|
||||||
|
<></>
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,79 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,w
|
||||||
border-width: 0 1px $width 1px;
|
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
|
// Background
|
||||||
|
|
||||||
html, body, #gtoria-root {
|
html, body, #gtoria-root {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue