From a0220f3cd97ac391b11c14ce61f159fa70575f7e Mon Sep 17 00:00:00 2001 From: overskul Date: Sun, 23 Mar 2025 01:01:41 +0300 Subject: [PATCH 1/7] refactor: select dialog --- src/dialogs/select.js | 244 ++++++++++++++++++++++-------------------- 1 file changed, 129 insertions(+), 115 deletions(-) diff --git a/src/dialogs/select.js b/src/dialogs/select.js index 35ce1ae6e..9a1d7518f 100644 --- a/src/dialogs/select.js +++ b/src/dialogs/select.js @@ -1,6 +1,6 @@ -import tile from "components/tile"; -import actionStack from "lib/actionStack"; -import restoreTheme from "lib/restoreTheme"; +import tile from 'components/tile'; +import actionStack from 'lib/actionStack'; +import restoreTheme from 'lib/restoreTheme'; /** * @typedef {object} SelectOptions @@ -11,123 +11,137 @@ import restoreTheme from "lib/restoreTheme"; * @property {function():void} [onHide] */ +/** + * @typedef {object} SelectItem + * @property {string} [value] + * @property {string} [text] + * @property {string} [icon] + * @property {boolean} [disabled] + * @property {string} [letters] + */ + /** * Create a select dialog * @param {string} title Title of the select - * @param {string[]} options [value, text, icon, disable?] or string - * @param {SelectOptions | boolean} opts options or rejectOnCancel + * @param {string | string[] | SelectItem} items Object or [value, text, icon, disable?, letters?] or String + * @param {SelectOptions | boolean} options options or rejectOnCancel * @returns {Promise} */ -function select(title, options, opts = {}) { - let rejectOnCancel = false; - if (typeof opts === "boolean") { - rejectOnCancel = opts; - opts = {}; - } - return new Promise((resolve, reject) => { - const $titleSpan = title ? ( - {title} - ) : null; - const $list = ( - - ); - const selectDiv = ( -
- {$titleSpan ? [$titleSpan, $list] : $list} -
- ); - const mask = ; - let $defaultVal; - - if (opts.hideOnSelect === undefined) opts.hideOnSelect = true; - - options.map((option) => { - let value = null; - let text = null; - let lead = null; - let disabled = false; - if (Array.isArray(option)) { - value = option[0]; - text = option[1]; - - if (option.length > 2 && typeof option[2] === "string") { - const icon = option[2]; - if (icon === "letters") { - const letters = option[4]; - lead = ; - } else { - lead = ; - } - } - - option.map((o, i) => { - if (typeof o === "boolean" && i > 1) disabled = !o; - }); - } else { - value = text = option; - } - - const $item = tile({ - lead, - text: , - }); - - if (opts.default === value) { - $item.classList.add("selected"); - $defaultVal = $item; - } - - $item.tabIndex = "0"; - - $item.onclick = function () { - if (value === undefined) return; - if (opts.hideOnSelect) hide(); - resolve(value); - }; - - if (disabled) $item.classList.add("disabled"); - - $list.append($item); - }); - - actionStack.push({ - id: "select", - action: cancel, - }); - - app.append(selectDiv, mask); - if ($defaultVal) $defaultVal.scrollIntoView(); - - const $firstChild = $defaultVal || $list.firstChild; - if ($firstChild && $firstChild.focus) $firstChild.focus(); - - restoreTheme(true); - - function cancel() { - hide(); - if (typeof opts.onCancel === "function") opts.onCancel(); - if (rejectOnCancel) reject(); - } - - function hideSelect() { - selectDiv.classList.add("hide"); - restoreTheme(); - setTimeout(() => { - selectDiv.remove(); - mask.remove(); - }, 300); - } - - function hide() { - if (typeof opts.onHide === "function") opts.onHide(); - actionStack.remove("select"); - hideSelect(); - let listItems = [...$list.children]; - listItems.map((item) => (item.onclick = null)); - } - }); +function select(title, items, options = {}) { + let rejectOnCancel = false; + if (typeof options === 'boolean') { + rejectOnCancel = options; + options = {}; + } + + return new Promise((res, rej) => { + const { textTransform = false, hideOnSelect = true } = options; + let $defaultVal; + + // elements + const $mask = ; + const $list = tag('ul', { + className: 'scroll' + !textTransform ? ' no-text-transform' : '' + }); + const $select = ( +
+ {title ? {title} : ''} + {$list} +
+ ); + + items.map(item => { + let lead, + itemOptions = { + value: null, + text: null, + icon: null, + disabled: true, + letters: '' + }; + + // init item options + if (typeof item === 'object') { + if (Array.isArray(item)) { + Object.keys(itemOptions).forEach( + (key, i) => (itemOptions[key] = item[i]) + ); + } else { + itemOptions = Object.assign({}, itemOptions, item); + } + } else { + itemOptions.value = item; + itemOptions.text = item; + } + + // handle icon + if (itemOptions.icon) { + if (itemOptions.icon === 'letters') { + lead = ( + + ); + } else { + lead = ; + } + } + + const $item = tile({ + lead, + text: + }); + + $item.tabIndex = '0'; + if (disabled) $item.classList.add('disabled'); + if (options.default === itemOptions.value) { + $item.classList.add('selected'); + $defaultVal = $item; + } + + // handle events + $item.onclick = function () { + if (!itemOptions.value) return; + if (hideOnSelect) hide(); + res(itemOptions.value); + }; + + $list.append($item); + }); + + actionStack.push({ + id: 'select', + action: cancel + }); + + app.append($select, $mask); + if ($defaultVal) $defaultVal.scrollIntoView(); + + const $firstChild = $defaultVal || $list.firstChild; + if ($firstChild && $firstChild.focus) $firstChild.focus(); + restoreTheme(true); + + function cancel() { + hide(); + if (typeof options.onCancel === 'function') options.onCancel(); + if (rejectOnCancel) reject(); + } + + function hideSelect() { + $select.classList.add('hide'); + restoreTheme(); + setTimeout(() => { + $select.remove(); + $mask.remove(); + }, 300); + } + + function hide() { + if (typeof options.onHide === 'function') options.onHide(); + actionStack.remove('select'); + hideSelect(); + let listItems = [...$list.children]; + listItems.map(item => (item.onclick = null)); + } + }); } export default select; From 0b38b2f9c00fa2f93d4598490f1448f8004927a8 Mon Sep 17 00:00:00 2001 From: overskul Date: Sun, 23 Mar 2025 21:59:03 +0300 Subject: [PATCH 2/7] feat: checkbox to select dialog --- src/dialogs/select.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/dialogs/select.js b/src/dialogs/select.js index 9a1d7518f..bdc7b02f9 100644 --- a/src/dialogs/select.js +++ b/src/dialogs/select.js @@ -1,4 +1,5 @@ import tile from 'components/tile'; +import Checkbox from 'components/checkbox'; import actionStack from 'lib/actionStack'; import restoreTheme from 'lib/restoreTheme'; @@ -52,12 +53,14 @@ function select(title, items, options = {}) { items.map(item => { let lead, + tail, itemOptions = { value: null, text: null, icon: null, - disabled: true, - letters: '' + disabled: false, + letters: '', + checkbox: null }; // init item options @@ -85,13 +88,21 @@ function select(title, items, options = {}) { } } + // handle checkbox + if (itemOptions.checkbox != null) { + tail = Checkbox({ + checked: itemOptions.checkbox + }); + } + const $item = tile({ lead, + tail, text: }); $item.tabIndex = '0'; - if (disabled) $item.classList.add('disabled'); + if (itemOptions.disabled) $item.classList.add('disabled'); if (options.default === itemOptions.value) { $item.classList.add('selected'); $defaultVal = $item; @@ -99,7 +110,9 @@ function select(title, items, options = {}) { // handle events $item.onclick = function () { + console.log('item clicked') if (!itemOptions.value) return; + console.log('item value exists') if (hideOnSelect) hide(); res(itemOptions.value); }; From 7e458a3a774077bbfb2c1a3d878d3b654182eaca Mon Sep 17 00:00:00 2001 From: overskul Date: Sun, 23 Mar 2025 22:02:44 +0300 Subject: [PATCH 3/7] fix: forget logs --- src/dialogs/select.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dialogs/select.js b/src/dialogs/select.js index bdc7b02f9..d88c07ea3 100644 --- a/src/dialogs/select.js +++ b/src/dialogs/select.js @@ -110,9 +110,7 @@ function select(title, items, options = {}) { // handle events $item.onclick = function () { - console.log('item clicked') if (!itemOptions.value) return; - console.log('item value exists') if (hideOnSelect) hide(); res(itemOptions.value); }; From a82a27105876c3d480d0680186d6ab69041012dd Mon Sep 17 00:00:00 2001 From: Overskul <52527794+overskul@users.noreply.github.com> Date: Tue, 22 Apr 2025 19:35:59 +0300 Subject: [PATCH 4/7] docs(select dialog): add missing JSDoc comments - Add missing `checkbox` property in jsDoc. --- src/dialogs/select.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dialogs/select.js b/src/dialogs/select.js index d88c07ea3..27231e9e4 100644 --- a/src/dialogs/select.js +++ b/src/dialogs/select.js @@ -19,12 +19,13 @@ import restoreTheme from 'lib/restoreTheme'; * @property {string} [icon] * @property {boolean} [disabled] * @property {string} [letters] + * @property {boolean} [checkbox] */ /** * Create a select dialog * @param {string} title Title of the select - * @param {string | string[] | SelectItem} items Object or [value, text, icon, disable?, letters?] or String + * @param {string | string[] | SelectItem} items Object or [value, text, icon, disable?, letters?, checkbox?] or String * @param {SelectOptions | boolean} options options or rejectOnCancel * @returns {Promise} */ From 56c819899d6087e93210d942aaa0e79fd5305aab Mon Sep 17 00:00:00 2001 From: Overskul <52527794+overskul@users.noreply.github.com> Date: Tue, 22 Apr 2025 19:42:56 +0300 Subject: [PATCH 5/7] Update(select dialog): Add extra condition to if statement --- src/dialogs/select.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dialogs/select.js b/src/dialogs/select.js index 27231e9e4..cd1fd0d5b 100644 --- a/src/dialogs/select.js +++ b/src/dialogs/select.js @@ -80,10 +80,8 @@ function select(title, items, options = {}) { // handle icon if (itemOptions.icon) { - if (itemOptions.icon === 'letters') { - lead = ( - - ); + if (itemOptions.icon === 'letters' && !!itemOptions.letters) { + lead = } else { lead = ; } From 819c0db5b027ad4347f628ba4358e7469ee9bbde Mon Sep 17 00:00:00 2001 From: overskul Date: Tue, 22 Apr 2025 20:30:04 +0300 Subject: [PATCH 6/7] fix(select dialog): format --- src/dialogs/select.js | 254 +++++++++++++++++++++--------------------- 1 file changed, 128 insertions(+), 126 deletions(-) diff --git a/src/dialogs/select.js b/src/dialogs/select.js index cd1fd0d5b..72bf50109 100644 --- a/src/dialogs/select.js +++ b/src/dialogs/select.js @@ -1,7 +1,7 @@ -import tile from 'components/tile'; -import Checkbox from 'components/checkbox'; -import actionStack from 'lib/actionStack'; -import restoreTheme from 'lib/restoreTheme'; +import tile from "components/tile"; +import Checkbox from "components/checkbox"; +import actionStack from "lib/actionStack"; +import restoreTheme from "lib/restoreTheme"; /** * @typedef {object} SelectOptions @@ -30,128 +30,130 @@ import restoreTheme from 'lib/restoreTheme'; * @returns {Promise} */ function select(title, items, options = {}) { - let rejectOnCancel = false; - if (typeof options === 'boolean') { - rejectOnCancel = options; - options = {}; - } - - return new Promise((res, rej) => { - const { textTransform = false, hideOnSelect = true } = options; - let $defaultVal; - - // elements - const $mask = ; - const $list = tag('ul', { - className: 'scroll' + !textTransform ? ' no-text-transform' : '' - }); - const $select = ( -
- {title ? {title} : ''} - {$list} -
- ); - - items.map(item => { - let lead, - tail, - itemOptions = { - value: null, - text: null, - icon: null, - disabled: false, - letters: '', - checkbox: null - }; - - // init item options - if (typeof item === 'object') { - if (Array.isArray(item)) { - Object.keys(itemOptions).forEach( - (key, i) => (itemOptions[key] = item[i]) - ); - } else { - itemOptions = Object.assign({}, itemOptions, item); - } - } else { - itemOptions.value = item; - itemOptions.text = item; - } - - // handle icon - if (itemOptions.icon) { - if (itemOptions.icon === 'letters' && !!itemOptions.letters) { - lead = - } else { - lead = ; - } - } - - // handle checkbox - if (itemOptions.checkbox != null) { - tail = Checkbox({ - checked: itemOptions.checkbox - }); - } - - const $item = tile({ - lead, - tail, - text: - }); - - $item.tabIndex = '0'; - if (itemOptions.disabled) $item.classList.add('disabled'); - if (options.default === itemOptions.value) { - $item.classList.add('selected'); - $defaultVal = $item; - } - - // handle events - $item.onclick = function () { - if (!itemOptions.value) return; - if (hideOnSelect) hide(); - res(itemOptions.value); - }; - - $list.append($item); - }); - - actionStack.push({ - id: 'select', - action: cancel - }); - - app.append($select, $mask); - if ($defaultVal) $defaultVal.scrollIntoView(); - - const $firstChild = $defaultVal || $list.firstChild; - if ($firstChild && $firstChild.focus) $firstChild.focus(); - restoreTheme(true); - - function cancel() { - hide(); - if (typeof options.onCancel === 'function') options.onCancel(); - if (rejectOnCancel) reject(); - } - - function hideSelect() { - $select.classList.add('hide'); - restoreTheme(); - setTimeout(() => { - $select.remove(); - $mask.remove(); - }, 300); - } - - function hide() { - if (typeof options.onHide === 'function') options.onHide(); - actionStack.remove('select'); - hideSelect(); - let listItems = [...$list.children]; - listItems.map(item => (item.onclick = null)); - } - }); + let rejectOnCancel = false; + if (typeof options === "boolean") { + rejectOnCancel = options; + options = {}; + } + + return new Promise((res, rej) => { + const { textTransform = false, hideOnSelect = true } = options; + let $defaultVal; + + // elements + const $mask = ; + const $list = tag("ul", { + className: "scroll" + !textTransform ? " no-text-transform" : "", + }); + const $select = ( +
+ {title ? {title} : ""} + {$list} +
+ ); + + items.map((item) => { + let lead, + tail, + itemOptions = { + value: null, + text: null, + icon: null, + disabled: false, + letters: "", + checkbox: null, + }; + + // init item options + if (typeof item === "object") { + if (Array.isArray(item)) { + Object.keys(itemOptions).forEach( + (key, i) => (itemOptions[key] = item[i]), + ); + } else { + itemOptions = Object.assign({}, itemOptions, item); + } + } else { + itemOptions.value = item; + itemOptions.text = item; + } + + // handle icon + if (itemOptions.icon) { + if (itemOptions.icon === "letters" && !!itemOptions.letters) { + lead = ( + + ); + } else { + lead = ; + } + } + + // handle checkbox + if (itemOptions.checkbox != null) { + tail = Checkbox({ + checked: itemOptions.checkbox, + }); + } + + const $item = tile({ + lead, + tail, + text: , + }); + + $item.tabIndex = "0"; + if (itemOptions.disabled) $item.classList.add("disabled"); + if (options.default === itemOptions.value) { + $item.classList.add("selected"); + $defaultVal = $item; + } + + // handle events + $item.onclick = function () { + if (!itemOptions.value) return; + if (hideOnSelect) hide(); + res(itemOptions.value); + }; + + $list.append($item); + }); + + actionStack.push({ + id: "select", + action: cancel, + }); + + app.append($select, $mask); + if ($defaultVal) $defaultVal.scrollIntoView(); + + const $firstChild = $defaultVal || $list.firstChild; + if ($firstChild && $firstChild.focus) $firstChild.focus(); + restoreTheme(true); + + function cancel() { + hide(); + if (typeof options.onCancel === "function") options.onCancel(); + if (rejectOnCancel) reject(); + } + + function hideSelect() { + $select.classList.add("hide"); + restoreTheme(); + setTimeout(() => { + $select.remove(); + $mask.remove(); + }, 300); + } + + function hide() { + if (typeof options.onHide === "function") options.onHide(); + actionStack.remove("select"); + hideSelect(); + let listItems = [...$list.children]; + listItems.map((item) => (item.onclick = null)); + } + }); } export default select; From 53137e6d577587bd65153348a7db6dbde0ec19e3 Mon Sep 17 00:00:00 2001 From: overskul Date: Wed, 23 Apr 2025 10:23:53 +0300 Subject: [PATCH 7/7] format --- src/dialogs/select.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/select.js b/src/dialogs/select.js index 72bf50109..a55374d52 100644 --- a/src/dialogs/select.js +++ b/src/dialogs/select.js @@ -1,5 +1,5 @@ -import tile from "components/tile"; import Checkbox from "components/checkbox"; +import tile from "components/tile"; import actionStack from "lib/actionStack"; import restoreTheme from "lib/restoreTheme";