WI custom styling for regex keys

- WI custom style for regex keys
- moved select2 styling to its own file
This commit is contained in:
Wolfsblvt 2024-05-07 05:44:18 +02:00
parent 5a45e64999
commit fda0e886e4
6 changed files with 127 additions and 65 deletions

View File

@ -171,3 +171,73 @@
.select2-results__option.select2-results__message::before {
display: none;
}
.select2-selection__choice__display {
/* Fix weird alignment on the left side */
margin-left: 1px;
}
/* Styling for choice remove icon */
span.select2.select2-container .select2-selection__choice__remove {
cursor: pointer;
transition: background-color 0.3s;
color: var(--SmartThemeBodyColor);
background-color: var(--black50a);
}
span.select2.select2-container .select2-selection__choice__remove:hover {
color: var(--SmartThemeBodyColor);
background-color: var(--white30a);
}
/* Custom class to support styling to show clickable choice options */
.select2_choice_clickable+span.select2-container .select2-selection__choice__display {
cursor: pointer;
transition: background-color 0.3s;
color: var(--SmartThemeBodyColor);
background-color: var(--black50a);
}
.select2_choice_clickable+span.select2-container .select2-selection__choice__display:hover {
background-color: var(--white30a);
}
/* Custom class to support same line multi inputs of select2 controls */
.select2_multi_sameline+span.select2-container .select2-selection--multiple {
display: flex;
flex-wrap: wrap;
}.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
/* Allow search placeholder to take up all space if needed */
flex-grow: 1;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
/* Fix weird styling choice or huge margin around selected options */
margin-block-start: 2px;
margin-block-end: 2px;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search__field {
/* Min height to reserve spacing */
min-height: calc(var(--mainFontSize) + 13px);
/* Min width to be clickable */
min-width: 4em;
align-content: center;
/* Fix search textarea alignment issue with UL elements */
margin-top: 0px;
height: unset;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
/* Min height to reserve spacing */
min-height: calc(var(--mainFontSize) + 13px);
}
/* Make search bar invisible unless the select2 is active, to save space */
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
height: 1px;
}
.select2_multi_sameline+span.select2-container.select2-container--focus .select2-selection--multiple .select2-search--inline {
height: unset;
}

View File

@ -197,20 +197,20 @@
display: none;
}
#world_info+span.select2-container .select2-selection__choice__remove,
#world_info+span.select2-container .select2-selection__choice__display {
cursor: pointer;
transition: background-color 0.3s;
span.select2-container .select2-selection__choice__display:has(> .regex_item),
span.select2-container .select2-results__option:has(> .regex_item) {
background-color: #D27D2D30;
}
.regex_item .regex_icon {
background-color: var(--black30a);
color: var(--SmartThemeBodyColor);
background-color: var(--black50a);
}
#world_info+span.select2-container .select2-selection__choice__display {
/* Fix weird alignment on the left side */
margin-left: 1px;
}
#world_info+span.select2-container .select2-selection__choice__remove:hover,
#world_info+span.select2-container .select2-selection__choice__display:hover {
background-color: var(--white30a);
}
border: 1px solid var(--SmartThemeBorderColor);
border-radius: 7px;
font-weight: bold;
font-size: calc(var(--mainFontSize) * 0.75);
padding: 0px 3px;
position: relative;
top: -1px;
margin-right: 3px;
}

View File

@ -3310,7 +3310,7 @@
<span data-i18n="Active World(s) for all chats"><small>Active World(s) for all chats</small></span>
</div>
<div class="range-block-range">
<select id="world_info" class="select2_multi_sameline" multiple>
<select id="world_info" class="select2_multi_sameline select2_choice_clickable" multiple>
<option value="" data-i18n="-- World Info not found --">-- World Info not found -- </option>
</select>
</div>

View File

@ -1450,6 +1450,18 @@ export function includesIgnoreCaseAndAccents(text, searchTerm) {
return normalizedText.includes(normalizedSearchTerm);
}
/**
* Returns a unique hash as ID for a select2 option text
*
* @param {string} option - The option
* @returns {string} A hashed version of that option
*/
export function getSelect2OptionId(option) {
return String(getStringHash(option));
}
/**
* Modifies the select2 options by adding not existing one and optionally selecting them
*
@ -1462,7 +1474,7 @@ export function includesIgnoreCaseAndAccents(text, searchTerm) {
export function select2ModifyOptions(element, items, { select = false, changeEventArgs = null } = {}) {
if (!items.length) return;
/** @type {{id: string, text: string}[]} */
const dataItems = items.map(x => typeof x === 'string' ? { id: x, text: x } : x);
const dataItems = items.map(x => typeof x === 'string' ? { id: getSelect2OptionId(x), text: x } : x);
dataItems.forEach(item => {
// Set the value, creating a new option if necessary

View File

@ -1,5 +1,5 @@
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js';
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions } from './utils.js';
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getStringHash, getSelect2OptionId } from './utils.js';
import { extension_settings, getContext } from './extensions.js';
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
import { registerSlashCommand } from './slash-commands.js';
@ -1146,6 +1146,12 @@ function deleteOriginalDataValue(data, uid) {
}
}
/**
* @typedef {object} Select2Option The option object for select2 controls
* @property {string} id - The unique ID inside this select
* @property {string} text - The text for this option
*/
/**
* Splits a given input string that contains one or more keywords or regexes, separated by commas.
*
@ -1162,12 +1168,12 @@ function splitKeywordsAndRegexes(input) {
// We can make this easy. Instead of writing another function to find and parse regexes,
// we gonna utilize the custom tokenizer that also handles the input.
// No need for validation here
const addFindCallback = (/** @type {{id: string, text: string}} */ item) => {
keywordsAndRegexes.push(item.id);
const addFindCallback = (/** @type {Select2Option} */ item) => {
keywordsAndRegexes.push(item.text);
}
const { term } = customTokenizer({ _type: 'custom_call', term: input }, undefined, addFindCallback);
addFindCallback({ id: term.trim(), text: term.trim() });
addFindCallback({ id: getSelect2OptionId(term.trim()), text: term.trim() });
return keywordsAndRegexes;
}
@ -1177,7 +1183,7 @@ function splitKeywordsAndRegexes(input) {
*
* @param {{_type: string, term: string}} input - The typed input
* @param {{options: object}} _selection - The selection even object (?)
* @param {function} callback - The original callback function to call if an item should be inserted
* @param {function(Select2Option):void} callback - The original callback function to call if an item should be inserted
* @returns {{term: string}} - The remaining part that is untokenized in the textbox
*/
function customTokenizer(input, _selection, callback) {
@ -1205,12 +1211,14 @@ function customTokenizer(input, _selection, callback) {
// So now the comma really means the token is done.
// We take the token up till now, and insert it. Empty will be skipped.
if (token) {
const isRegex = isValidRegex(token);
// Last chance to check for valid regex again. Because it might have been valid while typing, but now is not valid anymore and contains commas we need to split.
if (token.startsWith('/') && !isValidRegex(token)) {
if (token.startsWith('/') && !isRegex) {
const tokens = token.split(',').map(x => x.trim());
tokens.forEach(x => callback({ id: x, text: x }));
} else {
callback({ id: token, text: token });
callback({ id: getSelect2OptionId(token), text: token });
}
}
@ -1289,17 +1297,26 @@ function getWorldEntry(name, data, entry) {
event.stopPropagation();
});
function templateStyling(/** @type {Select2Option} */ item) {
const isRegex = isValidRegex(item.text);
if (!isRegex) return item.text;
return $('<span>').addClass('regex_item').text(item.text)
.prepend($('<span>').addClass('regex_icon').text("•*").attr('title', 'Regex'));
}
if (!isMobile()) {
input.select2({
tags: true,
tokenSeparators: [','],
tokenizer: customTokenizer,
placeholder: input.attr('placeholder'),
templateResult: templateStyling,
templateSelection: templateStyling,
});
input.on('change', function (_, { skipReset, noSave } = {}) {
const uid = $(this).data('uid');
/** @type {string[]} */
const keys = ($(this).select2('data')).map(x => x.id);
const keys = ($(this).select2('data')).map(x => x.text);
!skipReset && resetScrollHeight(this);
if (!noSave) {
@ -3421,8 +3438,9 @@ jQuery(() => {
if ($(event.target).hasClass('select2-selection__choice__display')) {
event.preventDefault();
// select2 still bubbles the event to open the dropdown. So we close it here
// select2 still bubbles the event to open the dropdown. So we close it here and remove focus
$('#world_info').select2('close');
setTimeout(() => $('#world_info + span.select2-container textarea').trigger('blur'), debounce_timeout.quick);
const name = $(event.target).text();
const selectedIndex = world_names.indexOf(name);

View File

@ -4035,42 +4035,4 @@ body:not(.movingUI) .drawer-content.maximized {
height: 100vh;
z-index: 9999;
}
}
/* Custom class to support same line multi inputs of select2 controls */
.select2_multi_sameline+span.select2-container .select2-selection--multiple {
display: flex;
flex-wrap: wrap;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
flex-grow: 1; /* Allow search placeholder to take up all space if needed */
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
/* Fix weird styling choice or huge margin around selected options */
margin-block-start: 2px;
margin-block-end: 2px;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search__field {
min-height: calc(var(--mainFontSize) + 13px);
min-width: 4em;
align-content: center;
/* Fix search textarea alignment issue with UL elements */
margin-top: 0px;
height: unset;
}
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
min-height: calc(var(--mainFontSize) + 13px);
}
/* Make search bar invisible unless the select2 is active, to save space */
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
height: 1px;
}
.select2_multi_sameline+span.select2-container.select2-container--open .select2-selection--multiple .select2-search--inline {
height: unset;
}