flexdesk 0.17.0 → 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.
Files changed (45) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +399 -3
  3. package/canvas.js +18 -0
  4. package/charts.js +21 -0
  5. package/core.js +14 -0
  6. package/css/base.css +23 -1
  7. package/css/flexdesk.css +61 -1
  8. package/dist/charts.js +3 -5
  9. package/dist/charts.js.map +1 -1
  10. package/dist/{chunk-Q36EPT2R.js → chunk-LWV23YVE.js} +1614 -138
  11. package/dist/chunk-LWV23YVE.js.map +7 -0
  12. package/dist/{chunk-MCIXFNVF.js → chunk-RMTRWWEO.js} +2 -2
  13. package/dist/flexdesk.css +61 -1
  14. package/dist/flow.js +2 -2
  15. package/dist/flow.js.map +2 -2
  16. package/dist/tiles.js +2 -2
  17. package/dist/widgets.js +186 -55
  18. package/dist/widgets.js.map +2 -2
  19. package/dist/wm.js +90 -10
  20. package/dist/wm.js.map +2 -2
  21. package/editor.js +20 -0
  22. package/flow.js +21 -0
  23. package/host.js +14 -0
  24. package/package.json +10 -1
  25. package/src/flow/canvas_editor/editor.js +1 -1
  26. package/src/tiling/command_palette.js +46 -7
  27. package/src/tiling/content_registry.js +3 -0
  28. package/src/tiling/entity_sources.js +10 -0
  29. package/src/tiling/wm.js +65 -3
  30. package/src/ui/components/cell_editor_host.js +100 -18
  31. package/src/ui/components/cell_editors.js +480 -0
  32. package/src/ui/components/cell_grid.js +121 -8
  33. package/src/ui/components/cell_paste.js +243 -0
  34. package/src/ui/components/data_table.js +96 -10
  35. package/src/ui/components/settings_view.js +192 -51
  36. package/src/ui/components/staged_edits.js +19 -1
  37. package/src/ui/components/tsv.js +74 -0
  38. package/src/ui/notification_center.js +5 -5
  39. package/tiles.js +27 -0
  40. package/widgets.js +69 -0
  41. package/wm.js +28 -0
  42. package/dist/chunk-Q36EPT2R.js.map +0 -7
  43. package/dist/chunk-QWQJUDPD.js +0 -610
  44. package/dist/chunk-QWQJUDPD.js.map +0 -7
  45. /package/dist/{chunk-MCIXFNVF.js.map → chunk-RMTRWWEO.js.map} +0 -0
package/dist/tiles.js CHANGED
@@ -35,8 +35,8 @@ import {
35
35
  purge,
36
36
  resize,
37
37
  strideDownsampleShared
38
- } from "./chunk-MCIXFNVF.js";
39
- import "./chunk-Q36EPT2R.js";
38
+ } from "./chunk-RMTRWWEO.js";
39
+ import "./chunk-LWV23YVE.js";
40
40
  import {
41
41
  isHexColor
42
42
  } from "./chunk-CVMCZZX2.js";
package/dist/widgets.js CHANGED
@@ -14,9 +14,6 @@ import {
14
14
  renderMarkdown,
15
15
  setHelpProvider
16
16
  } from "./chunk-JKZLNKG6.js";
17
- import {
18
- AutocompleteField
19
- } from "./chunk-QWQJUDPD.js";
20
17
  import {
21
18
  TAB_LEVELS,
22
19
  createTabs
@@ -41,30 +38,51 @@ import {
41
38
  } from "./chunk-D3COLZZ2.js";
42
39
  import {
43
40
  ACTIONS,
41
+ AutocompleteField,
42
+ CELL_EDITORS,
44
43
  CellEditorHost,
45
44
  CellSelection,
46
45
  DEFAULT_HISTORY_LIMIT,
47
46
  DataTable,
47
+ EDITOR_MESSAGES,
48
48
  ISO_DATE,
49
49
  ISO_DATETIME,
50
50
  PAGING_HARD_LIMIT,
51
51
  PAGING_SOFT_LIMIT,
52
52
  StagedHistory,
53
53
  attachInlineRenamer,
54
+ createBoolEditor,
54
55
  createChipsFilter,
56
+ createChoiceEditor,
57
+ createDateTimeEditor,
58
+ createMultiTextEditor,
59
+ createNumberEditor,
55
60
  createRafResizeObserver,
56
61
  createTextEditor,
62
+ dateOrder,
57
63
  detectSeries,
64
+ editorTypeOf,
58
65
  formatDate,
66
+ formatDateTimeForEdit,
59
67
  formatDuration,
68
+ formatNumberForEdit,
69
+ fromTSV,
60
70
  matchDateFilter,
61
71
  matchDurationFilter,
72
+ normalizeOptions,
73
+ numberSeparators,
74
+ parseBool,
75
+ parseChoice,
62
76
  parseDatePeriod,
77
+ parseDateTime,
63
78
  parseDateValue,
64
79
  parseDuration,
80
+ parseNumber,
65
81
  projectSeries,
66
- resolvePaging
67
- } from "./chunk-Q36EPT2R.js";
82
+ resolvePaging,
83
+ toTSV,
84
+ tsvCell
85
+ } from "./chunk-LWV23YVE.js";
68
86
  import {
69
87
  ColorField,
70
88
  DEFAULT_WINDOW_STORAGE_KEY,
@@ -3921,6 +3939,51 @@ function fitNumber(n, { min, max, step }) {
3921
3939
  if (lo != null && at(k) < lo) k = Math.ceil((lo - base) / s - EPS);
3922
3940
  return at(k);
3923
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
+ }
3924
3987
  var SettingsView = class extends ComponentBase {
3925
3988
  /**
3926
3989
  * @param {object} o
@@ -3930,6 +3993,8 @@ var SettingsView = class extends ComponentBase {
3930
3993
  * @param {string} [o.category] the category shown first (default: the first)
3931
3994
  * @param {number} [o.textDebounceMs]
3932
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
3933
3998
  */
3934
3999
  constructor({
3935
4000
  store,
@@ -3938,6 +4003,7 @@ var SettingsView = class extends ComponentBase {
3938
4003
  category = null,
3939
4004
  textDebounceMs = SETTINGS_TEXT_DEBOUNCE_MS,
3940
4005
  title = "Settings",
4006
+ sidebar = true,
3941
4007
  logger
3942
4008
  } = {}) {
3943
4009
  super({ eventBus, logger });
@@ -3953,6 +4019,8 @@ var SettingsView = class extends ComponentBase {
3953
4019
  this._query = "";
3954
4020
  this._debounceMs = textDebounceMs;
3955
4021
  this._title = title;
4022
+ this._sidebar = sidebar !== false;
4023
+ this._listeners = /* @__PURE__ */ new Set();
3956
4024
  this.root = null;
3957
4025
  this._nav = null;
3958
4026
  this._content = null;
@@ -3966,36 +4034,19 @@ var SettingsView = class extends ComponentBase {
3966
4034
  mount(container) {
3967
4035
  if (this.root) return this.root;
3968
4036
  const root = el2("div", "twm-settings");
3969
- const side = el2("aside", "twm-settings__sidebar");
3970
- const head = el2("div", "twm-settings__sidebar-title");
3971
- head.append(icon("settings"), el2("span", "", this._title));
3972
- const search = el2("label", "twm-settings__search");
3973
- const input = el2("input", "twm-settings__search-input");
3974
- input.type = "search";
3975
- input.placeholder = "Filter settings\u2026";
3976
- input.autocomplete = "off";
3977
- input.spellcheck = false;
3978
- input.setAttribute("aria-label", "Filter settings");
3979
- input.addEventListener("input", () => this.setQuery(input.value));
3980
- search.append(icon("search"), input);
3981
- this._search = input;
3982
- const nav = el2("nav", "twm-settings__nav");
3983
- nav.setAttribute("aria-label", "Settings categories");
3984
- nav.addEventListener("click", (e) => {
3985
- const btn = e.target.closest?.("[data-category]");
3986
- if (btn && nav.contains(btn)) this.selectCategory(btn.dataset.category);
3987
- });
3988
- this._nav = nav;
3989
- const resetAll = el2("button", "twm-btn twm-settings__reset-all");
3990
- resetAll.type = "button";
3991
- resetAll.append(icon("restart_alt"), el2("span", "", "Reset all"));
3992
- resetAll.addEventListener("click", () => {
3993
- this.resetAll();
3994
- });
3995
- 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
+ }
3996
4047
  const content = el2("div", "twm-settings__content");
3997
4048
  this._content = content;
3998
- root.append(side, content);
4049
+ root.append(content);
3999
4050
  this.root = root;
4000
4051
  container.appendChild(root);
4001
4052
  this._mounted = true;
@@ -4006,6 +4057,7 @@ var SettingsView = class extends ComponentBase {
4006
4057
  this._subs.push(this.eventBus.on("settings:registered", (p) => this.#onRegistered(p)));
4007
4058
  this.#paintNav();
4008
4059
  this.#paintContent();
4060
+ this.#notify();
4009
4061
  return root;
4010
4062
  }
4011
4063
  /** The category on screen (its id), or null. */
@@ -4016,6 +4068,19 @@ var SettingsView = class extends ComponentBase {
4016
4068
  getQuery() {
4017
4069
  return this._query;
4018
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
+ }
4019
4084
  /** Show one category; clears the filter. */
4020
4085
  selectCategory(id) {
4021
4086
  if (!this.root) return;
@@ -4025,6 +4090,7 @@ var SettingsView = class extends ComponentBase {
4025
4090
  if (this._search) this._search.value = "";
4026
4091
  this.#paintNav();
4027
4092
  this.#paintContent();
4093
+ this.#notify();
4028
4094
  }
4029
4095
  /** Search every category; an empty query goes back to the category. */
4030
4096
  setQuery(query) {
@@ -4034,6 +4100,7 @@ var SettingsView = class extends ComponentBase {
4034
4100
  if (this._search && this._search.value !== q) this._search.value = q;
4035
4101
  this.#paintNav();
4036
4102
  this.#paintContent();
4103
+ this.#notify();
4037
4104
  }
4038
4105
  /** Reset one category after `confirm`; resolves true when it was reset. */
4039
4106
  async resetCategory(id = this.getCategory()) {
@@ -4079,6 +4146,7 @@ var SettingsView = class extends ComponentBase {
4079
4146
  this._nav = null;
4080
4147
  this._content = null;
4081
4148
  this._search = null;
4149
+ this._listeners.clear();
4082
4150
  super.dispose();
4083
4151
  }
4084
4152
  // ── store events ─────────────────────────────────────────────────────
@@ -4097,10 +4165,11 @@ var SettingsView = class extends ComponentBase {
4097
4165
  const touches = this.#searching() || !this._rows.size || paths.some((p) => schema[p]?.category === active);
4098
4166
  if (touches) {
4099
4167
  this.#paintContent();
4100
- return;
4168
+ } else {
4169
+ this.#flushAll();
4170
+ this.#syncAll();
4101
4171
  }
4102
- this.#flushAll();
4103
- this.#syncAll();
4172
+ this.#notify();
4104
4173
  }
4105
4174
  /** True while a filter is in effect: the trimmed query is not empty. */
4106
4175
  #searching() {
@@ -4122,20 +4191,13 @@ var SettingsView = class extends ComponentBase {
4122
4191
  return cats.find((c) => c.id === this._category) ?? cats[0] ?? null;
4123
4192
  }
4124
4193
  #paintNav() {
4125
- const active = this.#searching() ? null : this.getCategory();
4126
- const buttons = this.store.getCategories().map((cat) => {
4127
- const b = el2("button", "twm-settings__cat");
4128
- b.type = "button";
4129
- b.dataset.category = cat.id;
4130
- if (cat.id === active) {
4131
- b.classList.add("twm-settings__cat--active");
4132
- b.setAttribute("aria-current", "page");
4133
- }
4134
- b.append(icon(cat.icon || "tune"), el2("span", "twm-settings__cat-label", cat.label ?? cat.id));
4135
- if (cat.description) b.title = String(cat.description);
4136
- return b;
4137
- });
4138
- 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);
4139
4201
  }
4140
4202
  #paintContent() {
4141
4203
  this.#flushAll();
@@ -4400,6 +4462,54 @@ var SettingsView = class extends ComponentBase {
4400
4462
  }
4401
4463
  }
4402
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
+ };
4403
4513
  function settingsViewContent(opts = {}) {
4404
4514
  return (hostEl, props = {}) => {
4405
4515
  const view = new SettingsView({ ...opts, category: props?.category ?? opts.category ?? null });
@@ -6630,7 +6740,7 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
6630
6740
  this.container = existingContainer;
6631
6741
  this.rootEl = existingContainer.querySelector(".twm-notification-center");
6632
6742
  this.listEl = existingContainer.querySelector(".twm-notification-center__list");
6633
- this.liveRegionEl = existingContainer.querySelector(".notification-center__live-region");
6743
+ this.liveRegionEl = existingContainer.querySelector(".twm-notification-center__live-region");
6634
6744
  this._mounted = true;
6635
6745
  this.#subscribeEvents();
6636
6746
  this.#claimToastBus();
@@ -6644,7 +6754,7 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
6644
6754
  this.listEl = document.createElement("div");
6645
6755
  this.listEl.className = "twm-notification-center__list";
6646
6756
  this.liveRegionEl = document.createElement("div");
6647
- this.liveRegionEl.className = "notification-center__live-region";
6757
+ this.liveRegionEl.className = "twm-notification-center__live-region";
6648
6758
  this.liveRegionEl.setAttribute("aria-live", "polite");
6649
6759
  this.liveRegionEl.setAttribute("aria-atomic", "true");
6650
6760
  this.liveRegionEl.style.position = "absolute";
@@ -7067,7 +7177,7 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
7067
7177
  if (!this.#summaryEl) {
7068
7178
  const more2 = document.createElement("button");
7069
7179
  more2.type = "button";
7070
- more2.className = "notification twm-notification--summary";
7180
+ more2.className = "twm-notification twm-notification--summary";
7071
7181
  more2.addEventListener("click", () => {
7072
7182
  const held = [...this.held];
7073
7183
  try {
@@ -7194,8 +7304,8 @@ var NotificationCenter = class _NotificationCenter extends ComponentBase {
7194
7304
  #patchItem(entry, notification) {
7195
7305
  const item = entry.el;
7196
7306
  if (entry.severity !== notification.severity) {
7197
- if (entry.severity) item.classList.remove(`notification--${entry.severity}`);
7198
- 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}`);
7199
7309
  entry.severity = notification.severity;
7200
7310
  }
7201
7311
  if (entry.titleEl) _NotificationCenter.#setText(entry.titleEl, notification.title);
@@ -8337,6 +8447,7 @@ export {
8337
8447
  ALERT_LEVELS,
8338
8448
  ActionDropdown,
8339
8449
  AutocompleteField,
8450
+ CELL_EDITORS,
8340
8451
  COLUMN_FIT,
8341
8452
  CONVERSATION_COMPOSER_MAX_PX,
8342
8453
  CONVERSATION_PIN_WITHIN_PX,
@@ -8352,6 +8463,7 @@ export {
8352
8463
  DataTable,
8353
8464
  DocumentOutline,
8354
8465
  DragReorder,
8466
+ EDITOR_MESSAGES,
8355
8467
  FactTable,
8356
8468
  HelpModal,
8357
8469
  ISO_DATE,
@@ -8373,6 +8485,7 @@ export {
8373
8485
  SCALAR_TYPES,
8374
8486
  SETTINGS_TEXT_DEBOUNCE_MS,
8375
8487
  STEP_STATES,
8488
+ SettingsCategories,
8376
8489
  SettingsView,
8377
8490
  SlideOutPanel,
8378
8491
  SliderField,
@@ -8390,11 +8503,16 @@ export {
8390
8503
  attachInlineRenamer,
8391
8504
  barCovered,
8392
8505
  createAlertLog,
8506
+ createBoolEditor,
8393
8507
  createChipsFilter,
8508
+ createChoiceEditor,
8509
+ createDateTimeEditor,
8394
8510
  createDetailHeader,
8395
8511
  createDocumentOutline,
8396
8512
  createFactTable,
8397
8513
  createMenuBar,
8514
+ createMultiTextEditor,
8515
+ createNumberEditor,
8398
8516
  createRafResizeObserver,
8399
8517
  createRecordList,
8400
8518
  createScrollLock,
@@ -8406,14 +8524,19 @@ export {
8406
8524
  createTreeCategory,
8407
8525
  createTreeItem,
8408
8526
  createTreeNode,
8527
+ dateOrder,
8409
8528
  dayLabel,
8410
8529
  detectSeries,
8530
+ editorTypeOf,
8411
8531
  endsTextSelection,
8412
8532
  filterTree,
8413
8533
  fitColumns,
8414
8534
  formatDate,
8535
+ formatDateTimeForEdit,
8415
8536
  formatDuration,
8416
8537
  formatElapsed,
8538
+ formatNumberForEdit,
8539
+ fromTSV,
8417
8540
  getToastBus,
8418
8541
  helpCategories,
8419
8542
  helpCopy,
@@ -8438,12 +8561,18 @@ export {
8438
8561
  mountGalleryPicker,
8439
8562
  normalizeHex,
8440
8563
  normalizeLevel,
8564
+ normalizeOptions,
8565
+ numberSeparators,
8441
8566
  openConfirm,
8442
8567
  openForm,
8443
8568
  openModal,
8569
+ parseBool,
8570
+ parseChoice,
8444
8571
  parseDatePeriod,
8572
+ parseDateTime,
8445
8573
  parseDateValue,
8446
8574
  parseDuration,
8575
+ parseNumber,
8447
8576
  projectSeries,
8448
8577
  refTypesFor,
8449
8578
  renderMarkdown,
@@ -8466,12 +8595,14 @@ export {
8466
8595
  showSelectDialog,
8467
8596
  suppressNativeContextMenu,
8468
8597
  thumbGeometry,
8598
+ toTSV,
8469
8599
  toastError,
8470
8600
  toastInfo,
8471
8601
  toastSuccess,
8472
8602
  toastWarn,
8473
8603
  toggleCategory,
8474
8604
  toggleNode,
8605
+ tsvCell,
8475
8606
  updateDetailHeader,
8476
8607
  wheelStripSideways,
8477
8608
  worstLevel