1
0
mirror of https://github.com/flarum/core.git synced 2025-07-25 02:31:17 +02:00

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: { actions: {
reply: function() { reply: function() {
this.set('controllers.composer.showing', true); var composer = this.get('controllers.composer');
this.set('controllers.composer.title', 'Replying to <em>'+this.get('model.title')+'</em>'); // 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 // 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'; 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 // When we enter the discussions list view, we no longer want the
// discussions list to be in pane mode. // discussions list to be in pane mode.

View File

@@ -8,33 +8,67 @@
right: 0; right: 0;
z-index: @zindex-navbar-fixed; z-index: @zindex-navbar-fixed;
pointer-events: none; pointer-events: none;
.transition(left 0.2s);
.with-pane & {
left: @index-pane-width;
}
} }
.composer { .composer {
pointer-events: auto; pointer-events: auto;
margin-left: 200px; margin-left: -20px;
margin-right: 200px;
.box-shadow(0 2px 6px rgba(0, 0, 0, 0.25)); .box-shadow(0 2px 6px rgba(0, 0, 0, 0.25));
border-radius: 4px 4px 0 0; 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 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 { .composer-content {
padding: 0 20px 15px; padding: 20px 20px 15px;
.minimized & {
padding: 10px 20px;
}
} }
.composer-handle { .composer-handle {
height: 20px; height: 20px;
margin-bottom: -20px;
cursor: row-resize; cursor: row-resize;
position: relative;
.minimized & {
display: none;
}
} }
.composer-controls { .composer-controls {
float: right; position: absolute;
margin: -5px 15px 0 0; right: 10px;
top: 10px;
} }
.composer-avatar { .composer-avatar {
float: left; float: left;
width: 64px; .avatar-size(64px);
height: 64px;
.minimized & {
display: none;
}
} }
.composer-body { .composer-body {
margin-left: 84px; margin-left: 90px;
& h3 { & h3 {
margin: 5px 0 10px; margin: 5px 0 10px;
@@ -42,20 +76,30 @@
font-size: 16px; font-size: 16px;
font-weight: normal; 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; 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"> <header id="header" class="global-header">
@@ -31,11 +31,11 @@
<main id="main" class="global-main"> <main id="main" class="global-main">
{{outlet}} {{outlet}}
{{!-- <div class="composer-container"> <div class="composer-container">
<div class="container"> <div class="container">
{{render "composer"}} {{render "composer"}}
</div> </div>
</div> --}} </div>
</main> </main>
<footer id="footer" class="global-footer"> <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"> <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"> <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-image"></i></button>
<button class="btn btn-default btn-icon"><i class="fa fa-fw fa-paperclip"></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-handle"></div>
<div class="composer-controls btn-group"> <div class="composer-controls btn-group">
<div class="btn-group dropdown"> {{#unless minimized}}
<a href="#" {{action "fullScreen"}} class="btn btn-icon btn-link dropdown-toggle" data-toggle="dropdown">{{fa-icon "ellipsis-v"}}</a> <div class="btn-group dropdown">
<ul class="dropdown-menu pull-right"> <a href="#" {{action "fullScreen"}} class="btn btn-icon btn-link dropdown-toggle" data-toggle="dropdown">{{fa-icon "ellipsis-v"}}</a>
<li><a href="#">{{fa-icon "expand"}} Full Screen</a></li> <ul class="dropdown-menu pull-right">
<li><a href="#">{{fa-icon "external-link"}} Pop-Out</a></li> <li><a href="#">{{fa-icon "expand"}} Full Screen</a></li>
</ul> <li><a href="#">{{fa-icon "external-link"}} Pop-Out</a></li>
</div> </ul>
<a href="#" {{action "hide"}} class="btn btn-icon btn-link">{{fa-icon "chevron-down"}}</a> </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> <a href="#" {{action "close"}} class="btn btn-icon btn-link">{{fa-icon "times"}}</a>
</div> </div>
@@ -21,7 +23,7 @@
<h3>{{{title}}}</h3> <h3>{{{title}}}</h3>
<div class="composer-editor"> <div class="composer-editor">
{{ui/controls/text-editor placeholder=""}} {{ui/controls/text-editor placeholder="" save="save"}}
</div> </div>
</div> </div>

View File

@@ -4,35 +4,65 @@ export default Ember.View.extend({
classNames: ['composer'], classNames: ['composer'],
// classNameBindings: ['controller.showing:showing'], classNameBindings: ['controller.showing:showing', 'controller.minimized:minimized', 'active'],
// showingChanged: function() { showingChanged: function() {
// if (this.$()) { if (! this.$()) {
// var view = this; return;
// 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'),
// panePinnedChanged: function() { var view = this;
// if (this.$()) { Ember.run.scheduleOnce('afterRender', function() {
// var discussions = this.get('controller.controllers.discussions'); view.$().css('bottom', view.get('controller.showing') ? -view.$().outerHeight() : 0);
// var $this = this.$(); view.$().animate({bottom: view.get('controller.showing') ? 0 : -view.$().outerHeight()}, 'fast', function() {
// Ember.run.scheduleOnce('afterRender', function() { if (view.get('controller.showing')) {
// var discussion = $('.discussion-pane'); Ember.$(this).find('textarea').focus();
// var width = discussion.length ? discussion.offset().left : $('#body').offset().left; }
// $this.css('left', width); });
// }); view.updateBottomPadding();
// } });
// }.observes('controller.controllers.discussions.paned', 'controller.controllers.discussions.panePinned'), }.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() { didInsertElement: function() {
// this.showingChanged(); this.showingChanged();
// this.panePinnedChanged(); 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) { populateControlsDefault: function(controls) {
var view = this; var view = this;
var ReplyItem = ActionButton.extend({ var reply = ActionButton.create({
label: 'Reply', label: 'Reply',
icon: 'reply', icon: 'reply',
classNameBindings: ['className', 'replying:disabled'],
replying: function() {
return this.get('parentController.controllers.composer.showing');
}.property('parentController.controllers.composer.showing'),
action: function() { action: function() {
var lastPost = $('.posts .item:last'); view.get('streamContent').send('goToIndex', view.get('controller.stream.count') - 1);
$('html, body').animate({scrollTop: lastPost.offset().top + lastPost.outerHeight() - $(window).height() + $('.composer').height() + 19}, 'fast');
view.get('controller').send('reply'); view.get('controller').send('reply');
}, },
parentController: this.get('controller'),
}); });
controls.pushObjectWithTag(ReplyItem.create(), 'reply'); controls.pushObjectWithTag(reply, 'reply');
}.on('populateControls') }.on('populateControls')
}); });