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/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
- const side = el2("aside", "twm-settings__sidebar");
3988
- const head = el2("div", "twm-settings__sidebar-title");
3989
- head.append(icon("settings"), el2("span", "", this._title));
3990
- const search = el2("label", "twm-settings__search");
3991
- const input = el2("input", "twm-settings__search-input");
3992
- input.type = "search";
3993
- input.placeholder = "Filter settings\u2026";
3994
- input.autocomplete = "off";
3995
- input.spellcheck = false;
3996
- input.setAttribute("aria-label", "Filter settings");
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(side, content);
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
- return;
4168
+ } else {
4169
+ this.#flushAll();
4170
+ this.#syncAll();
4119
4171
  }
4120
- this.#flushAll();
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
- const active = this.#searching() ? null : this.getCategory();
4144
- const buttons = this.store.getCategories().map((cat) => {
4145
- const b = el2("button", "twm-settings__cat");
4146
- b.type = "button";
4147
- b.dataset.category = cat.id;
4148
- if (cat.id === active) {
4149
- b.classList.add("twm-settings__cat--active");
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,