reorganize code into smaller files & dirs
This commit is contained in:
28
packages/app/src/Components/Button/AsyncButton.css
Normal file
28
packages/app/src/Components/Button/AsyncButton.css
Normal file
@ -0,0 +1,28 @@
|
||||
.spinner-wrapper {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.spinner-button > span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.light .spinner-button {
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--font-secondary);
|
||||
box-shadow: rgba(0, 0, 0, 0.08) 0 1px 1px;
|
||||
}
|
||||
|
||||
.light .spinner-button:hover {
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 3px;
|
||||
}
|
||||
|
||||
.light .spinner-button > span {
|
||||
color: black;
|
||||
}
|
34
packages/app/src/Components/Button/AsyncButton.tsx
Normal file
34
packages/app/src/Components/Button/AsyncButton.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import "./AsyncButton.css";
|
||||
import React, { ForwardedRef } from "react";
|
||||
import Spinner from "@/Components/Icons/Spinner";
|
||||
import useLoading from "@/Hooks/useLoading";
|
||||
import classNames from "classnames";
|
||||
|
||||
export interface AsyncButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
onClick?: (e: React.MouseEvent) => Promise<void> | void;
|
||||
}
|
||||
|
||||
const AsyncButton = React.forwardRef<HTMLButtonElement, AsyncButtonProps>((props, ref) => {
|
||||
const { handle, loading } = useLoading(props.onClick, props.disabled);
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref as ForwardedRef<HTMLButtonElement>}
|
||||
type="button"
|
||||
disabled={loading || props.disabled}
|
||||
{...props}
|
||||
className={classNames("spinner-button", props.className)}
|
||||
onClick={handle}>
|
||||
<span style={{ visibility: loading ? "hidden" : "visible" }}>{props.children}</span>
|
||||
{loading && (
|
||||
<span className="spinner-wrapper">
|
||||
<Spinner />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
AsyncButton.displayName = "AsyncButton";
|
||||
|
||||
export default AsyncButton;
|
24
packages/app/src/Components/Button/AsyncIcon.tsx
Normal file
24
packages/app/src/Components/Button/AsyncIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import useLoading from "@/Hooks/useLoading";
|
||||
import Spinner from "@/Components/Icons/Spinner";
|
||||
|
||||
export type AsyncIconProps = React.HTMLProps<HTMLDivElement> & {
|
||||
iconName: string;
|
||||
iconSize?: number;
|
||||
onClick?: (e: React.MouseEvent) => Promise<void> | void;
|
||||
};
|
||||
|
||||
export function AsyncIcon(props: AsyncIconProps) {
|
||||
const { loading, handle } = useLoading(props.onClick, props.disabled);
|
||||
|
||||
const mergedProps = { ...props } as Record<string, unknown>;
|
||||
delete mergedProps["iconName"];
|
||||
delete mergedProps["iconSize"];
|
||||
delete mergedProps["loading"];
|
||||
return (
|
||||
<div {...mergedProps} onClick={handle} className={props.className}>
|
||||
{loading ? <Spinner /> : <Icon name={props.iconName} size={props.iconSize} />}
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
22
packages/app/src/Components/Button/BackButton.css
Normal file
22
packages/app/src/Components/Button/BackButton.css
Normal file
@ -0,0 +1,22 @@
|
||||
.back-button {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: var(--highlight);
|
||||
font-weight: 400;
|
||||
font-size: var(--font-size);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.back-button svg {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.back-button:hover:hover,
|
||||
.light .back-button:hover {
|
||||
text-decoration: underline;
|
||||
box-shadow: none !important;
|
||||
background: none !important;
|
||||
}
|
29
packages/app/src/Components/Button/BackButton.tsx
Normal file
29
packages/app/src/Components/Button/BackButton.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import "./BackButton.css";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
|
||||
import messages from "../messages";
|
||||
|
||||
interface BackButtonProps {
|
||||
text?: string;
|
||||
onClick?(): void;
|
||||
}
|
||||
|
||||
const BackButton = ({ text, onClick }: BackButtonProps) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const onClickHandler = () => {
|
||||
if (onClick) {
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button className="back-button" type="button" onClick={onClickHandler}>
|
||||
<Icon name="arrowBack" />
|
||||
{text || formatMessage(messages.Back)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackButton;
|
15
packages/app/src/Components/Button/CloseButton.tsx
Normal file
15
packages/app/src/Components/Button/CloseButton.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import classNames from "classnames";
|
||||
|
||||
export default function CloseButton({ onClick, className }: { onClick?: () => void; className?: string }) {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={classNames(
|
||||
"self-center circle flex flex-shrink-0 flex-grow-0 items-center justify-center hover:opacity-80 bg-dark p-2 cursor-pointer",
|
||||
className,
|
||||
)}>
|
||||
<Icon name="close" size={12} />
|
||||
</div>
|
||||
);
|
||||
}
|
21
packages/app/src/Components/Button/IconButton.tsx
Normal file
21
packages/app/src/Components/Button/IconButton.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import classNames from "classnames";
|
||||
import Icon, { IconProps } from "@/Components/Icons/Icon";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
interface IconButtonProps {
|
||||
onClick?: () => void;
|
||||
icon: IconProps;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
const IconButton = ({ onClick, icon, children, className }: IconButtonProps) => {
|
||||
return (
|
||||
<button className={classNames("icon", className)} type="button" onClick={onClick}>
|
||||
<Icon {...icon} />
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default IconButton;
|
24
packages/app/src/Components/Button/LogoutButton.tsx
Normal file
24
packages/app/src/Components/Button/LogoutButton.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { logout } from "@/Utils/Login";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import messages from "../messages";
|
||||
|
||||
export default function LogoutButton() {
|
||||
const navigate = useNavigate();
|
||||
const login = useLogin(s => ({ publicKey: s.publicKey, id: s.id }));
|
||||
|
||||
if (!login.publicKey) return;
|
||||
return (
|
||||
<button
|
||||
className="secondary"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
logout(login.id);
|
||||
navigate("/");
|
||||
}}>
|
||||
<FormattedMessage {...messages.Logout} />
|
||||
</button>
|
||||
);
|
||||
}
|
20
packages/app/src/Components/Button/NavLink.tsx
Normal file
20
packages/app/src/Components/Button/NavLink.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { NavLink as RouterNavLink, NavLinkProps, useLocation } from "react-router-dom";
|
||||
|
||||
export default function NavLink(props: NavLinkProps) {
|
||||
const { to, onClick, ...rest } = props;
|
||||
const location = useLocation();
|
||||
|
||||
const isActive = location.pathname === to.toString();
|
||||
|
||||
const handleClick = event => {
|
||||
if (onClick) {
|
||||
onClick(event);
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
}
|
||||
};
|
||||
|
||||
return <RouterNavLink to={to} onClick={handleClick} {...rest} />;
|
||||
}
|
Reference in New Issue
Block a user