Implemented client rename and some small channel tree optimisations

This commit is contained in:
WolverinDEV 2020-09-26 22:53:33 +02:00
parent 1aedcfc0ac
commit 6fc3ac4c14
10 changed files with 90 additions and 32 deletions

View file

@ -642,7 +642,8 @@ export class ChannelTree {
contextmenu.Entry.CLOSE(on_close)
);
}
private open_multiselect_context_menu(entries: ChannelTreeEntry<any>[], x: number, y: number) {
public open_multiselect_context_menu(entries: ChannelTreeEntry<any>[], x: number, y: number) {
const clients = entries.filter(e => e instanceof ClientEntry) as ClientEntry[];
const channels = entries.filter(e => e instanceof ChannelEntry) as ChannelEntry[];
const server = entries.find(e => e instanceof ServerEntry) as ServerEntry;

View file

@ -16,13 +16,11 @@
margin-left: -16px;
text-align: center;
flex-shrink: 0;
&.down {
align-self: normal;
}
:global .arrow {
border-color: hsla(220, 5%, 30%, 1);
}
}
.channelType {

View file

@ -18,6 +18,10 @@ html:root {
margin-right: 2px;
}
.statusIcon {
flex-shrink: 0;
}
.clientName {
line-height: 16px;
min-width: 2em;

View file

@ -109,6 +109,7 @@ class ChannelTreeController {
this.channelTree.events.register_handler(this);
this.channelTree.channels.forEach(channel => this.initializeChannelEvents(channel));
this.channelTree.clients.forEach(client => this.initializeClientEvents(client));
}
destroy() {
@ -177,7 +178,6 @@ class ChannelTreeController {
/* general channel tree event handlers */
@EventHandler<ChannelTreeEvents>("notify_channel_list_received")
private handleChannelListReceived() {
console.error("Channel list received");
this.channelTreeInitialized = true;
this.channelTree.channels.forEach(channel => this.initializeChannelEvents(channel));
this.channelTree.clients.forEach(channel => this.initializeClientEvents(channel));
@ -656,7 +656,7 @@ function initializeTreeController(events: Registry<ChannelTreeUIEvents>, channel
}
if (channelTree.selection.is_multi_select() && entry.isSelected()) {
/* TODO: Spawn the context menu! */
channelTree.open_multiselect_context_menu(channelTree.selection.selected_entries, event.pageX, event.pageY);
return;
}

View file

@ -92,7 +92,7 @@ const ChannelName = React.memo((props: { channelName: string | undefined, alignm
const ChannelCollapsedIndicator = (props: { collapsed: boolean, onToggle: () => void }) => {
return <div className={channelStyle.containerArrow + (!props.collapsed ? " " + channelStyle.down : "")}>
<div className={"arrow " + (props.collapsed ? "right" : "down")} onClick={event => {
<div className={viewStyle.arrow + " " + (props.collapsed ? viewStyle.right : viewStyle.down)} onClick={event => {
event.preventDefault();
props.onToggle();
}}/>
@ -126,7 +126,7 @@ export class RendererChannel extends React.Component<{ channel: RDPChannel }, {}
return (
<div
className={viewStyle.treeEntry + " " + channelStyle.channelEntry + " " + (channel.selected ? viewStyle.selected : "")}
style={{ paddingLeft: channel.offsetLeft, top: channel.offsetTop }}
style={{ top: channel.offsetTop }}
onMouseUp={event => {
if (event.button !== 0) {
return; /* only left mouse clicks */
@ -156,6 +156,7 @@ export class RendererChannel extends React.Component<{ channel: RDPChannel }, {}
events.fire("action_channel_open_file_browser", { treeEntryId: entryId });
}}
>
<div className={viewStyle.leftPadding} style={{ paddingLeft: channel.offsetLeft + "em" }} />
<UnreadMarkerRenderer entry={this.props.channel} ref={this.props.channel.refUnread} />
{collapsedIndicator}
{channelIcon}

View file

@ -14,7 +14,7 @@ const viewStyle = require("./View.scss");
/* TODO: Render a talk power request */
export class ClientStatus extends React.Component<{ client: RDPClient }, {}> {
render() {
return <IconRenderer icon={this.props.client.status} />
return <IconRenderer icon={this.props.client.status} className={clientStyle.statusIcon} />
}
}
@ -115,11 +115,34 @@ interface ClientNameEditProps {
initialName: string;
}
declare global{
interface HTMLElement {
createTextRange;
}
}
function selectText(node: HTMLElement) {
if (document.body.createTextRange) {
const range = document.body.createTextRange();
range.moveToElementText(node);
range.select();
} else if (window.getSelection) {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges();
selection.addRange(range);
} else {
console.warn("Could not select text in node: Unsupported browser.");
}
}
class ClientNameEdit extends React.Component<ClientNameEditProps, {}> {
private readonly refDiv: React.RefObject<HTMLDivElement> = React.createRef();
componentDidMount(): void {
this.refDiv.current.focus();
selectText(this.refDiv.current);
}
render() {
@ -145,7 +168,6 @@ class ClientNameEdit extends React.Component<ClientNameEditProps, {}> {
}
}
/* TODO: Client rename! */
export class RendererClient extends React.Component<{ client: RDPClient }, {}> {
render() {
const client = this.props.client;
@ -154,7 +176,7 @@ export class RendererClient extends React.Component<{ client: RDPClient }, {}> {
return (
<div className={clientStyle.clientEntry + " " + viewStyle.treeEntry + " " + (selected ? viewStyle.selected : "")}
style={{ paddingLeft: client.offsetLeft, top: client.offsetTop }}
style={{ top: client.offsetTop }}
onContextMenu={event => {
if (settings.static(Settings.KEY_DISABLE_CONTEXT_MENU)) {
return;
@ -176,6 +198,7 @@ export class RendererClient extends React.Component<{ client: RDPClient }, {}> {
}}
onDoubleClick={() => events.fire("action_client_double_click", { treeEntryId: client.entryId })}
>
<div className={viewStyle.leftPadding} style={{ paddingLeft: client.offsetLeft + "em" }} />
<UnreadMarkerRenderer entry={client} ref={client.refUnread} />
<ClientStatus client={client} ref={client.refStatus} />
{...(client.rename ? [

View file

@ -209,7 +209,6 @@ export class RDPChannelTree {
@EventHandler<ChannelTreeUIEvents>("notify_tree_entries")
private handleNotifyTreeEntries(event: ChannelTreeUIEvents["notify_tree_entries"]) {
console.error("Having entries");
const oldEntryInstances = this.treeEntries;
this.treeEntries = {};
@ -241,12 +240,11 @@ export class RDPChannelTree {
}
this.treeEntries[entry.entryId] = result;
result.handlePositionUpdate(index * ChannelTreeView.EntryHeight, entry.depth * 16 + 2);
result.handlePositionUpdate(index * ChannelTreeView.EntryHeight, entry.depth);
return result;
}).filter(e => !!e);
console.error("Obsolete entries: %o", oldEntryInstances);
Object.keys(oldEntryInstances).map(key => oldEntryInstances[key]).forEach(entry => {
entry.destroy();
});
@ -473,9 +471,9 @@ export class RDPClient extends RDPEntry {
handleOpenRename(initialValue: string) {
if(!initialValue) {
this.refClient.current?.forceUpdate();
this.rename = false;
this.renameDefault = undefined;
this.refClient.current?.forceUpdate();
return;
}
if(!this.handle.refTree.current || !this.refClient.current) {

View file

@ -38,7 +38,7 @@ export class ServerRenderer extends React.Component<{ server: RDPServer }, {}> {
return (
<div
className={serverStyle.serverEntry + " " + viewStyle.treeEntry + " " + (selected ? viewStyle.selected : "")}
style={{ paddingLeft: server.offsetLeft, top: server.offsetTop }}
style={{ top: server.offsetTop }}
onMouseUp={event => {
if (event.button !== 0) {
return; /* only left mouse clicks */
@ -59,6 +59,7 @@ export class ServerRenderer extends React.Component<{ server: RDPServer }, {}> {
events.fire("action_show_context_menu", { treeEntryId: server.entryId, pageX: event.pageX, pageY: event.pageY });
}}
>
<div className={viewStyle.leftPadding} style={{ paddingLeft: server.offsetLeft + "em" }} />
<UnreadMarkerRenderer entry={server} ref={server.refUnread} />
<div className={"icon client-server_green " + serverStyle.server_type}/>
<div className={serverStyle.name}>{name}</div>

View file

@ -29,21 +29,6 @@ export interface ChannelTreeViewState {
tree: RDPEntry[];
}
/*
export function renderFlatTreeEntry(entry: FlatTreeEntry) {
if(entry.rendered) { return entry.rendered; }
if(entry.type === "channel") {
entry.rendered = <RendererChannel entryId={entry.entryId} offsetTop={entry.index * ChannelTreeView.EntryHeight} offsetLeft={entry.depth * 16 + 2} key={entry.entryId} />;
} else if(entry.type === "client" || entry.type === "client-local") {
entry.rendered = <RendererClient entryId={entry.entryId} offsetTop={entry.index * ChannelTreeView.EntryHeight} offsetLeft={entry.depth * 16 + 2} key={entry.entryId} localClient={entry.type === "client-local"} />;
} else {
entry.rendered = <div className={viewStyle.treeEntry} style={{ top: entry.index * ChannelTreeView.EntryHeight + "px" }} key={entry.entryId}><a>{entry.type + " - " + entry.entryId}</a></div>;
}
return entry.rendered;
}
*/
@ReactEventHandler<ChannelTreeView>(e => e.props.events)
@BatchUpdateAssignment(BatchUpdateType.CHANNEL_TREE)
export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewProperties, ChannelTreeViewState> {

View file

@ -91,6 +91,13 @@ html:root {
}
}
.leftPadding {
flex-shrink: 0;
padding-left: 2px;
font-size: 16px;
}
&.move {
.treeEntry.selected {
background-color: var(--channel-tree-entry-move);
@ -113,4 +120,44 @@ html:root {
&.smoothScroll {
scroll-behavior: smooth;
}
}
.arrow {
display: inline-block;
border: solid hsla(220, 5%, 30%, 1);
border-width: 0 .2em .2em 0;
padding: .21em;
height: .5em;
width: .5em;
&.right {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
&.left {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
&.up {
transform: rotate(-135deg);
-webkit-transform: rotate(-135deg);
}
&.down {
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
}
}
@media all and (max-width: 250px) {
.channelTree .leftPadding {
font-size: 6px;
}
.treeEntry {
padding-left: 10px; /* space for the arrow */
}
}