mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-22 18:31:57 +03:00
cb7a038b08
refs https://github.com/TryGhost/Ghost/issues/9724 - focus was not being returned to the editor after the Unsplash modal was closed so although the image card looked selected the editor was not responding to any key commands
264 lines
7.2 KiB
JavaScript
264 lines
7.2 KiB
JavaScript
import $ from 'jquery';
|
|
import Component from '@ember/component';
|
|
import countWords, {stripTags} from '../utils/count-words';
|
|
import layout from '../templates/components/koenig-card-image';
|
|
import {
|
|
IMAGE_EXTENSIONS,
|
|
IMAGE_MIME_TYPES
|
|
} from 'ghost-admin/components/gh-image-uploader';
|
|
import {computed, set, setProperties} from '@ember/object';
|
|
import {htmlSafe} from '@ember/string';
|
|
import {isEmpty} from '@ember/utils';
|
|
import {run} from '@ember/runloop';
|
|
import {inject as service} from '@ember/service';
|
|
|
|
export default Component.extend({
|
|
ui: service(),
|
|
layout,
|
|
|
|
// attrs
|
|
files: null,
|
|
payload: null,
|
|
isSelected: false,
|
|
isEditing: false,
|
|
imageExtensions: IMAGE_EXTENSIONS,
|
|
imageMimeTypes: IMAGE_MIME_TYPES,
|
|
|
|
// properties
|
|
handlesDragDrop: true,
|
|
|
|
// closure actions
|
|
selectCard() {},
|
|
deselectCard() {},
|
|
editCard() {},
|
|
saveCard() {},
|
|
deleteCard() {},
|
|
moveCursorToNextSection() {},
|
|
moveCursorToPrevSection() {},
|
|
addParagraphAfterCard() {},
|
|
registerComponent() {},
|
|
|
|
imageSelector: computed('payload.imageSelector', function () {
|
|
let selector = this.payload.imageSelector;
|
|
let imageSelectors = {
|
|
unsplash: 'gh-unsplash'
|
|
};
|
|
|
|
return imageSelectors[selector];
|
|
}),
|
|
|
|
counts: computed('payload.{src,caption}', function () {
|
|
let wordCount = 0;
|
|
let imageCount = 0;
|
|
|
|
if (this.payload.src) {
|
|
imageCount += 1;
|
|
}
|
|
|
|
if (this.payload.caption) {
|
|
wordCount += countWords(stripTags(this.payload.caption));
|
|
}
|
|
|
|
return {wordCount, imageCount};
|
|
}),
|
|
|
|
kgImgStyle: computed('payload.cardWidth', function () {
|
|
let cardWidth = this.payload.cardWidth;
|
|
|
|
if (cardWidth === 'wide') {
|
|
return 'image-wide';
|
|
}
|
|
|
|
if (cardWidth === 'full') {
|
|
return 'image-full';
|
|
}
|
|
|
|
return 'image-normal';
|
|
}),
|
|
|
|
toolbar: computed('payload.{cardWidth,src}', function () {
|
|
let cardWidth = this.payload.cardWidth;
|
|
let items = [];
|
|
|
|
if (this.payload.src) {
|
|
items.push({
|
|
title: 'Regular',
|
|
icon: 'koenig/kg-img-regular',
|
|
iconClass: `${!cardWidth ? 'fill-blue-l2' : 'fill-white'}`,
|
|
action: run.bind(this, this._changeCardWidth, '')
|
|
});
|
|
|
|
items.push({
|
|
title: 'Wide',
|
|
icon: 'koenig/kg-img-wide',
|
|
iconClass: `${cardWidth === 'wide' ? 'fill-blue-l2' : 'fill-white'}`,
|
|
action: run.bind(this, this._changeCardWidth, 'wide')
|
|
});
|
|
|
|
items.push({
|
|
title: 'Full',
|
|
icon: 'koenig/kg-img-full',
|
|
iconClass: `${cardWidth === 'full' ? 'fill-blue-l2' : 'fill-white'}`,
|
|
action: run.bind(this, this._changeCardWidth, 'full')
|
|
});
|
|
|
|
items.push({divider: true});
|
|
|
|
items.push({
|
|
title: 'Replace image',
|
|
icon: 'koenig/kg-replace',
|
|
iconClass: 'fill-white',
|
|
action: run.bind(this, this._triggerFileDialog)
|
|
});
|
|
}
|
|
|
|
if (items.length > 0) {
|
|
return {items};
|
|
}
|
|
}),
|
|
|
|
init() {
|
|
this._super(...arguments);
|
|
|
|
if (!this.payload) {
|
|
this.set('payload', {});
|
|
}
|
|
|
|
this.registerComponent(this);
|
|
},
|
|
|
|
didReceiveAttrs() {
|
|
this._super(...arguments);
|
|
|
|
// `payload.files` can be set if we have an externaly set image that
|
|
// should be uploaded. Typical example would be from a paste or drag/drop
|
|
if (!isEmpty(this.payload.files)) {
|
|
run.schedule('afterRender', this, function () {
|
|
this.set('files', this.payload.files);
|
|
|
|
// we don't want to persist any file data in the document
|
|
delete this.payload.files;
|
|
});
|
|
}
|
|
},
|
|
|
|
actions: {
|
|
updateSrc(images) {
|
|
let [image] = images;
|
|
this._updatePayloadAttr('src', image.url);
|
|
},
|
|
|
|
updateCaption(caption) {
|
|
this._updatePayloadAttr('caption', caption);
|
|
},
|
|
|
|
/**
|
|
* Opens a file selection dialog - Triggered by "Upload Image" buttons,
|
|
* searches for the hidden file input within the .gh-setting element
|
|
* containing the clicked button then simulates a click
|
|
* @param {MouseEvent} event - MouseEvent fired by the button click
|
|
*/
|
|
triggerFileDialog(event) {
|
|
this._triggerFileDialog(event);
|
|
},
|
|
|
|
setPreviewSrc(files) {
|
|
let file = files[0];
|
|
if (file) {
|
|
let reader = new FileReader();
|
|
|
|
reader.onload = (e) => {
|
|
this.set('previewSrc', htmlSafe(e.target.result));
|
|
};
|
|
|
|
reader.readAsDataURL(file);
|
|
}
|
|
},
|
|
|
|
resetSrcs() {
|
|
this.set('previewSrc', null);
|
|
this._updatePayloadAttr('src', null);
|
|
},
|
|
|
|
selectFromImageSelector({src, caption, alt}) {
|
|
let {payload, saveCard} = this;
|
|
let searchTerm;
|
|
|
|
setProperties(payload, {src, caption, alt, searchTerm});
|
|
|
|
this.send('closeImageSelector');
|
|
|
|
saveCard(payload, false);
|
|
},
|
|
|
|
closeImageSelector() {
|
|
if (!this.payload.src) {
|
|
return this.deleteCard();
|
|
}
|
|
|
|
set(this.payload, 'imageSelector', undefined);
|
|
|
|
// ensure focus is returned to the editor so that the card which
|
|
// appears selected behaves as if it's selected
|
|
this.editor.focus();
|
|
}
|
|
},
|
|
|
|
dragOver(event) {
|
|
if (!event.dataTransfer) {
|
|
return;
|
|
}
|
|
|
|
// this is needed to work around inconsistencies with dropping files
|
|
// from Chrome's downloads bar
|
|
if (navigator.userAgent.indexOf('Chrome') > -1) {
|
|
let eA = event.dataTransfer.effectAllowed;
|
|
event.dataTransfer.dropEffect = (eA === 'move' || eA === 'linkMove') ? 'move' : 'copy';
|
|
}
|
|
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.set('isDraggedOver', true);
|
|
},
|
|
|
|
dragLeave(event) {
|
|
event.preventDefault();
|
|
this.set('isDraggedOver', false);
|
|
},
|
|
|
|
drop(event) {
|
|
event.preventDefault();
|
|
this.set('isDraggedOver', false);
|
|
|
|
if (event.dataTransfer.files) {
|
|
this.set('files', [event.dataTransfer.files[0]]);
|
|
}
|
|
},
|
|
|
|
_changeCardWidth(cardWidth) {
|
|
this._updatePayloadAttr('cardWidth', cardWidth);
|
|
},
|
|
|
|
_updatePayloadAttr(attr, value) {
|
|
let payload = this.payload;
|
|
let save = this.saveCard;
|
|
|
|
set(payload, attr, value);
|
|
|
|
// update the mobiledoc and stay in edit mode
|
|
save(payload, false);
|
|
},
|
|
|
|
_triggerFileDialog(event) {
|
|
let target = event && event.target || this.element;
|
|
|
|
// simulate click to open file dialog
|
|
// using jQuery because IE11 doesn't support MouseEvent
|
|
$(target)
|
|
.closest('.__mobiledoc-card')
|
|
.find('input[type="file"]')
|
|
.click();
|
|
}
|
|
});
|