diff --git a/menu/satus.js b/menu/satus.js index 3e128c808..33e09b0e4 100644 --- a/menu/satus.js +++ b/menu/satus.js @@ -562,6 +562,10 @@ satus.toIndex = function(index, child, parent) { satus.on = function(element, listeners) { if (listeners) { for (var type in listeners) { + if (type == 'parentObject') { + continue; + } + var listener = listeners[type]; if (type === 'selectionchange') { @@ -742,52 +746,53 @@ satus.render = function(skeleton, container, property, childrenOnly, prepend, sk this.properties(element, skeleton.properties); this.on(element, skeleton.on); - element.storage = (function() { - var parent = element, - key = skeleton.storage || property || false, - value; - - if (satus.isFunction(key)) { - key = key(); - } - - if (skeleton.storage !== false) { - if (key) { - value = satus.storage.get(key); - } - - if (skeleton.hasOwnProperty('value') && value === undefined) { - value = skeleton.value; + // dont add storage component to storage: false elements + if (skeleton.storage != false) { + element.storage = (function() { + var parent = element, + key = skeleton.storage || property || false, + value; + + if (satus.isFunction(key)) { + key = key(); } - } - - return Object.defineProperties({}, { - key: { - get: function() { - return key; - }, - set: function(string) { - key = string; + + if (skeleton.storage !== false) { + if (key) { + value = satus.storage.get(key); } - }, - value: { - get: function() { - return value; + + if (skeleton.hasOwnProperty('value') && value === undefined) { + value = skeleton.value; + } + } + + return Object.defineProperties({}, { + key: { + get: function() { + return key; + }, + set: function(string) { + key = string; + } }, - set: function(val) { - value = val; - - if (satus.storage.get(key) != val) { - if (skeleton.storage !== false) { + value: { + get: function() { + return value; + }, + set: function(val) { + value = val; + + if (satus.storage.get(key) != val) { satus.storage.set(key, val); + + parent.dispatchEvent(new CustomEvent('change')); } - - parent.dispatchEvent(new CustomEvent('change')); } } - } - }); - }()); + }); + }()); + } if (this.components[camelizedTagName]) { this.components[camelizedTagName](element, skeleton); @@ -1119,11 +1124,36 @@ satus.components.modal = function(component, skeleton) { }; component.scrim.addEventListener('click', function() { - // this is someone clicking outside of modal dialog, try cancel() first if default modal.confirm - if (skeleton.cancel && satus.isFunction(skeleton.cancel)) { - skeleton.cancel(); + // this is someone clicking outside of modal dialog + switch (skeleton.variant) { + case 'confirm': + if (skeleton.buttons?.cancel) { + // modal.confirm.buttons variant have own closing mechanism, lets try to click cancel button + if (skeleton.buttons.cancel?.rendered?.click && satus.isFunction(skeleton.buttons.cancel.rendered.click)) { + skeleton.buttons.cancel.rendered.click(); + } else { + // cant find cancel button, just force close it + this.parentNode.close(); + } + } else { + // modal.confirm simplified variant, try optional cancel() then close() + if (skeleton.cancel && satus.isFunction(skeleton.cancel)) { + skeleton.cancel(); + } + this.parentNode.close(); + } + break; + + case 'vertical-menu': + this.parentNode.close(); + break; + + case 'shortcut': + case 'color-picker': + // click cancel button + skeleton.actions.cancel.rendered.click(); + break; } - this.parentNode.close(); }); if (satus.isset(skeleton.content)) { @@ -1169,9 +1199,10 @@ satus.components.modal.confirm = function(component, skeleton) { }, on: { click: function() { - // no listeners for this Event currently exist in the codebase - this.modalProvider.dispatchEvent(new CustomEvent('cancel')); - this.modalProvider.skeleton.cancel(); + // cancel() is optional in modal.confirm simplified variant + if (this.modalProvider.skeleton.cancel && satus.isFunction(this.modalProvider.skeleton.cancel)) { + this.modalProvider.skeleton.cancel(); + } this.modalProvider.close(); } } @@ -1184,9 +1215,10 @@ satus.components.modal.confirm = function(component, skeleton) { }, on: { click: function() { - // no listeners for this Event currently exist in the codebase - this.modalProvider.dispatchEvent(new CustomEvent('confirm')); - this.modalProvider.skeleton.ok(); + // ok() is optional in modal.confirm simplified variant + if (this.modalProvider.skeleton.ok && satus.isFunction(this.modalProvider.skeleton.ok)) { + this.modalProvider.skeleton.ok(); + } this.modalProvider.close(); } } @@ -1303,6 +1335,8 @@ satus.components.textField = function(component, skeleton) { }, set: function(value) { this.input.value = value; + + this.dispatchEvent(new CustomEvent('change')); } }); @@ -1310,6 +1344,10 @@ satus.components.textField = function(component, skeleton) { component.syntax.set(skeleton.syntax); } + if (component.skeleton.storage) { + component.value = component.storage.value; + } + selection.setAttribute('disabled', ''); line_numbers.update = function() { @@ -1409,16 +1447,25 @@ satus.components.textField = function(component, skeleton) { component.hiddenValue.textContent = ''; }; - document.addEventListener('selectionchange', function(event) { + // global listener, make sure we remove when element no longer exists + function selectionchange(event) { + if (!document.body.contains(component)) { + document.removeEventListener('selectionchange', selectionchange); + return; + } component.lineNumbers.update(); component.pre.update(); component.cursor.update(); - }); + }; + + document.addEventListener('selectionchange', selectionchange); input.addEventListener('input', function() { var component = this.parentNode.parentNode; - component.storage.value = this.value; + if (component.skeleton.storage) { + component.storage.value = this.value; + } component.lineNumbers.update(); component.pre.update(); @@ -1442,21 +1489,11 @@ satus.components.textField = function(component, skeleton) { this.cursor.update(); }); - component.value = component.storage.value || ''; - component.addEventListener('render', function() { component.lineNumbers.update(); component.pre.update(); component.cursor.update(); }); - - if (skeleton.on) { - for (var type in skeleton.on) { - input.addEventListener(type, function(event) { - this.parentNode.parentNode.dispatchEvent(new Event(event.type)); - }); - } - } }; /*-------------------------------------------------------------- >>> CHART @@ -1871,7 +1908,7 @@ satus.components.colorPicker = function(component, skeleton) { var modal = this.skeleton.parentSkeleton.parentSkeleton, hsl = modal.value; - hsl[0] = this.storage.value; + hsl[0] = this.value; this.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg,' + hsl[1] + '%, ' + hsl[2] + '%)'; this.parentNode.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg, 100%, 50%)'; @@ -1923,7 +1960,7 @@ satus.components.colorPicker = function(component, skeleton) { } } } - }, this.baseProvider.layers[0]); + }, this.baseProvider); }); }; /*-------------------------------------------------------------- @@ -1986,13 +2023,12 @@ satus.components.slider = function(component, skeleton) { input.min = skeleton.min || 0; input.max = skeleton.max || 1; input.step = skeleton.step || 1; - input.value = component.storage.value || skeleton.value || 0; + input.value = component.storage?.value || skeleton.value || 0; text_input.addEventListener('blur', function() { var component = this.parentNode.parentNode; component.input.value = Number(this.value.replace(/[^0-9.]/g, '')); - component.storage.value = Number(component.input.value); component.update(); }); @@ -2002,7 +2038,6 @@ satus.components.slider = function(component, skeleton) { var component = this.parentNode.parentNode; component.input.value = Number(this.value.replace(/[^0-9.]/g, '')); - component.storage.value = Number(component.input.value); component.update(); } @@ -2011,7 +2046,7 @@ satus.components.slider = function(component, skeleton) { input.addEventListener('input', function() { var component = this.parentNode.parentNode; - component.storage.value = Number(this.value); + component.value = Number(this.value); component.update(); }); @@ -2245,6 +2280,7 @@ satus.components.shortcut = function(component, skeleton) { component.addEventListener('click', function() { satus.render({ component: 'modal', + variant: 'shortcut', properties: { parent: this }, @@ -3131,7 +3167,7 @@ satus.user.device.connection = function() { --------------------------------------------------------------*/ satus.search = function(query, object, callback) { - var elements = ['switch', 'select', 'slider', 'shortcut', 'radio', 'color-picker', 'label'], + var elements = ['switch', 'select', 'slider', 'shortcut', 'radio', 'color-picker', 'label', 'button'], threads = 0, results = {}, excluded = [ @@ -3150,12 +3186,18 @@ satus.search = function(query, object, callback) { function parse(items, parent) { threads++; - for (var key in items) { - if (excluded.indexOf(key) === -1) { + for (const key in items) { + if (!excluded.includes(key)) { var item = items[key]; - if (item.component && item.text && elements.indexOf(item.component) !== -1 - && (satus.locale.data[item.text] ? satus.locale.data[item.text] : item.text).toLowerCase().indexOf(query) !== -1) { + if (item.component && item.text + // list of elements we allow search on + && elements.includes(item.component) + // only pass buttons whose parents are variant: 'card' or special case 'appearance' (this one abuses variant tag for CSS) + && (item.component != 'button' || item.parentObject?.variant == "card" || item.parentObject?.variant == "appearance") + // try to match query against localized description, fallback on component name + && (satus.locale.data[item.text] ? satus.locale.data[item.text] : item.text).toLowerCase().includes(query)) { + // plop matching results in array - this means we cant have two elements with same name in results results[key] = Object.assign({}, item); } diff --git a/menu/skeleton-parts/appearance.js b/menu/skeleton-parts/appearance.js index d0958163b..f1cc70b07 100644 --- a/menu/skeleton-parts/appearance.js +++ b/menu/skeleton-parts/appearance.js @@ -683,9 +683,10 @@ extension.skeleton.main.layers.section.appearance.on.click.comments = { component: "section", variant: "card", - comments: { + comments_show: { component: "select", text: "comments", + storage: 'comments', options: [{ text: "normal", diff --git a/menu/skeleton-parts/search.js b/menu/skeleton-parts/search.js index 1730a5a95..03ecd0d0c 100644 --- a/menu/skeleton-parts/search.js +++ b/menu/skeleton-parts/search.js @@ -14,10 +14,14 @@ extension.skeleton.header.sectionEnd.search.on.click = { on: { render: function () { this.focus(); + if (extension.search) { + this.value = extension.search; + this.dispatchEvent(new CustomEvent('input')); + } }, blur: function () { if (this.value.length === 0) { - var search_results = document.querySelector('.search-results'); + let search_results = document.querySelector('.search-results'); if (search_results) { search_results.close(); @@ -26,56 +30,39 @@ extension.skeleton.header.sectionEnd.search.on.click = { this.remove(); } }, - keydown: function (event) { - var self = this; - - setTimeout(function () { - if (self.storage.value.length === 0 && event.key === 'Backspace') { - var search_results = document.querySelector('.search-results'); - - if (search_results) { - search_results.close(); - } - - self.baseProvider.classList.remove('search-mode'); - } - }); - }, input: function (event) { - var self = this, + let self = this, value = this.value.trim(); + extension.search = value; + if (value.length > 0) { satus.search(value, extension.skeleton, function (results) { - var search_results = document.querySelector('.search-results'), + let search_results = document.querySelector('.search-results'), skeleton = { component: 'modal', class: 'search-results' }; - for (var key in results) { - var result = results[key], - parent = result; + for (let key in results) { + let result = results[key], + parent = result, + category, + subcategory, + text; + + while (parent.parentObject && !parent.parentObject.category) { - while ( - parent.parentObject && - !parent.parentObject.category - ) { parent = parent.parentObject; } - var category = ''; - if (parent.parentObject && parent.parentObject.label && parent.parentObject.label.text) { category = parent.parentObject.label.text; } parent = result; - while ( - parent.parentObject && - parent.parentObject.component !== 'button' - ) { + while (parent.parentObject && parent.parentObject.component !== 'button') { parent = parent.parentObject; } @@ -83,15 +70,15 @@ extension.skeleton.header.sectionEnd.search.on.click = { if (parent) { if (parent.label) { - var subcategory = parent.label.text; + subcategory = parent.label.text; } else { - var subcategory = parent.text; + subcategory = parent.text; } if (category === subcategory) { - var text = satus.locale.get(category); + text = satus.locale.get(category); } else { - var text = satus.locale.get(category) + ' > ' + satus.locale.get(subcategory); + text = satus.locale.get(category) + ' > ' + satus.locale.get(subcategory); } skeleton[category + subcategory + '_label'] = { @@ -134,7 +121,7 @@ extension.skeleton.header.sectionEnd.search.on.click = { } } else { if (search_results) { - var surface = document.querySelector('.search-results .satus-modal__surface'); + let surface = document.querySelector('.search-results .satus-modal__surface'); satus.empty(surface); @@ -142,26 +129,51 @@ extension.skeleton.header.sectionEnd.search.on.click = { } else { self.setAttribute('results', ''); - satus.render(skeleton, self.baseProvider); + search_results = satus.render(skeleton, self.baseProvider); + + // we need global listener here + function hidesearch(event) { + // make sure to clean it after closing search results + if (!document.body.contains(search_results)) { + document.removeEventListener('click', hidesearch); + } + // hide search results when clicking on result that is a 'button' inside search results + if (search_results.contains(event.target) && event.target.className.includes('satus-button') + // dont close on modal popups + && !(event.target.skeleton?.on?.click?.component == "modal") + // shortcut are also modal popups + && !(event.target.skeleton?.component == "shortcut")) { + search_results.close(); + self.skeleton.close.rendered.click(); + } else if (event.target.closest('.satus-modal.satus-modal--vertical-menu') && event.target.closest('.satus-button')) { + // hide search results when clicking on vertical-menu button + search_results.close(); + self.skeleton.close.rendered.click(); + } + } + + document.addEventListener('click', hidesearch); + + if (extension.searchPosition) { + search_results.childNodes[1].scrollTop = extension.searchPosition; + } document.querySelector('.search-results .satus-modal__scrim').addEventListener('click', function () { - var text_field = this.parentElement.baseProvider.skeleton.header.sectionEnd.textField.rendered, - search_results = document.querySelector('.search-results'); + // this is someone clicking outside of Search results window + let search_results = document.querySelector('.search-results'); if (search_results) { + extension.searchPosition = search_results.childNodes[1].scrollTop; search_results.close(); } - text_field.value = ''; - text_field.style.display = ''; - - self.removeAttribute('results'); + self.skeleton.close.rendered.click() }); } } }, true); } else { - var search_results = document.querySelector('.search-results'); + let search_results = document.querySelector('.search-results'); if (search_results) { search_results.close(); @@ -177,9 +189,10 @@ extension.skeleton.header.sectionEnd.search.on.click = { variant: 'icon', on: { click: function () { - var search_results = document.querySelector('.search-results'); + let search_results = document.querySelector('.search-results'); if (search_results) { + extension.searchPosition = search_results.childNodes[1].scrollTop; search_results.close(); } @@ -205,8 +218,3 @@ extension.skeleton.header.sectionEnd.search.on.click = { } } }; - - - - - diff --git a/menu/skeleton.js b/menu/skeleton.js index b6136141e..723369210 100644 --- a/menu/skeleton.js +++ b/menu/skeleton.js @@ -11,7 +11,9 @@ --------------------------------------------------------------*/ extension.skeleton = { - component: 'base' + component: 'base', + search: false, + searchPosition: 0 }; @@ -245,4 +247,4 @@ extension.skeleton.main = { } }, "frame": { component: 'iframe', class: 'frame', attr: { 'src': 'https://improvedtube.com/wishes', 'style': 'border: none; bottom: 0px; overflow: hidden; width:326px; position:absolute; height:212px; left:-6px !important' } } } -}; \ No newline at end of file +};