mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 12:21:36 +03:00
Migrated <GhUnsplashPhoto>
to glimmer component
refs https://github.com/TryGhost/Ghost/issues/14101 - extracted the ratio zoom handling to a `{{ratio-zoom}}` modifier to clean up the component and avoid needing lifecycle hooks that don't exist in Glimmer components - disabled `no-nested-interactive` linting in the template - not ideal but we'd need a much bigger design refactor to eliminate the nested links
This commit is contained in:
parent
8be8eb05bc
commit
f0432ba82c
@ -1769,3 +1769,11 @@ remove|ember-template-lint|no-invalid-interactive|3|45|3|45|a6f8ead2f0f3f3bdd702
|
|||||||
remove|ember-template-lint|no-invalid-interactive|80|132|80|132|b6e7f26b5f7ac647dd8659b6a67fa0d0de27142f|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash.hbs
|
remove|ember-template-lint|no-invalid-interactive|80|132|80|132|b6e7f26b5f7ac647dd8659b6a67fa0d0de27142f|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash.hbs
|
||||||
remove|ember-template-lint|no-passed-in-event-handlers|27|24|27|24|b5cce16b65649e02f9ce2a14986a6f83e46cbd58|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash.hbs
|
remove|ember-template-lint|no-passed-in-event-handlers|27|24|27|24|b5cce16b65649e02f9ce2a14986a6f83e46cbd58|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash.hbs
|
||||||
remove|ember-template-lint|no-passed-in-event-handlers|28|24|28|24|fb4c7afaa35fdebcf2f71540bdb1903fd70bb9d7|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash.hbs
|
remove|ember-template-lint|no-passed-in-event-handlers|28|24|28|24|fb4c7afaa35fdebcf2f71540bdb1903fd70bb9d7|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash.hbs
|
||||||
|
remove|ember-template-lint|no-action|1|46|1|46|7deb52aad96dd83886b4877b4042e1d7e173f96e|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
remove|ember-template-lint|no-action|18|63|18|63|be51c27cc7a2bc76b71c185656c46f6d0496d6ad|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
remove|ember-template-lint|no-nested-interactive|6|16|6|16|9b80f9fdc2a6a96bd2a8489ab62540b8d7d8e3b0|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
remove|ember-template-lint|no-nested-interactive|7|16|7|16|e0cc49f31d676d97f88b72277592aef5c790ad15|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
remove|ember-template-lint|no-nested-interactive|11|20|11|20|23958701600d242ed32febf00abfb86671b44ad3|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
remove|ember-template-lint|no-nested-interactive|14|20|14|20|fbc1273324d6ef3210b3c903dda0e18daaf35bcd|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
remove|ember-template-lint|no-nested-interactive|18|16|18|16|7c6cd3a62707e321896096224b805580adfe62a1|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
remove|ember-template-lint|require-valid-alt-text|12|24|12|24|67be77b9e376bf354f1ea2b8eca8e987dd4bd9cf|1643760000000|1646352000000|1648940400000|app/components/gh-unsplash-photo.hbs
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
<a class="gh-unsplash-photo" href="#" onclick={{action "zoom"}} data-unsplash-zoomed-photo={{if this.zoomed this.photo.id}} data-test-unsplash-photo={{this.photo.id}} style={{this.style}}>
|
{{!-- template-lint-disable no-nested-interactive --}}
|
||||||
|
<a class="gh-unsplash-photo" href="#" {{on "click" this.zoom}} {{ratio-zoom zoomed=@zoomed ratio=@photo.ratio}} style={{this.style}} data-test-unsplash-photo={{@photo.id}}>
|
||||||
<div class="gh-unsplash-photo-container" style={{this.containerStyle}} data-test-unsplash-photo-container>
|
<div class="gh-unsplash-photo-container" style={{this.containerStyle}} data-test-unsplash-photo-container>
|
||||||
<img src={{this.imageUrl}} alt={{this.photo.description}} width={{this.width}} height={{this.height}} data-test-unsplash-photo-image />
|
<img src={{this.imageUrl}} alt={{@photo.description}} width={{this.width}} height={{this.height}} data-test-unsplash-photo-image />
|
||||||
<div class="gh-unsplash-photo-overlay">
|
<div class="gh-unsplash-photo-overlay">
|
||||||
<div class="gh-unsplash-photo-header">
|
<div class="gh-unsplash-photo-header">
|
||||||
<a class="gh-unsplash-button-likes gh-unsplash-button" href="{{this.photo.links.html}}?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit" target="_blank" rel="noopener noreferrer">{{svg-jar "unsplash-heart"}}{{this.photo.likes}}</a>
|
<a class="gh-unsplash-button-likes gh-unsplash-button" href="{{@photo.links.html}}?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit" target="_blank" rel="noopener noreferrer">{{svg-jar "unsplash-heart"}}{{@photo.likes}}</a>
|
||||||
<a class="gh-unsplash-button-download gh-unsplash-button" href="{{this.photo.links.download}}/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit&force=true">{{svg-jar "download"}}</a>
|
<a class="gh-unsplash-button-download gh-unsplash-button" href="{{@photo.links.download}}/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit&force=true">{{svg-jar "download"}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="gh-unsplash-photo-footer">
|
<div class="gh-unsplash-photo-footer">
|
||||||
<div class="gh-unsplash-photo-author">
|
<div class="gh-unsplash-photo-author">
|
||||||
<a class="gh-unsplash-photo-author-img" href="{{this.photo.user.links.html}}?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit" target="_blank" rel="noopener noreferrer">
|
<a class="gh-unsplash-photo-author-img" href="{{@photo.user.links.html}}?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit" target="_blank" rel="noopener noreferrer">
|
||||||
<img src="{{this.photo.user.profile_image.medium}}" />
|
<img src="{{@photo.user.profile_image.medium}}" alt={{@photo.alt_description}} />
|
||||||
</a>
|
</a>
|
||||||
<a class="gh-unsplash-photo-author-name" href="{{this.photo.user.links.html}}?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit" target="_blank" rel="noopener noreferrer">
|
<a class="gh-unsplash-photo-author-name" href="{{@photo.user.links.html}}?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit" target="_blank" rel="noopener noreferrer">
|
||||||
{{this.photo.user.name}}
|
{{@photo.user.name}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a class="gh-unsplash-button" href="#" onclick={{action "select"}}>Insert image</a>
|
<a class="gh-unsplash-button" href="#" {{on "click" this.select}}>Insert image</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,32 +1,23 @@
|
|||||||
import $ from 'jquery';
|
import Component from '@glimmer/component';
|
||||||
import Component from '@ember/component';
|
import {action} from '@ember/object';
|
||||||
import {computed} from '@ember/object';
|
|
||||||
import {htmlSafe} from '@ember/template';
|
import {htmlSafe} from '@ember/template';
|
||||||
import {run} from '@ember/runloop';
|
import {tracked} from '@glimmer/tracking';
|
||||||
|
|
||||||
export default Component.extend({
|
export default class GhUnsplashPhoto extends Component {
|
||||||
|
@tracked height = 0;
|
||||||
|
@tracked width = 1200;
|
||||||
|
|
||||||
height: 0,
|
get style() {
|
||||||
photo: null,
|
return htmlSafe(this.args.zoomed ? 'width: auto; margin: 0;' : '');
|
||||||
tagName: '',
|
}
|
||||||
width: 1200,
|
|
||||||
zoomed: false,
|
|
||||||
|
|
||||||
// closure actions
|
|
||||||
select() {},
|
|
||||||
zoom() {},
|
|
||||||
|
|
||||||
style: computed('zoomed', function () {
|
|
||||||
return htmlSafe(this.zoomed ? 'width: auto; margin: 0;' : '');
|
|
||||||
}),
|
|
||||||
|
|
||||||
// avoid "binding style attributes" warnings
|
// avoid "binding style attributes" warnings
|
||||||
containerStyle: computed('photo.color', 'zoomed', function () {
|
get containerStyle() {
|
||||||
let styles = [];
|
const styles = [];
|
||||||
let ratio = this.get('photo.ratio');
|
const ratio = this.args.photo.ratio;
|
||||||
let zoomed = this.zoomed;
|
const zoomed = this.args.zoomed;
|
||||||
|
|
||||||
styles.push(`background-color: ${this.get('photo.color')}`);
|
styles.push(`background-color: ${this.args.photo.color}`);
|
||||||
|
|
||||||
if (zoomed) {
|
if (zoomed) {
|
||||||
styles.push(`cursor: zoom-out`);
|
styles.push(`cursor: zoom-out`);
|
||||||
@ -35,118 +26,39 @@ export default Component.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return htmlSafe(styles.join('; '));
|
return htmlSafe(styles.join('; '));
|
||||||
}),
|
}
|
||||||
|
|
||||||
imageUrl: computed('photo.urls.regular', function () {
|
get imageUrl() {
|
||||||
let url = this.get('photo.urls.regular');
|
let url = this.args.photo.urls.regular;
|
||||||
|
|
||||||
url = url.replace('&w=1080', '&w=1200');
|
url = url.replace('&w=1080', '&w=1200');
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}),
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
this.set('height', this.width * this.photo.ratio);
|
|
||||||
|
|
||||||
if (this.zoomed && !this._zoomed) {
|
|
||||||
this._setZoomedSize();
|
|
||||||
}
|
}
|
||||||
this._zoomed = this.zoomed;
|
|
||||||
|
|
||||||
if (this.zoomed && !this._resizeHandler) {
|
constructor() {
|
||||||
this._setupResizeHandler();
|
super(...arguments);
|
||||||
} else if (!this.zoomed && this._resizeHandler) {
|
this.height = this.width * this.args.photo.ratio;
|
||||||
this._teardownResizeHandler();
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement() {
|
@action
|
||||||
this._super(...arguments);
|
|
||||||
this._hasRendered = true;
|
|
||||||
if (this.zoomed) {
|
|
||||||
this._setZoomedSize();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this._teardownResizeHandler();
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
select(event) {
|
select(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.select(this.photo);
|
this.args.select(this.args.photo);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
zoom(event) {
|
zoom(event) {
|
||||||
let $target = $(event.target);
|
const {target} = event;
|
||||||
|
|
||||||
// only zoom when it wasn't one of the child links clicked
|
// only zoom when it wasn't one of the child links clicked
|
||||||
if (!$target.is('a') && $target.closest('a').hasClass('gh-unsplash-photo')) {
|
if (!target.matches('a') && target.closest('a').classList.contains('gh-unsplash-photo')) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.zoom(this.photo);
|
this.args.zoom(this.args.photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't propagate otherwise we can trigger the closeZoom action on the overlay
|
// don't propagate otherwise we can trigger the closeZoom action on the overlay
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_setZoomedSize() {
|
|
||||||
if (!this._hasRendered) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let a = document.querySelector(`[data-unsplash-zoomed-photo="${this.photo.id}"]`);
|
|
||||||
|
|
||||||
a.style.width = '100%';
|
|
||||||
a.style.height = '100%';
|
|
||||||
|
|
||||||
let offsets = a.getBoundingClientRect();
|
|
||||||
let ratio = this.photo.ratio;
|
|
||||||
|
|
||||||
let maxHeight = {
|
|
||||||
width: offsets.height / ratio,
|
|
||||||
height: offsets.height
|
|
||||||
};
|
|
||||||
|
|
||||||
let maxWidth = {
|
|
||||||
width: offsets.width,
|
|
||||||
height: offsets.width * ratio
|
|
||||||
};
|
|
||||||
|
|
||||||
let usableSize = null;
|
|
||||||
|
|
||||||
if (ratio <= 1) {
|
|
||||||
usableSize = maxWidth.height > offsets.height ? maxHeight : maxWidth;
|
|
||||||
} else {
|
|
||||||
usableSize = maxHeight.width > offsets.width ? maxWidth : maxHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.style.width = `${usableSize.width}px`;
|
|
||||||
a.style.height = `${usableSize.height}px`;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setupResizeHandler() {
|
|
||||||
if (this._resizeHandler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._resizeHandler = run.bind(this, this._handleResize);
|
|
||||||
window.addEventListener('resize', this._resizeHandler);
|
|
||||||
},
|
|
||||||
|
|
||||||
_teardownResizeHandler() {
|
|
||||||
window.removeEventListener('resize', this._resizeHandler);
|
|
||||||
this._resizeHandler = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleResize() {
|
|
||||||
this._throttleResize = run.throttle(this, this._setZoomedSize, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
68
ghost/admin/app/modifiers/ratio-zoom.js
Normal file
68
ghost/admin/app/modifiers/ratio-zoom.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import Modifier from 'ember-modifier';
|
||||||
|
import {bind, throttle} from '@ember/runloop';
|
||||||
|
|
||||||
|
export default class RatioZoom extends Modifier {
|
||||||
|
resizeHandler = null;
|
||||||
|
|
||||||
|
didReceiveArguments() {
|
||||||
|
const {zoomed} = this.args.named;
|
||||||
|
|
||||||
|
if (zoomed) {
|
||||||
|
this.setZoomedSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroy() {
|
||||||
|
this.removeResizeEventListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
setZoomedSize() {
|
||||||
|
const {ratio} = this.args.named;
|
||||||
|
|
||||||
|
this.element.style.width = '100%';
|
||||||
|
this.element.style.height = '100%';
|
||||||
|
|
||||||
|
const offsets = this.element.getBoundingClientRect();
|
||||||
|
|
||||||
|
let maxHeight = {
|
||||||
|
width: offsets.height / ratio,
|
||||||
|
height: offsets.height
|
||||||
|
};
|
||||||
|
|
||||||
|
let maxWidth = {
|
||||||
|
width: offsets.width,
|
||||||
|
height: offsets.width * ratio
|
||||||
|
};
|
||||||
|
|
||||||
|
let usableSize = null;
|
||||||
|
|
||||||
|
if (ratio <= 1) {
|
||||||
|
usableSize = maxWidth.height > offsets.height ? maxHeight : maxWidth;
|
||||||
|
} else {
|
||||||
|
usableSize = maxHeight.width > offsets.width ? maxWidth : maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.style.width = `${usableSize.width}px`;
|
||||||
|
this.element.style.height = `${usableSize.height}px`;
|
||||||
|
|
||||||
|
this.addResizeEventListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResize() {
|
||||||
|
throttle(this, this.setZoomedSize, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
addResizeEventListener() {
|
||||||
|
if (!this.resizeHandler) {
|
||||||
|
this.resizeHandler = bind(this, this.handleResize);
|
||||||
|
window.addEventListener('resize', this.resizeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeResizeEventListener() {
|
||||||
|
if (this.resizeHandler) {
|
||||||
|
window.removeEventListener('resize', this.resizeHandler);
|
||||||
|
this.resizeHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user