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
This commit is contained in:
Osama Sayegh 2019-02-26 00:04:14 +03:00 committed by Sam
parent 45db98dd3e
commit d5efe2d7ee
5 changed files with 65 additions and 112 deletions

View File

@ -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() {

View File

@ -4,7 +4,7 @@
typed=(action "typed")
cancelled=(action "cancelled")
save=(action "save")}}
<div class="grippie"></div>
{{#if visible}}
{{composer-messages composer=model
messageCount=messageCount

View File

@ -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

View File

@ -67,6 +67,14 @@
}
}
.discourse-touch {
.open {
.grippie {
padding: 7px 0;
}
}
}
.composer-popup-container {
max-width: 1500px;
margin-left: auto;

View File

@ -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("<div class='grippie'></div>").find('.grippie').bind("mousedown", {
el: this
}, start());
});
};