From d5efe2d7eed93daaeef79de1a9340d0d8c39d743 Mon Sep 17 00:00:00 2001 From: Osama Sayegh Date: Tue, 26 Feb 2019 00:04:14 +0300 Subject: [PATCH] UX: make composer resize work on touch devices (#7068) * UX: make composer resize work on touch devices This also replaces a vendor dependency with a small built-in resize mechanism. * Make blue bar's larger padding specific to touch devices --- .../discourse/components/composer-body.js.es6 | 67 ++++++++++--- .../discourse/templates/composer.hbs | 2 +- app/assets/javascripts/vendor.js | 1 - app/assets/stylesheets/desktop/compose.scss | 8 ++ vendor/assets/javascripts/div_resizer.js | 99 ------------------- 5 files changed, 65 insertions(+), 112 deletions(-) delete mode 100644 vendor/assets/javascripts/div_resizer.js diff --git a/app/assets/javascripts/discourse/components/composer-body.js.es6 b/app/assets/javascripts/discourse/components/composer-body.js.es6 index 5d194eb511b..c5365650f9e 100644 --- a/app/assets/javascripts/discourse/components/composer-body.js.es6 +++ b/app/assets/javascripts/discourse/components/composer-body.js.es6 @@ -8,6 +8,17 @@ import positioningWorkaround from "discourse/lib/safari-hacks"; import { headerHeight } from "discourse/components/site-header"; import KeyEnterEscape from "discourse/mixins/key-enter-escape"; +const START_EVENTS = "touchstart mousedown"; +const DRAG_EVENTS = "touchmove mousemove"; +const END_EVENTS = "touchend mouseup"; + +const MIN_COMPOSER_SIZE = 240; +const THROTTLE_RATE = 20; + +function mouseYPos(e) { + return e.clientY || (e.touches && e.touches[0] && e.touches[0].clientY); +} + export default Ember.Component.extend(KeyEnterEscape, { elementId: "reply-control", @@ -84,17 +95,53 @@ export default Ember.Component.extend(KeyEnterEscape, { } }, + setupComposerResizeEvents() { + const $composer = this.$(); + const $grippie = this.$(".grippie"); + const $document = Ember.$(document); + let origComposerSize = 0; + let lastMousePos = 0; + + const performDrag = event => { + $composer.trigger("div-resizing"); + $composer.addClass("clear-transitions"); + const currentMousePos = mouseYPos(event); + let size = origComposerSize + (lastMousePos - currentMousePos); + + const winHeight = Ember.$(window).height(); + size = Math.min(size, winHeight - headerHeight()); + size = Math.max(size, MIN_COMPOSER_SIZE); + const sizePx = `${size}px`; + this.movePanels(sizePx); + $composer.height(sizePx); + }; + + const throttledPerformDrag = (event => { + event.preventDefault(); + Ember.run.throttle(this, performDrag, event, THROTTLE_RATE); + }).bind(this); + + const endDrag = () => { + $document.off(DRAG_EVENTS, throttledPerformDrag); + $document.off(END_EVENTS, endDrag); + $composer.removeClass("clear-transitions"); + $composer.focus(); + }; + + $grippie.on(START_EVENTS, event => { + event.preventDefault(); + origComposerSize = $composer.height(); + lastMousePos = mouseYPos(event); + $document.on(DRAG_EVENTS, throttledPerformDrag); + $document.on(END_EVENTS, endDrag); + }); + }, + didInsertElement() { this._super(...arguments); - const $replyControl = $("#reply-control"); + this.setupComposerResizeEvents(); + const resize = () => Ember.run(() => this.resize()); - - $replyControl.DivResizer({ - resize, - maxHeight: winHeight => winHeight - headerHeight(), - onDrag: sizePx => this.movePanels(sizePx) - }); - const triggerOpen = () => { if (this.get("composer.composeState") === Composer.OPEN) { this.appEvents.trigger("composer:opened"); @@ -102,13 +149,11 @@ export default Ember.Component.extend(KeyEnterEscape, { }; triggerOpen(); - afterTransition($replyControl, () => { + afterTransition(this.$(), () => { resize(); triggerOpen(); }); positioningWorkaround(this.$()); - - this.appEvents.on("composer:resize", this, this.resize); }, willDestroyElement() { diff --git a/app/assets/javascripts/discourse/templates/composer.hbs b/app/assets/javascripts/discourse/templates/composer.hbs index 9f512baa83d..834f65d84c7 100644 --- a/app/assets/javascripts/discourse/templates/composer.hbs +++ b/app/assets/javascripts/discourse/templates/composer.hbs @@ -4,7 +4,7 @@ typed=(action "typed") cancelled=(action "cancelled") save=(action "save")}} - +
{{#if visible}} {{composer-messages composer=model messageCount=messageCount diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js index 5279b2c8912..0503964494e 100644 --- a/app/assets/javascripts/vendor.js +++ b/app/assets/javascripts/vendor.js @@ -9,7 +9,6 @@ //= require Markdown.Converter.js //= require bootbox.js //= require bootstrap-modal.js -//= require div_resizer //= require caret_position //= require favcount.js //= require jquery.ba-resize.js diff --git a/app/assets/stylesheets/desktop/compose.scss b/app/assets/stylesheets/desktop/compose.scss index 21152aa007d..8fd3c360c8e 100644 --- a/app/assets/stylesheets/desktop/compose.scss +++ b/app/assets/stylesheets/desktop/compose.scss @@ -67,6 +67,14 @@ } } +.discourse-touch { + .open { + .grippie { + padding: 7px 0; + } + } +} + .composer-popup-container { max-width: 1500px; margin-left: auto; diff --git a/vendor/assets/javascripts/div_resizer.js b/vendor/assets/javascripts/div_resizer.js deleted file mode 100644 index 9bb7b3ccc20..00000000000 --- a/vendor/assets/javascripts/div_resizer.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - This is a jQuery plugin to support resizing text areas. - - Originally based off text area resizer by Ryan O'Dell : http://plugins.jquery.com/misc/textarea.js - - @module $.fn.DivResizer -**/ - -var div, endDrag, grip, lastMousePos, min, mousePosition, originalDivHeight, originalPos, performDrag, startDrag, wrappedEndDrag, wrappedPerformDrag; -div = void 0; -originalPos = void 0; -originalDivHeight = void 0; -lastMousePos = 0; -min = 230; -grip = void 0; -wrappedEndDrag = void 0; -wrappedPerformDrag = void 0; - -startDrag = function(e, opts) { - div = $(e.data.el); - div.addClass('clear-transitions'); - div.blur(); - lastMousePos = mousePosition(e).y; - originalPos = lastMousePos; - originalDivHeight = div.height(); - wrappedPerformDrag = (function() { - return function(e) { - return performDrag(e, opts); - }; - })(); - wrappedEndDrag = (function() { - return function(e) { - return endDrag(e, opts); - }; - })(); - $(document).mousemove(wrappedPerformDrag).mouseup(wrappedEndDrag); - return false; -}; - -performDrag = function(e, opts) { - $(div).trigger("div-resizing"); - - var size, sizePx, thisMousePos; - thisMousePos = mousePosition(e).y; - size = originalDivHeight + (originalPos - thisMousePos); - lastMousePos = thisMousePos; - - var maxHeight = $(window).height(); - if (opts.maxHeight) { - maxHeight = opts.maxHeight(maxHeight); - } - size = Math.min(size, maxHeight); - size = Math.max(min, size); - sizePx = size + "px"; - if (typeof opts.onDrag === "function") { - opts.onDrag(sizePx); - } - div.height(sizePx); - if (size < min) { - endDrag(e, opts); - } - return false; -}; - -endDrag = function(e, opts) { - $(document).unbind("mousemove", wrappedPerformDrag).unbind("mouseup", wrappedEndDrag); - div.removeClass('clear-transitions'); - div.focus(); - if (typeof opts.resize === "function") { - opts.resize(); - } - $(div).trigger("div-resized"); - div = null; -}; - -mousePosition = function(e) { - return { - x: e.clientX + document.documentElement.scrollLeft, - y: e.clientY + document.documentElement.scrollTop - }; -}; - -$.fn.DivResizer = function(opts) { - return this.each(function() { - var grippie, start, staticOffset; - div = $(this); - if (div.hasClass("processed")) return; - div.addClass("processed"); - staticOffset = null; - start = function() { - return function(e) { - return startDrag(e, opts); - }; - }; - grippie = div.prepend("
").find('.grippie').bind("mousedown", { - el: this - }, start()); - }); -};