mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 14:43:08 +03:00
Koenig - Extract card caption input into component
refs https://github.com/TryGhost/Ghost/issues/9623 - add new `{{koenig-caption-input}}` component - yield koenig-caption-input instance from the `{{koenig-card}}` component - remove duplicated caption logic from image and embed cards - remove placeholder when caption input has focus
This commit is contained in:
parent
231315e2f6
commit
2dc916299b
@ -0,0 +1,110 @@
|
||||
import Component from '@ember/component';
|
||||
import layout from '../templates/components/koenig-caption-input';
|
||||
import {computed} from '@ember/object';
|
||||
import {kgStyle} from 'ember-cli-ghost-spirit/helpers/kg-style';
|
||||
import {run} from '@ember/runloop';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'figcaption',
|
||||
classNameBindings: ['figCaptionClass'],
|
||||
layout,
|
||||
|
||||
caption: '',
|
||||
placeholder: '',
|
||||
|
||||
_keypressHandler: null,
|
||||
_keydownHandler: null,
|
||||
|
||||
update() {},
|
||||
onDidInsertElement() {},
|
||||
|
||||
figCaptionClass: computed(function () {
|
||||
return `${kgStyle(['figcaption'])} w-100`;
|
||||
}),
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.captureInput && !this._keypressHandler) {
|
||||
this._attachHandlers();
|
||||
}
|
||||
|
||||
if (!this.captureInput && this._keypressHandler) {
|
||||
this._detachHandlers();
|
||||
}
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this._detachHandlers();
|
||||
},
|
||||
|
||||
_attachHandlers() {
|
||||
if (!this._keypressHandler) {
|
||||
this._keypressHandler = run.bind(this, this._handleKeypress);
|
||||
window.addEventListener('keypress', this._keypressHandler);
|
||||
}
|
||||
|
||||
if (!this._keydownHandler) {
|
||||
this._keydownHandler = run.bind(this, this._handleKeydown);
|
||||
window.addEventListener('keydown', this._keydownHandler);
|
||||
}
|
||||
},
|
||||
|
||||
_detachHandlers() {
|
||||
window.removeEventListener('keypress', this._keypressHandler);
|
||||
window.removeEventListener('keydown', this._keydownHandler);
|
||||
this._keypressHandler = null;
|
||||
this._keydownHandler = null;
|
||||
},
|
||||
|
||||
// only fires if the card is selected, moves focus to the caption input so
|
||||
// that it's possible to start typing without explicitly focusing the input
|
||||
_handleKeypress(event) {
|
||||
let captionInput = this.element.querySelector('[name="caption"]');
|
||||
|
||||
if (captionInput && captionInput !== document.activeElement) {
|
||||
captionInput.value = `${captionInput.value}${event.key}`;
|
||||
captionInput.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
// this will be fired for keydown events when the caption input is focused,
|
||||
// we look for cursor movements or the enter key to defocus and trigger the
|
||||
// corresponding editor behaviour
|
||||
_handleKeydown(event) {
|
||||
let captionInput = this.element.querySelector('[name="caption"]');
|
||||
|
||||
if (event.target === captionInput) {
|
||||
if (event.key === 'Escape') {
|
||||
captionInput.blur();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
captionInput.blur();
|
||||
this.addParagraphAfterCard();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
let selectionStart = captionInput.selectionStart;
|
||||
let length = captionInput.value.length;
|
||||
|
||||
if ((event.key === 'ArrowUp' || event.key === 'ArrowLeft') && selectionStart === 0) {
|
||||
captionInput.blur();
|
||||
this.moveCursorToPrevSection();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((event.key === 'ArrowDown' || event.key === 'ArrowRight') && selectionStart === length) {
|
||||
captionInput.blur();
|
||||
this.moveCursorToNextSection();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
@ -26,10 +26,10 @@ export default Component.extend({
|
||||
deselectCard() {},
|
||||
editCard() {},
|
||||
saveCard() {},
|
||||
deleteCard() { },
|
||||
moveCursorToNextSection() { },
|
||||
moveCursorToPrevSection() { },
|
||||
addParagraphAfterCard() { },
|
||||
deleteCard() {},
|
||||
moveCursorToNextSection() {},
|
||||
moveCursorToPrevSection() {},
|
||||
addParagraphAfterCard() {},
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
@ -43,19 +43,8 @@ export default Component.extend({
|
||||
this._loadPayloadScript();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this._detachHandlers();
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelect() {
|
||||
this._attachHandlers();
|
||||
},
|
||||
|
||||
onDeselect() {
|
||||
this._detachHandlers();
|
||||
|
||||
if (this.payload.url && !this.payload.html && !this.hasError) {
|
||||
this.convertUrl.perform(this.payload.url);
|
||||
} else {
|
||||
@ -101,74 +90,6 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_attachHandlers() {
|
||||
if (!this._keypressHandler) {
|
||||
this._keypressHandler = run.bind(this, this._handleKeypress);
|
||||
window.addEventListener('keypress', this._keypressHandler);
|
||||
}
|
||||
|
||||
if (!this._keydownHandler) {
|
||||
this._keydownHandler = run.bind(this, this._handleKeydown);
|
||||
window.addEventListener('keydown', this._keydownHandler);
|
||||
}
|
||||
},
|
||||
|
||||
_detachHandlers() {
|
||||
window.removeEventListener('keypress', this._keypressHandler);
|
||||
window.removeEventListener('keydown', this._keydownHandler);
|
||||
this._keypressHandler = null;
|
||||
this._keydownHandler = null;
|
||||
},
|
||||
|
||||
// only fires if the card is selected, moves focus to the caption input so
|
||||
// that it's possible to start typing without explicitly focusing the input
|
||||
_handleKeypress(event) {
|
||||
let captionInput = this.element.querySelector('[name="caption"]');
|
||||
|
||||
if (captionInput && captionInput !== document.activeElement) {
|
||||
captionInput.value = `${captionInput.value}${event.key}`;
|
||||
captionInput.focus();
|
||||
}
|
||||
},
|
||||
|
||||
// this will be fired for keydown events when the caption input is focused,
|
||||
// we look for cursor movements or the enter key to defocus and trigger the
|
||||
// corresponding editor behaviour
|
||||
_handleKeydown(event) {
|
||||
let captionInput = this.element.querySelector('[name="caption"]');
|
||||
|
||||
if (event.target === captionInput) {
|
||||
if (event.key === 'Escape') {
|
||||
captionInput.blur();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
captionInput.blur();
|
||||
this.addParagraphAfterCard();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
let selectionStart = captionInput.selectionStart;
|
||||
let length = captionInput.value.length;
|
||||
|
||||
if ((event.key === 'ArrowUp' || event.key === 'ArrowLeft') && selectionStart === 0) {
|
||||
captionInput.blur();
|
||||
this.moveCursorToPrevSection();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((event.key === 'ArrowDown' || event.key === 'ArrowRight') && selectionStart === length) {
|
||||
captionInput.blur();
|
||||
this.moveCursorToNextSection();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
convertUrl: task(function* (url) {
|
||||
if (isBlank(url)) {
|
||||
this.deleteCard();
|
||||
|
@ -110,11 +110,6 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this._detachHandlers();
|
||||
},
|
||||
|
||||
actions: {
|
||||
updateSrc(images) {
|
||||
let [image] = images;
|
||||
@ -125,14 +120,6 @@ export default Component.extend({
|
||||
this._updatePayloadAttr('caption', caption);
|
||||
},
|
||||
|
||||
onSelect() {
|
||||
this._attachHandlers();
|
||||
},
|
||||
|
||||
onDeselect() {
|
||||
this._detachHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens a file selection dialog - Triggered by "Upload Image" buttons,
|
||||
* searches for the hidden file input within the .gh-setting element
|
||||
@ -176,74 +163,6 @@ export default Component.extend({
|
||||
save(payload, false);
|
||||
},
|
||||
|
||||
_attachHandlers() {
|
||||
if (!this._keypressHandler) {
|
||||
this._keypressHandler = run.bind(this, this._handleKeypress);
|
||||
window.addEventListener('keypress', this._keypressHandler);
|
||||
}
|
||||
|
||||
if (!this._keydownHandler) {
|
||||
this._keydownHandler = run.bind(this, this._handleKeydown);
|
||||
window.addEventListener('keydown', this._keydownHandler);
|
||||
}
|
||||
},
|
||||
|
||||
_detachHandlers() {
|
||||
window.removeEventListener('keypress', this._keypressHandler);
|
||||
window.removeEventListener('keydown', this._keydownHandler);
|
||||
this._keypressHandler = null;
|
||||
this._keydownHandler = null;
|
||||
},
|
||||
|
||||
// only fires if the card is selected, moves focus to the caption input so
|
||||
// that it's possible to start typing without explicitly focusing the input
|
||||
_handleKeypress(event) {
|
||||
let captionInput = this.element.querySelector('[name="caption"]');
|
||||
|
||||
if (captionInput && captionInput !== document.activeElement) {
|
||||
captionInput.value = `${captionInput.value}${event.key}`;
|
||||
captionInput.focus();
|
||||
}
|
||||
},
|
||||
|
||||
// this will be fired for keydown events when the caption input is focused,
|
||||
// we look for cursor movements or the enter key to defocus and trigger the
|
||||
// corresponding editor behaviour
|
||||
_handleKeydown(event) {
|
||||
let captionInput = this.element.querySelector('[name="caption"]');
|
||||
|
||||
if (event.target === captionInput) {
|
||||
if (event.key === 'Escape') {
|
||||
captionInput.blur();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
captionInput.blur();
|
||||
this.addParagraphAfterCard();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
let selectionStart = captionInput.selectionStart;
|
||||
let length = captionInput.value.length;
|
||||
|
||||
if ((event.key === 'ArrowUp' || event.key === 'ArrowLeft') && selectionStart === 0) {
|
||||
captionInput.blur();
|
||||
this.moveCursorToPrevSection();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((event.key === 'ArrowDown' || event.key === 'ArrowRight') && selectionStart === length) {
|
||||
captionInput.blur();
|
||||
this.moveCursorToNextSection();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_triggerFileDialog(event) {
|
||||
let target = event && event.target || this.element;
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
<input
|
||||
placeholder={{if isFocused "" placeholder}}
|
||||
value={{caption}}
|
||||
type="text"
|
||||
class="miw-100 tc bn form-text bg-transparent"
|
||||
name="caption"
|
||||
oninput={{action update value="target.value"}}
|
||||
onfocus={{action (mut isFocused) true}}
|
||||
onblur={{action (mut isFocused) false}}
|
||||
>
|
@ -3,13 +3,16 @@
|
||||
isSelected=isSelected
|
||||
isEditing=isEditing
|
||||
selectCard=(action selectCard)
|
||||
onSelect=(action "onSelect")
|
||||
deselectCard=(action deselectCard)
|
||||
onDeselect=(action "onDeselect")
|
||||
editCard=(action editCard)
|
||||
toolbar=toolbar
|
||||
hasEditMode=false
|
||||
showSelectedOutline=payload.html
|
||||
addParagraphAfterCard=addParagraphAfterCard
|
||||
moveCursorToPrevSection=moveCursorToPrevSection
|
||||
moveCursorToNextSection=moveCursorToNextSection
|
||||
as |card|
|
||||
}}
|
||||
{{#if payload.html}}
|
||||
<div class="kg-card-hover">
|
||||
@ -19,16 +22,11 @@
|
||||
</div>
|
||||
|
||||
{{#if (or isSelected payload.caption)}}
|
||||
<figcaption class="{{kg-style "figcaption"}} w-100">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type caption for embed (optional)"
|
||||
class="miw-100 tc bn form-text bg-transparent"
|
||||
name="caption"
|
||||
value={{payload.caption}}
|
||||
oninput={{action "updateCaption" value="target.value"}}
|
||||
>
|
||||
</figcaption>
|
||||
{{card.captionInput
|
||||
caption=payload.caption
|
||||
update=(action "updateCaption")
|
||||
placeholder="Type caption for embed (optional)"
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
|
@ -5,11 +5,13 @@
|
||||
isEditing=isEditing
|
||||
selectCard=(action selectCard)
|
||||
deselectCard=(action deselectCard)
|
||||
onSelect=(action "onSelect")
|
||||
onDeselect=(action "onDeselect")
|
||||
editCard=(action editCard)
|
||||
toolbar=toolbar
|
||||
hasEditMode=false
|
||||
addParagraphAfterCard=addParagraphAfterCard
|
||||
moveCursorToPrevSection=moveCursorToPrevSection
|
||||
moveCursorToNextSection=moveCursorToNextSection
|
||||
as |card|
|
||||
}}
|
||||
{{#gh-uploader
|
||||
files=files
|
||||
@ -51,14 +53,10 @@
|
||||
{{/gh-uploader}}
|
||||
|
||||
{{#if (or isSelected payload.caption)}}
|
||||
<figcaption class="{{kg-style "figcaption"}} w-100">
|
||||
<input
|
||||
value={{payload.caption}}
|
||||
type="text"
|
||||
class="miw-100 tc bn form-text bg-transparent"
|
||||
name="caption"
|
||||
oninput={{action "updateCaption" value="target.value"}}
|
||||
placeholder="Type caption for image (optional)">
|
||||
</figcaption>
|
||||
{{card.captionInput
|
||||
caption=payload.caption
|
||||
update=(action "updateCaption")
|
||||
placeholder="Type caption for image (optional)"
|
||||
}}
|
||||
{{/if}}
|
||||
{{/koenig-card}}
|
||||
|
@ -4,7 +4,12 @@
|
||||
{{/sticky-element}}
|
||||
{{/if}}
|
||||
|
||||
{{yield}}
|
||||
{{yield (hash captionInput=(component "koenig-caption-input"
|
||||
captureInput=isSelected
|
||||
addParagraphAfterCard=addParagraphAfterCard
|
||||
moveCursorToPrevSection=moveCursorToPrevSection
|
||||
moveCursorToNextSection=moveCursorToNextSection
|
||||
))}}
|
||||
|
||||
{{#if toolbar}}
|
||||
<ul data-toolbar="true" class="kg-action-bar bg-darkgrey-d1 inline-flex pa0 ma0 pl1 pr1 nl1 list br3 shadow-2 items-center absolute white sans-serif f8 fw6 tracked-2 anim-fast-bezier z-999 {{if showToolbar "" "o-0 pop-down"}}" style={{toolbarStyle}}>
|
||||
@ -25,4 +30,4 @@
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
{{/if}}
|
@ -0,0 +1 @@
|
||||
export {default} from 'koenig-editor/components/koenig-caption-input';
|
@ -0,0 +1,24 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: koenig-caption-input', function () {
|
||||
setupComponentTest('koenig-caption-input', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it.skip('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#koenig-caption-input}}
|
||||
// template content
|
||||
// {{/koenig-caption-input}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{koenig-caption-input}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user