Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 84 additions & 58 deletions src/dialogs/select.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Checkbox from "components/checkbox";
import tile from "components/tile";
import actionStack from "lib/actionStack";
import restoreTheme from "lib/restoreTheme";
Expand All @@ -11,84 +12,110 @@ 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]
* @property {boolean} [checkbox]
*/

/**
* 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?, checkbox?] or String
* @param {SelectOptions | boolean} options options or rejectOnCancel
* @returns {Promise<string>}
*/
function select(title, options, opts = {}) {
function select(title, items, options = {}) {
let rejectOnCancel = false;
if (typeof opts === "boolean") {
rejectOnCancel = opts;
opts = {};
if (typeof options === "boolean") {
rejectOnCancel = options;
options = {};
}
return new Promise((resolve, reject) => {
const $titleSpan = title ? (
<strong className="title">{title}</strong>
) : null;
const $list = (
<ul
className={`scroll ${opts.textTransform === false ? " no-text-transform" : ""}`}
></ul>
);
const selectDiv = (

return new Promise((res, rej) => {
const { textTransform = false, hideOnSelect = true } = options;
let $defaultVal;

// elements
const $mask = <span className="mask" onclick={cancel}></span>;
const $list = tag("ul", {
className: "scroll" + !textTransform ? " no-text-transform" : "",
});
const $select = (
<div className="prompt select">
{$titleSpan ? [$titleSpan, $list] : $list}
{title ? <strong className="title">{title}</strong> : ""}
{$list}
</div>
);
const mask = <span className="mask" onclick={cancel}></span>;
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 = <i className="icon letters" data-letters={letters}></i>;
} else {
lead = <i className={`icon ${icon}`}></i>;
}
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 = (
<i className="icon letters" data-letters={itemOptions.letters}></i>
);
} else {
lead = <i className={`icon ${itemOptions.icon}`}></i>;
}
}

option.map((o, i) => {
if (typeof o === "boolean" && i > 1) disabled = !o;
// handle checkbox
if (itemOptions.checkbox != null) {
tail = Checkbox({
checked: itemOptions.checkbox,
});
} else {
value = text = option;
}

const $item = tile({
lead,
text: <span className="text" innerHTML={text}></span>,
tail,
text: <span className="text" innerHTML={itemOptions.text}></span>,
});

if (opts.default === value) {
$item.tabIndex = "0";
if (itemOptions.disabled) $item.classList.add("disabled");
if (options.default === itemOptions.value) {
$item.classList.add("selected");
$defaultVal = $item;
}

$item.tabIndex = "0";

// handle events
$item.onclick = function () {
if (value === undefined) return;
if (opts.hideOnSelect) hide();
resolve(value);
if (!itemOptions.value) return;
if (hideOnSelect) hide();
res(itemOptions.value);
};

if (disabled) $item.classList.add("disabled");

$list.append($item);
});

Expand All @@ -97,31 +124,30 @@ function select(title, options, opts = {}) {
action: cancel,
});

app.append(selectDiv, mask);
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 opts.onCancel === "function") opts.onCancel();
if (typeof options.onCancel === "function") options.onCancel();
if (rejectOnCancel) reject();
}

function hideSelect() {
selectDiv.classList.add("hide");
$select.classList.add("hide");
restoreTheme();
setTimeout(() => {
selectDiv.remove();
mask.remove();
$select.remove();
$mask.remove();
}, 300);
}

function hide() {
if (typeof opts.onHide === "function") opts.onHide();
if (typeof options.onHide === "function") options.onHide();
actionStack.remove("select");
hideSelect();
let listItems = [...$list.children];
Expand Down