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() {},
|
deselectCard() {},
|
||||||
editCard() {},
|
editCard() {},
|
||||||
saveCard() {},
|
saveCard() {},
|
||||||
deleteCard() { },
|
deleteCard() {},
|
||||||
moveCursorToNextSection() { },
|
moveCursorToNextSection() {},
|
||||||
moveCursorToPrevSection() { },
|
moveCursorToPrevSection() {},
|
||||||
addParagraphAfterCard() { },
|
addParagraphAfterCard() {},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
@ -43,19 +43,8 @@ export default Component.extend({
|
|||||||
this._loadPayloadScript();
|
this._loadPayloadScript();
|
||||||
},
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this._detachHandlers();
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onSelect() {
|
|
||||||
this._attachHandlers();
|
|
||||||
},
|
|
||||||
|
|
||||||
onDeselect() {
|
onDeselect() {
|
||||||
this._detachHandlers();
|
|
||||||
|
|
||||||
if (this.payload.url && !this.payload.html && !this.hasError) {
|
if (this.payload.url && !this.payload.html && !this.hasError) {
|
||||||
this.convertUrl.perform(this.payload.url);
|
this.convertUrl.perform(this.payload.url);
|
||||||
} else {
|
} 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) {
|
convertUrl: task(function* (url) {
|
||||||
if (isBlank(url)) {
|
if (isBlank(url)) {
|
||||||
this.deleteCard();
|
this.deleteCard();
|
||||||
|
@ -110,11 +110,6 @@ export default Component.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this._detachHandlers();
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
updateSrc(images) {
|
updateSrc(images) {
|
||||||
let [image] = images;
|
let [image] = images;
|
||||||
@ -125,14 +120,6 @@ export default Component.extend({
|
|||||||
this._updatePayloadAttr('caption', caption);
|
this._updatePayloadAttr('caption', caption);
|
||||||
},
|
},
|
||||||
|
|
||||||
onSelect() {
|
|
||||||
this._attachHandlers();
|
|
||||||
},
|
|
||||||
|
|
||||||
onDeselect() {
|
|
||||||
this._detachHandlers();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a file selection dialog - Triggered by "Upload Image" buttons,
|
* Opens a file selection dialog - Triggered by "Upload Image" buttons,
|
||||||
* searches for the hidden file input within the .gh-setting element
|
* searches for the hidden file input within the .gh-setting element
|
||||||
@ -176,74 +163,6 @@ export default Component.extend({
|
|||||||
save(payload, false);
|
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) {
|
_triggerFileDialog(event) {
|
||||||
let target = event && event.target || this.element;
|
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
|
isSelected=isSelected
|
||||||
isEditing=isEditing
|
isEditing=isEditing
|
||||||
selectCard=(action selectCard)
|
selectCard=(action selectCard)
|
||||||
onSelect=(action "onSelect")
|
|
||||||
deselectCard=(action deselectCard)
|
deselectCard=(action deselectCard)
|
||||||
onDeselect=(action "onDeselect")
|
onDeselect=(action "onDeselect")
|
||||||
editCard=(action editCard)
|
editCard=(action editCard)
|
||||||
toolbar=toolbar
|
toolbar=toolbar
|
||||||
hasEditMode=false
|
hasEditMode=false
|
||||||
showSelectedOutline=payload.html
|
showSelectedOutline=payload.html
|
||||||
|
addParagraphAfterCard=addParagraphAfterCard
|
||||||
|
moveCursorToPrevSection=moveCursorToPrevSection
|
||||||
|
moveCursorToNextSection=moveCursorToNextSection
|
||||||
|
as |card|
|
||||||
}}
|
}}
|
||||||
{{#if payload.html}}
|
{{#if payload.html}}
|
||||||
<div class="kg-card-hover">
|
<div class="kg-card-hover">
|
||||||
@ -19,16 +22,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if (or isSelected payload.caption)}}
|
{{#if (or isSelected payload.caption)}}
|
||||||
<figcaption class="{{kg-style "figcaption"}} w-100">
|
{{card.captionInput
|
||||||
<input
|
caption=payload.caption
|
||||||
type="text"
|
update=(action "updateCaption")
|
||||||
placeholder="Type caption for embed (optional)"
|
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>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
isEditing=isEditing
|
isEditing=isEditing
|
||||||
selectCard=(action selectCard)
|
selectCard=(action selectCard)
|
||||||
deselectCard=(action deselectCard)
|
deselectCard=(action deselectCard)
|
||||||
onSelect=(action "onSelect")
|
|
||||||
onDeselect=(action "onDeselect")
|
|
||||||
editCard=(action editCard)
|
editCard=(action editCard)
|
||||||
toolbar=toolbar
|
toolbar=toolbar
|
||||||
hasEditMode=false
|
hasEditMode=false
|
||||||
|
addParagraphAfterCard=addParagraphAfterCard
|
||||||
|
moveCursorToPrevSection=moveCursorToPrevSection
|
||||||
|
moveCursorToNextSection=moveCursorToNextSection
|
||||||
|
as |card|
|
||||||
}}
|
}}
|
||||||
{{#gh-uploader
|
{{#gh-uploader
|
||||||
files=files
|
files=files
|
||||||
@ -51,14 +53,10 @@
|
|||||||
{{/gh-uploader}}
|
{{/gh-uploader}}
|
||||||
|
|
||||||
{{#if (or isSelected payload.caption)}}
|
{{#if (or isSelected payload.caption)}}
|
||||||
<figcaption class="{{kg-style "figcaption"}} w-100">
|
{{card.captionInput
|
||||||
<input
|
caption=payload.caption
|
||||||
value={{payload.caption}}
|
update=(action "updateCaption")
|
||||||
type="text"
|
placeholder="Type caption for image (optional)"
|
||||||
class="miw-100 tc bn form-text bg-transparent"
|
}}
|
||||||
name="caption"
|
|
||||||
oninput={{action "updateCaption" value="target.value"}}
|
|
||||||
placeholder="Type caption for image (optional)">
|
|
||||||
</figcaption>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/koenig-card}}
|
{{/koenig-card}}
|
||||||
|
@ -4,7 +4,12 @@
|
|||||||
{{/sticky-element}}
|
{{/sticky-element}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{yield}}
|
{{yield (hash captionInput=(component "koenig-caption-input"
|
||||||
|
captureInput=isSelected
|
||||||
|
addParagraphAfterCard=addParagraphAfterCard
|
||||||
|
moveCursorToPrevSection=moveCursorToPrevSection
|
||||||
|
moveCursorToNextSection=moveCursorToNextSection
|
||||||
|
))}}
|
||||||
|
|
||||||
{{#if toolbar}}
|
{{#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}}>
|
<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}}
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</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