diff --git a/components/common/DropDown.js b/components/common/DropDown.js
index df559ef9..b6b2357b 100644
--- a/components/common/DropDown.js
+++ b/components/common/DropDown.js
@@ -15,6 +15,7 @@ export default function DropDown({
}) {
const [showMenu, setShowMenu] = useState(false);
const ref = useRef();
+ const selectedOption = options.find(e => e.value === value);
function handleShowMenu() {
setShowMenu(state => !state);
@@ -40,7 +41,13 @@ export default function DropDown({
} className={styles.icon} size="small" />
{showMenu && (
-
onSelect(value, e)}
diff --git a/components/common/Menu.module.css b/components/common/Menu.module.css
index 65551837..369e37c8 100644
--- a/components/common/Menu.module.css
+++ b/components/common/Menu.module.css
@@ -44,3 +44,7 @@
.divider {
border-top: 1px solid var(--gray300);
}
+
+.selected {
+ font-weight: 600;
+}
diff --git a/components/common/MenuButton.js b/components/common/MenuButton.js
new file mode 100644
index 00000000..4798ab1d
--- /dev/null
+++ b/components/common/MenuButton.js
@@ -0,0 +1,58 @@
+import React, { useState, useRef } from 'react';
+import classNames from 'classnames';
+import Menu from 'components/common/Menu';
+import Button from 'components/common/Button';
+import useDocumentClick from 'hooks/useDocumentClick';
+import styles from './MenuButton.module.css';
+
+export default function MenuButton({
+ icon,
+ value,
+ options,
+ menuPosition = 'bottom',
+ menuAlign = 'right',
+ onSelect,
+ renderValue,
+}) {
+ const [showMenu, setShowMenu] = useState(false);
+ const ref = useRef();
+ const selectedOption = options.find(e => e.value === value);
+
+ function handleSelect(value) {
+ onSelect(value);
+ setShowMenu(false);
+ }
+
+ function toggleMenu() {
+ setShowMenu(state => !state);
+ }
+
+ useDocumentClick(e => {
+ if (!ref.current.contains(e.target)) {
+ setShowMenu(false);
+ }
+ });
+
+ return (
+
+
+ {showMenu && (
+
+ )}
+
+ );
+}
diff --git a/components/settings/LanguageButton.module.css b/components/common/MenuButton.module.css
similarity index 100%
rename from components/settings/LanguageButton.module.css
rename to components/common/MenuButton.module.css
diff --git a/components/common/UserButton.module.css b/components/common/UserButton.module.css
deleted file mode 100644
index a0b2c2ab..00000000
--- a/components/common/UserButton.module.css
+++ /dev/null
@@ -1,25 +0,0 @@
-.container {
- display: flex;
- position: relative;
- cursor: pointer;
-}
-
-.username {
- border-bottom: 1px solid var(--gray500);
-}
-
-.username:hover {
- background: var(--gray50);
-}
-
-.menu {
- z-index: 100;
-}
-
-.open {
- background: var(--gray200);
-}
-
-.open:hover {
- background: var(--gray200);
-}
diff --git a/components/layout/Header.js b/components/layout/Header.js
index ee027ee9..639db7ea 100644
--- a/components/layout/Header.js
+++ b/components/layout/Header.js
@@ -3,10 +3,10 @@ import { FormattedMessage } from 'react-intl';
import { useSelector } from 'react-redux';
import classNames from 'classnames';
import Link from 'components/common/Link';
-import UserButton from 'components/common/UserButton';
import Icon from 'components/common/Icon';
import LanguageButton from 'components/settings/LanguageButton';
import ThemeButton from 'components/settings/ThemeButton';
+import UserButton from 'components/settings/UserButton';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
diff --git a/components/settings/LanguageButton.js b/components/settings/LanguageButton.js
index 63a09712..e643f070 100644
--- a/components/settings/LanguageButton.js
+++ b/components/settings/LanguageButton.js
@@ -1,35 +1,17 @@
-import React, { useState, useRef } from 'react';
-import classNames from 'classnames';
+import React from 'react';
import Head from 'next/head';
-import Menu from 'components/common/Menu';
-import Button from 'components/common/Button';
import { menuOptions } from 'lib/lang';
import useLocale from 'hooks/useLocale';
-import useDocumentClick from 'hooks/useDocumentClick';
+import MenuButton from 'components/common/MenuButton';
import Globe from 'assets/globe.svg';
-import styles from './LanguageButton.module.css';
-export default function LanguageButton({ menuPosition = 'bottom', menuAlign = 'left' }) {
- const [showMenu, setShowMenu] = useState(false);
+export default function LanguageButton() {
const [locale, setLocale] = useLocale();
- const ref = useRef();
- const selectedLocale = menuOptions.find(e => e.value === locale)?.display;
function handleSelect(value) {
setLocale(value);
- setShowMenu(false);
}
- function toggleMenu() {
- setShowMenu(state => !state);
- }
-
- useDocumentClick(e => {
- if (!ref.current.contains(e.target)) {
- setShowMenu(false);
- }
- });
-
return (
<>
@@ -46,25 +28,13 @@ export default function LanguageButton({ menuPosition = 'bottom', menuAlign = 'l
/>
)}
-
-
}
- className={classNames({ [styles.open]: showMenu })}
- onClick={toggleMenu}
- variant="light"
- >
-
{selectedLocale}
-
- {showMenu && (
-
- )}
-
+
}
+ options={menuOptions}
+ value={locale}
+ renderValue={option => option?.display}
+ onSelect={handleSelect}
+ />
>
);
}
diff --git a/components/settings/ThemeButton.js b/components/settings/ThemeButton.js
index 2c036079..a31440b7 100644
--- a/components/settings/ThemeButton.js
+++ b/components/settings/ThemeButton.js
@@ -13,12 +13,12 @@ export default function ThemeButton() {
const transitions = useTransition(theme, theme => theme, {
from: {
opacity: 0,
- transform: `translateY(${theme === 'light' ? '-20px' : '20px'}) scale(0.5)`,
+ transform: `translateY(${theme === 'light' ? '20px' : '-20px'}) scale(0.5)`,
},
enter: { opacity: 1, transform: 'translateY(0px) scale(1)' },
leave: {
opacity: 0,
- transform: `translateY(${theme === 'light' ? '20px' : '-20px'}) scale(0.5)`,
+ transform: `translateY(${theme === 'light' ? '-20px' : '20px'}) scale(0.5)`,
},
});
diff --git a/components/common/UserButton.js b/components/settings/UserButton.js
similarity index 52%
rename from components/common/UserButton.js
rename to components/settings/UserButton.js
index a4e70276..c7604cb5 100644
--- a/components/common/UserButton.js
+++ b/components/settings/UserButton.js
@@ -1,20 +1,15 @@
-import React, { useState, useRef } from 'react';
+import React from 'react';
import { FormattedMessage } from 'react-intl';
import { useSelector } from 'react-redux';
import { useRouter } from 'next/router';
-import Menu from './Menu';
-import Icon from './Icon';
-import Button from './Button';
-import useDocumentClick from 'hooks/useDocumentClick';
+import MenuButton from 'components/common/MenuButton';
+import Icon from 'components/common/Icon';
import User from 'assets/user.svg';
import Chevron from 'assets/chevron-down.svg';
import styles from './UserButton.module.css';
-import classNames from 'classnames';
export default function UserButton() {
- const [showMenu, setShowMenu] = useState(false);
const user = useSelector(state => state.user);
- const ref = useRef();
const router = useRouter();
const menuOptions = [
@@ -34,8 +29,6 @@ export default function UserButton() {
];
function handleSelect(value) {
- setShowMenu(false);
-
if (value === 'logout') {
router.push('/logout');
} else if (value === 'profile') {
@@ -43,32 +36,12 @@ export default function UserButton() {
}
}
- useDocumentClick(e => {
- if (!ref.current.contains(e.target)) {
- setShowMenu(false);
- }
- });
-
return (
-
- }
- className={classNames({ [styles.open]: showMenu })}
- onClick={() => setShowMenu(state => !state)}
- size="large"
- variant="light"
- >
- } size="small" />
-
- {showMenu && (
-
- )}
-
+
} size="large" />}
+ value={
} size="small" />}
+ options={menuOptions}
+ onSelect={handleSelect}
+ />
);
}
diff --git a/components/settings/UserButton.module.css b/components/settings/UserButton.module.css
new file mode 100644
index 00000000..dbb616ff
--- /dev/null
+++ b/components/settings/UserButton.module.css
@@ -0,0 +1,7 @@
+.username {
+ border-bottom: 1px solid var(--gray500);
+}
+
+.username:hover {
+ background: var(--gray50);
+}
diff --git a/lib/lang.js b/lib/lang.js
index 54dfa6de..58c38e33 100644
--- a/lib/lang.js
+++ b/lib/lang.js
@@ -44,18 +44,18 @@ export const dateLocales = {
};
export const menuOptions = [
- { label: 'English', value: 'en-US', display: 'EN' },
- { label: '中文', value: 'zh-CN', display: 'CN' },
- { label: 'Dansk', value: 'da-DK', display: 'DA' },
- { label: 'Deutsch', value: 'de-DE', display: 'DE' },
- { label: 'Español', value: 'es-MX', display: 'ES' },
- { label: 'Français', value: 'fr-FR', display: 'FR' },
- { label: '日本語', value: 'ja-JP', display: 'JA' },
- { label: 'Монгол', value: 'mn-MN', display: 'MN' },
- { label: 'Nederlands', value: 'nl-NL', display: 'NL' },
- { label: 'Русский', value: 'ru-RU', display: 'RU' },
- { label: 'Svenska', value: 'sv-SE', display: 'SV' },
- { label: 'Turkish', value: 'tr-TR', display: 'TR' },
+ { label: 'English', value: 'en-US', display: 'en' },
+ { label: '中文', value: 'zh-CN', display: 'cn' },
+ { label: 'Dansk', value: 'da-DK', display: 'da' },
+ { label: 'Deutsch', value: 'de-DE', display: 'de' },
+ { label: 'Español', value: 'es-MX', display: 'es' },
+ { label: 'Français', value: 'fr-FR', display: 'fr' },
+ { label: '日本語', value: 'ja-JP', display: 'ja' },
+ { label: 'Монгол', value: 'mn-MN', display: 'mn' },
+ { label: 'Nederlands', value: 'nl-NL', display: 'nl' },
+ { label: 'Русский', value: 'ru-RU', display: 'ru' },
+ { label: 'Svenska', value: 'sv-SE', display: 'sv' },
+ { label: 'Turkish', value: 'tr-TR', display: 'tr' },
];
export function dateFormat(date, str, locale) {
diff --git a/package.json b/package.json
index a87cb324..fdcd512b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "umami",
- "version": "0.45.0",
+ "version": "0.46.0",
"description": "A simple, fast, website analytics alternative to Google Analytics. ",
"author": "Mike Cao
",
"license": "MIT",