Skip to content

Commit d0523e3

Browse files
Fixed select box issue and improved multiple aspects of it (#1296)
Co-authored-by: Copilot <[email protected]>
1 parent d1ce957 commit d0523e3

File tree

2 files changed

+108
-25
lines changed

2 files changed

+108
-25
lines changed

src/dialogs/select.js

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Checkbox from "components/checkbox";
22
import tile from "components/tile";
3+
import DOMPurify from "dompurify";
34
import actionStack from "lib/actionStack";
45
import restoreTheme from "lib/restoreTheme";
56

@@ -20,6 +21,8 @@ import restoreTheme from "lib/restoreTheme";
2021
* @property {boolean} [disabled]
2122
* @property {string} [letters]
2223
* @property {boolean} [checkbox]
24+
* @property {HTMLElement} [tailElement]
25+
* @property {function(Event):void} [ontailclick]
2326
*/
2427

2528
/**
@@ -43,33 +46,45 @@ function select(title, items, options = {}) {
4346
// elements
4447
const $mask = <span className="mask" onclick={cancel}></span>;
4548
const $list = tag("ul", {
46-
className: "scroll" + !textTransform ? " no-text-transform" : "",
49+
className: `scroll${!textTransform ? " no-text-transform" : ""}`,
4750
});
51+
const $titleSpan = title ? (
52+
<strong className="title">{title}</strong>
53+
) : null;
4854
const $select = (
4955
<div className="prompt select">
50-
{title ? <strong className="title">{title}</strong> : ""}
51-
{$list}
56+
{$titleSpan ? [$titleSpan, $list] : $list}
5257
</div>
5358
);
5459

60+
// Track tail click handlers for cleanup
61+
const tailClickHandlers = new Map();
62+
5563
items.map((item) => {
5664
let lead,
57-
tail,
65+
tail = null,
5866
itemOptions = {
5967
value: null,
6068
text: null,
6169
icon: null,
6270
disabled: false,
6371
letters: "",
6472
checkbox: null,
73+
tailElement: null,
74+
ontailclick: null,
6575
};
6676

6777
// init item options
6878
if (typeof item === "object") {
6979
if (Array.isArray(item)) {
80+
// This format does NOT support custom tail or handlers so pass object :)
7081
Object.keys(itemOptions).forEach(
7182
(key, i) => (itemOptions[key] = item[i]),
7283
);
84+
85+
item.map((o, i) => {
86+
if (typeof o === "boolean" && i > 1) itemOptions.disabled = !o;
87+
});
7388
} else {
7489
itemOptions = Object.assign({}, itemOptions, item);
7590
}
@@ -78,7 +93,7 @@ function select(title, items, options = {}) {
7893
itemOptions.text = item;
7994
}
8095

81-
// handle icon
96+
// handle icon (lead)
8297
if (itemOptions.icon) {
8398
if (itemOptions.icon === "letters" && !!itemOptions.letters) {
8499
lead = (
@@ -89,8 +104,10 @@ function select(title, items, options = {}) {
89104
}
90105
}
91106

92-
// handle checkbox
93-
if (itemOptions.checkbox != null) {
107+
// handle tail (checkbox or custom element)
108+
if (itemOptions.tailElement) {
109+
tail = itemOptions.tailElement;
110+
} else if (itemOptions.checkbox != null) {
94111
tail = Checkbox({
95112
checked: itemOptions.checkbox,
96113
});
@@ -99,7 +116,12 @@ function select(title, items, options = {}) {
99116
const $item = tile({
100117
lead,
101118
tail,
102-
text: <span className="text" innerHTML={itemOptions.text}></span>,
119+
text: (
120+
<span
121+
className="text"
122+
innerHTML={DOMPurify.sanitize(itemOptions.text)}
123+
></span>
124+
),
103125
});
104126

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

112-
// handle events
113-
$item.onclick = function () {
134+
$item.onclick = function (e) {
135+
// Check if clicked element or any parent up to the item has data-action
136+
let target = e.target;
137+
while (target && target !== $item) {
138+
if (target.hasAttribute("data-action")) {
139+
// Stop propagation and prevent default
140+
e.stopPropagation();
141+
e.preventDefault();
142+
return false;
143+
}
144+
target = target.parentElement;
145+
}
146+
114147
if (!itemOptions.value) return;
115148
if (hideOnSelect) hide();
116149
res(itemOptions.value);
117150
};
118151

152+
// Handle tail click event if a custom tail and handler are provided
153+
if (itemOptions.tailElement && itemOptions.ontailclick && tail) {
154+
// Apply the pointer-events: all directly to the tail element
155+
tail.style.pointerEvents = "all";
156+
157+
const tailClickHandler = function (e) {
158+
e.stopPropagation();
159+
e.preventDefault();
160+
itemOptions.ontailclick.call($item, e);
161+
};
162+
163+
tail.addEventListener("click", tailClickHandler);
164+
tailClickHandlers.set(tail, tailClickHandler);
165+
}
166+
119167
$list.append($item);
120168
});
121169

@@ -134,7 +182,7 @@ function select(title, items, options = {}) {
134182
function cancel() {
135183
hide();
136184
if (typeof options.onCancel === "function") options.onCancel();
137-
if (rejectOnCancel) reject();
185+
if (rejectOnCancel) rej();
138186
}
139187

140188
function hideSelect() {
@@ -152,6 +200,11 @@ function select(title, items, options = {}) {
152200
hideSelect();
153201
let listItems = [...$list.children];
154202
listItems.map((item) => (item.onclick = null));
203+
// Clean up tail click handlers
204+
tailClickHandlers.forEach((handler, element) => {
205+
element.removeEventListener("click", handler);
206+
});
207+
tailClickHandlers.clear();
155208
}
156209
});
157210
}

src/lib/recents.js

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,29 @@ const recents = {
9191
for (let dir of dirs) {
9292
const { url } = dir;
9393

94-
all.push([
95-
{
96-
type: "dir",
97-
val: dir,
94+
const dirValue = {
95+
type: "dir",
96+
val: dir,
97+
};
98+
99+
const tailElement = tag("span", {
100+
className: "icon clearclose",
101+
dataset: {
102+
action: "clear",
103+
},
104+
});
105+
106+
all.push({
107+
value: dirValue,
108+
text: shortName(url),
109+
icon: "folder",
110+
tailElement: tailElement,
111+
ontailclick: (e) => {
112+
const $item = e.currentTarget.closest(".tile");
113+
if ($item) $item.remove();
114+
this.removeFolder(dir.url);
98115
},
99-
shortName(url),
100-
"icon folder",
101-
]);
116+
});
102117
}
103118
}
104119

@@ -107,14 +122,29 @@ const recents = {
107122
for (let file of files) {
108123
if (!file) continue;
109124
const name = shortName(Url.parse(file).url);
110-
all.push([
111-
{
112-
type: "file",
113-
val: file,
125+
126+
const fileValue = {
127+
type: "file",
128+
val: file,
129+
};
130+
const tailElement = tag("span", {
131+
className: "icon clearclose",
132+
dataset: {
133+
action: "clear",
134+
},
135+
});
136+
137+
all.push({
138+
value: fileValue,
139+
text: name,
140+
icon: helpers.getIconForFile(name),
141+
tailElement: tailElement,
142+
ontailclick: (e) => {
143+
const $item = e.currentTarget.closest(".tile");
144+
if ($item) $item.remove();
145+
this.removeFile(file);
114146
},
115-
name,
116-
helpers.getIconForFile(name),
117-
]);
147+
});
118148
}
119149
}
120150

0 commit comments

Comments
 (0)