diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.hbs b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.hbs index 8959ac42cb..14ddb4ee87 100644 --- a/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.hbs +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.hbs @@ -6,91 +6,101 @@ @selectCard={{action this.selectCard}} @deselectCard={{action this.deselectCard}} @editCard={{action this.editCard}} - @saveAsSnippet={{this.saveAsSnippet}} + @saveAsSnippet={{if this.payload.src this.saveAsSnippet}} @toolbar={{this.toolbar}} @hasEditMode={{false}} + @onDeselect={{action "onDeselect"}} @addParagraphAfterCard={{this.addParagraphAfterCard}} @moveCursorToPrevSection={{this.moveCursorToPrevSection}} @moveCursorToNextSection={{this.moveCursorToNextSection}} @editor={{this.editor}} as |card| > - -
- {{#if (or this.previewSrc this.payload.src)}} - {{this.payload.alt}} - {{#if this.isDraggedOver}} -
- - Drop to replace image - + {{#if (eq this.imageSelector.type "placeholder")}} + {{!-- image selector placeholder (eg, gif browser) --}} + {{component this.imageSelector.component + searchTerm=this.payload.searchTerm + select=(action "selectFromImageSelector") + close=(action "closeImageSelector")}} + {{else}} + {{!-- standard image upload placeholder --}} + +
+ {{#if (or this.previewSrc this.payload.src)}} + {{this.payload.alt}} + {{#if this.isDraggedOver}} +
+ + Drop to replace image + +
+ {{/if}} + {{/if}} + + {{#if (or uploader.errors uploader.isUploading (not this.payload.src))}} +
+ {{#if uploader.errors}} + + {{uploader.errors.firstObject.message}} + + {{/if}} + + {{#if this.isDraggedOver}} + + Drop it like it's hot 🔥 + + {{else if uploader.isUploading}} + {{uploader.progressBar}} + {{else if (not this.previewSrc this.payload.src)}} + + {{/if}}
{{/if}} +
+ +
+ +
+
+ + {{#if (or this.isSelected (clean-basic-html this.payload.caption))}} + {{#if this.isEditingAlt}} + + {{else}} + {{/if}} - {{#if (or uploader.errors uploader.isUploading (not this.payload.src))}} -
- {{#if uploader.errors}} - - {{uploader.errors.firstObject.message}} - - {{/if}} - - {{#if this.isDraggedOver}} - - Drop it like it's hot 🔥 - - {{else if uploader.isUploading}} - {{uploader.progressBar}} - {{else if (not this.previewSrc this.payload.src)}} - - {{/if}} -
+ {{#if this.isSelected}} + {{/if}} -
- -
- -
- - - {{#if (or this.isSelected (clean-basic-html this.payload.caption))}} - {{#if this.isEditingAlt}} - - {{else}} - - {{/if}} - - {{#if this.isSelected}} - {{/if}} {{/if}} - {{#if this.imageSelector}} - {{component this.imageSelector + {{#if (eq this.imageSelector.type "modal")}} + {{component this.imageSelector.component searchTerm=this.payload.searchTerm select=(action "selectFromImageSelector") close=(action "closeImageSelector")}} diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.js b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.js index 21b6a66059..b634bddf43 100644 --- a/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.js +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image.js @@ -4,6 +4,7 @@ import { IMAGE_EXTENSIONS, IMAGE_MIME_TYPES } from 'ghost-admin/components/gh-image-uploader'; +import {NO_CURSOR_MOVEMENT} from './koenig-editor'; import {action, computed, set, setProperties} from '@ember/object'; import {utils as ghostHelperUtils} from '@tryghost/helpers'; import {isEmpty} from '@ember/utils'; @@ -39,15 +40,19 @@ export default Component.extend({ addParagraphAfterCard() {}, registerComponent() {}, - isEmpty: computed('payload.{imageSelector,src}', function () { - return !this.payload.imageSelector && !this.payload.src; - }), + isEmpty: computed.not('payload.src'), imageSelector: computed('payload.imageSelector', function () { let selector = this.payload.imageSelector; let imageSelectors = { - unsplash: 'gh-unsplash', - tenor: 'gh-tenor' + unsplash: { + component: 'gh-unsplash', + type: 'modal' + }, + tenor: { + component: 'koenig-card-image/selector-tenor', + type: 'placeholder' + } }; return imageSelectors[selector]; @@ -247,9 +252,19 @@ export default Component.extend({ }); }, - closeImageSelector() { + closeImageSelector(reselectParagraph = true) { if (!this.payload.src) { - return this.deleteCard(); + return this.editor.run((postEditor) => { + let {builder} = postEditor; + let cardSection = this.env.postModel; + let p = builder.createMarkupSection('p'); + + postEditor.replaceSection(cardSection, p); + + if (reselectParagraph) { + postEditor.setRange(p.tailPosition()); + } + }); } set(this.payload, 'imageSelector', undefined); @@ -266,6 +281,12 @@ export default Component.extend({ cancelEditLink() { this.set('isEditing', false); this.set('isEditingLink', false); + }, + + onDeselect() { + if (this.imageSelector?.type === 'placeholder' && !this.payload.src) { + this.send('closeImageSelector', false); + } } }, diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.hbs b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.hbs new file mode 100644 index 0000000000..8d8c7cc667 --- /dev/null +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.hbs @@ -0,0 +1,66 @@ +
+ {{!-- static header --}} +
+ + {{svg-jar "search"}} + + +
+ + {{!-- content container --}} +
+ {{!-- scrollable image container --}} +
+ {{#if this.tenor.gifs}} +
+ {{#each this.tenor.columns as |gifs|}} +
+ {{#each gifs as |gif|}} + + {{/each}} +
+ {{/each}} +
+ {{else if (and this.tenor.searchTerm (not this.tenor.error this.tenor.isLoading))}} +
+
+ No photos found +

No gifs found for '{{this.tenor.searchTerm}}'

+
+
+ {{/if}} + + {{#if this.tenor.error}} +
+
+ Network error +

{{this.tenor.error}} (retry)

+
+
+ {{/if}} + + {{#if this.tenor.isLoading}} +
+
+
+ {{/if}} + + {{#unless this.tenor.isLoading}} + + {{/unless}} +
+
+
\ No newline at end of file diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.js b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.js new file mode 100644 index 0000000000..9049a26f74 --- /dev/null +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.js @@ -0,0 +1,66 @@ +import Component from '@glimmer/component'; +import {action} from '@ember/object'; +import {inject as service} from '@ember/service'; + +// number of columns based on selector container width +const TWO_COLUMN_WIDTH = 540; +const THREE_COLUMN_WIDTH = 940; + +export default class KoenigCardImageTenorSelector extends Component { + @service tenor; + + willDestroy() { + super.willDestroy(...arguments); + this._resizeObserver?.disconnect(); + } + + @action + search(event) { + const term = event.target.value; + this.tenor.updateSearch(term); + } + + @action + didInsertContainer(containerElem) { + if (this.args.searchTerm !== this.tenor.searchTerm) { + this.tenor.updateSearch(this.args.searchTerm); + } + + this._resizeObserver = new ResizeObserver((entries) => { + const [containerEntry] = entries; + const contentBoxSize = Array.isArray(containerEntry.contentBoxSize) ? containerEntry.contentBoxSize[0] : containerEntry.contentBoxSize; + + const width = contentBoxSize.inlineSize; + + let columns = 4; + + if (width <= TWO_COLUMN_WIDTH) { + columns = 2; + } else if (width <= THREE_COLUMN_WIDTH) { + columns = 3; + } + + this.tenor.changeColumnCount(columns); + }); + this._resizeObserver.observe(containerElem); + } + + @action + select(gif, event) { + event?.preventDefault(); + event?.stopPropagation(); + + const media = gif.media[0].gif; + + const payload = { + src: media.url, + width: media.dims[0], + height: media.dims[1], + caption: '', + type: 'gif' + }; + + this.args.selector.insertCard('image', payload); + this.args.selector.close(); + } +} diff --git a/ghost/admin/lib/koenig-editor/addon/options/cards.js b/ghost/admin/lib/koenig-editor/addon/options/cards.js index 6574dabfb4..bfd56842e1 100644 --- a/ghost/admin/lib/koenig-editor/addon/options/cards.js +++ b/ghost/admin/lib/koenig-editor/addon/options/cards.js @@ -185,7 +185,12 @@ export const CARD_MENU = [ desc: '/gif [search term]', iconClass: 'kg-card-type-unsplash', matches: ['gif', 'giphy', 'tenor'], - type: 'selector', + type: 'card', + replaceArg: 'image', + params: ['searchTerm'], + payload: { + imageSelector: 'tenor' + }, selectorComponent: 'koenig-media-selector-tenor', isAvailable: ['feature.gifsCard', 'config.tenor.apiKey'] }, diff --git a/ghost/admin/lib/koenig-editor/app/components/koenig-card-image/selector-tenor.js b/ghost/admin/lib/koenig-editor/app/components/koenig-card-image/selector-tenor.js new file mode 100644 index 0000000000..1ef3916fe5 --- /dev/null +++ b/ghost/admin/lib/koenig-editor/app/components/koenig-card-image/selector-tenor.js @@ -0,0 +1 @@ +export {default} from 'koenig-editor/components/koenig-card-image/selector-tenor';