diff --git a/crabfit-frontend/src/App.tsx b/crabfit-frontend/src/App.tsx
index a31812d..743c72f 100644
--- a/crabfit-frontend/src/App.tsx
+++ b/crabfit-frontend/src/App.tsx
@@ -107,6 +107,11 @@ const App = () => {
},
})}
/>
+
+ }>
+
+
+
(
}>
@@ -135,10 +140,6 @@ const App = () => {
)} />
- }>
-
-
-
{eggVisible && setEggVisible(false)} />}
diff --git a/crabfit-frontend/src/components/Donate/Donate.tsx b/crabfit-frontend/src/components/Donate/Donate.tsx
index 03dabfe..d245e57 100644
--- a/crabfit-frontend/src/components/Donate/Donate.tsx
+++ b/crabfit-frontend/src/components/Donate/Donate.tsx
@@ -1,15 +1,33 @@
-import { useEffect } from 'react';
+import { useState, useEffect, useRef } from 'react';
import { Button } from 'components';
import { useTWAStore } from 'stores';
import { useTranslation } from 'react-i18next';
+import {
+ Wrapper,
+ Options,
+} from './donateStyle';
+
const PAYMENT_METHOD = 'https://play.google.com/billing';
const SKU = 'crab_donation';
-const Donate = ({ onDonate = null }) => {
+const Donate = () => {
const store = useTWAStore();
const { t } = useTranslation('common');
+ const firstLinkRef = useRef();
+ const buttonRef = useRef();
+ const modalRef = useRef();
+ const [isOpen, _setIsOpen] = useState(false);
+
+ const setIsOpen = open => {
+ _setIsOpen(open);
+
+ if (open) {
+ window.setTimeout(() => firstLinkRef.current.focus(), 150);
+ }
+ };
+
useEffect(() => {
if (store.TWA === undefined) {
store.setTWA(document.referrer.includes('android-app://fit.crab'));
@@ -71,7 +89,7 @@ const Donate = ({ onDonate = null }) => {
};
return (
-
+
+ {
+ if (modalRef.current.contains(e.relatedTarget)) return;
+ setIsOpen(false);
+ }}
+ >
+ setIsOpen(false)} ref={firstLinkRef} href="https://www.paypal.com/donate?business=N89X6YXRT5HKW&item_name=Crab+Fit+Donation¤cy_code=AUD&amount=2" target="_blank" rel="noreferrer">{t('donate.options.$2')}
+ setIsOpen(false)} href="https://www.paypal.com/donate?business=N89X6YXRT5HKW&item_name=Crab+Fit+Donation¤cy_code=AUD&amount=5" target="_blank" rel="noreferrer">{t('donate.options.$5')}
+ setIsOpen(false)} href="https://www.paypal.com/donate?business=N89X6YXRT5HKW&item_name=Crab+Fit+Donation¤cy_code=AUD&amount=10" target="_blank" rel="noreferrer">{t('donate.options.$10')}
+ setIsOpen(false)} href="https://www.paypal.com/donate?business=N89X6YXRT5HKW&item_name=Crab+Fit+Donation¤cy_code=AUD" target="_blank" rel="noreferrer">{t('donate.options.choose')}
+
+
);
}
diff --git a/crabfit-frontend/src/components/Donate/donateStyle.ts b/crabfit-frontend/src/components/Donate/donateStyle.ts
new file mode 100644
index 0000000..280813f
--- /dev/null
+++ b/crabfit-frontend/src/components/Donate/donateStyle.ts
@@ -0,0 +1,52 @@
+import styled from '@emotion/styled';
+
+export const Wrapper = styled.div`
+ margin-top: 6px;
+ margin-left: 12px;
+ position: relative;
+`;
+
+export const Options = styled.div`
+ position: absolute;
+ bottom: calc(100% + 20px);
+ right: 0;
+ background-color: ${props => props.theme.background};
+ ${props => props.theme.mode === 'dark' && `
+ border: 1px solid ${props.theme.primaryBackground};
+ `}
+ z-index: 60;
+ padding: 4px 10px;
+ border-radius: 14px;
+ box-sizing: border-box;
+ max-width: calc(100vw - 20px);
+ box-shadow: 0 3px 6px 0 rgba(0,0,0,.3);
+
+ visibility: hidden;
+ pointer-events: none;
+ opacity: 0;
+ transform: translateY(5px);
+ transition: opacity .15s, transform .15s, visibility .15s;
+
+ ${props => props.isOpen && `
+ pointer-events: all;
+ opacity: 1;
+ transform: translateY(0);
+ visibility: visible;
+ `}
+
+ & a {
+ display: block;
+ white-space: nowrap;
+ text-align: center;
+ padding: 4px 20px;
+ margin: 6px 0;
+ text-decoration: none;
+ border-radius: 100px;
+ background-color: ${props => props.theme.primary};
+ color: ${props => props.theme.background};
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+`;
diff --git a/crabfit-frontend/src/components/Footer/Footer.tsx b/crabfit-frontend/src/components/Footer/Footer.tsx
index cbbfcef..8603aaf 100644
--- a/crabfit-frontend/src/components/Footer/Footer.tsx
+++ b/crabfit-frontend/src/components/Footer/Footer.tsx
@@ -1,28 +1,15 @@
-import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Donate } from 'components';
-import { Wrapper, Link } from './footerStyle';
+import { Wrapper } from './footerStyle';
const Footer = (props) => {
- const [donateMode, setDonateMode] = useState(false);
const { t } = useTranslation('common');
return (
-
- {donateMode ? (
- <>
- {t('donate.options.$2')}
- {t('donate.options.$5')}
- {t('donate.options.$10')}
- {t('donate.options.choose')}
- >
- ) : (
- <>
- {t('donate.info')}
- setDonateMode(true)} />
- >
- )}
+
+ {t('donate.info')}
+
);
};
diff --git a/crabfit-frontend/src/components/Footer/footerStyle.ts b/crabfit-frontend/src/components/Footer/footerStyle.ts
index dd03a69..4e04db7 100644
--- a/crabfit-frontend/src/components/Footer/footerStyle.ts
+++ b/crabfit-frontend/src/components/Footer/footerStyle.ts
@@ -19,20 +19,4 @@ export const Wrapper = styled.footer`
margin-bottom: 20px;
}
`}
-
- ${props => props.donateMode && `
- display: flex;
- align-items: center;
- justify-content: space-between;
- flex-wrap: wrap;
- `}
-`;
-
-export const Link = styled.a`
- padding: 11px 10px;
- white-space: nowrap;
-
- & strong {
- font-weight: 800;
- }
`;
diff --git a/crabfit-frontend/src/components/Settings/Settings.tsx b/crabfit-frontend/src/components/Settings/Settings.tsx
index c8c7308..301d8f2 100644
--- a/crabfit-frontend/src/components/Settings/Settings.tsx
+++ b/crabfit-frontend/src/components/Settings/Settings.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useRef } from 'react';
import { useTheme } from '@emotion/react';
import { useTranslation } from 'react-i18next';
import dayjs from 'dayjs';
@@ -27,9 +27,18 @@ const setDefaults = (lang, store) => {
const Settings = () => {
const theme = useTheme();
const store = useSettingsStore();
- const [isOpen, setIsOpen] = useState(false);
+ const [isOpen, _setIsOpen] = useState(false);
const { t, i18n } = useTranslation('common');
const setLocale = useLocaleUpdateStore(state => state.setLocale);
+ const firstControlRef = useRef();
+
+ const setIsOpen = open => {
+ _setIsOpen(open);
+
+ if (open) {
+ window.setTimeout(() => firstControlRef.current.focus(), 150);
+ }
+ };
useEffect(() => {
if (Object.keys(locales).includes(i18n.language)) {
@@ -57,7 +66,6 @@ const Settings = () => {
<>
setIsOpen(!isOpen)} title={t('options.name')}
>
@@ -78,6 +86,7 @@ const Settings = () => {
}}
value={store.weekStart === 0 ? 'Sunday' : 'Monday'}
onChange={value => store.setWeekStart(value === 'Sunday' ? 0 : 1)}
+ inputRef={firstControlRef}
/>
props.isOpen && `
pointer-events: all;
opacity: 1;
transform: translateY(0);
+ visibility: visible;
`}
`;
diff --git a/crabfit-frontend/src/components/ToggleField/ToggleField.tsx b/crabfit-frontend/src/components/ToggleField/ToggleField.tsx
index d1a73f8..2387faf 100644
--- a/crabfit-frontend/src/components/ToggleField/ToggleField.tsx
+++ b/crabfit-frontend/src/components/ToggleField/ToggleField.tsx
@@ -15,6 +15,7 @@ const ToggleField = ({
options = [],
value,
onChange,
+ inputRef,
...props
}) => (
@@ -30,6 +31,7 @@ const ToggleField = ({
id={`${name}-${label}`}
checked={value === key}
onChange={() => onChange(key)}
+ ref={inputRef}
/>
{label}
diff --git a/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts b/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts
index 461676d..e5301f0 100644
--- a/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts
+++ b/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts
@@ -9,6 +9,7 @@ export const ToggleContainer = styled.div`
border: 1px solid ${props => props.theme.primary};
border-radius: 3px;
overflow: hidden;
+
&:focus-within {
outline: Highlight auto 1px;
outline: -webkit-focus-ring-color auto 1px;