Copy button (#550)

* cop button

* cleanup

* fix wrong type

* updates to try to set the cmdinput position (as well as text).  fix button alignment, change checkmark to green (and extend), and remove the transition from parent component and move to copy (sawka)
This commit is contained in:
Red J Adaya 2024-04-06 03:06:04 +08:00 committed by GitHub
parent 84cea373a8
commit 5a6575a393
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 85 additions and 36 deletions

View File

@ -6,7 +6,7 @@ import "./button.less";
interface ButtonProps {
children: React.ReactNode;
onClick?: () => void;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
disabled?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
@ -14,6 +14,7 @@ interface ButtonProps {
autoFocus?: boolean;
className?: string;
termInline?: boolean;
title?: string;
}
class Button extends React.Component<ButtonProps> {
@ -23,14 +24,14 @@ class Button extends React.Component<ButtonProps> {
};
@boundMethod
handleClick() {
handleClick(e) {
if (this.props.onClick && !this.props.disabled) {
this.props.onClick();
this.props.onClick(e);
}
}
render() {
const { leftIcon, rightIcon, children, disabled, style, autoFocus, termInline, className } = this.props;
const { leftIcon, rightIcon, children, disabled, style, autoFocus, termInline, className, title } = this.props;
return (
<button
@ -39,6 +40,7 @@ class Button extends React.Component<ButtonProps> {
disabled={disabled}
style={style}
autoFocus={autoFocus}
title={title}
>
{leftIcon && <span className="icon-left">{leftIcon}</span>}
{children}

View File

@ -0,0 +1,7 @@
.copy-button {
padding: 5px 5px;
.fa-check {
color: var(--app-success-color);
}
}

View File

@ -0,0 +1,51 @@
import * as React from "react";
import { Button } from "./button";
import { boundMethod } from "autobind-decorator";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import "./copybutton.less";
type CopyButtonProps = {
title: string;
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
};
@mobxReact.observer
class CopyButton extends React.Component<CopyButtonProps, {}> {
isCopied: OV<boolean> = mobx.observable.box(false, { name: "isCopied" });
@boundMethod
handleOnClick(e: React.MouseEvent<HTMLButtonElement>) {
if (this.isCopied.get()) {
return;
}
mobx.action(() => {
this.isCopied.set(true);
})();
setTimeout(() => {
mobx.action(() => {
this.isCopied.set(false);
})();
}, 2000);
if (this.props.onClick) {
this.props.onClick(e);
}
}
render() {
const { title, onClick } = this.props;
const isCopied = this.isCopied.get();
return (
<Button onClick={this.handleOnClick} className="copy-button secondary ghost" title={title}>
{isCopied ? (
<i className="fa-sharp fa-solid fa-check"></i>
) : (
<i className="fa-sharp fa-solid fa-copy"></i>
)}
</Button>
);
}
}
export { CopyButton };

View File

@ -18,3 +18,4 @@ export { Toggle } from "./toggle";
export { Tooltip } from "./tooltip";
export { TabIcon } from "./tabicon";
export { DatePicker } from "./datepicker";
export { CopyButton } from "./copybutton";

View File

@ -357,6 +357,10 @@
cursor: pointer;
}
.wave-button {
padding: 5px 5px;
}
visibility: hidden;
}

View File

@ -14,7 +14,7 @@ import localizedFormat from "dayjs/plugin/localizedFormat";
import customParseFormat from "dayjs/plugin/customParseFormat";
import { Line } from "@/app/line/linecomps";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import { TextField, Dropdown, Button, DatePicker } from "@/elements";
import { TextField, Dropdown, Button, CopyButton } from "@/elements";
import { ReactComponent as ChevronLeftIcon } from "@/assets/icons/history/chevron-left.svg";
import { ReactComponent as ChevronRightIcon } from "@/assets/icons/history/chevron-right.svg";
@ -22,8 +22,6 @@ import { ReactComponent as RightIcon } from "@/assets/icons/history/right.svg";
import { ReactComponent as SearchIcon } from "@/assets/icons/history/search.svg";
import { ReactComponent as TrashIcon } from "@/assets/icons/trash.svg";
import { ReactComponent as CheckedCheckbox } from "@/assets/icons/checked-checkbox.svg";
import { ReactComponent as CheckIcon } from "@/assets/icons/line/check.svg";
import { ReactComponent as CopyIcon } from "@/assets/icons/history/copy.svg";
import "./history.less";
import { MainView } from "../common/elements/mainview";
@ -115,7 +113,6 @@ class HistoryCmdStr extends React.Component<
cmdstr: string;
onUse: () => void;
onCopy: () => void;
isCopied: boolean;
fontSize: "normal" | "large";
limitHeight: boolean;
},
@ -138,24 +135,17 @@ class HistoryCmdStr extends React.Component<
}
render() {
const { isCopied, cmdstr, fontSize, limitHeight } = this.props;
const { cmdstr, fontSize, limitHeight } = this.props;
return (
<div className={cn("cmdstr-code", { "is-large": fontSize == "large" }, { "limit-height": limitHeight })}>
<If condition={isCopied}>
<div key="copied" className="copied-indicator">
<div>copied</div>
</div>
</If>
<div key="code" className="code-div">
<code>{cmdstr}</code>
</div>
<div key="copy" className="actions-block">
<div className="action-item" onClick={this.handleCopy} title="copy">
<CopyIcon className="icon" />
</div>
<div key="use" className="action-item" title="Use Command" onClick={this.handleUse}>
<CheckIcon className="icon" />
</div>
<CopyButton onClick={this.handleCopy} title="Copy" />
<Button className="secondary ghost" title="Use Command" onClick={this.handleUse}>
<i className="fa-sharp fa-solid fa-play"></i>
</Button>
</div>
</div>
);
@ -190,7 +180,6 @@ class HistoryView extends React.Component<{}, {}> {
tableRszObs: ResizeObserver;
sessionDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "sessionDropdownActive" });
remoteDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "remoteDropdownActive" });
copiedItemId: OV<string> = mobx.observable.box(null, { name: "copiedItemId" });
@boundMethod
handleNext() {
@ -377,14 +366,6 @@ class HistoryView extends React.Component<{}, {}> {
return;
}
navigator.clipboard.writeText(item.cmdstr);
mobx.action(() => {
this.copiedItemId.set(item.historyid);
})();
setTimeout(() => {
mobx.action(() => {
this.copiedItemId.set(null);
})();
}, 600);
}
@boundMethod
@ -394,7 +375,7 @@ class HistoryView extends React.Component<{}, {}> {
}
mobx.action(() => {
GlobalModel.showSessionView();
GlobalModel.inputModel.setCurLine(item.cmdstr);
GlobalModel.inputModel.updateCmdLine({ str: item.cmdstr, pos: item.cmdstr.length });
setTimeout(() => GlobalModel.inputModel.giveFocus(), 50);
})();
}
@ -569,7 +550,6 @@ class HistoryView extends React.Component<{}, {}> {
cmdstr={item.cmdstr}
onUse={() => this.handleUse(item)}
onCopy={() => this.handleCopy(item)}
isCopied={this.copiedItemId.get() == item.historyid}
fontSize="normal"
limitHeight={true}
/>

View File

@ -253,9 +253,9 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
controlRef: React.RefObject<HTMLDivElement> = React.createRef();
lastHeight: number = 0;
lastSP: StrWithPos = { str: "", pos: appconst.NoStrPos };
version: OV<number> = mobx.observable.box(0); // forces render updates
mainInputFocused: OV<boolean> = mobx.observable.box(true);
historyFocused: OV<boolean> = mobx.observable.box(false);
version: OV<number> = mobx.observable.box(0, { name: "textAreaInput-version" }); // forces render updates
mainInputFocused: OV<boolean> = mobx.observable.box(true, { name: "textAreaInput-mainInputFocused" });
historyFocused: OV<boolean> = mobx.observable.box(false, { name: "textAreaInput-historyFocused" });
incVersion(): void {
const v = this.version.get();
@ -288,9 +288,13 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
setFocus(): void {
const inputModel = GlobalModel.inputModel;
if (inputModel.historyFocus.get()) {
this.historyInputRef.current.focus();
if (this.historyInputRef.current != null && document.activeElement != this.historyInputRef.current) {
this.historyInputRef.current.focus();
}
} else {
this.mainInputRef.current.focus();
if (this.mainInputRef.current != null && document.activeElement != this.mainInputRef.current) {
this.mainInputRef.current.focus();
}
}
}