Work on composer, early implementation of replying

This commit is contained in:
Toby Zerner 2015-01-30 12:22:19 +10:30
parent edce73d6e9
commit 12622e6c28
11 changed files with 229 additions and 72 deletions

View File

@ -0,0 +1,9 @@
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
save: function() {
this.sendAction('save', this.get('value'));
}
}
});

View File

@ -0,0 +1,42 @@
import Ember from 'ember';
export default Ember.Controller.extend({
needs: ['index', 'application'],
user: Ember.Object.create({avatarNumber: 1}),
discussion: null,
showing: true,
minimized: false,
title: 'Replying to <em>Some Discussion Title</em>',
actions: {
close: function() {
this.set('showing', false);
},
minimize: function() {
this.set('minimized', true);
},
show: function() {
this.set('minimized', false);
},
save: function(value) {
var store = this.store;
var discussion = this.get('discussion');
var controller = this;
var post = store.createRecord('post', {
content: value,
discussion: discussion
});
post.save().then(function(post) {
discussion.set('posts', discussion.get('posts')+','+post.get('id'));
controller.get('delegate').send('replyAdded', post);
});
}
}
});

View File

@ -40,8 +40,26 @@ export default Ember.ObjectController.extend(Ember.Evented, {
actions: {
reply: function() {
this.set('controllers.composer.showing', true);
this.set('controllers.composer.title', 'Replying to <em>'+this.get('model.title')+'</em>');
var composer = this.get('controllers.composer');
// composer.beginPropertyChanges();
composer.set('minimized', false);
composer.set('showing', true);
composer.set('title', 'Replying to <em>'+this.get('model.title')+'</em>');
composer.set('delegate', this);
composer.set('discussion', this.get('model'));
// composer.endPropertyChanges();
},
replyAdded: function(post) {
var stream = this.get('stream');
stream.set('ids', this.get('model.postIds'));
var index = stream.get('count') - 1;
stream.get('content').pushObject(Ember.Object.create({
indexStart: index,
indexEnd: index,
content: post
}));
this.get('controllers.composer').set('showing', false);
},
// This action is called when the start position of the discussion

View File

@ -0,0 +1,16 @@
import Ember from 'ember';
export default Ember.Mixin.create({
activate: function() {
var cssClass = this.toCssClass();
Ember.$('body').addClass(cssClass);
},
deactivate: function() {
Ember.$('body').removeClass(this.toCssClass());
},
toCssClass: function() {
return this.routeName.replace(/\./g, '-').dasherize();
}
});

View File

@ -1,6 +1,8 @@
import Ember from 'ember';
export default Ember.Route.extend({
import AddCssClassToBodyMixin from '../../mixins/add-css-class-to-body';
export default Ember.Route.extend(AddCssClassToBodyMixin, {
// When we enter the discussions list view, we no longer want the
// discussions list to be in pane mode.

View File

@ -8,33 +8,67 @@
right: 0;
z-index: @zindex-navbar-fixed;
pointer-events: none;
.transition(left 0.2s);
.with-pane & {
left: @index-pane-width;
}
}
.composer {
pointer-events: auto;
margin-left: 200px;
margin-left: -20px;
margin-right: 200px;
.box-shadow(0 2px 6px rgba(0, 0, 0, 0.25));
border-radius: 4px 4px 0 0;
background: rgba(255, 255, 255, 0.98);
background: rgba(255, 255, 255, 0.9);
transform: translateZ(0); // Fix for Chrome bug where a transparent white background is actually gray
position: relative;
.transition(~"margin-left 0.2s, margin-right 0.2s, background 0.2s");
.index-index & {
margin-left: 205px;
margin-right: -20px;
}
&.active {
background: rgba(255, 255, 255, 0.98);
}
&.minimized {
height: 50px;
cursor: pointer;
}
}
.composer-content {
padding: 0 20px 15px;
padding: 20px 20px 15px;
.minimized & {
padding: 10px 20px;
}
}
.composer-handle {
height: 20px;
margin-bottom: -20px;
cursor: row-resize;
position: relative;
.minimized & {
display: none;
}
}
.composer-controls {
float: right;
margin: -5px 15px 0 0;
position: absolute;
right: 10px;
top: 10px;
}
.composer-avatar {
float: left;
width: 64px;
height: 64px;
.avatar-size(64px);
.minimized & {
display: none;
}
}
.composer-body {
margin-left: 84px;
margin-left: 90px;
& h3 {
margin: 5px 0 10px;
@ -42,20 +76,30 @@
font-size: 16px;
font-weight: normal;
}
}
.composer-editor textarea {
background: none;
border-radius: 0;
padding: 0;
margin-bottom: 10px;
height: 200px;
border: 0;
resize: none;
color: @fl-body-color;
font-size: 15px;
line-height: 1.6;
&:focus {
.minimized & {
margin-left: 0;
}
}
.composer-editor {
& textarea {
background: none;
border-radius: 0;
padding: 0;
margin-bottom: 10px;
height: 200px;
border: 0;
resize: none;
color: @fl-body-color;
font-size: 14px;
line-height: 1.6;
&:focus {
background: none;
}
}
.minimized & {
visibility: hidden;
}
}

View File

@ -1,4 +1,4 @@
<div {{bind-attr class=":page panePinned:with-pane"}}>
<div id="page" {{bind-attr class=":page panePinned:with-pane"}}>
<header id="header" class="global-header">
@ -31,11 +31,11 @@
<main id="main" class="global-main">
{{outlet}}
{{!-- <div class="composer-container">
<div class="composer-container">
<div class="container">
{{render "composer"}}
</div>
</div> --}}
</div>
</main>
<footer id="footer" class="global-footer">

View File

@ -1,7 +1,7 @@
<textarea class="form-control" {{bind-attr placeholder=placeholder}}></textarea>
{{textarea value=value placeholder=placeholder class="form-control"}}
<div class="composer-editor-controls">
<button class="btn btn-primary">Submit Reply</button>
<button class="btn btn-primary" {{action "save"}}>Submit Reply</button>
<div class="btn-group">
<button class="btn btn-default btn-icon"><i class="fa fa-fw fa-image"></i></button>
<button class="btn btn-default btn-icon"><i class="fa fa-fw fa-paperclip"></i></button>

View File

@ -1,14 +1,16 @@
<div class="composer-handle"></div>
<div class="composer-controls btn-group">
<div class="btn-group dropdown">
<a href="#" {{action "fullScreen"}} class="btn btn-icon btn-link dropdown-toggle" data-toggle="dropdown">{{fa-icon "ellipsis-v"}}</a>
<ul class="dropdown-menu pull-right">
<li><a href="#">{{fa-icon "expand"}} Full Screen</a></li>
<li><a href="#">{{fa-icon "external-link"}} Pop-Out</a></li>
</ul>
</div>
<a href="#" {{action "hide"}} class="btn btn-icon btn-link">{{fa-icon "chevron-down"}}</a>
{{#unless minimized}}
<div class="btn-group dropdown">
<a href="#" {{action "fullScreen"}} class="btn btn-icon btn-link dropdown-toggle" data-toggle="dropdown">{{fa-icon "ellipsis-v"}}</a>
<ul class="dropdown-menu pull-right">
<li><a href="#">{{fa-icon "expand"}} Full Screen</a></li>
<li><a href="#">{{fa-icon "external-link"}} Pop-Out</a></li>
</ul>
</div>
<a href="#" {{action "minimize"}} class="btn btn-icon btn-link">{{fa-icon "chevron-down"}}</a>
{{/unless}}
<a href="#" {{action "close"}} class="btn btn-icon btn-link">{{fa-icon "times"}}</a>
</div>
@ -21,7 +23,7 @@
<h3>{{{title}}}</h3>
<div class="composer-editor">
{{ui/controls/text-editor placeholder=""}}
{{ui/controls/text-editor placeholder="" save="save"}}
</div>
</div>

View File

@ -4,35 +4,65 @@ export default Ember.View.extend({
classNames: ['composer'],
// classNameBindings: ['controller.showing:showing'],
classNameBindings: ['controller.showing:showing', 'controller.minimized:minimized', 'active'],
// showingChanged: function() {
// if (this.$()) {
// var view = this;
// this.$().animate({bottom: this.get('controller.showing') ? 20 : -this.$().height()}, 'fast', function() {
// if (view.get('controller.showing')) {
// $(this).find('textarea').focus();
// }
// });
// $('#body').animate({marginBottom: this.get('controller.showing') ? this.$().height() + 20 : 0}, 'fast');
// }
// }.observes('controller.showing'),
showingChanged: function() {
if (! this.$()) {
return;
}
// panePinnedChanged: function() {
// if (this.$()) {
// var discussions = this.get('controller.controllers.discussions');
// var $this = this.$();
// Ember.run.scheduleOnce('afterRender', function() {
// var discussion = $('.discussion-pane');
// var width = discussion.length ? discussion.offset().left : $('#body').offset().left;
// $this.css('left', width);
// });
// }
// }.observes('controller.controllers.discussions.paned', 'controller.controllers.discussions.panePinned'),
var view = this;
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')) {
Ember.$(this).find('textarea').focus();
}
});
view.updateBottomPadding();
});
}.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();
view.$().css('height', oldHeight).animate({height: newHeight}, 'fast', function() {
view.$().css('height', '');
if (! view.get('controller.minimized')) {
view.$('textarea').focus();
}
});
});
}.observes('controller.minimized'),
didInsertElement: function() {
// this.showingChanged();
// this.panePinnedChanged();
this.showingChanged();
this.minimizedChanged();
var controller = this.get('controller');
this.$('.composer-content').click(function() {
if (controller.get('minimized')) {
controller.send('show');
}
});
var view = this;
this.$('textarea').focus(function() {
view.set('active', true);
}).blur(function() {
view.set('active', false);
});
},
updateBottomPadding: function() {
Ember.$('#main').animate({paddingBottom: this.get('controller.showing') ? this.$().outerHeight() - Ember.$('#footer').outerHeight(true) : 0}, 'fast');
}
});

View File

@ -89,20 +89,14 @@ export default Ember.View.extend(Ember.Evented, {
populateControlsDefault: function(controls) {
var view = this;
var ReplyItem = ActionButton.extend({
var reply = ActionButton.create({
label: 'Reply',
icon: 'reply',
classNameBindings: ['className', 'replying:disabled'],
replying: function() {
return this.get('parentController.controllers.composer.showing');
}.property('parentController.controllers.composer.showing'),
action: function() {
var lastPost = $('.posts .item:last');
$('html, body').animate({scrollTop: lastPost.offset().top + lastPost.outerHeight() - $(window).height() + $('.composer').height() + 19}, 'fast');
view.get('streamContent').send('goToIndex', view.get('controller.stream.count') - 1);
view.get('controller').send('reply');
},
parentController: this.get('controller'),
});
controls.pushObjectWithTag(ReplyItem.create(), 'reply');
controls.pushObjectWithTag(reply, 'reply');
}.on('populateControls')
});