mirror of
https://github.com/flarum/framework.git
synced 2025-06-07 09:04:33 +08:00
Clean up/refactor composer, add escape hotkey
This commit is contained in:
@ -7,37 +7,43 @@ var precompileTemplate = Ember.Handlebars.compile;
|
|||||||
export default Ember.Component.extend(Ember.Evented, {
|
export default Ember.Component.extend(Ember.Evented, {
|
||||||
layoutName: 'components/discussions/composer-body',
|
layoutName: 'components/discussions/composer-body',
|
||||||
|
|
||||||
placeholder: 'Write your reply...',
|
|
||||||
submitLabel: 'Post Reply',
|
submitLabel: 'Post Reply',
|
||||||
|
placeholder: '',
|
||||||
value: '',
|
value: '',
|
||||||
|
submit: null,
|
||||||
|
loading: false,
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
var headerItems = TaggedArray.create();
|
var controls = TaggedArray.create();
|
||||||
this.trigger('populateHeader', headerItems);
|
this.trigger('populateControls', controls);
|
||||||
this.set('headerItems', headerItems);
|
this.set('controls', controls);
|
||||||
},
|
},
|
||||||
|
|
||||||
populateHeader: function(header) {
|
populateControls: function(controls) {
|
||||||
var title = Ember.Component.create({
|
var title = Ember.Component.create({
|
||||||
tagName: 'h3',
|
tagName: 'h3',
|
||||||
layout: precompileTemplate('Replying to <em>{{component.discussion.title}}</em>'),
|
layout: precompileTemplate('Replying to <em>{{component.discussion.title}}</em>'),
|
||||||
component: this
|
component: this
|
||||||
});
|
});
|
||||||
header.pushObjectWithTag(title, 'title');
|
controls.pushObjectWithTag(title, 'title');
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
submit: function(value) {
|
submit: function(value) {
|
||||||
this.get('submit').call(this, value);
|
this.get('submit')(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
willExit: function(abort) {
|
willExit: function(abort) {
|
||||||
|
// If the user has typed something, prompt them before exiting
|
||||||
|
// this composer state.
|
||||||
if (this.get('value') && ! confirm('You have not posted your reply. Do you wish to discard it?')) {
|
if (this.get('value') && ! confirm('You have not posted your reply. Do you wish to discard it?')) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this.set('loading', false);
|
this.set('loading', false);
|
||||||
this.set('value', '');
|
this.set('value', '');
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,8 @@ import TaggedArray from '../../../utils/tagged-array';
|
|||||||
import ActionButton from './action-button';
|
import ActionButton from './action-button';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
|
disabled: false,
|
||||||
|
|
||||||
classNames: ['text-editor'],
|
classNames: ['text-editor'],
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
|
@ -1,15 +1,27 @@
|
|||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export var PositionEnum = {
|
||||||
|
HIDDEN: 'hidden',
|
||||||
|
NORMAL: 'normal',
|
||||||
|
MINIMIZED: 'minimized',
|
||||||
|
FULLSCREEN: 'fullscreen'
|
||||||
|
};
|
||||||
|
|
||||||
export default Ember.Controller.extend(Ember.Evented, {
|
export default Ember.Controller.extend(Ember.Evented, {
|
||||||
|
|
||||||
needs: ['index', 'application'],
|
|
||||||
|
|
||||||
content: null,
|
content: null,
|
||||||
|
position: PositionEnum.HIDDEN,
|
||||||
|
|
||||||
showing: false,
|
visible: Ember.computed.or('normal', 'minimized', 'fullscreen'),
|
||||||
minimized: false,
|
normal: Ember.computed.equal('position', PositionEnum.NORMAL),
|
||||||
fullScreen: false,
|
minimized: Ember.computed.equal('position', PositionEnum.MINIMIZED),
|
||||||
|
fullscreen: Ember.computed.equal('position', PositionEnum.FULLSCREEN),
|
||||||
|
|
||||||
|
// Switch out the composer's content for a new component. The old
|
||||||
|
// component will be given the opportunity to abort the switch. Note:
|
||||||
|
// there appears to be a bug in Ember where the content binding won't
|
||||||
|
// update in the view if we switch the value out immediately. As a
|
||||||
|
// workaround, we set it to null, and then set it to its new value in the
|
||||||
|
// next run loop iteration.
|
||||||
switchContent: function(newContent) {
|
switchContent: function(newContent) {
|
||||||
var composer = this;
|
var composer = this;
|
||||||
this.confirmExit().then(function() {
|
this.confirmExit().then(function() {
|
||||||
@ -20,6 +32,9 @@ export default Ember.Controller.extend(Ember.Evented, {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Ask the content component if it's OK to close it, and give it the
|
||||||
|
// opportunity to abort. The content component must respond to the
|
||||||
|
// `willExit(abort)` action, and call `abort()` if we should not proceed.
|
||||||
confirmExit: function() {
|
confirmExit: function() {
|
||||||
var composer = this;
|
var composer = this;
|
||||||
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
|
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
|
||||||
@ -33,39 +48,43 @@ export default Ember.Controller.extend(Ember.Evented, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
show: function() {
|
||||||
|
var composer = this;
|
||||||
|
|
||||||
|
// We do this in the next run loop because we need to wait for new
|
||||||
|
// content to be switched in. See `switchContent` above.
|
||||||
|
Ember.run.next(function() {
|
||||||
|
composer.set('position', PositionEnum.NORMAL);
|
||||||
|
composer.trigger('focus');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this.set('position', PositionEnum.HIDDEN);
|
||||||
|
var content = this.get('content');
|
||||||
|
if (content) {
|
||||||
|
content.send('reset');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
close: function() {
|
close: function() {
|
||||||
var composer = this;
|
var composer = this;
|
||||||
this.confirmExit().then(function() {
|
this.confirmExit().then(function() {
|
||||||
composer.send('hide');
|
composer.send('hide');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
hide: function() {
|
|
||||||
this.set('showing', false);
|
|
||||||
this.set('fullScreen', false);
|
|
||||||
var content = this.get('content');
|
|
||||||
if (content) {
|
|
||||||
content.send('reset');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
minimize: function() {
|
minimize: function() {
|
||||||
this.set('minimized', true);
|
this.set('position', PositionEnum.MINIMIZED);
|
||||||
this.set('fullScreen', false);
|
|
||||||
},
|
},
|
||||||
show: function() {
|
|
||||||
var composer = this;
|
fullscreen: function() {
|
||||||
Ember.run.next(function() {
|
this.set('position', PositionEnum.FULLSCREEN);
|
||||||
composer.set('showing', true);
|
|
||||||
composer.set('minimized', false);
|
|
||||||
composer.trigger('focus');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fullScreen: function() {
|
|
||||||
this.set('fullScreen', true);
|
|
||||||
this.set('minimized', false);
|
|
||||||
this.trigger('focus');
|
this.trigger('focus');
|
||||||
},
|
},
|
||||||
exitFullScreen: function() {
|
|
||||||
this.set('fullScreen', false);
|
exitFullscreen: function() {
|
||||||
|
this.set('position', PositionEnum.NORMAL);
|
||||||
this.trigger('focus');
|
this.trigger('focus');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.btn-minimize .fa {
|
.fa-minus.minimize {
|
||||||
vertical-align: -5px;
|
vertical-align: -5px;
|
||||||
}
|
}
|
||||||
.composer-avatar {
|
.composer-avatar {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{{user-avatar user class="composer-avatar"}}
|
{{user-avatar user class="composer-avatar"}}
|
||||||
|
|
||||||
<div class="composer-body">
|
<div class="composer-body">
|
||||||
{{ui/controls/item-list items=headerItems class="composer-header list-inline"}}
|
{{ui/controls/item-list items=controls class="composer-header list-inline"}}
|
||||||
|
|
||||||
<div class="composer-editor">
|
<div class="composer-editor">
|
||||||
{{ui/controls/text-editor submit="submit" value=value placeholder=placeholder submitLabel=submitLabel}}
|
{{ui/controls/text-editor submit="submit" value=value placeholder=placeholder submitLabel=submitLabel disabled=loading}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
{{textarea value=value placeholder=placeholder class="form-control"}}
|
{{textarea value=value placeholder=placeholder class="form-control flexible-height" disabled=disabled}}
|
||||||
|
|
||||||
{{ui/controls/item-list items=controlItems class="text-editor-controls"}}
|
{{ui/controls/item-list items=controlItems class="text-editor-controls fade" classNameBindings="value:in"}}
|
||||||
|
@ -1,112 +1,66 @@
|
|||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
import { PositionEnum } from '../controllers/composer';
|
||||||
import ActionButton from '../components/ui/controls/action-button';
|
import ActionButton from '../components/ui/controls/action-button';
|
||||||
import TaggedArray from '../utils/tagged-array';
|
import TaggedArray from '../utils/tagged-array';
|
||||||
|
|
||||||
|
var $ = Ember.$;
|
||||||
|
|
||||||
export default Ember.View.extend(Ember.Evented, {
|
export default Ember.View.extend(Ember.Evented, {
|
||||||
|
|
||||||
classNames: ['composer'],
|
classNames: ['composer'],
|
||||||
|
|
||||||
classNameBindings: [
|
classNameBindings: [
|
||||||
'controller.showing:showing',
|
'minimized',
|
||||||
'controller.minimized:minimized',
|
'fullscreen',
|
||||||
'controller.fullScreen:fullscreen',
|
|
||||||
'active'
|
'active'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
position: Ember.computed.alias('controller.position'),
|
||||||
|
visible: Ember.computed.alias('controller.visible'),
|
||||||
|
normal: Ember.computed.alias('controller.normal'),
|
||||||
|
minimized: Ember.computed.alias('controller.minimized'),
|
||||||
|
fullscreen: Ember.computed.alias('controller.fullscreen'),
|
||||||
|
|
||||||
|
// Calculate the composer's current height, based on the intended height
|
||||||
|
// (which is set when the resizing handle is dragged), and the composer's
|
||||||
|
// current state.
|
||||||
computedHeight: function() {
|
computedHeight: function() {
|
||||||
if (this.get('controller.minimized') || this.get('controller.fullScreen')) {
|
if (this.get('minimized')) {
|
||||||
return '';
|
return '';
|
||||||
|
} else if (this.get('fullscreen')) {
|
||||||
|
return $(window).height();
|
||||||
} else {
|
} else {
|
||||||
return Math.max(200, Math.min(this.get('height'), $(window).height() - $('#header').outerHeight()));
|
return Math.max(200, Math.min(this.get('height'), $(window).height() - $('#header').outerHeight()));
|
||||||
}
|
}
|
||||||
}.property('height', 'controller.minimized', 'controller.fullScreen'),
|
}.property('height', 'minimized', 'fullscreen'),
|
||||||
|
|
||||||
updateHeight: function() {
|
|
||||||
if (! this.$()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var view = this;
|
|
||||||
Ember.run.scheduleOnce('afterRender', function() {
|
|
||||||
view.$().height(view.get('computedHeight'));
|
|
||||||
view.updateTextareaHeight();
|
|
||||||
// view.updateBottomPadding();
|
|
||||||
});
|
|
||||||
}.observes('computedHeight'),
|
|
||||||
|
|
||||||
showingChanged: function() {
|
|
||||||
if (! this.$()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var view = this;
|
|
||||||
if (view.get('controller.showing')) {
|
|
||||||
view.$().show();
|
|
||||||
}
|
|
||||||
Ember.run.scheduleOnce('afterRender', function() {
|
|
||||||
view.$().css('bottom', view.get('controller.showing') ? -view.$().outerHeight() : 0);
|
|
||||||
view.$().animate({bottom: view.get('controller.showing') ? 0 : -view.$().outerHeight()}, 'fast', function() {
|
|
||||||
if (view.get('controller.showing')) {
|
|
||||||
view.focus();
|
|
||||||
} else {
|
|
||||||
view.$().hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
view.updateBottomPadding(true);
|
|
||||||
});
|
|
||||||
}.observes('controller.showing'),
|
|
||||||
|
|
||||||
minimizedChanged: function() {
|
|
||||||
if (! this.$() || ! this.get('controller.showing')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var view = this;
|
|
||||||
var oldHeight = this.$().height();
|
|
||||||
Ember.run.scheduleOnce('afterRender', function() {
|
|
||||||
var newHeight = view.$().height();
|
|
||||||
view.updateBottomPadding(true);
|
|
||||||
view.$().css('height', oldHeight).animate({height: newHeight}, 'fast', function() {
|
|
||||||
if (! view.get('controller.minimized')) {
|
|
||||||
view.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}.observes('controller.minimized'),
|
|
||||||
|
|
||||||
fullScreenChanged: function() {
|
|
||||||
if (! this.$()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var view = this;
|
|
||||||
Ember.run.scheduleOnce('afterRender', function() {
|
|
||||||
view.updateTextareaHeight();
|
|
||||||
});
|
|
||||||
}.observes('controller.fullScreen'),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
|
var view = this;
|
||||||
|
var controller = this.get('controller');
|
||||||
|
|
||||||
|
// Hide the composer to begin with.
|
||||||
|
this.set('height', this.$().height());
|
||||||
this.$().hide();
|
this.$().hide();
|
||||||
|
|
||||||
var controller = this.get('controller');
|
// If the composer is minimized, allow the user to click anywhere on
|
||||||
|
// it to show it.
|
||||||
this.$('.composer-content').click(function() {
|
this.$('.composer-content').click(function() {
|
||||||
if (controller.get('minimized')) {
|
if (view.get('minimized')) {
|
||||||
controller.send('show');
|
controller.send('show');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var view = this;
|
// Modulate the view's active property/class according to the focus
|
||||||
|
// state of any inputs.
|
||||||
this.$().on('focus', ':input', function() {
|
this.$().on('focus', ':input', function() {
|
||||||
view.set('active', true);
|
view.set('active', true);
|
||||||
}).on('blur', ':input', function() {
|
}).on('blur', ':input', function() {
|
||||||
view.set('active', false);
|
view.set('active', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.set('height', this.$().height());
|
// Focus on the first input when the controller wants to focus.
|
||||||
|
|
||||||
controller.on('focus', this, this.focus);
|
controller.on('focus', this, this.focus);
|
||||||
|
|
||||||
|
// Set up the handle so that the composer can be resized.
|
||||||
$(window).on('resize', {view: this}, this.windowWasResized).resize();
|
$(window).on('resize', {view: this}, this.windowWasResized).resize();
|
||||||
|
|
||||||
var dragData = {view: this};
|
var dragData = {view: this};
|
||||||
@ -123,107 +77,36 @@ export default Ember.View.extend(Ember.Evented, {
|
|||||||
$(document)
|
$(document)
|
||||||
.on('mousemove', dragData, this.mouseWasMoved)
|
.on('mousemove', dragData, this.mouseWasMoved)
|
||||||
.on('mouseup', dragData, this.mouseWasReleased);
|
.on('mouseup', dragData, this.mouseWasReleased);
|
||||||
|
|
||||||
|
// When the escape key is pressed on any inputs, close the composer.
|
||||||
|
this.$().on('keydown', ':input', 'esc', function() {
|
||||||
|
controller.send('close');
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
willDestroyElement: function() {
|
willDestroyElement: function() {
|
||||||
$(window)
|
$(window).off('resize', this.windowWasResized);
|
||||||
.off('resize', this.windowWasResized);
|
|
||||||
|
|
||||||
$(document)
|
$(document)
|
||||||
.off('mousemove', this.mouseWasMoved)
|
.off('mousemove', this.mouseWasMoved)
|
||||||
.off('mouseup', this.mouseWasReleased);
|
.off('mouseup', this.mouseWasReleased);
|
||||||
},
|
},
|
||||||
|
|
||||||
windowWasResized: function(event) {
|
// Update the amount of padding-bottom on the body so that the page's
|
||||||
var view = event.data.view;
|
// content will still be visible above the composer when the page is
|
||||||
view.notifyPropertyChange('computedHeight');
|
// scrolled right to the bottom.
|
||||||
},
|
updateBodyPadding: function(animate) {
|
||||||
|
// Before we change anything, work out if we're currently scrolled
|
||||||
|
// right to the bottom of the page. If we are, we'll want to anchor
|
||||||
|
// the body's scroll position to the bottom after we update the
|
||||||
|
// padding.
|
||||||
|
var anchorScroll = $(window).scrollTop() + $(window).height() >= $(document).height();
|
||||||
|
|
||||||
mouseWasMoved: function(event) {
|
var func = animate ? 'animate' : 'css';
|
||||||
if (! event.data.handle) {
|
var paddingBottom = this.get('visible') ? this.get('computedHeight') - Ember.$('#footer').outerHeight(true) : 0;
|
||||||
return;
|
$('#main')[func]({paddingBottom: paddingBottom}, 'fast');
|
||||||
}
|
|
||||||
var view = event.data.view;
|
|
||||||
|
|
||||||
var deltaPixels = event.data.mouseStart - event.clientY;
|
if (anchorScroll) {
|
||||||
view.set('height', event.data.heightStart + deltaPixels);
|
|
||||||
view.updateTextareaHeight();
|
|
||||||
view.updateBottomPadding();
|
|
||||||
},
|
|
||||||
|
|
||||||
mouseWasReleased: function(event) {
|
|
||||||
if (! event.data.handle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.data.handle = null;
|
|
||||||
$('body').css('cursor', '');
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshControls: function() {
|
|
||||||
var controlItems = TaggedArray.create();
|
|
||||||
this.trigger('populateControls', controlItems);
|
|
||||||
this.set('controlItems', controlItems);
|
|
||||||
}.observes('controller.minimized', 'controller.fullScreen'),
|
|
||||||
|
|
||||||
populateControls: function(controls) {
|
|
||||||
var controller = this.get('controller');
|
|
||||||
|
|
||||||
if (controller.get('fullScreen')) {
|
|
||||||
var exit = ActionButton.create({
|
|
||||||
icon: 'compress',
|
|
||||||
title: 'Exit Full Screen',
|
|
||||||
className: 'btn btn-icon btn-link',
|
|
||||||
action: function() {
|
|
||||||
controller.send('exitFullScreen');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
controls.pushObjectWithTag(exit, 'exit');
|
|
||||||
} else {
|
|
||||||
if (! controller.get('minimized')) {
|
|
||||||
var minimize = ActionButton.create({
|
|
||||||
icon: 'minus',
|
|
||||||
title: 'Minimize',
|
|
||||||
className: 'btn btn-icon btn-link btn-minimize',
|
|
||||||
action: function() {
|
|
||||||
controller.send('minimize');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
controls.pushObjectWithTag(minimize, 'minimize');
|
|
||||||
|
|
||||||
var fullScreen = ActionButton.create({
|
|
||||||
icon: 'expand',
|
|
||||||
title: 'Full Screen',
|
|
||||||
className: 'btn btn-icon btn-link',
|
|
||||||
action: function() {
|
|
||||||
controller.send('fullScreen');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
controls.pushObjectWithTag(fullScreen, 'fullScreen');
|
|
||||||
}
|
|
||||||
|
|
||||||
var close = ActionButton.create({
|
|
||||||
icon: 'times',
|
|
||||||
title: 'Close',
|
|
||||||
className: 'btn btn-icon btn-link',
|
|
||||||
action: function() {
|
|
||||||
controller.send('close');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
controls.pushObjectWithTag(close, 'close');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
focus: function() {
|
|
||||||
this.$().find(':input:enabled:visible:first').focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateBottomPadding: function(animate) {
|
|
||||||
var top = $(document).height() - $(window).height();
|
|
||||||
var isBottom = $(window).scrollTop() >= top;
|
|
||||||
|
|
||||||
$('#main')[animate ? 'animate' : 'css']({paddingBottom: this.get('controller.showing') ? this.$().outerHeight() - Ember.$('#footer').outerHeight(true) : 0}, 'fast');
|
|
||||||
|
|
||||||
if (isBottom) {
|
|
||||||
if (animate) {
|
if (animate) {
|
||||||
$('html, body').animate({scrollTop: $(document).height()}, 'fast');
|
$('html, body').animate({scrollTop: $(document).height()}, 'fast');
|
||||||
} else {
|
} else {
|
||||||
@ -232,13 +115,151 @@ export default Ember.View.extend(Ember.Evented, {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateTextareaHeight: function() {
|
// Update the height of the stuff inside of the composer. There should be
|
||||||
|
// an element with the class .flexible-height — this element is intended
|
||||||
|
// to fill up the height of the composer, minus the space taken up by the
|
||||||
|
// composer's header/footer/etc.
|
||||||
|
updateContentHeight: function() {
|
||||||
var content = this.$('.composer-content');
|
var content = this.$('.composer-content');
|
||||||
this.$('textarea').height((this.get('computedHeight') || this.$().height())
|
this.$('.flexible-height').height(this.get('computedHeight')
|
||||||
- parseInt(content.css('padding-top'))
|
- parseInt(content.css('padding-top'))
|
||||||
- parseInt(content.css('padding-bottom'))
|
- parseInt(content.css('padding-bottom'))
|
||||||
- this.$('.composer-header').outerHeight(true)
|
- this.$('.composer-header').outerHeight(true)
|
||||||
- this.$('.text-editor-controls').outerHeight(true));
|
- this.$('.text-editor-controls').outerHeight(true));
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// OBSERVERS
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Whenever the composer is minimized or goes to/from fullscreen, we need
|
||||||
|
// to re-populate the control buttons, because their configuration depends
|
||||||
|
// on the composer's current state.
|
||||||
|
refreshControls: function() {
|
||||||
|
var controlItems = TaggedArray.create();
|
||||||
|
this.trigger('populateControls', controlItems);
|
||||||
|
this.set('controlItems', controlItems);
|
||||||
|
}.observes('minimized', 'fullscreen'),
|
||||||
|
|
||||||
|
// Whenever the composer's computed height changes, update the DOM to
|
||||||
|
// reflect it.
|
||||||
|
updateHeight: function() {
|
||||||
|
if (!this.$()) { return; }
|
||||||
|
|
||||||
|
var view = this;
|
||||||
|
Ember.run.scheduleOnce('afterRender', function() {
|
||||||
|
view.$().height(view.get('computedHeight'));
|
||||||
|
view.updateContentHeight();
|
||||||
|
});
|
||||||
|
}.observes('computedHeight'),
|
||||||
|
|
||||||
|
positionWillChange: function() {
|
||||||
|
this.set('oldPosition', this.get('position'));
|
||||||
|
}.observesBefore('position'),
|
||||||
|
|
||||||
|
// Whenever the composer's display state changes, update the DOM to slide
|
||||||
|
// it in or out.
|
||||||
|
positionDidChange: function() {
|
||||||
|
var $composer = this.$();
|
||||||
|
if (!$composer) { return; }
|
||||||
|
var view = this;
|
||||||
|
|
||||||
|
// At this stage, the position property has just changed, and the
|
||||||
|
// class name hasn't been altered in the DOM. So, we can grab the
|
||||||
|
// composer's current height which we might want to animate from.
|
||||||
|
// After the DOM has updated, we animate to its new height.
|
||||||
|
var oldHeight = $composer.height();
|
||||||
|
|
||||||
|
Ember.run.scheduleOnce('afterRender', function() {
|
||||||
|
var newHeight = $composer.height();
|
||||||
|
|
||||||
|
switch (view.get('position')) {
|
||||||
|
case PositionEnum.HIDDEN:
|
||||||
|
$composer.animate({bottom: -oldHeight}, 'fast', function() {
|
||||||
|
$composer.hide();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PositionEnum.NORMAL:
|
||||||
|
if (view.get('oldPosition') !== PositionEnum.FULLSCREEN) {
|
||||||
|
$composer.show();
|
||||||
|
$composer.css({height: oldHeight}).animate({bottom: 0, height: newHeight}, 'fast', function() {
|
||||||
|
view.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PositionEnum.MINIMIZED:
|
||||||
|
$composer.css({height: oldHeight}).animate({height: newHeight}, 'fast', function() {
|
||||||
|
view.focus();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.get('position') !== PositionEnum.FULLSCREEN) {
|
||||||
|
view.updateBodyPadding(true);
|
||||||
|
}
|
||||||
|
view.updateContentHeight();
|
||||||
|
});
|
||||||
|
}.observes('position'),
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// LISTENERS
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
windowWasResized: function(event) {
|
||||||
|
// Force a recalculation of the computed height, because its value
|
||||||
|
// depends on the window's height.
|
||||||
|
var view = event.data.view;
|
||||||
|
view.notifyPropertyChange('computedHeight');
|
||||||
|
},
|
||||||
|
|
||||||
|
mouseWasMoved: function(event) {
|
||||||
|
if (! event.data.handle) { return; }
|
||||||
|
var view = event.data.view;
|
||||||
|
|
||||||
|
// Work out how much the mouse has been moved, and set the height
|
||||||
|
// relative to the old one based on that. Then update the content's
|
||||||
|
// height so that it fills the height of the composer, and update the
|
||||||
|
// body's padding.
|
||||||
|
var deltaPixels = event.data.mouseStart - event.clientY;
|
||||||
|
view.set('height', event.data.heightStart + deltaPixels);
|
||||||
|
view.updateContentHeight();
|
||||||
|
view.updateBodyPadding();
|
||||||
|
},
|
||||||
|
|
||||||
|
mouseWasReleased: function(event) {
|
||||||
|
if (! event.data.handle) { return; }
|
||||||
|
event.data.handle = null;
|
||||||
|
$('body').css('cursor', '');
|
||||||
|
},
|
||||||
|
|
||||||
|
focus: function() {
|
||||||
|
this.$().find(':input:enabled:visible:first').focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
populateControls: function(controls) {
|
||||||
|
var controller = this.get('controller');
|
||||||
|
var addControl = function(action, icon, title) {
|
||||||
|
var control = ActionButton.create({
|
||||||
|
icon: icon,
|
||||||
|
title: title,
|
||||||
|
className: 'btn btn-icon btn-link',
|
||||||
|
action: function() {
|
||||||
|
controller.send(action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
controls.pushObjectWithTag(control, action);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.get('fullscreen')) {
|
||||||
|
addControl('exitFullscreen', 'compress', 'Exit Full Screen');
|
||||||
|
} else {
|
||||||
|
if (! this.get('minimized')) {
|
||||||
|
addControl('minimize', 'minus minimize', 'Minimize');
|
||||||
|
addControl('fullscreen', 'expand', 'Full Screen');
|
||||||
|
}
|
||||||
|
addControl('close', 'times', 'Close');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user