mirror of
https://github.com/flarum/framework.git
synced 2025-01-31 16:12:24 +08:00
Work on composer, early implementation of replying
This commit is contained in:
parent
edce73d6e9
commit
12622e6c28
9
ember/app/components/ui/controls/text-editor.js
Normal file
9
ember/app/components/ui/controls/text-editor.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
actions: {
|
||||
save: function() {
|
||||
this.sendAction('save', this.get('value'));
|
||||
}
|
||||
}
|
||||
});
|
42
ember/app/controllers/composer.js
Normal file
42
ember/app/controllers/composer.js
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
|
@ -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
|
||||
|
|
16
ember/app/mixins/add-css-class-to-body.js
Normal file
16
ember/app/mixins/add-css-class-to-body.js
Normal 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();
|
||||
}
|
||||
});
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -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')
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user