Menu for adding accounts (#366)
* Menu for adding accounts * Cleanup menu, remove buttons
This commit is contained in:
parent
ef74d7d1e7
commit
99b9596bdd
@ -3,13 +3,12 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountJson } from '@polkadot/extension-base/background/types';
|
||||
import { ThemeProps } from '../../types';
|
||||
|
||||
import React, { useCallback, useContext, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import genesisOptions from '@polkadot/extension-chains/genesisHashes';
|
||||
|
||||
import { ActionContext, Address, Checkbox, Dropdown, Link } from '../../components';
|
||||
import { ActionContext, Address, Checkbox, Dropdown, Link, MenuDivider } from '../../components';
|
||||
import { editAccount, showAccount, tieAccount } from '../../messaging';
|
||||
import { Name } from '../../partials';
|
||||
|
||||
@ -78,7 +77,7 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
|
||||
Derive New Account
|
||||
</Link>
|
||||
)}
|
||||
<div className='divider' />
|
||||
<MenuDivider />
|
||||
{!isExternal && (
|
||||
<Link
|
||||
className='menuItem'
|
||||
@ -95,7 +94,7 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
|
||||
>
|
||||
Forget Account
|
||||
</Link>
|
||||
<div className='divider' />
|
||||
<MenuDivider />
|
||||
<Checkbox
|
||||
checked={!isHidden}
|
||||
className='menuItem'
|
||||
@ -139,17 +138,11 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(Account)(({ theme }: ThemeProps) => `
|
||||
export default styled(Account)`
|
||||
.address {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
padding-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid ${theme.inputBorderColor};
|
||||
}
|
||||
|
||||
.editName {
|
||||
position: absolute;
|
||||
flex: 1;
|
||||
@ -168,4 +161,4 @@ export default styled(Account)(({ theme }: ThemeProps) => `
|
||||
min-width: 13rem;
|
||||
padding: 4px 16px;
|
||||
}
|
||||
`);
|
||||
`;
|
||||
|
||||
@ -2,14 +2,14 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ThemeProps } from '../types';
|
||||
import { ThemeProps } from '../../types';
|
||||
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { ActionContext } from '../components';
|
||||
import { ActionContext } from '../../components';
|
||||
import Header from '../../partials/Header';
|
||||
import AddAccountImage from './AddAccountImage';
|
||||
import Header from './Header';
|
||||
|
||||
interface Props extends ThemeProps {
|
||||
className?: string;
|
||||
@ -8,7 +8,7 @@
|
||||
/* eslint-disable react/jsx-sort-props */
|
||||
/* eslint-disable react/jsx-max-props-per-line */
|
||||
|
||||
import { ThemeProps } from '../types';
|
||||
import { ThemeProps } from '../../types';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
@ -2,19 +2,16 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ThemeProps } from '../../types';
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { AccountContext, Button, ButtonArea, ButtonWithSubtitle, MediaContext, Svg } from '../../components';
|
||||
import { AddAccount, Header } from '../../partials';
|
||||
import { AccountContext } from '../../components';
|
||||
import { Header } from '../../partials';
|
||||
import AccountsTree from './AccountsTree';
|
||||
import qrIcon from '../../assets/qr.svg';
|
||||
import AddAccount from './AddAccount';
|
||||
|
||||
export default function Accounts (): React.ReactElement {
|
||||
const { hierarchy, master } = useContext(AccountContext);
|
||||
const mediaAllowed = useContext(MediaContext);
|
||||
const { hierarchy } = useContext(AccountContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -23,6 +20,7 @@ export default function Accounts (): React.ReactElement {
|
||||
: (
|
||||
<>
|
||||
<Header
|
||||
showAdd
|
||||
showSettings
|
||||
text={'Accounts'}
|
||||
/>
|
||||
@ -37,37 +35,10 @@ export default function Accounts (): React.ReactElement {
|
||||
</>
|
||||
)
|
||||
}
|
||||
<ButtonArea>
|
||||
<ButtonWithSubtitle
|
||||
subTitle={master ? 'Derive from master' : 'With new seed'}
|
||||
title='Create New Account'
|
||||
to={master ? `/account/derive/${master.address}` : '/account/create'}
|
||||
/>
|
||||
<ButtonWithSubtitle
|
||||
subTitle='I have a pre-existing seed'
|
||||
title='Import an Account'
|
||||
to='/account/import-seed'
|
||||
/>
|
||||
{mediaAllowed && (
|
||||
<QrButton to='/account/import-qr'>
|
||||
<Svg src={qrIcon} />
|
||||
</QrButton>
|
||||
)}
|
||||
</ButtonArea>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const QrButton = styled(Button)`
|
||||
width: 60px;
|
||||
|
||||
${Svg} {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: ${({ theme }: ThemeProps): string => theme.buttonTextColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const AccountsArea = styled.div`
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { ButtonArea, Checkbox, MnemonicSeed, NextStepButton, VerticalSpace, Warning } from '../../components';
|
||||
import useToast from '../../hooks/useToast';
|
||||
|
||||
|
||||
@ -7,8 +7,8 @@ import { ThemeProps } from '../../types';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Address, Svg } from '../../components';
|
||||
import arrow from '../../assets/arrow-down.svg';
|
||||
import { Address, Svg } from '../../components';
|
||||
import useOutsideClick from '../../hooks/useOutsideClick';
|
||||
|
||||
interface Props {
|
||||
@ -21,7 +21,6 @@ interface Props {
|
||||
function AddressDropdown ({ allAddresses, className, onSelect, selectedAddress }: Props): React.ReactElement<Props> {
|
||||
const [isDropdownVisible, setDropdownVisible] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const hideDropdown = useCallback(() => setDropdownVisible(false), []);
|
||||
const toggleDropdown = useCallback(() => setDropdownVisible(!isDropdownVisible), [isDropdownVisible]);
|
||||
const selectParent = (newParent: string) => (): void => onSelect(newParent);
|
||||
|
||||
@ -5,9 +5,8 @@
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import upload from '../assets/file-upload.svg';
|
||||
import { ActionContext, Address, ButtonArea, NextStepButton, TextAreaWithLabel, ValidatedInput, VerticalSpace, ActionText } from '../components';
|
||||
import { createAccountSuri, validateSeed, jsonRestoreWindowOpen } from '../messaging';
|
||||
import { ActionContext, Address, ButtonArea, NextStepButton, TextAreaWithLabel, ValidatedInput, VerticalSpace } from '../components';
|
||||
import { createAccountSuri, validateSeed } from '../messaging';
|
||||
import { Header, Name, Password } from '../partials';
|
||||
import { allOf, isNotShorterThan, Result } from '../util/validators';
|
||||
|
||||
@ -53,15 +52,6 @@ export default function Import (): React.ReactElement {
|
||||
}
|
||||
}, [account, name, onAction, password]);
|
||||
|
||||
const _toJson = useCallback((): void => {
|
||||
// are we in a popup?
|
||||
if (window.innerWidth <= 480) {
|
||||
jsonRestoreWindowOpen().catch(console.error);
|
||||
} else {
|
||||
onAction('/account/restore-json');
|
||||
}
|
||||
}, [onAction]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderWithSmallerMargin
|
||||
@ -95,15 +85,6 @@ export default function Import (): React.ReactElement {
|
||||
</ButtonArea>
|
||||
</>
|
||||
)}
|
||||
{!account &&
|
||||
<ButtonsRow>
|
||||
<ActionText
|
||||
icon={upload}
|
||||
onClick={_toJson}
|
||||
text='Restore from JSON backup'
|
||||
/>
|
||||
</ButtonsRow>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -118,12 +99,3 @@ const SeedInput = styled(TextAreaWithLabel)`
|
||||
height: unset;
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonsRow = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
${ActionText} {
|
||||
margin-right: 32px;
|
||||
}
|
||||
`;
|
||||
|
||||
@ -7,8 +7,8 @@ import { ThemeProps } from '../../types';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Svg } from '../../components';
|
||||
import ArrowLeftImage from '../../assets/arrowLeft.svg';
|
||||
import { Svg } from '../../components';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
||||
3
packages/extension-ui/src/assets/plus.svg
Normal file
3
packages/extension-ui/src/assets/plus.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?><svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 52 52" style="enable-background:new 0 0 52 52;" xml:space="preserve"><path d="M26,0C11.664,0,0,11.663,0,26s11.664,26,26,26s26-11.663,26-26S40.336,0,26,0z M38.5,28H28v11c0,1.104-0.896,2-2,2
|
||||
s-2-0.896-2-2V28H13.5c-1.104,0-2-0.896-2-2s0.896-2,2-2H24V14c0-1.104,0.896-2,2-2s2,0.896,2,2v10h10.5c1.104,0,2,0.896,2,2
|
||||
S39.604,28,38.5,28z"/><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></svg>
|
||||
|
After Width: | Height: | Size: 625 B |
2
packages/extension-ui/src/assets/secret.svg
Normal file
2
packages/extension-ui/src/assets/secret.svg
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
<svg id="Capa_1" enable-background="new 0 0 512.007 512.007" height="512" viewBox="0 0 512.007 512.007" width="512" xmlns="http://www.w3.org/2000/svg"><g><path d="m497.007 281.013h-10v-17c0-34.003-24.032-62.486-56-69.391v-114.609c0-7.717-5.897-14.232-13.627-14.931-1.18-.108-402.547-.147-404.195.052-7.415.907-13.052 7.142-13.167 14.648-.002.145-.064 288.148.054 289.517.566 6.664 5.52 12.22 12.209 13.455 2.166.4-17.056.259 308.726.259v49c0 8.284 6.716 15 15 15h161c8.284 0 15-6.716 15-15v-136c0-8.284-6.716-15-15-15zm-40-17v17h-82v-17c0-22.607 18.393-41 41-41s41 18.393 41 41zm-117.158 17-64.774-56.999 125.932-110.82v81.428c-31.968 6.904-56 35.388-56 69.391v17zm36.407-186-160.274 141.041-161.075-141.041zm-346.249 18.072 126.683 110.926-126.683 110.929zm24.899 239.928 124.553-109.064 26.666 23.349c5.659 4.955 14.126 4.96 19.791-.024l26.453-23.278 68.638 60.398v48.619zm427.101 64h-131v-106h131z"/><path d="m416.007 345.013c-8.284 0-15 6.716-15 15v8c0 8.284 6.716 15 15 15s15-6.716 15-15v-8c0-8.284-6.716-15-15-15z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -15,14 +15,13 @@ import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
|
||||
import copy from '../assets/copy.svg';
|
||||
import details from '../assets/details.svg';
|
||||
import parentArrow from '../assets/arrowParentLabel.svg';
|
||||
|
||||
import { AccountContext, SettingsContext } from './contexts';
|
||||
import Identicon from './Identicon';
|
||||
import Menu from './Menu';
|
||||
import Svg from './Svg';
|
||||
import useOutsideClick from '../hooks/useOutsideClick';
|
||||
import useMetadata from '../hooks/useMetadata';
|
||||
import useToast from '../hooks/useToast';
|
||||
import Identicon from './Identicon';
|
||||
import Menu from './Menu';
|
||||
import Svg from './Svg';
|
||||
|
||||
interface Props {
|
||||
address?: string | null;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
import NunitosansregularWoff2 from '../assets/fonts/Nunitosansregular.woff2';
|
||||
import NunitosansregularWoff from '../assets/fonts/Nunitosansregular.woff';
|
||||
import NunitosanssemiboldWoff2 from '../assets/fonts/Nunitosanssemibold.woff2';
|
||||
|
||||
@ -12,11 +12,18 @@ interface Props extends ThemeProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
isDanger?: boolean;
|
||||
isDisabled?: boolean;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
}
|
||||
|
||||
function Link ({ children, className, onClick, to }: Props): React.ReactElement<Props> {
|
||||
function Link ({ children, className = '', isDisabled, onClick, to }: Props): React.ReactElement<Props> {
|
||||
if (isDisabled) {
|
||||
return (
|
||||
<div className={`${className} isDisabled`}>{children}</div>
|
||||
);
|
||||
}
|
||||
|
||||
return to
|
||||
? (
|
||||
<RouterLink
|
||||
@ -42,7 +49,7 @@ export default styled(Link)(({ isDanger, theme }: Props) => `
|
||||
align-items: center;
|
||||
color: ${isDanger ? theme.textColorDanger : theme.textColor};
|
||||
display: flex;
|
||||
opacity: 0.9;
|
||||
opacity: 0.85;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
|
||||
@ -54,4 +61,8 @@ export default styled(Link)(({ isDanger, theme }: Props) => `
|
||||
&:visited {
|
||||
color: ${isDanger ? theme.textColorDanger : theme.textColor};
|
||||
}
|
||||
|
||||
&.isDisabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
`);
|
||||
|
||||
24
packages/extension-ui/src/components/MenuDivider.tsx
Normal file
24
packages/extension-ui/src/components/MenuDivider.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ThemeProps } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
interface Props extends ThemeProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function MenuDivider ({ className }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<div className={className} />
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(MenuDivider)(({ theme }: Props) => `
|
||||
padding-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid ${theme.inputBorderColor};
|
||||
`);
|
||||
39
packages/extension-ui/src/components/MenuItem.tsx
Normal file
39
packages/extension-ui/src/components/MenuItem.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Title from './Title';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
noBorder?: boolean;
|
||||
title?: React.ReactNode;
|
||||
}
|
||||
|
||||
function MenuItem ({ children, className = '', title }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<div className={className}>
|
||||
{title && (
|
||||
<Title className='itemTitle'>{title}</Title>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(MenuItem)`
|
||||
padding: 0 16px;
|
||||
max-width: 100%;
|
||||
|
||||
> .itemTitle {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&+& {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
@ -10,9 +10,9 @@ interface Props {
|
||||
src: string;
|
||||
}
|
||||
|
||||
export default styled.span<Props>`
|
||||
display: block;
|
||||
mask: ${({ src }): string => `url(${src})`};
|
||||
export default styled.span<Props>(({ src, theme }: Props & ThemeProps) => `
|
||||
background: ${theme.textColor};
|
||||
display: inline-block;
|
||||
mask: url(${src});
|
||||
mask-size: cover;
|
||||
background: ${({ theme }: ThemeProps): string => theme.textColor};
|
||||
`;
|
||||
`);
|
||||
|
||||
@ -22,6 +22,8 @@ export { default as Link } from './Link';
|
||||
export { default as List } from './List';
|
||||
export { default as Loading } from './Loading';
|
||||
export { default as Menu } from './Menu';
|
||||
export { default as MenuDivider } from './MenuDivider';
|
||||
export { default as MenuItem } from './MenuItem';
|
||||
export { default as MnemonicSeed } from './MnemonicSeed';
|
||||
export { default as NextStepButton } from './NextStepButton';
|
||||
export { default as Svg } from './Svg';
|
||||
|
||||
@ -12,7 +12,7 @@ import { ThemeProvider } from 'styled-components';
|
||||
|
||||
import { themes } from '../components';
|
||||
import Header from './Header';
|
||||
import Settings from './Settings';
|
||||
import Settings from './MenuSettings';
|
||||
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
@ -37,7 +37,7 @@ describe('Header component', () => {
|
||||
it('highlights gear icon when settings are toggled', () => {
|
||||
wrapper = mountHeader({ showSettings: true });
|
||||
|
||||
wrapper.find('div.settingsToggle').simulate('click');
|
||||
wrapper.find('div[data-toggle-settings]').simulate('click');
|
||||
|
||||
expect(wrapper.find(Settings).length).toBe(1);
|
||||
expect(wrapper.find('Gear').prop('isSelected')).toBe(true);
|
||||
|
||||
@ -8,28 +8,48 @@ import React, { useCallback, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import ArrowLeftImage from '../assets/arrowLeft.svg';
|
||||
import logo from '../assets/pjs.svg';
|
||||
import gear from '../assets/gear.svg';
|
||||
import plus from '../assets/plus.svg';
|
||||
import logo from '../assets/pjs.svg';
|
||||
import { Link, Svg } from '../components';
|
||||
import useOutsideClick from '../hooks/useOutsideClick';
|
||||
import Settings from './Settings';
|
||||
import MenuAdd from './MenuAdd';
|
||||
import MenuSettings from './MenuSettings';
|
||||
|
||||
interface Props extends ThemeProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
showAdd?: boolean;
|
||||
showBackArrow?: boolean;
|
||||
showSettings?: boolean;
|
||||
text?: React.ReactNode;
|
||||
showBackArrow?: boolean;
|
||||
}
|
||||
|
||||
function Header ({ children, className, showBackArrow, showSettings, text }: Props): React.ReactElement<Props> {
|
||||
const [showActionsMenu, setShowActionsMenu] = useState(false);
|
||||
const actionsRef = useRef(null);
|
||||
interface MenuSelectProps {
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
useOutsideClick(actionsRef, () => (showActionsMenu && setShowActionsMenu(!showActionsMenu)));
|
||||
function Header ({ children, className, showAdd, showBackArrow, showSettings, text }: Props): React.ReactElement<Props> {
|
||||
const [isAddOpen, setShowAdd] = useState(false);
|
||||
const [isSettingsOpen, setShowSettings] = useState(false);
|
||||
const addRef = useRef(null);
|
||||
const setRef = useRef(null);
|
||||
|
||||
useOutsideClick(addRef, (): void => {
|
||||
isAddOpen && setShowAdd(!isAddOpen);
|
||||
});
|
||||
|
||||
useOutsideClick(setRef, (): void => {
|
||||
isSettingsOpen && setShowSettings(!isSettingsOpen);
|
||||
});
|
||||
|
||||
const _toggleAdd = useCallback(
|
||||
(): void => setShowAdd((isAddOpen) => !isAddOpen),
|
||||
[]
|
||||
);
|
||||
|
||||
const _toggleSettings = useCallback(
|
||||
(): void => setShowActionsMenu((showActionsMenu) => !showActionsMenu),
|
||||
(): void => setShowSettings((isSettingsOpen) => !isSettingsOpen),
|
||||
[]
|
||||
);
|
||||
|
||||
@ -52,16 +72,30 @@ function Header ({ children, className, showBackArrow, showSettings, text }: Pro
|
||||
}
|
||||
<span className='logoText'>{text || 'polkadot{.js}'}</span>
|
||||
</div>
|
||||
{showSettings && (
|
||||
<div
|
||||
className='settingsToggle'
|
||||
onClick={_toggleSettings}
|
||||
>
|
||||
<Gear isSelected={showActionsMenu} />
|
||||
</div>
|
||||
<div className='popupMenus'>
|
||||
{showAdd && (
|
||||
<div
|
||||
className='popupToggle'
|
||||
onClick={_toggleAdd}
|
||||
>
|
||||
<Plus isSelected={isAddOpen} />
|
||||
</div>
|
||||
)}
|
||||
{showSettings && (
|
||||
<div
|
||||
className='popupToggle'
|
||||
data-toggle-settings
|
||||
onClick={_toggleSettings}
|
||||
>
|
||||
<Gear isSelected={isSettingsOpen} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isAddOpen && (
|
||||
<MenuAdd reference={addRef}/>
|
||||
)}
|
||||
{showActionsMenu && (
|
||||
<Settings reference={actionsRef}/>
|
||||
{isSettingsOpen && (
|
||||
<MenuSettings reference={setRef}/>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
@ -70,10 +104,10 @@ function Header ({ children, className, showBackArrow, showSettings, text }: Pro
|
||||
}
|
||||
|
||||
const BackLink = styled(Link)`
|
||||
width: min-content;
|
||||
text-decoration: underline;
|
||||
color: ${({ theme }: ThemeProps) => theme.labelColor};
|
||||
min-height: 52px;
|
||||
text-decoration: underline;
|
||||
width: min-content;
|
||||
|
||||
&:visited {
|
||||
color: ${({ theme }: ThemeProps) => theme.labelColor};
|
||||
@ -81,26 +115,26 @@ const BackLink = styled(Link)`
|
||||
`;
|
||||
|
||||
const ArrowLeft = styled(Svg).attrs(() => ({ src: ArrowLeftImage }))`
|
||||
background: ${({ theme }: ThemeProps) => theme.labelColor};
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 13px;
|
||||
background: ${({ theme }: ThemeProps) => theme.labelColor};
|
||||
`;
|
||||
|
||||
interface GearProps {
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const Gear = styled(Svg).attrs(() => ({ src: gear }))<GearProps>`
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin-right: 24px;
|
||||
align-self: center;
|
||||
background: ${({ isSelected, theme }: ThemeProps & GearProps): string => isSelected ? theme.primaryColor : theme.iconNeutralColor};
|
||||
const Gear = styled(Svg).attrs(() => ({ src: gear }))<MenuSelectProps>`
|
||||
background: ${({ isSelected, theme }: ThemeProps & MenuSelectProps): string => isSelected ? theme.primaryColor : theme.iconNeutralColor};
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
`;
|
||||
|
||||
Gear.displayName = 'Gear';
|
||||
|
||||
const Plus = styled(Svg).attrs(() => ({ src: plus }))<MenuSelectProps>`
|
||||
background: ${({ isSelected, theme }: ThemeProps & MenuSelectProps): string => isSelected ? theme.primaryColor : theme.iconNeutralColor};
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
`;
|
||||
|
||||
export default React.memo(styled(Header)(({ theme }: Props) => `
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
@ -143,11 +177,24 @@ export default React.memo(styled(Header)(({ theme }: Props) => `
|
||||
}
|
||||
}
|
||||
|
||||
.settingsToggle {
|
||||
.popupMenus {
|
||||
align-self: center;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
.popupToggle {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.popupToggle+.popupToggle {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
90
packages/extension-ui/src/partials/MenuAdd.tsx
Normal file
90
packages/extension-ui/src/partials/MenuAdd.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ThemeProps } from '../types';
|
||||
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import fileIcon from '../assets/file-upload.svg';
|
||||
import plusIcon from '../assets/plus.svg';
|
||||
import qrIcon from '../assets/qr.svg';
|
||||
import seedIcon from '../assets/secret.svg';
|
||||
import { AccountContext, Link, MediaContext, Menu, MenuDivider, MenuItem, Svg } from '../components';
|
||||
import { jsonRestoreWindowOpen } from '../messaging';
|
||||
|
||||
interface Props extends ThemeProps {
|
||||
className?: string;
|
||||
reference: React.MutableRefObject<null>;
|
||||
}
|
||||
|
||||
const isPopup = window.innerWidth <= 480;
|
||||
|
||||
function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
const { master } = useContext(AccountContext);
|
||||
const mediaAllowed = useContext(MediaContext);
|
||||
|
||||
const _openJson = useCallback((): void => {
|
||||
jsonRestoreWindowOpen().catch(console.error);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
className={className}
|
||||
reference={reference}
|
||||
>
|
||||
<MenuItem className='menuItem'>
|
||||
<Link to={master ? `/account/derive/${master.address}` : '/account/create'}>
|
||||
<Svg src={plusIcon} />
|
||||
<span>Create new account (root or derived)</span>
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem className='menuItem'>
|
||||
<Link to='/account/import-seed'>
|
||||
<Svg src={seedIcon} />
|
||||
<span>Import account from pre-existing seed</span>
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem className='menuItem'>
|
||||
<Link
|
||||
onClick={isPopup ? _openJson : undefined}
|
||||
to={isPopup ? undefined : '/account/restore-json'}
|
||||
>
|
||||
<Svg src={fileIcon} />
|
||||
<span>Restore account from backup JSON file</span>
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem className='menuItem'>
|
||||
<Link
|
||||
isDisabled={!mediaAllowed}
|
||||
to='/account/import-qr'
|
||||
>
|
||||
<Svg src={qrIcon} />
|
||||
<span>Attach external QR-signer account</span>
|
||||
</Link>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(MenuAdd)`
|
||||
margin-top: 50px;
|
||||
right: 50px; // 24 + 18 + 8
|
||||
user-select: none;
|
||||
|
||||
.menuItem {
|
||||
span:first-child {
|
||||
height: 20px;
|
||||
margin-right: 8px;
|
||||
opacity: 0.5;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
`);
|
||||
@ -9,7 +9,7 @@ import styled, { ThemeContext } from 'styled-components';
|
||||
import settings from '@polkadot/ui-settings';
|
||||
|
||||
import FullScreenIcon from '../assets/fullscreen.svg';
|
||||
import { ActionText, Checkbox, Dropdown, Menu, Svg, Switch, ThemeSwitchContext, Title, themes } from '../components';
|
||||
import { ActionText, Checkbox, Dropdown, Menu, MenuDivider, MenuItem, Svg, Switch, ThemeSwitchContext, themes } from '../components';
|
||||
import { windowOpen } from '../messaging';
|
||||
|
||||
interface Option {
|
||||
@ -27,7 +27,7 @@ const prefixOptions = settings.availablePrefixes
|
||||
.filter(({ value }) => value !== -1)
|
||||
.map(({ text, value }): Option => ({ text, value: `${value}` }));
|
||||
|
||||
function Settings ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
function MenuSettings ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
const [camera, setCamera] = useState(settings.camera === 'on');
|
||||
const [prefix, setPrefix] = useState(`${settings.prefix === -1 ? 42 : settings.prefix}`);
|
||||
const themeContext = useContext<Theme>(ThemeContext);
|
||||
@ -55,26 +55,34 @@ function Settings ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
className={className}
|
||||
reference={reference}
|
||||
>
|
||||
<div className='setting'>
|
||||
<Title className='title'>Theme</Title>
|
||||
<MenuItem
|
||||
className='setting'
|
||||
title='Theme'
|
||||
>
|
||||
<Switch
|
||||
checked={themeContext.id === themes.dark.id}
|
||||
checkedLabel='Dark'
|
||||
onChange={_onChangeTheme}
|
||||
uncheckedLabel='Light'
|
||||
/>
|
||||
</div>
|
||||
<div className='setting'>
|
||||
<Title className='title'>External QR accounts and Access</Title>
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
className='setting'
|
||||
title='External QR accounts and Access'
|
||||
>
|
||||
<Checkbox
|
||||
checked={camera}
|
||||
className='checkbox'
|
||||
label='Allow Camera Access'
|
||||
onChange={setCamera}
|
||||
/>
|
||||
</div>
|
||||
<div className='setting'>
|
||||
<Title className='title'>Display address format For:</Title>
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
className='setting'
|
||||
title='Display address format For'
|
||||
>
|
||||
<Dropdown
|
||||
className='dropdown'
|
||||
label=''
|
||||
@ -82,23 +90,26 @@ function Settings ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
options={prefixOptions}
|
||||
value={`${prefix}`}
|
||||
/>
|
||||
</div>
|
||||
</MenuItem>
|
||||
{isPopup && (
|
||||
<div className='setting'>
|
||||
<ActionText
|
||||
className='openWindow'
|
||||
icon={FullScreenIcon}
|
||||
onClick={windowOpen}
|
||||
text='Open extension in new window'
|
||||
/>
|
||||
</div>
|
||||
<>
|
||||
<MenuDivider />
|
||||
<MenuItem className='setting'>
|
||||
<ActionText
|
||||
className='openWindow'
|
||||
icon={FullScreenIcon}
|
||||
onClick={windowOpen}
|
||||
text='Open extension in new window'
|
||||
/>
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(Settings)(({ theme }: Props) => `
|
||||
margin-top: 56px;
|
||||
export default React.memo(styled(MenuSettings)(({ theme }: Props) => `
|
||||
margin-top: 50px;
|
||||
right: 24px;
|
||||
user-select: none;
|
||||
|
||||
@ -120,14 +131,12 @@ export default React.memo(styled(Settings)(({ theme }: Props) => `
|
||||
}
|
||||
|
||||
> .setting {
|
||||
padding: 0 16px;
|
||||
max-width: 100%;
|
||||
|
||||
> .checkbox {
|
||||
color: ${theme.textColor};
|
||||
line-height: 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0;
|
||||
|
||||
label {
|
||||
color: ${theme.textColor};
|
||||
@ -136,19 +145,10 @@ export default React.memo(styled(Settings)(({ theme }: Props) => `
|
||||
|
||||
> .dropdown {
|
||||
background: ${theme.background};
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 0;
|
||||
margin-top: 9px;
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
> .title {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.setting+.setting {
|
||||
padding-top: 18px;
|
||||
border-top: 1px solid ${theme.inputBorderColor};
|
||||
}
|
||||
`));
|
||||
@ -2,11 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export { default as AddAccount } from './AddAccount';
|
||||
export { default as Back } from './Back';
|
||||
export { default as DerivationPath } from './DerivationPath';
|
||||
export { default as Header } from './Header';
|
||||
export { default as MenuSettings } from './MenuSettings';
|
||||
export { default as Name } from './Name';
|
||||
export { default as Password } from './Password';
|
||||
export { default as Settings } from './Settings';
|
||||
export { default as HeaderWithSteps } from './HeaderWithSteps';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user