Refactor Part 1
This commit is contained in:
parent
bc897a6bfb
commit
9fef36eae3
18 changed files with 314 additions and 310 deletions
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Icon from "./icon";
|
||||
import styled from "styled-components";
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
|
||||
const AppContainer = styled.div`
|
||||
display: flex;
|
||||
|
@ -50,6 +50,11 @@ export interface IAppProps {
|
|||
displayURL: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders one app in the list
|
||||
* @param
|
||||
* @returns
|
||||
*/
|
||||
export const App = ({ name, icon, URL, displayURL }: IAppProps) => (
|
||||
<AppContainer>
|
||||
<IconContainer>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Item, SubHeadline } from "./elements";
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
|
||||
const GroupContainer = styled.div`
|
||||
display: flex;
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
{
|
||||
"categories": [
|
||||
{
|
||||
"name": "Networking",
|
||||
"items": [
|
||||
{
|
||||
"name": "pfSense",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "security"
|
||||
},
|
||||
{
|
||||
"name": "Pihole",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "vpn_lock"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Monitoring",
|
||||
"items": [
|
||||
{
|
||||
"name": "Tautulli",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "bar_chart"
|
||||
},
|
||||
{
|
||||
"name": "Grafana",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "show_chart"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"apps": [
|
||||
{
|
||||
"name": "Plex",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "tv"
|
||||
},
|
||||
{
|
||||
"name": "NextCloud",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "filter_drama"
|
||||
},
|
||||
{
|
||||
"name": "Ghost",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "rss_feed"
|
||||
},
|
||||
{
|
||||
"name": "Minecraft",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "games"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "ESXi",
|
||||
"displayURL": "example.com",
|
||||
"URL": "https://example.com",
|
||||
"icon": "dns"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "Wikis",
|
||||
"items": [
|
||||
{
|
||||
"name": "Wikipedia",
|
||||
"url": "https://en.wikipedia.org/"
|
||||
},
|
||||
{
|
||||
"name": "Arch Linux Wiki",
|
||||
"url": "https://archlinux.org/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Dev",
|
||||
"items": [
|
||||
{
|
||||
"name": "Codepen",
|
||||
"url": "https://codepen.io/"
|
||||
},
|
||||
{
|
||||
"name": "JSFiddle",
|
||||
"url": "https://jsfiddle.net/"
|
||||
},
|
||||
{
|
||||
"name": "Pastebin",
|
||||
"url": "https://pastebin.com/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Media",
|
||||
"items": [
|
||||
{
|
||||
"name": "Soundcloud",
|
||||
"url": "https://soundcloud.com"
|
||||
},
|
||||
{
|
||||
"name": "YouTube",
|
||||
"url": "https://youtube.com"
|
||||
},
|
||||
{
|
||||
"name": "Twitch",
|
||||
"url": "https://twitch.tv"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Social Networks",
|
||||
"items": [
|
||||
{
|
||||
"name": "Facebook",
|
||||
"url": "https://facebook.com"
|
||||
},
|
||||
{
|
||||
"name": "Twitter",
|
||||
"url": "https://twitter.com"
|
||||
},
|
||||
{
|
||||
"name": "Instagram",
|
||||
"url": "https://instagram.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Imageboards",
|
||||
"items": [
|
||||
{
|
||||
"name": "Reddit",
|
||||
"url": "https://reddit.com"
|
||||
},
|
||||
{
|
||||
"name": "4chan",
|
||||
"url": "https://4chan.org"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Tech",
|
||||
"items": [
|
||||
{
|
||||
"name": "Hackernoon",
|
||||
"url": "https://hackernoon.com"
|
||||
},
|
||||
{
|
||||
"name": "The Verge",
|
||||
"url": "https://theverge.com"
|
||||
},
|
||||
{
|
||||
"name": "Hackernews",
|
||||
"url": "https://news.ycombinator.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"imprint": {
|
||||
"name": {
|
||||
"text": "John Doe",
|
||||
"link": "#"
|
||||
},
|
||||
"address": {
|
||||
"text": "Null Ave. 1234, 90008 Los Angeles",
|
||||
"link": "#"
|
||||
},
|
||||
"phone": {
|
||||
"text": "+1-202-555-0167",
|
||||
"link": "#"
|
||||
},
|
||||
"email": {
|
||||
"text": "doe.john@example.com",
|
||||
"link": "#"
|
||||
},
|
||||
"url": {
|
||||
"text": "example.com",
|
||||
"link": "#"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
{
|
||||
"providers": [
|
||||
{
|
||||
"name": "Allmusic",
|
||||
"url": "https://www.allmusic.com/search/all/",
|
||||
"prefix": "/a"
|
||||
},
|
||||
{
|
||||
"name": "Discogs",
|
||||
"url": "https://www.discogs.com/search/?q=",
|
||||
"prefix": "/di"
|
||||
},
|
||||
{
|
||||
"name": "Duck Duck Go",
|
||||
"url": "https://duckduckgo.com/?q=",
|
||||
"prefix": "/d"
|
||||
},
|
||||
{
|
||||
"name": "iMDB",
|
||||
"url": "https://www.imdb.com/find?q=",
|
||||
"prefix": "/i"
|
||||
},
|
||||
{
|
||||
"name": "TheMovieDB",
|
||||
"url": "https://www.themoviedb.org/search?query=",
|
||||
"prefix": "/m"
|
||||
},
|
||||
{
|
||||
"name": "Reddit",
|
||||
"url": "https://www.reddit.com/search?q=",
|
||||
"prefix": "/r"
|
||||
},
|
||||
{
|
||||
"name": "Qwant",
|
||||
"url": "https://www.qwant.com/?q=",
|
||||
"prefix": "/q"
|
||||
},
|
||||
{
|
||||
"name": "Soundcloud",
|
||||
"url": "https://soundcloud.com/search?q=",
|
||||
"prefix": "/so"
|
||||
},
|
||||
{
|
||||
"name": "Spotify",
|
||||
"url": "https://open.spotify.com/search/results/",
|
||||
"prefix": "/s"
|
||||
},
|
||||
{
|
||||
"name": "TheTVDB",
|
||||
"url": "https://www.thetvdb.com/search?q=",
|
||||
"prefix": "/tv"
|
||||
},
|
||||
{
|
||||
"name": "Trakt",
|
||||
"url": "https://trakt.tv/search?query=",
|
||||
"prefix": "/t"
|
||||
},
|
||||
{
|
||||
"name": "YouTube",
|
||||
"url": "https://youtube.com/results?search_query=",
|
||||
"prefix": "/yt"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
{
|
||||
"themes": [
|
||||
{
|
||||
"label": "Classic",
|
||||
"value": 0,
|
||||
"mainColor": "#000000",
|
||||
"accentColor": "#1e272e",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
{
|
||||
"label": "Dark",
|
||||
"value": 1,
|
||||
"mainColor": "#ffffff",
|
||||
"accentColor": "#999999",
|
||||
"backgroundColor": "#000000"
|
||||
},
|
||||
{
|
||||
"label": "Raw",
|
||||
"value": 2,
|
||||
"mainColor": "",
|
||||
"accentColor": "",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
{
|
||||
"label": "Blackboard",
|
||||
"value": 3,
|
||||
"mainColor": "#fffdea",
|
||||
"accentColor": "#5c5c5c",
|
||||
"backgroundColor": "#1a1a1a"
|
||||
},
|
||||
{
|
||||
"label": "Gazette",
|
||||
"value": 4,
|
||||
"mainColor": "#000000",
|
||||
"accentColor": "#5c5c5c",
|
||||
"backgroundColor": "#F2F7FF"
|
||||
},
|
||||
{
|
||||
"label": "Espresso",
|
||||
"value": 5,
|
||||
"mainColor": "#d1b59a",
|
||||
"accentColor": "#4e4e4e",
|
||||
"backgroundColor": "#21211f"
|
||||
},
|
||||
{
|
||||
"label": "Cab",
|
||||
"value": 6,
|
||||
"mainColor": "#1f1f1f",
|
||||
"accentColor": "#424242",
|
||||
"backgroundColor": "#f6d305"
|
||||
},
|
||||
{
|
||||
"label": "Cloud",
|
||||
"value": 7,
|
||||
"mainColor": "#35342f",
|
||||
"accentColor": "#37bbe4",
|
||||
"backgroundColor": "#f1f2f0"
|
||||
},
|
||||
{
|
||||
"label": "Lime",
|
||||
"value": 8,
|
||||
"mainColor": "#aabbc3",
|
||||
"accentColor": "#aeea00",
|
||||
"backgroundColor": "#263238"
|
||||
},
|
||||
{
|
||||
"label": "White",
|
||||
"value": 9,
|
||||
"mainColor": "#222222",
|
||||
"accentColor": "#dddddd",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
{
|
||||
"label": "Tron",
|
||||
"value": 10,
|
||||
"mainColor": "#effbff",
|
||||
"accentColor": "#6ee2ff",
|
||||
"backgroundColor": "#242b33"
|
||||
},
|
||||
{
|
||||
"label": "Blues",
|
||||
"value": 11,
|
||||
"mainColor": "#eff1fc",
|
||||
"accentColor": "#6677eb",
|
||||
"backgroundColor": "#2b2c56"
|
||||
},
|
||||
{
|
||||
"label": "Passion",
|
||||
"value": 12,
|
||||
"mainColor": "#12005e",
|
||||
"accentColor": "#8e24aa",
|
||||
"backgroundColor": "#f5f5f5"
|
||||
},
|
||||
{
|
||||
"label": "Chalk",
|
||||
"value": 13,
|
||||
"mainColor": "#aabbc3",
|
||||
"accentColor": "#ff869a",
|
||||
"backgroundColor": "#263238"
|
||||
},
|
||||
{
|
||||
"label": "Paper",
|
||||
"value": 14,
|
||||
"mainColor": "#4c432e",
|
||||
"accentColor": "#aa9a73",
|
||||
"backgroundColor": "#f8f6f1"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
import Icon from "./icon";
|
||||
|
||||
// File for elements that are/can be reused across the entire site.
|
||||
|
@ -62,6 +62,7 @@ export const Button = styled.button`
|
|||
const StyledButton = styled.button`
|
||||
float: right;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
|
@ -69,16 +70,6 @@ const StyledButton = styled.button`
|
|||
}
|
||||
`;
|
||||
|
||||
export const RefreshButton = styled(Button)`
|
||||
display: relative;
|
||||
top: 0;
|
||||
float: right;
|
||||
`;
|
||||
|
||||
export const ErrorMessage = styled.p`
|
||||
color: red;
|
||||
`;
|
||||
|
||||
interface IIconButtonProps {
|
||||
icon: string;
|
||||
onClick: any;
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { ISearchProviderProps } from "./searchBar";
|
||||
import { IBookmarkGroupProps } from "./bookmarkGroup";
|
||||
import { IAppCategoryProps } from "./appCategory";
|
||||
import { IAppProps } from "./app";
|
||||
import { IThemeProps } from "./themeManager";
|
||||
import { IImprintProps } from "./imprint";
|
||||
|
||||
const errorMessage = "Failed to load data.";
|
||||
|
||||
const handleResponse = (response: any) => {
|
||||
if (response.ok) return response.json();
|
||||
throw new Error(errorMessage);
|
||||
};
|
||||
|
||||
// SECTION: Search Provider
|
||||
|
||||
export interface ISearchProviderDataProps {
|
||||
providers: Array<ISearchProviderProps>;
|
||||
error: string | boolean;
|
||||
}
|
||||
|
||||
export const useSearchProviderData = () => {
|
||||
const [
|
||||
searchProviderData,
|
||||
setSearchProviderData,
|
||||
] = useState<ISearchProviderDataProps>({ providers: [], error: false });
|
||||
|
||||
const fetchSearchProviderData = useCallback(() => {
|
||||
(process.env.NODE_ENV === "production"
|
||||
? fetch("/data/search.json").then(handleResponse)
|
||||
: import("./data/search.json")
|
||||
)
|
||||
.then((jsonResponse) => {
|
||||
setSearchProviderData({ ...jsonResponse, error: false });
|
||||
})
|
||||
.catch((error) => {
|
||||
setSearchProviderData({ providers: [], error: error.message });
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSearchProviderData();
|
||||
}, [fetchSearchProviderData]);
|
||||
|
||||
return { searchProviderData, fetchSearchProviderData };
|
||||
};
|
||||
|
||||
// SECTION: Bookmark data
|
||||
|
||||
export interface IBookmarkDataProps {
|
||||
groups: Array<IBookmarkGroupProps>;
|
||||
error: string | boolean;
|
||||
}
|
||||
|
||||
export const useBookmarkData = () => {
|
||||
const [bookmarkData, setBookmarkData] = useState<IBookmarkDataProps>({
|
||||
groups: [],
|
||||
error: false,
|
||||
});
|
||||
|
||||
const fetchBookmarkData = useCallback(() => {
|
||||
(process.env.NODE_ENV === "production"
|
||||
? fetch("/data/bookmarks.json").then(handleResponse)
|
||||
: import("./data/bookmarks.json")
|
||||
)
|
||||
.then((jsonResponse) => {
|
||||
setBookmarkData({ ...jsonResponse, error: false });
|
||||
})
|
||||
.catch((error) => {
|
||||
setBookmarkData({ groups: [], error: error.message });
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchBookmarkData();
|
||||
}, [fetchBookmarkData]);
|
||||
|
||||
return { bookmarkData, fetchBookmarkData };
|
||||
};
|
||||
|
||||
// SECTION: App data
|
||||
|
||||
export interface IAppDataProps {
|
||||
categories: Array<IAppCategoryProps>;
|
||||
apps: Array<IAppProps>;
|
||||
error: string | boolean;
|
||||
}
|
||||
|
||||
export const useAppData = () => {
|
||||
const [appData, setAppData] = useState({
|
||||
categories: [],
|
||||
apps: [],
|
||||
error: false,
|
||||
});
|
||||
|
||||
const fetchAppData = useCallback(() => {
|
||||
(process.env.NODE_ENV === "production"
|
||||
? fetch("/data/apps.json").then(handleResponse)
|
||||
: import("./data/apps.json")
|
||||
)
|
||||
.then((jsonResponse) => {
|
||||
setAppData({ ...jsonResponse, error: false });
|
||||
})
|
||||
.catch((error) => {
|
||||
setAppData({ categories: [], apps: [], error: error.message });
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAppData();
|
||||
}, [fetchAppData]);
|
||||
return { appData, fetchAppData };
|
||||
};
|
||||
|
||||
// Section: Theme Data
|
||||
|
||||
export interface IThemeDataProps {
|
||||
themes: Array<IThemeProps>;
|
||||
error: string | boolean;
|
||||
}
|
||||
|
||||
export const useThemeData = () => {
|
||||
const [themeData, setThemeData] = useState<IThemeDataProps>({
|
||||
themes: [],
|
||||
error: false,
|
||||
});
|
||||
|
||||
const fetchThemeData = useCallback(() => {
|
||||
(process.env.NODE_ENV === "production"
|
||||
? fetch("/data/themes.json").then(handleResponse)
|
||||
: import("./data/themes.json")
|
||||
)
|
||||
.then((jsonResponse) => {
|
||||
setThemeData({ ...jsonResponse, error: false });
|
||||
})
|
||||
.catch((error) => {
|
||||
setThemeData({ themes: [], error: error.message });
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchThemeData();
|
||||
}, [fetchThemeData]);
|
||||
return { themeData, fetchThemeData };
|
||||
};
|
||||
|
||||
// SECTION: Imprint Data
|
||||
|
||||
export interface IImprintDataProps {
|
||||
imprint: IImprintProps;
|
||||
error: string | boolean;
|
||||
}
|
||||
|
||||
export const useImprintData = () => {
|
||||
const [imprintData, setImprintData] = useState<IImprintDataProps>({
|
||||
imprint: {
|
||||
name: { text: "", link: "" },
|
||||
address: { text: "", link: "" },
|
||||
phone: { text: "", link: "" },
|
||||
email: { text: "", link: "" },
|
||||
url: { text: "", link: "" },
|
||||
},
|
||||
error: false,
|
||||
});
|
||||
|
||||
const fetchImprintData = useCallback(() => {
|
||||
(process.env.NODE_ENV === "production"
|
||||
? fetch("/data/imprint.json").then(handleResponse)
|
||||
: import("./data/imprint.json")
|
||||
)
|
||||
.then((jsonResponse: any) => {
|
||||
setImprintData({ ...jsonResponse, error: false });
|
||||
})
|
||||
.catch((error: any) => {
|
||||
setImprintData({
|
||||
imprint: {
|
||||
name: { text: "", link: "" },
|
||||
address: { text: "", link: "" },
|
||||
phone: { text: "", link: "" },
|
||||
email: { text: "", link: "" },
|
||||
url: { text: "", link: "" },
|
||||
},
|
||||
error: error.message,
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchImprintData();
|
||||
}, [fetchImprintData]);
|
||||
return { imprintData, fetchImprintData };
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
|
||||
const GreeterContainer = styled.div`
|
||||
padding: 2rem 0;
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
|
||||
export const RawIcon = styled.i`
|
||||
interface IIconProps {
|
||||
name: string;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
export const Icon = ({ name, size }: IIconProps) => {
|
||||
|
||||
let IconContainer = styled.i`
|
||||
font-family: "Material Icons";
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: ${size ? size : "24px"};
|
||||
color: ${selectedTheme.mainColor};
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
|
@ -19,18 +28,7 @@ export const RawIcon = styled.i`
|
|||
font-feature-settings: "liga";
|
||||
`;
|
||||
|
||||
interface IIconProps {
|
||||
name: string;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
export const ComponentIcon = ({ name, size }: IIconProps) => {
|
||||
let IconContainer = styled(RawIcon)`
|
||||
font-size: ${size ? size : "24px"};
|
||||
color: ${selectedTheme.mainColor};
|
||||
`;
|
||||
|
||||
return <IconContainer>{name}</IconContainer>;
|
||||
};
|
||||
|
||||
export default ComponentIcon;
|
||||
export default Icon;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Modal from "./modal";
|
||||
import styled from "styled-components";
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
import {
|
||||
ListContainer,
|
||||
ItemList,
|
||||
|
@ -14,11 +14,8 @@ const Headline = styled(Hl)`
|
|||
padding: 1rem 0;
|
||||
`;
|
||||
|
||||
const SubHeadline = styled(SHl)`
|
||||
const ModalSubHeadline = styled(SHl)`
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const ModalSubHeadline = styled(SubHeadline)`
|
||||
padding: 0.5rem 0;
|
||||
`;
|
||||
|
||||
|
@ -80,6 +77,7 @@ const Imprint = ({ imprint }: IImprintComponentProps) => (
|
|||
<Modal
|
||||
element="text"
|
||||
text="View Imprint"
|
||||
title="Legal Disclosure"
|
||||
condition={!window.location.href.endsWith("#imprint")}
|
||||
onClose={() => {
|
||||
if (window.location.href.endsWith("#imprint")) {
|
||||
|
@ -88,7 +86,6 @@ const Imprint = ({ imprint }: IImprintComponentProps) => (
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Headline>Legal Disclosure</Headline>
|
||||
<ModalSubHeadline>
|
||||
Information in accordance with section 5 TMG
|
||||
</ModalSubHeadline>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
|
||||
import { IconButton } from "./elements";
|
||||
import { Headline, IconButton } from "./elements";
|
||||
|
||||
const ModalContainer = styled.div`
|
||||
position: absolute;
|
||||
|
@ -30,36 +30,47 @@ const Text = styled.p`
|
|||
}
|
||||
`;
|
||||
|
||||
const TitleContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
interface IModalInterface {
|
||||
element: string;
|
||||
icon?: string;
|
||||
text?: string;
|
||||
condition?: boolean;
|
||||
title: string;
|
||||
onClose?: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Modal = (props: IModalInterface) => {
|
||||
const [modalHidden, setModalHidden] = useState(props.condition ?? true);
|
||||
const Modal = ({ element, icon, text, condition, title, onClose, children }: IModalInterface) => {
|
||||
const [modalHidden, setModalHidden] = useState(condition ?? true);
|
||||
|
||||
const closeModal = () => {
|
||||
if (props.onClose) props.onClose();
|
||||
if (onClose) onClose();
|
||||
setModalHidden(!modalHidden);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.element === "icon" && (
|
||||
<IconButton icon={props.icon ?? ""} onClick={() => closeModal()} />
|
||||
{element === "icon" && (
|
||||
<IconButton icon={icon ?? ""} onClick={() => closeModal()} />
|
||||
)}
|
||||
|
||||
{props.element === "text" && (
|
||||
<Text onClick={() => closeModal()}>{props.text}</Text>
|
||||
{element === "text" && (
|
||||
<Text onClick={() => closeModal()}>{text}</Text>
|
||||
)}
|
||||
|
||||
<ModalContainer hidden={modalHidden}>
|
||||
<IconButton icon="close" onClick={() => closeModal()} />
|
||||
{props.children}
|
||||
<TitleContainer>
|
||||
<Headline>{title}</Headline>
|
||||
<IconButton icon="close" onClick={() => closeModal()} />
|
||||
</TitleContainer>
|
||||
{children}
|
||||
</ModalContainer>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import selectedTheme from "./themeManager";
|
||||
import selectedTheme from "../lib/theme";
|
||||
|
||||
import { Button } from "./elements";
|
||||
|
||||
|
|
|
@ -1,21 +1,31 @@
|
|||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Select from "react-select";
|
||||
import Select, { Styles } from "react-select";
|
||||
|
||||
import { ISearchProviderProps } from "./searchBar";
|
||||
import selectedTheme, { setTheme, IThemeProps } from "./themeManager";
|
||||
import { Button, Headline as hl } from "./elements";
|
||||
import selectedTheme, { setTheme, IThemeProps } from "../lib/theme";
|
||||
import { Button, SubHeadline } from "./elements";
|
||||
|
||||
import Modal from "./modal";
|
||||
|
||||
const Headline = styled(hl)`
|
||||
padding: 0.5rem 0;
|
||||
`;
|
||||
/**
|
||||
* Complementary code to get hover pseudo-classes working in React
|
||||
* @param color the color of the element on hover
|
||||
* @param backgroundColor the background color of the element on hover
|
||||
* @param border the border of the element on hover
|
||||
* @param borderColor the border color of the element on hover
|
||||
*/
|
||||
interface IHoverProps {
|
||||
color?: string;
|
||||
backgroundColor?: string;
|
||||
border?: string;
|
||||
borderColor?: string;
|
||||
}
|
||||
|
||||
const SelectContainer = styled.div`
|
||||
padding-bottom: 1rem;
|
||||
`;
|
||||
interface IPseudoProps extends React.CSSProperties {
|
||||
"&:hover": IHoverProps
|
||||
}
|
||||
|
||||
const FormContainer = styled.div`
|
||||
display: grid;
|
||||
|
@ -42,47 +52,72 @@ const HeadCell = styled.th`
|
|||
background: none;
|
||||
`;
|
||||
|
||||
const SelectorStyle = {
|
||||
control: (provided: any) => ({
|
||||
...provided,
|
||||
fontWeight: "500",
|
||||
const Section = styled.div`
|
||||
padding: 1rem 0;
|
||||
`;
|
||||
|
||||
const SectionHeadline = styled(SubHeadline)`
|
||||
width: 100%;
|
||||
border-bottom: 1px solid ${selectedTheme.accentColor};
|
||||
margin-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
const SelectorStyle: Partial<Styles<IThemeProps, false>> = {
|
||||
indicatorSeparator: () => ({
|
||||
display: "none",
|
||||
}),
|
||||
container: (base: React.CSSProperties): React.CSSProperties => ({
|
||||
...base,
|
||||
margin: "0 2px",
|
||||
}),
|
||||
dropdownIndicator: (base: React.CSSProperties): IPseudoProps => ({
|
||||
...base,
|
||||
color: selectedTheme.mainColor,
|
||||
"&:hover": {
|
||||
color: selectedTheme.mainColor
|
||||
}
|
||||
}),
|
||||
control: (base: React.CSSProperties): IPseudoProps => ({
|
||||
...base,
|
||||
fontWeight: 500,
|
||||
color: selectedTheme.mainColor,
|
||||
textTransform: "uppercase",
|
||||
width: "12rem",
|
||||
background: "none",
|
||||
borderRadius: "0px",
|
||||
border: "1px solid " + selectedTheme.mainColor,
|
||||
boxShadow: 0,
|
||||
borderRadius: 0,
|
||||
border: "1px solid",
|
||||
borderColor: selectedTheme.mainColor,
|
||||
boxShadow: "none",
|
||||
"&:hover": {
|
||||
border: "1px solid " + selectedTheme.mainColor,
|
||||
border: "1px solid",
|
||||
borderColor: selectedTheme.mainColor
|
||||
},
|
||||
}),
|
||||
menu: (provided: any) => ({
|
||||
...provided,
|
||||
menu: (base: React.CSSProperties): React.CSSProperties => ({
|
||||
...base,
|
||||
backgroundColor: selectedTheme.backgroundColor,
|
||||
border: "1px solid " + selectedTheme.mainColor,
|
||||
borderRadius: 0,
|
||||
boxShadow: 0,
|
||||
boxShadow: "none",
|
||||
margin: "4px 0"
|
||||
}),
|
||||
option: (provided: any) => ({
|
||||
...provided,
|
||||
fontWeight: "500",
|
||||
option: (base: React.CSSProperties): IPseudoProps => ({
|
||||
...base,
|
||||
fontWeight: 500,
|
||||
color: selectedTheme.mainColor,
|
||||
textTransform: "uppercase",
|
||||
borderRadius: 0,
|
||||
boxShadow: 0,
|
||||
boxShadow: "none",
|
||||
backgroundColor: selectedTheme.backgroundColor,
|
||||
"&:hover": {
|
||||
backgroundColor: selectedTheme.mainColor,
|
||||
color: selectedTheme.backgroundColor,
|
||||
},
|
||||
}),
|
||||
singleValue: (provided: any) => {
|
||||
return {
|
||||
...provided,
|
||||
color: selectedTheme.mainColor,
|
||||
};
|
||||
},
|
||||
singleValue: (base: React.CSSProperties): React.CSSProperties => ({
|
||||
...base,
|
||||
color: selectedTheme.mainColor,
|
||||
}),
|
||||
};
|
||||
|
||||
interface ISettingsProps {
|
||||
|
@ -95,10 +130,13 @@ const Settings = ({ themes, providers }: ISettingsProps) => {
|
|||
|
||||
if (themes && providers) {
|
||||
return (
|
||||
<Modal element="icon" icon="settings">
|
||||
<Modal element="icon" icon="settings" title="Settings">
|
||||
{themes && (
|
||||
<SelectContainer>
|
||||
<Headline>Theme:</Headline>
|
||||
|
||||
|
||||
|
||||
<Section>
|
||||
<SectionHeadline>Theme:</SectionHeadline>
|
||||
<FormContainer>
|
||||
<Select
|
||||
options={themes}
|
||||
|
@ -108,29 +146,33 @@ const Settings = ({ themes, providers }: ISettingsProps) => {
|
|||
}}
|
||||
styles={SelectorStyle}
|
||||
/>
|
||||
|
||||
<Button onClick={() => setTheme(JSON.stringify(newTheme))}>
|
||||
Apply
|
||||
</Button>
|
||||
<Button onClick={() => window.location.reload()}>Refresh</Button>
|
||||
</FormContainer>
|
||||
</SelectContainer>
|
||||
</Section>
|
||||
)}
|
||||
{providers && (
|
||||
<Table>
|
||||
<tbody>
|
||||
<TableRow>
|
||||
<HeadCell>Search Provider</HeadCell>
|
||||
<HeadCell>Prefix</HeadCell>
|
||||
</TableRow>
|
||||
{providers.map((provider, index) => (
|
||||
<TableRow key={provider.name + index}>
|
||||
<TableCell>{provider.name}</TableCell>
|
||||
<TableCell>{provider.prefix}</TableCell>
|
||||
|
||||
<Section>
|
||||
<SectionHeadline>Search Providers</SectionHeadline>
|
||||
<Table>
|
||||
<tbody>
|
||||
<TableRow>
|
||||
<HeadCell>Search Provider</HeadCell>
|
||||
<HeadCell>Prefix</HeadCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
{providers.map((provider, index) => (
|
||||
<TableRow key={provider.name + index}>
|
||||
<TableCell>{provider.name}</TableCell>
|
||||
<TableCell>{provider.prefix}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</Section>
|
||||
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
export interface IThemeProps {
|
||||
label: string;
|
||||
value: number;
|
||||
mainColor: string;
|
||||
accentColor: string;
|
||||
backgroundColor: string;
|
||||
}
|
||||
|
||||
const defaultTheme: IThemeProps = {
|
||||
label: "Classic",
|
||||
value: 0,
|
||||
mainColor: "#000000",
|
||||
accentColor: "#1e272e",
|
||||
backgroundColor: "#ffffff",
|
||||
};
|
||||
|
||||
export const setTheme = (theme: string) => {
|
||||
if (theme !== undefined) {
|
||||
localStorage.setItem("theme", theme);
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
export const resetTheme = () => {
|
||||
localStorage.removeItem("theme");
|
||||
};
|
||||
|
||||
const getTheme = () => {
|
||||
let selectedTheme = defaultTheme;
|
||||
|
||||
if (localStorage.getItem("theme") != null) {
|
||||
selectedTheme = JSON.parse(localStorage.getItem("theme") || "{}");
|
||||
}
|
||||
|
||||
return selectedTheme;
|
||||
};
|
||||
|
||||
const selectedTheme = getTheme();
|
||||
|
||||
export default selectedTheme;
|
Loading…
Add table
Add a link
Reference in a new issue