mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
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:
parent
45db98dd3e
commit
d5efe2d7ee
@ -8,6 +8,17 @@ import positioningWorkaround from "discourse/lib/safari-hacks";
|
|||||||
import { headerHeight } from "discourse/components/site-header";
|
import { headerHeight } from "discourse/components/site-header";
|
||||||
import KeyEnterEscape from "discourse/mixins/key-enter-escape";
|
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, {
|
export default Ember.Component.extend(KeyEnterEscape, {
|
||||||
elementId: "reply-control",
|
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() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
const $replyControl = $("#reply-control");
|
this.setupComposerResizeEvents();
|
||||||
|
|
||||||
const resize = () => Ember.run(() => this.resize());
|
const resize = () => Ember.run(() => this.resize());
|
||||||
|
|
||||||
$replyControl.DivResizer({
|
|
||||||
resize,
|
|
||||||
maxHeight: winHeight => winHeight - headerHeight(),
|
|
||||||
onDrag: sizePx => this.movePanels(sizePx)
|
|
||||||
});
|
|
||||||
|
|
||||||
const triggerOpen = () => {
|
const triggerOpen = () => {
|
||||||
if (this.get("composer.composeState") === Composer.OPEN) {
|
if (this.get("composer.composeState") === Composer.OPEN) {
|
||||||
this.appEvents.trigger("composer:opened");
|
this.appEvents.trigger("composer:opened");
|
||||||
@ -102,13 +149,11 @@ export default Ember.Component.extend(KeyEnterEscape, {
|
|||||||
};
|
};
|
||||||
triggerOpen();
|
triggerOpen();
|
||||||
|
|
||||||
afterTransition($replyControl, () => {
|
afterTransition(this.$(), () => {
|
||||||
resize();
|
resize();
|
||||||
triggerOpen();
|
triggerOpen();
|
||||||
});
|
});
|
||||||
positioningWorkaround(this.$());
|
positioningWorkaround(this.$());
|
||||||
|
|
||||||
this.appEvents.on("composer:resize", this, this.resize);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
typed=(action "typed")
|
typed=(action "typed")
|
||||||
cancelled=(action "cancelled")
|
cancelled=(action "cancelled")
|
||||||
save=(action "save")}}
|
save=(action "save")}}
|
||||||
|
<div class="grippie"></div>
|
||||||
{{#if visible}}
|
{{#if visible}}
|
||||||
{{composer-messages composer=model
|
{{composer-messages composer=model
|
||||||
messageCount=messageCount
|
messageCount=messageCount
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
//= require Markdown.Converter.js
|
//= require Markdown.Converter.js
|
||||||
//= require bootbox.js
|
//= require bootbox.js
|
||||||
//= require bootstrap-modal.js
|
//= require bootstrap-modal.js
|
||||||
//= require div_resizer
|
|
||||||
//= require caret_position
|
//= require caret_position
|
||||||
//= require favcount.js
|
//= require favcount.js
|
||||||
//= require jquery.ba-resize.js
|
//= require jquery.ba-resize.js
|
||||||
|
@ -67,6 +67,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.discourse-touch {
|
||||||
|
.open {
|
||||||
|
.grippie {
|
||||||
|
padding: 7px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.composer-popup-container {
|
.composer-popup-container {
|
||||||
max-width: 1500px;
|
max-width: 1500px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
99
vendor/assets/javascripts/div_resizer.js
vendored
99
vendor/assets/javascripts/div_resizer.js
vendored
@ -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());
|
|
||||||
});
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user