chore: cleanup AsyncIcon element
This commit is contained in:
parent
497ef7bf9a
commit
dec2b9ce2e
@ -360,5 +360,9 @@
|
||||
<symbol id="x" viewBox="0 0 24 25" fill="none">
|
||||
<path d="M17.7071 8.20711C18.0976 7.81658 18.0976 7.18342 17.7071 6.79289C17.3166 6.40237 16.6834 6.40237 16.2929 6.79289L12 11.0858L7.70711 6.79289C7.31658 6.40237 6.68342 6.40237 6.29289 6.79289C5.90237 7.18342 5.90237 7.81658 6.29289 8.20711L10.5858 12.5L6.29289 16.7929C5.90237 17.1834 5.90237 17.8166 6.29289 18.2071C6.68342 18.5976 7.31658 18.5976 7.70711 18.2071L12 13.9142L16.2929 18.2071C16.6834 18.5976 17.3166 18.5976 17.7071 18.2071C18.0976 17.8166 18.0976 17.1834 17.7071 16.7929L13.4142 12.5L17.7071 8.20711Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
<symbol id="settings-04" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M11.772 7.4987L2.50033 7.4987C2.04009 7.4987 1.66699 7.1256 1.66699 6.66536C1.66699 6.20513 2.04009 5.83203 2.50033 5.83203L11.772 5.83203C12.142 4.39434 13.4471 3.33203 15.0003 3.33203C16.8413 3.33203 18.3337 4.82442 18.3337 6.66536C18.3337 8.50631 16.8413 9.9987 15.0003 9.9987C13.4471 9.9987 12.142 8.93639 11.772 7.4987Z" fill="currentColor"/>
|
||||
<path d="M5.00033 9.9987C3.15938 9.9987 1.66699 11.4911 1.66699 13.332C1.66699 15.173 3.15938 16.6654 5.00033 16.6654C6.55352 16.6654 7.85861 15.6031 8.22864 14.1654L17.5003 14.1654C17.9606 14.1654 18.3337 13.7923 18.3337 13.332C18.3337 12.8718 17.9606 12.4987 17.5003 12.4987L8.22864 12.4987C7.85861 11.061 6.55352 9.9987 5.00033 9.9987Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 97 KiB |
@ -1,31 +1,15 @@
|
||||
import "./AsyncButton.css";
|
||||
import React, { useState, ForwardedRef } from "react";
|
||||
import React, { ForwardedRef } from "react";
|
||||
import Spinner from "../Icons/Spinner";
|
||||
import useLoading from "Hooks/useLoading";
|
||||
import classNames from "classnames";
|
||||
|
||||
interface AsyncButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
disabled?: boolean;
|
||||
onClick(e: React.MouseEvent): Promise<void> | void;
|
||||
children?: React.ReactNode;
|
||||
export interface AsyncButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
onClick?: (e: React.MouseEvent) => Promise<void> | void;
|
||||
}
|
||||
|
||||
const AsyncButton = React.forwardRef<HTMLButtonElement, AsyncButtonProps>((props, ref) => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
async function handle(e: React.MouseEvent) {
|
||||
e.stopPropagation();
|
||||
if (loading || props.disabled) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
if (typeof props.onClick === "function") {
|
||||
const f = props.onClick(e);
|
||||
if (f instanceof Promise) {
|
||||
await f;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
const { handle, loading } = useLoading(props.onClick, props.disabled);
|
||||
|
||||
return (
|
||||
<button
|
||||
@ -33,7 +17,7 @@ const AsyncButton = React.forwardRef<HTMLButtonElement, AsyncButtonProps>((props
|
||||
type="button"
|
||||
disabled={loading || props.disabled}
|
||||
{...props}
|
||||
className={`spinner-button${props.className ? ` ${props.className}` : ""}`}
|
||||
className={classNames("spinner-button", props.className)}
|
||||
onClick={handle}>
|
||||
<span style={{ visibility: loading ? "hidden" : "visible" }}>{props.children}</span>
|
||||
{loading && (
|
||||
|
@ -1,35 +1,23 @@
|
||||
import Icon from "Icons/Icon";
|
||||
import useLoading from "Hooks/useLoading";
|
||||
import Spinner from "Icons/Spinner";
|
||||
import { HTMLProps, useState } from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
export interface AsyncIconProps extends HTMLProps<HTMLDivElement> {
|
||||
export type AsyncIconProps = React.HTMLProps<HTMLDivElement> & {
|
||||
iconName: string;
|
||||
iconSize?: number;
|
||||
loading?: boolean;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => Promise<void>;
|
||||
}
|
||||
onClick?: (e: React.MouseEvent) => Promise<void> | void;
|
||||
};
|
||||
|
||||
export function AsyncIcon(props: AsyncIconProps) {
|
||||
const [loading, setLoading] = useState(props.loading ?? false);
|
||||
|
||||
async function handleClick(e: React.MouseEvent<HTMLDivElement>) {
|
||||
setLoading(true);
|
||||
try {
|
||||
if (props.onClick) {
|
||||
await props.onClick(e);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
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={e => handleClick(e)}>
|
||||
<div {...mergedProps} onClick={handle} className={classNames("button-icon-sm", props.className)}>
|
||||
{loading ? <Spinner /> : <Icon name={props.iconName} size={props.iconSize} />}
|
||||
{props.children}
|
||||
</div>
|
||||
|
@ -211,7 +211,7 @@ export function NoteCreator() {
|
||||
});
|
||||
}
|
||||
|
||||
async function onSubmit(ev: React.MouseEvent<HTMLButtonElement>) {
|
||||
async function onSubmit(ev: React.MouseEvent) {
|
||||
ev.stopPropagation();
|
||||
await sendNote();
|
||||
}
|
||||
@ -450,14 +450,20 @@ export function NoteCreator() {
|
||||
showFollowingMark={false}
|
||||
/>
|
||||
{note.pollOptions === undefined && !note.replyTo && (
|
||||
<div className="note-creator-icon">
|
||||
<Icon name="pie-chart" onClick={() => note.update(v => (v.pollOptions = ["A", "B"]))} size={24} />
|
||||
</div>
|
||||
<AsyncIcon
|
||||
iconName="pie-chart"
|
||||
iconSize={24}
|
||||
onClick={() => note.update(v => (v.pollOptions = ["A", "B"]))}
|
||||
className="note-creator-icon"
|
||||
/>
|
||||
)}
|
||||
<AsyncIcon iconName="image-plus" iconSize={24} onClick={attachFile} className="note-creator-icon" />
|
||||
<button className="secondary" onClick={() => note.update(v => (v.advanced = !v.advanced))}>
|
||||
<FormattedMessage defaultMessage="Advanced" />
|
||||
</button>
|
||||
<AsyncIcon
|
||||
iconName="settings-04"
|
||||
iconSize={24}
|
||||
onClick={() => note.update(v => (v.advanced = !v.advanced))}
|
||||
className="note-creator-icon"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex g8">
|
||||
<button className="secondary" onClick={cancel}>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React, { HTMLProps, useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useLongPress } from "use-long-press";
|
||||
import { TaggedNostrEvent, ParsedZap, countLeadingZeros, NostrLink } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||
import classNames from "classnames";
|
||||
|
||||
import { formatShort } from "Number";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
@ -11,7 +12,7 @@ import { delay, findTag, normalizeReaction } from "SnortUtils";
|
||||
import { NoteCreator } from "Element/Event/NoteCreator";
|
||||
import SendSats from "Element/SendSats";
|
||||
import { ZapsSummary } from "Element/Event/Zap";
|
||||
import { AsyncIcon } from "Element/AsyncIcon";
|
||||
import { AsyncIcon, AsyncIconProps } from "Element/AsyncIcon";
|
||||
|
||||
import { useWallet } from "Wallet";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
@ -308,18 +309,11 @@ export default function NoteFooter(props: NoteFooterProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface AsyncFooterIconProps extends HTMLProps<HTMLDivElement> {
|
||||
iconName: string;
|
||||
value: number;
|
||||
loading?: boolean;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => Promise<void>;
|
||||
}
|
||||
|
||||
function AsyncFooterIcon(props: AsyncFooterIconProps) {
|
||||
function AsyncFooterIcon(props: AsyncIconProps & { value: number }) {
|
||||
const mergedProps = {
|
||||
...props,
|
||||
iconSize: 18,
|
||||
className: `transition duration-200 ease-in-out reaction-pill${props.className ? ` ${props.className}` : ""}`,
|
||||
className: classNames("transition duration-200 ease-in-out reaction-pill", props.className),
|
||||
};
|
||||
return (
|
||||
<AsyncIcon {...mergedProps}>
|
||||
|
20
packages/app/src/Hooks/useLoading.tsx
Normal file
20
packages/app/src/Hooks/useLoading.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { useState } from "react";
|
||||
|
||||
export default function useLoading<T>(fn: ((e: React.MouseEvent) => Promise<T> | T) | undefined, disabled?: boolean) {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
async function handle(e: React.MouseEvent) {
|
||||
e.stopPropagation();
|
||||
if (loading || disabled) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
if (typeof fn === "function") {
|
||||
await fn(e);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return { handle, loading };
|
||||
}
|
@ -24,6 +24,7 @@ import { getDisplayName } from "Element/User/DisplayName";
|
||||
import { Day } from "Const";
|
||||
import Tabs, { Tab } from "Element/Tabs";
|
||||
import classNames from "classnames";
|
||||
import { AsyncIcon } from "Element/AsyncIcon";
|
||||
|
||||
function notificationContext(ev: TaggedNostrEvent) {
|
||||
switch (ev.kind) {
|
||||
@ -194,15 +195,15 @@ function NotificationSummary({ evs }: { evs: Array<TaggedNostrEvent> }) {
|
||||
);
|
||||
}, [evs, period]);
|
||||
|
||||
const filterIcon = (f: NotificationSummaryFilter, icon: string, iconActiveClass?: string) => {
|
||||
const filterIcon = (f: NotificationSummaryFilter, icon: string, iconActiveClass: string) => {
|
||||
const active = hasFlag(filter, f);
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={classNames("icon-sm transparent", { active: active })}
|
||||
onClick={() => setFilter(v => v ^ f)}>
|
||||
<Icon name={icon} className={active ? iconActiveClass : undefined} />
|
||||
</button>
|
||||
<AsyncIcon
|
||||
className={classNames("button-icon-sm transparent", { active, [iconActiveClass]: active })}
|
||||
onClick={() => setFilter(v => v ^ f)}
|
||||
name={""}
|
||||
iconName={icon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -337,20 +337,15 @@ button.icon:hover {
|
||||
color: var(--highlight);
|
||||
}
|
||||
|
||||
button.icon-sm {
|
||||
.button-icon-sm {
|
||||
padding: 4px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: var(--gray-light) !important;
|
||||
}
|
||||
|
||||
button.icon-sm:not(.active):hover {
|
||||
background-color: var(--gray-dark);
|
||||
}
|
||||
|
||||
button.icon-sm.active {
|
||||
.button-icon-sm.active {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
|
@ -120,9 +120,6 @@
|
||||
"3KNMbJ": {
|
||||
"defaultMessage": "Articles"
|
||||
},
|
||||
"3Rx6Qo": {
|
||||
"defaultMessage": "Advanced"
|
||||
},
|
||||
"3cc4Ct": {
|
||||
"defaultMessage": "Light"
|
||||
},
|
||||
|
@ -39,7 +39,6 @@
|
||||
"2k0Cv+": "Dislikes ({n})",
|
||||
"2ukA4d": "{n} hours",
|
||||
"3KNMbJ": "Articles",
|
||||
"3Rx6Qo": "Advanced",
|
||||
"3cc4Ct": "Light",
|
||||
"3gOsZq": "Translators",
|
||||
"3qnJlS": "You are voting with {amount} sats",
|
||||
|
Loading…
x
Reference in New Issue
Block a user