mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 15:48:43 -05:00
UX: allow for an optional toolbar in composer in mobile
Allows preview of text, emoji, quoting, whisper
This commit is contained in:
parent
1a1dcb59f9
commit
3df2ee3431
7 changed files with 147 additions and 12 deletions
|
@ -5,7 +5,7 @@ import { linkSeenCategoryHashtags, fetchUnseenCategoryHashtags } from 'discourse
|
|||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['wmd-controls'],
|
||||
classNameBindings: [':wmd-controls', 'showPreview', 'showPreview::hide-preview'],
|
||||
classNameBindings: ['showToolbar:toolbar-visible', ':wmd-controls', 'showPreview', 'showPreview::hide-preview'],
|
||||
|
||||
uploadProgress: 0,
|
||||
showPreview: true,
|
||||
|
@ -343,12 +343,34 @@ export default Ember.Component.extend({
|
|||
},
|
||||
|
||||
showOptions() {
|
||||
// long term we want some smart positioning algorithm in popup-menu
|
||||
// the problem is that positioning in a fixed panel is a nightmare
|
||||
// cause offsetParent can end up returning a fixed element and then
|
||||
// using offset() is not going to work, so you end up needing special logic
|
||||
// especially since we allow for negative .top, provided there is room on screen
|
||||
const myPos = this.$().position();
|
||||
const buttonPos = this.$('.options').position();
|
||||
|
||||
const popupHeight = $('#reply-control .popup-menu').height();
|
||||
const popupWidth = $('#reply-control .popup-menu').width();
|
||||
|
||||
var top = myPos.top + buttonPos.top - 15;
|
||||
var left = myPos.left + buttonPos.left - (popupWidth/2);
|
||||
|
||||
const composerPos = $('#reply-control').position();
|
||||
|
||||
if (composerPos.top + top - popupHeight < 0) {
|
||||
top = top + popupHeight + this.$('.options').height() + 50;
|
||||
}
|
||||
|
||||
var replyWidth = $('#reply-control').width();
|
||||
if (left + popupWidth > replyWidth) {
|
||||
left = replyWidth - popupWidth - 40;
|
||||
}
|
||||
|
||||
this.sendAction('showOptions', { position: "absolute",
|
||||
left: myPos.left + buttonPos.left,
|
||||
top: myPos.top + buttonPos.top });
|
||||
left: left,
|
||||
top: top });
|
||||
},
|
||||
|
||||
showUploadModal(toolbarEvent) {
|
||||
|
|
|
@ -31,7 +31,7 @@ function Toolbar() {
|
|||
this.groups = [
|
||||
{group: 'fontStyles', buttons: []},
|
||||
{group: 'insertions', buttons: []},
|
||||
{group: 'extras', buttons: [], lastGroup: true}
|
||||
{group: 'extras', buttons: []}
|
||||
];
|
||||
|
||||
this.addButton({
|
||||
|
@ -105,6 +105,20 @@ function Toolbar() {
|
|||
title: 'composer.hr_title',
|
||||
perform: e => e.addText("\n\n----------\n")
|
||||
});
|
||||
|
||||
if (Discourse.Mobile.mobileView) {
|
||||
this.groups.push({group: 'mobileExtras', buttons: []});
|
||||
|
||||
this.addButton({
|
||||
id: 'preview',
|
||||
group: 'mobileExtras',
|
||||
icon: 'television',
|
||||
title: 'composer.hr_preview',
|
||||
perform: e => e.preview()
|
||||
});
|
||||
}
|
||||
|
||||
this.groups[this.groups.length-1].lastGroup = true;
|
||||
};
|
||||
|
||||
Toolbar.prototype.addButton = function(button) {
|
||||
|
@ -166,6 +180,7 @@ export function onToolbarCreate(func) {
|
|||
export default Ember.Component.extend({
|
||||
classNames: ['d-editor'],
|
||||
ready: false,
|
||||
forcePreview: false,
|
||||
insertLinkHidden: true,
|
||||
link: '',
|
||||
lastSel: null,
|
||||
|
@ -446,6 +461,10 @@ export default Ember.Component.extend({
|
|||
Ember.run.scheduleOnce("afterRender", () => this.$("textarea.d-editor-input").focus());
|
||||
},
|
||||
|
||||
_togglePreview() {
|
||||
this.toggleProperty('forcePreview');
|
||||
},
|
||||
|
||||
actions: {
|
||||
toolbarButton(button) {
|
||||
const selected = this._getSelected();
|
||||
|
@ -453,7 +472,8 @@ export default Ember.Component.extend({
|
|||
selected,
|
||||
applySurround: (head, tail, exampleKey) => this._applySurround(selected, head, tail, exampleKey),
|
||||
applyList: (head, exampleKey) => this._applyList(selected, head, exampleKey),
|
||||
addText: text => this._addText(selected, text)
|
||||
addText: text => this._addText(selected, text),
|
||||
preview: () => this._togglePreview()
|
||||
};
|
||||
|
||||
if (button.sendAction) {
|
||||
|
@ -463,6 +483,10 @@ export default Ember.Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
hidePreview() {
|
||||
this.set('forcePreview', false);
|
||||
},
|
||||
|
||||
showLinkModal() {
|
||||
this._lastSel = this._getSelected();
|
||||
this.set('insertLinkHidden', false);
|
||||
|
|
|
@ -54,12 +54,10 @@ export default Ember.Controller.extend({
|
|||
similarTopicsMessage: null,
|
||||
lastSimilaritySearch: null,
|
||||
optionsVisible: false,
|
||||
|
||||
lastValidatedAt: null,
|
||||
|
||||
isUploading: false,
|
||||
|
||||
topic: null,
|
||||
showToolbar: false,
|
||||
|
||||
_initializeSimilar: function() {
|
||||
this.set('similarTopics', []);
|
||||
|
@ -90,6 +88,10 @@ export default Ember.Controller.extend({
|
|||
this.toggleProperty('model.whisper');
|
||||
},
|
||||
|
||||
toggleToolbar() {
|
||||
this.toggleProperty('showToolbar');
|
||||
},
|
||||
|
||||
showOptions(loc) {
|
||||
this.appEvents.trigger('popup-menu:open', loc);
|
||||
this.set('optionsVisible', true);
|
||||
|
|
|
@ -89,11 +89,15 @@ function positioningWorkaround($fixedElement) {
|
|||
}
|
||||
|
||||
const checkForInputs = _.debounce(function(){
|
||||
$fixedElement.find('button,a:not(.mobile-file-upload)').each(function(idx, elem){
|
||||
$fixedElement.find('button:not(.hide-preview),a:not(.mobile-file-upload):not(.toggle-toolbar)').each(function(idx, elem){
|
||||
if ($(elem).parents('.autocomplete').length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($(elem).parents('.d-editor-button-bar').length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
attachTouchStart(this, function(evt){
|
||||
done = true;
|
||||
$(document.activeElement).blur();
|
||||
|
|
|
@ -25,9 +25,12 @@
|
|||
{{popup-input-tip validation=validation}}
|
||||
</div>
|
||||
|
||||
<div class="d-editor-preview-wrapper">
|
||||
<div class="d-editor-preview-wrapper {{if forcePreview 'force-preview'}}">
|
||||
<div class="d-editor-preview">
|
||||
{{{preview}}}
|
||||
</div>
|
||||
{{#if site.mobileView}}
|
||||
{{d-button action='hidePreview' class='hide-preview' label='composer.hide_preview'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
|
||||
{{render "composer-messages"}}
|
||||
<div class='control'>
|
||||
|
||||
{{#if site.mobileView}}
|
||||
<a href class='toggle-toolbar' {{action "toggleToolbar" bubbles=false}}></a>
|
||||
{{/if}}
|
||||
<a href class='toggler' {{action "toggle" bubbles=false}} title={{i18n 'composer.toggler'}}></a>
|
||||
|
||||
{{#if model.viewOpen}}
|
||||
|
@ -20,9 +24,11 @@
|
|||
|
||||
<div class='reply-to'>
|
||||
{{{model.actionTitle}}}
|
||||
{{#unless site.mobileView}}
|
||||
{{#if model.whisper}}
|
||||
<span class='whisper'>({{i18n "composer.whisper"}})</span>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
{{#if canEdit}}
|
||||
{{#if showEditReason}}
|
||||
|
@ -85,6 +91,7 @@
|
|||
groupsMentioned="groupsMentioned"
|
||||
importQuote="importQuote"
|
||||
showOptions="showOptions"
|
||||
showToolbar=showToolbar
|
||||
showUploadSelector="showUploadSelector"}}
|
||||
|
||||
{{#if currentUser}}
|
||||
|
@ -92,6 +99,12 @@
|
|||
{{plugin-outlet "composer-fields-below"}}
|
||||
<button {{action "save"}} tabindex="5" {{bind-attr class=":btn :btn-primary :create disableSubmit:disabled"}} title="{{i18n 'composer.title'}}">{{{model.saveIcon}}}{{model.saveText}}</button>
|
||||
<a href {{action "cancel"}} class='cancel' tabindex="6">{{i18n 'cancel'}}</a>
|
||||
|
||||
{{#if site.mobileView}}
|
||||
{{#if model.whisper}}
|
||||
<span class='whisper'><i class='fa fa-eye-slash'></i></span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,7 @@ input {
|
|||
bottom: 0;
|
||||
font-size: 1em;
|
||||
position: fixed;
|
||||
.toggler {
|
||||
.toggle-toolbar, .toggler {
|
||||
width: 15px;
|
||||
right: 1px;
|
||||
position: absolute;
|
||||
|
@ -48,6 +48,14 @@ input {
|
|||
content: "\f078";
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-toolbar {
|
||||
right: 30px;
|
||||
&:before {
|
||||
content: "\f0c9";
|
||||
}
|
||||
}
|
||||
|
||||
a.cancel {
|
||||
padding-left: 7px;
|
||||
line-height: 30px;
|
||||
|
@ -56,7 +64,7 @@ input {
|
|||
margin: 0 0 0 5px;
|
||||
.reply-to {
|
||||
overflow: hidden;
|
||||
max-width: 92%;
|
||||
max-width: 80%;
|
||||
white-space: nowrap;
|
||||
i {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
|
@ -235,6 +243,31 @@ input {
|
|||
.d-editor-preview-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.d-editor-preview-wrapper.force-preview {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 1000000;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: $secondary;
|
||||
|
||||
.d-editor-preview {
|
||||
height: 90%;
|
||||
height: calc(100% - 60px);
|
||||
border: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.hide-preview {
|
||||
position: fixed;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
z-index: 1000001;
|
||||
}
|
||||
}
|
||||
.d-editor-input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -260,6 +293,40 @@ input {
|
|||
.d-editor-button-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.wmd-controls.toolbar-visible .d-editor-input {
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.wmd-controls.toolbar-visible .d-editor-button-bar {
|
||||
|
||||
.btn.link, .btn.upload, .btn.rule, .btn.bullet, .btn.list, .btn.heading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
display: block;
|
||||
margin: 1px 4px;
|
||||
position: absolute;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
background-color: $secondary;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
width: calc(100% - 10px);
|
||||
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
button {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
}
|
||||
button.btn.no-text {
|
||||
margin: 0 2px;
|
||||
padding: 2px 5px;
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the category selector *NEVER* gets focus by default on mobile anywhere
|
||||
|
|
Loading…
Reference in a new issue