Merge branch 'staging' into char-cache-limit

This commit is contained in:
Cohee
2024-11-29 12:30:26 +00:00
4 changed files with 73 additions and 24 deletions

View File

@@ -419,30 +419,35 @@ export class SlashCommandHandler {
namedArgumentList: [ namedArgumentList: [
SlashCommandNamedArgument.fromProps({ SlashCommandNamedArgument.fromProps({
name: 'set', name: 'set',
description: 'QR set name', description: 'Name of QR set to add the context menu to',
typeList: [ARGUMENT_TYPE.STRING], typeList: [ARGUMENT_TYPE.STRING],
isRequired: true, isRequired: true,
enumProvider: localEnumProviders.qrSets, enumProvider: localEnumProviders.qrSets,
}), }),
SlashCommandNamedArgument.fromProps({ SlashCommandNamedArgument.fromProps({
name: 'label', name: 'label',
description: 'Quick Reply label', description: 'Label of Quick Reply to add the context menu to',
typeList: [ARGUMENT_TYPE.STRING], typeList: [ARGUMENT_TYPE.STRING],
enumProvider: localEnumProviders.qrEntries, enumProvider: localEnumProviders.qrEntries,
}), }),
SlashCommandNamedArgument.fromProps({ SlashCommandNamedArgument.fromProps({
name: 'id', name: 'id',
description: 'numeric ID of the QR, e.g., id=42', description: 'Numeric ID of Quick Reply to add the context menu to, e.g. id=42',
typeList: [ARGUMENT_TYPE.NUMBER], typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: localEnumProviders.qrIds, enumProvider: localEnumProviders.qrIds,
}), }),
new SlashCommandNamedArgument( new SlashCommandNamedArgument(
'chain', 'boolean', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', 'chain',
'If true, button QR is sent together with (before) the clicked QR from the context menu',
[ARGUMENT_TYPE.BOOLEAN],
false,
false,
'false',
), ),
], ],
unnamedArgumentList: [ unnamedArgumentList: [
SlashCommandArgument.fromProps({ SlashCommandArgument.fromProps({
description: 'QR set name', description: 'Name of QR set to add as a context menu',
typeList: [ARGUMENT_TYPE.STRING], typeList: [ARGUMENT_TYPE.STRING],
isRequired: true, isRequired: true,
enumProvider: localEnumProviders.qrSets, enumProvider: localEnumProviders.qrSets,
@@ -450,13 +455,16 @@ export class SlashCommandHandler {
], ],
helpString: ` helpString: `
<div> <div>
Add context menu preset to a QR. Add a context menu preset to a QR.
</div>
<div>
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
</div> </div>
<div> <div>
<strong>Example:</strong> <strong>Example:</strong>
<ul> <ul>
<li> <li>
<pre><code>/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset</code></pre> <pre><code>/qr-contextadd set=MyQRSetWithTheButton label=MyButton chain=true MyQRSetWithContextItems</code></pre>
</li> </li>
</ul> </ul>
</div> </div>
@@ -470,27 +478,27 @@ export class SlashCommandHandler {
namedArgumentList: [ namedArgumentList: [
SlashCommandNamedArgument.fromProps({ SlashCommandNamedArgument.fromProps({
name: 'set', name: 'set',
description: 'QR set name', description: 'Name of QR set to remove the context menu from',
typeList: [ARGUMENT_TYPE.STRING], typeList: [ARGUMENT_TYPE.STRING],
isRequired: true, isRequired: true,
enumProvider: localEnumProviders.qrSets, enumProvider: localEnumProviders.qrSets,
}), }),
SlashCommandNamedArgument.fromProps({ SlashCommandNamedArgument.fromProps({
name: 'label', name: 'label',
description: 'Quick Reply label', description: 'Label of Quick Reply to remove the context menu from',
typeList: [ARGUMENT_TYPE.STRING], typeList: [ARGUMENT_TYPE.STRING],
enumProvider: localEnumProviders.qrEntries, enumProvider: localEnumProviders.qrEntries,
}), }),
SlashCommandNamedArgument.fromProps({ SlashCommandNamedArgument.fromProps({
name: 'id', name: 'id',
description: 'numeric ID of the QR, e.g., id=42', description: 'Numeric ID of Quick Reply to remove the context menu from, e.g. id=42',
typeList: [ARGUMENT_TYPE.NUMBER], typeList: [ARGUMENT_TYPE.NUMBER],
enumProvider: localEnumProviders.qrIds, enumProvider: localEnumProviders.qrIds,
}), }),
], ],
unnamedArgumentList: [ unnamedArgumentList: [
SlashCommandArgument.fromProps({ SlashCommandArgument.fromProps({
description: 'QR set name', description: 'Name of QR set to remove',
typeList: [ARGUMENT_TYPE.STRING], typeList: [ARGUMENT_TYPE.STRING],
isRequired: true, isRequired: true,
enumProvider: localEnumProviders.qrSets, enumProvider: localEnumProviders.qrSets,
@@ -500,6 +508,9 @@ export class SlashCommandHandler {
<div> <div>
Remove context menu preset from a QR. Remove context menu preset from a QR.
</div> </div>
<div>
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
</div>
<div> <div>
<strong>Example:</strong> <strong>Example:</strong>
<ul> <ul>
@@ -541,6 +552,9 @@ export class SlashCommandHandler {
<div> <div>
Remove all context menu presets from a QR. Remove all context menu presets from a QR.
</div> </div>
<div>
If <code>id</code> and a label are both provided, <code>id</code> will be used.
</div>
<div> <div>
<strong>Example:</strong> <strong>Example:</strong>
<ul> <ul>
@@ -908,12 +922,11 @@ export class SlashCommandHandler {
} }
} }
createContextItem(args, name) { createContextItem(args, name) {
try { try {
this.api.createContextItem( this.api.createContextItem(
args.set, args.set,
args.label, args.id !== undefined ? Number(args.id) : args.label,
name, name,
isTrueBoolean(args.chain), isTrueBoolean(args.chain),
); );
@@ -923,14 +936,14 @@ export class SlashCommandHandler {
} }
deleteContextItem(args, name) { deleteContextItem(args, name) {
try { try {
this.api.deleteContextItem(args.set, args.label, name); this.api.deleteContextItem(args.set, args.id !== undefined ? Number(args.id) : args.label, name);
} catch (ex) { } catch (ex) {
toastr.error(ex.message); toastr.error(ex.message);
} }
} }
clearContextMenu(args, label) { clearContextMenu(args, label) {
try { try {
this.api.clearContextMenu(args.set, args.label ?? label); this.api.clearContextMenu(args.set, args.id !== undefined ? Number(args.id) : args.label ?? label);
} catch (ex) { } catch (ex) {
toastr.error(ex.message); toastr.error(ex.message);
} }

View File

@@ -19,7 +19,7 @@ export class ContextMenu {
this.itemList = this.build(qr).children; this.itemList = this.build(qr).children;
this.itemList.forEach(item => { this.itemList.forEach(item => {
item.onExpand = () => { item.onExpand = () => {
this.itemList.filter(it => it != item) this.itemList.filter(it => it !== item)
.forEach(it => it.collapse()); .forEach(it => it.collapse());
}; };
}); });
@@ -36,7 +36,9 @@ export class ContextMenu {
icon: qr.icon, icon: qr.icon,
showLabel: qr.showLabel, showLabel: qr.showLabel,
label: qr.label, label: qr.label,
title: qr.title,
message: (chainedMessage && qr.message ? `${chainedMessage} | ` : '') + qr.message, message: (chainedMessage && qr.message ? `${chainedMessage} | ` : '') + qr.message,
isHidden: qr.isHidden,
children: [], children: [],
}; };
qr.contextList.forEach((cl) => { qr.contextList.forEach((cl) => {
@@ -51,7 +53,9 @@ export class ContextMenu {
subTree.icon, subTree.icon,
subTree.showLabel, subTree.showLabel,
subTree.label, subTree.label,
subTree.title,
subTree.message, subTree.message,
subTree.isHidden,
(evt) => { (evt) => {
evt.stopPropagation(); evt.stopPropagation();
const finalQr = Object.assign(new QuickReply(), subQr); const finalQr = Object.assign(new QuickReply(), subQr);

View File

@@ -2,7 +2,7 @@ import { MenuItem } from './MenuItem.js';
export class MenuHeader extends MenuItem { export class MenuHeader extends MenuItem {
constructor(/**@type {String}*/label) { constructor(/**@type {String}*/label) {
super(null, null, label, null, null); super(null, null, label, null, null, false, null, []);
} }

View File

@@ -4,11 +4,12 @@ export class MenuItem {
/**@type {string}*/ icon; /**@type {string}*/ icon;
/**@type {boolean}*/ showLabel; /**@type {boolean}*/ showLabel;
/**@type {string}*/ label; /**@type {string}*/ label;
/**@type {string}*/ title;
/**@type {object}*/ value; /**@type {object}*/ value;
/**@type {boolean}*/ isHidden = false;
/**@type {function}*/ callback; /**@type {function}*/ callback;
/**@type {MenuItem[]}*/ childList = []; /**@type {MenuItem[]}*/ childList = [];
/**@type {SubMenu}*/ subMenu; /**@type {SubMenu}*/ subMenu;
/**@type {boolean}*/ isForceExpanded = false;
/**@type {HTMLElement}*/ root; /**@type {HTMLElement}*/ root;
@@ -19,35 +20,67 @@ export class MenuItem {
/** /**
* *
* @param {string} icon * @param {?string} icon
* @param {boolean} showLabel * @param {?boolean} showLabel
* @param {string} label * @param {string} label
* @param {?string} title Tooltip
* @param {object} value * @param {object} value
* @param {boolean} isHidden QR is Invisible (auto-execute only)
* @param {function} callback * @param {function} callback
* @param {MenuItem[]} children * @param {MenuItem[]} children
*/ */
constructor(icon, showLabel, label, value, callback, children = []) { constructor(icon, showLabel, label, title, value, isHidden, callback, children = []) {
this.icon = icon; this.icon = icon;
this.showLabel = showLabel; this.showLabel = showLabel;
this.label = label; this.label = label;
this.title = title;
this.value = value; this.value = value;
this.isHidden = isHidden;
this.callback = callback; this.callback = callback;
this.childList = children; this.childList = children;
} }
/**
* Renders the MenuItem
*
* A .qr--hidden class is added to:
* - the item if it is "Invisible (auto-execute only)"
* - the icon if no icon is set
* - the label if an icon is set and showLabel is false
*
* There is no .qr--hidden class defined in default CSS, since having items
* that are invisible on the QR bar but visible in the context menu,
* or icon-only on the QR bar but labelled in the context menu, is a valid use case.
*
* To hide optional labels when icons are present, add this user CSS:
* .ctx-menu .ctx-item .qr--button-label.qr--hidden {display: none;}
* To hide icons when no icon is present (removes unwanted padding):
* .ctx-menu .ctx-item .qr--button-icon.qr--hidden {display: none;}
* To hide items that are set "invisible":
* .ctx-menu .ctx-item.qr--hidden {display: none;}
* To target submenus only, use .ctx-menu .ctx-sub-menu .qr--hidden {display: none;}
*
* @returns {HTMLElement}
*/
render() { render() {
if (!this.root) { if (!this.root) {
const item = document.createElement('li'); { const item = document.createElement('li'); {
this.root = item; this.root = item;
item.classList.add('list-group-item'); item.classList.add('list-group-item');
item.classList.add('ctx-item'); item.classList.add('ctx-item');
item.title = this.value;
// if this item is Invisible, add the hidden class
if (this.isHidden) item.classList.add('qr--hidden');
// if a title/tooltip is set, add it, otherwise use the QR content
// same as for the main QR list
item.title = this.title || this.value;
if (this.callback) { if (this.callback) {
item.addEventListener('click', (evt) => this.callback(evt, this)); item.addEventListener('click', (evt) => this.callback(evt, this));
} }
const icon = document.createElement('div'); { const icon = document.createElement('div'); {
this.domIcon = icon;
icon.classList.add('qr--button-icon'); icon.classList.add('qr--button-icon');
icon.classList.add('fa-solid'); icon.classList.add('fa-solid');
if (!this.icon) icon.classList.add('qr--hidden'); if (!this.icon) icon.classList.add('qr--hidden');
@@ -55,7 +88,6 @@ export class MenuItem {
item.append(icon); item.append(icon);
} }
const lbl = document.createElement('div'); { const lbl = document.createElement('div'); {
this.domLabel = lbl;
lbl.classList.add('qr--button-label'); lbl.classList.add('qr--button-label');
if (this.icon && !this.showLabel) lbl.classList.add('qr--hidden'); if (this.icon && !this.showLabel) lbl.classList.add('qr--hidden');
lbl.textContent = this.label; lbl.textContent = this.label;