Implemented client rename and some small channel tree optimisations
This commit is contained in:
parent
1aedcfc0ac
commit
6fc3ac4c14
10 changed files with 90 additions and 32 deletions
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -18,6 +18,10 @@ html:root {
|
|||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.statusIcon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.clientName {
|
||||
line-height: 16px;
|
||||
min-width: 2em;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 ? [
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue