Skip to content

Commit e244016

Browse files
committed
Add multi-selection actions to settings page
Fixes #182, #206
1 parent 7091b36 commit e244016

File tree

5 files changed

+141
-10
lines changed

5 files changed

+141
-10
lines changed

css/main.css

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ label {
8888
float: right;
8989
}
9090

91-
.flex {
91+
.flex:not([hidden]) {
9292
display: flex;
9393
}
9494

@@ -295,7 +295,7 @@ input.short {
295295
max-width: 7ch;
296296
}
297297

298-
input:not([type="submit"]):not([type="button"]):not([type="hidden"]) {
298+
input:not([type="checkbox"]):not([type="submit"]):not([type="button"]):not([type="hidden"]) {
299299
background-color: var(--textbox-background);
300300
color: inherit;
301301
border: 1px solid var(--grey-90-a30);
@@ -305,21 +305,51 @@ input:not([type="submit"]):not([type="button"]):not([type="hidden"]) {
305305
transition: box-shadow 0.15s cubic-bezier(.07,.95,0,1);
306306
}
307307

308-
input:not([type="submit"]):not([type="button"]):not([type="hidden"]):hover {
308+
input:not([type="checkbox"]):not([type="submit"]):not([type="button"]):not([type="hidden"]):hover {
309309
border-color: var(--grey-90-a50);
310310
}
311311

312-
input:not([type="submit"]):not([type="button"]):not([type="hidden"]):focus {
312+
input:not([type="checkbox"]):not([type="submit"]):not([type="button"]):not([type="hidden"]):focus {
313313
border-color: var(--blue-50);
314314
box-shadow: 0 0 0 1px var(--blue-50), 0 0 0 4px var(--blue-50-a30);
315315
}
316316

317-
input:not([type="submit"]):not([type="button"]):not([type="hidden"]).error,
318-
input:not([type="submit"]):not([type="button"]):not([type="hidden"]):invalid {
317+
input:not([type="checkbox"]):not([type="submit"]):not([type="button"]):not([type="hidden"]).error,
318+
input:not([type="checkbox"]):not([type="submit"]):not([type="button"]):not([type="hidden"]):invalid {
319319
border-color: var(--red-50);
320320
box-shadow: 0 0 0 1px var(--red-50), 0 0 0 4px var(--red-50-a30);
321321
}
322322

323+
input[type="checkbox"] {
324+
-moz-appearance: none;
325+
border: 2px solid var(--grey-10-a10);
326+
border-radius: 2px;
327+
width: 16px;
328+
height: 16px;
329+
display: inline-block;
330+
}
331+
332+
input[type="checkbox"]::before {
333+
display: inline-block;
334+
width: 16px;
335+
height: 16px;
336+
margin: -2px;
337+
}
338+
339+
input[type="checkbox"]:checked::before {
340+
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M6.52,12.5a1,1,0,0,1-.705-.291l-3.52-3.5a1,1,0,1,1,1.41-1.418l2.812,2.8,5.774-5.793a1,1,0,0,1,1.416,1.412l-6.479,6.5A1,1,0,0,1,6.52,12.5Z" fill="white"/></svg>')
341+
}
342+
343+
input[type="checkbox"]:indeterminate::before {
344+
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect x="3" y="7" width="10" height="2" rx="1" fill="white"/></svg>');
345+
}
346+
347+
input[type="checkbox"]:indeterminate,
348+
input[type="checkbox"]:checked {
349+
border-color: transparent;
350+
background-color: var(--blue-60);
351+
}
352+
323353
.card {
324354
box-shadow: 0 2px 8px var(--grey-90-a10);
325355
background: var(--card-background);

pages/settings/settings.css

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ main > * {
2424
.feed {
2525
display: grid;
2626
padding: 10px;
27-
grid-template-columns: 1fr min-content;
28-
grid-template-areas: "title edit"
29-
"siteUrl edit";
27+
grid-template-columns: min-content 1fr min-content;
28+
grid-template-areas: "checkbox title edit"
29+
"checkbox siteUrl edit";
30+
column-gap: 10px;
31+
}
32+
33+
.feed:first-child {
34+
margin-top: 0;
3035
}
3136

3237
.broken {
@@ -47,6 +52,11 @@ main > * {
4752
font-weight: bold;
4853
}
4954

55+
.feed-checkbox {
56+
grid-area: checkbox;
57+
align-self: center;
58+
}
59+
5060
.feed-title {
5161
grid-area: title;
5262
}

pages/settings/settings.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@
88
</head>
99
<body>
1010
<main>
11-
<div class="toolbar flex">
11+
<header class="toolbar flex">
1212
<h1 class="grow" data-i18n-text="yourLivemarks"></h1>
1313
<button data-i18n-text="moreOptions" class="icon settings float-right" id="settings-toggle"></button>
1414
<button data-i18n-text="newLivemark" class="icon new icon-white primary float-right" id="add"></button>
15+
</header>
16+
<div class="toolbar flex" id="selection-toolbar" hidden>
17+
<input type="checkbox" id="select-all-checkbox">
18+
<span id="select-status"></span>
19+
<div class="grow"></div>
20+
<button class="">Move to folder</button>
21+
<button class="icon delete" id="delete-feed-selection">Delete</button>
1522
</div>
1623
<div class="empty-notice grow" id="feeds"></div>
1724
</main>

pages/settings/settings.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ window.onload = async () => {
8080
reader.readAsText(file);
8181
});
8282

83+
document.querySelector("#delete-feed-selection").addEventListener("click", () => {
84+
FeedMultiSelection.removeSelectedFeeds();
85+
});
86+
8387
loadFeeds();
8488
LivemarkStore.addChangeListener(loadFeeds);
8589
browser.bookmarks.onChanged.addListener(async (id) => {
@@ -159,15 +163,84 @@ async function loadFeeds() {
159163
feed.title = I18N.getMessage("settings_brokenLivemark");
160164
addFeedToList(feed, true);
161165
});
166+
167+
FeedMultiSelection.reset();
162168
}
163169

170+
const FeedMultiSelection = {
171+
selection: new Set(),
172+
selectAllCheckbox: document.getElementById("select-all-checkbox"),
173+
async _updateSelectAllCheckboxState() {
174+
const size = await LivemarkStore.getSize();
175+
const isEmptySelection = this.selection.size == 0;
176+
this.selectAllCheckbox.indeterminate = !isEmptySelection && this.selection.size < size;
177+
this.selectAllCheckbox.checked = !isEmptySelection && this.selection.size == size;
178+
this.selectAllCheckbox.disabled = size == 0;
179+
document.getElementById("selection-toolbar").hidden = size == 0;
180+
},
181+
addToSelection(feed) {
182+
this.selection.add(feed);
183+
this._updateSelectAllCheckboxState();
184+
},
185+
addAllToSelection() {
186+
for (const element of document.querySelectorAll("#feeds > .feed")) {
187+
this.selection.add(element.feedData);
188+
element.querySelector(".feed-checkbox").checked = true;
189+
}
190+
this._updateSelectAllCheckboxState();
191+
},
192+
removeFromSelection(feed) {
193+
this.selection.delete(feed);
194+
this._updateSelectAllCheckboxState();
195+
},
196+
reset() {
197+
this.selection.clear();
198+
for (const element of document.querySelectorAll("#feeds > .feed")) {
199+
element.querySelector(".feed-checkbox").checked = false;
200+
}
201+
this._updateSelectAllCheckboxState();
202+
203+
// XXX: probably doesn't belong here...
204+
this.selectAllCheckbox.onchange = () => {
205+
if (this.selectAllCheckbox.checked) {
206+
this.addAllToSelection();
207+
} else {
208+
this.reset();
209+
}
210+
};
211+
},
212+
213+
moveSelectedFeeds(folder) {
214+
// XXX: todo
215+
},
216+
async removeSelectedFeeds() {
217+
for (const feed of this.selection) {
218+
await LivemarkStore.remove(feed.id);
219+
}
220+
this.selection.clear();
221+
}
222+
};
223+
164224
function addFeedToList(feed, broken = false) {
165225
const item = document.createElement("div");
166226
item.className = "feed card";
227+
item.feedData = feed;
167228
if (broken) {
168229
item.classList.add("broken");
169230
}
170231

232+
const checkbox = document.createElement("input");
233+
checkbox.type = "checkbox";
234+
checkbox.className = "feed-checkbox";
235+
checkbox.onchange = () => {
236+
if (checkbox.checked) {
237+
FeedMultiSelection.addToSelection(feed);
238+
} else {
239+
FeedMultiSelection.removeFromSelection(feed);
240+
}
241+
};
242+
item.appendChild(checkbox);
243+
171244
const feedTitle = document.createElement("span");
172245
feedTitle.textContent = feed.title;
173246
feedTitle.className = "feed-title";

shared/livemark-store.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ const LivemarkStore = {
5555
return all;
5656
},
5757

58+
async getSize() {
59+
const livemarks = await browser.storage.sync.get();
60+
let size = 0;
61+
for (const key in livemarks) {
62+
if (key.startsWith(PREFIX)) {
63+
size++;
64+
}
65+
}
66+
return size;
67+
},
68+
5869
async add(feed) {
5970
const { title, parentId } = feed;
6071
const bookmark = await browser.bookmarks.create({

0 commit comments

Comments
 (0)