mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 05:37:34 +03:00
General Editor UI improvements. (#630)
refs https://github.com/TryGhost/Ghost/issues/8248 refs https://github.com/TryGhost/Ghost/issues/8194 closes https://github.com/TryGhost/Ghost/issues/8192 Miscellaneous editor reliability and usability fixes. - Improve the reliability of selection. - Ensure that the + menu appears even if there is a blank document (which meant the events weren't firing from mobiledoc itself) - When cards are added they are automatically selected and if possible go straight into edit mode (only works on the markdown card). - Fixes issues in Safari desktop, Safari mobile, and Firefox. - Tries to position UI on screen at all times. - Removes fastclick.
This commit is contained in:
parent
b683f692d1
commit
00f4cab7b9
@ -145,8 +145,12 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
editorKeyDown(event) {
|
||||
let editor = this.get('editor');
|
||||
// if the editor has a menu open then we don't want to capture inputs.
|
||||
if (this.get('editorMenuIsOpen')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let editor = this.get('editor');
|
||||
if (event.keyCode === 38) { // up arrow
|
||||
let selection = window.getSelection();
|
||||
if (!selection.rangeCount) {
|
||||
|
@ -52,7 +52,7 @@ export default Mixin.create({
|
||||
apiRoot: ghostPaths().apiRoot,
|
||||
assetPath: ghostPaths().assetRoot,
|
||||
editor: null,
|
||||
title: null,
|
||||
editorMenuIsOpen: false,
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
window.onbeforeunload = () => {
|
||||
@ -567,6 +567,12 @@ export default Mixin.create({
|
||||
|
||||
setEditor(editor) {
|
||||
this.set('editor', editor);
|
||||
},
|
||||
editorMenuIsOpen() {
|
||||
this.set('editorMenuIsOpen', true);
|
||||
},
|
||||
editorMenuIsClosed() {
|
||||
this.set('editorMenuIsOpen', false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -21,12 +21,38 @@
|
||||
resize: none;
|
||||
font-weight: 200;
|
||||
letter-spacing: 0.1px;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/*.__mobiledoc-editor *,
|
||||
.__mobiledoc-editor *:before,
|
||||
.__mobiledoc-editor *:after {
|
||||
box-sizing:unset;
|
||||
}*/
|
||||
|
||||
.kg-card {
|
||||
position: relative;
|
||||
display: block;
|
||||
display: inline-block; /* even though we hide the cursors there is still a
|
||||
zero width divider character on either side of this card,
|
||||
we need them to sit inline around this block otherwise we have
|
||||
a line at the top and bottom of the card. */
|
||||
width:100%;
|
||||
outline:none;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
z-index: 110; /* the title has a z-index of 100, this makes it sit above it. */
|
||||
}
|
||||
|
||||
.kg-card * {
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kg-card .is-editing * {
|
||||
user-select: auto;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.kg-card:hover,
|
||||
@ -40,9 +66,9 @@
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
margin-top: -56px;
|
||||
height: 46px;
|
||||
height: 10px;
|
||||
width:100%;
|
||||
overflow: visible;
|
||||
display: none;
|
||||
}
|
||||
.button-group {
|
||||
@ -54,30 +80,20 @@
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
border-radius:5px;
|
||||
|
||||
margin-top:-56px;
|
||||
height:46px;
|
||||
display:flex;
|
||||
box-shadow: 0 0 0 1px color(var(--darkgrey) l(-10%)), 0 8px 16px rgba(26,39,49,0.16), rgba(255,255,255,0.09) 0 1px 0 0 inset;
|
||||
}
|
||||
|
||||
/* keeps the hover when the cursor is moving from the card to the toolbar */
|
||||
.button-group:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom:-9px;
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.button-group:after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom:-9px;
|
||||
bottom:11px;
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
|
||||
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: transparent 10px solid;
|
||||
@ -85,7 +101,9 @@
|
||||
border-top: color(var(--darkgrey) l(-10%)) 8px solid;
|
||||
}
|
||||
|
||||
|
||||
.kg-card {
|
||||
min-height: 100px;
|
||||
}
|
||||
.kg-card.selected .kg-card-toolbar {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
@ -98,6 +116,7 @@
|
||||
font-size: 1.3rem;
|
||||
line-height: 46px;
|
||||
text-align:center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
font-weight: bold;
|
||||
padding:0 18px;
|
||||
@ -105,7 +124,7 @@
|
||||
|
||||
|
||||
.kg-card .kg-card-toolbar button {
|
||||
display: flex;
|
||||
/*display: flex;*/
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 46px;
|
||||
@ -120,6 +139,8 @@
|
||||
line-height: 46px;
|
||||
width:70px;
|
||||
text-align:center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
padding:0;
|
||||
}
|
||||
@ -129,6 +150,8 @@
|
||||
height:30px;
|
||||
width:60px;
|
||||
text-align:center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
margin:8px;
|
||||
|
||||
@ -155,6 +178,7 @@
|
||||
outline: none;
|
||||
border: none;
|
||||
resize: none;
|
||||
|
||||
}
|
||||
|
||||
.kg-card-toolbar button:hover {
|
||||
@ -220,11 +244,7 @@ textarea.ed_code {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* HTML Card
|
||||
*/
|
||||
|
||||
.kg-card-html {
|
||||
border: #ddd 1px solid;
|
||||
}
|
||||
/* markdown card */
|
||||
.kg-card-markdown textarea {
|
||||
resize: vertical;
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
color: color(var(--lightgrey) l(-10%));
|
||||
background: linear-gradient(
|
||||
color(var(--darkgrey) l(-3%)),
|
||||
@ -13,6 +15,7 @@
|
||||
);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 0 1px color(var(--darkgrey) l(-10%)), 0 8px 16px rgba(26,39,49,0.16), rgba(255,255,255,0.09) 0 1px 0 0 inset;
|
||||
z-index:110; /* places it above the title */
|
||||
}
|
||||
|
||||
.gh-toolbar:after {
|
||||
@ -28,6 +31,21 @@
|
||||
border-right: transparent 10px solid;
|
||||
border-top: color(var(--darkgrey) l(-10%)) 8px solid;
|
||||
}
|
||||
.gh-toolbar.tick-above:after {
|
||||
border: none;
|
||||
}
|
||||
.gh-toolbar.tick-full-left:after {
|
||||
left: 25%;
|
||||
}
|
||||
.gh-toolbar.tick-half-left:after {
|
||||
left: 40%;
|
||||
}
|
||||
.gh-toolbar.tick-full-right:after {
|
||||
left: 75%;
|
||||
}
|
||||
.gh-toolbar.tick-half-right:after {
|
||||
left: 60%;
|
||||
}
|
||||
|
||||
.gh-toolbar.is-link {
|
||||
width: 263px;
|
||||
|
@ -88,9 +88,11 @@
|
||||
/* Layout
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
|
||||
/*Exclude the editor*/
|
||||
*:not(.__mobiledoc-editor),
|
||||
*:not(.__mobiledoc-editor):before,
|
||||
*:not(.__mobiledoc-editor):after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
@ -1 +1 @@
|
||||
<div contenteditable="true" data-placeholder="Your Post Title" class="gh-editor-title" tabindex={{tabindex}}></div>
|
||||
<div contenteditable="true" data-placeholder="Your Post Title" class="gh-editor-title needsclick" tabindex={{tabindex}}></div>
|
@ -32,7 +32,7 @@
|
||||
</button>
|
||||
</section>
|
||||
</header>
|
||||
<div class="gh-editor-container">
|
||||
<div class="gh-editor-container needsclick">
|
||||
<div class="gh-editor-inner">
|
||||
{{gh-editor-title
|
||||
val=(readonly model.titleScratch)
|
||||
@ -43,6 +43,7 @@
|
||||
update=(action (perform updateTitle))
|
||||
id="gh-editor-title"
|
||||
koenigEditor=(readonly editor)
|
||||
editorMenuIsOpen=editorMenuIsOpen
|
||||
}}
|
||||
{{#if scheduleCountdown}}
|
||||
<time datetime="{{post.publishedAtUTC}}" class="gh-notification gh-notification-schedule">
|
||||
@ -60,6 +61,8 @@
|
||||
tabindex="2"
|
||||
containerSelector=".gh-editor-container"
|
||||
setEditor=(action "setEditor")
|
||||
menuIsOpen=(action "editorMenuIsOpen")
|
||||
menuIsClosed=(action "editorMenuIsClosed")
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,7 +3,6 @@
|
||||
"dependencies": {
|
||||
"devicejs": "0.2.7",
|
||||
"Faker": "3.1.0",
|
||||
"fastclick": "1.0.6",
|
||||
"google-caja": "6005.0.0",
|
||||
"jquery-file-upload": "9.12.3",
|
||||
"jquery-ui": "1.11.4",
|
||||
|
@ -3,6 +3,7 @@ export default {
|
||||
label: 'Divider',
|
||||
icon: '',
|
||||
genus: 'ember',
|
||||
launchMode: 'preview',
|
||||
buttons: {
|
||||
}
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ export default {
|
||||
label: 'Embed',
|
||||
icon: '',
|
||||
genus: 'ember',
|
||||
launchMode: 'preview',
|
||||
buttons: {
|
||||
edit: true
|
||||
}
|
||||
|
@ -2,5 +2,6 @@ export default {
|
||||
name: 'card-image',
|
||||
label: 'Image',
|
||||
icon: '',
|
||||
genus: 'ember'
|
||||
genus: 'ember',
|
||||
launchMode: 'preview'
|
||||
};
|
||||
|
@ -3,5 +3,6 @@ export default {
|
||||
label: 'Markdown',
|
||||
icon: '',
|
||||
genus: 'ember',
|
||||
launchMode: 'edit',
|
||||
buttons: {edit: true}
|
||||
};
|
||||
|
@ -24,9 +24,6 @@ export default Component.extend({
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
let payload = this.get('payload');
|
||||
this.isEditing = !payload.hasOwnProperty('html');
|
||||
this.isEditing = true;
|
||||
},
|
||||
actions: {
|
||||
selectCard() {
|
||||
|
@ -15,7 +15,7 @@ export const BLANK_DOC = {
|
||||
atoms: [],
|
||||
markups: [],
|
||||
cards: [],
|
||||
sections: []
|
||||
sections: [[1, 'p', [[0, [], 0, '']]]]
|
||||
};
|
||||
|
||||
export default Component.extend({
|
||||
@ -27,7 +27,6 @@ export default Component.extend({
|
||||
keyDownHandler: [],
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
let mobiledoc = this.get('value') || BLANK_DOC;
|
||||
let userCards = this.get('cards') || [];
|
||||
|
||||
@ -62,6 +61,12 @@ export default Component.extend({
|
||||
};
|
||||
|
||||
this.set('editor', new Mobiledoc.Editor(options));
|
||||
|
||||
// we use css media width for most things but need to know if a device is touch
|
||||
// to place the toolbar. Above the selected content on a mobile browser is the
|
||||
// cut | copy | paste menu so we need to place our toolbar below.
|
||||
this.set('isTouch', 'ontouchstart' in document.documentElement);
|
||||
|
||||
run.next(() => {
|
||||
if (this.get('setEditor')) {
|
||||
this.sendAction('setEditor', this.get('editor'));
|
||||
@ -96,15 +101,18 @@ export default Component.extend({
|
||||
if (this._rendered) {
|
||||
return;
|
||||
}
|
||||
let [editorDom] = this.$('.surface');
|
||||
let editor = this.get('editor');
|
||||
let $editor = this.$('.surface');
|
||||
let [domContainer] = $editor.parents(this.get('containerSelector'));
|
||||
let [editorDom] = $editor;
|
||||
editorDom.tabindex = this.get('tabindex');
|
||||
this.set('domContainer', domContainer);
|
||||
|
||||
this.domContainer = editorDom.parentNode.parentNode.parentNode.parentNode; // nasty nasty nasty.
|
||||
this.editor.render(editorDom);
|
||||
this._rendered = true;
|
||||
editor.render(editorDom);
|
||||
this.set('_rendered', true);
|
||||
|
||||
window.editor = this.editor;
|
||||
defaultCommands(this.editor); // initialise the custom text handlers for MD, etc.
|
||||
window.editor = editor;
|
||||
defaultCommands(editor); // initialise the custom text handlers for MD, etc.
|
||||
// shouldFocusEditor is only true when transitioning from new to edit, otherwise it's false or undefined.
|
||||
// therefore, if it's true it's after the first lot of content is entered and we expect the caret to be at the
|
||||
// end of the document.
|
||||
@ -115,10 +123,10 @@ export default Component.extend({
|
||||
let sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
this.editor._ensureFocus();
|
||||
editor._ensureFocus();
|
||||
}
|
||||
|
||||
this.editor.cursorDidChange(() => this.cursorMoved());
|
||||
editor.cursorDidChange(() => this.cursorMoved());
|
||||
|
||||
// listen to keydown events outside of the editor, used to handle keydown events in the cards.
|
||||
document.onkeydown = (event) => {
|
||||
@ -158,6 +166,15 @@ export default Component.extend({
|
||||
}
|
||||
let range = selection.getRangeAt(0); // get the actual range within the DOM.
|
||||
let position = range.getBoundingClientRect();
|
||||
if (position.left === 0 && position.top === 0) {
|
||||
// in safari if the range is collapsed you can't get it's location.
|
||||
// this is a bug as it's against the spec.
|
||||
if (editor.range.section) {
|
||||
position = editor.range.head.section.renderNode.element.getBoundingClientRect();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let windowHeight = window.innerHeight;
|
||||
|
||||
if (position.bottom > windowHeight) {
|
||||
@ -170,6 +187,12 @@ export default Component.extend({
|
||||
let id = $(editor.range.headSection.renderNode.element).find('.kg-card > div').attr('id');
|
||||
// let id = card.find('div').attr('id');
|
||||
window.getSelection().removeAllRanges();
|
||||
// if the element is first and we create a card with the '/' menu then the cursor moves before
|
||||
// element is placed in the dom properly. So we figure it out another way.
|
||||
if (!id) {
|
||||
id = editor.range.headSection.renderNode.element.children[0].children[0].id;
|
||||
}
|
||||
|
||||
this.send('selectCardHard', id);
|
||||
} else {
|
||||
this.send('deselectCard');
|
||||
@ -190,20 +213,28 @@ export default Component.extend({
|
||||
// keyboard events.
|
||||
// used when the content of the card is selected and it is editing.
|
||||
selectCard(cardId) {
|
||||
if (!cardId) {
|
||||
throw new Error('A selection must include a cardId');
|
||||
}
|
||||
|
||||
let card = this.get('emberCards').find((card) => card.id === cardId);
|
||||
let cardHolder = $(`#${cardId}`).parent('.kg-card');
|
||||
if (this.get('selectedCard') !== card) {
|
||||
let selectedCard = this.get('selectedCard');
|
||||
if (selectedCard && selectedCard !== card) {
|
||||
this.send('deselectCard');
|
||||
}
|
||||
// defer rendering until after the card is placed in the mobiledoc via the wormhole
|
||||
if (!cardHolder[0]) {
|
||||
run.schedule('afterRender', this, () => this.send('selectCard', cardId));
|
||||
return;
|
||||
}
|
||||
cardHolder.addClass('selected');
|
||||
cardHolder.removeClass('selected-hard');
|
||||
this.set('selectedCard', card);
|
||||
this.get('keyDownHandler').length = 0;
|
||||
// cardHolder.focus();
|
||||
document.onclick = (event) => {
|
||||
let target = $(event.target);
|
||||
let parent = target.parents('.kg-card');
|
||||
if (!target.hasClass('kg-card') && !target.hasClass('kg-card-button') && !target.hasClass('kg-card-button-text') && (!parent.length || parent[0] !== cardHolder[0])) {
|
||||
if (checkIfClickEventShouldCloseCard($(event.target), cardHolder)) {
|
||||
this.send('deselectCard');
|
||||
}
|
||||
};
|
||||
@ -212,20 +243,27 @@ export default Component.extend({
|
||||
// creating blocks under the card and deleting the card.
|
||||
// used when selecting the card with the keyboard or clicking on the toolbar.
|
||||
selectCardHard(cardId) {
|
||||
if (!cardId) {
|
||||
throw new Error('A selection must include a cardId');
|
||||
}
|
||||
|
||||
let card = this.get('emberCards').find((card) => card.id === cardId);
|
||||
let cardHolder = $(`#${cardId}`).parents('.kg-card');
|
||||
if (this.get('selectedCard') !== card) {
|
||||
let selectedCard = this.get('selectedCard');
|
||||
if (selectedCard && selectedCard !== card) {
|
||||
this.send('deselectCard');
|
||||
}
|
||||
// defer rendering until after the card is placed in the mobiledoc via the wormhole
|
||||
if (!cardHolder[0]) {
|
||||
run.schedule('afterRender', this, () => this.send('selectCardHard', cardId));
|
||||
return;
|
||||
}
|
||||
cardHolder.addClass('selected');
|
||||
cardHolder.addClass('selected-hard');
|
||||
this.set('selectedCard', card);
|
||||
|
||||
document.onclick = (event) => {
|
||||
let target = $(event.target);
|
||||
let parent = target.parents('.kg-card');
|
||||
|
||||
if (!target.hasClass('kg-card') && !target.hasClass('kg-card-button') && !target.hasClass('kg-card-button-text') && (!parent.length || parent[0] !== cardHolder[0])) {
|
||||
if (checkIfClickEventShouldCloseCard($(event.target), cardHolder)) {
|
||||
this.send('deselectCard');
|
||||
}
|
||||
};
|
||||
@ -239,11 +277,18 @@ export default Component.extend({
|
||||
editor.post.sections.forEach((section) => {
|
||||
let currentCard = $(section.renderNode.element);
|
||||
if (currentCard.find(`#${cardId}`).length) {
|
||||
if (section.prev) {
|
||||
if (section.prev && section.prev.isCardSection) {
|
||||
let prevCard = ($(section.prev.renderNode.element).find('.gh-card-holder').attr('id'));
|
||||
if (prevCard) {
|
||||
this.send('selectCardHard', prevCard);
|
||||
}
|
||||
} else if (section.prev) {
|
||||
let range = section.prev.toRange();
|
||||
range.tail.offset = 0;
|
||||
editor.selectRange(range);
|
||||
return;
|
||||
} else {
|
||||
$(this.titleQuery).focus();
|
||||
this.send('deselectCard');
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -253,11 +298,18 @@ export default Component.extend({
|
||||
editor.post.sections.forEach((section) => {
|
||||
let currentCard = $(section.renderNode.element);
|
||||
if (currentCard.find(`#${cardId}`).length) {
|
||||
if (section.next) {
|
||||
if (section.next && section.next.isCardSection) {
|
||||
let nextCard = ($(section.next.renderNode.element).find('.gh-card-holder').attr('id'));
|
||||
if (nextCard) {
|
||||
this.send('selectCardHard', nextCard);
|
||||
}
|
||||
} else if (section.next) {
|
||||
let range = section.next.toRange();
|
||||
range.tail.offset = 0;
|
||||
editor.selectRange(range);
|
||||
return;
|
||||
} else {
|
||||
$(this.titleQuery).focus();
|
||||
this.send('deselectCard');
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -282,6 +334,7 @@ export default Component.extend({
|
||||
|
||||
}
|
||||
}
|
||||
this.send('deselectCard');
|
||||
});
|
||||
|
||||
return false;
|
||||
@ -315,11 +368,32 @@ export default Component.extend({
|
||||
},
|
||||
stopEditingCard() {
|
||||
this.set('editedCard', null);
|
||||
},
|
||||
menuIsOpen() {
|
||||
this.sendAction('menuIsOpen');
|
||||
},
|
||||
menuIsClosed() {
|
||||
this.sendAction('menuIsClosed');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// takes two jquery objects, the target of a click event and a cardholder and sees if the click event should close the card or not
|
||||
function checkIfClickEventShouldCloseCard(target, cardHolder) {
|
||||
// see if this element or one of its ancestors is a card.
|
||||
let card = target.hasClass('kg-card') ? target : target.parents('.kg-card');
|
||||
|
||||
let isCardToggle = target.hasClass('kg-card-button') || target.parents('.gh-cardmenu').length || target.parents('.kg-card-toolbar').length;
|
||||
|
||||
// if we're selecting a card toggle (menu item) OR we have clicked on a card and the card is the one we expect//
|
||||
// then we shouldn't close the menu and return false.
|
||||
if (isCardToggle || (card.length && card[0] === cardHolder[0])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// // code for moving the cursor into the correct position of the title: (is buggy)
|
||||
|
||||
// // find the cursor position based on a pixel offset of an element.
|
||||
@ -334,7 +408,7 @@ export default Component.extend({
|
||||
// if (rect.top === rect.bottom) {
|
||||
// continue;
|
||||
// }
|
||||
// if(rect.left <= horizontal_offset && rect.right >= horizontal_offset) {
|
||||
// if (rect.left <= horizontal_offset && rect.right >= horizontal_offset) {
|
||||
// return i + (horizontal_offset >= (rect.left + rect.right) / 2 ? 1 : 0); // if the horizontal_offset is on the left hand side of the
|
||||
// // character then return `i`, if it's on the right return `i + 1`
|
||||
// }
|
||||
|
@ -5,6 +5,7 @@ import observer from 'ember-metal/observer';
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
classNameBindings: ['isEditing'],
|
||||
editing: observer('editedCard', function () {
|
||||
let editing = this.get('editedCard') === this.get('card');
|
||||
if (this.get('isEditing') && !editing) {
|
||||
@ -14,6 +15,19 @@ export default Component.extend({
|
||||
}),
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
let card = this.get('card');
|
||||
if (card.newlyCreated) {
|
||||
run.schedule('afterRender', this, () => {
|
||||
if (card.card.launchMode === 'edit') {
|
||||
this.send('startEdit');
|
||||
this.send('selectCard');
|
||||
} else {
|
||||
this.send('selectCardHard');
|
||||
}
|
||||
});
|
||||
this.set('isNew', true);
|
||||
card.newlyCreated = false;
|
||||
}
|
||||
},
|
||||
didRender() {
|
||||
// add the classname to the wrapping card as generated by mobiledoc.
|
||||
@ -26,9 +40,12 @@ export default Component.extend({
|
||||
|
||||
// the mobiledoc generated container.
|
||||
let mobiledocCard = this.$().parents('.__mobiledoc-card');
|
||||
|
||||
mobiledocCard.removeClass('__mobiledoc-card');
|
||||
mobiledocCard.addClass('kg-card');
|
||||
if (this.get('isNew')) {
|
||||
mobiledocCard.hide();
|
||||
mobiledocCard.fadeIn();
|
||||
}
|
||||
mobiledocCard.addClass(name ? `kg-${name}` : '');
|
||||
|
||||
mobiledocCard.attr('tabindex', 4);
|
||||
@ -37,6 +54,7 @@ export default Component.extend({
|
||||
this.send('selectCardHard');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
},
|
||||
@ -58,6 +76,11 @@ export default Component.extend({
|
||||
deselectCard() {
|
||||
this.sendAction('deselectCard', this.card.id);
|
||||
this.send('stopEdit');
|
||||
if (this.get('isNew')) {
|
||||
let mobiledocCard = this.$().parents('.kg-card');
|
||||
mobiledocCard.removeClass('new');
|
||||
this.set('isNew', false);
|
||||
}
|
||||
},
|
||||
selectCardHard() {
|
||||
this.sendAction('selectCardHard', this.card.id);
|
||||
|
@ -80,6 +80,7 @@ export default Component.extend({
|
||||
});
|
||||
|
||||
editor.cursorDidChange(() => {
|
||||
|
||||
if (!editor.range || !editor.range.head.section) {
|
||||
return;
|
||||
}
|
||||
@ -95,6 +96,14 @@ export default Component.extend({
|
||||
let editorOffset = $editor.offset();
|
||||
|
||||
this.set('isButton', true);
|
||||
|
||||
// we store the range for the current paragraph as we can lose it later.
|
||||
this.set('range', {
|
||||
section: editor.range.head.section,
|
||||
startOffset: editor.range.head.offset,
|
||||
endOffset: editor.range.head.offset
|
||||
});
|
||||
|
||||
run.schedule('afterRender', this,
|
||||
() => {
|
||||
let button = this.$('.gh-cardmenu-button');
|
||||
@ -109,16 +118,10 @@ export default Component.extend({
|
||||
},
|
||||
actions: {
|
||||
openMenu: function () { // eslint-disable-line
|
||||
let button = this.$('.gh-cardmenu-button');
|
||||
let editor = this.get('editor');
|
||||
let button = this.$('.gh-cardmenu-button'); // the ⊕ button.
|
||||
let $editor = $(this.get('containerSelector'));
|
||||
this.set('isOpen', true);
|
||||
|
||||
this.set('range', {
|
||||
section: editor.range.head.section,
|
||||
startOffset: editor.range.head.offset,
|
||||
endOffset: editor.range.head.offset
|
||||
});
|
||||
|
||||
this.set('selected', -1);
|
||||
this.set('selectedTool', null);
|
||||
|
||||
@ -127,14 +130,28 @@ export default Component.extend({
|
||||
run.schedule('afterRender', this,
|
||||
() => {
|
||||
let menu = this.$('.gh-cardmenu');
|
||||
menu.css('top', button.css('top'));
|
||||
let top = parseInt(button.css('top').replace('px', ''));
|
||||
|
||||
// calculate the parts of the menu that are hidden by the overflow.
|
||||
let hiddenByOverflow = ($editor.innerHeight() + $editor.scrollTop()) - (menu.height() + top);
|
||||
if (hiddenByOverflow < 0) {
|
||||
top = top + hiddenByOverflow - 30;
|
||||
}
|
||||
|
||||
menu.css('top', top);
|
||||
menu.css('left', button.css('left') + button.width());
|
||||
this.$('.gh-cardmenu-search-input').focus();
|
||||
menu.hide().fadeIn('fast', () => {
|
||||
this.$('.gh-cardmenu-search-input').focus();
|
||||
});
|
||||
});
|
||||
this.sendAction('menuIsOpen');
|
||||
},
|
||||
closeMenu: function () { // eslint-disable-line
|
||||
this.set('isOpen', false);
|
||||
this.set('isButton', false);
|
||||
this.$('.gh-cardmenu').fadeOut('fast', () => {
|
||||
this.set('isOpen', false);
|
||||
});
|
||||
this.sendAction('menuIsClosed');
|
||||
},
|
||||
closeMenuKeepButton: function () { // eslint-disable-line
|
||||
this.set('isOpen', false);
|
||||
|
@ -202,17 +202,36 @@ export default Component.extend({
|
||||
|
||||
let position = range.getBoundingClientRect();
|
||||
let edOffset = $editor.offset();
|
||||
|
||||
if (position.left === 0 && position.top === 0) {
|
||||
// in safari if the range is collapsed you can't get it's location.
|
||||
// this is a bug as it's against the spec.
|
||||
position = editor.range.head.section.renderNode.element.getBoundingClientRect();
|
||||
}
|
||||
run.schedule('afterRender', this,
|
||||
() => {
|
||||
let menu = this.$('.gh-cardmenu');
|
||||
menu.css('top', position.top + $editor.scrollTop() - edOffset.top + 20);
|
||||
menu.css('left', position.left + (position.width / 2) + $editor.scrollLeft() - edOffset.left);
|
||||
this.$('.gh-cardmenu-search-input').focus();
|
||||
let top = position.top + $editor.scrollTop() - edOffset.top + 20;
|
||||
let left = position.left + (position.width / 2) + $editor.scrollLeft() - edOffset.left;
|
||||
// calculate if parts of the menu that are hidden by the overflow.
|
||||
let hiddenByOverflowY = ($editor.innerHeight() + $editor.scrollTop()) - (menu.height() + top);
|
||||
// if the menu is off the bottom of the screen then place it above the cursor
|
||||
if (hiddenByOverflowY < 0) {
|
||||
menu.css('margin-top', -(menu.outerHeight() + 20));
|
||||
}
|
||||
let hiddenByOverflowX = ($editor.innerWidth() + $editor.scrollLeft()) - (menu.width() + left);
|
||||
// if the menu is off the bottom of the screen then place it above the cursor
|
||||
if (hiddenByOverflowX < 0) {
|
||||
menu.css('margin-left', -(menu.outerWidth() + 20));
|
||||
}
|
||||
|
||||
menu.css('top', top);
|
||||
menu.css('left', left);
|
||||
menu.hide().fadeIn('fast');
|
||||
});
|
||||
|
||||
this.sendAction('menuIsOpen');
|
||||
},
|
||||
closeMenu: function () { // eslint-disable-line
|
||||
this.set('isOpen', false);
|
||||
let editor = this.get('editor');
|
||||
// this.get('editor').unregisterKeyCommand('slash'); -- waiting for the next release for this
|
||||
|
||||
@ -222,6 +241,11 @@ export default Component.extend({
|
||||
editor._keyCommands.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.$('.gh-cardmenu').fadeOut('fast', () => {
|
||||
this.set('isOpen', false);
|
||||
});
|
||||
this.sendAction('menuIsClosed');
|
||||
},
|
||||
clickedMenu: function () { // eslint-disable-line
|
||||
// let{section, startOffset, endOffset} = this.get('range');
|
||||
|
@ -9,7 +9,7 @@ import Tools from '../options/default-tools';
|
||||
export default Component.extend({
|
||||
layout,
|
||||
classNames: ['gh-toolbar'],
|
||||
classNameBindings: ['isVisible', 'isLink'],
|
||||
classNameBindings: ['isVisible', 'isLink', 'tickFullLeft', 'tickHalfLeft', 'tickFullRight', 'tickHalfRight', 'tickAbove'],
|
||||
isVisible: false,
|
||||
tools: [],
|
||||
hasRendered: false,
|
||||
@ -150,8 +150,10 @@ function updateToolbarToRange(self, $holder, $editor, isMouseDown) {
|
||||
self.propertyWillChange('toolbar');
|
||||
self.propertyWillChange('toolbarBlocks');
|
||||
|
||||
if (!editor.range.isCollapsed) {
|
||||
// if we have a selection, then the toolbar appears just below said selection:
|
||||
// if we have a selection, then the toolbar appears just above said selection:
|
||||
// unless it's a selection around a single card (firefox bug)
|
||||
if (!editor.range.isCollapsed
|
||||
&& !(editor.range.head.section.isCardSection && editor.range.head.section === editor.range.tail.section)) {
|
||||
|
||||
let range = window.getSelection().getRangeAt(0); // get the actual range within the DOM.
|
||||
let position = range.getBoundingClientRect();
|
||||
@ -160,8 +162,53 @@ function updateToolbarToRange(self, $holder, $editor, isMouseDown) {
|
||||
self.set('isVisible', true);
|
||||
run.schedule('afterRender', this,
|
||||
() => {
|
||||
$holder.css('top', position.top + $editor.scrollTop() - $holder.height() - 20); // - edOffset.top+10
|
||||
$holder.css('left', position.left + (position.width / 2) + $editor.scrollLeft() - edOffset.left - ($holder.width() / 2));
|
||||
let width = $holder.width();
|
||||
let height = $holder.height();
|
||||
let top = position.top + $editor.scrollTop() - $holder.height() - 20;
|
||||
let left = position.left + (position.width / 2) + $editor.scrollLeft() - edOffset.left - (width / 2);
|
||||
let right = left + width;
|
||||
let edWidth = $editor[0].scrollWidth;
|
||||
if (left < 0) {
|
||||
if (Math.round(left / (width / 4)) === -1) {
|
||||
self.set('tickFullLeft', true);
|
||||
self.set('tickHalfLeft', false);
|
||||
self.set('tickFullRight', false);
|
||||
self.set('tickHalfRight', false);
|
||||
} else {
|
||||
self.set('tickFullLeft', false);
|
||||
self.set('tickHalfLeft', true);
|
||||
self.set('tickFullRight', false);
|
||||
self.set('tickHalfRight', false);
|
||||
}
|
||||
left = 0;
|
||||
} else if (right > edWidth) {
|
||||
if (Math.round((edWidth - right) / (width / 4)) === -1) {
|
||||
self.set('tickFullLeft', false);
|
||||
self.set('tickHalfLeft', false);
|
||||
self.set('tickFullRight', true);
|
||||
self.set('tickHalfRight', false);
|
||||
} else {
|
||||
self.set('tickFullLeft', false);
|
||||
self.set('tickHalfLeft', false);
|
||||
self.set('tickFullRight', false);
|
||||
self.set('tickHalfRight', true);
|
||||
}
|
||||
left = left + (edWidth - right);
|
||||
} else {
|
||||
self.set('tickFullLeft', false);
|
||||
self.set('tickHalfLeft', false);
|
||||
self.set('tickFullRight', false);
|
||||
self.set('tickHalfRight', false);
|
||||
}
|
||||
|
||||
if (self.get('isTouch') || top - $editor.scrollTop() < 0) {
|
||||
top = top + height + 60;
|
||||
self.set('tickAbove', true);
|
||||
} else {
|
||||
self.set('tickAbove', false);
|
||||
}
|
||||
$holder.css('top', top);
|
||||
$holder.css('left', left);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -34,6 +34,7 @@ export default function createCardFactory(toolbar) {
|
||||
let card = setupEmberCard({env, options, payload}, 'render');
|
||||
let div = document.createElement('div');
|
||||
div.id = card.id;
|
||||
div.className = 'gh-card-holder';
|
||||
return div;
|
||||
}
|
||||
return cardObject.willRender({env, options, payload});
|
||||
@ -70,7 +71,9 @@ export default function createCardFactory(toolbar) {
|
||||
if (payload.newlyCreated) {
|
||||
newlyCreated = true;
|
||||
delete payload.newlyCreated;
|
||||
env.save(payload, false);
|
||||
}
|
||||
|
||||
let card = EmberObject.create({
|
||||
id,
|
||||
env,
|
||||
|
@ -200,7 +200,7 @@ export default function (editor, toolbar) {
|
||||
onClick: (editor) => {
|
||||
editor.run((postEditor, section) => {
|
||||
let thisSection = section || editor.range.headSection;
|
||||
let card = postEditor.builder.createCardSection('card-image', {pos: 'top'});
|
||||
let card = postEditor.builder.createCardSection('card-image', {pos: 'top', newlyCreated: true});
|
||||
if (thisSection.text.length) {
|
||||
postEditor.insertSection(card);
|
||||
} else {
|
||||
@ -229,7 +229,7 @@ export default function (editor, toolbar) {
|
||||
onClick: (editor, section) => {
|
||||
editor.run((postEditor) => {
|
||||
let thisSection = section || editor.range.headSection;
|
||||
let card = postEditor.builder.createCardSection('card-html', {pos: 'top', html: thisSection.text});
|
||||
let card = postEditor.builder.createCardSection('card-html', {pos: 'top', html: thisSection.text, newlyCreated: true});
|
||||
// we can't replace a list item so we insert a card after it and then delete it.
|
||||
if (thisSection.isListItem) {
|
||||
editor.insertCard('card-html');
|
||||
@ -259,8 +259,12 @@ export default function (editor, toolbar) {
|
||||
onClick: (editor, section) => {
|
||||
editor.run((postEditor) => {
|
||||
let thisSection = section || editor.range.headSection;
|
||||
let card = postEditor.builder.createCardSection('card-hr', {pos: 'top'});
|
||||
postEditor.insertSection(card);
|
||||
let card = postEditor.builder.createCardSection('card-hr', {pos: 'top', newlyCreated: true});
|
||||
if (thisSection.text.length) {
|
||||
postEditor.insertSection(card);
|
||||
} else {
|
||||
postEditor.replaceSection(thisSection, card);
|
||||
}
|
||||
|
||||
if (!thisSection.next) {
|
||||
let newSection = editor.builder.createMarkupSection('p');
|
||||
|
@ -1,14 +1,43 @@
|
||||
{{#each emberCards as |card index|}}
|
||||
{{#ember-wormhole to=card.id}}
|
||||
{{koenig-card tabindex=index card=card apiRoot=apiRoot assetPath=assetPath selectCard=(action "selectCard") selectCardHard=(action "selectCardHard") deselectCard=(action "deselectCard") edit=(action "editCard") stopEdit=(action "stopEditingCard") editedCard=editedCard}}
|
||||
{{koenig-card
|
||||
tabindex=index
|
||||
card=card
|
||||
apiRoot=apiRoot
|
||||
assetPath=assetPath
|
||||
selectCard=(action "selectCard")
|
||||
selectCardHard=(action "selectCardHard")
|
||||
deselectCard=(action "deselectCard")
|
||||
edit=(action "editCard")
|
||||
stopEdit=(action "stopEditingCard")
|
||||
editedCard=editedCard}}
|
||||
{{/ember-wormhole}}
|
||||
{{/each}}
|
||||
<div class='gh-koenig'>
|
||||
<div class='surface' tabindex="{{tabindex}}"/>
|
||||
<div class='surface needsclick' tabindex="{{tabindex}}"/>
|
||||
</div>
|
||||
|
||||
{{yield}}
|
||||
|
||||
{{koenig-toolbar editor=editor assetPath=assetPath containerSelector=containerSelector}}
|
||||
{{koenig-slash-menu editor=editor assetPath=assetPath containerSelector=containerSelector}}
|
||||
{{koenig-plus-menu editor=editor assetPath=assetPath containerSelector=containerSelector}}
|
||||
{{koenig-toolbar
|
||||
editor=editor
|
||||
assetPath=assetPath
|
||||
containerSelector=containerSelector
|
||||
isTouch=isTouch
|
||||
}}
|
||||
{{koenig-slash-menu
|
||||
editor=editor
|
||||
assetPath=assetPath
|
||||
containerSelector=containerSelector
|
||||
isTouch=isTouch
|
||||
menuIsOpen=(action "menuIsOpen")
|
||||
menuIsClosed=(action "menuIsClosed")
|
||||
}}
|
||||
{{koenig-plus-menu
|
||||
editor=editor
|
||||
assetPath=assetPath
|
||||
containerSelector=containerSelector
|
||||
isTouch=isTouch
|
||||
menuIsOpen=(action "menuIsOpen")
|
||||
menuIsClosed=(action "menuIsClosed")
|
||||
}}
|
@ -19,12 +19,12 @@
|
||||
{{!--<button {{action "stopEdit"}} class='kg-card-button-text'>
|
||||
Cancel
|
||||
</button>--}}
|
||||
<button {{action "stopEdit"}} class='kg-card-button-save'>
|
||||
<button {{action "stopEdit"}} class='kg-card-button kg-card-button-save'>
|
||||
Done
|
||||
</button>
|
||||
{{else}}
|
||||
{{#if card.card.buttons.edit}}
|
||||
<button {{action "startEdit"}} class='kg-card-button-text'>
|
||||
<button {{action "startEdit"}} class='kg-card-button kg-card-button-text'>
|
||||
Edit
|
||||
</button>
|
||||
{{/if}}
|
||||
|
@ -49,7 +49,6 @@
|
||||
"ember-cli-code-coverage": "0.3.11",
|
||||
"ember-cli-dependency-checker": "1.3.0",
|
||||
"ember-cli-eslint": "3.0.3",
|
||||
"ember-cli-fastclick": "1.3.0",
|
||||
"ember-cli-htmlbars": "1.2.0",
|
||||
"ember-cli-htmlbars-inline-precompile": "0.3.6",
|
||||
"ember-cli-inject-live-reload": "1.6.1",
|
||||
|
@ -59,6 +59,17 @@ export function testInput(input, output, expect) {
|
||||
});
|
||||
}
|
||||
|
||||
export function testInputTimeout(input) {
|
||||
window.editor.element.focus();
|
||||
return Ember.Test.promise(function (resolve, reject) { // eslint-disable-line
|
||||
window.setTimeout(() => {
|
||||
resolve(window.editor.element.innerHTML);
|
||||
}, 300);
|
||||
|
||||
inputText(window.editor, input);
|
||||
});
|
||||
}
|
||||
|
||||
export function waitForRender(selector) {
|
||||
let isRejected = false;
|
||||
return Ember.Test.promise(function (resolve, reject) { // eslint-disable-line
|
||||
@ -78,3 +89,11 @@ export function waitForRender(selector) {
|
||||
checkIsRendered();
|
||||
});
|
||||
}
|
||||
|
||||
export function timeoutPromise(timeout) {
|
||||
return Ember.Test.promise(function (resolve) { // eslint-disable-line
|
||||
window.setTimeout(() => {
|
||||
resolve();
|
||||
}, timeout);
|
||||
});
|
||||
}
|
@ -3,19 +3,29 @@ import {expect} from 'chai';
|
||||
import {describe, it} from 'mocha';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {editorRendered, testInput} from '../../helpers/editor-helpers';
|
||||
import {editorRendered, testInput, testInputTimeout} from '../../helpers/editor-helpers';
|
||||
|
||||
describe('Integration: Component: gh-koenig', function () {
|
||||
setupComponentTest('gh-koenig', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
this.set('value', {
|
||||
version: '0.3.1',
|
||||
atoms: [],
|
||||
markups: [],
|
||||
cards: [],
|
||||
sections: []});
|
||||
});
|
||||
|
||||
describe('Makerable markdown support.', function() {
|
||||
it('plain text inputs (placebo)', function (done) {
|
||||
this.render(hbs`{{gh-koenig
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -35,6 +45,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -53,6 +64,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -71,6 +83,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -89,6 +102,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -108,6 +122,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -126,6 +141,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -144,6 +160,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -162,6 +179,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -181,6 +199,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -198,6 +217,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -217,6 +237,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -235,6 +256,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -256,6 +278,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -274,6 +297,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -292,6 +316,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -311,6 +336,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -329,6 +355,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -347,6 +374,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -366,6 +394,7 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -380,21 +409,23 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('Card markdown support.', function () {
|
||||
describe('Card markdown support.', function () {
|
||||
it('![]() creates an image card', function (done) {
|
||||
this.render(hbs`{{gh-koenig
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
.then(() => {
|
||||
let {editor} = window;
|
||||
editor.element.focus();
|
||||
return testInput('![image of something](https://unsplash.it/200/300/?random) ', '...', expect);
|
||||
return testInputTimeout('![image of something](https://unsplash.it/200/300/?random)');
|
||||
})
|
||||
.then(() => {
|
||||
.then((value) => {
|
||||
expect(value).to.have.string('kg-card-image');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -403,15 +434,17 @@ describe('Integration: Component: gh-koenig', function () {
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
.then(() => {
|
||||
let {editor} = window;
|
||||
editor.element.focus();
|
||||
return testInput('```some code``` ', '...', expect);
|
||||
return testInputTimeout('```some code```');
|
||||
})
|
||||
.then(() => {
|
||||
.then((value) => {
|
||||
expect(value).to.have.string('kg-card-markdown');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -3,19 +3,28 @@ import {expect} from 'chai';
|
||||
import {describe, it} from 'mocha';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {editorRendered, testInput, waitForRender, inputText} from '../../helpers/editor-helpers';
|
||||
import {editorRendered, waitForRender, inputText, timeoutPromise} from '../../helpers/editor-helpers';
|
||||
import $ from 'jquery';
|
||||
|
||||
describe('Integration: Component: gh-koenig-slashmenu', function () {
|
||||
setupComponentTest('gh-koenig', {
|
||||
integration: true
|
||||
});
|
||||
beforeEach(function () {
|
||||
this.set('value', {
|
||||
version: '0.3.1',
|
||||
atoms: [],
|
||||
markups: [],
|
||||
cards: [],
|
||||
sections: []});
|
||||
});
|
||||
|
||||
it('the slash menu appears on user input', function (done) {
|
||||
this.render(hbs`{{gh-koenig
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -31,11 +40,13 @@ describe('Integration: Component: gh-koenig-slashmenu', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it.skip('searches when a user types', function (done) {
|
||||
|
||||
it('searches when a user types', function (done) {
|
||||
this.render(hbs`{{gh-koenig
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
@ -46,14 +57,56 @@ describe('Integration: Component: gh-koenig-slashmenu', function () {
|
||||
return waitForRender('.gh-cardmenu');
|
||||
})
|
||||
.then(() => {
|
||||
let {editor} = window;
|
||||
let cardMenu = $('.gh-cardmenu');
|
||||
expect(cardMenu.children().length).to.equal(7);
|
||||
return testInput(' lis', '/ lis', expect);
|
||||
inputText(editor, ' bul');
|
||||
return timeoutPromise(500);
|
||||
})
|
||||
.then(() => {
|
||||
let cardMenu = $('.gh-cardmenu');
|
||||
expect(cardMenu.children().length).to.equal(2);
|
||||
expect(cardMenu.children().length).to.equal(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('ul tool', function (done) {
|
||||
this.set('editorMenuIsOpen', function () {});
|
||||
this.set('editorMenuIsClosed', function () {});
|
||||
|
||||
this.render(hbs`{{gh-koenig
|
||||
apiRoot='/todo'
|
||||
assetPath='/assets'
|
||||
containerSelector='.editor-holder'
|
||||
value=value
|
||||
menuIsOpen=editorMenuIsOpen
|
||||
menuIsClosed=editorMenuIsClosed
|
||||
}}`);
|
||||
|
||||
editorRendered()
|
||||
.then(() => {
|
||||
let {editor} = window;
|
||||
editor.element.focus();
|
||||
inputText(editor, '/');
|
||||
return waitForRender('.gh-cardmenu');
|
||||
})
|
||||
.then(() => {
|
||||
let {editor} = window;
|
||||
let cardMenu = $('.gh-cardmenu');
|
||||
expect(cardMenu.children().length).to.equal(7);
|
||||
inputText(editor, ' bul');
|
||||
return timeoutPromise(500);
|
||||
})
|
||||
.then(() => {
|
||||
$('.gh-cardmenu-card').click();
|
||||
done();
|
||||
});
|
||||
// .then(() => {
|
||||
//
|
||||
// })
|
||||
// .then(() => {
|
||||
// console.log(editor.element.innerHTML);
|
||||
// done();
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
@ -2330,15 +2330,6 @@ ember-cli-eslint@3.0.3:
|
||||
rsvp "^3.2.1"
|
||||
walk-sync "^0.3.0"
|
||||
|
||||
ember-cli-fastclick@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-fastclick/-/ember-cli-fastclick-1.3.0.tgz#1ff21c95ca9faebd5c1c0698ff652e7bdf8f5a83"
|
||||
dependencies:
|
||||
broccoli-funnel "^1.0.1"
|
||||
broccoli-merge-trees "^1.1.1"
|
||||
ember-cli-babel "^5.1.6"
|
||||
fastclick "^1.0.6"
|
||||
|
||||
ember-cli-get-component-path-option@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-get-component-path-option/-/ember-cli-get-component-path-option-1.0.0.tgz#0d7b595559e2f9050abed804f1d8eff1b08bc771"
|
||||
@ -2437,7 +2428,7 @@ ember-cli-mocha@0.13.2:
|
||||
mocha "^2.5.3"
|
||||
resolve "^1.1.7"
|
||||
|
||||
ember-cli-moment-shim@^3.1.0:
|
||||
ember-cli-moment-shim@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-moment-shim/-/ember-cli-moment-shim-3.1.0.tgz#ec6a39a0dcb4badeaf6dcb74a6c05b0bc576f721"
|
||||
dependencies:
|
||||
@ -3542,10 +3533,6 @@ fast-sourcemap-concat@^1.0.1:
|
||||
source-map "^0.4.2"
|
||||
source-map-url "^0.3.0"
|
||||
|
||||
fastclick@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/fastclick/-/fastclick-1.0.6.tgz#161625b27b1a5806405936bda9a2c1926d06be6a"
|
||||
|
||||
faye-websocket@~0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
|
||||
|
Loading…
Reference in New Issue
Block a user