reorganize code into smaller files & dirs

This commit is contained in:
Martti Malmi
2024-01-04 15:48:19 +02:00
parent 5ea2eb711f
commit afa6d39a56
321 changed files with 671 additions and 671 deletions

View 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;
}

View 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;

View 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>
);
}

View 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;
}

View 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;

View 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>
);
}

View 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;

View 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>
);
}

View 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} />;
}