feat: Add quick settings menu with theme switcher and other changes (#673)
Co-authored-by: Mo Bitar <mo@standardnotes.org> Co-authored-by: Antonella Sgarlatta <antsgar@gmail.com>
This commit is contained in:
@@ -39,7 +39,7 @@ export const AdvancedOptions: FunctionComponent<Props> = observer(
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="sn-dropdown-item font-bold"
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none font-bold"
|
||||
onClick={toggleShowAdvanced}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -3,7 +3,7 @@ import { WebApplication } from '@/ui_models/application';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { StateUpdater, useRef, useState } from 'preact/hooks';
|
||||
import { StateUpdater, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { AccountMenuPane } from '.';
|
||||
import { Button } from '../Button';
|
||||
import { Checkbox } from '../Checkbox';
|
||||
@@ -31,6 +31,10 @@ export const ConfirmPassword: FunctionComponent<Props> = observer(
|
||||
|
||||
const passwordInputRef = useRef<HTMLInputElement>();
|
||||
|
||||
useEffect(() => {
|
||||
passwordInputRef?.current?.focus();
|
||||
}, []);
|
||||
|
||||
const handlePasswordChange = (e: Event) => {
|
||||
if (e.target instanceof HTMLInputElement) {
|
||||
setConfirmPassword(e.target.value);
|
||||
|
||||
@@ -5,9 +5,10 @@ import { Icon } from '../Icon';
|
||||
import { formatLastSyncDate } from '@/preferences/panes/account/Sync';
|
||||
import { SyncQueueStrategy } from '@standardnotes/snjs';
|
||||
import { STRING_GENERIC_SYNC_ERROR } from '@/strings';
|
||||
import { useState } from 'preact/hooks';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { AccountMenuPane } from '.';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { JSXInternal } from 'preact/src/jsx';
|
||||
import { AppVersion } from '@/version';
|
||||
|
||||
type Props = {
|
||||
@@ -25,6 +26,9 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
const [lastSyncDate, setLastSyncDate] = useState(
|
||||
formatLastSyncDate(application.getLastSyncDate() as Date)
|
||||
);
|
||||
const [currentFocusedIndex, setCurrentFocusedIndex] = useState(0);
|
||||
|
||||
const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
||||
|
||||
const doSynchronization = async () => {
|
||||
setIsSyncingInProgress(true);
|
||||
@@ -53,9 +57,49 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
|
||||
const user = application.getUser();
|
||||
|
||||
const accountMenuRef = useRef<HTMLDivElement>();
|
||||
|
||||
const handleKeyDown: JSXInternal.KeyboardEventHandler<HTMLDivElement> = (
|
||||
event
|
||||
) => {
|
||||
switch (event.key) {
|
||||
case 'ArrowDown':
|
||||
setCurrentFocusedIndex((index) => {
|
||||
console.log(index, buttonRefs.current.length);
|
||||
if (index + 1 < buttonRefs.current.length) {
|
||||
return index + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
setCurrentFocusedIndex((index) => {
|
||||
if (index - 1 > -1) {
|
||||
return index - 1;
|
||||
} else {
|
||||
return buttonRefs.current.length - 1;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (buttonRefs.current[currentFocusedIndex]) {
|
||||
buttonRefs.current[currentFocusedIndex]?.focus();
|
||||
}
|
||||
}, [currentFocusedIndex]);
|
||||
|
||||
const pushRefToArray = (ref: HTMLButtonElement | null) => {
|
||||
if (ref && !buttonRefs.current.includes(ref)) {
|
||||
buttonRefs.current.push(ref);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between px-3 mt-1 mb-2">
|
||||
<div ref={accountMenuRef} onKeyDown={handleKeyDown}>
|
||||
<div className="flex items-center justify-between px-3 mt-1 mb-3">
|
||||
<div className="sn-account-menu-headline">Account</div>
|
||||
<div className="flex cursor-pointer" onClick={closeMenu}>
|
||||
<Icon type="close" className="color-grey-1" />
|
||||
@@ -105,7 +149,8 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
<div className="h-1px my-2 bg-border"></div>
|
||||
{user ? (
|
||||
<button
|
||||
className="sn-dropdown-item"
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
ref={pushRefToArray}
|
||||
onClick={() => {
|
||||
appState.accountMenu.closeAccountMenu();
|
||||
appState.preferences.setCurrentPane('account');
|
||||
@@ -118,7 +163,8 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className="sn-dropdown-item"
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
ref={pushRefToArray}
|
||||
onClick={() => {
|
||||
setMenuPane(AccountMenuPane.Register);
|
||||
}}
|
||||
@@ -127,7 +173,8 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
Create free account
|
||||
</button>
|
||||
<button
|
||||
className="sn-dropdown-item"
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
ref={pushRefToArray}
|
||||
onClick={() => {
|
||||
setMenuPane(AccountMenuPane.SignIn);
|
||||
}}
|
||||
@@ -138,7 +185,8 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
className="sn-dropdown-item justify-between"
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
ref={pushRefToArray}
|
||||
onClick={() => {
|
||||
appState.accountMenu.closeAccountMenu();
|
||||
appState.preferences.setCurrentPane('help-feedback');
|
||||
@@ -155,7 +203,8 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
<>
|
||||
<div className="h-1px my-2 bg-border"></div>
|
||||
<button
|
||||
className="sn-dropdown-item"
|
||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
||||
ref={pushRefToArray}
|
||||
onClick={() => {
|
||||
appState.accountMenu.setSigningOut(true);
|
||||
}}
|
||||
@@ -165,7 +214,7 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SignInPane } from './SignIn';
|
||||
import { CreateAccount } from './CreateAccount';
|
||||
import { ConfirmSignoutContainer } from '../ConfirmSignoutModal';
|
||||
import { ConfirmPassword } from './ConfirmPassword';
|
||||
import { JSXInternal } from 'preact/src/jsx';
|
||||
|
||||
export enum AccountMenuPane {
|
||||
GeneralMenu,
|
||||
@@ -87,14 +88,31 @@ const AccountMenu: FunctionComponent<Props> = observer(
|
||||
closeAccountMenu,
|
||||
} = appState.accountMenu;
|
||||
|
||||
const handleKeyDown: JSXInternal.KeyboardEventHandler<HTMLDivElement> = (
|
||||
event
|
||||
) => {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
if (currentPane === AccountMenuPane.GeneralMenu) {
|
||||
closeAccountMenu();
|
||||
} else if (currentPane === AccountMenuPane.ConfirmPassword) {
|
||||
setCurrentPane(AccountMenuPane.Register);
|
||||
} else {
|
||||
setCurrentPane(AccountMenuPane.GeneralMenu);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="sn-component">
|
||||
<div
|
||||
className={`sn-account-menu sn-dropdown ${
|
||||
className={`sn-menu-border sn-account-menu sn-dropdown ${
|
||||
shouldAnimateCloseMenu
|
||||
? 'slide-up-animation'
|
||||
: 'sn-dropdown--animated'
|
||||
} min-w-80 max-h-120 max-w-xs flex flex-col py-2 overflow-y-auto absolute`}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<MenuPaneSelector
|
||||
appState={appState}
|
||||
|
||||
Reference in New Issue
Block a user