From 61281a855c08d9a9eacd4e1da103acc3f59cfb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 19 Jan 2021 16:35:32 +0100 Subject: [PATCH] Redo expanding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 39 ++++++- src/components/views/messages/TextualBody.js | 111 ++++++++++--------- 2 files changed, 91 insertions(+), 59 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 5fc7a5f04b..c587251d3e 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -501,10 +501,12 @@ $left-gutter: 64px; } } -.mx_EventTile_content_collapsedCode { - pre { - max-height: 30vh; - } +.mx_EventTile_expandedCodeBlock { + max-height: 100vh; +} + +.mx_EventTile_collapsedCodeBlock { + max-height: 30vh; } .mx_EventTile:hover .mx_EventTile_body pre, @@ -531,6 +533,35 @@ $left-gutter: 64px; background-color: $message-action-bar-fg-color; } + +// Inserted adjacent to
 blocks, (See TextualBody)
+.mx_EventTile_expandButton {
+    position: absolute;
+    display: inline-block;
+    visibility: hidden;
+    cursor: pointer;
+    top: 6px;
+    right: 6px;
+    width: 19px;
+    height: 19px;
+    mask-image: url($copy-button-url);
+    background-color: $message-action-bar-fg-color;
+}
+
+// Inserted adjacent to 
 blocks, (See TextualBody)
+.mx_EventTile_collapseButton {
+    position: absolute;
+    display: inline-block;
+    visibility: hidden;
+    cursor: pointer;
+    top: 6px;
+    right: 6px;
+    width: 19px;
+    height: 19px;
+    mask-image: url($copy-button-url);
+    background-color: $message-action-bar-fg-color;
+}
+
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
 .mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton {
     visibility: visible;
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index ff864d9c13..8d341e4a63 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -35,7 +35,6 @@ import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
 import {toRightOf} from "../../structures/ContextMenu";
 import {copyPlaintext} from "../../../utils/strings";
 import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
-import classNames from "classnames";
 
 export default class TextualBody extends React.Component {
     static propTypes = {
@@ -70,7 +69,6 @@ export default class TextualBody extends React.Component {
 
             // track whether the preview widget is hidden
             widgetHidden: false,
-            codeBlockExpanded: SettingsStore.getValue("expandCodeByDefault"),
         };
     }
 
@@ -93,29 +91,70 @@ export default class TextualBody extends React.Component {
         this.calculateUrlPreview();
 
         if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") {
-            const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("code");
+            const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("pre");
             if (blocks.length > 0) {
+                for (let i = 0; i < blocks.length; i++) {
+                    this._handleCodeBlockExpansion(blocks[i]);
+                    this._addCodeCopyButton(blocks[i]);
+                }
                 // Do this asynchronously: parsing code takes time and we don't
                 // need to block the DOM update on it.
                 setTimeout(() => {
                     if (this._unmounted) return;
                     for (let i = 0; i < blocks.length; i++) {
-                        if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
-                            highlight.highlightBlock(blocks[i]);
-                        } else {
-                            // Only syntax highlight if there's a class starting with language-
-                            const classes = blocks[i].className.split(/\s+/).filter(function(cl) {
-                                return cl.startsWith('language-') && !cl.startsWith('language-_');
-                            });
-
-                            if (classes.length != 0) {
-                                highlight.highlightBlock(blocks[i]);
-                            }
-                        }
+                        this._highlightCode(blocks[i].firstChild);
                     }
                 }, 10);
             }
-            this._addCodeCopyButton();
+        }
+    }
+
+    _addCodeCopyButton(codeBlock) {
+        const button = document.createElement("span");
+        button.className = "mx_EventTile_copyButton";
+        button.onclick = async () => {
+            const copyCode = button.parentNode.getElementsByTagName("pre")[0];
+            const successful = await copyPlaintext(copyCode.textContent);
+
+            const buttonRect = button.getBoundingClientRect();
+            const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
+            const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
+                ...toRightOf(buttonRect, 2),
+                message: successful ? _t('Copied!') : _t('Failed to copy'),
+            });
+            button.onmouseleave = close;
+        };
+
+        // Wrap a div around 
 so that the copy button can be correctly positioned
+        // when the 
 overflows and is scrolled horizontally.
+        const div = document.createElement("div");
+        div.className = "mx_EventTile_pre_container";
+
+        // Insert containing div in place of 
 block
+        codeBlock.parentNode.replaceChild(div, codeBlock);
+
+        // Append 
 block and copy button to container
+        div.appendChild(codeBlock);
+        div.appendChild(button);
+    }
+
+    _handleCodeBlockExpansion(codeBlock) {
+        const expandCodeBlock = SettingsStore.getValue("expandCodeByDefault");
+        codeBlock.className = expandCodeBlock ? "mx_EventTile_expandedCodeBlock" : "mx_EventTile_collapsedCodeBlock";
+    }
+
+    _highlightCode(codeBlock) {
+        if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
+            highlight.highlightBlock(codeBlock);
+        } else {
+            // Only syntax highlight if there's a class starting with language-
+            const classes = codeBlock.className.split(/\s+/).filter(function(cl) {
+                return cl.startsWith('language-') && !cl.startsWith('language-_');
+            });
+
+            if (classes.length != 0) {
+                highlight.highlightBlock(codeBlock);
+            }
         }
     }
 
@@ -256,38 +295,6 @@ export default class TextualBody extends React.Component {
         }
     }
 
-    _addCodeCopyButton() {
-        // Add 'copy' buttons to pre blocks
-        Array.from(ReactDOM.findDOMNode(this).querySelectorAll('.mx_EventTile_body pre')).forEach((p) => {
-            const button = document.createElement("span");
-            button.className = "mx_EventTile_copyButton";
-            button.onclick = async () => {
-                const copyCode = button.parentNode.getElementsByTagName("pre")[0];
-                const successful = await copyPlaintext(copyCode.textContent);
-
-                const buttonRect = button.getBoundingClientRect();
-                const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
-                const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
-                    ...toRightOf(buttonRect, 2),
-                    message: successful ? _t('Copied!') : _t('Failed to copy'),
-                });
-                button.onmouseleave = close;
-            };
-
-            // Wrap a div around 
 so that the copy button can be correctly positioned
-            // when the 
 overflows and is scrolled horizontally.
-            const div = document.createElement("div");
-            div.className = "mx_EventTile_pre_container";
-
-            // Insert containing div in place of 
 block
-            p.parentNode.replaceChild(div, p);
-
-            // Append 
 block and copy button to container
-            div.appendChild(p);
-            div.appendChild(button);
-        });
-    }
-
     onCancelClick = event => {
         this.setState({ widgetHidden: true });
         // FIXME: persist this somewhere smarter than local storage
@@ -439,12 +446,6 @@ export default class TextualBody extends React.Component {
             });
         }
 
-        const defaultCaseClasses = classNames({
-            mx_MTextBody: true,
-            mx_EventTile_content: true,
-            mx_EventTile_content_collapsedCode: !this.state.codeBlockExpanded,
-        });
-
         switch (content.msgtype) {
             case "m.emote":
                 return (
@@ -470,7 +471,7 @@ export default class TextualBody extends React.Component {
                 );
             default: // including "m.text"
                 return (
-                    
+                    
                         { body }
                         { widgets }