From 70be004ee0475167bd8313f283fae82c34662841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Mon, 5 Apr 2021 15:06:14 +0200 Subject: [PATCH] Make autoscroll behaviour more intuitive (#143) * Fix editor shift * Make autoscrolling more intuitive --- assets/js/cell/index.js | 24 +++++++++++--- assets/js/lib/utils.js | 8 ++++- assets/package-lock.json | 31 +++++++++++++++++-- assets/package.json | 3 +- .../live/session_live/cell_component.ex | 2 +- 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/assets/js/cell/index.js b/assets/js/cell/index.js index 1a6daaf60..63b5d6249 100644 --- a/assets/js/cell/index.js +++ b/assets/js/cell/index.js @@ -3,6 +3,7 @@ import LiveEditor from "./live_editor"; import Markdown from "./markdown"; import { globalPubSub } from "../lib/pub_sub"; import { smoothlyScrollToElement } from "../lib/utils"; +import scrollIntoView from "scroll-into-view-if-needed"; /** * A hook managing a single cell. @@ -127,11 +128,24 @@ function handleInsertModeChanged(hook, insertMode) { if (hook.state.isFocused) { hook.state.insertMode = insertMode; - if (hook.state.insertMode) { - hook.state.liveEditor && hook.state.liveEditor.focus(); - smoothlyScrollToElement(hook.el); - } else { - hook.state.liveEditor && hook.state.liveEditor.blur(); + if (hook.state.liveEditor) { + if (hook.state.insertMode) { + hook.state.liveEditor.focus(); + // The insert mode may be enabled as a result of clicking the editor, + // in which case we want to wait until editor handles the click + // and sets new cursor position. + // To achieve this, we simply put this task at the end of event loop, + // ensuring all click handlers are executed first. + setTimeout(() => { + scrollIntoView(document.activeElement, { + scrollMode: "if-needed", + behavior: "smooth", + block: "center", + }); + }, 0); + } else { + hook.state.liveEditor.blur(); + } } } } diff --git a/assets/js/lib/utils.js b/assets/js/lib/utils.js index 22e59220d..bf4f71b9e 100644 --- a/assets/js/lib/utils.js +++ b/assets/js/lib/utils.js @@ -41,5 +41,11 @@ export function selectElementContent(element) { } export function smoothlyScrollToElement(element) { - element.scrollIntoView({ behavior: "smooth", block: "center" }); + const { height } = element.getBoundingClientRect(); + + if (height < window.innerHeight) { + element.scrollIntoView({ behavior: "smooth", block: "center" }); + } else { + element.scrollIntoView({ behavior: "smooth", block: "start" }); + } } diff --git a/assets/package-lock.json b/assets/package-lock.json index 39fa32e47..f6b63c888 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -17,7 +17,8 @@ "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view", - "remixicon": "^2.5.0" + "remixicon": "^2.5.0", + "scroll-into-view-if-needed": "^2.2.28" }, "devDependencies": { "@babel/core": "^7.0.0", @@ -43,7 +44,7 @@ } }, "../deps/phoenix": { - "version": "1.5.7", + "version": "1.5.8", "license": "MIT" }, "../deps/phoenix_html": { @@ -3722,6 +3723,11 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz", + "integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -13423,6 +13429,14 @@ "node": ">= 8.9.0" } }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.28", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz", + "integrity": "sha512-8LuxJSuFVc92+0AdNv4QOxRL4Abeo1DgLnGNkn1XlaujPH/3cCFz3QI60r2VNu4obJJROzgnIUw5TKQkZvZI1w==", + "dependencies": { + "compute-scroll-into-view": "^1.0.17" + } + }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -18942,6 +18956,11 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, + "compute-scroll-into-view": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz", + "integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -26819,6 +26838,14 @@ "ajv-keywords": "^3.5.2" } }, + "scroll-into-view-if-needed": { + "version": "2.2.28", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz", + "integrity": "sha512-8LuxJSuFVc92+0AdNv4QOxRL4Abeo1DgLnGNkn1XlaujPH/3cCFz3QI60r2VNu4obJJROzgnIUw5TKQkZvZI1w==", + "requires": { + "compute-scroll-into-view": "^1.0.17" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", diff --git a/assets/package.json b/assets/package.json index 6322dc02c..90d059113 100644 --- a/assets/package.json +++ b/assets/package.json @@ -22,7 +22,8 @@ "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view", - "remixicon": "^2.5.0" + "remixicon": "^2.5.0", + "scroll-into-view-if-needed": "^2.2.28" }, "devDependencies": { "@babel/core": "^7.0.0", diff --git a/lib/livebook_web/live/session_live/cell_component.ex b/lib/livebook_web/live/session_live/cell_component.ex index 55b66bc41..e77e88769 100644 --- a/lib/livebook_web/live/session_live/cell_component.ex +++ b/lib/livebook_web/live/session_live/cell_component.ex @@ -146,7 +146,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do defp render_editor(assigns) do ~L""" -
+