add context menu editor
This commit is contained in:
parent
5e4dc388eb
commit
af2b108730
|
@ -0,0 +1,21 @@
|
||||||
|
<div id="quickReply_contextMenuEditor_template">
|
||||||
|
<div class="quickReply_contextMenuEditor">
|
||||||
|
<h3><strong>Quick Reply Context Menu Editor</strong></h3>
|
||||||
|
<div id="quickReply_contextMenuEditor_content">
|
||||||
|
<template id="quickReply_contextMenuEditor_itemTemplate">
|
||||||
|
<div class="quickReplyContextMenuEditor_item flex-container alignitemscenter" data-order="0">
|
||||||
|
<span class="drag-handle ui-sortable-handle">☰</span>
|
||||||
|
<select class="quickReply_contextMenuEditor_preset"></select>
|
||||||
|
<label class="flex-container" title="When enabled, the current Quick Reply will be sent together with (before) the clicked QR from the context menu.">
|
||||||
|
Chaining:
|
||||||
|
<input type="checkbox" class="quickReply_contextMenuEditor_chaining">
|
||||||
|
</label>
|
||||||
|
<span class="quickReply_contextMenuEditor_remove menu_button menu_button_icon fa-solid fa-trash-can" title="Remove entry"></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="quickReply_contextMenuEditor_actions">
|
||||||
|
<span id="quickReply_contextMenuEditor_addPreset" class="menu_button menu_button_icon fa-solid fa-plus" title="Add preset to context menu"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -103,14 +103,69 @@ function onQuickReplyInput(id) {
|
||||||
function onQuickReplyLabelInput(id) {
|
function onQuickReplyLabelInput(id) {
|
||||||
extension_settings.quickReply.quickReplySlots[id - 1].label = $(`#quickReply${id}Label`).val();
|
extension_settings.quickReply.quickReplySlots[id - 1].label = $(`#quickReply${id}Label`).val();
|
||||||
let quickReplyLabel = extension_settings.quickReply.quickReplySlots[id - 1]?.label || '';
|
let quickReplyLabel = extension_settings.quickReply.quickReplySlots[id - 1]?.label || '';
|
||||||
const parts = quickReplyLabel.split('...');
|
$(`#quickReply${id}`).text(quickReplyLabel + (extension_settings.quickReply.quickReplySlots[id - 1]?.contextMenu?.length?'…':''));
|
||||||
if (parts.length > 1) {
|
|
||||||
quickReplyLabel = `${parts.shift()}…`;
|
|
||||||
}
|
|
||||||
$(`#quickReply${id}`).text(quickReplyLabel);
|
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onQuickReplyContextMenuChange(id) {
|
||||||
|
extension_settings.quickReply.quickReplySlots[id - 1].contextMenu = JSON.parse($(`#quickReplyContainer > [data-order="${id}"]`).attr('data-contextMenu'))
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onQuickReplyCtxButtonClick(id) {
|
||||||
|
const editorHtml = $(await $.get('scripts/extensions/quick-reply/contextMenuEditor.html'));
|
||||||
|
const popupResult = callPopup(editorHtml, "confirm", undefined, { okButton: "Save", wide:false, large:false, rows: 1 });
|
||||||
|
const qr = extension_settings.quickReply.quickReplySlots[id - 1];
|
||||||
|
if (!qr.contextMenu) {
|
||||||
|
qr.contextMenu = [];
|
||||||
|
}
|
||||||
|
/**@type {HTMLTemplateElement}*/
|
||||||
|
const tpl = document.querySelector('#quickReply_contextMenuEditor_itemTemplate');
|
||||||
|
const fillPresetSelect = (select, item) => {
|
||||||
|
[{name:'Select a preset', value:''}, ...presets].forEach(preset=>{
|
||||||
|
const opt = document.createElement('option'); {
|
||||||
|
opt.value = preset.value ?? preset.name;
|
||||||
|
opt.textContent = preset.name;
|
||||||
|
opt.selected = preset.name == item.preset;
|
||||||
|
select.append(opt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const addCtxItem = (item, idx) => {
|
||||||
|
const dom = tpl.content.cloneNode(true);
|
||||||
|
const ctxItem = dom.querySelector('.quickReplyContextMenuEditor_item');
|
||||||
|
ctxItem.setAttribute('data-order', idx);
|
||||||
|
const select = ctxItem.querySelector('.quickReply_contextMenuEditor_preset');
|
||||||
|
fillPresetSelect(select, item);
|
||||||
|
dom.querySelector('.quickReply_contextMenuEditor_chaining').checked = item.chain;
|
||||||
|
$('.quickReply_contextMenuEditor_remove', ctxItem).on('click', ()=>ctxItem.remove());
|
||||||
|
document.querySelector('#quickReply_contextMenuEditor_content').append(ctxItem);
|
||||||
|
}
|
||||||
|
[...qr.contextMenu, {}].forEach((item,idx)=>{
|
||||||
|
addCtxItem(item, idx)
|
||||||
|
});
|
||||||
|
$('#quickReply_contextMenuEditor_addPreset').on('click', ()=>{
|
||||||
|
addCtxItem({}, document.querySelector('#quickReply_contextMenuEditor_content').children.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#quickReply_contextMenuEditor_content').sortable({
|
||||||
|
delay: getSortableDelay(),
|
||||||
|
stop: ()=>{},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (await popupResult) {
|
||||||
|
qr.contextMenu = Array.from(document.querySelectorAll('#quickReply_contextMenuEditor_content > .quickReplyContextMenuEditor_item'))
|
||||||
|
.map(item=>({
|
||||||
|
preset: item.querySelector('.quickReply_contextMenuEditor_preset').value,
|
||||||
|
chain: item.querySelector('.quickReply_contextMenuEditor_chaining').checked,
|
||||||
|
}))
|
||||||
|
.filter(item=>item.preset);
|
||||||
|
$(`#quickReplyContainer[data-order="${id}"]`).attr('data-contextMenu', JSON.stringify(qr.contextMenu));
|
||||||
|
updateQuickReplyPreset();
|
||||||
|
onQuickReplyLabelInput(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function onQuickReplyEnabledInput() {
|
async function onQuickReplyEnabledInput() {
|
||||||
let isEnabled = $(this).prop('checked')
|
let isEnabled = $(this).prop('checked')
|
||||||
extension_settings.quickReply.quickReplyEnabled = !!isEnabled;
|
extension_settings.quickReply.quickReplyEnabled = !!isEnabled;
|
||||||
|
@ -186,40 +241,33 @@ function buildContextMenu(qr, chainMes=null, hierarchy=[], labelHierarchy=[]) {
|
||||||
mes: (chainMes&&qr.mes ? `${chainMes} | ` : '') + qr.mes,
|
mes: (chainMes&&qr.mes ? `${chainMes} | ` : '') + qr.mes,
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
const parts = qr.label.split('...');
|
qr.contextMenu?.forEach(ctxItem=>{
|
||||||
if (parts.length > 1) {
|
let chain = ctxItem.chain;
|
||||||
tree.label = parts.shift();
|
let subName = ctxItem.preset;
|
||||||
parts.forEach(subName=>{
|
const sub = presets.find(it=>it.name == subName);
|
||||||
let chain = false;
|
if (sub) {
|
||||||
if (subName[0] == '!') {
|
// prevent circular references
|
||||||
chain = true;
|
if (hierarchy.indexOf(sub.name) == -1) {
|
||||||
subName = subName.substring(1);
|
const nextHierarchy = [...hierarchy, sub.name];
|
||||||
|
const nextLabelHierarchy = [...labelHierarchy, tree.label];
|
||||||
|
tree.children.push(new MenuHeader(sub.name));
|
||||||
|
sub.quickReplySlots.forEach(subQr=>{
|
||||||
|
const subInfo = buildContextMenu(subQr, chain?tree.mes:null, nextHierarchy, nextLabelHierarchy);
|
||||||
|
tree.children.push(new MenuItem(
|
||||||
|
subInfo.label,
|
||||||
|
subInfo.mes,
|
||||||
|
(evt)=>{
|
||||||
|
evt.stopPropagation();
|
||||||
|
performQuickReply(subInfo.mes.replace(/%%parent(-\d+)?%%/g, (_, index)=>{
|
||||||
|
return nextLabelHierarchy.slice(parseInt(index ?? '-1'))[0];
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
subInfo.children,
|
||||||
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const sub = presets.find(it=>it.name == subName);
|
}
|
||||||
if (sub) {
|
});
|
||||||
// prevent circular references
|
|
||||||
if (hierarchy.indexOf(sub.name) == -1) {
|
|
||||||
const nextHierarchy = [...hierarchy, sub.name];
|
|
||||||
const nextLabelHierarchy = [...labelHierarchy, tree.label];
|
|
||||||
tree.children.push(new MenuHeader(sub.name));
|
|
||||||
sub.quickReplySlots.forEach(subQr=>{
|
|
||||||
const subInfo = buildContextMenu(subQr, chain?tree.mes:null, nextHierarchy, nextLabelHierarchy);
|
|
||||||
tree.children.push(new MenuItem(
|
|
||||||
subInfo.label,
|
|
||||||
subInfo.mes,
|
|
||||||
(evt)=>{
|
|
||||||
evt.stopPropagation();
|
|
||||||
performQuickReply(subInfo.mes.replace(/%%parent(-\d+)?%%/g, (_, index)=>{
|
|
||||||
return nextLabelHierarchy.slice(parseInt(index ?? '-1'))[0];
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
subInfo.children,
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
function addQuickReplyBar() {
|
function addQuickReplyBar() {
|
||||||
|
@ -229,9 +277,8 @@ function addQuickReplyBar() {
|
||||||
for (let i = 0; i < extension_settings.quickReply.numberOfSlots; i++) {
|
for (let i = 0; i < extension_settings.quickReply.numberOfSlots; i++) {
|
||||||
let quickReplyMes = extension_settings.quickReply.quickReplySlots[i]?.mes || '';
|
let quickReplyMes = extension_settings.quickReply.quickReplySlots[i]?.mes || '';
|
||||||
let quickReplyLabel = extension_settings.quickReply.quickReplySlots[i]?.label || '';
|
let quickReplyLabel = extension_settings.quickReply.quickReplySlots[i]?.label || '';
|
||||||
const parts = quickReplyLabel.split('...');
|
if (extension_settings.quickReply.quickReplySlots[i]?.contextMenu?.length) {
|
||||||
if (parts.length > 1) {
|
quickReplyLabel = `${quickReplyLabel}…`;
|
||||||
quickReplyLabel = `${parts.shift()}…`;
|
|
||||||
}
|
}
|
||||||
quickReplyButtonHtml += `<div title="${quickReplyMes}" class="quickReplyButton" data-index="${i}" id="quickReply${i + 1}">${quickReplyLabel}</div>`;
|
quickReplyButtonHtml += `<div title="${quickReplyMes}" class="quickReplyButton" data-index="${i}" id="quickReply${i + 1}">${quickReplyLabel}</div>`;
|
||||||
}
|
}
|
||||||
|
@ -251,12 +298,14 @@ function addQuickReplyBar() {
|
||||||
sendQuickReply(index);
|
sendQuickReply(index);
|
||||||
});
|
});
|
||||||
$('.quickReplyButton').on('contextmenu', function (evt) {
|
$('.quickReplyButton').on('contextmenu', function (evt) {
|
||||||
evt.preventDefault();
|
|
||||||
let index = $(this).data('index');
|
let index = $(this).data('index');
|
||||||
const qr = extension_settings.quickReply.quickReplySlots[index];
|
const qr = extension_settings.quickReply.quickReplySlots[index];
|
||||||
const tree = buildContextMenu(qr);
|
if (qr.contextMenu?.length) {
|
||||||
const menu = new ContextMenu(tree.children);
|
evt.preventDefault();
|
||||||
menu.show(evt);
|
const tree = buildContextMenu(qr);
|
||||||
|
const menu = new ContextMenu(tree.children);
|
||||||
|
menu.show(evt);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,9 +450,10 @@ function generateQuickReplyElements() {
|
||||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||||
let itemNumber = i + 1
|
let itemNumber = i + 1
|
||||||
quickReplyHtml += `
|
quickReplyHtml += `
|
||||||
<div class="flex-container alignitemscenter" data-order="${i}"}>
|
<div class="flex-container alignitemscenter" data-order="${i}">
|
||||||
<span class="drag-handle ui-sortable-handle">☰</span>
|
<span class="drag-handle ui-sortable-handle">☰</span>
|
||||||
<input class="text_pole wide30p" id="quickReply${i}Label" placeholder="(Button label)">
|
<input class="text_pole wide30p" id="quickReply${i}Label" placeholder="(Button label)">
|
||||||
|
<span class="menu_button menu_button_icon" id="quickReply${i}CtxButton" title="Configure context menu">⋮</span>
|
||||||
<textarea id="quickReply${i}Mes" placeholder="(Custom message or /command)" class="text_pole widthUnset flex1 autoSetHeight" rows="2"></textarea>
|
<textarea id="quickReply${i}Mes" placeholder="(Custom message or /command)" class="text_pole widthUnset flex1 autoSetHeight" rows="2"></textarea>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -414,6 +464,8 @@ function generateQuickReplyElements() {
|
||||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||||
$(`#quickReply${i}Mes`).on('input', function () { onQuickReplyInput(i); });
|
$(`#quickReply${i}Mes`).on('input', function () { onQuickReplyInput(i); });
|
||||||
$(`#quickReply${i}Label`).on('input', function () { onQuickReplyLabelInput(i); });
|
$(`#quickReply${i}Label`).on('input', function () { onQuickReplyLabelInput(i); });
|
||||||
|
$(`#quickReply${i}CtxButton`).on('click', function () { onQuickReplyCtxButtonClick(i); });
|
||||||
|
$(`#quickReplyContainer > [data-order="${i}"]`).attr('data-contextMenu', JSON.stringify(extension_settings.quickReply.quickReplySlots[i-1]?.contextMenu??[]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.quickReplySettings .inline-drawer-toggle').off('click').on('click', function () {
|
$('.quickReplySettings .inline-drawer-toggle').off('click').on('click', function () {
|
||||||
|
@ -475,6 +527,7 @@ function saveQROrder() {
|
||||||
//rebuild the extension_Settings array based on new order
|
//rebuild the extension_Settings array based on new order
|
||||||
i = 1
|
i = 1
|
||||||
$('#quickReplyContainer').children().each(function () {
|
$('#quickReplyContainer').children().each(function () {
|
||||||
|
onQuickReplyContextMenuChange(i)
|
||||||
onQuickReplyLabelInput(i)
|
onQuickReplyLabelInput(i)
|
||||||
onQuickReplyInput(i)
|
onQuickReplyInput(i)
|
||||||
i++
|
i++
|
||||||
|
|
Loading…
Reference in New Issue