flexdesk 0.17.8 → 0.17.9
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 +25 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/THIRD_PARTY_LICENSES.md +0 -0
- package/canvas.js +0 -0
- package/charts.js +0 -0
- package/core.js +0 -0
- package/css/base.css +21 -1
- package/css/flexdesk.css +21 -1
- package/css/overrides.css +0 -0
- package/css/reset.css +0 -0
- package/dist/canvas.js +0 -0
- package/dist/canvas.js.map +0 -0
- package/dist/charts.js +7 -6
- package/dist/charts.js.map +2 -2
- package/dist/{chunk-EIYNF4BD.js → chunk-2YTM3GJE.js} +2 -2
- package/dist/chunk-2YTM3GJE.js.map +7 -0
- package/dist/chunk-3PHCPZHT.js +0 -0
- package/dist/chunk-3PHCPZHT.js.map +0 -0
- package/dist/{chunk-UURZ37SD.js → chunk-7U4IB65H.js} +27 -9
- package/dist/chunk-7U4IB65H.js.map +7 -0
- package/dist/{chunk-D3COLZZ2.js → chunk-BE2PU3LL.js} +2 -2
- package/dist/{chunk-D3COLZZ2.js.map → chunk-BE2PU3LL.js.map} +0 -0
- package/dist/chunk-CVMCZZX2.js +0 -0
- package/dist/chunk-CVMCZZX2.js.map +0 -0
- package/dist/{chunk-UWO43BZU.js → chunk-F35XSQWA.js} +16 -20
- package/dist/chunk-F35XSQWA.js.map +7 -0
- package/dist/chunk-FOOS3T5L.js +0 -0
- package/dist/chunk-FOOS3T5L.js.map +0 -0
- package/dist/chunk-GOQLR5WR.js +0 -0
- package/dist/chunk-GOQLR5WR.js.map +0 -0
- package/dist/chunk-HNU6MM2B.js +0 -0
- package/dist/chunk-HNU6MM2B.js.map +0 -0
- package/dist/chunk-JKZLNKG6.js +0 -0
- package/dist/chunk-JKZLNKG6.js.map +0 -0
- package/dist/chunk-JYWURG5T.js +0 -0
- package/dist/chunk-JYWURG5T.js.map +0 -0
- package/dist/{chunk-RKNFBX5K.js → chunk-MIW7D6WC.js} +16 -4
- package/dist/{chunk-RKNFBX5K.js.map → chunk-MIW7D6WC.js.map} +2 -2
- package/dist/chunk-N7HC3ADV.js +0 -0
- package/dist/chunk-N7HC3ADV.js.map +0 -0
- package/dist/{chunk-LWV23YVE.js → chunk-NIJEJAZ7.js} +42 -18
- package/dist/chunk-NIJEJAZ7.js.map +7 -0
- package/dist/{chunk-HYJYYEPK.js → chunk-REJ5HLIM.js} +2 -2
- package/dist/{chunk-HYJYYEPK.js.map → chunk-REJ5HLIM.js.map} +0 -0
- package/dist/{chunk-WVFGV5FT.js → chunk-WUFLGETN.js} +39 -2
- package/dist/{chunk-WVFGV5FT.js.map → chunk-WUFLGETN.js.map} +3 -3
- package/dist/core.js +0 -0
- package/dist/core.js.map +0 -0
- package/dist/editor.js +2 -2
- package/dist/editor.js.map +0 -0
- package/dist/flexdesk.css +21 -1
- package/dist/flow.js +3 -3
- package/dist/flow.js.map +0 -0
- package/dist/host.js +0 -0
- package/dist/host.js.map +0 -0
- 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 +63 -20
- package/dist/tiles.js.map +2 -2
- package/dist/widgets.js +6 -6
- package/dist/widgets.js.map +0 -0
- package/dist/wm.js +5 -5
- package/dist/wm.js.map +0 -0
- package/docs/README.md +0 -0
- package/docs/best-practices.md +0 -0
- package/docs/components/charts.md +29 -0
- package/docs/components/command-palette.md +0 -0
- package/docs/components/conversation.md +0 -0
- package/docs/components/data-table-grid.md +0 -0
- package/docs/components/data-table.md +0 -0
- package/docs/components/editor.md +0 -0
- package/docs/components/flow-canvas-editor.md +0 -0
- package/docs/components/flow-editors.md +0 -0
- package/docs/components/flow-lane-editor.md +0 -0
- package/docs/components/flow-outline-editor.md +0 -0
- package/docs/components/forms-and-dialogs.md +0 -0
- package/docs/components/input-fields.md +0 -0
- package/docs/components/keymap.md +0 -0
- package/docs/components/markdown-and-outline.md +0 -0
- package/docs/components/menus.md +41 -0
- package/docs/components/notifications.md +0 -0
- package/docs/components/panels-and-desktops.md +0 -0
- package/docs/components/record-list-and-fact-table.md +0 -0
- package/docs/components/settings-view.md +0 -0
- package/docs/components/shell.md +0 -0
- package/docs/components/slide-out-panel.md +0 -0
- package/docs/components/tiles.md +25 -0
- package/docs/components/wizard.md +0 -0
- package/docs/concepts/building-and-packaging.md +0 -0
- package/docs/concepts/domain-neutrality.md +0 -0
- package/docs/concepts/host-port.md +0 -0
- package/docs/concepts/packages-and-entry-points.md +0 -0
- package/docs/concepts/peer-dependencies.md +0 -0
- package/docs/concepts/state-guard.md +0 -0
- package/docs/concepts/styling.md +0 -0
- package/docs/quickstart.md +0 -0
- package/editor.js +0 -0
- package/flow.js +0 -0
- package/host.js +0 -0
- package/package.json +1 -1
- package/src/canvas/canvas_adapter.js +0 -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 +0 -0
- package/src/canvas/layered_layout.js +0 -0
- package/src/charts/chart_types.js +3 -2
- package/src/charts/data_series_plot_window.js +0 -0
- package/src/charts/downsample.js +0 -0
- package/src/charts/plot_config.js +0 -0
- package/src/charts/plot_config_editor.js +8 -2
- package/src/charts/plotly_wrapper.js +0 -0
- package/src/core/event_bus.js +0 -0
- package/src/core/logging.js +0 -0
- package/src/core/settings.js +0 -0
- package/src/core/state_guard.js +0 -0
- package/src/core/state_guard_config.js +0 -0
- package/src/core/state_machine.js +0 -0
- package/src/editor/editor_pane.js +0 -0
- package/src/editor/monaco_editor_factory.js +0 -0
- package/src/editor/monaco_loader.js +0 -0
- package/src/editor/notebook_search.js +0 -0
- package/src/editor/notebook_tab_bar.js +26 -7
- package/src/editor/notebook_undo_manager.js +0 -0
- package/src/editor/split_pane_container.js +0 -0
- package/src/flow/canvas_editor/connections.js +0 -0
- package/src/flow/canvas_editor/editor.js +0 -0
- package/src/flow/canvas_editor/index.js +0 -0
- package/src/flow/canvas_editor/layout.js +0 -0
- package/src/flow/canvas_editor/strings.js +0 -0
- package/src/flow/kit/always_before.js +0 -0
- package/src/flow/kit/catalogue.js +0 -0
- 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 +0 -0
- package/src/flow/kit/keys.js +0 -0
- package/src/flow/kit/logic_graph.js +0 -0
- package/src/flow/kit/references.js +0 -0
- package/src/flow/kit/settings_panel.js +0 -0
- package/src/flow/kit/settings_schema.js +0 -0
- package/src/flow/kit/step_picker.js +0 -0
- package/src/flow/kit/strings.js +0 -0
- package/src/flow/kit/value_picker.js +0 -0
- package/src/flow/kit/widgets.js +0 -0
- package/src/flow/lanes/editor.js +0 -0
- package/src/flow/lanes/index.js +0 -0
- package/src/flow/lanes/layout.js +0 -0
- package/src/flow/lanes/pipeline.js +0 -0
- package/src/flow/lanes/preview.js +0 -0
- package/src/flow/lanes/strings.js +0 -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 +0 -0
- package/src/help/help_registry.js +0 -0
- package/src/host/external_links.js +0 -0
- package/src/host/file_picker.js +0 -0
- package/src/host/host.js +0 -0
- package/src/host/photino_host.js +0 -0
- package/src/host/photino_host.md +0 -0
- package/src/host/pywebview_host.js +0 -0
- package/src/tiles/chart_tiles.js +0 -0
- package/src/tiles/config_schema.js +0 -0
- package/src/tiles/figure_tile.js +0 -0
- package/src/tiles/layout_persistence.js +0 -0
- package/src/tiles/list_tiles.js +0 -0
- package/src/tiles/scope_picker.js +0 -0
- package/src/tiles/tile_base.js +67 -16
- package/src/tiles/tile_config_form.js +0 -0
- package/src/tiles/tile_grid.js +0 -0
- package/src/tiles/tile_registry.js +0 -0
- package/src/tiling/bottom_panel_tabs.js +0 -0
- package/src/tiling/command_palette.js +0 -0
- package/src/tiling/content_registry.js +0 -0
- package/src/tiling/desktops.js +0 -0
- package/src/tiling/entity_sources.js +0 -0
- package/src/tiling/keymap.js +0 -0
- package/src/tiling/kind_taxonomy.js +0 -0
- package/src/tiling/landing_table.js +0 -0
- package/src/tiling/loading_overlay.js +0 -0
- package/src/tiling/nav_panel.js +0 -0
- package/src/tiling/page_factory.js +0 -0
- package/src/tiling/panel_availability.js +0 -0
- package/src/tiling/panel_keys.js +0 -0
- package/src/tiling/shell.js +0 -0
- package/src/tiling/tab_strip.js +0 -0
- package/src/tiling/tile_breadcrumb.js +0 -0
- package/src/tiling/tile_renderer.js +0 -0
- package/src/tiling/tile_tab_menu.js +0 -0
- package/src/tiling/tile_tree.js +0 -0
- package/src/tiling/wm.js +0 -0
- 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 +19 -6
- 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 +0 -0
- package/src/ui/components/cell_editors.js +0 -0
- package/src/ui/components/cell_grid.js +0 -0
- package/src/ui/components/cell_paste.js +0 -0
- package/src/ui/components/cell_selection.js +0 -0
- package/src/ui/components/choice_controls.js +0 -0
- package/src/ui/components/color_field.js +0 -0
- package/src/ui/components/color_list_field.js +0 -0
- package/src/ui/components/column_fit.js +0 -0
- package/src/ui/components/column_groups.js +0 -0
- package/src/ui/components/column_order.js +0 -0
- package/src/ui/components/computing_status_window.js +0 -0
- package/src/ui/components/confirm_dialog.js +0 -0
- package/src/ui/components/context_menu.js +20 -20
- package/src/ui/components/conversation_view.js +0 -0
- package/src/ui/components/crosstab.js +0 -0
- package/src/ui/components/data_table.js +45 -19
- package/src/ui/components/detail_header.js +0 -0
- package/src/ui/components/document_outline.js +0 -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 +0 -0
- package/src/ui/components/gallery_picker.js +0 -0
- package/src/ui/components/header_actions.js +0 -0
- package/src/ui/components/inline_renamer.js +0 -0
- package/src/ui/components/job_progress.js +0 -0
- package/src/ui/components/managed_window.js +0 -0
- package/src/ui/components/menu_bar.js +0 -0
- package/src/ui/components/modal.js +0 -0
- package/src/ui/components/notification_history.js +0 -0
- package/src/ui/components/progress.js +0 -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 +0 -0
- package/src/ui/components/series.js +0 -0
- package/src/ui/components/settings_view.js +0 -0
- package/src/ui/components/slide_out_panel.js +0 -0
- package/src/ui/components/slider_field.js +0 -0
- package/src/ui/components/sortable_list.js +0 -0
- package/src/ui/components/staged_edits.js +0 -0
- package/src/ui/components/staged_history.js +0 -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 +0 -0
- package/src/ui/components/table_values.js +0 -0
- package/src/ui/components/tabs.js +0 -0
- package/src/ui/components/tag_input.js +0 -0
- package/src/ui/components/toast.js +0 -0
- package/src/ui/components/toggle_field.js +0 -0
- package/src/ui/components/tree_view.js +0 -0
- package/src/ui/components/tsv.js +0 -0
- package/src/ui/components/window_taskbar.js +0 -0
- package/src/ui/components/wizard.js +0 -0
- package/src/ui/controllers/window_chrome_controller.js +0 -0
- package/src/ui/notification_center.js +0 -0
- package/src/ui/utils/auto_scrollbars.js +0 -0
- package/src/ui/utils/markdown.js +0 -0
- package/src/ui/utils/menu_viewport.js +81 -0
- package/src/ui/utils/native_context_menu.js +0 -0
- package/src/ui/utils/overlay_scrollbar.js +0 -0
- 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 +0 -0
- package/src/ui/utils/tooltip_service.js +0 -0
- package/tiles.js +0 -0
- package/widgets.js +0 -0
- package/wm.js +0 -0
- package/dist/chunk-EIYNF4BD.js.map +0 -7
- package/dist/chunk-LWV23YVE.js.map +0 -7
- package/dist/chunk-UURZ37SD.js.map +0 -7
- package/dist/chunk-UWO43BZU.js.map +0 -7
package/src/tiles/tile_base.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import { openRawTracesWindow } from '../charts/plot_popout_window.js';
|
|
10
10
|
import { formatVariableLabel } from './config_schema.js';
|
|
11
|
+
import { ActionDropdown } from '../ui/components/action_dropdown.js';
|
|
12
|
+
import { modalHost } from '../ui/components/modal.js';
|
|
11
13
|
// The host is INJECTED, not fetched from a global.
|
|
12
14
|
//
|
|
13
15
|
// This imported `appHost()` from ui/js/ecoagent/app_host.js — a tile reaching into
|
|
@@ -403,6 +405,12 @@ export class TileBase {
|
|
|
403
405
|
if (this._disposed) return;
|
|
404
406
|
this._disposed = true;
|
|
405
407
|
|
|
408
|
+
// An open ⋯ menu lives on document.body; take it back first.
|
|
409
|
+
this._closeTileMenu();
|
|
410
|
+
this._menuButton = null;
|
|
411
|
+
this._menuDropdown = null;
|
|
412
|
+
this._menuHome = null;
|
|
413
|
+
|
|
406
414
|
// Close expand window if open
|
|
407
415
|
if (this._expandWindow?.isVisible) {
|
|
408
416
|
this._expandWindow.close();
|
|
@@ -485,6 +493,13 @@ export class TileBase {
|
|
|
485
493
|
* @private
|
|
486
494
|
*/
|
|
487
495
|
_buildChrome() {
|
|
496
|
+
// A rebuilt header gets a new ⋯ menu; an open old one must not stay
|
|
497
|
+
// behind on document.body.
|
|
498
|
+
this._closeTileMenu();
|
|
499
|
+
this._menuButton = null;
|
|
500
|
+
this._menuDropdown = null;
|
|
501
|
+
this._menuHome = null;
|
|
502
|
+
|
|
488
503
|
// Header with drag handle and controls
|
|
489
504
|
const header = document.createElement('div');
|
|
490
505
|
header.className = 'tile-header';
|
|
@@ -584,23 +599,13 @@ export class TileBase {
|
|
|
584
599
|
const menuBtn = header.querySelector('.tile-menu-btn');
|
|
585
600
|
const menuDropdown = header.querySelector('.tile-menu-dropdown');
|
|
586
601
|
if (menuBtn && menuDropdown) {
|
|
602
|
+
this._menuButton = menuBtn;
|
|
603
|
+
this._menuDropdown = menuDropdown;
|
|
604
|
+
this._menuHome = menuDropdown.parentNode;
|
|
587
605
|
menuBtn.addEventListener('click', (e) => {
|
|
588
606
|
e.stopPropagation();
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if (!isOpen) {
|
|
592
|
-
// Close on outside click
|
|
593
|
-
const closeMenu = (ev) => {
|
|
594
|
-
if (!menuDropdown.contains(ev.target) && ev.target !== menuBtn) {
|
|
595
|
-
menuDropdown.hidden = true;
|
|
596
|
-
document.removeEventListener('pointerdown', closeMenu, true);
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
// Delay to avoid immediate close from the same click
|
|
600
|
-
requestAnimationFrame(() => {
|
|
601
|
-
document.addEventListener('pointerdown', closeMenu, true);
|
|
602
|
-
});
|
|
603
|
-
}
|
|
607
|
+
if (menuDropdown.hidden) this._openTileMenu();
|
|
608
|
+
else this._closeTileMenu();
|
|
604
609
|
});
|
|
605
610
|
// One listener per rendered entry, dispatching by `data-action`
|
|
606
611
|
// into `_onMenuAction`. Wiring each action by name here is what
|
|
@@ -609,7 +614,7 @@ export class TileBase {
|
|
|
609
614
|
menuDropdown.querySelectorAll('.tile-menu-item').forEach((itemEl) => {
|
|
610
615
|
itemEl.addEventListener('click', (e) => {
|
|
611
616
|
e.stopPropagation();
|
|
612
|
-
|
|
617
|
+
this._closeTileMenu();
|
|
613
618
|
this._onMenuAction(itemEl.dataset.action);
|
|
614
619
|
});
|
|
615
620
|
});
|
|
@@ -654,6 +659,52 @@ export class TileBase {
|
|
|
654
659
|
}
|
|
655
660
|
}
|
|
656
661
|
|
|
662
|
+
/**
|
|
663
|
+
* Open the ⋯ menu. It is drawn on `document.body` (or the open modal's
|
|
664
|
+
* host, as `showContextMenu` is), not inside the tile: a tile clips its
|
|
665
|
+
* content, so a menu wider than a narrow tile lost its left part. It is
|
|
666
|
+
* placed with `ActionDropdown.position` — fixed, under the button (above
|
|
667
|
+
* when there is no room), right edges aligned, kept inside the window.
|
|
668
|
+
* @private
|
|
669
|
+
*/
|
|
670
|
+
_openTileMenu() {
|
|
671
|
+
const btn = this._menuButton;
|
|
672
|
+
const menu = this._menuDropdown;
|
|
673
|
+
if (!btn || !menu || !menu.hidden) return;
|
|
674
|
+
(modalHost() || document.body).appendChild(menu);
|
|
675
|
+
menu.hidden = false;
|
|
676
|
+
ActionDropdown.position(btn, menu, { align: 'end' });
|
|
677
|
+
// Close on a press outside the menu and its button. Added a frame
|
|
678
|
+
// later so the press that opened it does not close it.
|
|
679
|
+
this._menuOutsideListener = (ev) => {
|
|
680
|
+
if (!menu.contains(ev.target) && !btn.contains(ev.target)) this._closeTileMenu();
|
|
681
|
+
};
|
|
682
|
+
const listener = this._menuOutsideListener;
|
|
683
|
+
requestAnimationFrame(() => {
|
|
684
|
+
if (this._menuOutsideListener === listener) {
|
|
685
|
+
document.addEventListener('pointerdown', listener, true);
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Close the ⋯ menu and put it back in the tile's header.
|
|
692
|
+
* @private
|
|
693
|
+
*/
|
|
694
|
+
_closeTileMenu() {
|
|
695
|
+
const menu = this._menuDropdown;
|
|
696
|
+
if (this._menuOutsideListener) {
|
|
697
|
+
document.removeEventListener('pointerdown', this._menuOutsideListener, true);
|
|
698
|
+
this._menuOutsideListener = null;
|
|
699
|
+
}
|
|
700
|
+
if (!menu) return;
|
|
701
|
+
menu.hidden = true;
|
|
702
|
+
for (const prop of ['position', 'top', 'bottom', 'left', 'right', 'maxHeight', 'overflowY']) {
|
|
703
|
+
menu.style[prop] = '';
|
|
704
|
+
}
|
|
705
|
+
if (this._menuHome && menu.parentNode !== this._menuHome) this._menuHome.appendChild(menu);
|
|
706
|
+
}
|
|
707
|
+
|
|
657
708
|
/**
|
|
658
709
|
* Handle config button click — highlight tile and emit config request.
|
|
659
710
|
* @private
|
|
File without changes
|
package/src/tiles/tile_grid.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiling/desktops.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/tiling/keymap.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiling/nav_panel.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiling/panel_keys.js
CHANGED
|
File without changes
|
package/src/tiling/shell.js
CHANGED
|
File without changes
|
package/src/tiling/tab_strip.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiling/tile_tree.js
CHANGED
|
File without changes
|
package/src/tiling/wm.js
CHANGED
|
File without changes
|
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
|
|
@@ -379,8 +379,12 @@ export class ActionDropdown {
|
|
|
379
379
|
*
|
|
380
380
|
* @param {HTMLElement} trigger - The element the dropdown opens from
|
|
381
381
|
* @param {HTMLElement} menu - The dropdown menu element to position
|
|
382
|
+
* @param {Object} [opts]
|
|
383
|
+
* @param {'start'|'end'} [opts.align='start'] - Which trigger edge the menu
|
|
384
|
+
* lines up with first: `'start'` its left edge, `'end'` its right edge.
|
|
385
|
+
* Either flips to the other edge when it would leave the window.
|
|
382
386
|
*/
|
|
383
|
-
static position(trigger, menu) {
|
|
387
|
+
static position(trigger, menu, { align = 'start' } = {}) {
|
|
384
388
|
if (!trigger || !menu) return;
|
|
385
389
|
|
|
386
390
|
// Reset any prior inline position so natural size can be measured.
|
|
@@ -424,12 +428,21 @@ export class ActionDropdown {
|
|
|
424
428
|
top = EDGE_MARGIN;
|
|
425
429
|
}
|
|
426
430
|
|
|
427
|
-
// ── Horizontal: align
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (
|
|
431
|
-
// Align right edges instead.
|
|
431
|
+
// ── Horizontal: align with the trigger's start (left) or end (right)
|
|
432
|
+
// edge, flip to the other edge if off-screen ──
|
|
433
|
+
let left;
|
|
434
|
+
if (align === 'end') {
|
|
432
435
|
left = triggerRect.right - menuRect.width;
|
|
436
|
+
if (left < EDGE_MARGIN) {
|
|
437
|
+
// Align left edges instead.
|
|
438
|
+
left = triggerRect.left;
|
|
439
|
+
}
|
|
440
|
+
} else {
|
|
441
|
+
left = triggerRect.left;
|
|
442
|
+
if (left + menuRect.width > vw - EDGE_MARGIN) {
|
|
443
|
+
// Align right edges instead.
|
|
444
|
+
left = triggerRect.right - menuRect.width;
|
|
445
|
+
}
|
|
433
446
|
}
|
|
434
447
|
|
|
435
448
|
// Still off-screen? Clamp to edges.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -24,9 +24,16 @@
|
|
|
24
24
|
* Disabled items are skipped by the arrows rather than focusable-but-inert.
|
|
25
25
|
* `<button>` rather than a div with `tabindex`: the browser then gives Enter and
|
|
26
26
|
* Space for free and screen readers announce it without further help.
|
|
27
|
+
*
|
|
28
|
+
* ── It stays inside the window (BUG-0040) ─────────────────────────────
|
|
29
|
+
* The top was clamped at 0 but nothing capped the height, so a long menu ran
|
|
30
|
+
* off the bottom and could not scroll. `placeMenuInViewport` caps it at the
|
|
31
|
+
* window's height less a margin; it scrolls inside, the arrows scroll the
|
|
32
|
+
* focused item into view, and scrolling the menu itself does not close it.
|
|
27
33
|
*/
|
|
28
34
|
|
|
29
35
|
import { modalHost } from './modal.js';
|
|
36
|
+
import { placeMenuInViewport, moveMenuFocus, focusMenuItem } from '../utils/menu_viewport.js';
|
|
30
37
|
|
|
31
38
|
let _activeMenu = null;
|
|
32
39
|
let _returnFocusTo = null;
|
|
@@ -92,23 +99,20 @@ export function showContextMenu(x, y, items, onAction) {
|
|
|
92
99
|
menu.style.display = 'block';
|
|
93
100
|
_activeMenu = menu;
|
|
94
101
|
|
|
95
|
-
// Position
|
|
96
|
-
|
|
97
|
-
const left = Math.min(x, window.innerWidth - rect.width - 8);
|
|
98
|
-
const top = Math.min(y, window.innerHeight - rect.height - 8);
|
|
99
|
-
menu.style.left = `${Math.max(0, left)}px`;
|
|
100
|
-
menu.style.top = `${Math.max(0, top)}px`;
|
|
102
|
+
// Position inside the window, scrolling when taller than it (BUG-0040).
|
|
103
|
+
placeMenuInViewport(menu, x, y);
|
|
101
104
|
|
|
102
|
-
// Close on outside click / scroll / Escape.
|
|
105
|
+
// Close on outside click / scroll / Escape. A scroll INSIDE the menu is
|
|
106
|
+
// the menu being read, not the page moving under it.
|
|
103
107
|
setTimeout(() => {
|
|
104
108
|
document.addEventListener('mousedown', _outsideHandler, { once: true, capture: true });
|
|
105
109
|
}, 0);
|
|
106
110
|
document.addEventListener('keydown', _keyHandler);
|
|
107
|
-
window.addEventListener('scroll',
|
|
111
|
+
window.addEventListener('scroll', _scrollHandler, { capture: true });
|
|
108
112
|
|
|
109
113
|
// Focus the first item the keyboard can actually use. `preventScroll` so a
|
|
110
114
|
// menu opened near the bottom of a long page does not jump it.
|
|
111
|
-
_enabledItems(menu)[0]
|
|
115
|
+
focusMenuItem(_enabledItems(menu)[0]);
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
export function hideContextMenu() {
|
|
@@ -119,6 +123,7 @@ export function hideContextMenu() {
|
|
|
119
123
|
_activeMenu = null;
|
|
120
124
|
_returnFocusTo = null;
|
|
121
125
|
document.removeEventListener('keydown', _keyHandler);
|
|
126
|
+
window.removeEventListener('scroll', _scrollHandler, { capture: true });
|
|
122
127
|
// Only take focus back if the menu still had it. A click elsewhere has
|
|
123
128
|
// already moved focus deliberately and must not be undone.
|
|
124
129
|
if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });
|
|
@@ -134,17 +139,12 @@ function _enabledItems(menu) {
|
|
|
134
139
|
function _keyHandler(e) {
|
|
135
140
|
if (!_activeMenu) return;
|
|
136
141
|
if (e.key === 'Escape') { e.preventDefault(); hideContextMenu(); return; }
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (e.
|
|
142
|
-
|
|
143
|
-
else if (e.key === 'Home') next = items[0];
|
|
144
|
-
else if (e.key === 'End') next = items[items.length - 1];
|
|
145
|
-
if (!next) return;
|
|
146
|
-
e.preventDefault();
|
|
147
|
-
next.focus({ preventScroll: true });
|
|
142
|
+
moveMenuFocus(e, _enabledItems(_activeMenu));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function _scrollHandler(e) {
|
|
146
|
+
if (_activeMenu && e.target?.nodeType && _activeMenu.contains(e.target)) return;
|
|
147
|
+
hideContextMenu();
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
function _outsideHandler(e) {
|
|
File without changes
|
|
File without changes
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { createRafResizeObserver } from '../utils/raf_resize_observer.js';
|
|
23
|
+
import { placeMenuInViewport, moveMenuFocus, focusMenuItem } from '../utils/menu_viewport.js';
|
|
23
24
|
import {
|
|
24
25
|
ISO_DATE, ISO_DATETIME, formatDate, formatDuration, matchDateFilter,
|
|
25
26
|
matchDurationFilter, parseDateValue, parseDuration,
|
|
@@ -764,6 +765,8 @@ export class DataTable {
|
|
|
764
765
|
// STORY-0010. The right-click the custom items on show were built for,
|
|
765
766
|
// read again when one is chosen.
|
|
766
767
|
this._contextMenuCtx = null;
|
|
768
|
+
// BUG-0040. What had focus when the menu opened, given it back on close.
|
|
769
|
+
this._contextMenuReturnFocus = null;
|
|
767
770
|
// STORY-0010 `keyboardNav`: the row the arrows last moved to.
|
|
768
771
|
this._navCursor = null;
|
|
769
772
|
this._filterDropdownEl = null;
|
|
@@ -7281,6 +7284,7 @@ export class DataTable {
|
|
|
7281
7284
|
|
|
7282
7285
|
const menu = document.createElement('div');
|
|
7283
7286
|
menu.className = 'twm-context-menu data-context-menu';
|
|
7287
|
+
menu.setAttribute('role', 'menu');
|
|
7284
7288
|
menu.style.display = 'none';
|
|
7285
7289
|
menu.style.position = 'fixed';
|
|
7286
7290
|
menu.style.zIndex = '10001';
|
|
@@ -7338,19 +7342,31 @@ export class DataTable {
|
|
|
7338
7342
|
if (event?.target && menu?.contains(event.target)) return;
|
|
7339
7343
|
this._hideContextMenu();
|
|
7340
7344
|
};
|
|
7341
|
-
|
|
7342
|
-
|
|
7345
|
+
// BUG-0040. The keyboard reaches every item: Up/Down/Home/End move
|
|
7346
|
+
// focus (and scroll the item into view in a menu taller than the
|
|
7347
|
+
// window), Enter and Space choose, Escape closes.
|
|
7348
|
+
const onMenuKey = (event) => {
|
|
7349
|
+
if (event.key === 'Escape') { this._hideContextMenu(); return; }
|
|
7350
|
+
if (!menu || menu.style.display === 'none') return;
|
|
7351
|
+
const focused = document.activeElement;
|
|
7352
|
+
if ((event.key === 'Enter' || event.key === ' ')
|
|
7353
|
+
&& focused?.classList?.contains('twm-context-menu-item') && menu.contains(focused)) {
|
|
7354
|
+
event.preventDefault();
|
|
7355
|
+
focused.click();
|
|
7356
|
+
return;
|
|
7357
|
+
}
|
|
7358
|
+
moveMenuFocus(event, this._contextMenuEnabledItems());
|
|
7343
7359
|
};
|
|
7344
7360
|
const hideOnResize = () => this._hideContextMenu();
|
|
7345
7361
|
|
|
7346
7362
|
document.addEventListener('click', hideOnGlobal, true);
|
|
7347
7363
|
document.addEventListener('scroll', hideOnGlobal, true);
|
|
7348
7364
|
window.addEventListener('resize', hideOnResize);
|
|
7349
|
-
document.addEventListener('keydown',
|
|
7365
|
+
document.addEventListener('keydown', onMenuKey);
|
|
7350
7366
|
|
|
7351
7367
|
this._contextMenuGlobals.push(() => document.removeEventListener('click', hideOnGlobal, true));
|
|
7352
7368
|
this._contextMenuGlobals.push(() => document.removeEventListener('scroll', hideOnGlobal, true));
|
|
7353
|
-
this._contextMenuGlobals.push(() => document.removeEventListener('keydown',
|
|
7369
|
+
this._contextMenuGlobals.push(() => document.removeEventListener('keydown', onMenuKey));
|
|
7354
7370
|
this._contextMenuGlobals.push(() => window.removeEventListener('resize', hideOnResize));
|
|
7355
7371
|
}
|
|
7356
7372
|
|
|
@@ -7362,10 +7378,23 @@ export class DataTable {
|
|
|
7362
7378
|
}
|
|
7363
7379
|
|
|
7364
7380
|
_hideContextMenu() {
|
|
7365
|
-
|
|
7366
|
-
|
|
7381
|
+
const menu = this._contextMenuEl;
|
|
7382
|
+
const returnTo = this._contextMenuReturnFocus;
|
|
7383
|
+
this._contextMenuReturnFocus = null;
|
|
7384
|
+
const held = !!menu && menu.style.display !== 'none' && menu.contains(document.activeElement);
|
|
7385
|
+
if (menu) {
|
|
7386
|
+
menu.style.display = 'none';
|
|
7367
7387
|
}
|
|
7368
7388
|
this._disarmContextMenuGlobals();
|
|
7389
|
+
// BUG-0040. Focus goes back where it was, but only if the menu still
|
|
7390
|
+
// had it: a click elsewhere has moved it deliberately.
|
|
7391
|
+
if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });
|
|
7392
|
+
}
|
|
7393
|
+
|
|
7394
|
+
/** BUG-0040. The items the keyboard can land on. */
|
|
7395
|
+
_contextMenuEnabledItems() {
|
|
7396
|
+
const menu = this._contextMenuEl;
|
|
7397
|
+
return menu ? [...menu.querySelectorAll('.twm-context-menu-item:not(.disabled)')] : [];
|
|
7369
7398
|
}
|
|
7370
7399
|
|
|
7371
7400
|
_showContextMenu(clientX, clientY, custom = null) {
|
|
@@ -7381,20 +7410,17 @@ export class DataTable {
|
|
|
7381
7410
|
item.style.pointerEvents = hasSelection ? '' : 'none';
|
|
7382
7411
|
});
|
|
7383
7412
|
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
if (
|
|
7390
|
-
left = window.innerWidth - rect.width - 8;
|
|
7391
|
-
}
|
|
7392
|
-
if (top + rect.height > window.innerHeight) {
|
|
7393
|
-
top = window.innerHeight - rect.height - 8;
|
|
7394
|
-
}
|
|
7413
|
+
// BUG-0040. Every item is a menu item the keyboard can focus.
|
|
7414
|
+
menu.querySelectorAll('.twm-context-menu-item').forEach((item) => {
|
|
7415
|
+
item.tabIndex = -1;
|
|
7416
|
+
if (!item.hasAttribute('role')) item.setAttribute('role', 'menuitem');
|
|
7417
|
+
});
|
|
7418
|
+
if (!menu.contains(document.activeElement)) this._contextMenuReturnFocus = document.activeElement;
|
|
7395
7419
|
|
|
7396
|
-
|
|
7397
|
-
menu.style.
|
|
7420
|
+
// BUG-0040. Inside the window, at most its height, scrolling when longer.
|
|
7421
|
+
menu.style.display = 'block';
|
|
7422
|
+
placeMenuInViewport(menu, clientX, clientY);
|
|
7423
|
+
focusMenuItem(this._contextMenuEnabledItems()[0]);
|
|
7398
7424
|
}
|
|
7399
7425
|
|
|
7400
7426
|
/**
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/ui/components/tsv.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/ui/utils/markdown.js
CHANGED
|
File without changes
|