Skip to content
Merged
Show file tree
Hide file tree
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
75 changes: 64 additions & 11 deletions src/dialogs/select.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Checkbox from "components/checkbox";
import tile from "components/tile";
import DOMPurify from "dompurify";
import actionStack from "lib/actionStack";
import restoreTheme from "lib/restoreTheme";

Expand All @@ -20,6 +21,8 @@ import restoreTheme from "lib/restoreTheme";
* @property {boolean} [disabled]
* @property {string} [letters]
* @property {boolean} [checkbox]
* @property {HTMLElement} [tailElement]
* @property {function(Event):void} [ontailclick]
*/

/**
Expand All @@ -43,33 +46,45 @@ function select(title, items, options = {}) {
// elements
const $mask = <span className="mask" onclick={cancel}></span>;
const $list = tag("ul", {
className: "scroll" + !textTransform ? " no-text-transform" : "",
className: `scroll${!textTransform ? " no-text-transform" : ""}`,
});
const $titleSpan = title ? (
<strong className="title">{title}</strong>
) : null;
const $select = (
<div className="prompt select">
{title ? <strong className="title">{title}</strong> : ""}
{$list}
{$titleSpan ? [$titleSpan, $list] : $list}
</div>
);

// Track tail click handlers for cleanup
const tailClickHandlers = new Map();

items.map((item) => {
let lead,
tail,
tail = null,
itemOptions = {
value: null,
text: null,
icon: null,
disabled: false,
letters: "",
checkbox: null,
tailElement: null,
ontailclick: null,
};

// init item options
if (typeof item === "object") {
if (Array.isArray(item)) {
// This format does NOT support custom tail or handlers so pass object :)
Object.keys(itemOptions).forEach(
(key, i) => (itemOptions[key] = item[i]),
);

item.map((o, i) => {
if (typeof o === "boolean" && i > 1) itemOptions.disabled = !o;
});
} else {
itemOptions = Object.assign({}, itemOptions, item);
}
Expand All @@ -78,7 +93,7 @@ function select(title, items, options = {}) {
itemOptions.text = item;
}

// handle icon
// handle icon (lead)
if (itemOptions.icon) {
if (itemOptions.icon === "letters" && !!itemOptions.letters) {
lead = (
Expand All @@ -89,8 +104,10 @@ function select(title, items, options = {}) {
}
}

// handle checkbox
if (itemOptions.checkbox != null) {
// handle tail (checkbox or custom element)
if (itemOptions.tailElement) {
tail = itemOptions.tailElement;
} else if (itemOptions.checkbox != null) {
tail = Checkbox({
checked: itemOptions.checkbox,
});
Expand All @@ -99,7 +116,12 @@ function select(title, items, options = {}) {
const $item = tile({
lead,
tail,
text: <span className="text" innerHTML={itemOptions.text}></span>,
text: (
<span
className="text"
innerHTML={DOMPurify.sanitize(itemOptions.text)}
></span>
),
});

$item.tabIndex = "0";
Expand All @@ -109,13 +131,39 @@ function select(title, items, options = {}) {
$defaultVal = $item;
}

// handle events
$item.onclick = function () {
$item.onclick = function (e) {
// Check if clicked element or any parent up to the item has data-action
let target = e.target;
while (target && target !== $item) {
if (target.hasAttribute("data-action")) {
// Stop propagation and prevent default
e.stopPropagation();
e.preventDefault();
return false;
}
target = target.parentElement;
}

if (!itemOptions.value) return;
if (hideOnSelect) hide();
res(itemOptions.value);
};

// Handle tail click event if a custom tail and handler are provided
if (itemOptions.tailElement && itemOptions.ontailclick && tail) {
// Apply the pointer-events: all directly to the tail element
tail.style.pointerEvents = "all";

const tailClickHandler = function (e) {
e.stopPropagation();
e.preventDefault();
itemOptions.ontailclick.call($item, e);
};

tail.addEventListener("click", tailClickHandler);
tailClickHandlers.set(tail, tailClickHandler);
}

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

Expand All @@ -134,7 +182,7 @@ function select(title, items, options = {}) {
function cancel() {
hide();
if (typeof options.onCancel === "function") options.onCancel();
if (rejectOnCancel) reject();
if (rejectOnCancel) rej();
}

function hideSelect() {
Expand All @@ -152,6 +200,11 @@ function select(title, items, options = {}) {
hideSelect();
let listItems = [...$list.children];
listItems.map((item) => (item.onclick = null));
// Clean up tail click handlers
tailClickHandlers.forEach((handler, element) => {
element.removeEventListener("click", handler);
});
tailClickHandlers.clear();
}
});
}
Expand Down
58 changes: 44 additions & 14 deletions src/lib/recents.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,29 @@ const recents = {
for (let dir of dirs) {
const { url } = dir;

all.push([
{
type: "dir",
val: dir,
const dirValue = {
type: "dir",
val: dir,
};

const tailElement = tag("span", {
className: "icon clearclose",
dataset: {
action: "clear",
},
});

all.push({
value: dirValue,
text: shortName(url),
icon: "folder",
tailElement: tailElement,
ontailclick: (e) => {
const $item = e.currentTarget.closest(".tile");
if ($item) $item.remove();
this.removeFolder(dir.url);
},
shortName(url),
"icon folder",
]);
});
}
}

Expand All @@ -107,14 +122,29 @@ const recents = {
for (let file of files) {
if (!file) continue;
const name = shortName(Url.parse(file).url);
all.push([
{
type: "file",
val: file,

const fileValue = {
type: "file",
val: file,
};
const tailElement = tag("span", {
className: "icon clearclose",
dataset: {
action: "clear",
},
});

all.push({
value: fileValue,
text: name,
icon: helpers.getIconForFile(name),
tailElement: tailElement,
ontailclick: (e) => {
const $item = e.currentTarget.closest(".tile");
if ($item) $item.remove();
this.removeFile(file);
},
name,
helpers.getIconForFile(name),
]);
});
}
}

Expand Down