working on small history UI updates

This commit is contained in:
sawka 2023-11-02 01:02:50 -07:00
parent d528f3db27
commit 5708b3f19f
6 changed files with 179 additions and 46 deletions

View File

@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Checkbox">
<rect width="16" height="16" rx="4" fill="#58C142"/>
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M12.1757 4.76285C12.5828 5.13604 12.6104 5.76861 12.2372 6.17573L7.79324 11.0236C7.19873 11.6722 6.17628 11.6722 5.58177 11.0236L3.76285 9.03937C3.38966 8.63225 3.41716 7.99968 3.82428 7.62649C4.2314 7.2533 4.86397 7.2808 5.23716 7.68792L6.68751 9.27011L10.7629 4.82428C11.136 4.41716 11.7686 4.38966 12.1757 4.76285Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 574 B

View File

@ -22,6 +22,8 @@
@text-secondary: #C3C8C2;
@text-caption: #8b918a;
@accent-color: #3B3F3A;
@status-outline: #151715;
@dropdown-menu: rgba(21, 23, 21, 1);

View File

@ -9,6 +9,31 @@
fill: @base-color;
}
.history-checkbox {
&.state-unchecked {
width: 16px;
height: 16px;
border-radius: 4px;
border: 1px solid #3B3F3A;
background: rgba(213, 254, 175, 0.03);
}
&.checkbox-icon {
width: 16px;
height: 16px;
position: relative;
top: 2px;
}
&.state-partial {
width: 16px;
height: 16px;
fill: rgba(213, 254, 175, 0.03);
stroke-width: 1px;
stroke: #3B3F3A;
}
}
.is-left {
position: absolute;
left: 0.5em;
@ -152,6 +177,7 @@
margin-top: 10px;
margin-left: 10px;
align-items: center;
height: 32px;
.is-hidden {
display: none;
@ -162,14 +188,20 @@
padding-top: 4px;
}
.trash-icon {
width: 12px;
height: 12px;
fill: @text-secondary;
}
.control-checkbox {
margin: 0.3em 1.2em;
margin-left: 15px;
}
.control-button {
cursor: pointer;
color: #aaa;
margin-left: 10px;
margin-left: 12px;
.icon {
vertical-align: text-bottom;
@ -277,19 +309,21 @@
tr.history-item {
padding: 0 10px 0 10px;
display: flex;
border-top: 1px solid #333;
border-bottom: 1px solid rgba(250, 250, 250, 0.10);
align-items: center;
height: 40px;
color: @text-secondary;
&.is-selected {
background-color: #003;
background-color: #222;
}
&.is-selected:hover {
background-color: #336;
background-color: #333;
}
&:hover {
background-color: #333;
background-color: #222;
td.bookmark i {
display: block;
@ -304,8 +338,7 @@
td.selectbox {
flex: 0 0 auto;
flex-basis: 24px;
margin-right: 1em;
flex-basis: 25px;
cursor: pointer;
}
@ -318,16 +351,15 @@
td.ts {
flex: 0 0 auto;
flex-basis: 74px;
font-weight: bold;
margin-right: 1em;
flex-basis: 86px;
margin-left: 24px;
}
td.workspace {
flex: 0 0 auto;
flex-basis: 120px;
text-overflow: ellipsis;
margin-right: 1em;
margin-left: 24px;
}
td.remote {
@ -337,17 +369,15 @@
padding-right: 5px;
max-width: 150px;
overflow: hidden;
margin-right: 2em;
margin-left: 24px;
}
td.cmdstr {
color: @term-white;
flex: 1 0 0;
padding-left: 20px;
border-radius: 3px;
white-space: pre;
max-height: 70px;
cursor: pointer;
min-width: 300px;
padding-top: 4px;
padding-bottom: 4px;
@ -361,6 +391,21 @@
visibility: visible;
}
}
td.downarrow {
display: flex;
width: 32px;
justify-content: center;
align-items: center;
align-self: stretch;
cursor: pointer;
.down-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
}
}
}
}

View File

@ -27,6 +27,9 @@ import { ReactComponent as SquareCheckIcon } from "../assets/icons/history/squar
import { ReactComponent as SquareMinusIcon } from "../assets/icons/history/square-minus.svg";
import { ReactComponent as SquareIcon } from "../assets/icons/history/square.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";
@ -91,6 +94,86 @@ function formatSessionName(snames: Record<string, string>, sessionId: string): s
return "#" + sname;
}
@mobxReact.observer
class HistoryCheckbox extends React.Component<{ checked: boolean, partialCheck?: boolean, onClick?: () => void }, {}> {
@boundMethod
clickHandler(): void {
if (this.props.onClick) {
this.props.onClick();
}
}
render() {
if (this.props.checked) {
return <CheckedCheckbox onClick={this.clickHandler} className="history-checkbox checkbox-icon" />;
}
if (this.props.partialCheck) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<rect x="0.5" y="0.5" width="15" height="15" rx="3.5" fill="#D5FEAF" fill-opacity="0.026"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 8C4 6.89543 4.89543 6 6 6H10C11.1046 6 12 6.89543 12 8C12 9.10457 11.1046 10 10 10H6C4.89543 10 4 9.10457 4 8Z" fill="#58C142"/>
<rect x="0.5" y="0.5" width="15" height="15" rx="3.5" stroke="#3B3F3A"/>
</svg>
);
}
else {
return <div onClick={this.clickHandler} className="history-checkbox state-unchecked"/>
}
}
}
class HistoryCmdStr extends React.Component<
{
cmdstr: string;
onUse: () => void;
onCopy: () => void;
isCopied: boolean;
fontSize: "normal" | "large";
limitHeight: boolean;
},
{}
> {
@boundMethod
handleUse(e: any) {
e.stopPropagation();
if (this.props.onUse != null) {
this.props.onUse();
}
}
@boundMethod
handleCopy(e: any) {
e.stopPropagation();
if (this.props.onCopy != null) {
this.props.onCopy();
}
}
render() {
let { isCopied, 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="use" className="use-button hoverEffect" title="Use Command" onClick={this.handleUse}>
<CheckIcon className="icon" />
</div>
<div key="code" className="code-div">
<code>{cmdstr}</code>
</div>
<div key="copy" className="copy-control hoverEffect">
<div className="inner-copy" onClick={this.handleCopy} title="copy">
<CopyIcon className="icon" />
</div>
</div>
</div>
);
}
}
@mobxReact.observer
class HistoryView extends React.Component<{}, {}> {
tableRef: React.RefObject<any> = React.createRef();
@ -330,13 +413,6 @@ class HistoryView extends React.Component<{}, {}> {
let hasMore = hvm.hasMore.get();
let offset = hvm.offset.get();
let numSelected = hvm.selectedItems.size;
let controlCheckboxIcon = <SquareIcon className="icon" />;
if (numSelected > 0) {
controlCheckboxIcon = <SquareMinusIcon className="icon" />;
}
if (numSelected > 0 && numSelected == items.length) {
controlCheckboxIcon = <SquareCheckIcon className="icon" />;
}
let activeItemId = hvm.activeItem.get();
let activeItem = hvm.getHistoryItemById(activeItemId);
let activeLine: LineType = null;
@ -376,8 +452,8 @@ class HistoryView extends React.Component<{}, {}> {
<div onClick={this.toggleSessionDropdown}>
<span className="label">
{hvm.searchSessionId.get() == null
? "Limit Workspace"
: formatSessionName(snames, hvm.searchSessionId.get())}
? "Limit Workspace"
: formatSessionName(snames, hvm.searchSessionId.get())}
</span>
<AngleDownIcon className="icon" />
</div>
@ -410,8 +486,8 @@ class HistoryView extends React.Component<{}, {}> {
<div onClick={this.toggleRemoteDropdown}>
<span className="label">
{hvm.searchRemoteId.get() == null
? "Limit Remote"
: formatRemoteName(rnames, { remoteid: hvm.searchRemoteId.get() })}
? "Limit Remote"
: formatRemoteName(rnames, { remoteid: hvm.searchRemoteId.get() })}
</span>
<AngleDownIcon className="icon" />
</div>
@ -486,7 +562,7 @@ class HistoryView extends React.Component<{}, {}> {
</div>
<div className={cn("control-bar", "is-top", { "is-hidden": items.length == 0 })}>
<div className="control-checkbox" onClick={this.handleControlCheckbox} title="Toggle Selection">
{controlCheckboxIcon}
<HistoryCheckbox checked={numSelected > 0 && numSelected == items.length} partialCheck={numSelected > 0}/>
</div>
<div
className={cn(
@ -497,8 +573,8 @@ class HistoryView extends React.Component<{}, {}> {
onClick={this.handleClickDelete}
>
<span>
<TrashIcon className="icon" title="Purge Selected Items" />
&nbsp;Delete Items
<TrashIcon className="trash-icon" title="Purge Selected Items" />
&nbsp;Delete Items
</span>
</div>
<div className="spacer" />
@ -527,21 +603,10 @@ class HistoryView extends React.Component<{}, {}> {
className={cn("history-item", { "is-selected": hvm.selectedItems.get(item.historyid) })}
>
<td className="selectbox" onClick={() => this.handleSelect(item.historyid)}>
<If condition={hvm.selectedItems.get(item.historyid)}>
<SquareCheckIcon className="icon" />
</If>
<If condition={!hvm.selectedItems.get(item.historyid)}>
<SquareIcon className="icon" />
</If>
<HistoryCheckbox checked={hvm.selectedItems.get(item.historyid)}/>
</td>
<td className="bookmark" style={{ display: "none" }}>
<FavoritesIcon className="icon" />
</td>
<td className="ts">{getHistoryViewTs(nowDate, item.ts)}</td>
<td className="workspace">{formatSSName(snames, scrnames, item)}</td>
<td className="remote">{formatRemoteName(rnames, item.remote)}</td>
<td className="cmdstr" onClick={() => this.activateItem(item.historyid)}>
<CmdStrCode
<td className="cmdstr">
<HistoryCmdStr
cmdstr={item.cmdstr}
onUse={() => this.handleUse(item)}
onCopy={() => this.handleCopy(item)}
@ -550,6 +615,21 @@ class HistoryView extends React.Component<{}, {}> {
limitHeight={true}
/>
</td>
<td className="workspace text-standard">{formatSSName(snames, scrnames, item)}</td>
<td className="remote text-standard">{formatRemoteName(rnames, item.remote)}</td>
<td className="ts text-standard">{getHistoryViewTs(nowDate, item.ts)}</td>
<td className="downarrow" onClick={() => this.activateItem(item.historyid)}>
<If condition={activeItemId != item.historyid}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M12.1297 6.62492C12.3999 6.93881 12.3645 7.41237 12.0506 7.68263L8.48447 10.7531C8.20296 10.9955 7.78645 10.9952 7.50519 10.7526L3.94636 7.68213C3.63274 7.41155 3.59785 6.93796 3.86843 6.62434C4.13901 6.31072 4.6126 6.27583 4.92622 6.54641L7.99562 9.19459L11.0719 6.54591C11.3858 6.27565 11.8594 6.31102 12.1297 6.62492Z" fill="#C3C8C2"/>
</svg>
</If>
<If condition={activeItemId == item.historyid}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3.87035 9.37508C3.60009 9.06119 3.63546 8.58763 3.94936 8.31737L7.51553 5.24692C7.79704 5.00455 8.21355 5.00476 8.49481 5.24742L12.0536 8.31787C12.3673 8.58845 12.4022 9.06204 12.1316 9.37566C11.861 9.68928 11.3874 9.72417 11.0738 9.45359L8.00438 6.80541L4.92806 9.45409C4.61416 9.72435 4.14061 9.68898 3.87035 9.37508Z" fill="#C3C8C2"/>
</svg>
</If>
</td>
</tr>
<If condition={activeItemId == item.historyid}>
<tr className="active-history-item">

View File

@ -21,7 +21,6 @@ let MagicLayout = {
// the 3 is for descenders, which get cut off in the terminal without this
TermDescendersHeight: 3,
TermWidthBuffer: 15,
};
let m = MagicLayout;

View File

@ -13,10 +13,11 @@ import "./markdown.less";
type OV<V> = mobx.IObservableValue<V>;
const MaxMarkdownSize = 200000;
const DefaultMaxMarkdownWidth = 1000;
@mobxReact.observer
class SimpleMarkdownRenderer extends React.Component<
{ data: T.ExtBlob; context: T.RendererContext; opts: T.RendererOpts; savedHeight: number },
{ data: T.ExtBlob; context: T.RendererContext; opts: T.RendererOpts; savedHeight: number, lineState: T.LineStateType },
{}
> {
markdownText: OV<string> = mobx.observable.box(null, { name: "markdownText" });
@ -73,7 +74,7 @@ class SimpleMarkdownRenderer extends React.Component<
maxHeight: opts.maxSize.height,
}}
>
<Markdown text={this.markdownText.get()} style={{ maxHeight: opts.maxSize.height }} />
<Markdown text={this.markdownText.get()} style={{ maxHeight: opts.maxSize.height, maxWidth: DefaultMaxMarkdownWidth }} />
</div>
</div>
);