flexdesk 0.15.2 → 0.16.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 (195) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +70 -0
  3. package/THIRD_PARTY_LICENSES.md +0 -0
  4. package/css/base.css +0 -0
  5. package/css/flexdesk.css +153 -0
  6. package/css/overrides.css +0 -0
  7. package/css/reset.css +0 -0
  8. package/css/tokens.css +0 -0
  9. package/dist/canvas.js +0 -0
  10. package/dist/canvas.js.map +0 -0
  11. package/dist/charts.js +0 -0
  12. package/dist/charts.js.map +0 -0
  13. package/dist/chunk-3PHCPZHT.js +0 -0
  14. package/dist/chunk-3PHCPZHT.js.map +0 -0
  15. package/dist/chunk-6JSOVNID.js +0 -0
  16. package/dist/chunk-6JSOVNID.js.map +0 -0
  17. package/dist/chunk-B5Q53LAJ.js +111 -0
  18. package/dist/chunk-B5Q53LAJ.js.map +7 -0
  19. package/dist/chunk-FBGP5KN5.js +0 -0
  20. package/dist/chunk-FBGP5KN5.js.map +0 -0
  21. package/dist/chunk-FL5KFNQH.js +0 -0
  22. package/dist/chunk-FL5KFNQH.js.map +0 -0
  23. package/dist/chunk-FOOS3T5L.js +0 -0
  24. package/dist/chunk-FOOS3T5L.js.map +0 -0
  25. package/dist/chunk-JYWURG5T.js +0 -0
  26. package/dist/chunk-JYWURG5T.js.map +0 -0
  27. package/dist/chunk-NLTOY57M.js +0 -0
  28. package/dist/chunk-NLTOY57M.js.map +0 -0
  29. package/dist/chunk-NNFTETK3.js +0 -0
  30. package/dist/chunk-NNFTETK3.js.map +0 -0
  31. package/dist/chunk-O5OHMWBB.js +0 -0
  32. package/dist/chunk-O5OHMWBB.js.map +0 -0
  33. package/dist/chunk-RQM5Z2MU.js +0 -0
  34. package/dist/chunk-RQM5Z2MU.js.map +0 -0
  35. package/dist/chunk-SN4LSOAR.js +0 -0
  36. package/dist/chunk-SN4LSOAR.js.map +0 -0
  37. package/dist/{chunk-CWPMLX67.js → chunk-W6FBJLPW.js} +2 -110
  38. package/dist/{chunk-CWPMLX67.js.map → chunk-W6FBJLPW.js.map} +3 -3
  39. package/dist/chunk-WVFGV5FT.js +0 -0
  40. package/dist/chunk-WVFGV5FT.js.map +0 -0
  41. package/dist/core.js +0 -0
  42. package/dist/core.js.map +0 -0
  43. package/dist/editor.js +0 -0
  44. package/dist/editor.js.map +0 -0
  45. package/dist/flexdesk.css +153 -0
  46. package/dist/flow.js +0 -0
  47. package/dist/flow.js.map +0 -0
  48. package/dist/host.js +80 -1
  49. package/dist/host.js.map +3 -3
  50. package/dist/reset.css +0 -0
  51. package/dist/tile_registry-6WZPTUZV.js +0 -0
  52. package/dist/tile_registry-6WZPTUZV.js.map +0 -0
  53. package/dist/tiles.js +471 -16
  54. package/dist/tiles.js.map +4 -4
  55. package/dist/tokens.css +0 -0
  56. package/dist/widgets.js +73 -9
  57. package/dist/widgets.js.map +3 -3
  58. package/dist/wm.js +4 -2
  59. package/dist/wm.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/canvas/canvas_adapter.js +0 -0
  62. package/src/canvas/edge_router.js +0 -0
  63. package/src/canvas/graph/connector_router.js +0 -0
  64. package/src/canvas/graph/history_service.js +0 -0
  65. package/src/canvas/graph/node_platform.js +0 -0
  66. package/src/canvas/index.js +0 -0
  67. package/src/charts/chart_types.js +0 -0
  68. package/src/charts/data_series_plot_window.js +0 -0
  69. package/src/charts/downsample.js +0 -0
  70. package/src/charts/plot_config.js +0 -0
  71. package/src/charts/plot_popout_window.js +0 -0
  72. package/src/charts/plotly_wrapper.js +0 -0
  73. package/src/core/event_bus.js +0 -0
  74. package/src/core/logging.js +0 -0
  75. package/src/core/settings.js +0 -0
  76. package/src/core/state_guard.js +0 -0
  77. package/src/core/state_guard_config.js +0 -0
  78. package/src/core/state_machine.js +0 -0
  79. package/src/editor/editor_pane.js +0 -0
  80. package/src/editor/monaco_editor_factory.js +0 -0
  81. package/src/editor/monaco_loader.js +0 -0
  82. package/src/editor/notebook_search.js +0 -0
  83. package/src/editor/notebook_tab_bar.js +0 -0
  84. package/src/editor/notebook_undo_manager.js +0 -0
  85. package/src/editor/split_pane_container.js +0 -0
  86. package/src/flow/canvas_editor/editor.js +0 -0
  87. package/src/flow/canvas_editor/index.js +0 -0
  88. package/src/flow/canvas_editor/layout.js +0 -0
  89. package/src/flow/canvas_editor/strings.js +0 -0
  90. package/src/flow/kit/always_before.js +0 -0
  91. package/src/flow/kit/catalogue.js +0 -0
  92. package/src/flow/kit/chip_input.js +0 -0
  93. package/src/flow/kit/dom.js +0 -0
  94. package/src/flow/kit/findings.js +0 -0
  95. package/src/flow/kit/history.js +0 -0
  96. package/src/flow/kit/index.js +0 -0
  97. package/src/flow/kit/keys.js +0 -0
  98. package/src/flow/kit/logic_graph.js +0 -0
  99. package/src/flow/kit/references.js +0 -0
  100. package/src/flow/kit/settings_panel.js +0 -0
  101. package/src/flow/kit/settings_schema.js +0 -0
  102. package/src/flow/kit/step_picker.js +0 -0
  103. package/src/flow/kit/strings.js +0 -0
  104. package/src/flow/kit/value_picker.js +0 -0
  105. package/src/flow/kit/widgets.js +0 -0
  106. package/src/flow/lanes/editor.js +0 -0
  107. package/src/flow/lanes/index.js +0 -0
  108. package/src/flow/lanes/layout.js +0 -0
  109. package/src/flow/lanes/pipeline.js +0 -0
  110. package/src/flow/lanes/preview.js +0 -0
  111. package/src/flow/lanes/strings.js +0 -0
  112. package/src/flow/outline/editor.js +0 -0
  113. package/src/flow/outline/index.js +0 -0
  114. package/src/flow/outline/mapping.js +0 -0
  115. package/src/flow/outline/menu.js +0 -0
  116. package/src/flow/outline/operations.js +0 -0
  117. package/src/flow/outline/recognise.js +0 -0
  118. package/src/flow/outline/strings.js +0 -0
  119. package/src/help/help_modal.js +0 -0
  120. package/src/help/help_registry.js +0 -0
  121. package/src/host/external_links.js +94 -0
  122. package/src/host/host.js +4 -1
  123. package/src/host/photino_host.js +7 -0
  124. package/src/host/photino_host.md +18 -0
  125. package/src/host/pywebview_host.js +0 -0
  126. package/src/tiles/chart_tiles.js +0 -0
  127. package/src/tiles/config_schema.js +0 -0
  128. package/src/tiles/layout_persistence.js +0 -0
  129. package/src/tiles/list_tiles.js +422 -0
  130. package/src/tiles/scope_picker.js +96 -0
  131. package/src/tiles/tile_base.js +11 -0
  132. package/src/tiles/tile_grid.js +36 -1
  133. package/src/tiles/tile_registry.js +0 -0
  134. package/src/tiling/bottom_panel_tabs.js +0 -0
  135. package/src/tiling/command_palette.js +0 -0
  136. package/src/tiling/content_registry.js +0 -0
  137. package/src/tiling/desktops.js +0 -0
  138. package/src/tiling/entity_sources.js +0 -0
  139. package/src/tiling/keymap.js +0 -0
  140. package/src/tiling/kind_taxonomy.js +0 -0
  141. package/src/tiling/landing_table.js +0 -0
  142. package/src/tiling/loading_overlay.js +0 -0
  143. package/src/tiling/nav_panel.js +0 -0
  144. package/src/tiling/page_factory.js +0 -0
  145. package/src/tiling/panel_keys.js +0 -0
  146. package/src/tiling/shell.js +0 -0
  147. package/src/tiling/tab_strip.js +0 -0
  148. package/src/tiling/tile_breadcrumb.js +0 -0
  149. package/src/tiling/tile_renderer.js +0 -0
  150. package/src/tiling/tile_tab_menu.js +0 -0
  151. package/src/tiling/tile_tree.js +0 -0
  152. package/src/tiling/wm.js +0 -0
  153. package/src/tiling/zoom.js +0 -0
  154. package/src/ui/base/component_base.js +0 -0
  155. package/src/ui/base/controller_base.js +0 -0
  156. package/src/ui/base/page_base.js +0 -0
  157. package/src/ui/components/about_dialog.js +0 -0
  158. package/src/ui/components/action_dropdown.js +0 -0
  159. package/src/ui/components/alert_log.js +0 -0
  160. package/src/ui/components/alerts_pane.js +0 -0
  161. package/src/ui/components/attribute_list_editor.js +0 -0
  162. package/src/ui/components/autocomplete_field.js +0 -0
  163. package/src/ui/components/column_fit.js +0 -0
  164. package/src/ui/components/computing_status_window.js +0 -0
  165. package/src/ui/components/confirm_dialog.js +0 -0
  166. package/src/ui/components/context_menu.js +0 -0
  167. package/src/ui/components/data_table.js +0 -0
  168. package/src/ui/components/detail_header.js +0 -0
  169. package/src/ui/components/drag_reorder.js +0 -0
  170. package/src/ui/components/fact_table.js +0 -0
  171. package/src/ui/components/gallery_picker.js +0 -0
  172. package/src/ui/components/inline_renamer.js +0 -0
  173. package/src/ui/components/managed_window.js +0 -0
  174. package/src/ui/components/modal.js +0 -0
  175. package/src/ui/components/notification_history.js +0 -0
  176. package/src/ui/components/record_list.js +0 -0
  177. package/src/ui/components/row_activation.js +0 -0
  178. package/src/ui/components/slide_out_panel.js +0 -0
  179. package/src/ui/components/slider_field.js +0 -0
  180. package/src/ui/components/sortable_list.js +26 -4
  181. package/src/ui/components/table_filter_chips.js +0 -0
  182. package/src/ui/components/table_state_store.js +0 -0
  183. package/src/ui/components/table_values.js +0 -0
  184. package/src/ui/components/toast.js +0 -0
  185. package/src/ui/components/tree_view.js +0 -0
  186. package/src/ui/components/window_taskbar.js +0 -0
  187. package/src/ui/controllers/panel_state_machine.js +0 -0
  188. package/src/ui/controllers/window_chrome_controller.js +0 -0
  189. package/src/ui/notification_center.js +0 -0
  190. package/src/ui/utils/auto_scrollbars.js +0 -0
  191. package/src/ui/utils/native_context_menu.js +61 -0
  192. package/src/ui/utils/overlay_scrollbar.js +0 -0
  193. package/src/ui/utils/raf_resize_observer.js +0 -0
  194. package/src/ui/utils/scroll_lock.js +0 -0
  195. package/src/ui/utils/tooltip_service.js +0 -0
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -1789,6 +1789,32 @@ so the window snaps, takes Win+arrows and resizes like any other.
1789
1789
  frame styles, return the whole window from `WM_NCCALCSIZE`, start gestures with
1790
1790
  `WM_NCLBUTTONDOWN`. Don't use Photino's `SetChromeless`, which loses all of that.
1791
1791
 
1792
+ ### Links out, and the browser's right-click menu (0.16.0)
1793
+
1794
+ In a native window a `target="_blank"` link or a `window.open()` opens nothing,
1795
+ or a bare popup with no way back. Hand them to the system browser instead:
1796
+
1797
+ ```js
1798
+ import { routeExternalLinks } from '@flexdesk/host';
1799
+ import { suppressNativeContextMenu } from '@flexdesk/widgets';
1800
+
1801
+ routeExternalLinks({ host }); // a no-op without host.shell, so call it anywhere
1802
+ suppressNativeContextMenu(); // only your own menus, plus the native one in text
1803
+ ```
1804
+
1805
+ `routeExternalLinks` sends http(s) and mailto links that leave the app (a
1806
+ `_blank` target, another origin, a middle click) and external `window.open`
1807
+ calls to `host.shell.openExternal(url)`. That is a new optional host
1808
+ capability, which `createPhotinoHost()` offers as `shell.openExternal`. If the
1809
+ native side refuses, the original `window.open` runs. In a browser tab the
1810
+ host has no `shell`, and nothing changes.
1811
+
1812
+ `suppressNativeContextMenu` prevents the browser's or webview's own menu (Back,
1813
+ Reload, Print, Inspect) on any right-click that no handler claimed with
1814
+ `preventDefault()`. It keeps the native menu in editable text and on selected
1815
+ text, where Paste and Copy are the point, and wherever `allow(event)` says so.
1816
+ Both calls return `{ dispose }`.
1817
+
1792
1818
  ## Peer dependencies
1793
1819
 
1794
1820
  `@flexdesk/charts` needs Plotly (4.4 MB). `@flexdesk/editor` needs Monaco (14 MB).
@@ -1869,6 +1895,50 @@ A chart tile's **Download PNG** (0.14.1) goes through the grid's `host`
1869
1895
  toasts where the file went, a cancel says nothing — and with no host, or a
1870
1896
  `saveFile` that answers null, the browser download it always was.
1871
1897
 
1898
+ ### List and people tiles, and one scope for the board (0.16.1)
1899
+
1900
+ Two more widgets, registered by `registerListTiles()` (idempotent, nothing at
1901
+ import). Like the chart tile they take **rows you have already worked out**;
1902
+ what they own is what every list on a board needs: the count ("7 of 12"), the
1903
+ line that says what empty means, a button in the bar, a click, the keyboard and
1904
+ a right-click menu per row.
1905
+
1906
+ ```js
1907
+ import { registerListTiles, createScopePicker } from '@flexdesk/tiles';
1908
+ registerListTiles();
1909
+ const late = grid.addTile('flexdesk.list', { x: 0, y: 0, w: 12, h: 4 },
1910
+ { title: 'Overdue', empty_text: 'Nothing is late.', max_rows: 0 });
1911
+ late.onRowActivate = ({ row, event }) => open(row.key, { modified: event.ctrlKey });
1912
+ late.rowMenu = ({ row }) => [{ label: 'Open', icon: 'open_in_new', action: 'open' }];
1913
+ late.onRowMenuAction = ({ action, row }) => { /* … */ };
1914
+ late.onAction = () => openAsList();
1915
+ late.setData({
1916
+ rows: [{ key: '7', icon: 'task', lead: 'TASK-0007', title: 'Sign-off',
1917
+ cells: [{ text: 'priya' }, { text: '9 days late', tone: 'danger' }] }],
1918
+ action: { label: 'Open as a list', icon: 'list' },
1919
+ });
1920
+ grid.addTile('flexdesk.people', { x: 0, y: 4, w: 6, h: 4 }, { title: 'Who has what' })
1921
+ .setData({ people: [{ key: '', name: 'Nobody', muted: true, count: 2,
1922
+ pills: [{ text: '2 undated', tone: 'muted' }] }] });
1923
+ ```
1924
+
1925
+ - A row is `role="button"`: click, Enter and Space call `onRowActivate` with the
1926
+ event; Up/Down/Home/End move. Right-click, the context-menu key and Shift+F10
1927
+ open the row's menu (`showContextMenu`'s items; none means no menu). A row
1928
+ carries `data-row-key`; `rowsDraggable` makes rows draggable.
1929
+ - `{rows: []}` keeps the bar and says `empty` (or `config.empty_text`) where the
1930
+ rows would be; `setData(null)` is the empty state. Text is always text.
1931
+ - `tone` (`danger`, `warning`, `ok`, `muted`) picks a colour only; `className`
1932
+ on a row or a cell is yours, added as it is.
1933
+ - `groupByPerson(items, personOf)` groups for a people tile, most first.
1934
+
1935
+ **One scope for the whole board.** `createScopePicker({options, value, onChange})`
1936
+ is a row of buttons, one pressed. `grid.setScope(value)` (or
1937
+ `new TileGrid({ scope, onScopeChange })`) hands the value to every tile as
1938
+ `tile.scope` and `tile.onScopeChange(scope)`, a tile added later starts with
1939
+ it, a grid with a data source reloads its tiles, and the grid's `onScopeChange`
1940
+ is told. What a scope means is yours; it is not part of the layout.
1941
+
1872
1942
  ## Styling
1873
1943
 
1874
1944
  Three stylesheets, and that is the whole CSS contract:
File without changes
package/css/base.css CHANGED
File without changes
package/css/flexdesk.css CHANGED
@@ -3565,6 +3565,159 @@ body.twm-window-maximized .twm-resize-handle {
3565
3565
  font-size: var(--font-size-xs);
3566
3566
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
3567
3567
  }
3568
+ /* ── the list and people tiles (0.16.1, list_tiles.js) ──────────────────
3569
+ A bar (count, button), the rows, a note. The rows scroll inside the tile;
3570
+ the bar and the note stay put. */
3571
+ .fd-list-tile {
3572
+ flex: 1 1 auto;
3573
+ display: flex;
3574
+ flex-direction: column;
3575
+ min-height: 0;
3576
+ gap: var(--space-xs);
3577
+ margin: calc(-1 * var(--space-sm)) 0;
3578
+ }
3579
+ .fd-list-tile__bar {
3580
+ flex: 0 0 auto;
3581
+ display: flex;
3582
+ align-items: center;
3583
+ gap: var(--space-sm);
3584
+ min-height: 24px;
3585
+ }
3586
+ .fd-list-tile__count {
3587
+ color: var(--text-muted);
3588
+ font-size: var(--font-size-xs);
3589
+ font-variant-numeric: tabular-nums;
3590
+ }
3591
+ .fd-list-tile__spacer { flex: 1 1 auto; }
3592
+ .fd-list-tile__action {
3593
+ display: inline-flex;
3594
+ align-items: center;
3595
+ gap: 4px;
3596
+ padding: 2px 8px;
3597
+ border: 1px solid var(--border-subtle);
3598
+ border-radius: var(--radius-sm);
3599
+ background: transparent;
3600
+ color: var(--text-secondary);
3601
+ font: inherit;
3602
+ font-size: var(--font-size-xs);
3603
+ cursor: pointer;
3604
+ }
3605
+ .fd-list-tile__action:hover { color: var(--text-bright); border-color: var(--text-muted); }
3606
+ .fd-list-tile__action .material-symbols-outlined { font-size: 15px; }
3607
+ .fd-list-tile__rows {
3608
+ flex: 1 1 auto;
3609
+ min-height: 0;
3610
+ overflow-y: auto;
3611
+ overflow-x: hidden;
3612
+ }
3613
+ .fd-list-tile__empty {
3614
+ padding: var(--space-sm) 2px;
3615
+ color: var(--text-muted);
3616
+ font-size: var(--font-size-sm);
3617
+ }
3618
+ .fd-list-tile__row {
3619
+ display: flex;
3620
+ align-items: center;
3621
+ gap: var(--space-sm);
3622
+ min-width: 0;
3623
+ padding: 3px 4px;
3624
+ border-radius: var(--radius-sm);
3625
+ font-size: var(--font-size-sm);
3626
+ cursor: pointer;
3627
+ outline: none;
3628
+ }
3629
+ .fd-list-tile__row:hover { background: rgba(255, 255, 255, 0.05); }
3630
+ .fd-list-tile__row:focus-visible { box-shadow: inset 0 0 0 1px var(--accent-bright); }
3631
+ .fd-list-tile__icon { flex: 0 0 auto; font-size: 16px; color: var(--text-muted); }
3632
+ .fd-list-tile__lead {
3633
+ flex: 0 0 auto;
3634
+ color: var(--text-muted);
3635
+ font-variant-numeric: tabular-nums;
3636
+ white-space: nowrap;
3637
+ }
3638
+ .fd-list-tile__title {
3639
+ flex: 1 1 auto;
3640
+ min-width: 0;
3641
+ color: var(--text-bright);
3642
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
3643
+ }
3644
+ .fd-list-tile__cell {
3645
+ flex: 0 0 auto;
3646
+ display: inline-flex;
3647
+ align-items: center;
3648
+ gap: 2px;
3649
+ max-width: 40%;
3650
+ color: var(--text-secondary);
3651
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
3652
+ }
3653
+ .fd-list-tile__cell .material-symbols-outlined { font-size: 14px; }
3654
+ .fd-list-tile__cell--danger, .fd-people-tile__pill--danger { color: var(--color-danger); }
3655
+ .fd-list-tile__cell--warning, .fd-people-tile__pill--warning { color: var(--color-warning); }
3656
+ .fd-list-tile__cell--ok, .fd-people-tile__pill--ok { color: var(--color-success); }
3657
+ .fd-list-tile__cell--muted, .fd-people-tile__pill--muted { color: var(--text-muted); }
3658
+ .fd-list-tile__note {
3659
+ flex: 0 0 auto;
3660
+ font-size: var(--font-size-xs);
3661
+ color: var(--text-muted);
3662
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
3663
+ }
3664
+ .fd-people-tile__name {
3665
+ flex: 0 1 auto;
3666
+ min-width: 0;
3667
+ color: var(--text-bright);
3668
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
3669
+ }
3670
+ .fd-people-tile__name--muted { color: var(--text-muted); font-style: italic; }
3671
+ .fd-people-tile__pills {
3672
+ flex: 1 1 auto;
3673
+ display: flex;
3674
+ flex-wrap: nowrap;
3675
+ gap: 4px;
3676
+ min-width: 0;
3677
+ overflow: hidden;
3678
+ }
3679
+ .fd-people-tile__pill {
3680
+ flex: 0 0 auto;
3681
+ padding: 0 6px;
3682
+ border: 1px solid var(--border-subtle);
3683
+ border-radius: 9px;
3684
+ font-size: var(--font-size-xs);
3685
+ line-height: 16px;
3686
+ color: var(--text-secondary);
3687
+ white-space: nowrap;
3688
+ }
3689
+ .fd-people-tile__count {
3690
+ flex: 0 0 auto;
3691
+ color: var(--text-secondary);
3692
+ font-size: var(--font-size-xs);
3693
+ font-variant-numeric: tabular-nums;
3694
+ }
3695
+
3696
+ /* ── the scope picker (0.16.1, scope_picker.js): one pressed button of a row */
3697
+ .fd-scope-picker {
3698
+ display: inline-flex;
3699
+ align-items: stretch;
3700
+ border: 1px solid var(--border-subtle);
3701
+ border-radius: var(--radius-sm);
3702
+ overflow: hidden;
3703
+ }
3704
+ .fd-scope-picker__btn {
3705
+ padding: 3px 10px;
3706
+ border: 0;
3707
+ border-left: 1px solid var(--border-subtle);
3708
+ background: transparent;
3709
+ color: var(--text-secondary);
3710
+ font: inherit;
3711
+ font-size: var(--font-size-sm);
3712
+ white-space: nowrap;
3713
+ cursor: pointer;
3714
+ }
3715
+ .fd-scope-picker__btn:first-child { border-left: 0; }
3716
+ .fd-scope-picker__btn:hover { color: var(--text-bright); background: rgba(255, 255, 255, 0.05); }
3717
+ .fd-scope-picker__btn--on,
3718
+ .fd-scope-picker__btn--on:hover { background: var(--accent); color: #fff; }
3719
+ .fd-scope-picker__btn:focus-visible { outline: 1px solid var(--accent-bright); outline-offset: -2px; }
3720
+
3568
3721
  /* ===================================================================
3569
3722
  REUSABLE ACTION DROPDOWN COMPONENT
3570
3723
  Used by Add Scenario, Add Widget, and other action dropdowns
package/css/overrides.css CHANGED
File without changes
package/css/reset.css CHANGED
File without changes
package/css/tokens.css CHANGED
File without changes
package/dist/canvas.js CHANGED
File without changes
File without changes
package/dist/charts.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,111 @@
1
+ import {
2
+ modalHost
3
+ } from "./chunk-NNFTETK3.js";
4
+
5
+ // src/ui/components/context_menu.js
6
+ var _activeMenu = null;
7
+ var _returnFocusTo = null;
8
+ function showContextMenu(x, y, items, onAction) {
9
+ hideContextMenu();
10
+ _returnFocusTo = document.activeElement;
11
+ const menu = document.createElement("div");
12
+ menu.className = "twm-context-menu ea-context-menu";
13
+ menu.setAttribute("role", "menu");
14
+ for (const it of items) {
15
+ if (it.separator) {
16
+ const sep = document.createElement("div");
17
+ sep.className = "twm-context-menu__separator";
18
+ sep.setAttribute("role", "separator");
19
+ menu.appendChild(sep);
20
+ continue;
21
+ }
22
+ const row = document.createElement("button");
23
+ row.type = "button";
24
+ row.setAttribute("role", "menuitem");
25
+ let cls = "twm-context-menu-item";
26
+ if (it.danger) cls += " twm-delete-node";
27
+ if (it.disabled) cls += " disabled";
28
+ row.className = cls;
29
+ if (it.disabled) {
30
+ row.disabled = true;
31
+ row.setAttribute("aria-disabled", "true");
32
+ }
33
+ if (it.title) row.title = it.title;
34
+ row.innerHTML = `
35
+ <span class="material-symbols-outlined">${it.icon || ""}</span>
36
+ <span>${escapeHtml(it.label)}</span>
37
+ `;
38
+ if (!it.disabled) {
39
+ row.addEventListener("click", (e) => {
40
+ e.stopPropagation();
41
+ hideContextMenu();
42
+ onAction?.(it.action);
43
+ });
44
+ }
45
+ menu.appendChild(row);
46
+ }
47
+ (modalHost() || document.body).appendChild(menu);
48
+ menu.style.display = "block";
49
+ _activeMenu = menu;
50
+ const rect = menu.getBoundingClientRect();
51
+ const left = Math.min(x, window.innerWidth - rect.width - 8);
52
+ const top = Math.min(y, window.innerHeight - rect.height - 8);
53
+ menu.style.left = `${Math.max(0, left)}px`;
54
+ menu.style.top = `${Math.max(0, top)}px`;
55
+ setTimeout(() => {
56
+ document.addEventListener("mousedown", _outsideHandler, { once: true, capture: true });
57
+ }, 0);
58
+ document.addEventListener("keydown", _keyHandler);
59
+ window.addEventListener("scroll", hideContextMenu, { once: true, capture: true });
60
+ _enabledItems(menu)[0]?.focus({ preventScroll: true });
61
+ }
62
+ function hideContextMenu() {
63
+ if (!_activeMenu) return;
64
+ const returnTo = _returnFocusTo;
65
+ const held = _activeMenu.contains(document.activeElement);
66
+ _activeMenu.remove();
67
+ _activeMenu = null;
68
+ _returnFocusTo = null;
69
+ document.removeEventListener("keydown", _keyHandler);
70
+ if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });
71
+ }
72
+ function _enabledItems(menu) {
73
+ return [...menu.querySelectorAll(".twm-context-menu-item:not(.disabled)")];
74
+ }
75
+ function _keyHandler(e) {
76
+ if (!_activeMenu) return;
77
+ if (e.key === "Escape") {
78
+ e.preventDefault();
79
+ hideContextMenu();
80
+ return;
81
+ }
82
+ const items = _enabledItems(_activeMenu);
83
+ if (items.length === 0) return;
84
+ const at = items.indexOf(document.activeElement);
85
+ let next = null;
86
+ if (e.key === "ArrowDown") next = items[(at + 1 + items.length) % items.length];
87
+ else if (e.key === "ArrowUp") next = items[(at - 1 + items.length) % items.length];
88
+ else if (e.key === "Home") next = items[0];
89
+ else if (e.key === "End") next = items[items.length - 1];
90
+ if (!next) return;
91
+ e.preventDefault();
92
+ next.focus({ preventScroll: true });
93
+ }
94
+ function _outsideHandler(e) {
95
+ if (_activeMenu && !_activeMenu.contains(e.target)) hideContextMenu();
96
+ }
97
+ function escapeHtml(s) {
98
+ return String(s).replace(/[&<>"']/g, (c) => ({
99
+ "&": "&amp;",
100
+ "<": "&lt;",
101
+ ">": "&gt;",
102
+ '"': "&quot;",
103
+ "'": "&#39;"
104
+ })[c]);
105
+ }
106
+
107
+ export {
108
+ showContextMenu,
109
+ hideContextMenu
110
+ };
111
+ //# sourceMappingURL=chunk-B5Q53LAJ.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/ui/components/context_menu.js"],
4
+ "sourcesContent": ["/**\n * context_menu.js \u2014 small popup menu that matches Ecosim's `.context-menu`\n * idiom (see main_new.css line 9713 and ui/js/ui/components/data_table.js).\n *\n * Usage:\n * showContextMenu(x, y, [\n * { label: 'Open', icon: 'open_in_new', action: 'open' },\n * { separator: true },\n * { label: 'Delete', icon: 'delete', action: 'delete', danger: true },\n * ], (action) => { ... });\n *\n * The menu auto-closes on outside click / scroll / Escape.\n *\n * \u2500\u2500 It is operable by keyboard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * Items were bare `<div>`s with a click listener: no role, no tab stop, no\n * arrow-key movement, and `showContextMenu` never moved focus into the menu.\n * A consumer that opened this from Shift+F10 \u2014 the rail in Tables does \u2014 put a\n * menu on screen that the keyboard could only dismiss. Every command in it was\n * unreachable without a pointer, and nothing said so.\n *\n * So an item is a `<button role=\"menuitem\">`, the first enabled one takes focus\n * when the menu opens, Up/Down/Home/End move between them and wrap, Enter and\n * Space activate, Escape closes, and focus returns to whatever had it before.\n * Disabled items are skipped by the arrows rather than focusable-but-inert.\n * `<button>` rather than a div with `tabindex`: the browser then gives Enter and\n * Space for free and screen readers announce it without further help.\n */\n\nimport { modalHost } from './modal.js';\n\nlet _activeMenu = null;\nlet _returnFocusTo = null;\n\nexport function showContextMenu(x, y, items, onAction) {\n hideContextMenu();\n\n // Whatever had focus when the menu opened gets it back when the menu\n // closes. Without this a keyboard user who presses Escape is returned to\n // `document.body` and has to tab back to where they were.\n _returnFocusTo = document.activeElement;\n\n const menu = document.createElement('div');\n menu.className = 'twm-context-menu ea-context-menu';\n menu.setAttribute('role', 'menu');\n\n for (const it of items) {\n if (it.separator) {\n const sep = document.createElement('div');\n sep.className = 'twm-context-menu__separator';\n sep.setAttribute('role', 'separator');\n menu.appendChild(sep);\n continue;\n }\n const row = document.createElement('button');\n row.type = 'button';\n row.setAttribute('role', 'menuitem');\n let cls = 'twm-context-menu-item';\n if (it.danger) cls += ' twm-delete-node';\n if (it.disabled) cls += ' disabled';\n row.className = cls;\n if (it.disabled) {\n row.disabled = true;\n row.setAttribute('aria-disabled', 'true');\n }\n // A DISABLED ROW WITH NO EXPLANATION IS A DEAD CONTROL. `title` was\n // accepted by callers and rendered by nothing \u2014 the item was built with\n // one, the row silently dropped it, and the user got a greyed line with\n // no way to learn why. It is the same shape as the classes this\n // repository keeps finding: no error, no throw, and invisible to every\n // test that checks the row is disabled.\n if (it.title) row.title = it.title;\n row.innerHTML = `\n <span class=\"material-symbols-outlined\">${it.icon || ''}</span>\n <span>${escapeHtml(it.label)}</span>\n `;\n if (!it.disabled) {\n row.addEventListener('click', (e) => {\n e.stopPropagation();\n hideContextMenu();\n onAction?.(it.action);\n });\n }\n menu.appendChild(row);\n }\n\n // C25. THE SAME QUESTION A MODAL ASKS: which window is the user in? A\n // consumer spanning two browser windows sets the host when its focus moves;\n // null \u2014 every consumer today \u2014 is `document.body`, exactly as before. A\n // menu in the wrong window is worse than a modal in the wrong window,\n // because it is positioned at coordinates from the OTHER one.\n (modalHost() || document.body).appendChild(menu);\n menu.style.display = 'block';\n _activeMenu = menu;\n\n // Position with viewport clamping.\n const rect = menu.getBoundingClientRect();\n const left = Math.min(x, window.innerWidth - rect.width - 8);\n const top = Math.min(y, window.innerHeight - rect.height - 8);\n menu.style.left = `${Math.max(0, left)}px`;\n menu.style.top = `${Math.max(0, top)}px`;\n\n // Close on outside click / scroll / Escape.\n setTimeout(() => {\n document.addEventListener('mousedown', _outsideHandler, { once: true, capture: true });\n }, 0);\n document.addEventListener('keydown', _keyHandler);\n window.addEventListener('scroll', hideContextMenu, { once: true, capture: true });\n\n // Focus the first item the keyboard can actually use. `preventScroll` so a\n // menu opened near the bottom of a long page does not jump it.\n _enabledItems(menu)[0]?.focus({ preventScroll: true });\n}\n\nexport function hideContextMenu() {\n if (!_activeMenu) return;\n const returnTo = _returnFocusTo;\n const held = _activeMenu.contains(document.activeElement);\n _activeMenu.remove();\n _activeMenu = null;\n _returnFocusTo = null;\n document.removeEventListener('keydown', _keyHandler);\n // Only take focus back if the menu still had it. A click elsewhere has\n // already moved focus deliberately and must not be undone.\n if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });\n}\n\nfunction _enabledItems(menu) {\n return [...menu.querySelectorAll('.twm-context-menu-item:not(.disabled)')];\n}\n\n/** Up/Down/Home/End move; the list WRAPS, which is what a menu of four items\n * wants and what every desktop menu does. Enter and Space are the button's\n * own, so they are not bound here. */\nfunction _keyHandler(e) {\n if (!_activeMenu) return;\n if (e.key === 'Escape') { e.preventDefault(); hideContextMenu(); return; }\n const items = _enabledItems(_activeMenu);\n if (items.length === 0) return;\n const at = items.indexOf(document.activeElement);\n let next = null;\n if (e.key === 'ArrowDown') next = items[(at + 1 + items.length) % items.length];\n else if (e.key === 'ArrowUp') next = items[(at - 1 + items.length) % items.length];\n else if (e.key === 'Home') next = items[0];\n else if (e.key === 'End') next = items[items.length - 1];\n if (!next) return;\n e.preventDefault();\n next.focus({ preventScroll: true });\n}\n\nfunction _outsideHandler(e) {\n if (_activeMenu && !_activeMenu.contains(e.target)) hideContextMenu();\n}\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
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
@@ -1,7 +1,3 @@
1
- import {
2
- modalHost
3
- } from "./chunk-NNFTETK3.js";
4
-
5
1
  // src/help/help_registry.js
6
2
  var EMPTY_PROVIDER = Object.freeze({
7
3
  getCategories: () => [],
@@ -361,115 +357,11 @@ if (typeof document !== "undefined") {
361
357
  setupGlobalHelpShortcut();
362
358
  }
363
359
 
364
- // src/ui/components/context_menu.js
365
- var _activeMenu = null;
366
- var _returnFocusTo = null;
367
- function showContextMenu(x, y, items, onAction) {
368
- hideContextMenu();
369
- _returnFocusTo = document.activeElement;
370
- const menu = document.createElement("div");
371
- menu.className = "twm-context-menu ea-context-menu";
372
- menu.setAttribute("role", "menu");
373
- for (const it of items) {
374
- if (it.separator) {
375
- const sep = document.createElement("div");
376
- sep.className = "twm-context-menu__separator";
377
- sep.setAttribute("role", "separator");
378
- menu.appendChild(sep);
379
- continue;
380
- }
381
- const row = document.createElement("button");
382
- row.type = "button";
383
- row.setAttribute("role", "menuitem");
384
- let cls = "twm-context-menu-item";
385
- if (it.danger) cls += " twm-delete-node";
386
- if (it.disabled) cls += " disabled";
387
- row.className = cls;
388
- if (it.disabled) {
389
- row.disabled = true;
390
- row.setAttribute("aria-disabled", "true");
391
- }
392
- if (it.title) row.title = it.title;
393
- row.innerHTML = `
394
- <span class="material-symbols-outlined">${it.icon || ""}</span>
395
- <span>${escapeHtml(it.label)}</span>
396
- `;
397
- if (!it.disabled) {
398
- row.addEventListener("click", (e) => {
399
- e.stopPropagation();
400
- hideContextMenu();
401
- onAction?.(it.action);
402
- });
403
- }
404
- menu.appendChild(row);
405
- }
406
- (modalHost() || document.body).appendChild(menu);
407
- menu.style.display = "block";
408
- _activeMenu = menu;
409
- const rect = menu.getBoundingClientRect();
410
- const left = Math.min(x, window.innerWidth - rect.width - 8);
411
- const top = Math.min(y, window.innerHeight - rect.height - 8);
412
- menu.style.left = `${Math.max(0, left)}px`;
413
- menu.style.top = `${Math.max(0, top)}px`;
414
- setTimeout(() => {
415
- document.addEventListener("mousedown", _outsideHandler, { once: true, capture: true });
416
- }, 0);
417
- document.addEventListener("keydown", _keyHandler);
418
- window.addEventListener("scroll", hideContextMenu, { once: true, capture: true });
419
- _enabledItems(menu)[0]?.focus({ preventScroll: true });
420
- }
421
- function hideContextMenu() {
422
- if (!_activeMenu) return;
423
- const returnTo = _returnFocusTo;
424
- const held = _activeMenu.contains(document.activeElement);
425
- _activeMenu.remove();
426
- _activeMenu = null;
427
- _returnFocusTo = null;
428
- document.removeEventListener("keydown", _keyHandler);
429
- if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });
430
- }
431
- function _enabledItems(menu) {
432
- return [...menu.querySelectorAll(".twm-context-menu-item:not(.disabled)")];
433
- }
434
- function _keyHandler(e) {
435
- if (!_activeMenu) return;
436
- if (e.key === "Escape") {
437
- e.preventDefault();
438
- hideContextMenu();
439
- return;
440
- }
441
- const items = _enabledItems(_activeMenu);
442
- if (items.length === 0) return;
443
- const at = items.indexOf(document.activeElement);
444
- let next = null;
445
- if (e.key === "ArrowDown") next = items[(at + 1 + items.length) % items.length];
446
- else if (e.key === "ArrowUp") next = items[(at - 1 + items.length) % items.length];
447
- else if (e.key === "Home") next = items[0];
448
- else if (e.key === "End") next = items[items.length - 1];
449
- if (!next) return;
450
- e.preventDefault();
451
- next.focus({ preventScroll: true });
452
- }
453
- function _outsideHandler(e) {
454
- if (_activeMenu && !_activeMenu.contains(e.target)) hideContextMenu();
455
- }
456
- function escapeHtml(s) {
457
- return String(s).replace(/[&<>"']/g, (c) => ({
458
- "&": "&amp;",
459
- "<": "&lt;",
460
- ">": "&gt;",
461
- '"': "&quot;",
462
- "'": "&#39;"
463
- })[c]);
464
- }
465
-
466
360
  export {
467
361
  setHelpProvider,
468
362
  helpProvider,
469
363
  helpCategories,
470
364
  helpCopy,
471
- HelpModal,
472
- showContextMenu,
473
- hideContextMenu
365
+ HelpModal
474
366
  };
475
- //# sourceMappingURL=chunk-CWPMLX67.js.map
367
+ //# sourceMappingURL=chunk-W6FBJLPW.js.map