mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
add generic popups with their own elements
This commit is contained in:
@@ -4511,6 +4511,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- various fullscreen popups -->
|
<!-- various fullscreen popups -->
|
||||||
|
<template id="shadow_popup_template">
|
||||||
|
<div class="shadow_popup">
|
||||||
|
<div class="dialogue_popup">
|
||||||
|
<div class="dialogue_popup_holder">
|
||||||
|
<div class="dialogue_popup_text">
|
||||||
|
<h3 class="margin0">text</h3>
|
||||||
|
</div>
|
||||||
|
<textarea class="dialogue_popup_input text_pole" rows="1"></textarea>
|
||||||
|
<div class="dialogue_popup_controls">
|
||||||
|
<div class="dialogue_popup_ok menu_button" data-i18n="Delete">Delete</div>
|
||||||
|
<div class="dialogue_popup_cancel menu_button" data-i18n="Cancel">Cancel</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<div id="shadow_popup">
|
<div id="shadow_popup">
|
||||||
<div id="dialogue_popup">
|
<div id="dialogue_popup">
|
||||||
<div id="dialogue_popup_holder">
|
<div id="dialogue_popup_holder">
|
||||||
|
@@ -821,7 +821,7 @@ let create_save = {
|
|||||||
//animation right menu
|
//animation right menu
|
||||||
export const ANIMATION_DURATION_DEFAULT = 125;
|
export const ANIMATION_DURATION_DEFAULT = 125;
|
||||||
export let animation_duration = ANIMATION_DURATION_DEFAULT;
|
export let animation_duration = ANIMATION_DURATION_DEFAULT;
|
||||||
let animation_easing = 'ease-in-out';
|
export let animation_easing = 'ease-in-out';
|
||||||
let popup_type = '';
|
let popup_type = '';
|
||||||
let chat_file_for_del = '';
|
let chat_file_for_del = '';
|
||||||
let online_status = 'no_connection';
|
let online_status = 'no_connection';
|
||||||
|
225
public/scripts/popup.js
Normal file
225
public/scripts/popup.js
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
import { animation_duration, animation_easing } from '../script.js';
|
||||||
|
import { delay } from './utils.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**@readonly*/
|
||||||
|
/**@enum {Number}*/
|
||||||
|
export const POPUP_TYPE = {
|
||||||
|
'TEXT': 1,
|
||||||
|
'CONFIRM': 2,
|
||||||
|
'INPUT': 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**@readonly*/
|
||||||
|
/**@enum {Boolean}*/
|
||||||
|
export const POPUP_RESULT = {
|
||||||
|
'AFFIRMATIVE': true,
|
||||||
|
'NEGATIVE': false,
|
||||||
|
'CANCELLED': undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class Popup {
|
||||||
|
/**@type {POPUP_TYPE}*/ type;
|
||||||
|
|
||||||
|
/**@type {HTMLElement}*/ dom;
|
||||||
|
/**@type {HTMLElement}*/ dlg;
|
||||||
|
/**@type {HTMLElement}*/ text;
|
||||||
|
/**@type {HTMLTextAreaElement}*/ input;
|
||||||
|
/**@type {HTMLElement}*/ ok;
|
||||||
|
/**@type {HTMLElement}*/ cancel;
|
||||||
|
|
||||||
|
/**@type {POPUP_RESULT}*/ result;
|
||||||
|
/**@type {any}*/ value;
|
||||||
|
|
||||||
|
/**@type {Promise}*/ promise;
|
||||||
|
/**@type {Function}*/ resolver;
|
||||||
|
|
||||||
|
/**@type {Function}*/ keyListenerBound;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{okButton?: string, cancelButton?: string, rows?: number, wide?: boolean, large?: boolean, allowHorizontalScrolling?: boolean, allowVerticalScrolling?: boolean }} PopupOptions - Options for the popup.
|
||||||
|
* @param {JQuery<HTMLElement>|string|Element} text - Text to display in the popup.
|
||||||
|
* @param {POPUP_TYPE} type - One of Popup.TYPE
|
||||||
|
* @param {string} inputValue - Value to set the input to.
|
||||||
|
* @param {PopupOptions} options - Options for the popup.
|
||||||
|
*/
|
||||||
|
constructor(text, type, inputValue = '', { okButton, cancelButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling } = {}) {
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
|
/**@type {HTMLTemplateElement}*/
|
||||||
|
const template = document.querySelector('#shadow_popup_template');
|
||||||
|
// @ts-ignore
|
||||||
|
this.dom = template.content.cloneNode(true).querySelector('.shadow_popup');
|
||||||
|
const dlg = this.dom.querySelector('.dialogue_popup');
|
||||||
|
// @ts-ignore
|
||||||
|
this.dlg = dlg;
|
||||||
|
this.text = this.dom.querySelector('.dialogue_popup_text');
|
||||||
|
this.input = this.dom.querySelector('.dialogue_popup_input');
|
||||||
|
this.ok = this.dom.querySelector('.dialogue_popup_ok');
|
||||||
|
this.cancel = this.dom.querySelector('.dialogue_popup_cancel');
|
||||||
|
|
||||||
|
if (wide) dlg.classList.add('wide_dialogue_popup');
|
||||||
|
if (large) dlg.classList.add('large_dialogue_popup');
|
||||||
|
if (allowHorizontalScrolling) dlg.classList.add('horizontal_scrolling_dialogue_popup');
|
||||||
|
if (allowVerticalScrolling) dlg.classList.add('vertical_scrolling_dialogue_popup');
|
||||||
|
|
||||||
|
this.ok.textContent = okButton ?? 'OK';
|
||||||
|
this.cancel.textContent = cancelButton ?? 'Cancel';
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case POPUP_TYPE.TEXT: {
|
||||||
|
this.input.style.display = 'none';
|
||||||
|
this.cancel.style.display = 'none';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case POPUP_TYPE.CONFIRM: {
|
||||||
|
this.input.style.display = 'none';
|
||||||
|
this.ok.textContent = okButton ?? 'Yes';
|
||||||
|
this.cancel.textContent = cancelButton ?? 'No';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case POPUP_TYPE.INPUT: {
|
||||||
|
this.input.style.display = 'block';
|
||||||
|
this.ok.textContent = okButton ?? 'Save';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// illegal argument
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.input.value = inputValue;
|
||||||
|
this.input.rows = rows ?? 1;
|
||||||
|
|
||||||
|
this.text.innerHTML = '';
|
||||||
|
if (text instanceof jQuery) {
|
||||||
|
$(this.text).append(text);
|
||||||
|
} else if (text instanceof HTMLElement) {
|
||||||
|
this.text.append(text);
|
||||||
|
} else if (typeof text == 'string') {
|
||||||
|
this.text.innerHTML = text;
|
||||||
|
} else {
|
||||||
|
// illegal argument
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ok.addEventListener('click', ()=>this.completeAffirmative());
|
||||||
|
this.cancel.addEventListener('click', ()=>this.completeNegative());
|
||||||
|
const keyListener = (evt)=>{
|
||||||
|
switch (evt.key) {
|
||||||
|
case 'Escape': {
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
this.completeCancelled();
|
||||||
|
window.removeEventListener('keydown', keyListenerBound);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const keyListenerBound = keyListener.bind(this);
|
||||||
|
window.addEventListener('keydown', keyListenerBound);
|
||||||
|
}
|
||||||
|
|
||||||
|
async show() {
|
||||||
|
document.body.append(this.dom);
|
||||||
|
this.dom.style.display = 'block';
|
||||||
|
switch(this.type) {
|
||||||
|
case POPUP_TYPE.INPUT: {
|
||||||
|
this.input.focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this.dom).transition({
|
||||||
|
opacity: 1,
|
||||||
|
duration: animation_duration,
|
||||||
|
easing: animation_easing,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.promise = new Promise((resolve) => {
|
||||||
|
this.resolver = resolve;
|
||||||
|
});
|
||||||
|
return this.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
completeAffirmative() {
|
||||||
|
switch (this.type) {
|
||||||
|
case POPUP_TYPE.TEXT:
|
||||||
|
case POPUP_TYPE.CONFIRM: {
|
||||||
|
this.value = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case POPUP_TYPE.INPUT: {
|
||||||
|
this.value = this.input.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.result = POPUP_RESULT.AFFIRMATIVE;
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
completeNegative() {
|
||||||
|
switch (this.type) {
|
||||||
|
case POPUP_TYPE.TEXT:
|
||||||
|
case POPUP_TYPE.CONFIRM:
|
||||||
|
case POPUP_TYPE.INPUT: {
|
||||||
|
this.value = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.result = POPUP_RESULT.NEGATIVE;
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
completeCancelled() {
|
||||||
|
switch (this.type) {
|
||||||
|
case POPUP_TYPE.TEXT:
|
||||||
|
case POPUP_TYPE.CONFIRM:
|
||||||
|
case POPUP_TYPE.INPUT: {
|
||||||
|
this.value = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.result = POPUP_RESULT.CANCELLED;
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
$(this.dom).transition({
|
||||||
|
opacity: 0,
|
||||||
|
duration: animation_duration,
|
||||||
|
easing: animation_easing,
|
||||||
|
});
|
||||||
|
delay(animation_duration).then(()=>{
|
||||||
|
this.dom.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.resolver(this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a blocking popup with a given text and type.
|
||||||
|
* @param {JQuery<HTMLElement>|string|Element} text - Text to display in the popup.
|
||||||
|
* @param {POPUP_TYPE} type
|
||||||
|
* @param {string} inputValue - Value to set the input to.
|
||||||
|
* @param {PopupOptions} options - Options for the popup.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function callGenericPopup(text, type, inputValue = '', { okButton, cancelButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling } = {}) {
|
||||||
|
const popup = new Popup(
|
||||||
|
text,
|
||||||
|
type,
|
||||||
|
inputValue,
|
||||||
|
{ okButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling },
|
||||||
|
);
|
||||||
|
return popup.show();
|
||||||
|
}
|
@@ -2059,7 +2059,8 @@ grammarly-extension {
|
|||||||
/* Focus */
|
/* Focus */
|
||||||
|
|
||||||
#bulk_tag_popup,
|
#bulk_tag_popup,
|
||||||
#dialogue_popup {
|
#dialogue_popup,
|
||||||
|
.dialogue_popup {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
max-width: 90vw;
|
max-width: 90vw;
|
||||||
max-width: 90svw;
|
max-width: 90svw;
|
||||||
@@ -2112,7 +2113,8 @@ grammarly-extension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#bulk_tag_popup_holder,
|
#bulk_tag_popup_holder,
|
||||||
#dialogue_popup_holder {
|
#dialogue_popup_holder,
|
||||||
|
.dialogue_popup_holder {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -2120,13 +2122,15 @@ grammarly-extension {
|
|||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#dialogue_popup_text {
|
#dialogue_popup_text,
|
||||||
|
.dialogue_popup_text {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#dialogue_popup_controls {
|
#dialogue_popup_controls,
|
||||||
|
.dialogue_popup_controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
@@ -2134,14 +2138,16 @@ grammarly-extension {
|
|||||||
|
|
||||||
#bulk_tag_popup_reset,
|
#bulk_tag_popup_reset,
|
||||||
#bulk_tag_popup_remove_mutual,
|
#bulk_tag_popup_remove_mutual,
|
||||||
#dialogue_popup_ok {
|
#dialogue_popup_ok,
|
||||||
|
.dialogue_popup_ok {
|
||||||
background-color: var(--crimson70a);
|
background-color: var(--crimson70a);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#bulk_tag_popup_reset:hover,
|
#bulk_tag_popup_reset:hover,
|
||||||
#bulk_tag_popup_remove_mutual:hover,
|
#bulk_tag_popup_remove_mutual:hover,
|
||||||
#dialogue_popup_ok:hover {
|
#dialogue_popup_ok:hover,
|
||||||
|
.dialogue_popup_ok:hover {
|
||||||
background-color: var(--crimson-hover);
|
background-color: var(--crimson-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2149,13 +2155,15 @@ grammarly-extension {
|
|||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
#dialogue_popup_input {
|
#dialogue_popup_input,
|
||||||
|
.dialogue_popup_input {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#bulk_tag_popup_cancel,
|
#bulk_tag_popup_cancel,
|
||||||
#dialogue_popup_cancel {
|
#dialogue_popup_cancel,
|
||||||
|
.dialogue_popup_cancel {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2220,7 +2228,7 @@ grammarly-extension {
|
|||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#shadow_popup {
|
#shadow_popup, .shadow_popup {
|
||||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||||
-webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
-webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||||
background-color: var(--black30a);
|
background-color: var(--black30a);
|
||||||
@@ -2232,6 +2240,9 @@ grammarly-extension {
|
|||||||
height: 100svh;
|
height: 100svh;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
&.shadow_popup {
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#bgtest {
|
#bgtest {
|
||||||
|
Reference in New Issue
Block a user