mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-01 13:54:35 +03:00
cd6d167f77
refs https://github.com/TryGhost/Team/issues/1219 - added optional `adjustOnResize` argument to `{{movable}}` modifier - when the movable element's size changes (based on a `ResizeObserver` event) the passed in action is called with the element and the current x/y position - having this handled via the `{{movable}}` modifier is needed because that's what knows about any CSS translations that are present and allows the drag position to remain in sync with any resize-related adjustments - return value of the action is expected to be a new `{x, y}` tuple - used `adjustOnResize` in the `<KoenigSettingsPanel>` component so that any time the settings panel grows and causes part of it to be off-screen we re-position so that the whole panel is kept on-screen
94 lines
3.1 KiB
JavaScript
94 lines
3.1 KiB
JavaScript
import Component from '@glimmer/component';
|
|
import {action} from '@ember/object';
|
|
import {bind} from '@ember/runloop';
|
|
import {task} from 'ember-concurrency-decorators';
|
|
import {timeout} from 'ember-concurrency';
|
|
|
|
const CARD_SPACING = 20;
|
|
const MIN_RIGHT_SPACING = 20;
|
|
const MIN_TOP_SPACING = 66 + 20; // 66 is publish menu and word count size
|
|
|
|
export default class KoenigSettingsPanelComponent extends Component {
|
|
constructor() {
|
|
super(...arguments);
|
|
this._windowResizeHandler = bind(this, this.debounceWindowResizeTask.perform);
|
|
window.addEventListener('resize', this._windowResizeHandler);
|
|
}
|
|
|
|
willDestroy() {
|
|
super.willDestroy(...arguments);
|
|
window.removeEventListener('resize', this._windowResizeHandler);
|
|
}
|
|
|
|
@action
|
|
registerAndPosition(panelElem) {
|
|
this.panelElem = panelElem;
|
|
this.positionPanel(panelElem);
|
|
}
|
|
|
|
@action
|
|
positionPanel(panelElem) {
|
|
if (!panelElem) {
|
|
return;
|
|
}
|
|
|
|
const panelRect = panelElem.getBoundingClientRect();
|
|
const containerRect = panelElem.parentElement.getBoundingClientRect();
|
|
|
|
const containerMiddle = containerRect.top + (containerRect.height / 2);
|
|
|
|
// position vertically centered
|
|
// if part of panel would be off screen adjust to keep minimum distance from window top/botom
|
|
let top = Math.max(containerMiddle - (panelRect.height / 2), MIN_TOP_SPACING);
|
|
if (top + panelRect.height > window.innerHeight - MIN_TOP_SPACING) {
|
|
top = window.innerHeight - MIN_TOP_SPACING - panelRect.height;
|
|
}
|
|
|
|
// position to right of panel
|
|
// if part of panel would be off screen adjust to keep minimum distance from window edge
|
|
let left = containerRect.right + CARD_SPACING;
|
|
if (left + panelRect.width > window.innerWidth - MIN_RIGHT_SPACING) {
|
|
left = window.innerWidth - panelRect.width - MIN_RIGHT_SPACING;
|
|
}
|
|
|
|
panelElem.style.top = `${top}px`;
|
|
panelElem.style.left = `${left}px`;
|
|
}
|
|
|
|
@task({restartable: true})
|
|
*debounceWindowResizeTask() {
|
|
yield timeout(250);
|
|
this.positionPanel(this.panelElem);
|
|
}
|
|
|
|
// called when panel is expanded/collapsed by changing settings
|
|
@action
|
|
calculateResizeAdjustment(panelElem, {x, y}) {
|
|
const panelRect = panelElem.getBoundingClientRect();
|
|
|
|
const topIsOffscreen = panelRect.top < 0;
|
|
const bottomIsOffscreen = panelRect.bottom > window.innerHeight;
|
|
|
|
if (topIsOffscreen && bottomIsOffscreen) {
|
|
// there's not much we can do here, the screen is too small.
|
|
// leave as-is to avoid any weird jumping
|
|
return {x, y};
|
|
}
|
|
|
|
if (topIsOffscreen) {
|
|
const yAdjustment = Math.abs(panelRect.top) + 10;
|
|
|
|
return {x, y: y + yAdjustment};
|
|
}
|
|
|
|
if (bottomIsOffscreen) {
|
|
const yAdjustment = -Math.abs(panelRect.bottom - window.innerHeight) - 10;
|
|
|
|
return {x, y: y + yAdjustment};
|
|
}
|
|
|
|
// no adjustment needed
|
|
return {x, y};
|
|
}
|
|
}
|