-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathtop-bar.tsx
More file actions
181 lines (170 loc) · 6.45 KB
/
top-bar.tsx
File metadata and controls
181 lines (170 loc) · 6.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import React from "react";
import { useMemo, useLayoutEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import logoWide from "src/assets/svg/logo-wide.svg";
import logoSquare from "src/assets/svg/logo-square.svg";
import { Image } from "src/components/image";
import { Link } from "src/components/link";
import { Locale, useLocale } from "src/components/locale";
import { DictionaryKeys } from "src/components/locale/dictionary";
import { changeLanguage } from "src/redux/actions/settings";
import { useAppSelector } from "src/redux/store";
import { stripLanguageCodeFromHRef } from "src/utils/website-language";
import { useSearchModal } from "src/utils/search-modal";
import { Language, LANGUAGES } from "@dzcode.io/models/dist/language";
export interface TopBarProps {
version: string;
links: Array<{ localeKey: DictionaryKeys<"navbar-section">; href: string }>;
}
type Theme = "light" | "dark";
export function TopBar({ version, links }: TopBarProps): JSX.Element {
const { pathname } = useLocation();
const languageLessPathname = useMemo(() => stripLanguageCodeFromHRef(pathname), [pathname]);
const activeIndex = useMemo(() => {
return links.findIndex(({ href }) => languageLessPathname.startsWith(href));
}, [languageLessPathname, links]);
const { showModal } = useSearchModal();
const selectedLanguageCode = useAppSelector((state) => state.settings.languageCode);
const { selectedLanguage, languageOptions } = useMemo(() => {
let selectedLanguage!: Language;
const languageOptions: Array<Language> = [];
LANGUAGES.forEach((language) => {
if (language.code === selectedLanguageCode) selectedLanguage = language;
else languageOptions.push(language);
});
return { selectedLanguage, languageOptions };
}, [selectedLanguageCode]);
const { localize } = useLocale();
const [theme, setTheme] = useState((): Theme => {
const savedTheme = localStorage.getItem("theme") || "";
if (["light", "dark"].includes(savedTheme)) return savedTheme as Theme;
return window.matchMedia?.("(prefers-color-scheme: light)")?.matches ? "light" : "dark";
});
useLayoutEffect(() => {
localStorage.setItem("theme", theme);
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
return (
<div className="bg-neutral">
<div className="m-auto flex max-w-7xl flex-row gap-4 p-4 items-center">
<Link href={"/"} className="flex lg:hidden">
<Image className="h-9" src={logoSquare} alt="DzCode i/o SVG Logo wide" />
</Link>
<Link href={`https://github.com/dzcode-io/dzcode.io/releases/tag/${version}`}>
{version}
</Link>
<div className="flex-1" />
<button className="btn btn-ghost btn-circle btn-sm lg:hidden" onClick={showModal}>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
</button>
<label className="input input-bordered input-sm hidden lg:flex items-center gap-2">
<input
type="text"
className="grow cursor-pointer"
placeholder={localize("navbar-section-search")}
onClick={showModal}
readOnly
onFocus={(e) => e.target.blur()}
/>
<kbd className="kbd kbd-sm">/</kbd>
</label>
<div className="dropdown dropdown-end">
<div tabIndex={0} role="button">
{selectedLanguage.label}
</div>
<ul tabIndex={0} className="menu dropdown-content z-[1] rounded-box bg-base-300">
{languageOptions.map(({ code, label }, index) => (
<li
key={index}
onClick={() => {
changeLanguage(code);
(document.activeElement as HTMLElement)?.blur();
}}
className="cursor-pointer"
>
<a>{label}</a>
</li>
))}
</ul>
</div>
<div>
<label htmlFor="theme-toggle" className="flex cursor-pointer gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
<input
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
id="theme-toggle"
type="checkbox"
value="dzcodeLight"
className="theme-controller toggle"
checked={theme === "light"}
/>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="5" />
<path d="M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4" />
</svg>
</label>
</div>
</div>
<div className="m-auto flex max-w-7xl flex-row justify-center gap-2 lg:justify-between">
<Link href={"/"} className="hidden lg:flex">
<Image
className="m-2 h-7 w-auto self-center"
src={logoWide}
alt="DzCode i/o SVG Logo wide"
/>
</Link>
<div
role="tablist"
className="tabs tabs-lifted tab-border-none pr-2 pl-2 tabs-lg overflow-x-auto"
>
{links.map(({ localeKey, href }, index) => (
<Link
href={href}
key={index}
role="tab"
className={`whitespace-nowrap tab ${activeIndex === index ? "tab-active" : ""}`}
data-testid={`top-bar-to:${href}`}
>
<Locale {...{ localeKey }} />
</Link>
))}
</div>
</div>
</div>
);
}