flexdesk 0.17.1 → 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +178 -0
- package/css/base.css +23 -1
- package/css/flexdesk.css +37 -1
- package/dist/flexdesk.css +37 -1
- package/dist/widgets.js +143 -50
- package/dist/widgets.js.map +2 -2
- package/dist/wm.js +90 -10
- package/dist/wm.js.map +2 -2
- package/package.json +1 -1
- package/src/tiling/command_palette.js +46 -7
- package/src/tiling/content_registry.js +3 -0
- package/src/tiling/entity_sources.js +10 -0
- package/src/tiling/wm.js +65 -3
- package/src/ui/components/settings_view.js +192 -51
- package/src/ui/notification_center.js +5 -5
package/dist/widgets.js
CHANGED
|
@@ -3939,6 +3939,51 @@ function fitNumber(n, { min, max, step }) {
|
|
|
3939
3939
|
if (lo != null && at(k) < lo) k = Math.ceil((lo - base) / s - EPS);
|
|
3940
3940
|
return at(k);
|
|
3941
3941
|
}
|
|
3942
|
+
function filterBox(onInput) {
|
|
3943
|
+
const label = el2("label", "twm-settings__search");
|
|
3944
|
+
const input = el2("input", "twm-settings__search-input");
|
|
3945
|
+
input.type = "search";
|
|
3946
|
+
input.placeholder = "Filter settings\u2026";
|
|
3947
|
+
input.autocomplete = "off";
|
|
3948
|
+
input.spellcheck = false;
|
|
3949
|
+
input.setAttribute("aria-label", "Filter settings");
|
|
3950
|
+
input.addEventListener("input", () => onInput(input.value));
|
|
3951
|
+
label.append(icon("search"), input);
|
|
3952
|
+
return { label, input };
|
|
3953
|
+
}
|
|
3954
|
+
function categoryNav(onSelect) {
|
|
3955
|
+
const nav = el2("nav", "twm-settings__nav");
|
|
3956
|
+
nav.setAttribute("aria-label", "Settings categories");
|
|
3957
|
+
nav.addEventListener("click", (e) => {
|
|
3958
|
+
const btn = e.target.closest?.("[data-category]");
|
|
3959
|
+
if (btn && nav.contains(btn)) onSelect(btn.dataset.category);
|
|
3960
|
+
});
|
|
3961
|
+
return nav;
|
|
3962
|
+
}
|
|
3963
|
+
function paintCategories(nav, categories, active) {
|
|
3964
|
+
const buttons = categories.map((cat) => {
|
|
3965
|
+
const b = el2("button", "twm-settings__cat");
|
|
3966
|
+
b.type = "button";
|
|
3967
|
+
b.dataset.category = cat.id;
|
|
3968
|
+
if (cat.id === active) {
|
|
3969
|
+
b.classList.add("twm-settings__cat--active");
|
|
3970
|
+
b.setAttribute("aria-current", "page");
|
|
3971
|
+
}
|
|
3972
|
+
b.append(icon(cat.icon || "tune"), el2("span", "twm-settings__cat-label", cat.label ?? cat.id));
|
|
3973
|
+
if (cat.description) b.title = String(cat.description);
|
|
3974
|
+
return b;
|
|
3975
|
+
});
|
|
3976
|
+
nav.replaceChildren(...buttons);
|
|
3977
|
+
}
|
|
3978
|
+
function resetAllButton(onClick) {
|
|
3979
|
+
const b = el2("button", "twm-btn twm-settings__reset-all");
|
|
3980
|
+
b.type = "button";
|
|
3981
|
+
b.append(icon("restart_alt"), el2("span", "", "Reset all"));
|
|
3982
|
+
b.addEventListener("click", () => {
|
|
3983
|
+
onClick();
|
|
3984
|
+
});
|
|
3985
|
+
return b;
|
|
3986
|
+
}
|
|
3942
3987
|
var SettingsView = class extends ComponentBase {
|
|
3943
3988
|
/**
|
|
3944
3989
|
* @param {object} o
|
|
@@ -3948,6 +3993,8 @@ var SettingsView = class extends ComponentBase {
|
|
|
3948
3993
|
* @param {string} [o.category] the category shown first (default: the first)
|
|
3949
3994
|
* @param {number} [o.textDebounceMs]
|
|
3950
3995
|
* @param {string} [o.title='Settings'] the sidebar heading
|
|
3996
|
+
* @param {boolean} [o.sidebar=true] false leaves the sidebar (filter,
|
|
3997
|
+
* categories, *Reset all*) out, for a `SettingsCategories` elsewhere
|
|
3951
3998
|
*/
|
|
3952
3999
|
constructor({
|
|
3953
4000
|
store,
|
|
@@ -3956,6 +4003,7 @@ var SettingsView = class extends ComponentBase {
|
|
|
3956
4003
|
category = null,
|
|
3957
4004
|
textDebounceMs = SETTINGS_TEXT_DEBOUNCE_MS,
|
|
3958
4005
|
title = "Settings",
|
|
4006
|
+
sidebar = true,
|
|
3959
4007
|
logger
|
|
3960
4008
|
} = {}) {
|
|
3961
4009
|
super({ eventBus, logger });
|
|
@@ -3971,6 +4019,8 @@ var SettingsView = class extends ComponentBase {
|
|
|
3971
4019
|
this._query = "";
|
|
3972
4020
|
this._debounceMs = textDebounceMs;
|
|
3973
4021
|
this._title = title;
|
|
4022
|
+
this._sidebar = sidebar !== false;
|
|
4023
|
+
this._listeners = /* @__PURE__ */ new Set();
|
|
3974
4024
|
this.root = null;
|
|
3975
4025
|
this._nav = null;
|
|
3976
4026
|
this._content = null;
|
|
@@ -3984,36 +4034,19 @@ var SettingsView = class extends ComponentBase {
|
|
|
3984
4034
|
mount(container) {
|
|
3985
4035
|
if (this.root) return this.root;
|
|
3986
4036
|
const root = el2("div", "twm-settings");
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
input.addEventListener("input", () => this.setQuery(input.value));
|
|
3998
|
-
search.append(icon("search"), input);
|
|
3999
|
-
this._search = input;
|
|
4000
|
-
const nav = el2("nav", "twm-settings__nav");
|
|
4001
|
-
nav.setAttribute("aria-label", "Settings categories");
|
|
4002
|
-
nav.addEventListener("click", (e) => {
|
|
4003
|
-
const btn = e.target.closest?.("[data-category]");
|
|
4004
|
-
if (btn && nav.contains(btn)) this.selectCategory(btn.dataset.category);
|
|
4005
|
-
});
|
|
4006
|
-
this._nav = nav;
|
|
4007
|
-
const resetAll = el2("button", "twm-btn twm-settings__reset-all");
|
|
4008
|
-
resetAll.type = "button";
|
|
4009
|
-
resetAll.append(icon("restart_alt"), el2("span", "", "Reset all"));
|
|
4010
|
-
resetAll.addEventListener("click", () => {
|
|
4011
|
-
this.resetAll();
|
|
4012
|
-
});
|
|
4013
|
-
side.append(head, search, nav, resetAll);
|
|
4037
|
+
if (this._sidebar) {
|
|
4038
|
+
const side = el2("aside", "twm-settings__sidebar");
|
|
4039
|
+
const head = el2("div", "twm-settings__sidebar-title");
|
|
4040
|
+
head.append(icon("settings"), el2("span", "", this._title));
|
|
4041
|
+
const search = filterBox((text) => this.setQuery(text));
|
|
4042
|
+
this._search = search.input;
|
|
4043
|
+
this._nav = categoryNav((id) => this.selectCategory(id));
|
|
4044
|
+
side.append(head, search.label, this._nav, resetAllButton(() => this.resetAll()));
|
|
4045
|
+
root.append(side);
|
|
4046
|
+
}
|
|
4014
4047
|
const content = el2("div", "twm-settings__content");
|
|
4015
4048
|
this._content = content;
|
|
4016
|
-
root.append(
|
|
4049
|
+
root.append(content);
|
|
4017
4050
|
this.root = root;
|
|
4018
4051
|
container.appendChild(root);
|
|
4019
4052
|
this._mounted = true;
|
|
@@ -4024,6 +4057,7 @@ var SettingsView = class extends ComponentBase {
|
|
|
4024
4057
|
this._subs.push(this.eventBus.on("settings:registered", (p) => this.#onRegistered(p)));
|
|
4025
4058
|
this.#paintNav();
|
|
4026
4059
|
this.#paintContent();
|
|
4060
|
+
this.#notify();
|
|
4027
4061
|
return root;
|
|
4028
4062
|
}
|
|
4029
4063
|
/** The category on screen (its id), or null. */
|
|
@@ -4034,6 +4068,19 @@ var SettingsView = class extends ComponentBase {
|
|
|
4034
4068
|
getQuery() {
|
|
4035
4069
|
return this._query;
|
|
4036
4070
|
}
|
|
4071
|
+
/** True while the filter is in effect (its trimmed text is not empty). */
|
|
4072
|
+
isFiltering() {
|
|
4073
|
+
return this.#searching();
|
|
4074
|
+
}
|
|
4075
|
+
/**
|
|
4076
|
+
* `fn({ category, query })` after every change of the category, the filter
|
|
4077
|
+
* or the registered categories, until the view is disposed. Answers the
|
|
4078
|
+
* unsubscribe.
|
|
4079
|
+
*/
|
|
4080
|
+
subscribe(fn) {
|
|
4081
|
+
this._listeners.add(fn);
|
|
4082
|
+
return () => this._listeners.delete(fn);
|
|
4083
|
+
}
|
|
4037
4084
|
/** Show one category; clears the filter. */
|
|
4038
4085
|
selectCategory(id) {
|
|
4039
4086
|
if (!this.root) return;
|
|
@@ -4043,6 +4090,7 @@ var SettingsView = class extends ComponentBase {
|
|
|
4043
4090
|
if (this._search) this._search.value = "";
|
|
4044
4091
|
this.#paintNav();
|
|
4045
4092
|
this.#paintContent();
|
|
4093
|
+
this.#notify();
|
|
4046
4094
|
}
|
|
4047
4095
|
/** Search every category; an empty query goes back to the category. */
|
|
4048
4096
|
setQuery(query) {
|
|
@@ -4052,6 +4100,7 @@ var SettingsView = class extends ComponentBase {
|
|
|
4052
4100
|
if (this._search && this._search.value !== q) this._search.value = q;
|
|
4053
4101
|
this.#paintNav();
|
|
4054
4102
|
this.#paintContent();
|
|
4103
|
+
this.#notify();
|
|
4055
4104
|
}
|
|
4056
4105
|
/** Reset one category after `confirm`; resolves true when it was reset. */
|
|
4057
4106
|
async resetCategory(id = this.getCategory()) {
|
|
@@ -4097,6 +4146,7 @@ var SettingsView = class extends ComponentBase {
|
|
|
4097
4146
|
this._nav = null;
|
|
4098
4147
|
this._content = null;
|
|
4099
4148
|
this._search = null;
|
|
4149
|
+
this._listeners.clear();
|
|
4100
4150
|
super.dispose();
|
|
4101
4151
|
}
|
|
4102
4152
|
// ── store events ─────────────────────────────────────────────────────
|
|
@@ -4115,10 +4165,11 @@ var SettingsView = class extends ComponentBase {
|
|
|
4115
4165
|
const touches = this.#searching() || !this._rows.size || paths.some((p) => schema[p]?.category === active);
|
|
4116
4166
|
if (touches) {
|
|
4117
4167
|
this.#paintContent();
|
|
4118
|
-
|
|
4168
|
+
} else {
|
|
4169
|
+
this.#flushAll();
|
|
4170
|
+
this.#syncAll();
|
|
4119
4171
|
}
|
|
4120
|
-
this.#
|
|
4121
|
-
this.#syncAll();
|
|
4172
|
+
this.#notify();
|
|
4122
4173
|
}
|
|
4123
4174
|
/** True while a filter is in effect: the trimmed query is not empty. */
|
|
4124
4175
|
#searching() {
|
|
@@ -4140,20 +4191,13 @@ var SettingsView = class extends ComponentBase {
|
|
|
4140
4191
|
return cats.find((c) => c.id === this._category) ?? cats[0] ?? null;
|
|
4141
4192
|
}
|
|
4142
4193
|
#paintNav() {
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
b.setAttribute("aria-current", "page");
|
|
4151
|
-
}
|
|
4152
|
-
b.append(icon(cat.icon || "tune"), el2("span", "twm-settings__cat-label", cat.label ?? cat.id));
|
|
4153
|
-
if (cat.description) b.title = String(cat.description);
|
|
4154
|
-
return b;
|
|
4155
|
-
});
|
|
4156
|
-
this._nav.replaceChildren(...buttons);
|
|
4194
|
+
if (!this._nav) return;
|
|
4195
|
+
paintCategories(this._nav, this.store.getCategories(), this.#searching() ? null : this.getCategory());
|
|
4196
|
+
}
|
|
4197
|
+
/** Tell the listeners, after the content is drawn (it settles the category). */
|
|
4198
|
+
#notify() {
|
|
4199
|
+
const state = { category: this.getCategory(), query: this._query };
|
|
4200
|
+
for (const fn of [...this._listeners]) fn(state);
|
|
4157
4201
|
}
|
|
4158
4202
|
#paintContent() {
|
|
4159
4203
|
this.#flushAll();
|
|
@@ -4418,6 +4462,54 @@ var SettingsView = class extends ComponentBase {
|
|
|
4418
4462
|
}
|
|
4419
4463
|
}
|
|
4420
4464
|
};
|
|
4465
|
+
var SettingsCategories = class extends ComponentBase {
|
|
4466
|
+
/**
|
|
4467
|
+
* @param {object} o
|
|
4468
|
+
* @param {SettingsView} o.view the view it lists the categories of
|
|
4469
|
+
*/
|
|
4470
|
+
constructor({ view, logger } = {}) {
|
|
4471
|
+
if (!(view instanceof SettingsView)) {
|
|
4472
|
+
throw new TypeError("SettingsCategories: `view` must be a SettingsView");
|
|
4473
|
+
}
|
|
4474
|
+
super({ eventBus: view.eventBus, logger });
|
|
4475
|
+
this.view = view;
|
|
4476
|
+
this.root = null;
|
|
4477
|
+
this._search = null;
|
|
4478
|
+
this._nav = null;
|
|
4479
|
+
this._unsubscribe = null;
|
|
4480
|
+
}
|
|
4481
|
+
/** Build the list into `container` (once). */
|
|
4482
|
+
mount(container) {
|
|
4483
|
+
if (this.root) return this.root;
|
|
4484
|
+
const view = this.view;
|
|
4485
|
+
const root = el2("div", "twm-settings-categories");
|
|
4486
|
+
const search = filterBox((text) => view.setQuery(text));
|
|
4487
|
+
this._search = search.input;
|
|
4488
|
+
this._nav = categoryNav((id) => view.selectCategory(id));
|
|
4489
|
+
root.append(search.label, this._nav, resetAllButton(() => view.resetAll()));
|
|
4490
|
+
this.root = root;
|
|
4491
|
+
container.appendChild(root);
|
|
4492
|
+
this._mounted = true;
|
|
4493
|
+
this._unsubscribe = view.subscribe(() => this.#paint());
|
|
4494
|
+
this.#paint();
|
|
4495
|
+
return root;
|
|
4496
|
+
}
|
|
4497
|
+
dispose() {
|
|
4498
|
+
this._unsubscribe?.();
|
|
4499
|
+
this._unsubscribe = null;
|
|
4500
|
+
this.root?.remove();
|
|
4501
|
+
this.root = null;
|
|
4502
|
+
this._search = null;
|
|
4503
|
+
this._nav = null;
|
|
4504
|
+
super.dispose();
|
|
4505
|
+
}
|
|
4506
|
+
#paint() {
|
|
4507
|
+
const view = this.view;
|
|
4508
|
+
const q = view.getQuery();
|
|
4509
|
+
if (this._search.value !== q) this._search.value = q;
|
|
4510
|
+
paintCategories(this._nav, view.store.getCategories(), view.isFiltering() ? null : view.getCategory());
|
|
4511
|
+
}
|
|
4512
|
+
};
|
|
4421
4513
|
function settingsViewContent(opts = {}) {
|
|
4422
4514
|
return (hostEl, props = {}) => {
|
|
4423
4515
|
const view = new SettingsView({ ...opts, category: props?.category ?? opts.category ?? null });
|
|
@@ -6648,7 +6740,7 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
|
|
|
6648
6740
|
this.container = existingContainer;
|
|
6649
6741
|
this.rootEl = existingContainer.querySelector(".twm-notification-center");
|
|
6650
6742
|
this.listEl = existingContainer.querySelector(".twm-notification-center__list");
|
|
6651
|
-
this.liveRegionEl = existingContainer.querySelector(".notification-center__live-region");
|
|
6743
|
+
this.liveRegionEl = existingContainer.querySelector(".twm-notification-center__live-region");
|
|
6652
6744
|
this._mounted = true;
|
|
6653
6745
|
this.#subscribeEvents();
|
|
6654
6746
|
this.#claimToastBus();
|
|
@@ -6662,7 +6754,7 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
|
|
|
6662
6754
|
this.listEl = document.createElement("div");
|
|
6663
6755
|
this.listEl.className = "twm-notification-center__list";
|
|
6664
6756
|
this.liveRegionEl = document.createElement("div");
|
|
6665
|
-
this.liveRegionEl.className = "notification-center__live-region";
|
|
6757
|
+
this.liveRegionEl.className = "twm-notification-center__live-region";
|
|
6666
6758
|
this.liveRegionEl.setAttribute("aria-live", "polite");
|
|
6667
6759
|
this.liveRegionEl.setAttribute("aria-atomic", "true");
|
|
6668
6760
|
this.liveRegionEl.style.position = "absolute";
|
|
@@ -7085,7 +7177,7 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
|
|
|
7085
7177
|
if (!this.#summaryEl) {
|
|
7086
7178
|
const more2 = document.createElement("button");
|
|
7087
7179
|
more2.type = "button";
|
|
7088
|
-
more2.className = "notification twm-notification--summary";
|
|
7180
|
+
more2.className = "twm-notification twm-notification--summary";
|
|
7089
7181
|
more2.addEventListener("click", () => {
|
|
7090
7182
|
const held = [...this.held];
|
|
7091
7183
|
try {
|
|
@@ -7212,8 +7304,8 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
|
|
|
7212
7304
|
#patchItem(entry, notification) {
|
|
7213
7305
|
const item = entry.el;
|
|
7214
7306
|
if (entry.severity !== notification.severity) {
|
|
7215
|
-
if (entry.severity) item.classList.remove(`notification--${entry.severity}`);
|
|
7216
|
-
item.classList.add("notification", `notification--${notification.severity}`);
|
|
7307
|
+
if (entry.severity) item.classList.remove(`twm-notification--${entry.severity}`);
|
|
7308
|
+
item.classList.add("twm-notification", `twm-notification--${notification.severity}`);
|
|
7217
7309
|
entry.severity = notification.severity;
|
|
7218
7310
|
}
|
|
7219
7311
|
if (entry.titleEl) _NotificationCenter.#setText(entry.titleEl, notification.title);
|
|
@@ -8393,6 +8485,7 @@ export {
|
|
|
8393
8485
|
SCALAR_TYPES,
|
|
8394
8486
|
SETTINGS_TEXT_DEBOUNCE_MS,
|
|
8395
8487
|
STEP_STATES,
|
|
8488
|
+
SettingsCategories,
|
|
8396
8489
|
SettingsView,
|
|
8397
8490
|
SlideOutPanel,
|
|
8398
8491
|
SliderField,
|