diff --git a/.eslintrc.json b/.eslintrc.json index f279e226..de639b85 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -20,7 +20,7 @@ "next" ], - "plugins": ["@typescript-eslint","prettier"], + "plugins": ["@typescript-eslint", "prettier"], "settings": { "import/resolver": { "alias": { @@ -46,7 +46,9 @@ "react/react-in-jsx-scope": "off", "react/prop-types": "off", "import/no-anonymous-default-export": "off", - "@next/next/no-img-element": "off" + "@next/next/no-img-element": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-explicit-any": "off" }, "globals": { "React": "writable" diff --git a/components/common/EventDataButton.js b/components/common/EventDataButton.js index 5393d7d5..1b95f950 100644 --- a/components/common/EventDataButton.js +++ b/components/common/EventDataButton.js @@ -1,5 +1,4 @@ import EventDataForm from 'components/metrics/EventDataForm'; -import PropTypes from 'prop-types'; import { useState } from 'react'; import { Button, Icon, Modal, Icons } from 'react-basics'; import { FormattedMessage } from 'react-intl'; @@ -44,8 +43,4 @@ function EventDataButton({ websiteId }) { ); } -EventDataButton.propTypes = { - websiteId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), -}; - export default EventDataButton; diff --git a/components/common/Favicon.js b/components/common/Favicon.js index ad7056af..2127d482 100644 --- a/components/common/Favicon.js +++ b/components/common/Favicon.js @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import styles from './Favicon.module.css'; function getHostName(url) { @@ -20,8 +19,4 @@ function Favicon({ domain, ...props }) { ) : null; } -Favicon.propTypes = { - domain: PropTypes.string, -}; - export default Favicon; diff --git a/components/common/FilterButtons.js b/components/common/FilterButtons.js index b4b42291..4d08a132 100644 --- a/components/common/FilterButtons.js +++ b/components/common/FilterButtons.js @@ -1,24 +1,11 @@ -import PropTypes from 'prop-types'; -import ButtonLayout from 'components/layout/ButtonLayout'; -import { ButtonGroup } from 'react-basics'; +import { ButtonGroup, Button, Flexbox } from 'react-basics'; -function FilterButtons({ buttons, selected, onClick }) { +export default function FilterButtons({ items, selectedKey, onSelect }) { return ( - - - + + + {({ key, label }) => } + + ); } - -FilterButtons.propTypes = { - buttons: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node, - value: PropTypes.any.isRequired, - }), - ), - selected: PropTypes.any, - onClick: PropTypes.func, -}; - -export default FilterButtons; diff --git a/components/common/Icon.js b/components/common/Icon.js deleted file mode 100644 index 362d5376..00000000 --- a/components/common/Icon.js +++ /dev/null @@ -1,28 +0,0 @@ -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import styles from './Icon.module.css'; - -function Icon({ icon, className, size = 'medium', ...props }) { - return ( -
- {icon} -
- ); -} - -Icon.propTypes = { - className: PropTypes.string, - icon: PropTypes.node.isRequired, - size: PropTypes.oneOf(['xlarge', 'large', 'medium', 'small', 'xsmall']), -}; - -export default Icon; diff --git a/components/common/Icon.module.css b/components/common/Icon.module.css deleted file mode 100644 index 5b431668..00000000 --- a/components/common/Icon.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.icon { - display: inline-flex; - justify-content: center; - align-items: center; - vertical-align: middle; -} - -.icon svg { - fill: currentColor; -} - -.xlarge > svg { - width: 48px; - height: 48px; -} - -.large > svg { - width: 24px; - height: 24px; -} - -.medium > svg { - width: 16px; - height: 16px; -} - -.small > svg { - width: 12px; - height: 12px; -} - -.xsmall > svg { - width: 10px; - height: 10px; -} diff --git a/components/common/Link.js b/components/common/Link.js deleted file mode 100644 index 3721a9a7..00000000 --- a/components/common/Link.js +++ /dev/null @@ -1,34 +0,0 @@ -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import NextLink from 'next/link'; -import { Icon } from 'react-basics'; -import styles from './Link.module.css'; - -function Link({ className, icon, children, size, iconRight, onClick, ...props }) { - return ( - - - {icon && } - {children} - - - ); -} - -Link.propTypes = { - className: PropTypes.string, - icon: PropTypes.node, - children: PropTypes.node, - size: PropTypes.oneOf(['large', 'small', 'xsmall']), - iconRight: PropTypes.bool, -}; - -export default Link; diff --git a/components/common/Link.module.css b/components/common/Link.module.css deleted file mode 100644 index 0476bd92..00000000 --- a/components/common/Link.module.css +++ /dev/null @@ -1,42 +0,0 @@ -a.link, -a.link:active, -a.link:visited { - position: relative; - color: var(--base900); - text-decoration: none; - display: inline-flex; - align-items: center; -} - -a.link span { - border-bottom: 2px solid transparent; -} - -a.link:hover span { - border-bottom: 2px solid var(--primary400); -} - -a.link.large { - font-size: var(--font-size-lg); -} - -a.link.small { - font-size: var(--font-size-sm); -} - -a.link.xsmall { - font-size: var(--font-size-xs); -} - -a.link .icon + * { - margin-left: 10px; -} - -a.link.iconRight .icon { - order: 1; - margin-left: 10px; -} - -a.link.iconRight .icon + * { - margin: 0; -} diff --git a/components/common/Menu.js b/components/common/Menu.js deleted file mode 100644 index 771ed05d..00000000 --- a/components/common/Menu.js +++ /dev/null @@ -1,69 +0,0 @@ -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import styles from './Menu.module.css'; - -function Menu({ - options = [], - selectedOption, - className, - float, - align = 'left', - optionClassName, - selectedClassName, - onSelect = () => {}, -}) { - return ( -
- {options - .filter(({ hidden }) => !hidden) - .map(option => { - const { label, value, className: customClassName, render, divider } = option; - - return render ? ( - render(option) - ) : ( -
onSelect(value, e)} - > - {label} -
- ); - })} -
- ); -} - -Menu.propTypes = { - options: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node, - value: PropTypes.any, - className: PropTypes.string, - render: PropTypes.func, - divider: PropTypes.bool, - }), - ), - selectedOption: PropTypes.any, - className: PropTypes.string, - float: PropTypes.oneOf(['top', 'bottom']), - align: PropTypes.oneOf(['left', 'right']), - optionClassName: PropTypes.string, - selectedClassName: PropTypes.string, - onSelect: PropTypes.func, -}; - -export default Menu; diff --git a/components/common/Menu.module.css b/components/common/Menu.module.css deleted file mode 100644 index d9a23371..00000000 --- a/components/common/Menu.module.css +++ /dev/null @@ -1,49 +0,0 @@ -.menu { - background: var(--base50); - border: 1px solid var(--base500); - border-radius: 4px; - overflow: hidden; - z-index: 100; -} - -.option { - background: var(--base50); - padding: 4px 16px; - cursor: pointer; - white-space: nowrap; -} - -.option:hover { - background: var(--base100); -} - -.float { - position: absolute; - min-width: 100px; -} - -.top { - bottom: 100%; - margin-bottom: 5px; -} - -.bottom { - top: 100%; - margin-top: 5px; -} - -.left { - left: 0; -} - -.right { - right: 0; -} - -.divider { - border-top: 1px solid var(--base300); -} - -.selected { - font-weight: 600; -} diff --git a/components/common/MenuButton.js b/components/common/MenuButton.js deleted file mode 100644 index 69a448d0..00000000 --- a/components/common/MenuButton.js +++ /dev/null @@ -1,87 +0,0 @@ -import { useState, useRef } from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import Menu from 'components/common/Menu'; -import useDocumentClick from 'hooks/useDocumentClick'; -import styles from './MenuButton.module.css'; -import { Button } from 'react-basics'; - -function MenuButton({ - children, - value, - options, - buttonClassName, - buttonVariant, - menuClassName, - menuPosition = 'bottom', - menuAlign = 'right', - onSelect, - renderValue, - hideLabel, -}) { - 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 && ( - - )} -
- ); -} - -MenuButton.propTypes = { - icon: PropTypes.node, - value: PropTypes.any, - options: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node, - value: PropTypes.any, - className: PropTypes.string, - render: PropTypes.func, - divider: PropTypes.bool, - }), - ), - buttonClassName: PropTypes.string, - menuClassName: PropTypes.string, - menuPosition: PropTypes.oneOf(['top', 'bottom']), - menuAlign: PropTypes.oneOf(['left', 'right']), - onSelect: PropTypes.func, - renderValue: PropTypes.func, -}; - -export default MenuButton; diff --git a/components/common/MenuButton.module.css b/components/common/MenuButton.module.css deleted file mode 100644 index a59ae562..00000000 --- a/components/common/MenuButton.module.css +++ /dev/null @@ -1,20 +0,0 @@ -.container { - display: flex; - position: relative; - cursor: pointer; -} - -.button { - border: 1px solid transparent; - border-radius: 4px; -} - -.text { - font-size: var(--font-size-sm); -} - -.open, -.open:hover { - background: var(--base50); - border: 1px solid var(--base500); -} diff --git a/components/common/NavMenu.js b/components/common/NavMenu.js deleted file mode 100644 index 8994daa0..00000000 --- a/components/common/NavMenu.js +++ /dev/null @@ -1,46 +0,0 @@ -import PropTypes from 'prop-types'; -import { useRouter } from 'next/router'; -import classNames from 'classnames'; -import styles from './NavMenu.module.css'; - -function NavMenu({ options = [], className, onSelect = () => {} }) { - const router = useRouter(); - - return ( -
- {options - .filter(({ hidden }) => !hidden) - .map(option => { - const { label, value, className: customClassName, render } = option; - - return render ? ( - render(option) - ) : ( -
onSelect(value, e)} - > - {label} -
- ); - })} -
- ); -} - -NavMenu.propTypes = { - options: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node, - value: PropTypes.any, - className: PropTypes.string, - render: PropTypes.func, - }), - ), - className: PropTypes.string, - onSelect: PropTypes.func, -}; -export default NavMenu; diff --git a/components/common/NavMenu.module.css b/components/common/NavMenu.module.css deleted file mode 100644 index e0b01945..00000000 --- a/components/common/NavMenu.module.css +++ /dev/null @@ -1,22 +0,0 @@ -.menu { - color: var(--base800); - border: 1px solid var(--base500); - border-radius: 4px; - overflow: hidden; - z-index: 2; -} - -.option { - padding: 8px 16px; - cursor: pointer; - border-radius: 4px; -} - -.option:hover { - background: var(--base75); -} - -.selected { - color: var(--base900); - font-weight: 600; -} diff --git a/components/common/NoData.js b/components/common/NoData.js index 4ee593ae..4a23675d 100644 --- a/components/common/NoData.js +++ b/components/common/NoData.js @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import classNames from 'classnames'; import { FormattedMessage } from 'react-intl'; import styles from './NoData.module.css'; @@ -11,8 +10,4 @@ function NoData({ className }) { ); } -NoData.propTypes = { - className: PropTypes.string, -}; - export default NoData; diff --git a/components/common/OverflowText.js b/components/common/OverflowText.js index 9be705b8..aaeb9c46 100644 --- a/components/common/OverflowText.js +++ b/components/common/OverflowText.js @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { useCallback, useEffect, useRef, useState } from 'react'; import ReactTooltip from 'react-tooltip'; @@ -58,9 +57,4 @@ const OverflowText = ({ children, tooltipId }) => { ); }; -OverflowText.propTypes = { - children: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - tooltipId: PropTypes.string.isRequired, -}; - export default OverflowText; diff --git a/components/common/Tag.js b/components/common/Tag.js index b9005d86..d30f0d0f 100644 --- a/components/common/Tag.js +++ b/components/common/Tag.js @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import classNames from 'classnames'; import styles from './Tag.module.css'; @@ -6,9 +5,4 @@ function Tag({ className, children }) { return {children}; } -Tag.propTypes = { - className: PropTypes.string, - children: PropTypes.node, -}; - export default Tag; diff --git a/components/common/WorldMap.js b/components/common/WorldMap.js index 55ee3109..8afd84e0 100644 --- a/components/common/WorldMap.js +++ b/components/common/WorldMap.js @@ -1,6 +1,5 @@ import { useState, useMemo } from 'react'; import { useRouter } from 'next/router'; -import PropTypes from 'prop-types'; import ReactTooltip from 'react-tooltip'; import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps'; import classNames from 'classnames'; @@ -89,15 +88,4 @@ function WorldMap({ data, className }) { ); } -WorldMap.propTypes = { - data: PropTypes.arrayOf( - PropTypes.shape({ - x: PropTypes.string, - y: PropTypes.number, - z: PropTypes.number, - }), - ), - className: PropTypes.string, -}; - export default WorldMap; diff --git a/components/helpers/StickyHeader.js b/components/helpers/StickyHeader.js index 7f97b12d..29daeb51 100644 --- a/components/helpers/StickyHeader.js +++ b/components/helpers/StickyHeader.js @@ -1,48 +1,32 @@ -import { useState, useRef, useEffect } from 'react'; +import { useEffect, useRef } from 'react'; +import { useMeasure, useCombinedRefs } from 'react-basics'; import classNames from 'classnames'; +import useSticky from 'hooks/useSticky'; +import { UI_LAYOUT_BODY } from 'lib/constants'; export default function StickyHeader({ className, stickyClassName, stickyStyle, - children, enabled = true, + children, }) { - const [sticky, setSticky] = useState(false); - const ref = useRef(); - const top = useRef(0); - - useEffect(() => { - const checkPosition = () => { - if (ref.current) { - if (!top.current) { - top.current = ref.current.offsetTop + ref.current.offsetHeight; - } - const state = window.pageYOffset > top.current; - if (sticky !== state) { - setSticky(state); - } - } - }; - - if (enabled) { - checkPosition(); - window.addEventListener('scroll', checkPosition); - } - - return () => { - window.removeEventListener('scroll', checkPosition); - }; - }, [sticky, enabled]); + const { ref: scrollRef, isSticky } = useSticky({ scrollElementId: UI_LAYOUT_BODY }); + const { ref: measureRef, dimensions } = useMeasure(); return (
- {children} +
+ {children} +
); } diff --git a/components/input/LanguageButton.js b/components/input/LanguageButton.js index 687f639f..f01b4716 100644 --- a/components/input/LanguageButton.js +++ b/components/input/LanguageButton.js @@ -18,14 +18,13 @@ export default function LanguageButton({ tooltipPosition = 'top' }) { return ( - + - {formatMessage(labels.language)} - +
{items.map(({ value, label }) => { diff --git a/components/input/LogoutButton.js b/components/input/LogoutButton.js index f6f2450e..31b4317d 100644 --- a/components/input/LogoutButton.js +++ b/components/input/LogoutButton.js @@ -1,4 +1,4 @@ -import { Button, Icon, Icons, PopupTrigger, Tooltip } from 'react-basics'; +import { Button, Icon, Icons, Tooltip } from 'react-basics'; import Link from 'next/link'; import { labels } from 'components/messages'; import { useIntl } from 'react-intl'; @@ -8,14 +8,13 @@ export default function LogoutButton({ tooltipPosition = 'top' }) { return ( - + - {formatMessage(labels.logout)} - + ); diff --git a/components/input/RefreshButton.js b/components/input/RefreshButton.js index b223fecf..635a6270 100644 --- a/components/input/RefreshButton.js +++ b/components/input/RefreshButton.js @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; import { useIntl } from 'react-intl'; -import { Button, Icon, Tooltip } from '../react-basics'; +import { Button, Icon, Tooltip } from 'react-basics'; import useStore from 'store/queries'; import { setDateRange } from 'store/websites'; import useDateRange from 'hooks/useDateRange'; diff --git a/components/layout/AppLayout.js b/components/layout/AppLayout.js index 2b5f0b92..d89c34b9 100644 --- a/components/layout/AppLayout.js +++ b/components/layout/AppLayout.js @@ -2,6 +2,7 @@ import { Container } from 'react-basics'; import Head from 'next/head'; import NavBar from 'components/layout/NavBar'; import useRequireLogin from 'hooks/useRequireLogin'; +import { UI_LAYOUT_BODY } from 'lib/constants'; import styles from './AppLayout.module.css'; export default function AppLayout({ title, children }) { @@ -19,7 +20,7 @@ export default function AppLayout({ title, children }) {
-
+
{children}
diff --git a/components/layout/AppLayout.module.css b/components/layout/AppLayout.module.css index 71d61ada..746ada39 100644 --- a/components/layout/AppLayout.module.css +++ b/components/layout/AppLayout.module.css @@ -2,8 +2,6 @@ display: grid; grid-template-rows: 1fr; grid-template-columns: max-content 1fr; - height: 100vh; - overflow: hidden; } .nav { @@ -13,4 +11,5 @@ .body { grid-area: 1 / 2; overflow: auto; + height: 100vh; } diff --git a/components/layout/ButtonLayout.js b/components/layout/ButtonLayout.js deleted file mode 100644 index dd6a390d..00000000 --- a/components/layout/ButtonLayout.js +++ /dev/null @@ -1,16 +0,0 @@ -import classNames from 'classnames'; -import styles from './ButtonLayout.module.css'; - -export default function ButtonLayout({ className, children, align = 'center' }) { - return ( -
- {children} -
- ); -} diff --git a/components/layout/ButtonLayout.module.css b/components/layout/ButtonLayout.module.css deleted file mode 100644 index 5b979360..00000000 --- a/components/layout/ButtonLayout.module.css +++ /dev/null @@ -1,21 +0,0 @@ -.buttons { - display: flex; - align-items: center; - width: 100%; -} - -.buttons button + * { - margin-left: 10px; -} - -.center { - justify-content: center; -} - -.left { - justify-content: flex-start; -} - -.right { - justify-content: flex-end; -} diff --git a/components/layout/Footer.js b/components/layout/Footer.js index ec1571fb..452e070b 100644 --- a/components/layout/Footer.js +++ b/components/layout/Footer.js @@ -3,7 +3,6 @@ import { useRouter } from 'next/router'; import Script from 'next/script'; import classNames from 'classnames'; import { useIntl, defineMessages } from 'react-intl'; -import Link from 'components/common/Link'; import { CURRENT_VERSION, HOMEPAGE_URL, REPO_URL } from 'lib/constants'; import styles from './Footer.module.css'; @@ -22,15 +21,15 @@ export default function Footer({ className }) {
{formatMessage(messages.poweredBy, { name: ( - + umami - + ), })}
- {`v${CURRENT_VERSION}`} + {`v${CURRENT_VERSION}`} {!pathname.includes('/share/') &&