flexdesk 0.16.0 → 0.17.0
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 +93 -0
- package/LICENSE +0 -0
- package/README.md +2261 -42
- package/THIRD_PARTY_LICENSES.md +0 -0
- package/css/base.css +1834 -75
- package/css/flexdesk.css +3373 -238
- package/css/overrides.css +16 -0
- package/css/reset.css +0 -0
- package/css/tokens.css +0 -0
- package/dist/canvas.js +6 -239
- package/dist/canvas.js.map +3 -3
- package/dist/charts.js +709 -18
- package/dist/charts.js.map +4 -4
- package/dist/chunk-3PHCPZHT.js +0 -0
- package/dist/chunk-3PHCPZHT.js.map +0 -0
- package/dist/chunk-4A7SJC3T.js +503 -0
- package/dist/chunk-4A7SJC3T.js.map +7 -0
- package/dist/{chunk-6JSOVNID.js → chunk-CVMCZZX2.js} +635 -10
- package/dist/chunk-CVMCZZX2.js.map +7 -0
- package/dist/chunk-D3COLZZ2.js +1311 -0
- package/dist/chunk-D3COLZZ2.js.map +7 -0
- package/dist/chunk-FOOS3T5L.js +0 -0
- package/dist/chunk-FOOS3T5L.js.map +0 -0
- package/dist/{chunk-SN4LSOAR.js → chunk-GOQLR5WR.js} +452 -2
- package/dist/chunk-GOQLR5WR.js.map +7 -0
- package/dist/{chunk-FL5KFNQH.js → chunk-HNU6MM2B.js} +52 -17
- package/dist/chunk-HNU6MM2B.js.map +7 -0
- package/dist/chunk-HYJYYEPK.js +375 -0
- package/dist/chunk-HYJYYEPK.js.map +7 -0
- package/dist/chunk-JKZLNKG6.js +2041 -0
- package/dist/chunk-JKZLNKG6.js.map +7 -0
- package/dist/chunk-JYWURG5T.js +0 -0
- package/dist/chunk-JYWURG5T.js.map +0 -0
- package/dist/{chunk-RQM5Z2MU.js → chunk-MCIXFNVF.js} +633 -311
- package/dist/chunk-MCIXFNVF.js.map +7 -0
- package/dist/chunk-N7HC3ADV.js +30 -0
- package/dist/chunk-N7HC3ADV.js.map +7 -0
- package/dist/{chunk-FBGP5KN5.js → chunk-Q36EPT2R.js} +3268 -203
- package/dist/chunk-Q36EPT2R.js.map +7 -0
- package/dist/chunk-QWQJUDPD.js +610 -0
- package/dist/chunk-QWQJUDPD.js.map +7 -0
- package/dist/{chunk-NLTOY57M.js → chunk-UURZ37SD.js} +28 -48
- package/dist/chunk-UURZ37SD.js.map +7 -0
- package/dist/chunk-UWO43BZU.js +111 -0
- package/dist/chunk-UWO43BZU.js.map +7 -0
- package/dist/chunk-WVFGV5FT.js +0 -0
- package/dist/chunk-WVFGV5FT.js.map +0 -0
- package/dist/core.js +170 -118
- package/dist/core.js.map +2 -2
- package/dist/editor.js +156 -70
- package/dist/editor.js.map +2 -2
- package/dist/flexdesk.css +3373 -238
- package/dist/flow.js +2789 -306
- package/dist/flow.js.map +4 -4
- package/dist/host.js +109 -3
- package/dist/host.js.map +3 -3
- package/dist/reset.css +0 -0
- package/dist/tile_registry-6WZPTUZV.js +0 -0
- package/dist/tile_registry-6WZPTUZV.js.map +0 -0
- package/dist/tiles.js +1131 -253
- package/dist/tiles.js.map +4 -4
- package/dist/tokens.css +0 -0
- package/dist/widgets.js +3254 -2112
- package/dist/widgets.js.map +4 -4
- package/dist/wm.js +1227 -345
- package/dist/wm.js.map +4 -4
- package/package.json +3 -1
- package/src/canvas/canvas_adapter.js +5 -0
- package/src/canvas/edge_router.js +0 -0
- package/src/canvas/graph/connector_router.js +0 -0
- package/src/canvas/graph/history_service.js +0 -0
- package/src/canvas/graph/node_platform.js +0 -0
- package/src/canvas/index.js +1 -0
- package/src/canvas/layered_layout.js +244 -0
- package/src/charts/chart_types.js +0 -0
- package/src/charts/data_series_plot_window.js +0 -0
- package/src/charts/downsample.js +2 -2
- package/src/charts/plot_config.js +53 -29
- package/src/charts/plot_config_editor.js +627 -0
- package/src/charts/plot_grid.js +449 -0
- package/src/charts/plot_popout_window.js +103 -317
- package/src/charts/plotly_wrapper.js +22 -2
- package/src/charts/series_transforms.js +206 -0
- package/src/core/event_bus.js +0 -0
- package/src/core/logging.js +1 -1
- package/src/core/settings.js +105 -19
- package/src/core/state_guard.js +93 -51
- package/src/core/state_guard_config.js +85 -74
- package/src/core/state_machine.js +0 -0
- package/src/editor/editor_pane.js +3 -3
- package/src/editor/monaco_editor_factory.js +124 -60
- package/src/editor/monaco_loader.js +103 -28
- package/src/editor/notebook_search.js +0 -0
- package/src/editor/notebook_tab_bar.js +38 -52
- package/src/editor/notebook_undo_manager.js +0 -0
- package/src/editor/split_pane_container.js +6 -3
- package/src/flow/canvas_editor/connections.js +90 -0
- package/src/flow/canvas_editor/editor.js +2351 -153
- package/src/flow/canvas_editor/index.js +2 -1
- package/src/flow/canvas_editor/layout.js +119 -12
- package/src/flow/canvas_editor/strings.js +80 -0
- package/src/flow/kit/always_before.js +0 -0
- package/src/flow/kit/catalogue.js +97 -3
- package/src/flow/kit/chip_input.js +0 -0
- package/src/flow/kit/dom.js +0 -0
- package/src/flow/kit/findings.js +0 -0
- package/src/flow/kit/history.js +0 -0
- package/src/flow/kit/index.js +3 -3
- package/src/flow/kit/keys.js +7 -1
- package/src/flow/kit/logic_graph.js +97 -15
- package/src/flow/kit/references.js +0 -0
- package/src/flow/kit/settings_panel.js +0 -0
- package/src/flow/kit/settings_schema.js +2 -1
- package/src/flow/kit/step_picker.js +0 -0
- package/src/flow/kit/strings.js +3 -1
- package/src/flow/kit/value_picker.js +0 -0
- package/src/flow/kit/widgets.js +86 -109
- package/src/flow/lanes/editor.js +136 -14
- package/src/flow/lanes/index.js +3 -1
- package/src/flow/lanes/layout.js +0 -0
- package/src/flow/lanes/pipeline.js +44 -2
- package/src/flow/lanes/preview.js +0 -0
- package/src/flow/lanes/strings.js +1 -0
- package/src/flow/outline/editor.js +0 -0
- package/src/flow/outline/index.js +0 -0
- package/src/flow/outline/mapping.js +0 -0
- package/src/flow/outline/menu.js +0 -0
- package/src/flow/outline/operations.js +0 -0
- package/src/flow/outline/recognise.js +0 -0
- package/src/flow/outline/strings.js +0 -0
- package/src/help/help_modal.js +168 -42
- package/src/help/help_registry.js +12 -1
- package/src/host/external_links.js +0 -0
- package/src/host/file_picker.js +117 -0
- package/src/host/host.js +10 -2
- package/src/host/photino_host.js +9 -0
- package/src/host/photino_host.md +21 -3
- package/src/host/pywebview_host.js +10 -0
- package/src/tiles/chart_tiles.js +53 -245
- package/src/tiles/config_schema.js +8 -0
- package/src/tiles/figure_tile.js +322 -0
- package/src/tiles/layout_persistence.js +3 -3
- package/src/tiles/list_tiles.js +422 -0
- package/src/tiles/scope_picker.js +96 -0
- package/src/tiles/tile_base.js +12 -1
- package/src/tiles/tile_config_form.js +442 -0
- package/src/tiles/tile_grid.js +38 -1
- package/src/tiles/tile_registry.js +0 -0
- package/src/tiling/bottom_panel_tabs.js +67 -99
- package/src/tiling/command_palette.js +64 -19
- package/src/tiling/content_registry.js +4 -1
- package/src/tiling/desktops.js +11 -1
- package/src/tiling/entity_sources.js +0 -0
- package/src/tiling/keymap.js +403 -128
- package/src/tiling/kind_taxonomy.js +78 -0
- package/src/tiling/landing_table.js +0 -0
- package/src/tiling/loading_overlay.js +0 -0
- package/src/tiling/nav_panel.js +59 -10
- package/src/tiling/page_factory.js +5 -5
- package/src/tiling/panel_availability.js +49 -0
- package/src/tiling/panel_keys.js +0 -0
- package/src/tiling/shell.js +114 -12
- package/src/tiling/tab_strip.js +0 -0
- package/src/tiling/tile_breadcrumb.js +0 -0
- package/src/tiling/tile_renderer.js +105 -9
- package/src/tiling/tile_tab_menu.js +0 -0
- package/src/tiling/tile_tree.js +0 -0
- package/src/tiling/wm.js +562 -30
- package/src/tiling/zoom.js +0 -0
- package/src/ui/base/component_base.js +0 -0
- package/src/ui/base/controller_base.js +0 -0
- package/src/ui/base/page_base.js +0 -0
- package/src/ui/components/about_dialog.js +0 -0
- package/src/ui/components/action_dropdown.js +30 -2
- package/src/ui/components/alert_log.js +0 -0
- package/src/ui/components/alerts_pane.js +0 -0
- package/src/ui/components/attribute_list_editor.js +0 -0
- package/src/ui/components/autocomplete_field.js +0 -0
- package/src/ui/components/cell_editor_host.js +211 -0
- package/src/ui/components/cell_grid.js +648 -0
- package/src/ui/components/cell_selection.js +220 -0
- package/src/ui/components/choice_controls.js +351 -0
- package/src/ui/components/color_field.js +211 -0
- package/src/ui/components/color_list_field.js +210 -0
- package/src/ui/components/column_fit.js +0 -0
- package/src/ui/components/column_groups.js +314 -0
- package/src/ui/components/column_order.js +142 -0
- package/src/ui/components/computing_status_window.js +98 -25
- package/src/ui/components/confirm_dialog.js +81 -38
- package/src/ui/components/context_menu.js +2 -2
- package/src/ui/components/conversation_view.js +440 -0
- package/src/ui/components/crosstab.js +289 -0
- package/src/ui/components/data_table.js +601 -62
- package/src/ui/components/detail_header.js +22 -11
- package/src/ui/components/document_outline.js +397 -0
- package/src/ui/components/drag_reorder.js +0 -0
- package/src/ui/components/fact_table.js +0 -0
- package/src/ui/components/fill_handle.js +322 -0
- package/src/ui/components/gallery_picker.js +19 -8
- package/src/ui/components/header_actions.js +55 -0
- package/src/ui/components/inline_renamer.js +0 -0
- package/src/ui/components/job_progress.js +51 -0
- package/src/ui/components/managed_window.js +27 -4
- package/src/ui/components/menu_bar.js +745 -0
- package/src/ui/components/modal.js +449 -75
- package/src/ui/components/notification_history.js +3 -3
- package/src/ui/components/progress.js +483 -0
- package/src/ui/components/record_list.js +0 -0
- package/src/ui/components/row_activation.js +0 -0
- package/src/ui/components/section_stack.js +298 -0
- package/src/ui/components/series.js +173 -0
- package/src/ui/components/settings_view.js +634 -0
- package/src/ui/components/slide_out_panel.js +43 -6
- package/src/ui/components/slider_field.js +275 -18
- package/src/ui/components/sortable_list.js +26 -4
- package/src/ui/components/staged_edits.js +343 -0
- package/src/ui/components/staged_history.js +172 -0
- package/src/ui/components/table_filter_chips.js +0 -0
- package/src/ui/components/table_state_store.js +0 -0
- package/src/ui/components/table_structure.js +182 -0
- package/src/ui/components/table_values.js +0 -0
- package/src/ui/components/tabs.js +497 -0
- package/src/ui/components/tag_input.js +215 -0
- package/src/ui/components/toast.js +25 -8
- package/src/ui/components/toggle_field.js +175 -0
- package/src/ui/components/tree_view.js +55 -34
- package/src/ui/components/window_taskbar.js +0 -0
- package/src/ui/components/wizard.js +563 -0
- package/src/ui/controllers/window_chrome_controller.js +2 -2
- package/src/ui/notification_center.js +375 -119
- package/src/ui/utils/auto_scrollbars.js +0 -0
- package/src/ui/utils/markdown.js +709 -0
- package/src/ui/utils/native_context_menu.js +0 -0
- package/src/ui/utils/overlay_scrollbar.js +16 -12
- package/src/ui/utils/raf_resize_observer.js +0 -0
- package/src/ui/utils/scroll_lock.js +0 -0
- package/src/ui/utils/strip_scroll.js +55 -0
- package/src/ui/utils/tooltip_service.js +0 -8
- package/dist/chunk-6JSOVNID.js.map +0 -7
- package/dist/chunk-CWPMLX67.js +0 -475
- package/dist/chunk-CWPMLX67.js.map +0 -7
- package/dist/chunk-FBGP5KN5.js.map +0 -7
- package/dist/chunk-FL5KFNQH.js.map +0 -7
- package/dist/chunk-NLTOY57M.js.map +0 -7
- package/dist/chunk-NNFTETK3.js +0 -633
- package/dist/chunk-NNFTETK3.js.map +0 -7
- package/dist/chunk-O5OHMWBB.js +0 -284
- package/dist/chunk-O5OHMWBB.js.map +0 -7
- package/dist/chunk-RQM5Z2MU.js.map +0 -7
- package/dist/chunk-SN4LSOAR.js.map +0 -7
- package/src/ui/controllers/panel_state_machine.js +0 -634
package/src/tiling/zoom.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/ui/base/page_base.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -23,6 +23,7 @@ const GAP = 8;
|
|
|
23
23
|
* trigger: buttonElement,
|
|
24
24
|
* options: [
|
|
25
25
|
* { type: 'option1', label: 'Option 1', icon: 'icon_name', description: 'Description' },
|
|
26
|
+
* { separator: true },
|
|
26
27
|
* { type: 'option2', label: 'Option 2', icon: 'icon_name', description: 'Description' }
|
|
27
28
|
* ],
|
|
28
29
|
* onSelect: (option) => console.log('Selected:', option.type),
|
|
@@ -34,12 +35,22 @@ const GAP = 8;
|
|
|
34
35
|
* dropdown.toggle();
|
|
35
36
|
* dropdown.destroy();
|
|
36
37
|
* ```
|
|
38
|
+
*
|
|
39
|
+
* ── Separators ────────────────────────────────────────────────────────
|
|
40
|
+
* An entry `{ separator: true }` draws a divider instead of a button: a
|
|
41
|
+
* `<div role="separator" class="twm-action-dropdown-separator">`, which takes
|
|
42
|
+
* no focus, is never reached by the arrow keys (they move between the option
|
|
43
|
+
* buttons only) and never reaches `onSelect` (it has no click handler). A
|
|
44
|
+
* separator only separates: one at the start or the end of the list, or one
|
|
45
|
+
* right after another, is dropped, so a list assembled from groups that may
|
|
46
|
+
* come out empty never draws a stray or doubled rule.
|
|
37
47
|
*/
|
|
38
48
|
export class ActionDropdown {
|
|
39
49
|
/**
|
|
40
50
|
* @param {Object} config
|
|
41
51
|
* @param {HTMLElement} config.trigger - Button element that triggers the dropdown
|
|
42
|
-
* @param {Array} config.options - Array of option objects with { type, label, icon, description? }
|
|
52
|
+
* @param {Array} config.options - Array of option objects with { type, label, icon, description? },
|
|
53
|
+
* or `{ separator: true }` for a divider between them
|
|
43
54
|
* @param {Function} config.onSelect - Callback when an option is selected
|
|
44
55
|
* @param {string} [config.className=''] - Additional CSS class for the dropdown
|
|
45
56
|
* @param {string} [config.menuId] - Optional ID for the menu element
|
|
@@ -98,13 +109,30 @@ export class ActionDropdown {
|
|
|
98
109
|
}
|
|
99
110
|
|
|
100
111
|
/**
|
|
101
|
-
* Render option buttons into a
|
|
112
|
+
* Render option buttons, and dividers for separator entries, into a
|
|
113
|
+
* container element. `data-index` stays the entry's index in `options`.
|
|
102
114
|
* @param {HTMLElement} container
|
|
103
115
|
* @private
|
|
104
116
|
*/
|
|
105
117
|
_renderOptions(container) {
|
|
106
118
|
container.innerHTML = '';
|
|
119
|
+
let pendingSeparator = false;
|
|
107
120
|
this.options.forEach((opt, index) => {
|
|
121
|
+
if (opt?.separator) {
|
|
122
|
+
// Drawn only once an option follows it, and only if an option
|
|
123
|
+
// came before it: leading, trailing and doubled separators
|
|
124
|
+
// collapse.
|
|
125
|
+
pendingSeparator = container.childElementCount > 0;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (pendingSeparator) {
|
|
129
|
+
const sep = document.createElement('div');
|
|
130
|
+
sep.className = 'twm-action-dropdown-separator';
|
|
131
|
+
sep.setAttribute('role', 'separator');
|
|
132
|
+
container.appendChild(sep);
|
|
133
|
+
pendingSeparator = false;
|
|
134
|
+
}
|
|
135
|
+
|
|
108
136
|
const optionBtn = document.createElement('button');
|
|
109
137
|
optionBtn.type = 'button';
|
|
110
138
|
optionBtn.className = 'twm-action-dropdown-option';
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cell_editor_host.js — STORY-0003. ONE CELL BEING EDITED: open an editor in
|
|
3
|
+
* it, and close it exactly once — by a commit or by a cancel.
|
|
4
|
+
*
|
|
5
|
+
* The editor itself comes from a FACTORY, so a consumer can bring any editor
|
|
6
|
+
* it likes (DataTable's `editors` option); the plain text box below is the
|
|
7
|
+
* default. A factory is called with
|
|
8
|
+
*
|
|
9
|
+
* { value, row, rowIdx, colIdx, column: {index, header, type},
|
|
10
|
+
* initialText, commit(value), cancel() }
|
|
11
|
+
*
|
|
12
|
+
* and returns either an element, or `{ el, getValue?, focus?, destroy? }`.
|
|
13
|
+
* `initialText` is the character typed to open the editor (null when it was
|
|
14
|
+
* opened by F2, Enter or a double-click): the editor starts with it in place
|
|
15
|
+
* of the value, as a spreadsheet does.
|
|
16
|
+
*
|
|
17
|
+
* ══ THE KEYS, WHICH THE HOST OWNS ════════════════════════════════════════
|
|
18
|
+
*
|
|
19
|
+
* Enter commit, and the table moves the cursor down (Shift: up)
|
|
20
|
+
* Tab commit, and the cursor moves right (Shift: left)
|
|
21
|
+
* Escape cancel: the cell is drawn as it was, and nothing is called
|
|
22
|
+
* focus out commit, without moving — a click elsewhere keeps the edit
|
|
23
|
+
*
|
|
24
|
+
* Enter and Tab commit `getValue()` — an input, select or textarea's `value`
|
|
25
|
+
* when the factory returned the field itself; any other editor without a
|
|
26
|
+
* `getValue` calls `commit(value)` itself. Such an editor has no value the host
|
|
27
|
+
* can read: Enter, Tab and the focus leaving do nothing, and `commit()` from
|
|
28
|
+
* outside (its row leaving a virtual window) CANCELS it. Every key
|
|
29
|
+
* pressed in the editor stops there: the table's own keys (the arrows, Ctrl+A,
|
|
30
|
+
* Ctrl+C) are not the editor's.
|
|
31
|
+
*
|
|
32
|
+
* ══ EXACTLY ONCE ═════════════════════════════════════════════════════════
|
|
33
|
+
*
|
|
34
|
+
* A session closes once. Escape's own repaint takes the focused input out of
|
|
35
|
+
* the document, and a browser fires `focusout` for that — the commit listener.
|
|
36
|
+
* The session is marked closed BEFORE anything is taken out, so that late
|
|
37
|
+
* `focusout` (and a second Enter, and an editor that calls `commit` twice)
|
|
38
|
+
* finds nothing to do.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The default editor: a text box holding the value as text (or the character
|
|
43
|
+
* that opened it). Left unchanged, it commits the value it was given — not
|
|
44
|
+
* that value turned into a string — so opening and leaving a number cell is
|
|
45
|
+
* no edit at all.
|
|
46
|
+
*/
|
|
47
|
+
export function createTextEditor({ value, initialText = null }) {
|
|
48
|
+
const input = document.createElement('input');
|
|
49
|
+
input.type = 'text';
|
|
50
|
+
input.className = 'twm-dt-editor';
|
|
51
|
+
input.spellcheck = false;
|
|
52
|
+
input.setAttribute('autocomplete', 'off');
|
|
53
|
+
const start = value == null ? '' : String(value);
|
|
54
|
+
input.value = initialText != null ? String(initialText) : start;
|
|
55
|
+
return {
|
|
56
|
+
el: input,
|
|
57
|
+
getValue: () => (initialText == null && input.value === start ? value : input.value),
|
|
58
|
+
focus: () => {
|
|
59
|
+
input.focus({ preventScroll: true });
|
|
60
|
+
const end = input.value.length;
|
|
61
|
+
try { input.setSelectionRange(end, end); } catch (_) { /* not a text type */ }
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** What a factory returned, as one shape — or null when it returned nothing. */
|
|
67
|
+
function normalise(made) {
|
|
68
|
+
if (!made) return null;
|
|
69
|
+
const isNode = typeof Node !== 'undefined' && made instanceof Node;
|
|
70
|
+
const el = isNode ? made : made.el;
|
|
71
|
+
if (!el || (typeof Node !== 'undefined' && !(el instanceof Node))) return null;
|
|
72
|
+
const own = isNode ? {} : made;
|
|
73
|
+
let getValue = typeof own.getValue === 'function' ? own.getValue : null;
|
|
74
|
+
// A form field's value is the editor's; any other element says for itself.
|
|
75
|
+
if (!getValue && el.matches?.('input, select, textarea')) getValue = () => el.value;
|
|
76
|
+
return {
|
|
77
|
+
el,
|
|
78
|
+
getValue,
|
|
79
|
+
focus: typeof own.focus === 'function' ? own.focus : null,
|
|
80
|
+
destroy: typeof own.destroy === 'function' ? own.destroy : null,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class CellEditorHost {
|
|
85
|
+
/**
|
|
86
|
+
* @param {object} o
|
|
87
|
+
* @param {(session, value, move, reason) => void} o.onCommit after the
|
|
88
|
+
* cell is drawn as it was again; `move` is 'down', 'up', 'right', 'left'
|
|
89
|
+
* or null; `reason` is 'key', 'blur', 'editor' or 'scroll'.
|
|
90
|
+
* @param {(session, reason) => void} o.onCancel
|
|
91
|
+
*/
|
|
92
|
+
constructor({ onCommit, onCancel }) {
|
|
93
|
+
this._onCommit = onCommit;
|
|
94
|
+
this._onCancel = onCancel;
|
|
95
|
+
/** The open session, or null. */
|
|
96
|
+
this.session = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Open an editor in `td`. Returns whether one opened (a factory that
|
|
101
|
+
* throws or returns nothing opens none, and the cell is untouched).
|
|
102
|
+
*/
|
|
103
|
+
open({ td, factory, value, row, rowIdx, colIdx, column, initialText = null, ...extra }) {
|
|
104
|
+
if (this.session) return false;
|
|
105
|
+
const s = {
|
|
106
|
+
...extra, td, value, row, rowIdx, colIdx, column,
|
|
107
|
+
previous: value, closed: false, offs: [], saved: null, editor: null,
|
|
108
|
+
};
|
|
109
|
+
let made = null;
|
|
110
|
+
try {
|
|
111
|
+
made = factory({
|
|
112
|
+
value, row, rowIdx, colIdx, column, initialText,
|
|
113
|
+
commit: (v) => this._finish(s, v, null, 'editor'),
|
|
114
|
+
cancel: () => this._cancel(s, 'editor'),
|
|
115
|
+
});
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.error('[DataTable] cell editor factory threw', err);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const editor = normalise(made);
|
|
121
|
+
if (!editor) return false;
|
|
122
|
+
s.editor = editor;
|
|
123
|
+
s.saved = [...td.childNodes];
|
|
124
|
+
s.savedTitle = td.getAttribute('title');
|
|
125
|
+
this.session = s;
|
|
126
|
+
td.classList.add('twm-dt-cell--editing');
|
|
127
|
+
td.replaceChildren(editor.el);
|
|
128
|
+
|
|
129
|
+
const on = (type, fn) => {
|
|
130
|
+
editor.el.addEventListener(type, fn);
|
|
131
|
+
s.offs.push(() => editor.el.removeEventListener(type, fn));
|
|
132
|
+
};
|
|
133
|
+
on('keydown', (ev) => {
|
|
134
|
+
if (ev.isComposing) return;
|
|
135
|
+
ev.stopPropagation();
|
|
136
|
+
if (ev.key === 'Escape') {
|
|
137
|
+
ev.preventDefault();
|
|
138
|
+
this._cancel(s, 'key');
|
|
139
|
+
} else if (ev.key === 'Enter' && !ev.altKey && !ev.ctrlKey && !ev.metaKey && editor.getValue) {
|
|
140
|
+
ev.preventDefault();
|
|
141
|
+
this._finish(s, editor.getValue(), ev.shiftKey ? 'up' : 'down', 'key');
|
|
142
|
+
} else if (ev.key === 'Tab' && !ev.altKey && !ev.ctrlKey && !ev.metaKey && editor.getValue) {
|
|
143
|
+
ev.preventDefault();
|
|
144
|
+
this._finish(s, editor.getValue(), ev.shiftKey ? 'left' : 'right', 'key');
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
// A press inside the editor is the editor's, not a click on the cell.
|
|
148
|
+
for (const type of ['click', 'dblclick', 'mousedown', 'contextmenu']) {
|
|
149
|
+
on(type, (ev) => ev.stopPropagation());
|
|
150
|
+
}
|
|
151
|
+
on('focusout', (ev) => {
|
|
152
|
+
if (s.closed || !editor.getValue) return;
|
|
153
|
+
const to = ev.relatedTarget;
|
|
154
|
+
if (to && editor.el.contains(to)) return;
|
|
155
|
+
// The window losing focus (another app) is not a click elsewhere.
|
|
156
|
+
const doc = editor.el.ownerDocument;
|
|
157
|
+
if (!to && doc && typeof doc.hasFocus === 'function' && !doc.hasFocus()) return;
|
|
158
|
+
this._finish(s, editor.getValue(), null, 'blur');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
if (editor.focus) editor.focus();
|
|
163
|
+
else editor.el.focus?.({ preventScroll: true });
|
|
164
|
+
} catch (_) { /* not focusable */ }
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Commit the open session with what its editor holds. */
|
|
169
|
+
commit(move = null, reason = 'editor') {
|
|
170
|
+
const s = this.session;
|
|
171
|
+
if (!s) return false;
|
|
172
|
+
if (!s.editor.getValue) { this._cancel(s, reason); return false; }
|
|
173
|
+
return this._finish(s, s.editor.getValue(), move, reason);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Cancel the open session: the cell is drawn as it was. */
|
|
177
|
+
cancel(reason = 'editor') {
|
|
178
|
+
const s = this.session;
|
|
179
|
+
if (!s) return false;
|
|
180
|
+
return this._cancel(s, reason);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Take the editor out and put the cell's own content back. */
|
|
184
|
+
_close(s) {
|
|
185
|
+
s.closed = true;
|
|
186
|
+
if (this.session === s) this.session = null;
|
|
187
|
+
for (const off of s.offs) { try { off(); } catch (_) { /* gone */ } }
|
|
188
|
+
s.offs = [];
|
|
189
|
+
try { s.editor.destroy?.(); } catch (err) {
|
|
190
|
+
console.error('[DataTable] cell editor destroy threw', err);
|
|
191
|
+
}
|
|
192
|
+
s.td.classList.remove('twm-dt-cell--editing');
|
|
193
|
+
s.td.replaceChildren(...s.saved);
|
|
194
|
+
if (s.savedTitle == null) s.td.removeAttribute('title');
|
|
195
|
+
else s.td.setAttribute('title', s.savedTitle);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
_finish(s, value, move, reason) {
|
|
199
|
+
if (s.closed) return false;
|
|
200
|
+
this._close(s);
|
|
201
|
+
this._onCommit?.(s, value, move, reason);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_cancel(s, reason) {
|
|
206
|
+
if (s.closed) return false;
|
|
207
|
+
this._close(s);
|
|
208
|
+
this._onCancel?.(s, reason);
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
}
|