flexdesk 0.16.1 → 0.17.1

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 (262) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +0 -0
  3. package/README.md +2435 -42
  4. package/THIRD_PARTY_LICENSES.md +0 -0
  5. package/canvas.js +18 -0
  6. package/charts.js +21 -0
  7. package/core.js +14 -0
  8. package/css/base.css +1834 -75
  9. package/css/flexdesk.css +3165 -159
  10. package/css/overrides.css +16 -0
  11. package/css/reset.css +0 -0
  12. package/css/tokens.css +0 -0
  13. package/dist/canvas.js +6 -239
  14. package/dist/canvas.js.map +3 -3
  15. package/dist/charts.js +707 -18
  16. package/dist/charts.js.map +4 -4
  17. package/dist/chunk-3PHCPZHT.js +0 -0
  18. package/dist/chunk-3PHCPZHT.js.map +0 -0
  19. package/dist/chunk-4A7SJC3T.js +503 -0
  20. package/dist/chunk-4A7SJC3T.js.map +7 -0
  21. package/dist/{chunk-6JSOVNID.js → chunk-CVMCZZX2.js} +635 -10
  22. package/dist/chunk-CVMCZZX2.js.map +7 -0
  23. package/dist/chunk-D3COLZZ2.js +1311 -0
  24. package/dist/chunk-D3COLZZ2.js.map +7 -0
  25. package/dist/chunk-FOOS3T5L.js +0 -0
  26. package/dist/chunk-FOOS3T5L.js.map +0 -0
  27. package/dist/{chunk-SN4LSOAR.js → chunk-GOQLR5WR.js} +452 -2
  28. package/dist/chunk-GOQLR5WR.js.map +7 -0
  29. package/dist/{chunk-FL5KFNQH.js → chunk-HNU6MM2B.js} +52 -17
  30. package/dist/chunk-HNU6MM2B.js.map +7 -0
  31. package/dist/chunk-HYJYYEPK.js +375 -0
  32. package/dist/chunk-HYJYYEPK.js.map +7 -0
  33. package/dist/chunk-JKZLNKG6.js +2041 -0
  34. package/dist/chunk-JKZLNKG6.js.map +7 -0
  35. package/dist/chunk-JYWURG5T.js +0 -0
  36. package/dist/chunk-JYWURG5T.js.map +0 -0
  37. package/dist/{chunk-FBGP5KN5.js → chunk-LWV23YVE.js} +5073 -532
  38. package/dist/chunk-LWV23YVE.js.map +7 -0
  39. package/dist/chunk-N7HC3ADV.js +30 -0
  40. package/dist/chunk-N7HC3ADV.js.map +7 -0
  41. package/dist/{chunk-RQM5Z2MU.js → chunk-RMTRWWEO.js} +633 -311
  42. package/dist/chunk-RMTRWWEO.js.map +7 -0
  43. package/dist/{chunk-NLTOY57M.js → chunk-UURZ37SD.js} +28 -48
  44. package/dist/chunk-UURZ37SD.js.map +7 -0
  45. package/dist/{chunk-B5Q53LAJ.js → chunk-UWO43BZU.js} +2 -2
  46. package/dist/chunk-UWO43BZU.js.map +7 -0
  47. package/dist/chunk-WVFGV5FT.js +0 -0
  48. package/dist/chunk-WVFGV5FT.js.map +0 -0
  49. package/dist/core.js +170 -118
  50. package/dist/core.js.map +2 -2
  51. package/dist/editor.js +156 -70
  52. package/dist/editor.js.map +2 -2
  53. package/dist/flexdesk.css +3165 -159
  54. package/dist/flow.js +2789 -306
  55. package/dist/flow.js.map +4 -4
  56. package/dist/host.js +109 -3
  57. package/dist/host.js.map +3 -3
  58. package/dist/reset.css +0 -0
  59. package/dist/tile_registry-6WZPTUZV.js +0 -0
  60. package/dist/tile_registry-6WZPTUZV.js.map +0 -0
  61. package/dist/tiles.js +657 -234
  62. package/dist/tiles.js.map +4 -4
  63. package/dist/tokens.css +0 -0
  64. package/dist/widgets.js +3290 -2124
  65. package/dist/widgets.js.map +4 -4
  66. package/dist/wm.js +1227 -347
  67. package/dist/wm.js.map +4 -4
  68. package/editor.js +20 -0
  69. package/flow.js +21 -0
  70. package/host.js +14 -0
  71. package/package.json +12 -1
  72. package/src/canvas/canvas_adapter.js +5 -0
  73. package/src/canvas/edge_router.js +0 -0
  74. package/src/canvas/graph/connector_router.js +0 -0
  75. package/src/canvas/graph/history_service.js +0 -0
  76. package/src/canvas/graph/node_platform.js +0 -0
  77. package/src/canvas/index.js +1 -0
  78. package/src/canvas/layered_layout.js +244 -0
  79. package/src/charts/chart_types.js +0 -0
  80. package/src/charts/data_series_plot_window.js +0 -0
  81. package/src/charts/downsample.js +2 -2
  82. package/src/charts/plot_config.js +53 -29
  83. package/src/charts/plot_config_editor.js +627 -0
  84. package/src/charts/plot_grid.js +449 -0
  85. package/src/charts/plot_popout_window.js +103 -317
  86. package/src/charts/plotly_wrapper.js +22 -2
  87. package/src/charts/series_transforms.js +206 -0
  88. package/src/core/event_bus.js +0 -0
  89. package/src/core/logging.js +1 -1
  90. package/src/core/settings.js +105 -19
  91. package/src/core/state_guard.js +93 -51
  92. package/src/core/state_guard_config.js +85 -74
  93. package/src/core/state_machine.js +0 -0
  94. package/src/editor/editor_pane.js +3 -3
  95. package/src/editor/monaco_editor_factory.js +124 -60
  96. package/src/editor/monaco_loader.js +103 -28
  97. package/src/editor/notebook_search.js +0 -0
  98. package/src/editor/notebook_tab_bar.js +38 -52
  99. package/src/editor/notebook_undo_manager.js +0 -0
  100. package/src/editor/split_pane_container.js +6 -3
  101. package/src/flow/canvas_editor/connections.js +90 -0
  102. package/src/flow/canvas_editor/editor.js +2351 -153
  103. package/src/flow/canvas_editor/index.js +2 -1
  104. package/src/flow/canvas_editor/layout.js +119 -12
  105. package/src/flow/canvas_editor/strings.js +80 -0
  106. package/src/flow/kit/always_before.js +0 -0
  107. package/src/flow/kit/catalogue.js +97 -3
  108. package/src/flow/kit/chip_input.js +0 -0
  109. package/src/flow/kit/dom.js +0 -0
  110. package/src/flow/kit/findings.js +0 -0
  111. package/src/flow/kit/history.js +0 -0
  112. package/src/flow/kit/index.js +3 -3
  113. package/src/flow/kit/keys.js +7 -1
  114. package/src/flow/kit/logic_graph.js +97 -15
  115. package/src/flow/kit/references.js +0 -0
  116. package/src/flow/kit/settings_panel.js +0 -0
  117. package/src/flow/kit/settings_schema.js +2 -1
  118. package/src/flow/kit/step_picker.js +0 -0
  119. package/src/flow/kit/strings.js +3 -1
  120. package/src/flow/kit/value_picker.js +0 -0
  121. package/src/flow/kit/widgets.js +86 -109
  122. package/src/flow/lanes/editor.js +136 -14
  123. package/src/flow/lanes/index.js +3 -1
  124. package/src/flow/lanes/layout.js +0 -0
  125. package/src/flow/lanes/pipeline.js +44 -2
  126. package/src/flow/lanes/preview.js +0 -0
  127. package/src/flow/lanes/strings.js +1 -0
  128. package/src/flow/outline/editor.js +0 -0
  129. package/src/flow/outline/index.js +0 -0
  130. package/src/flow/outline/mapping.js +0 -0
  131. package/src/flow/outline/menu.js +0 -0
  132. package/src/flow/outline/operations.js +0 -0
  133. package/src/flow/outline/recognise.js +0 -0
  134. package/src/flow/outline/strings.js +0 -0
  135. package/src/help/help_modal.js +168 -42
  136. package/src/help/help_registry.js +12 -1
  137. package/src/host/external_links.js +0 -0
  138. package/src/host/file_picker.js +117 -0
  139. package/src/host/host.js +10 -2
  140. package/src/host/photino_host.js +9 -0
  141. package/src/host/photino_host.md +21 -3
  142. package/src/host/pywebview_host.js +10 -0
  143. package/src/tiles/chart_tiles.js +53 -245
  144. package/src/tiles/config_schema.js +8 -0
  145. package/src/tiles/figure_tile.js +322 -0
  146. package/src/tiles/layout_persistence.js +3 -3
  147. package/src/tiles/list_tiles.js +7 -7
  148. package/src/tiles/scope_picker.js +0 -0
  149. package/src/tiles/tile_base.js +1 -1
  150. package/src/tiles/tile_config_form.js +442 -0
  151. package/src/tiles/tile_grid.js +2 -0
  152. package/src/tiles/tile_registry.js +0 -0
  153. package/src/tiling/bottom_panel_tabs.js +67 -99
  154. package/src/tiling/command_palette.js +64 -19
  155. package/src/tiling/content_registry.js +4 -1
  156. package/src/tiling/desktops.js +11 -1
  157. package/src/tiling/entity_sources.js +0 -0
  158. package/src/tiling/keymap.js +403 -128
  159. package/src/tiling/kind_taxonomy.js +78 -0
  160. package/src/tiling/landing_table.js +0 -0
  161. package/src/tiling/loading_overlay.js +0 -0
  162. package/src/tiling/nav_panel.js +59 -10
  163. package/src/tiling/page_factory.js +5 -5
  164. package/src/tiling/panel_availability.js +49 -0
  165. package/src/tiling/panel_keys.js +0 -0
  166. package/src/tiling/shell.js +114 -12
  167. package/src/tiling/tab_strip.js +0 -0
  168. package/src/tiling/tile_breadcrumb.js +0 -0
  169. package/src/tiling/tile_renderer.js +105 -9
  170. package/src/tiling/tile_tab_menu.js +0 -0
  171. package/src/tiling/tile_tree.js +0 -0
  172. package/src/tiling/wm.js +562 -30
  173. package/src/tiling/zoom.js +0 -0
  174. package/src/ui/base/component_base.js +0 -0
  175. package/src/ui/base/controller_base.js +0 -0
  176. package/src/ui/base/page_base.js +0 -0
  177. package/src/ui/components/about_dialog.js +0 -0
  178. package/src/ui/components/action_dropdown.js +30 -2
  179. package/src/ui/components/alert_log.js +0 -0
  180. package/src/ui/components/alerts_pane.js +0 -0
  181. package/src/ui/components/attribute_list_editor.js +0 -0
  182. package/src/ui/components/autocomplete_field.js +0 -0
  183. package/src/ui/components/cell_editor_host.js +293 -0
  184. package/src/ui/components/cell_editors.js +480 -0
  185. package/src/ui/components/cell_grid.js +761 -0
  186. package/src/ui/components/cell_paste.js +243 -0
  187. package/src/ui/components/cell_selection.js +220 -0
  188. package/src/ui/components/choice_controls.js +351 -0
  189. package/src/ui/components/color_field.js +211 -0
  190. package/src/ui/components/color_list_field.js +210 -0
  191. package/src/ui/components/column_fit.js +0 -0
  192. package/src/ui/components/column_groups.js +314 -0
  193. package/src/ui/components/column_order.js +142 -0
  194. package/src/ui/components/computing_status_window.js +98 -25
  195. package/src/ui/components/confirm_dialog.js +81 -38
  196. package/src/ui/components/context_menu.js +2 -2
  197. package/src/ui/components/conversation_view.js +440 -0
  198. package/src/ui/components/crosstab.js +289 -0
  199. package/src/ui/components/data_table.js +696 -71
  200. package/src/ui/components/detail_header.js +22 -11
  201. package/src/ui/components/document_outline.js +397 -0
  202. package/src/ui/components/drag_reorder.js +0 -0
  203. package/src/ui/components/fact_table.js +0 -0
  204. package/src/ui/components/fill_handle.js +322 -0
  205. package/src/ui/components/gallery_picker.js +19 -8
  206. package/src/ui/components/header_actions.js +55 -0
  207. package/src/ui/components/inline_renamer.js +0 -0
  208. package/src/ui/components/job_progress.js +51 -0
  209. package/src/ui/components/managed_window.js +27 -4
  210. package/src/ui/components/menu_bar.js +745 -0
  211. package/src/ui/components/modal.js +449 -75
  212. package/src/ui/components/notification_history.js +3 -3
  213. package/src/ui/components/progress.js +483 -0
  214. package/src/ui/components/record_list.js +0 -0
  215. package/src/ui/components/row_activation.js +0 -0
  216. package/src/ui/components/section_stack.js +298 -0
  217. package/src/ui/components/series.js +173 -0
  218. package/src/ui/components/settings_view.js +634 -0
  219. package/src/ui/components/slide_out_panel.js +43 -6
  220. package/src/ui/components/slider_field.js +275 -18
  221. package/src/ui/components/sortable_list.js +0 -0
  222. package/src/ui/components/staged_edits.js +361 -0
  223. package/src/ui/components/staged_history.js +172 -0
  224. package/src/ui/components/table_filter_chips.js +0 -0
  225. package/src/ui/components/table_state_store.js +0 -0
  226. package/src/ui/components/table_structure.js +182 -0
  227. package/src/ui/components/table_values.js +0 -0
  228. package/src/ui/components/tabs.js +497 -0
  229. package/src/ui/components/tag_input.js +215 -0
  230. package/src/ui/components/toast.js +25 -8
  231. package/src/ui/components/toggle_field.js +175 -0
  232. package/src/ui/components/tree_view.js +55 -34
  233. package/src/ui/components/tsv.js +74 -0
  234. package/src/ui/components/window_taskbar.js +0 -0
  235. package/src/ui/components/wizard.js +563 -0
  236. package/src/ui/controllers/window_chrome_controller.js +2 -2
  237. package/src/ui/notification_center.js +375 -119
  238. package/src/ui/utils/auto_scrollbars.js +0 -0
  239. package/src/ui/utils/markdown.js +709 -0
  240. package/src/ui/utils/native_context_menu.js +0 -0
  241. package/src/ui/utils/overlay_scrollbar.js +16 -12
  242. package/src/ui/utils/raf_resize_observer.js +0 -0
  243. package/src/ui/utils/scroll_lock.js +0 -0
  244. package/src/ui/utils/strip_scroll.js +55 -0
  245. package/src/ui/utils/tooltip_service.js +0 -8
  246. package/tiles.js +27 -0
  247. package/widgets.js +69 -0
  248. package/wm.js +28 -0
  249. package/dist/chunk-6JSOVNID.js.map +0 -7
  250. package/dist/chunk-B5Q53LAJ.js.map +0 -7
  251. package/dist/chunk-FBGP5KN5.js.map +0 -7
  252. package/dist/chunk-FL5KFNQH.js.map +0 -7
  253. package/dist/chunk-NLTOY57M.js.map +0 -7
  254. package/dist/chunk-NNFTETK3.js +0 -633
  255. package/dist/chunk-NNFTETK3.js.map +0 -7
  256. package/dist/chunk-O5OHMWBB.js +0 -284
  257. package/dist/chunk-O5OHMWBB.js.map +0 -7
  258. package/dist/chunk-RQM5Z2MU.js.map +0 -7
  259. package/dist/chunk-SN4LSOAR.js.map +0 -7
  260. package/dist/chunk-W6FBJLPW.js +0 -367
  261. package/dist/chunk-W6FBJLPW.js.map +0 -7
  262. package/src/ui/controllers/panel_state_machine.js +0 -634
@@ -1,633 +0,0 @@
1
- import {
2
- ManagedWindow
3
- } from "./chunk-6JSOVNID.js";
4
-
5
- // src/ui/components/modal.js
6
- var _modalHost = null;
7
- function setModalHost(el) {
8
- _modalHost = el || null;
9
- }
10
- function modalHost() {
11
- return _modalHost;
12
- }
13
- var _modalSeq = 1;
14
- function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFieldChange = null } = {}) {
15
- return new Promise((resolve) => {
16
- const body = document.createElement("div");
17
- body.className = "twm-modal__body-host";
18
- body.innerHTML = `
19
- <form class="twm-modal__form">
20
- <div class="twm-modal__fields">
21
- ${fields.map((f) => _renderEntry(f, defaults[f.name] ?? f.default)).join("")}
22
- </div>
23
- <div class="twm-modal__error" data-role="form-error" hidden></div>
24
- <div class="twm-modal__actions">
25
- <button type="button" class="twm-btn" data-action="cancel">Cancel</button>
26
- <button type="submit" class="twm-btn twm-btn--primary">${submitLabel}</button>
27
- </div>
28
- </form>
29
- `;
30
- const win = new ManagedWindow({
31
- container: _modalHost,
32
- id: `twm-modal-${_modalSeq++}`,
33
- title: title || "Dialog",
34
- icon: "edit_note",
35
- content: body,
36
- modal: true,
37
- canMinimize: false,
38
- canMaximize: false,
39
- canResize: false,
40
- canDrag: true,
41
- defaultWidth: _estimateWidth(fields),
42
- defaultHeight: _estimateHeight(fields),
43
- onClose: () => {
44
- if (!_resolved) {
45
- _resolved = true;
46
- resolve(null);
47
- }
48
- }
49
- });
50
- win.show();
51
- let _resolved = false;
52
- const close = (result) => {
53
- if (_resolved) return;
54
- _resolved = true;
55
- resolve(result);
56
- try {
57
- win.close({ force: true });
58
- } catch {
59
- }
60
- };
61
- const errEl = body.querySelector('[data-role="form-error"]');
62
- const showError = (msg) => {
63
- if (!errEl) return;
64
- errEl.textContent = msg;
65
- errEl.hidden = !msg;
66
- };
67
- body.querySelector('[data-action="cancel"]')?.addEventListener("click", () => close(null));
68
- const formFields = fields.filter((f) => !f.section);
69
- for (const f of formFields) {
70
- if (f.type === "select" && f.create) _wireComboboxHint(body, f);
71
- if (f.type === "picker") _wirePicker(body, f);
72
- }
73
- const rowHidden = (name) => !!body.querySelector(`[data-field-row="${name}"]`)?.hidden;
74
- const clearFieldError = (name) => {
75
- const row = body.querySelector(`[data-field-row="${name}"]`);
76
- const errEl2 = body.querySelector(`[data-field-error="${name}"]`);
77
- if (row) row.classList.remove("twm-is-invalid");
78
- if (errEl2) {
79
- errEl2.textContent = "";
80
- errEl2.hidden = true;
81
- }
82
- };
83
- const setFieldError = (name, msg) => {
84
- const row = body.querySelector(`[data-field-row="${name}"]`);
85
- const errEl2 = body.querySelector(`[data-field-error="${name}"]`);
86
- if (row) row.classList.add("twm-is-invalid");
87
- if (errEl2) {
88
- errEl2.textContent = msg;
89
- errEl2.hidden = false;
90
- }
91
- };
92
- for (const f of formFields) {
93
- const el = body.querySelector(`[name="${f.name}"]`);
94
- el?.addEventListener("input", () => clearFieldError(f.name));
95
- el?.addEventListener("change", () => clearFieldError(f.name));
96
- }
97
- if (typeof onFieldChange === "function") {
98
- const fieldOf = (name) => formFields.find((f) => f.name === name) || null;
99
- const api = {
100
- setVisible(name, on) {
101
- const row = body.querySelector(`[data-field-row="${name}"]`);
102
- if (!row) return;
103
- row.hidden = !on;
104
- row.style.display = on ? "" : "none";
105
- clearFieldError(name);
106
- for (const el of row.querySelectorAll("input, select, textarea")) {
107
- if (!on && el.required) {
108
- el.required = false;
109
- el.dataset.twmRequired = "1";
110
- } else if (on && el.dataset.twmRequired) {
111
- el.required = true;
112
- delete el.dataset.twmRequired;
113
- }
114
- }
115
- },
116
- get(name) {
117
- const el = body.querySelector(`[name="${name}"]`);
118
- if (!el) return void 0;
119
- return el.type === "checkbox" ? !!el.checked : el.value;
120
- },
121
- set(name, value) {
122
- const el = body.querySelector(`[name="${name}"]`);
123
- if (!el) return;
124
- if (el.type === "checkbox") {
125
- el.checked = !!value;
126
- return;
127
- }
128
- el.value = value == null ? "" : String(value);
129
- const f = fieldOf(name);
130
- if (f?.type === "picker") {
131
- const display = body.querySelector(`[data-picker-display="${name}"]`);
132
- if (display) display.value = _pickerLabel(f, el.value);
133
- }
134
- }
135
- };
136
- const fire = (name) => {
137
- try {
138
- onFieldChange(name, name == null ? void 0 : api.get(name), api);
139
- } catch (err) {
140
- console.error("[modal] onFieldChange threw", err);
141
- }
142
- };
143
- for (const f of formFields) {
144
- const el = body.querySelector(`[name="${f.name}"]`);
145
- el?.addEventListener("change", () => fire(f.name));
146
- }
147
- fire(null);
148
- }
149
- body.querySelector("form").addEventListener("submit", async (e) => {
150
- e.preventDefault();
151
- const submitBtn = body.querySelector('button[type="submit"]');
152
- showError("");
153
- const data = {};
154
- for (const f of formFields) {
155
- const el = body.querySelector(`[name="${f.name}"]`);
156
- if (!el) continue;
157
- let v;
158
- if (f.type === "checkbox") {
159
- v = !!el.checked;
160
- } else if (f.type === "number") {
161
- v = el.value === "" ? null : Number(el.value);
162
- } else {
163
- v = el.value;
164
- }
165
- if (f.type === "select" && f.create) {
166
- const spec = _createSpec(f);
167
- const known = new Set(_optionValues(f.options));
168
- if (v && !known.has(v)) {
169
- if (submitBtn) submitBtn.disabled = true;
170
- let created;
171
- let failMsg = null;
172
- try {
173
- created = await spec.onCreate(v);
174
- } catch (err) {
175
- created = null;
176
- failMsg = err?.message || String(err);
177
- }
178
- if (submitBtn) submitBtn.disabled = false;
179
- if (created == null) {
180
- const hint = spec.hint || f.label.toLowerCase();
181
- showError(failMsg ? `Couldn't create ${hint} "${v}": ${failMsg}` : `Couldn't create ${hint} "${v}". See the toast for details.`);
182
- return;
183
- }
184
- v = created;
185
- }
186
- }
187
- data[f.name] = v;
188
- }
189
- let firstBadName = null;
190
- for (const f of formFields) {
191
- clearFieldError(f.name);
192
- if (rowHidden(f.name)) continue;
193
- let msg = null;
194
- if (f.type === "picker" && f.required && !data[f.name]) msg = "Required.";
195
- else if (typeof f.validator !== "function") continue;
196
- else {
197
- try {
198
- msg = f.validator(data[f.name], data);
199
- } catch (err) {
200
- msg = err?.message || String(err);
201
- }
202
- }
203
- if (msg) {
204
- setFieldError(f.name, msg);
205
- if (!firstBadName) firstBadName = f.name;
206
- }
207
- }
208
- if (firstBadName) {
209
- body.querySelector(`[data-picker="${firstBadName}"]`)?.focus();
210
- body.querySelector(`[name="${firstBadName}"]:not([type="hidden"])`)?.focus();
211
- return;
212
- }
213
- close(data);
214
- });
215
- requestAnimationFrame(() => {
216
- const first = [...body.querySelectorAll('input:not([type="hidden"]), select, textarea')].find((el) => !el.closest("[data-field-row]")?.hidden);
217
- first?.focus();
218
- });
219
- });
220
- }
221
- function openConfirm({
222
- title,
223
- message,
224
- confirmLabel = "OK",
225
- cancelLabel = "Cancel",
226
- danger = false,
227
- icon = null
228
- } = {}) {
229
- return new Promise((resolve) => {
230
- const body = document.createElement("div");
231
- body.className = "twm-modal__body-host";
232
- const cls = danger ? "twm-btn twm-btn--danger" : "twm-btn twm-btn--primary";
233
- body.innerHTML = `
234
- <div class="twm-modal__body">${message}</div>
235
- <div class="twm-modal__actions">
236
- <button type="button" class="twm-btn" data-action="cancel">${cancelLabel}</button>
237
- <button type="button" class="${cls}" data-action="confirm">${confirmLabel}</button>
238
- </div>
239
- `;
240
- let _resolved = false;
241
- const win = new ManagedWindow({
242
- container: _modalHost,
243
- id: `twm-confirm-${_modalSeq++}`,
244
- title: title || "Confirm",
245
- icon: icon || (danger ? "warning" : "help"),
246
- content: body,
247
- modal: true,
248
- canMinimize: false,
249
- canMaximize: false,
250
- canResize: false,
251
- canDrag: true,
252
- defaultWidth: 380,
253
- defaultHeight: 180,
254
- onClose: () => {
255
- if (!_resolved) {
256
- _resolved = true;
257
- resolve(false);
258
- }
259
- }
260
- });
261
- win.show();
262
- const close = (v) => {
263
- if (_resolved) return;
264
- _resolved = true;
265
- resolve(v);
266
- try {
267
- win.close({ force: true });
268
- } catch {
269
- }
270
- };
271
- body.querySelector('[data-action="cancel"]').addEventListener("click", () => close(false));
272
- body.querySelector('[data-action="confirm"]').addEventListener("click", () => close(true));
273
- requestAnimationFrame(() => {
274
- body.querySelector(`[data-action="${danger ? "cancel" : "confirm"}"]`)?.focus();
275
- });
276
- });
277
- }
278
- function openModal({
279
- title = "Dialog",
280
- icon = "info",
281
- content = null,
282
- actions = null,
283
- width = 640,
284
- height = 480,
285
- onMount = null,
286
- backdropBlur = void 0,
287
- backdropOpacity = void 0,
288
- maximizable = false,
289
- onMaximizeChange = null
290
- } = {}) {
291
- return new Promise((resolve) => {
292
- const body = document.createElement("div");
293
- body.className = "twm-modal__body-host";
294
- const bodyInner = document.createElement("div");
295
- bodyInner.className = "twm-modal__body twm-modal__body--rich";
296
- if (content instanceof HTMLElement) bodyInner.appendChild(content);
297
- body.appendChild(bodyInner);
298
- const acts = Array.isArray(actions) && actions.length > 0 ? actions : [{ label: "Close", value: null }];
299
- const actionsEl = document.createElement("div");
300
- actionsEl.className = "twm-modal__actions";
301
- acts.forEach((a, i) => {
302
- const btn = document.createElement("button");
303
- btn.type = "button";
304
- let cls = "twm-btn";
305
- if (a.primary) cls += " twm-btn--primary";
306
- if (a.danger) cls += " twm-btn--danger";
307
- btn.className = cls;
308
- if (a.icon) {
309
- const glyph = document.createElement("span");
310
- glyph.className = "material-symbols-outlined twm-btn__glyph";
311
- glyph.textContent = String(a.icon);
312
- glyph.setAttribute("aria-hidden", "true");
313
- const text = document.createElement("span");
314
- text.className = "twm-btn__label";
315
- text.textContent = String(a.label || "");
316
- btn.append(glyph, text);
317
- } else {
318
- btn.textContent = String(a.label || "");
319
- }
320
- btn.dataset.actionIdx = String(i);
321
- if (a.disabled) btn.disabled = true;
322
- if (a.value !== void 0 && a.value !== null) {
323
- btn.dataset.actionValue = String(a.value);
324
- }
325
- actionsEl.appendChild(btn);
326
- });
327
- body.appendChild(actionsEl);
328
- let _resolved = false;
329
- let _onMax = null;
330
- const _dropMaxListener = () => {
331
- if (!_onMax) return;
332
- window.removeEventListener("managed-window-maximized", _onMax);
333
- _onMax = null;
334
- };
335
- const win = new ManagedWindow({
336
- container: _modalHost,
337
- id: `twm-modal-${_modalSeq++}`,
338
- title,
339
- icon,
340
- content: body,
341
- modal: true,
342
- backdropBlur,
343
- backdropOpacity,
344
- canMinimize: false,
345
- // C29. OPT-IN, and `false` is still the default — a confirmation
346
- // and a two-field form have nothing to do with the extra room, and
347
- // a button that grows a dialog nobody wanted grown is noise in the
348
- // one place a user looks for the X. `onMaximize` is deliberately
349
- // NOT set: see the C29 note in the header — with nothing claiming
350
- // the gesture, `toggleMaximize` runs the rectangle, which is the
351
- // only thing maximise can mean for a window with no tile.
352
- canMaximize: !!maximizable,
353
- canResize: true,
354
- canDrag: true,
355
- defaultWidth: width,
356
- defaultHeight: height,
357
- // Esc, the X and the backdrop all land here without passing through
358
- // `close`, so the listener is dropped here as well as there — the
359
- // three dismissals a user reaches for most are exactly the ones
360
- // that would otherwise leak it.
361
- onClose: () => {
362
- _dropMaxListener();
363
- if (!_resolved) {
364
- _resolved = true;
365
- resolve(null);
366
- }
367
- }
368
- });
369
- win.show();
370
- if (maximizable && typeof onMaximizeChange === "function") {
371
- _onMax = (e) => {
372
- if (e.detail?.id !== win.id) return;
373
- try {
374
- onMaximizeChange(!!e.detail.maximized, bodyInner);
375
- } catch (err) {
376
- console.warn("[modal] onMaximizeChange threw", err);
377
- }
378
- };
379
- window.addEventListener("managed-window-maximized", _onMax);
380
- }
381
- const close = (value) => {
382
- if (_resolved) return;
383
- _resolved = true;
384
- _dropMaxListener();
385
- resolve(value);
386
- try {
387
- win.close({ force: true });
388
- } catch {
389
- }
390
- };
391
- actionsEl.addEventListener("click", (e) => {
392
- const btn = e.target.closest("[data-action-idx]");
393
- if (!btn) return;
394
- const idx = Number(btn.dataset.actionIdx);
395
- const a = acts[idx];
396
- if (!a) return;
397
- if (a.keepOpen) {
398
- try {
399
- a.onClick?.(close);
400
- } catch (err) {
401
- console.warn(err);
402
- }
403
- return;
404
- }
405
- close(a.value);
406
- });
407
- const controls = {
408
- // NOT `CSS.escape`. It is a browser global that jsdom does not
409
- // provide, and this file is mounted under jsdom by four render
410
- // tests — so reaching for it turns "the dialog is valid" into a
411
- // ReferenceError in every one of them, and into nothing at all in a
412
- // headless consumer. An attribute selector needs `"` and `\`
413
- // escaped and nothing else.
414
- actionButton: (value) => actionsEl.querySelector(
415
- `[data-action-value="${String(value).replace(/["\\]/g, "\\$&")}"]`
416
- ),
417
- setActionEnabled(value, enabled) {
418
- const btn = controls.actionButton(value);
419
- if (btn) btn.disabled = !enabled;
420
- return !!btn;
421
- }
422
- };
423
- if (typeof onMount === "function") {
424
- requestAnimationFrame(() => {
425
- try {
426
- onMount(bodyInner, controls);
427
- } catch (err) {
428
- console.warn(err);
429
- }
430
- });
431
- }
432
- requestAnimationFrame(() => {
433
- const field = bodyInner.querySelector(
434
- 'input:not([type="hidden"]):not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]), select:not([disabled])'
435
- );
436
- if (field) {
437
- field.focus();
438
- try {
439
- field.setSelectionRange?.(field.value.length, field.value.length);
440
- } catch {
441
- }
442
- return;
443
- }
444
- const idx = acts.findIndex((a) => a.primary);
445
- const which = idx >= 0 ? idx : acts.length - 1;
446
- actionsEl.querySelector(`[data-action-idx="${which}"]`)?.focus();
447
- });
448
- });
449
- }
450
- function _estimateHeight(fields) {
451
- let body = 0;
452
- for (const f of fields) {
453
- if (f.section) body += 32;
454
- else if (f.type === "textarea") {
455
- const rows = Number(f.rows) || 4;
456
- body += 32 + rows * 18;
457
- if (f.hint) body += 16;
458
- } else if (f.type === "checkbox") {
459
- body += 32;
460
- if (f.hint) body += 16;
461
- } else {
462
- body += 46;
463
- if (f.hint) body += 16;
464
- }
465
- }
466
- const base = 36 + 46 + 28 + body;
467
- const max = Math.floor(window.innerHeight * 0.8);
468
- return Math.max(200, Math.min(base, max));
469
- }
470
- function _estimateWidth(fields) {
471
- const wide = fields.some((f) => f && (f.type === "textarea" || f.section));
472
- return wide ? 640 : 480;
473
- }
474
- function _optionValues(options) {
475
- return (options || []).map((o) => typeof o === "object" ? o.value : o);
476
- }
477
- function _createSpec(f) {
478
- if (!f || !f.create) return null;
479
- if (f.create === true) return { onCreate: async (v) => v, hint: null };
480
- return {
481
- onCreate: typeof f.create.onCreate === "function" ? f.create.onCreate : async (v) => v,
482
- hint: f.create.hint ?? null
483
- };
484
- }
485
- function _pickerLabel(f, value) {
486
- if (typeof f.displayFor !== "function") return value == null ? "" : String(value);
487
- try {
488
- return String(f.displayFor(value) ?? "");
489
- } catch (err) {
490
- console.warn("[modal] displayFor threw", err);
491
- return String(value ?? "");
492
- }
493
- }
494
- function _wirePicker(host, f) {
495
- const hidden = host.querySelector(`input[type="hidden"][name="${f.name}"]`);
496
- const display = host.querySelector(`[data-picker-display="${f.name}"]`);
497
- const btn = host.querySelector(`[data-picker="${f.name}"]`);
498
- if (!hidden || !display || !btn || typeof f.pick !== "function") return;
499
- let busy = false;
500
- const open = async () => {
501
- if (busy) return;
502
- busy = true;
503
- let picked;
504
- try {
505
- picked = await f.pick(hidden.value);
506
- } catch (err) {
507
- console.warn("[modal] pick threw", err);
508
- picked = null;
509
- } finally {
510
- busy = false;
511
- }
512
- if (picked == null) return;
513
- const isPair = typeof picked === "object";
514
- hidden.value = String((isPair ? picked.value : picked) ?? "");
515
- display.value = isPair && picked.label != null ? String(picked.label) : _pickerLabel(f, hidden.value);
516
- hidden.dispatchEvent(new Event("change", { bubbles: true }));
517
- };
518
- btn.addEventListener("click", open);
519
- display.addEventListener("click", open);
520
- }
521
- function _wireComboboxHint(host, f) {
522
- const input = host.querySelector(`input[name="${f.name}"]`);
523
- const hintEl = host.querySelector(`.twm-combobox__hint[data-for="${f.name}"]`);
524
- if (!input || !hintEl) return;
525
- if (f.create === true) return;
526
- const known = new Set(_optionValues(f.options));
527
- const label = _createSpec(f).hint || "new entry";
528
- const update = () => {
529
- const v = input.value.trim();
530
- if (v && !known.has(v)) {
531
- hintEl.textContent = `\u21B3 will create ${label} \u201C${v}\u201D`;
532
- hintEl.hidden = false;
533
- } else {
534
- hintEl.hidden = true;
535
- }
536
- };
537
- input.addEventListener("input", update);
538
- update();
539
- }
540
- function _renderEntry(f, value) {
541
- if (f && f.section != null) {
542
- const attr = f.name ? ` data-field-row="${f.name}"` : "";
543
- return `<div class="twm-modal__section"${attr}>${_esc(f.section)}</div>`;
544
- }
545
- return _renderField(f, value);
546
- }
547
- function _renderField(f, value) {
548
- const required = f.required ? "required" : "";
549
- const rowMod = f.type === "textarea" ? " twm-modal__row--multiline" : "";
550
- const hintHtml = f.hint ? `<div class="twm-modal__hint--field">${_esc(f.hint)}</div>` : "";
551
- const errHtml = `<div class="twm-modal__field-error" data-field-error="${f.name}" hidden></div>`;
552
- const wrap = (inner) => `
553
- <label class="twm-modal__row${rowMod}" data-field-row="${f.name}">
554
- <span>${_esc(f.label || "")}</span>
555
- ${inner}
556
- ${hintHtml}
557
- ${errHtml}
558
- </label>
559
- `;
560
- if (f.type === "checkbox") {
561
- const checked = value === true || value === "true" ? "checked" : "";
562
- return `
563
- <label class="twm-modal__row" data-field-row="${f.name}">
564
- <span></span>
565
- <span class="twm-modal__check">
566
- <input name="${f.name}" type="checkbox" ${checked}>
567
- <span>${_esc(f.label || "")}</span>
568
- </span>
569
- ${hintHtml}
570
- ${errHtml}
571
- </label>
572
- `;
573
- }
574
- if (f.type === "textarea") {
575
- const v2 = value == null ? "" : String(value);
576
- const rows = f.rows != null ? `rows="${Number(f.rows) || 4}"` : 'rows="4"';
577
- const placeholder2 = f.placeholder ? `placeholder="${_esc(f.placeholder)}"` : "";
578
- return wrap(`<textarea name="${f.name}" ${rows} ${placeholder2} ${required}>${_esc(v2)}</textarea>`);
579
- }
580
- const v = value == null ? "" : String(value);
581
- if (f.type === "picker") {
582
- const placeholder2 = f.placeholder ? `placeholder="${_esc(f.placeholder)}"` : "";
583
- return wrap(`
584
- <div class="twm-picker">
585
- <input type="hidden" name="${f.name}" value="${_esc(v)}">
586
- <input class="twm-picker__display" data-picker-display="${f.name}" type="text"
587
- value="${_esc(_pickerLabel(f, v))}" ${placeholder2} readonly>
588
- <button type="button" class="twm-btn twm-picker__btn" data-picker="${f.name}"
589
- aria-label="Search" title="Search">
590
- <span class="material-symbols-outlined" aria-hidden="true">search</span>
591
- </button>
592
- </div>
593
- `);
594
- }
595
- if (f.type === "select" && f.create) {
596
- const dlId = `ea-dl-${f.name}-${_modalSeq}`;
597
- const opts = _optionValues(f.options).map((ov) => `<option value="${_esc(ov)}"></option>`).join("");
598
- const placeholder2 = f.placeholder ? `placeholder="${_esc(f.placeholder)}"` : 'placeholder="type to pick or create\u2026"';
599
- return wrap(`
600
- <div class="twm-combobox">
601
- <input name="${f.name}" type="text" list="${dlId}"
602
- value="${_esc(v)}" ${placeholder2} ${required}
603
- autocomplete="off">
604
- <datalist id="${dlId}">${opts}</datalist>
605
- <span class="twm-combobox__hint" data-for="${f.name}" hidden></span>
606
- </div>
607
- `);
608
- }
609
- if (f.type === "select") {
610
- const opts = (f.options || []).map((o) => {
611
- const ov = typeof o === "object" ? o.value : o;
612
- const ol = typeof o === "object" ? o.label : o;
613
- return `<option value="${_esc(ov)}" ${ov === v ? "selected" : ""}>${_esc(ol)}</option>`;
614
- }).join("");
615
- return wrap(`<select name="${f.name}" ${required}>${opts}</select>`);
616
- }
617
- const step = f.step != null ? `step="${f.step}"` : "";
618
- const placeholder = f.placeholder ? `placeholder="${_esc(f.placeholder)}"` : "";
619
- const type = f.type === "number" || f.type === "password" ? f.type : "text";
620
- return wrap(`<input name="${f.name}" type="${type}" value="${_esc(v)}" ${step} ${placeholder} ${required}>`);
621
- }
622
- function _esc(s) {
623
- return String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
624
- }
625
-
626
- export {
627
- setModalHost,
628
- modalHost,
629
- openForm,
630
- openConfirm,
631
- openModal
632
- };
633
- //# sourceMappingURL=chunk-NNFTETK3.js.map