flexdesk 0.1.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/LICENSE +21 -0
- package/README.md +195 -0
- package/THIRD_PARTY_LICENSES.md +53 -0
- package/css/base.css +3439 -0
- package/css/flexdesk.css +5797 -0
- package/css/overrides.css +2349 -0
- package/css/reset.css +46 -0
- package/css/tokens.css +137 -0
- package/dist/charts.js +780 -0
- package/dist/charts.js.map +7 -0
- package/dist/chunk-3PHCPZHT.js +222 -0
- package/dist/chunk-3PHCPZHT.js.map +7 -0
- package/dist/chunk-CT4YXXLP.js +1770 -0
- package/dist/chunk-CT4YXXLP.js.map +7 -0
- package/dist/chunk-DRYCDMEG.js +2217 -0
- package/dist/chunk-DRYCDMEG.js.map +7 -0
- package/dist/chunk-DVU44T77.js +855 -0
- package/dist/chunk-DVU44T77.js.map +7 -0
- package/dist/chunk-FL5KFNQH.js +640 -0
- package/dist/chunk-FL5KFNQH.js.map +7 -0
- package/dist/chunk-FOOS3T5L.js +117 -0
- package/dist/chunk-FOOS3T5L.js.map +7 -0
- package/dist/chunk-JYWURG5T.js +32 -0
- package/dist/chunk-JYWURG5T.js.map +7 -0
- package/dist/chunk-TLZUUFOE.js +276 -0
- package/dist/chunk-TLZUUFOE.js.map +7 -0
- package/dist/chunk-UCJ2WD4D.js +625 -0
- package/dist/chunk-UCJ2WD4D.js.map +7 -0
- package/dist/chunk-WVFGV5FT.js +205 -0
- package/dist/chunk-WVFGV5FT.js.map +7 -0
- package/dist/core.js +1098 -0
- package/dist/core.js.map +7 -0
- package/dist/editor.js +1661 -0
- package/dist/editor.js.map +7 -0
- package/dist/flexdesk.css +5797 -0
- package/dist/host.js +132 -0
- package/dist/host.js.map +7 -0
- package/dist/reset.css +46 -0
- package/dist/tile_registry-6WZPTUZV.js +28 -0
- package/dist/tile_registry-6WZPTUZV.js.map +7 -0
- package/dist/tiles.js +2068 -0
- package/dist/tiles.js.map +7 -0
- package/dist/tokens.css +137 -0
- package/dist/widgets.js +5438 -0
- package/dist/widgets.js.map +7 -0
- package/dist/wm.js +4830 -0
- package/dist/wm.js.map +7 -0
- package/package.json +80 -0
- package/src/charts/chart_types.js +699 -0
- package/src/charts/data_series_plot_window.js +761 -0
- package/src/charts/downsample.js +119 -0
- package/src/charts/plot_config.js +176 -0
- package/src/charts/plot_popout_window.js +1560 -0
- package/src/charts/plotly_wrapper.js +432 -0
- package/src/core/event_bus.js +230 -0
- package/src/core/logging.js +300 -0
- package/src/core/settings.js +813 -0
- package/src/core/state_guard.js +382 -0
- package/src/core/state_guard_config.js +309 -0
- package/src/core/state_machine.js +258 -0
- package/src/editor/editor_pane.js +264 -0
- package/src/editor/monaco_editor_factory.js +449 -0
- package/src/editor/monaco_loader.js +74 -0
- package/src/editor/notebook_search.js +460 -0
- package/src/editor/notebook_tab_bar.js +456 -0
- package/src/editor/notebook_undo_manager.js +108 -0
- package/src/editor/split_pane_container.js +197 -0
- package/src/help/help_modal.js +409 -0
- package/src/help/help_registry.js +65 -0
- package/src/host/host.js +69 -0
- package/src/host/pywebview_host.js +133 -0
- package/src/tiles/config_schema.js +256 -0
- package/src/tiles/layout_persistence.js +662 -0
- package/src/tiles/tile_base.js +785 -0
- package/src/tiles/tile_grid.js +905 -0
- package/src/tiles/tile_registry.js +136 -0
- package/src/tiling/command_palette.js +328 -0
- package/src/tiling/content_registry.js +110 -0
- package/src/tiling/desktops.js +133 -0
- package/src/tiling/entity_sources.js +179 -0
- package/src/tiling/keymap.js +156 -0
- package/src/tiling/kind_taxonomy.js +172 -0
- package/src/tiling/landing_table.js +717 -0
- package/src/tiling/loading_overlay.js +33 -0
- package/src/tiling/nav_panel.js +142 -0
- package/src/tiling/page_factory.js +215 -0
- package/src/tiling/panel_keys.js +145 -0
- package/src/tiling/shell.js +437 -0
- package/src/tiling/tile_breadcrumb.js +222 -0
- package/src/tiling/tile_renderer.js +459 -0
- package/src/tiling/tile_tab_menu.js +340 -0
- package/src/tiling/tile_tree.js +880 -0
- package/src/tiling/wm.js +1352 -0
- package/src/ui/base/component_base.js +30 -0
- package/src/ui/base/controller_base.js +48 -0
- package/src/ui/base/page_base.js +102 -0
- package/src/ui/components/about_dialog.js +224 -0
- package/src/ui/components/action_dropdown.js +389 -0
- package/src/ui/components/attribute_list_editor.js +652 -0
- package/src/ui/components/autocomplete_field.js +732 -0
- package/src/ui/components/computing_status_window.js +81 -0
- package/src/ui/components/confirm_dialog.js +307 -0
- package/src/ui/components/context_menu.js +87 -0
- package/src/ui/components/data_table.js +2228 -0
- package/src/ui/components/detail_header.js +177 -0
- package/src/ui/components/drag_reorder.js +0 -0
- package/src/ui/components/gallery_picker.js +147 -0
- package/src/ui/components/inline_renamer.js +129 -0
- package/src/ui/components/managed_window.js +804 -0
- package/src/ui/components/modal.js +512 -0
- package/src/ui/components/notification_history.js +264 -0
- package/src/ui/components/slide_out_panel.js +185 -0
- package/src/ui/components/slider_field.js +122 -0
- package/src/ui/components/sortable_list.js +309 -0
- package/src/ui/components/table_state_store.js +81 -0
- package/src/ui/components/toast.js +59 -0
- package/src/ui/components/tree_view.js +460 -0
- package/src/ui/components/window_taskbar.js +143 -0
- package/src/ui/controllers/panel_state_machine.js +634 -0
- package/src/ui/controllers/window_chrome_controller.js +659 -0
- package/src/ui/notification_center.js +576 -0
- package/src/ui/utils/overlay_scrollbar.js +492 -0
- package/src/ui/utils/raf_resize_observer.js +31 -0
- package/src/ui/utils/tooltip_service.js +406 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComponentBase (js_new)
|
|
3
|
+
*
|
|
4
|
+
* Ultra-lightweight base for leaf UI widgets rendered inside controllers/panels.
|
|
5
|
+
* Handles props/state updates and deterministic teardown without storing data on
|
|
6
|
+
* DOM elements.
|
|
7
|
+
*/
|
|
8
|
+
export class ComponentBase {
|
|
9
|
+
constructor({ eventBus, logger } = {}) {
|
|
10
|
+
if (new.target === ComponentBase) {
|
|
11
|
+
throw new Error('ComponentBase is abstract – extend it instead');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.eventBus = eventBus;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
this._mounted = false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
mount(/* container, props */) {
|
|
20
|
+
throw new Error('ComponentBase.mount must be implemented by subclasses');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
update(/* props */) {
|
|
24
|
+
// default no-op for stateless components
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
dispose() {
|
|
28
|
+
this._mounted = false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ControllerBase (js_new)
|
|
3
|
+
*
|
|
4
|
+
* Shared superclass for UI controllers (tab, panel, layout, etc.). Provides
|
|
5
|
+
* deterministic teardown, scoped event subscriptions, and dependency injection.
|
|
6
|
+
*/
|
|
7
|
+
export class ControllerBase {
|
|
8
|
+
constructor({ eventBus, dataManager, logger } = {}) {
|
|
9
|
+
if (new.target === ControllerBase) {
|
|
10
|
+
throw new Error('ControllerBase is abstract – extend it instead');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this.eventBus = eventBus;
|
|
14
|
+
this.dataManager = dataManager;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
this._disposers = new Set();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register a disposer callback to guarantee teardown.
|
|
21
|
+
*/
|
|
22
|
+
trackDisposer(disposer) {
|
|
23
|
+
if (typeof disposer === 'function') {
|
|
24
|
+
this._disposers.add(disposer);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Attach controller-specific DOM/event wiring. Subclasses implement this.
|
|
30
|
+
*/
|
|
31
|
+
initialize(/* options */) {
|
|
32
|
+
throw new Error('ControllerBase.initialize must be implemented by subclasses');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Dispose all tracked resources.
|
|
37
|
+
*/
|
|
38
|
+
dispose() {
|
|
39
|
+
for (const disposer of this._disposers) {
|
|
40
|
+
try {
|
|
41
|
+
disposer();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
this.logger?.error?.('ui', 'Controller disposer failed', err);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
this._disposers.clear();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PageBase (js_new)
|
|
3
|
+
*
|
|
4
|
+
* Abstract shell for every top-level UI page (node workspace, simulation, data).
|
|
5
|
+
* Handles dependency injection + shared lifecycle hooks so individual pages only
|
|
6
|
+
* focus on domain logic.
|
|
7
|
+
*
|
|
8
|
+
* ## Async Initialization Pattern
|
|
9
|
+
* Pages with async initialization should:
|
|
10
|
+
* 1. Store the init promise: `this._initPromise = this.init();` in mount()
|
|
11
|
+
* 2. Use `await this.waitForReady()` in show()/hydrate() to wait for init
|
|
12
|
+
* 3. Mark completion by resolving the promise (init() returns)
|
|
13
|
+
*
|
|
14
|
+
* Example:
|
|
15
|
+
* ```javascript
|
|
16
|
+
* mount(container) {
|
|
17
|
+
* this.container = container;
|
|
18
|
+
* this._initPromise = this.init(); // Store promise
|
|
19
|
+
* this._mounted = true;
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* async show() {
|
|
23
|
+
* await this.waitForReady(); // Wait for init if still running
|
|
24
|
+
* this.refresh();
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class PageBase {
|
|
29
|
+
constructor({ eventBus, dataManager, notificationCenter, logger } = {}) {
|
|
30
|
+
if (new.target === PageBase) {
|
|
31
|
+
throw new Error('PageBase is abstract – extend it instead');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.eventBus = eventBus;
|
|
35
|
+
this.dataManager = dataManager;
|
|
36
|
+
this.notificationCenter = notificationCenter;
|
|
37
|
+
this.logger = logger;
|
|
38
|
+
this._mounted = false;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Promise that resolves when async initialization completes.
|
|
42
|
+
* Pages with async init should set this in mount().
|
|
43
|
+
* @type {Promise<void>|null}
|
|
44
|
+
*/
|
|
45
|
+
this._initPromise = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Mount the page into a DOM container. Concrete pages must override this.
|
|
50
|
+
* For pages with async initialization, store the init promise:
|
|
51
|
+
* `this._initPromise = this.init();`
|
|
52
|
+
*/
|
|
53
|
+
mount(/* container */) {
|
|
54
|
+
throw new Error('PageBase.mount must be implemented by subclasses');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Wait for the page to complete initialization.
|
|
59
|
+
* Returns immediately if no async init or if already complete.
|
|
60
|
+
* @returns {Promise<void>}
|
|
61
|
+
*/
|
|
62
|
+
async waitForReady() {
|
|
63
|
+
if (this._initPromise) {
|
|
64
|
+
try {
|
|
65
|
+
await this._initPromise;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
this.logger?.warn?.('page', 'Init promise rejected', err);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Optional hook invoked after the initial mount to hydrate data/state.
|
|
74
|
+
* For pages with async init, this should await waitForReady().
|
|
75
|
+
*/
|
|
76
|
+
async hydrate(/* options */) {
|
|
77
|
+
// default: wait for init to complete
|
|
78
|
+
await this.waitForReady();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Optional hook for when the page becomes active (tab switched, etc.).
|
|
83
|
+
*/
|
|
84
|
+
onActivated(/* context */) {
|
|
85
|
+
// default no-op
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Optional hook for when the page is hidden/unmounted but not disposed.
|
|
90
|
+
*/
|
|
91
|
+
onDeactivated(/* context */) {
|
|
92
|
+
// default no-op
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Dispose resources/event subscriptions.
|
|
97
|
+
*/
|
|
98
|
+
dispose() {
|
|
99
|
+
this._mounted = false;
|
|
100
|
+
this._initPromise = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* About Dialog Component
|
|
3
|
+
*
|
|
4
|
+
* A modal that shows a product's name, version, license, homepage and
|
|
5
|
+
* third-party credits. It knows NOTHING about which product — the caller
|
|
6
|
+
* supplies all of it.
|
|
7
|
+
*
|
|
8
|
+
* It used to hardcode EcoAgent's: PRODUCT_NAME, the GitHub URL, the tagline
|
|
9
|
+
* ("Heterogeneous-agent, stock-flow-consistent macroeconomic simulation") and
|
|
10
|
+
* two dependency lists. A library that ships an About box for somebody else's
|
|
11
|
+
* application is not a library.
|
|
12
|
+
*
|
|
13
|
+
* EcoAgent's values now live in ui/js/ecoagent/about.js and are pushed in.
|
|
14
|
+
*
|
|
15
|
+
* @typedef {{name: string, license: string, url: string}} Dep
|
|
16
|
+
* @typedef {{title: string, deps: Dep[]}} DepSection
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { ManagedWindow } from './managed_window.js';
|
|
20
|
+
|
|
21
|
+
function createExternalLink(text, url, className) {
|
|
22
|
+
const a = document.createElement('a');
|
|
23
|
+
a.className = className;
|
|
24
|
+
a.textContent = text;
|
|
25
|
+
a.href = url;
|
|
26
|
+
a.title = url;
|
|
27
|
+
a.addEventListener('click', (e) => {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
window.open(url, '_blank');
|
|
30
|
+
});
|
|
31
|
+
return a;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildDepSection(title, deps) {
|
|
35
|
+
const section = document.createElement('div');
|
|
36
|
+
section.className = 'twm-about-dialog__section';
|
|
37
|
+
|
|
38
|
+
const heading = document.createElement('div');
|
|
39
|
+
heading.className = 'twm-about-dialog__section-title';
|
|
40
|
+
heading.textContent = title;
|
|
41
|
+
section.appendChild(heading);
|
|
42
|
+
|
|
43
|
+
const list = document.createElement('div');
|
|
44
|
+
list.className = 'twm-about-dialog__dep-list';
|
|
45
|
+
|
|
46
|
+
for (const dep of deps) {
|
|
47
|
+
const row = document.createElement('div');
|
|
48
|
+
row.className = 'twm-about-dialog__dep';
|
|
49
|
+
|
|
50
|
+
const link = createExternalLink(dep.name, dep.url, 'twm-about-dialog__dep-name');
|
|
51
|
+
row.appendChild(link);
|
|
52
|
+
|
|
53
|
+
const lic = document.createElement('span');
|
|
54
|
+
lic.className = 'twm-about-dialog__dep-license';
|
|
55
|
+
lic.textContent = dep.license;
|
|
56
|
+
row.appendChild(lic);
|
|
57
|
+
|
|
58
|
+
list.appendChild(row);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
section.appendChild(list);
|
|
62
|
+
return section;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Show the About dialog for whatever product the caller names.
|
|
67
|
+
*
|
|
68
|
+
* @param {Object} product
|
|
69
|
+
* @param {string} product.name Product name — the title and the heading.
|
|
70
|
+
* @param {string} [product.version]
|
|
71
|
+
* @param {string} [product.tagline]
|
|
72
|
+
* @param {string} [product.license] Shown as "License: <x>"; omitted if absent.
|
|
73
|
+
* @param {string} [product.url] Homepage; omitted if absent.
|
|
74
|
+
* @param {string} [product.urlLabel='Homepage']
|
|
75
|
+
* @param {string} [product.logo='info'] Material symbol shown beside the title.
|
|
76
|
+
* @param {DepSection[]} [product.credits=[]] Third-party credit sections.
|
|
77
|
+
* @returns {Promise<void>} Resolves when the dialog is closed.
|
|
78
|
+
*/
|
|
79
|
+
export function showAboutDialog(product = {}) {
|
|
80
|
+
const {
|
|
81
|
+
name = 'Application',
|
|
82
|
+
version = '',
|
|
83
|
+
tagline = '',
|
|
84
|
+
license = '',
|
|
85
|
+
url = '',
|
|
86
|
+
urlLabel = 'Homepage',
|
|
87
|
+
logo = 'info',
|
|
88
|
+
credits = [],
|
|
89
|
+
} = product;
|
|
90
|
+
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
let resolved = false;
|
|
93
|
+
|
|
94
|
+
const finish = () => {
|
|
95
|
+
if (resolved) return;
|
|
96
|
+
resolved = true;
|
|
97
|
+
dialogWindow.close();
|
|
98
|
+
resolve();
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// --- Content ---
|
|
102
|
+
const contentEl = document.createElement('div');
|
|
103
|
+
contentEl.className = 'twm-about-dialog__content';
|
|
104
|
+
|
|
105
|
+
// Header
|
|
106
|
+
const header = document.createElement('div');
|
|
107
|
+
header.className = 'twm-about-dialog__header';
|
|
108
|
+
|
|
109
|
+
const logoEl = document.createElement('span');
|
|
110
|
+
logoEl.className = 'twm-about-dialog__logo material-symbols-outlined';
|
|
111
|
+
logoEl.textContent = logo;
|
|
112
|
+
header.appendChild(logoEl);
|
|
113
|
+
|
|
114
|
+
const title = document.createElement('h2');
|
|
115
|
+
title.className = 'twm-about-dialog__title';
|
|
116
|
+
title.textContent = name;
|
|
117
|
+
header.appendChild(title);
|
|
118
|
+
|
|
119
|
+
if (version) {
|
|
120
|
+
const versionEl = document.createElement('div');
|
|
121
|
+
versionEl.className = 'twm-about-dialog__version';
|
|
122
|
+
versionEl.textContent = `Version ${version}`;
|
|
123
|
+
header.appendChild(versionEl);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (tagline) {
|
|
127
|
+
const taglineEl = document.createElement('div');
|
|
128
|
+
taglineEl.className = 'twm-about-dialog__tagline';
|
|
129
|
+
taglineEl.textContent = tagline;
|
|
130
|
+
header.appendChild(taglineEl);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
contentEl.appendChild(header);
|
|
134
|
+
|
|
135
|
+
// Body (scrollable)
|
|
136
|
+
const body = document.createElement('div');
|
|
137
|
+
body.className = 'twm-about-dialog__body';
|
|
138
|
+
|
|
139
|
+
// Project section — only if there is something to put in it.
|
|
140
|
+
if (url || license) {
|
|
141
|
+
const projectSection = document.createElement('div');
|
|
142
|
+
projectSection.className = 'twm-about-dialog__section';
|
|
143
|
+
|
|
144
|
+
const projectTitle = document.createElement('div');
|
|
145
|
+
projectTitle.className = 'twm-about-dialog__section-title';
|
|
146
|
+
projectTitle.textContent = 'Project';
|
|
147
|
+
projectSection.appendChild(projectTitle);
|
|
148
|
+
|
|
149
|
+
const links = document.createElement('div');
|
|
150
|
+
links.className = 'twm-about-dialog__links';
|
|
151
|
+
|
|
152
|
+
if (url) {
|
|
153
|
+
const link = createExternalLink(urlLabel, url, 'twm-about-dialog__link');
|
|
154
|
+
const icon = document.createElement('span');
|
|
155
|
+
icon.className = 'material-symbols-outlined';
|
|
156
|
+
icon.textContent = 'open_in_new';
|
|
157
|
+
link.prepend(icon);
|
|
158
|
+
links.appendChild(link);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (license) {
|
|
162
|
+
const licenseSpan = document.createElement('span');
|
|
163
|
+
licenseSpan.className = 'twm-about-dialog__license-text';
|
|
164
|
+
const licIcon = document.createElement('span');
|
|
165
|
+
licIcon.className = 'material-symbols-outlined';
|
|
166
|
+
licIcon.textContent = 'license';
|
|
167
|
+
licenseSpan.appendChild(licIcon);
|
|
168
|
+
licenseSpan.appendChild(document.createTextNode(`License: ${license}`));
|
|
169
|
+
links.appendChild(licenseSpan);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
projectSection.appendChild(links);
|
|
173
|
+
body.appendChild(projectSection);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const section of credits) {
|
|
177
|
+
body.appendChild(buildDepSection(section.title, section.deps ?? []));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
contentEl.appendChild(body);
|
|
181
|
+
|
|
182
|
+
// Footer
|
|
183
|
+
const footer = document.createElement('div');
|
|
184
|
+
footer.className = 'twm-about-dialog__footer';
|
|
185
|
+
|
|
186
|
+
const closeBtn = document.createElement('button');
|
|
187
|
+
closeBtn.type = 'button';
|
|
188
|
+
closeBtn.className = 'twm-about-dialog__btn twm-about-dialog__btn--close';
|
|
189
|
+
closeBtn.textContent = 'Close';
|
|
190
|
+
closeBtn.addEventListener('click', finish);
|
|
191
|
+
footer.appendChild(closeBtn);
|
|
192
|
+
|
|
193
|
+
contentEl.appendChild(footer);
|
|
194
|
+
|
|
195
|
+
// --- Window ---
|
|
196
|
+
const dialogWindow = new ManagedWindow({
|
|
197
|
+
id: 'about-dialog',
|
|
198
|
+
title: `About ${name}`,
|
|
199
|
+
icon: 'info',
|
|
200
|
+
content: contentEl,
|
|
201
|
+
minWidth: 420,
|
|
202
|
+
minHeight: 400,
|
|
203
|
+
defaultWidth: 460,
|
|
204
|
+
defaultHeight: 540,
|
|
205
|
+
canMinimize: false,
|
|
206
|
+
canMaximize: false,
|
|
207
|
+
canResize: false,
|
|
208
|
+
canDrag: false,
|
|
209
|
+
modal: true,
|
|
210
|
+
onClose: () => {
|
|
211
|
+
if (!resolved) {
|
|
212
|
+
resolved = true;
|
|
213
|
+
resolve();
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
dialogWindow.show();
|
|
219
|
+
|
|
220
|
+
requestAnimationFrame(() => {
|
|
221
|
+
closeBtn.focus();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|