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
|
||||
// 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 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';
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
:
|
||||
<></>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue