mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-13 10:42:55 +01:00
Merge pull request #1337 from artisticMink/hotfix/bulk-edit-enhanced
Fixes for bulk editing overlay
This commit is contained in:
commit
bc2b3e9c4e
@ -1,7 +1,3 @@
|
|||||||
#rm_print_characters_block .character_select,
|
|
||||||
#rm_print_characters_block .group_select{
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#rm_print_characters_block.group_overlay_mode_select .character_select {
|
#rm_print_characters_block.group_overlay_mode_select .character_select {
|
||||||
transition: background-color 0.4s ease;
|
transition: background-color 0.4s ease;
|
||||||
@ -9,6 +5,15 @@
|
|||||||
background-color: rgba(170, 170, 170, 0.15);
|
background-color: rgba(170, 170, 170, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#rm_print_characters_block.group_overlay_mode_select .group_select {
|
||||||
|
cursor: auto;
|
||||||
|
filter: saturate(0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#rm_print_characters_block.group_overlay_mode_select .group_select:hover {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
#rm_print_characters_block.group_overlay_mode_select .character_select input.bulk_select_checkbox {
|
#rm_print_characters_block.group_overlay_mode_select .character_select input.bulk_select_checkbox {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@ -26,7 +31,7 @@
|
|||||||
#character_context_menu {
|
#character_context_menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
z-index: 10000;
|
z-index: 9998;
|
||||||
background-color: var(--black90a);
|
background-color: var(--black90a);
|
||||||
border: 1px solid var(--black90a);
|
border: 1px solid var(--black90a);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
@ -93,11 +93,11 @@
|
|||||||
<div id="bg1"></div>
|
<div id="bg1"></div>
|
||||||
<div id="character_context_menu" class="hidden">
|
<div id="character_context_menu" class="hidden">
|
||||||
<ul>
|
<ul>
|
||||||
<li><button id="character_context_menu_favorite">Favorite</button></li>
|
<li><button id="character_context_menu_favorite" data-i18n="Favorite">Favorite</button></li>
|
||||||
<li><button id="character_context_menu_tag">Tag</button></li>
|
<li><button id="character_context_menu_tag" data-i18n="Tag">Tag</button></li>
|
||||||
<li><button id="character_context_menu_duplicate">Duplicate</button></li>
|
<li><button id="character_context_menu_duplicate" data-i18n="Duplicate">Duplicate</button></li>
|
||||||
<li><button id="character_context_menu_persona">Persona</button></li>
|
<li><button id="character_context_menu_persona" data-i18n="Persona">Persona</button></li>
|
||||||
<li><button id="character_context_menu_delete">Delete</button></li>
|
<li><button id="character_context_menu_delete" data-i18n="Delete">Delete</button></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!-- top bar central settings buttons -->
|
<!-- top bar central settings buttons -->
|
||||||
|
@ -159,6 +159,15 @@ class CharacterContextMenu {
|
|||||||
contextMenu.style.top = `${positionY}px`;
|
contextMenu.style.top = `${positionY}px`;
|
||||||
|
|
||||||
document.getElementById(BulkEditOverlay.contextMenuId).classList.remove('hidden');
|
document.getElementById(BulkEditOverlay.contextMenuId).classList.remove('hidden');
|
||||||
|
|
||||||
|
// Adjust position if context menu is outside of viewport
|
||||||
|
const boundingRect = contextMenu.getBoundingClientRect();
|
||||||
|
if (boundingRect.right > window.innerWidth) {
|
||||||
|
contextMenu.style.left = `${positionX - (boundingRect.right - window.innerWidth)}px`;
|
||||||
|
}
|
||||||
|
if (boundingRect.bottom > window.innerHeight) {
|
||||||
|
contextMenu.style.top = `${positionY - (boundingRect.bottom - window.innerHeight)}px`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -277,6 +286,7 @@ class BulkEditOverlay {
|
|||||||
static containerId = 'rm_print_characters_block';
|
static containerId = 'rm_print_characters_block';
|
||||||
static contextMenuId = 'character_context_menu';
|
static contextMenuId = 'character_context_menu';
|
||||||
static characterClass = 'character_select';
|
static characterClass = 'character_select';
|
||||||
|
static groupClass = 'group_select';
|
||||||
static selectModeClass = 'group_overlay_mode_select';
|
static selectModeClass = 'group_overlay_mode_select';
|
||||||
static selectedClass = 'character_selected';
|
static selectedClass = 'character_selected';
|
||||||
static legacySelectedClass = 'bulk_select_checkbox';
|
static legacySelectedClass = 'bulk_select_checkbox';
|
||||||
@ -288,6 +298,20 @@ class BulkEditOverlay {
|
|||||||
#stateChangeCallbacks = [];
|
#stateChangeCallbacks = [];
|
||||||
#selectedCharacters = [];
|
#selectedCharacters = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks other pointer actions when the context menu is open
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
#contextMenuOpen = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the next character select should be skipped
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
#cancelNextToggle = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type HTMLElement
|
* @type HTMLElement
|
||||||
*/
|
*/
|
||||||
@ -332,7 +356,6 @@ class BulkEditOverlay {
|
|||||||
return bulkEditOverlayInstance
|
return bulkEditOverlayInstance
|
||||||
|
|
||||||
this.container = document.getElementById(BulkEditOverlay.containerId);
|
this.container = document.getElementById(BulkEditOverlay.containerId);
|
||||||
this.container.addEventListener('click', this.handleCancelClick);
|
|
||||||
|
|
||||||
eventSource.on(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER, this.handleStateChange);
|
eventSource.on(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER, this.handleStateChange);
|
||||||
bulkEditOverlayInstance = Object.freeze(this);
|
bulkEditOverlayInstance = Object.freeze(this);
|
||||||
@ -364,8 +387,9 @@ class BulkEditOverlay {
|
|||||||
elements.forEach(element => element.addEventListener('dragend', this.handleLongPressEnd));
|
elements.forEach(element => element.addEventListener('dragend', this.handleLongPressEnd));
|
||||||
elements.forEach(element => element.addEventListener('touchmove', this.handleLongPressEnd));
|
elements.forEach(element => element.addEventListener('touchmove', this.handleLongPressEnd));
|
||||||
|
|
||||||
const grid = document.getElementById(BulkEditOverlay.containerId);
|
// Cohee: It only triggers when clicking on a margin between the elements?
|
||||||
grid.addEventListener('click', this.handleCancelClick);
|
// Feel free to fix or remove this, I'm not sure how to.
|
||||||
|
//this.container.addEventListener('click', this.handleCancelClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -377,7 +401,9 @@ class BulkEditOverlay {
|
|||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case BulkEditOverlayState.browse:
|
case BulkEditOverlayState.browse:
|
||||||
this.container.classList.remove(BulkEditOverlay.selectModeClass);
|
this.container.classList.remove(BulkEditOverlay.selectModeClass);
|
||||||
|
this.#contextMenuOpen = false;
|
||||||
this.#enableClickEventsForCharacters();
|
this.#enableClickEventsForCharacters();
|
||||||
|
this.#enableClickEventsForGroups();
|
||||||
this.clearSelectedCharacters();
|
this.clearSelectedCharacters();
|
||||||
this.disableContextMenu();
|
this.disableContextMenu();
|
||||||
this.#disableBulkEditButtonHighlight();
|
this.#disableBulkEditButtonHighlight();
|
||||||
@ -386,6 +412,7 @@ class BulkEditOverlay {
|
|||||||
case BulkEditOverlayState.select:
|
case BulkEditOverlayState.select:
|
||||||
this.container.classList.add(BulkEditOverlay.selectModeClass);
|
this.container.classList.add(BulkEditOverlay.selectModeClass);
|
||||||
this.#disableClickEventsForCharacters();
|
this.#disableClickEventsForCharacters();
|
||||||
|
this.#disableClickEventsForGroups();
|
||||||
this.enableContextMenu();
|
this.enableContextMenu();
|
||||||
this.#enableBulkEditButtonHighlight();
|
this.#enableBulkEditButtonHighlight();
|
||||||
break;
|
break;
|
||||||
@ -399,7 +426,7 @@ class BulkEditOverlay {
|
|||||||
* set a click event to hide the custom context menu.
|
* set a click event to hide the custom context menu.
|
||||||
*/
|
*/
|
||||||
enableContextMenu = () => {
|
enableContextMenu = () => {
|
||||||
document.getElementById('rm_print_characters_block').addEventListener('contextmenu', this.handleContextMenuShow);
|
this.container.addEventListener('contextmenu', this.handleContextMenuShow);
|
||||||
document.addEventListener('click', this.handleContextMenuHide);
|
document.addEventListener('click', this.handleContextMenuHide);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +435,7 @@ class BulkEditOverlay {
|
|||||||
* menu to be opened.
|
* menu to be opened.
|
||||||
*/
|
*/
|
||||||
disableContextMenu = () => {
|
disableContextMenu = () => {
|
||||||
document.getElementById('rm_print_characters_block').removeEventListener('contextmenu', this.handleContextMenuShow);
|
this.container.removeEventListener('contextmenu', this.handleContextMenuShow);
|
||||||
document.removeEventListener('click', this.handleContextMenuHide);
|
document.removeEventListener('click', this.handleContextMenuHide);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,26 +447,52 @@ class BulkEditOverlay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens menu on long-press.
|
||||||
|
*
|
||||||
|
* @param event - Pointer event
|
||||||
|
*/
|
||||||
handleHold = (event) => {
|
handleHold = (event) => {
|
||||||
if (0 !== event.button && event.type !== 'touchstart') return;
|
if (0 !== event.button && event.type !== 'touchstart') return;
|
||||||
|
if (this.#contextMenuOpen) {
|
||||||
|
this.#contextMenuOpen = false;
|
||||||
|
this.#cancelNextToggle = true;
|
||||||
|
CharacterContextMenu.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancel = false;
|
||||||
|
|
||||||
|
const cancelHold = (event) => cancel = true;
|
||||||
|
this.container.addEventListener('mouseup', cancelHold);
|
||||||
|
this.container.addEventListener('touchend', cancelHold);
|
||||||
|
|
||||||
this.isLongPress = true;
|
this.isLongPress = true;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.isLongPress) {
|
if (this.isLongPress && !cancel) {
|
||||||
if (this.state === BulkEditOverlayState.browse)
|
if (this.state === BulkEditOverlayState.browse) {
|
||||||
this.selectState();
|
this.selectState();
|
||||||
else if (this.state === BulkEditOverlayState.select)
|
} else if (this.state === BulkEditOverlayState.select) {
|
||||||
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
|
this.#contextMenuOpen = true;
|
||||||
}
|
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
|
||||||
}, BulkEditOverlay.longPressDelay);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container.removeEventListener('mouseup', cancelHold);
|
||||||
|
this.container.removeEventListener('touchend', cancelHold);
|
||||||
|
},
|
||||||
|
BulkEditOverlay.longPressDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLongPressEnd = () => {
|
handleLongPressEnd = (event) => {
|
||||||
this.isLongPress = false;
|
this.isLongPress = false;
|
||||||
|
if (this.#contextMenuOpen) event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelClick = () => {
|
handleCancelClick = () => {
|
||||||
this.state = BulkEditOverlayState.browse;
|
if (false === this.#contextMenuOpen) this.state = BulkEditOverlayState.browse;
|
||||||
|
this.#contextMenuOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -453,6 +506,17 @@ class BulkEditOverlay {
|
|||||||
event.clientY || event.touches[0].clientY,
|
event.clientY || event.touches[0].clientY,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#stopEventPropagation = (event) => {
|
||||||
|
if (this.#contextMenuOpen) {
|
||||||
|
this.handleContextMenuHide(event);
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
#enableClickEventsForGroups = () => this.#getDisabledElements().forEach((element) => element.removeEventListener('click', this.#stopEventPropagation));
|
||||||
|
|
||||||
|
#disableClickEventsForGroups = () => this.#getDisabledElements().forEach((element) => element.addEventListener('click', this.#stopEventPropagation));
|
||||||
|
|
||||||
#enableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.removeEventListener('click', this.toggleCharacterSelected));
|
#enableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.removeEventListener('click', this.toggleCharacterSelected));
|
||||||
|
|
||||||
#disableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.addEventListener('click', this.toggleCharacterSelected));
|
#disableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.addEventListener('click', this.toggleCharacterSelected));
|
||||||
@ -463,6 +527,8 @@ class BulkEditOverlay {
|
|||||||
|
|
||||||
#getEnabledElements = () => [...this.container.getElementsByClassName(BulkEditOverlay.characterClass)];
|
#getEnabledElements = () => [...this.container.getElementsByClassName(BulkEditOverlay.characterClass)];
|
||||||
|
|
||||||
|
#getDisabledElements = () =>[...this.container.getElementsByClassName(BulkEditOverlay.groupClass)];
|
||||||
|
|
||||||
toggleCharacterSelected = event => {
|
toggleCharacterSelected = event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
@ -473,25 +539,28 @@ class BulkEditOverlay {
|
|||||||
|
|
||||||
const legacyBulkEditCheckbox = character.querySelector('.' + BulkEditOverlay.legacySelectedClass);
|
const legacyBulkEditCheckbox = character.querySelector('.' + BulkEditOverlay.legacySelectedClass);
|
||||||
|
|
||||||
if (alreadySelected) {
|
// Only toggle when context menu is closed and wasn't just closed.
|
||||||
character.classList.remove(BulkEditOverlay.selectedClass);
|
if (!this.#contextMenuOpen && !this.#cancelNextToggle)
|
||||||
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = false;
|
if (alreadySelected) {
|
||||||
this.dismissCharacter(characterId);
|
character.classList.remove(BulkEditOverlay.selectedClass);
|
||||||
} else {
|
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = false;
|
||||||
character.classList.add(BulkEditOverlay.selectedClass)
|
this.dismissCharacter(characterId);
|
||||||
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = true;
|
} else {
|
||||||
this.selectCharacter(characterId);
|
character.classList.add(BulkEditOverlay.selectedClass)
|
||||||
}
|
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = true;
|
||||||
|
this.selectCharacter(characterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#cancelNextToggle = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContextMenuShow = (event) => {
|
handleContextMenuShow = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
document.getElementById(BulkEditOverlay.containerId).style.pointerEvents = 'none';
|
|
||||||
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
|
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
|
||||||
|
this.#contextMenuOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContextMenuHide = (event) => {
|
handleContextMenuHide = (event) => {
|
||||||
document.getElementById(BulkEditOverlay.containerId).style.pointerEvents = '';
|
|
||||||
let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId);
|
let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId);
|
||||||
if (false === contextMenu.contains(event.target)) {
|
if (false === contextMenu.contains(event.target)) {
|
||||||
CharacterContextMenu.hide();
|
CharacterContextMenu.hide();
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import { FILTER_TYPES, FilterHelper } from "./filters.js";
|
import { FILTER_TYPES, FilterHelper } from "./filters.js";
|
||||||
|
|
||||||
import { groupCandidatesFilter, selected_group } from "./group-chats.js";
|
import { groupCandidatesFilter, selected_group } from "./group-chats.js";
|
||||||
import { uuidv4 } from "./utils.js";
|
import { onlyUnique, uuidv4 } from "./utils.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
tags,
|
tags,
|
||||||
@ -153,6 +153,7 @@ function addTagToMap(tagId, characterId = null) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tag_map[key].push(tagId);
|
tag_map[key].push(tagId);
|
||||||
|
tag_map[key] = tag_map[key].filter(onlyUnique);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user