mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
refactored some common components
This commit is contained in:
parent
5e9c111e0f
commit
a9a220273f
67 changed files with 238 additions and 220 deletions
16
src/components/common/ArcProgress/index.tsx
Normal file
16
src/components/common/ArcProgress/index.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { describeArc } from '~/utils/dom';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface IProps {
|
||||
size: number;
|
||||
progress?: number;
|
||||
}
|
||||
|
||||
export const ArcProgress: FC<IProps> = ({ size, progress = 0 }) => (
|
||||
<svg className={styles.icon} width={size} height={size}>
|
||||
<path d={describeArc(size / 2, size / 2, size / 2 - 2, 360 * (1 - progress), 360)} />
|
||||
</svg>
|
||||
);
|
6
src/components/common/ArcProgress/styles.module.scss
Normal file
6
src/components/common/ArcProgress/styles.module.scss
Normal file
|
@ -0,0 +1,6 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.icon {
|
||||
fill: $color_danger;
|
||||
stroke: none;
|
||||
}
|
|
@ -2,8 +2,8 @@ import React, { FC, MouseEventHandler, useEffect, useRef } from 'react';
|
|||
|
||||
import { clearAllBodyScrollLocks, disableBodyScroll } from 'body-scroll-lock';
|
||||
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||
import { Icon } from '~/components/common/Icon';
|
||||
import { LoaderCircle } from '~/components/common/LoaderCircle';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import React, { useCallback, useState, VFC } from 'react';
|
||||
|
||||
import { MenuDots } from '~/components/common/MenuDots';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface MenuAction {
|
||||
title: string;
|
||||
action: () => void;
|
||||
}
|
||||
interface CornerMenuProps {
|
||||
actions: MenuAction[];
|
||||
}
|
||||
|
||||
const CornerMenu: VFC<CornerMenuProps> = ({ actions }) => {
|
||||
const [is_menu_opened, setIsMenuOpened] = useState(false);
|
||||
|
||||
const onFocus = useCallback(() => setIsMenuOpened(true), [setIsMenuOpened]);
|
||||
const onBlur = useCallback(() => setIsMenuOpened(false), [setIsMenuOpened]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.wrap}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<MenuDots onClick={onFocus} />
|
||||
|
||||
{is_menu_opened && (
|
||||
<div className={styles.menu}>
|
||||
{actions.map(({ title, action }) => (
|
||||
<div className={styles.item} onMouseDown={action} key={title}>
|
||||
{title}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { CornerMenu };
|
|
@ -1,56 +0,0 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: -3px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
z-index: 10;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 6;
|
||||
white-space: nowrap;
|
||||
|
||||
animation: appear 0.25s forwards;
|
||||
}
|
||||
|
||||
.item {
|
||||
user-select: none;
|
||||
font: $font_12_semibold;
|
||||
text-transform: uppercase;
|
||||
padding: 8px $gap;
|
||||
background: $content_bg;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: $radius;
|
||||
border-top-right-radius: $radius;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-left-radius: $radius;
|
||||
border-bottom-right-radius: $radius;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $color_primary;
|
||||
}
|
||||
}
|
20
src/components/common/DropHereIcon/index.tsx
Normal file
20
src/components/common/DropHereIcon/index.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { SVGProps } from '~/utils/types';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface Props extends SVGProps {}
|
||||
|
||||
const DropHereIcon: FC<Props> = ({ ...rest }) => (
|
||||
<svg viewBox="0 0 24 24" stroke="none" {...rest}>
|
||||
<path d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z" />
|
||||
|
||||
<path
|
||||
d="M17,11l-1.41-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5 L17,11z"
|
||||
className={styles.arrow}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export { DropHereIcon };
|
8
src/components/common/DropHereIcon/styles.module.scss
Normal file
8
src/components/common/DropHereIcon/styles.module.scss
Normal file
|
@ -0,0 +1,8 @@
|
|||
@keyframes bounce {
|
||||
0% { transform: translate(0, -5%); }
|
||||
100% { transform: translate(0, 5%); }
|
||||
}
|
||||
|
||||
.arrow {
|
||||
animation: bounce alternate infinite 0.25s;
|
||||
}
|
23
src/components/common/Icon/index.tsx
Normal file
23
src/components/common/Icon/index.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { IIcon } from '~/types';
|
||||
|
||||
type IProps = React.SVGAttributes<SVGElement> & {
|
||||
size?: number;
|
||||
icon: IIcon;
|
||||
};
|
||||
|
||||
export const Icon: FC<IProps> = ({
|
||||
size = 20, icon, style, ...props
|
||||
}) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
style={{ ...style, outline: 'none' }}
|
||||
{...props}
|
||||
>
|
||||
<use xlinkHref={`#${icon}`} />
|
||||
</svg>
|
||||
);
|
|
@ -8,7 +8,7 @@ import React, {
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||
import { LoaderCircle } from '~/components/common/LoaderCircle';
|
||||
import { DivProps } from '~/utils/types';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import React, { FC, SVGAttributes } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { describeArc } from '~/utils/dom';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface IProps extends SVGAttributes<SVGElement> {
|
||||
size: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const LoaderCircleInner: FC<IProps> = ({ size, className, ...props }) => (
|
||||
<svg className={classNames(styles.icon, className)} width={size} height={size} {...props}>
|
||||
<path d={describeArc(size / 2, size / 2, size / 2, 0, 90)} />
|
||||
<path d={describeArc(size / 2, size / 2, size / 2, 180, 270)} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export { LoaderCircleInner };
|
|
@ -0,0 +1,6 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.icon {
|
||||
fill: currentColor;
|
||||
stroke: none;
|
||||
}
|
19
src/components/common/LoaderCircle/index.tsx
Normal file
19
src/components/common/LoaderCircle/index.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { SVGProps } from '~/utils/types';
|
||||
|
||||
import { LoaderCircleInner } from './components/LoaderCircleInner';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface IProps extends SVGProps {
|
||||
size?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const LoaderCircle: FC<IProps> = ({ size = 24, className }) => (
|
||||
<div className={classNames(styles.wrap, 'loader-circle', className)}>
|
||||
<LoaderCircleInner size={size} />
|
||||
</div>
|
||||
);
|
29
src/components/common/LoaderCircle/styles.module.scss
Normal file
29
src/components/common/LoaderCircle/styles.module.scss
Normal file
|
@ -0,0 +1,29 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
transform: scale(4);
|
||||
}
|
||||
}
|
||||
|
||||
.wrap {
|
||||
animation: spin infinite 0.75s linear;
|
||||
display: inline-flex;
|
||||
transform-origin: 50% 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
25
src/components/common/LoaderScreen/index.tsx
Normal file
25
src/components/common/LoaderScreen/index.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { LoaderCircle } from '../LoaderCircle';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface LoaderScreenProps {
|
||||
className?: string;
|
||||
align?: 'top' | 'middle';
|
||||
}
|
||||
|
||||
const LoaderScreen: FC<LoaderScreenProps> = ({
|
||||
className,
|
||||
align = 'middle',
|
||||
}) => (
|
||||
<div
|
||||
className={classNames(styles.screen, styles[`align-${align}`], className)}
|
||||
>
|
||||
<LoaderCircle size={32} />
|
||||
</div>
|
||||
);
|
||||
|
||||
export { LoaderScreen };
|
14
src/components/common/LoaderScreen/styles.module.scss
Normal file
14
src/components/common/LoaderScreen/styles.module.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $gap;
|
||||
|
||||
&.align-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { ButtonProps } from '~/utils/types';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface Props extends ButtonProps {}
|
||||
|
||||
const MenuDots: FC<Props> = ({ ...rest }) => (
|
||||
<button {...rest} className={classNames(styles.button, rest.className)}>
|
||||
<div className={styles.dots}>
|
||||
<Icon icon="menu" size={24} />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
export { MenuDots };
|
|
@ -1,39 +0,0 @@
|
|||
@import 'src/styles/variables';
|
||||
@import 'src/styles/mixins';
|
||||
|
||||
.button {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
fill: white;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dots {
|
||||
@include blur;
|
||||
|
||||
padding: 5px 0 0 0;
|
||||
background: $content_bg;
|
||||
border-radius: $radius;
|
||||
width: 18px;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
opacity: 0.3;
|
||||
transition: opacity 0.25s;
|
||||
|
||||
:hover > & {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue