Make IconButton its own element, move CopyButton to use it (#357)

This commit is contained in:
Evan Simkowitz
2024-09-09 12:35:53 -07:00
committed by GitHub
parent c3924daac3
commit 9e3c9f9253
14 changed files with 83 additions and 89 deletions
-15
View File
@@ -133,21 +133,6 @@
}
}
.iconbutton {
cursor: pointer;
opacity: 0.7;
align-items: center;
&:hover {
opacity: 1;
}
&.disabled {
cursor: default;
opacity: 0.45 !important;
}
}
.connection-button {
display: flex;
align-items: center;
+5 -5
View File
@@ -8,7 +8,6 @@ import {
ConnectionButton,
ControllerStatusIcon,
getBlockHeaderIcon,
IconButton,
Input,
} from "@/app/block/blockutil";
import { Button } from "@/app/element/button";
@@ -29,6 +28,7 @@ import {
} from "@/app/store/global";
import * as services from "@/app/store/services";
import { WshServer } from "@/app/store/wshserver";
import { IconButton } from "@/element/iconbutton";
import { MagnifyIcon } from "@/element/magnify";
import { NodeModel } from "@/layout/index";
import * as keyutil from "@/util/keyutil";
@@ -88,7 +88,7 @@ function handleHeaderContextMenu(
ContextMenuModel.showContextMenu(menu, e);
}
function getViewIconElem(viewIconUnion: string | HeaderIconButton, blockData: Block): JSX.Element {
function getViewIconElem(viewIconUnion: string | IconButtonDecl, blockData: Block): JSX.Element {
if (viewIconUnion == null || typeof viewIconUnion === "string") {
const viewIcon = viewIconUnion as string;
return <div className="block-frame-view-icon">{getBlockHeaderIcon(viewIcon, blockData)}</div>;
@@ -99,7 +99,7 @@ function getViewIconElem(viewIconUnion: string | HeaderIconButton, blockData: Bl
const OptMagnifyButton = React.memo(
({ magnified, toggleMagnify, disabled }: { magnified: boolean; toggleMagnify: () => void; disabled: boolean }) => {
const magnifyDecl: HeaderIconButton = {
const magnifyDecl: IconButtonDecl = {
elemtype: "iconbutton",
icon: <MagnifyIcon enabled={magnified} />,
title: magnified ? "Minimize" : "Magnify",
@@ -124,7 +124,7 @@ function computeEndIcons(
if (endIconButtons && endIconButtons.length > 0) {
endIconsElem.push(...endIconButtons.map((button, idx) => <IconButton key={idx} decl={button} />));
}
const settingsDecl: HeaderIconButton = {
const settingsDecl: IconButtonDecl = {
elemtype: "iconbutton",
icon: "cog",
title: "Settings",
@@ -139,7 +139,7 @@ function computeEndIcons(
disabled={magnifyDisabled}
/>
);
const closeDecl: HeaderIconButton = {
const closeDecl: IconButtonDecl = {
elemtype: "iconbutton",
icon: "xmark-large",
title: "Close",
-15
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
import { NumActiveConnColors } from "@/app/block/blockframe";
import { useLongClick } from "@/app/hook/useLongClick";
import { getConnStatusAtom, waveEventSubscribe, WOS } from "@/app/store/global";
import * as services from "@/app/store/services";
import { makeORef } from "@/app/store/wos";
@@ -139,20 +138,6 @@ export function getBlockHeaderIcon(blockIcon: string, blockData: Block): React.R
return blockIconElem;
}
export const IconButton = React.memo(({ decl, className }: { decl: HeaderIconButton; className?: string }) => {
const buttonRef = React.useRef<HTMLDivElement>(null);
useLongClick(buttonRef, decl.click, decl.longClick, decl.disabled);
return (
<div
ref={buttonRef}
className={clsx("iconbutton", className, decl.className, { disabled: decl.disabled })}
title={decl.title}
>
{typeof decl.icon === "string" ? <i className={util.makeIconClass(decl.icon, true)} /> : decl.icon}
</div>
);
});
interface ConnectionButtonProps {
connection: string;
changeConnModalAtom: jotai.PrimitiveAtom<boolean>;
+3 -8
View File
@@ -2,15 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
.copy-button {
padding: 5px 5px !important;
opacity: 0.5;
&:hover {
opacity: 1;
}
&.copied {
opacity: 1;
color: var(--success-color);
i {
color: var(--success-color);
}
}
}
+11 -8
View File
@@ -3,8 +3,8 @@
import { clsx } from "clsx";
import { useEffect, useRef, useState } from "react";
import { Button } from "./button";
import "./copybutton.less";
import { IconButton } from "./iconbutton";
type CopyButtonProps = {
title: string;
@@ -43,13 +43,16 @@ const CopyButton = ({ title, className, onClick }: CopyButtonProps) => {
}, []);
return (
<Button
onClick={handleOnClick}
className={clsx("copy-button secondary ghost", className, { copied: isCopied })}
title={title}
>
{isCopied ? <i className="fa-sharp fa-solid fa-check"></i> : <i className="fa-sharp fa-solid fa-copy"></i>}
</Button>
<IconButton
decl={{
elemtype: "iconbutton",
icon: isCopied ? "check" : "copy",
title,
className: clsx("copy-button", { copied: isCopied }),
click: handleOnClick,
}}
className={className}
></IconButton>
);
};
+14
View File
@@ -0,0 +1,14 @@
.iconbutton {
cursor: pointer;
opacity: 0.7;
align-items: center;
&:hover {
opacity: 1;
}
&.disabled {
cursor: default;
opacity: 0.45 !important;
}
}
+19
View File
@@ -0,0 +1,19 @@
import { useLongClick } from "@/app/hook/useLongClick";
import { makeIconClass } from "@/util/util";
import clsx from "clsx";
import { memo, useRef } from "react";
import "./iconbutton.less";
export const IconButton = memo(({ decl, className }: { decl: IconButtonDecl; className?: string }) => {
const buttonRef = useRef<HTMLDivElement>(null);
useLongClick(buttonRef, decl.click, decl.longClick, decl.disabled);
return (
<div
ref={buttonRef}
className={clsx("iconbutton", className, decl.className, { disabled: decl.disabled })}
title={decl.title}
>
{typeof decl.icon === "string" ? <i className={makeIconClass(decl.icon, true)} /> : decl.icon}
</div>
);
});
+4 -21
View File
@@ -85,29 +85,12 @@
top: 0;
right: 0;
border-radius: 4px;
backdrop-filter: blur(8px);
margin: 2px 2px;
padding: 4px 4px;
align-items: center;
justify-content: flex-end;
i {
color: var(--line-actions-inactive-color);
margin-left: 4px;
&:first-child {
margin-left: 0px;
}
&:hover {
color: var(--line-actions-active-color);
}
&.fa-check {
color: var(--success-color);
}
&.fa-square-terminal {
cursor: pointer;
}
}
gap: 4px;
}
&:hover .codeblock-actions {
+11 -1
View File
@@ -17,6 +17,7 @@ import rehypeSlug from "rehype-slug";
import RemarkFlexibleToc, { TocItem } from "remark-flexible-toc";
import remarkGfm from "remark-gfm";
import { openLink } from "../store/global";
import { IconButton } from "./iconbutton";
import "./markdown.less";
const Link = ({
@@ -90,7 +91,16 @@ const CodeBlock = ({ children, onClickExecute }: CodeBlockProps) => {
{children}
<div className="codeblock-actions">
<CopyButton onClick={handleCopy} title="Copy" />
{onClickExecute && <i className="fa-regular fa-square-terminal" onClick={handleExecute}></i>}
{onClickExecute && (
<IconButton
decl={{
elemtype: "iconbutton",
icon: "square-terminal",
click: handleExecute,
className: "fa-regular",
}}
/>
)}
</div>
</pre>
);
+2 -2
View File
@@ -183,7 +183,7 @@ Other useful metadata values to override block titles, icons, colors, themes, et
class HelpViewModel implements ViewModel {
viewType: string;
showTocAtom: PrimitiveAtom<boolean>;
endIconButtons: Atom<HeaderIconButton[]>;
endIconButtons: Atom<IconButtonDecl[]>;
constructor() {
this.viewType = "help";
@@ -195,7 +195,7 @@ class HelpViewModel implements ViewModel {
title: "Table of Contents",
click: () => this.showTocToggle(),
},
] as HeaderIconButton[]);
] as IconButtonDecl[]);
}
showTocToggle() {
+5 -5
View File
@@ -80,11 +80,11 @@ export class PreviewModel implements ViewModel {
blockId: string;
nodeModel: NodeModel;
blockAtom: jotai.Atom<Block>;
viewIcon: jotai.Atom<string | HeaderIconButton>;
viewIcon: jotai.Atom<string | IconButtonDecl>;
viewName: jotai.Atom<string>;
viewText: jotai.Atom<HeaderElem[]>;
preIconButton: jotai.Atom<HeaderIconButton>;
endIconButtons: jotai.Atom<HeaderIconButton[]>;
preIconButton: jotai.Atom<IconButtonDecl>;
endIconButtons: jotai.Atom<IconButtonDecl[]>;
previewTextRef: React.RefObject<HTMLDivElement>;
editMode: jotai.Atom<boolean>;
canPreview: jotai.PrimitiveAtom<boolean>;
@@ -294,7 +294,7 @@ export class PreviewModel implements ViewModel {
icon: "arrows-rotate",
click: () => this.refreshCallback?.(),
},
] as HeaderIconButton[];
] as IconButtonDecl[];
} else if (!isCeView && mimeType.startsWith("text/markdown")) {
return [
{
@@ -303,7 +303,7 @@ export class PreviewModel implements ViewModel {
title: "Table of Contents",
click: () => this.markdownShowTocToggle(),
},
] as HeaderIconButton[];
] as IconButtonDecl[];
}
return null;
});
+3 -3
View File
@@ -44,11 +44,11 @@ export class WaveAiModel implements ViewModel {
viewType: string;
blockId: string;
blockAtom: Atom<Block>;
viewIcon?: Atom<string | HeaderIconButton>;
viewIcon?: Atom<string | IconButtonDecl>;
viewName?: Atom<string>;
viewText?: Atom<string | HeaderElem[]>;
preIconButton?: Atom<HeaderIconButton>;
endIconButtons?: Atom<HeaderIconButton[]>;
preIconButton?: Atom<IconButtonDecl>;
endIconButtons?: Atom<IconButtonDecl[]>;
messagesAtom: PrimitiveAtom<Array<ChatMessageType>>;
addMessageAtom: WritableAtom<unknown, [message: ChatMessageType], void>;
updateLastMessageAtom: WritableAtom<unknown, [text: string, isUpdating: boolean], void>;
+1 -1
View File
@@ -18,7 +18,7 @@ export class WebViewModel implements ViewModel {
viewType: string;
blockId: string;
blockAtom: jotai.Atom<Block>;
viewIcon: jotai.Atom<string | HeaderIconButton>;
viewIcon: jotai.Atom<string | IconButtonDecl>;
viewName: jotai.Atom<string>;
viewText: jotai.Atom<HeaderElem[]>;
url: jotai.PrimitiveAtom<string>;
+5 -5
View File
@@ -148,9 +148,9 @@ declare global {
type SubjectWithRef<T> = rxjs.Subject<T> & { refCount: number; release: () => void };
type HeaderElem = HeaderIconButton | HeaderText | HeaderInput | HeaderDiv | HeaderTextButton | ConnectionButton;
type HeaderElem = IconButtonDecl | HeaderText | HeaderInput | HeaderDiv | HeaderTextButton | ConnectionButton;
type HeaderIconButton = {
type IconButtonDecl = {
elemtype: "iconbutton";
icon: string | React.ReactNode;
className?: string;
@@ -207,11 +207,11 @@ declare global {
interface ViewModel {
viewType: string;
viewIcon?: jotai.Atom<string | HeaderIconButton>;
viewIcon?: jotai.Atom<string | IconButtonDecl>;
viewName?: jotai.Atom<string>;
viewText?: jotai.Atom<string | HeaderElem[]>;
preIconButton?: jotai.Atom<HeaderIconButton>;
endIconButtons?: jotai.Atom<HeaderIconButton[]>;
preIconButton?: jotai.Atom<IconButtonDecl>;
endIconButtons?: jotai.Atom<IconButtonDecl[]>;
blockBg?: jotai.Atom<MetaType>;
manageConnection?: jotai.Atom<boolean>;