Rename themes after colors (leaf = green, moon = blue)
This commit introduces the language and theme dropdowns as being invisible, and includes work related to improving how the dropdowns are implemented.
This commit is contained in:
parent
47847b2545
commit
9e393fcdf7
22 changed files with 88 additions and 109 deletions
|
@ -100,5 +100,5 @@ body .root .content.theme.ar {
|
|||
}
|
||||
}
|
||||
|
||||
@import "themes/moon";
|
||||
@import "themes/leaf";
|
||||
@import "themes/blue";
|
||||
@import "themes/green";
|
||||
|
|
|
@ -61,6 +61,20 @@
|
|||
justify-content: flex-end !important;
|
||||
}
|
||||
|
||||
.content.theme {
|
||||
.row.dropdown-row {
|
||||
.react-select.theme {
|
||||
cursor: pointer;
|
||||
ul li.blue {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content.theme.en {}
|
||||
|
||||
.content.theme.ar {
|
||||
|
@ -102,5 +116,5 @@
|
|||
}
|
||||
}
|
||||
|
||||
@import "themes/moon";
|
||||
@import "themes/leaf";
|
||||
@import "themes/blue";
|
||||
@import "themes/green";
|
||||
|
|
7
src/css/themes/blue.scss
Normal file
7
src/css/themes/blue.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
@import "blue/layout";
|
||||
@import "blue/pages/SurahIndex";
|
||||
@import "blue/pages/SurahStream";
|
||||
|
||||
.root .content.theme.blue.ar {
|
||||
direction: rtl;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.root .content.theme.moon {
|
||||
.root .content.theme.blue {
|
||||
@import "colors";
|
||||
|
||||
.row.title {
|
|
@ -1,5 +1,5 @@
|
|||
.root .content.theme.moon {
|
||||
@import "themes/moon/colors";
|
||||
.root .content.theme.blue {
|
||||
@import "themes/blue/colors";
|
||||
|
||||
.row.title {
|
||||
justify-content: center;
|
|
@ -1,6 +1,6 @@
|
|||
.root .content.theme.moon {
|
||||
@import "themes/moon/colors";
|
||||
@import "themes/moon/components/Icon";
|
||||
.root .content.theme.blue {
|
||||
@import "themes/blue/colors";
|
||||
@import "themes/blue/components/Icon";
|
||||
|
||||
.row.details {
|
||||
color: $gold-primary;
|
||||
|
@ -20,8 +20,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.root .content.theme.moon.ar {
|
||||
@import "themes/moon/colors";
|
||||
.root .content.theme.blue.ar {
|
||||
@import "themes/blue/colors";
|
||||
|
||||
.row.dropdown-row {
|
||||
line-height: 30px;
|
7
src/css/themes/green.scss
Normal file
7
src/css/themes/green.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
@import "green/layout";
|
||||
@import "green/pages/SurahIndex";
|
||||
@import "green/pages/SurahStream";
|
||||
|
||||
.root .content.theme.green.ar {
|
||||
direction: rtl;
|
||||
}
|
|
@ -1,11 +1,6 @@
|
|||
.root .content.theme.leaf {
|
||||
.root .content.theme.green {
|
||||
@import "colors";
|
||||
|
||||
.header .image {
|
||||
background-image: url("/images/leaf.svg");
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.row.title {
|
||||
color: $green;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
.root .content.theme.leaf {
|
||||
@import "themes/leaf/colors";
|
||||
.root .content.theme.green {
|
||||
@import "themes/green/colors";
|
||||
|
||||
ul.body.index a {
|
||||
&:active, &:link, &:visited {
|
|
@ -1,6 +1,6 @@
|
|||
.root .content.theme.leaf {
|
||||
@import "themes/leaf/colors";
|
||||
@import "themes/leaf/components/Icon";
|
||||
.root .content.theme.green {
|
||||
@import "themes/green/colors";
|
||||
@import "themes/green/components/Icon";
|
||||
|
||||
.row.details {
|
||||
color: $green;
|
||||
|
@ -33,8 +33,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.root .content.theme.leaf.ar {
|
||||
@import "themes/leaf/colors";
|
||||
.root .content.theme.green.ar {
|
||||
@import "themes/green/colors";
|
||||
|
||||
.row.dropdown-row {
|
||||
.surah-name {
|
|
@ -1,7 +0,0 @@
|
|||
@import "leaf/layout";
|
||||
@import "leaf/pages/SurahIndex";
|
||||
@import "leaf/pages/SurahStream";
|
||||
|
||||
.root .content.theme.leaf.ar {
|
||||
direction: rtl;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
@import "moon/layout";
|
||||
@import "moon/pages/SurahIndex";
|
||||
@import "moon/pages/SurahStream";
|
||||
|
||||
.root .content.theme.moon.ar {
|
||||
direction: rtl;
|
||||
}
|
|
@ -1,14 +1,21 @@
|
|||
import React from "react";
|
||||
import { Select, SelectOption } from "components/Select";
|
||||
import { Select } from "components/Select";
|
||||
|
||||
interface Props {
|
||||
locale: string;
|
||||
onChange: (o: SelectOption) => void;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export function LanguageSelect({ locale, onChange }: Props) {
|
||||
export function LanguageSelect({ locale, path = "" }: Props) {
|
||||
return (
|
||||
<Select value={locale} className="language" onChange={onChange}>
|
||||
<Select
|
||||
value={locale}
|
||||
className="language"
|
||||
onChange={(el: JSX.Element) => {
|
||||
const locale = el.props.value;
|
||||
location.replace([locale, path].join("/"));
|
||||
}}
|
||||
>
|
||||
<option value="ar">عربي</option>
|
||||
<option value="en">English</option>
|
||||
</Select>
|
||||
|
|
|
@ -5,43 +5,20 @@ export type ChangeEvent = React.MouseEvent<HTMLLIElement> & {
|
|||
target: HTMLLIElement;
|
||||
};
|
||||
|
||||
export interface SelectOption {
|
||||
innerText: string;
|
||||
value: string;
|
||||
reactEvent: ChangeEvent;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
children: JSX.Element[];
|
||||
onChange: (e: SelectOption) => void;
|
||||
onChange: (e: JSX.Element) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const findOption = (value: string, children: JSX.Element[]) => {
|
||||
const activeOption = children.find(o => o.props.value === value);
|
||||
if (activeOption) {
|
||||
return activeOption.props.children;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const createOption = (e: ChangeEvent, children: JSX.Element[]): SelectOption => {
|
||||
const { target } = e;
|
||||
const value = target.getAttribute("data-value")!;
|
||||
return {
|
||||
innerText: findOption(value, children),
|
||||
value,
|
||||
reactEvent: e,
|
||||
};
|
||||
const find = (option: string, options: JSX.Element[]) => {
|
||||
return options.find(o => o.props.value === option);
|
||||
};
|
||||
|
||||
export function Select({ value, children, onChange, className }: Props) {
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const [activeOption, setActiveOption] = useState<string | null>(
|
||||
findOption(value, children),
|
||||
);
|
||||
const [activeOption, setActiveOption] = useState<JSX.Element>(find(value, children));
|
||||
const openSelect = (e: React.MouseEvent<HTMLSpanElement>) => {
|
||||
e.stopPropagation();
|
||||
setOpen(true);
|
||||
|
@ -49,9 +26,9 @@ export function Select({ value, children, onChange, className }: Props) {
|
|||
const selectOption = (e: ChangeEvent) => {
|
||||
e.stopPropagation();
|
||||
const target: HTMLLIElement = e.target;
|
||||
const option = createOption(e, children);
|
||||
const option = find(String(target.value), children);
|
||||
onChange(option);
|
||||
setActiveOption(target.innerText);
|
||||
setActiveOption(option);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
|
@ -61,15 +38,19 @@ export function Select({ value, children, onChange, className }: Props) {
|
|||
|
||||
return (
|
||||
<div className={classnames("react-select", className)}>
|
||||
<span className="active-option" onClick={openSelect}>
|
||||
{activeOption}
|
||||
</span>
|
||||
<span
|
||||
className={classnames("active-option", activeOption.props.value)}
|
||||
onClick={openSelect}
|
||||
/>
|
||||
<ul hidden={!open}>
|
||||
{children.map((option: JSX.Element, key: number) => {
|
||||
return (
|
||||
<li key={key} data-value={option.props.value} onClick={selectOption}>
|
||||
{option.props.children}
|
||||
</li>
|
||||
<li
|
||||
key={key}
|
||||
data-value={option.props.value}
|
||||
className={option.props.value}
|
||||
onClick={selectOption}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { Select, SelectOption } from "components/Select";
|
||||
import { Select } from "components/Select";
|
||||
|
||||
interface Props {
|
||||
setTheme: (theme: string) => void;
|
||||
|
@ -11,14 +11,10 @@ export function ThemeSelect({ setTheme, theme }: Props) {
|
|||
<Select
|
||||
className="theme"
|
||||
value={theme}
|
||||
onChange={(o: SelectOption) => setTheme(o.value)}
|
||||
onChange={(o: JSX.Element) => setTheme(o.props.value)}
|
||||
>
|
||||
<option value="blue">
|
||||
<span className="blue" />
|
||||
</option>
|
||||
<option value="green">
|
||||
<span className="green" />
|
||||
</option>
|
||||
<option className="blue" value="blue" />
|
||||
<option className="green" value="green" />
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,13 +6,7 @@ const THEMES: Theme[] = ["blue", "green"];
|
|||
const DEFAULT_THEME = "blue";
|
||||
|
||||
export function useTheme(): [Theme, (t: string) => void] {
|
||||
const [theme, setTheme] = useState<Theme | null>(null);
|
||||
const cookie = getCookie("theme");
|
||||
|
||||
useEffect(() => {
|
||||
const _theme = THEMES.find((theme: Theme) => cookie === theme);
|
||||
setTheme(_theme || DEFAULT_THEME);
|
||||
}, []);
|
||||
const [theme, setTheme] = useState<Theme>(DEFAULT_THEME);
|
||||
|
||||
function _setTheme(newTheme: string) {
|
||||
const matchedTheme = THEMES.find((theme: Theme) => newTheme === theme);
|
||||
|
@ -22,5 +16,11 @@ export function useTheme(): [Theme, (t: string) => void] {
|
|||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const cookie = getCookie("theme");
|
||||
const _theme = THEMES.find((theme: Theme) => cookie === theme);
|
||||
_setTheme(_theme || DEFAULT_THEME);
|
||||
}, []);
|
||||
|
||||
return [theme, _setTheme];
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import classNames from "classnames";
|
|||
|
||||
import * as Quran from "lib/Quran";
|
||||
import { useTheme } from "hooks/useTheme";
|
||||
import { SelectOption } from "components/Select";
|
||||
import { ThemeSelect } from "components/ThemeSelect";
|
||||
import { LanguageSelect } from "components/LanguageSelect";
|
||||
import { i18n, formatNumber, TFunction } from "lib/i18n";
|
||||
|
@ -17,9 +16,6 @@ interface Props {
|
|||
|
||||
function SurahIndex({ locale, surahs, t }: Props) {
|
||||
const [theme, setTheme] = useTheme();
|
||||
const onLanguageChange = (o: SelectOption) => {
|
||||
document.location.replace(`/${o.value}/`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames("content", "theme", theme, locale)}>
|
||||
|
@ -28,7 +24,7 @@ function SurahIndex({ locale, surahs, t }: Props) {
|
|||
</a>
|
||||
<div className="row dropdown-row">
|
||||
<ThemeSelect theme={theme} setTheme={setTheme} />
|
||||
<LanguageSelect locale={locale} onChange={onLanguageChange} />
|
||||
<LanguageSelect locale={locale} />
|
||||
</div>
|
||||
<ul className="body index scroll-y">
|
||||
{surahs.map((surah, key) => (
|
||||
|
|
|
@ -5,7 +5,6 @@ import classNames from "classnames";
|
|||
import { useTheme } from "hooks/useTheme";
|
||||
import { Timer } from "components/Timer";
|
||||
import { Stream } from "components/Stream";
|
||||
import { SelectOption } from "components/Select";
|
||||
import { ThemeSelect } from "components/ThemeSelect";
|
||||
import { LanguageSelect } from "components/LanguageSelect";
|
||||
import { AudioControl } from "components/AudioControl";
|
||||
|
@ -41,15 +40,6 @@ function SurahStream({ node, recitations, locale, paused, t }: Props) {
|
|||
const readyToRender = stream.length > 0;
|
||||
const ayah = stream[stream.length - 1];
|
||||
const hasCompactLayout = ["ar"].includes(locale);
|
||||
const onLanguageChange = (o: SelectOption) => {
|
||||
const locale = o.value;
|
||||
const params = [["paused", isPaused ? "t" : null]];
|
||||
const query = params
|
||||
.filter(([, v]) => v)
|
||||
.flatMap(([k, v]) => `${k}=${v}`)
|
||||
.join("&");
|
||||
location.replace(`/${locale}/${surah.slug}/?${query}`);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setEndOfStream(false);
|
||||
|
@ -68,7 +58,7 @@ function SurahStream({ node, recitations, locale, paused, t }: Props) {
|
|||
{hasCompactLayout && (
|
||||
<span className="surah-name">{surah.localizedName}</span>
|
||||
)}
|
||||
<LanguageSelect locale={locale} onChange={onLanguageChange} />
|
||||
<LanguageSelect locale={locale} path={surah.slug} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
|
Loading…
Reference in a new issue