Port image cropper to new popup
This commit is contained in:
parent
974b98ed8e
commit
e0000bade6
|
@ -4873,12 +4873,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- various fullscreen popups -->
|
<!-- various fullscreen popups -->
|
||||||
<template id="popup_template" data-i18n="[popup-button-save]popup-button-save;[popup-button-yes]popup-button-yes;[popup-button-no]popup-button-no;[popup-button-cancel]popup-button-cancel;[popup-button-import]popup-button-import" popup-button-save="Save" popup-button-yes="Yes" popup-button-no="No" popup-button-cancel="Cancel" popup-button-import="Import"> <!-- localization data holder for popups -->
|
<template id="popup_template" data-i18n="[popup-button-save]popup-button-save;[popup-button-yes]popup-button-yes;[popup-button-no]popup-button-no;[popup-button-cancel]popup-button-cancel;[popup-button-import]popup-button-import;[popup-button-crop]popup-button-crop" popup-button-save="Save" popup-button-yes="Yes" popup-button-no="No" popup-button-cancel="Cancel" popup-button-import="Import" popup-button-crop="Crop"> <!-- localization data holder for popups -->
|
||||||
<dialog class="popup">
|
<dialog class="popup">
|
||||||
<div class="popup-body">
|
<div class="popup-body">
|
||||||
<div class="popup-content">
|
<div class="popup-content">
|
||||||
<h3 class="popup-header">text</h3>
|
<h3 class="popup-header">text</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="popup-crop-wrap">
|
||||||
|
<img class="popup-crop-image" src="">
|
||||||
|
</div>
|
||||||
<textarea class="popup-input text_pole result-control" rows="1" data-result="1" data-result-event="submit"></textarea>
|
<textarea class="popup-input text_pole result-control" rows="1" data-result="1" data-result-event="submit"></textarea>
|
||||||
<div class="popup-controls">
|
<div class="popup-controls">
|
||||||
<div class="popup-button-ok menu_button result-control" data-result="1" data-i18n="Delete">Delete</div>
|
<div class="popup-button-ok menu_button result-control" data-result="1" data-i18n="Delete">Delete</div>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { power_user } from './power-user.js';
|
||||||
import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js';
|
import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js';
|
||||||
|
|
||||||
/** @readonly */
|
/** @readonly */
|
||||||
|
@ -11,6 +12,8 @@ export const POPUP_TYPE = {
|
||||||
INPUT: 3,
|
INPUT: 3,
|
||||||
/** Popup without any button controls. Used to simply display content, with a small X in the corner. */
|
/** Popup without any button controls. Used to simply display content, with a small X in the corner. */
|
||||||
DISPLAY: 4,
|
DISPLAY: 4,
|
||||||
|
/** Popup that displays an image to crop. Returns a cropped image in result. */
|
||||||
|
CROP: 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @readonly */
|
/** @readonly */
|
||||||
|
@ -36,6 +39,8 @@ export const POPUP_RESULT = {
|
||||||
* @property {CustomPopupButton[]|string[]?} [customButtons=null] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward.
|
* @property {CustomPopupButton[]|string[]?} [customButtons=null] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward.
|
||||||
* @property {(popup: Popup) => boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close
|
* @property {(popup: Popup) => boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close
|
||||||
* @property {(popup: Popup) => void?} [onClose=null] - Handler called after the popup closes, but before the DOM is cleaned up
|
* @property {(popup: Popup) => void?} [onClose=null] - Handler called after the popup closes, but before the DOM is cleaned up
|
||||||
|
* @property {number?} [cropAspect=null] - Aspect ratio for the crop popup
|
||||||
|
* @property {string?} [cropImage=null] - Image URL to display in the crop popup
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +89,8 @@ export class Popup {
|
||||||
/** @type {HTMLElement} */ okButton;
|
/** @type {HTMLElement} */ okButton;
|
||||||
/** @type {HTMLElement} */ cancelButton;
|
/** @type {HTMLElement} */ cancelButton;
|
||||||
/** @type {HTMLElement} */ closeButton;
|
/** @type {HTMLElement} */ closeButton;
|
||||||
|
/** @type {HTMLElement} */ cropWrap;
|
||||||
|
/** @type {HTMLImageElement} */ cropImage;
|
||||||
/** @type {POPUP_RESULT|number?} */ defaultResult;
|
/** @type {POPUP_RESULT|number?} */ defaultResult;
|
||||||
/** @type {CustomPopupButton[]|string[]?} */ customButtons;
|
/** @type {CustomPopupButton[]|string[]?} */ customButtons;
|
||||||
|
|
||||||
|
@ -92,6 +99,7 @@ export class Popup {
|
||||||
|
|
||||||
/** @type {POPUP_RESULT|number} */ result;
|
/** @type {POPUP_RESULT|number} */ result;
|
||||||
/** @type {any} */ value;
|
/** @type {any} */ value;
|
||||||
|
/** @type {any} */ cropData;
|
||||||
|
|
||||||
/** @type {HTMLElement} */ lastFocus;
|
/** @type {HTMLElement} */ lastFocus;
|
||||||
|
|
||||||
|
@ -106,7 +114,7 @@ export class Popup {
|
||||||
* @param {string} [inputValue=''] - The initial value of the input field
|
* @param {string} [inputValue=''] - The initial value of the input field
|
||||||
* @param {PopupOptions} [options={}] - Additional options for the popup
|
* @param {PopupOptions} [options={}] - Additional options for the popup
|
||||||
*/
|
*/
|
||||||
constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, transparent = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, onClosing = null, onClose = null } = {}) {
|
constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, transparent = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, onClosing = null, onClose = null, cropAspect = null, cropImage = null } = {}) {
|
||||||
Popup.util.popups.push(this);
|
Popup.util.popups.push(this);
|
||||||
|
|
||||||
// Make this popup uniquely identifiable
|
// Make this popup uniquely identifiable
|
||||||
|
@ -128,6 +136,8 @@ export class Popup {
|
||||||
this.okButton = this.dlg.querySelector('.popup-button-ok');
|
this.okButton = this.dlg.querySelector('.popup-button-ok');
|
||||||
this.cancelButton = this.dlg.querySelector('.popup-button-cancel');
|
this.cancelButton = this.dlg.querySelector('.popup-button-cancel');
|
||||||
this.closeButton = this.dlg.querySelector('.popup-button-close');
|
this.closeButton = this.dlg.querySelector('.popup-button-close');
|
||||||
|
this.cropWrap = this.dlg.querySelector('.popup-crop-wrap');
|
||||||
|
this.cropImage = this.dlg.querySelector('.popup-crop-image');
|
||||||
|
|
||||||
this.dlg.setAttribute('data-id', this.id);
|
this.dlg.setAttribute('data-id', this.id);
|
||||||
if (wide) this.dlg.classList.add('wide_dialogue_popup');
|
if (wide) this.dlg.classList.add('wide_dialogue_popup');
|
||||||
|
@ -169,6 +179,7 @@ export class Popup {
|
||||||
// General styling for all types first, that might be overriden for specific types below
|
// General styling for all types first, that might be overriden for specific types below
|
||||||
this.input.style.display = 'none';
|
this.input.style.display = 'none';
|
||||||
this.closeButton.style.display = 'none';
|
this.closeButton.style.display = 'none';
|
||||||
|
this.cropWrap.style.display = 'none';
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case POPUP_TYPE.TEXT: {
|
case POPUP_TYPE.TEXT: {
|
||||||
|
@ -190,6 +201,22 @@ export class Popup {
|
||||||
this.closeButton.style.display = 'block';
|
this.closeButton.style.display = 'block';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case POPUP_TYPE.CROP: {
|
||||||
|
this.cropWrap.style.display = 'block';
|
||||||
|
this.cropImage.src = cropImage;
|
||||||
|
if (!okButton) this.okButton.textContent = template.getAttribute('popup-button-crop');
|
||||||
|
$(this.cropImage).cropper({
|
||||||
|
aspectRatio: cropAspect ?? 2 / 3,
|
||||||
|
autoCropArea: 1,
|
||||||
|
viewMode: 2,
|
||||||
|
rotatable: false,
|
||||||
|
crop: function (event) {
|
||||||
|
this.cropData = event.detail;
|
||||||
|
this.cropData.want_resize = !power_user.never_resize_avatars;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
console.warn('Unknown popup type.', type);
|
console.warn('Unknown popup type.', type);
|
||||||
break;
|
break;
|
||||||
|
@ -347,6 +374,13 @@ export class Popup {
|
||||||
else value = false; // Might a custom negative value?
|
else value = false; // Might a custom negative value?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cropped image should be returned as a data URL
|
||||||
|
if (this.type === POPUP_TYPE.CROP) {
|
||||||
|
value = result >= POPUP_RESULT.AFFIRMATIVE
|
||||||
|
? $(this.cropImage).data('cropper').getCroppedCanvas().toDataURL('image/jpeg')
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { callPopup, getCropPopup, getRequestHeaders } from '../script.js';
|
import { getRequestHeaders } from '../script.js';
|
||||||
import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from './popup.js';
|
import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from './popup.js';
|
||||||
import { renderTemplateAsync } from './templates.js';
|
import { renderTemplateAsync } from './templates.js';
|
||||||
import { ensureImageFormatSupported, getBase64Async, humanFileSize } from './utils.js';
|
import { ensureImageFormatSupported, getBase64Async, humanFileSize } from './utils.js';
|
||||||
|
@ -727,14 +727,14 @@ async function openUserProfile() {
|
||||||
*/
|
*/
|
||||||
async function cropAndUploadAvatar(handle, file) {
|
async function cropAndUploadAvatar(handle, file) {
|
||||||
const dataUrl = await getBase64Async(await ensureImageFormatSupported(file));
|
const dataUrl = await getBase64Async(await ensureImageFormatSupported(file));
|
||||||
const croppedImage = await callPopup(getCropPopup(dataUrl), 'avatarToCrop', '', { cropAspect: 1 });
|
const croppedImage = await callGenericPopup('Set the crop position of the avatar image', POPUP_TYPE.CROP, '', { cropAspect: 1, cropImage: dataUrl });
|
||||||
if (!croppedImage) {
|
if (!croppedImage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await changeAvatar(handle, String(croppedImage));
|
await changeAvatar(handle, String(croppedImage));
|
||||||
|
|
||||||
return croppedImage;
|
return String(croppedImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4154,13 +4154,20 @@ h5 {
|
||||||
grid-template-columns: 340px auto;
|
grid-template-columns: 340px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#avatarCropWrap {
|
#avatarCropWrap,
|
||||||
|
.popup-crop-wrap {
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#avatarToCrop {
|
.popup-crop-wrap {
|
||||||
|
max-height: 75vh;
|
||||||
|
max-height: 75svh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#avatarToCrop,
|
||||||
|
.popup-crop-wrap img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
/* This rule is very important, please do not ignore this! */
|
/* This rule is very important, please do not ignore this! */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue