From 6fc3ac4c14f093ffcb5715066eb3c33ed6e14d6c Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 26 Sep 2020 22:53:33 +0200 Subject: [PATCH] Implemented client rename and some small channel tree optimisations --- shared/js/tree/ChannelTree.tsx | 3 +- shared/js/ui/tree/Channel.scss | 6 +-- shared/js/ui/tree/Client.scss | 4 ++ shared/js/ui/tree/Controller.tsx | 4 +- shared/js/ui/tree/RendererChannel.tsx | 5 ++- shared/js/ui/tree/RendererClient.tsx | 29 +++++++++++-- shared/js/ui/tree/RendererDataProvider.tsx | 6 +-- shared/js/ui/tree/RendererServer.tsx | 3 +- shared/js/ui/tree/RendererView.tsx | 15 ------- shared/js/ui/tree/View.scss | 47 ++++++++++++++++++++++ 10 files changed, 90 insertions(+), 32 deletions(-) diff --git a/shared/js/tree/ChannelTree.tsx b/shared/js/tree/ChannelTree.tsx index 2386e853..d7b550bb 100644 --- a/shared/js/tree/ChannelTree.tsx +++ b/shared/js/tree/ChannelTree.tsx @@ -642,7 +642,8 @@ export class ChannelTree { contextmenu.Entry.CLOSE(on_close) ); } - private open_multiselect_context_menu(entries: ChannelTreeEntry[], x: number, y: number) { + + public open_multiselect_context_menu(entries: ChannelTreeEntry[], 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; diff --git a/shared/js/ui/tree/Channel.scss b/shared/js/ui/tree/Channel.scss index fb8e6905..f8e7e20b 100644 --- a/shared/js/ui/tree/Channel.scss +++ b/shared/js/ui/tree/Channel.scss @@ -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 { diff --git a/shared/js/ui/tree/Client.scss b/shared/js/ui/tree/Client.scss index ad70dec9..996891bf 100644 --- a/shared/js/ui/tree/Client.scss +++ b/shared/js/ui/tree/Client.scss @@ -18,6 +18,10 @@ html:root { margin-right: 2px; } + .statusIcon { + flex-shrink: 0; + } + .clientName { line-height: 16px; min-width: 2em; diff --git a/shared/js/ui/tree/Controller.tsx b/shared/js/ui/tree/Controller.tsx index 5e442761..ff74f45e 100644 --- a/shared/js/ui/tree/Controller.tsx +++ b/shared/js/ui/tree/Controller.tsx @@ -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("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, 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; } diff --git a/shared/js/ui/tree/RendererChannel.tsx b/shared/js/ui/tree/RendererChannel.tsx index ee43b202..013434cd 100644 --- a/shared/js/ui/tree/RendererChannel.tsx +++ b/shared/js/ui/tree/RendererChannel.tsx @@ -92,7 +92,7 @@ const ChannelName = React.memo((props: { channelName: string | undefined, alignm const ChannelCollapsedIndicator = (props: { collapsed: boolean, onToggle: () => void }) => { return
-
{ +
{ event.preventDefault(); props.onToggle(); }}/> @@ -126,7 +126,7 @@ export class RendererChannel extends React.Component<{ channel: RDPChannel }, {} return (
{ 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 }); }} > +
{collapsedIndicator} {channelIcon} diff --git a/shared/js/ui/tree/RendererClient.tsx b/shared/js/ui/tree/RendererClient.tsx index 0d539c38..f1e22e6a 100644 --- a/shared/js/ui/tree/RendererClient.tsx +++ b/shared/js/ui/tree/RendererClient.tsx @@ -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 + return } } @@ -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 { private readonly refDiv: React.RefObject = React.createRef(); componentDidMount(): void { this.refDiv.current.focus(); + selectText(this.refDiv.current); } render() { @@ -145,7 +168,6 @@ class ClientNameEdit extends React.Component { } } -/* 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 (
{ 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 })} > +
{...(client.rename ? [ diff --git a/shared/js/ui/tree/RendererDataProvider.tsx b/shared/js/ui/tree/RendererDataProvider.tsx index 5267e850..9a0b0934 100644 --- a/shared/js/ui/tree/RendererDataProvider.tsx +++ b/shared/js/ui/tree/RendererDataProvider.tsx @@ -209,7 +209,6 @@ export class RDPChannelTree { @EventHandler("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) { diff --git a/shared/js/ui/tree/RendererServer.tsx b/shared/js/ui/tree/RendererServer.tsx index 52ac3f7b..7cdeb1dc 100644 --- a/shared/js/ui/tree/RendererServer.tsx +++ b/shared/js/ui/tree/RendererServer.tsx @@ -38,7 +38,7 @@ export class ServerRenderer extends React.Component<{ server: RDPServer }, {}> { return (
{ 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 }); }} > +
{name}
diff --git a/shared/js/ui/tree/RendererView.tsx b/shared/js/ui/tree/RendererView.tsx index ad33b6a5..15af45e9 100644 --- a/shared/js/ui/tree/RendererView.tsx +++ b/shared/js/ui/tree/RendererView.tsx @@ -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 = ; - } else if(entry.type === "client" || entry.type === "client-local") { - entry.rendered = ; - } else { - entry.rendered = ; - } - return entry.rendered; -} - */ - @ReactEventHandler(e => e.props.events) @BatchUpdateAssignment(BatchUpdateType.CHANNEL_TREE) export class ChannelTreeView extends ReactComponentBase { diff --git a/shared/js/ui/tree/View.scss b/shared/js/ui/tree/View.scss index 11876f7c..8fbe5968 100644 --- a/shared/js/ui/tree/View.scss +++ b/shared/js/ui/tree/View.scss @@ -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 */ + } } \ No newline at end of file