Merge pull request #1337 from artisticMink/hotfix/bulk-edit-enhanced

Fixes for bulk editing overlay
This commit is contained in:
Cohee 2023-11-10 00:10:27 +02:00 committed by GitHub
commit bc2b3e9c4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 36 deletions

View File

@ -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 {
transition: background-color 0.4s ease;
@ -9,6 +5,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 {
display: none !important;
}
@ -26,7 +31,7 @@
#character_context_menu {
position: absolute;
padding: 3px;
z-index: 10000;
z-index: 9998;
background-color: var(--black90a);
border: 1px solid var(--black90a);
border-radius: 10px;

View File

@ -93,11 +93,11 @@
<div id="bg1"></div>
<div id="character_context_menu" class="hidden">
<ul>
<li><button id="character_context_menu_favorite">Favorite</button></li>
<li><button id="character_context_menu_tag">Tag</button></li>
<li><button id="character_context_menu_duplicate">Duplicate</button></li>
<li><button id="character_context_menu_persona">Persona</button></li>
<li><button id="character_context_menu_delete">Delete</button></li>
<li><button id="character_context_menu_favorite" data-i18n="Favorite">Favorite</button></li>
<li><button id="character_context_menu_tag" data-i18n="Tag">Tag</button></li>
<li><button id="character_context_menu_duplicate" data-i18n="Duplicate">Duplicate</button></li>
<li><button id="character_context_menu_persona" data-i18n="Persona">Persona</button></li>
<li><button id="character_context_menu_delete" data-i18n="Delete">Delete</button></li>
</ul>
</div>
<!-- top bar central settings buttons -->

View File

@ -159,6 +159,15 @@ class CharacterContextMenu {
contextMenu.style.top = `${positionY}px`;
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 contextMenuId = 'character_context_menu';
static characterClass = 'character_select';
static groupClass = 'group_select';
static selectModeClass = 'group_overlay_mode_select';
static selectedClass = 'character_selected';
static legacySelectedClass = 'bulk_select_checkbox';
@ -288,6 +298,20 @@ class BulkEditOverlay {
#stateChangeCallbacks = [];
#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
*/
@ -332,7 +356,6 @@ class BulkEditOverlay {
return bulkEditOverlayInstance
this.container = document.getElementById(BulkEditOverlay.containerId);
this.container.addEventListener('click', this.handleCancelClick);
eventSource.on(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER, this.handleStateChange);
bulkEditOverlayInstance = Object.freeze(this);
@ -364,8 +387,9 @@ class BulkEditOverlay {
elements.forEach(element => element.addEventListener('dragend', this.handleLongPressEnd));
elements.forEach(element => element.addEventListener('touchmove', this.handleLongPressEnd));
const grid = document.getElementById(BulkEditOverlay.containerId);
grid.addEventListener('click', this.handleCancelClick);
// Cohee: It only triggers when clicking on a margin between the elements?
// 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) {
case BulkEditOverlayState.browse:
this.container.classList.remove(BulkEditOverlay.selectModeClass);
this.#contextMenuOpen = false;
this.#enableClickEventsForCharacters();
this.#enableClickEventsForGroups();
this.clearSelectedCharacters();
this.disableContextMenu();
this.#disableBulkEditButtonHighlight();
@ -386,6 +412,7 @@ class BulkEditOverlay {
case BulkEditOverlayState.select:
this.container.classList.add(BulkEditOverlay.selectModeClass);
this.#disableClickEventsForCharacters();
this.#disableClickEventsForGroups();
this.enableContextMenu();
this.#enableBulkEditButtonHighlight();
break;
@ -399,7 +426,7 @@ class BulkEditOverlay {
* set a click event to hide the custom context menu.
*/
enableContextMenu = () => {
document.getElementById('rm_print_characters_block').addEventListener('contextmenu', this.handleContextMenuShow);
this.container.addEventListener('contextmenu', this.handleContextMenuShow);
document.addEventListener('click', this.handleContextMenuHide);
}
@ -408,7 +435,7 @@ class BulkEditOverlay {
* menu to be opened.
*/
disableContextMenu = () => {
document.getElementById('rm_print_characters_block').removeEventListener('contextmenu', this.handleContextMenuShow);
this.container.removeEventListener('contextmenu', this.handleContextMenuShow);
document.removeEventListener('click', this.handleContextMenuHide);
}
@ -420,26 +447,52 @@ class BulkEditOverlay {
}
}
/**
* Opens menu on long-press.
*
* @param event - Pointer event
*/
handleHold = (event) => {
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;
setTimeout(() => {
if (this.isLongPress) {
if (this.state === BulkEditOverlayState.browse)
this.selectState();
else if (this.state === BulkEditOverlayState.select)
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
}
}, BulkEditOverlay.longPressDelay);
if (this.isLongPress && !cancel) {
if (this.state === BulkEditOverlayState.browse) {
this.selectState();
} else if (this.state === BulkEditOverlayState.select) {
this.#contextMenuOpen = true;
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
}
}
this.container.removeEventListener('mouseup', cancelHold);
this.container.removeEventListener('touchend', cancelHold);
},
BulkEditOverlay.longPressDelay);
}
handleLongPressEnd = () => {
handleLongPressEnd = (event) => {
this.isLongPress = false;
if (this.#contextMenuOpen) event.stopPropagation();
}
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,
];
#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));
#disableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.addEventListener('click', this.toggleCharacterSelected));
@ -463,6 +527,8 @@ class BulkEditOverlay {
#getEnabledElements = () => [...this.container.getElementsByClassName(BulkEditOverlay.characterClass)];
#getDisabledElements = () =>[...this.container.getElementsByClassName(BulkEditOverlay.groupClass)];
toggleCharacterSelected = event => {
event.stopPropagation();
@ -473,25 +539,28 @@ class BulkEditOverlay {
const legacyBulkEditCheckbox = character.querySelector('.' + BulkEditOverlay.legacySelectedClass);
if (alreadySelected) {
character.classList.remove(BulkEditOverlay.selectedClass);
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = false;
this.dismissCharacter(characterId);
} else {
character.classList.add(BulkEditOverlay.selectedClass)
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = true;
this.selectCharacter(characterId);
}
// Only toggle when context menu is closed and wasn't just closed.
if (!this.#contextMenuOpen && !this.#cancelNextToggle)
if (alreadySelected) {
character.classList.remove(BulkEditOverlay.selectedClass);
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = false;
this.dismissCharacter(characterId);
} else {
character.classList.add(BulkEditOverlay.selectedClass)
if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = true;
this.selectCharacter(characterId);
}
this.#cancelNextToggle = false;
}
handleContextMenuShow = (event) => {
event.preventDefault();
document.getElementById(BulkEditOverlay.containerId).style.pointerEvents = 'none';
CharacterContextMenu.show(...this.#getContextMenuPosition(event));
this.#contextMenuOpen = true;
}
handleContextMenuHide = (event) => {
document.getElementById(BulkEditOverlay.containerId).style.pointerEvents = '';
let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId);
if (false === contextMenu.contains(event.target)) {
CharacterContextMenu.hide();

View File

@ -10,7 +10,7 @@ import {
import { FILTER_TYPES, FilterHelper } from "./filters.js";
import { groupCandidatesFilter, selected_group } from "./group-chats.js";
import { uuidv4 } from "./utils.js";
import { onlyUnique, uuidv4 } from "./utils.js";
export {
tags,
@ -153,6 +153,7 @@ function addTagToMap(tagId, characterId = null) {
}
else {
tag_map[key].push(tagId);
tag_map[key] = tag_map[key].filter(onlyUnique);
}
}