@thatopen/services 0.0.6 → 0.1.3
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/README.md +46 -46
- package/dist/built-in/index.d.ts +723 -0
- package/dist/cli.js +22 -0
- package/dist/core/client.d.ts +10 -1
- package/dist/index.cjs.js +2 -2
- package/dist/index.es.js +87 -71
- package/dist/types/files.d.ts +6 -0
- package/dist/vite-env.d.ts +1 -1
- package/docs/builtin/paths.json +22 -6
- package/package.json +99 -99
- package/resources/AGENTS.md +5 -3
- package/src/built-in/AppManager/example.ts +86 -86
- package/src/built-in/CDEManager/example.ts +214 -214
- package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
- package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
- package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
- package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
- package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
- package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
- package/src/built-in/FileList/example.ts +107 -107
- package/src/built-in/TabbedNavigation/example.ts +115 -115
- package/src/built-in/UIManager/src/app/example.ts +99 -0
- package/src/built-in/UIManager/src/app/examples/AppState/example.ts +142 -0
- package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +125 -0
- package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +92 -0
- package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +161 -0
- package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +104 -0
- package/src/built-in/ViewportsManager/example.ts +106 -106
- package/src/built-in/index.ts +755 -755
- package/src/cli/templates/app/README.md +241 -241
- package/src/cli/templates/app/index.html +27 -27
- package/src/cli/templates/app/package.json +27 -25
- package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +87 -91
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
- package/src/cli/templates/app/src/bim-components/index.ts +1 -1
- package/src/cli/templates/app/src/main.ts +56 -89
- package/src/cli/templates/app/src/setups/cloud-runner.ts +9 -13
- package/src/cli/templates/app/src/setups/fragments.ts +18 -0
- package/src/cli/templates/app/src/setups/ifc-loader.ts +12 -0
- package/src/cli/templates/app/src/setups/index.ts +3 -3
- package/src/cli/templates/app/src/ui-components/AppPanel/index.ts +160 -0
- package/src/cli/templates/app/src/ui-components/index.ts +1 -2
- package/src/cli/templates/app/tsconfig.json +17 -16
- package/src/cli/templates/cloud-component/README.md +205 -205
- package/src/cli/templates/cloud-component/_thatopen +5 -5
- package/src/cli/templates/cloud-component/declarations.json +4 -4
- package/src/cli/templates/cloud-component/package.json +21 -21
- package/src/cli/templates/cloud-component/src/main.ts +70 -70
- package/src/cli/templates/cloud-component/tsconfig.json +16 -16
- package/src/cli/templates/shared/CLAUDE.md +1 -1
- package/src/cli/templates/shared/_gitignore +4 -4
- package/src/cli/templates/app/src/app.ts +0 -16
- package/src/cli/templates/app/src/globals.ts +0 -1
- package/src/cli/templates/app/src/setups/ui-manager.ts +0 -27
- package/src/cli/templates/app/src/setups/viewports-manager.ts +0 -22
- package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +0 -26
- package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +0 -1
- package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +0 -15
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +0 -37
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +0 -1
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +0 -14
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* MD
|
|
2
|
+
## Floating layout
|
|
3
|
+
---
|
|
4
|
+
Developers building BIM coordination tools often want the 3D model to fill the
|
|
5
|
+
entire window while control panels float above it — but standard grid layouts
|
|
6
|
+
give every area equal standing, shrinking the viewer into one cell and leaving
|
|
7
|
+
visible seams between it and the panels.
|
|
8
|
+
|
|
9
|
+
top-app supports a floating mode where one element is mounted as an absolute
|
|
10
|
+
background that fills the container edge-to-edge, and all other layout areas
|
|
11
|
+
render as a transparent overlay on top of it with independent pointer events.
|
|
12
|
+
|
|
13
|
+
This tutorial covers assigning the viewer as the absolute background of the app;
|
|
14
|
+
defining an overlay layout where a side panel floats above the viewer without
|
|
15
|
+
sharing grid space; and annotating why the base element is excluded from the
|
|
16
|
+
layout template.
|
|
17
|
+
|
|
18
|
+
By the end, you'll have a full-screen 3D viewer with a floating side panel
|
|
19
|
+
hovering above it, with the model filling the entire window and the panel
|
|
20
|
+
receiving its own pointer events.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { html } from "lit";
|
|
24
|
+
import * as THREE from "three";
|
|
25
|
+
import * as OBC from "@thatopen/components";
|
|
26
|
+
import * as OBF from "@thatopen/components-front";
|
|
27
|
+
import * as FRAGS from "@thatopen/fragments";
|
|
28
|
+
import * as BUI from "@thatopen/ui";
|
|
29
|
+
import { PlatformClient } from "thatopen-services";
|
|
30
|
+
import uiManagerDef from "../../../../index";
|
|
31
|
+
import type { App } from "../../index";
|
|
32
|
+
|
|
33
|
+
const UIManager = uiManagerDef.componentDefinition;
|
|
34
|
+
const client = PlatformClient.fromPlatformContext();
|
|
35
|
+
const { components } = (await client.setup(
|
|
36
|
+
{ OBC, OBF, BUI, THREE, FRAGS },
|
|
37
|
+
{ uuid: UIManager.uuid },
|
|
38
|
+
)) as { components: OBC.Components };
|
|
39
|
+
components.get(UIManager).init();
|
|
40
|
+
|
|
41
|
+
const app = document.createElement("top-app") as unknown as App;
|
|
42
|
+
|
|
43
|
+
app.setup = (waitUntil) => {
|
|
44
|
+
waitUntil(
|
|
45
|
+
(async () => {
|
|
46
|
+
const fragments = components.get(OBC.FragmentsManager);
|
|
47
|
+
const workerUrl = await FRAGS.FragmentsModels.getWorker();
|
|
48
|
+
fragments.init(await FRAGS.toClassicWorker(workerUrl), {
|
|
49
|
+
classicWorker: true,
|
|
50
|
+
});
|
|
51
|
+
})(),
|
|
52
|
+
"Fragments Core",
|
|
53
|
+
);
|
|
54
|
+
return { components, client };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
app.elements = {
|
|
58
|
+
// "viewer" is referenced by app.base — rendered as a position:absolute layer
|
|
59
|
+
// behind all layout content so it fills the container edge-to-edge.
|
|
60
|
+
viewer: () =>
|
|
61
|
+
html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
|
|
62
|
+
|
|
63
|
+
// Panels declared here appear in the floating content grid on top of the viewer.
|
|
64
|
+
// Their container has pointer-events: none; each child re-enables them individually.
|
|
65
|
+
panel: () => html`
|
|
66
|
+
<bim-panel>
|
|
67
|
+
<bim-panel-section label="Models" icon="solar:layers-bold">
|
|
68
|
+
<bim-label>Load models here</bim-label>
|
|
69
|
+
</bim-panel-section>
|
|
70
|
+
</bim-panel>
|
|
71
|
+
`,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Setting app.base to an element key mounts that element as the absolute background.
|
|
75
|
+
// Layout templates then only list the floating overlay areas — not the base element.
|
|
76
|
+
// Without app.base, every area in the template is a peer grid cell (inline mode).
|
|
77
|
+
app.base = "viewer";
|
|
78
|
+
|
|
79
|
+
app.layouts = {
|
|
80
|
+
main: {
|
|
81
|
+
label: "Main",
|
|
82
|
+
icon: "solar:home-bold",
|
|
83
|
+
// Only overlay areas appear here. "viewer" is not listed because it is the base.
|
|
84
|
+
template: `"panel" 1fr / 22rem`,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
app.layout = "main";
|
|
89
|
+
|
|
90
|
+
const container = document.getElementById("that-open-app") ?? document.body;
|
|
91
|
+
container.appendChild(app);
|
|
92
|
+
document.body.style.margin = "0";
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/* MD
|
|
2
|
+
## Toasts, file drop, and cross-component notifications
|
|
3
|
+
---
|
|
4
|
+
Developers building BIM processing UIs need to surface status updates, errors,
|
|
5
|
+
and file-drop feedback to users from different points in the component tree —
|
|
6
|
+
but reaching back to the app root to trigger a notification from a deeply nested
|
|
7
|
+
component requires threading a reference through every layer in between.
|
|
8
|
+
|
|
9
|
+
top-app provides a built-in toast system that any descendant can trigger by
|
|
10
|
+
dispatching a standard DOM event, a drag-and-drop overlay that activates
|
|
11
|
+
automatically when files enter the window, and a drop-zone message that can be
|
|
12
|
+
computed from live state at render time rather than set once at initialization.
|
|
13
|
+
|
|
14
|
+
This tutorial covers showing a success toast when the app finishes booting;
|
|
15
|
+
building a component that dispatches notification events to report async progress
|
|
16
|
+
and completion without a reference to the root; handling dropped files, filtering
|
|
17
|
+
them by extension, and showing a warning when none are supported; and setting the
|
|
18
|
+
drop-zone message as a function that reads a live model counter.
|
|
19
|
+
|
|
20
|
+
By the end, you'll have an app with a full notification layer — a boot
|
|
21
|
+
confirmation toast, async status reporting from a child component, and a
|
|
22
|
+
drag-and-drop zone whose message updates as models are loaded.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { html, LitElement } from "lit";
|
|
26
|
+
import { customElement, state } from "lit/decorators.js";
|
|
27
|
+
import * as THREE from "three";
|
|
28
|
+
import * as OBC from "@thatopen/components";
|
|
29
|
+
import * as OBF from "@thatopen/components-front";
|
|
30
|
+
import * as FRAGS from "@thatopen/fragments";
|
|
31
|
+
import * as BUI from "@thatopen/ui";
|
|
32
|
+
import { PlatformClient } from "thatopen-services";
|
|
33
|
+
import uiManagerDef from "../../../../index";
|
|
34
|
+
import type { App, AppReadyEvent } from "../../index";
|
|
35
|
+
|
|
36
|
+
// A child component that dispatches top:notification to surface messages
|
|
37
|
+
// to the nearest top-app ancestor — no reference to the app element needed.
|
|
38
|
+
@customElement("status-reporter")
|
|
39
|
+
class StatusReporter extends LitElement {
|
|
40
|
+
@state() private _busy = false;
|
|
41
|
+
|
|
42
|
+
private _simulateWork() {
|
|
43
|
+
this._busy = true;
|
|
44
|
+
|
|
45
|
+
// Dispatching top:notification from any descendant of top-app shows a toast.
|
|
46
|
+
// bubbles:true + composed:true lets it cross shadow-DOM boundaries.
|
|
47
|
+
this.dispatchEvent(
|
|
48
|
+
new CustomEvent("top:notification", {
|
|
49
|
+
bubbles: true,
|
|
50
|
+
composed: true,
|
|
51
|
+
detail: { message: "Analysis started", type: "info" },
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
this._busy = false;
|
|
57
|
+
this.dispatchEvent(
|
|
58
|
+
new CustomEvent("top:notification", {
|
|
59
|
+
bubbles: true,
|
|
60
|
+
composed: true,
|
|
61
|
+
detail: {
|
|
62
|
+
message: "Analysis complete",
|
|
63
|
+
type: "success",
|
|
64
|
+
description: "Found 342 elements across 5 categories.",
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
}, 2000);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
render() {
|
|
72
|
+
return html`
|
|
73
|
+
<bim-panel label="Reporter">
|
|
74
|
+
<bim-panel-section label="Actions" icon="solar:play-bold">
|
|
75
|
+
<bim-button
|
|
76
|
+
label="Run Analysis"
|
|
77
|
+
icon="solar:play-bold"
|
|
78
|
+
.loading=${this._busy}
|
|
79
|
+
@click=${this._simulateWork}
|
|
80
|
+
></bim-button>
|
|
81
|
+
</bim-panel-section>
|
|
82
|
+
</bim-panel>
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ---- app wiring ----
|
|
88
|
+
|
|
89
|
+
const UIManager = uiManagerDef.componentDefinition;
|
|
90
|
+
const client = PlatformClient.fromPlatformContext();
|
|
91
|
+
const { components } = (await client.setup(
|
|
92
|
+
{ OBC, OBF, BUI, THREE, FRAGS },
|
|
93
|
+
{ uuid: UIManager.uuid },
|
|
94
|
+
)) as { components: OBC.Components };
|
|
95
|
+
components.get(UIManager).init();
|
|
96
|
+
|
|
97
|
+
const app = document.createElement("top-app") as unknown as App;
|
|
98
|
+
|
|
99
|
+
app.setup = (waitUntil) => {
|
|
100
|
+
waitUntil(
|
|
101
|
+
(async () => {
|
|
102
|
+
const fragments = components.get(OBC.FragmentsManager);
|
|
103
|
+
const workerUrl = await FRAGS.FragmentsModels.getWorker();
|
|
104
|
+
fragments.init(await FRAGS.toClassicWorker(workerUrl), {
|
|
105
|
+
classicWorker: true,
|
|
106
|
+
});
|
|
107
|
+
})(),
|
|
108
|
+
"Fragments Core",
|
|
109
|
+
);
|
|
110
|
+
return { components, client };
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
app.elements = {
|
|
114
|
+
viewer: () =>
|
|
115
|
+
html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
|
|
116
|
+
panel: () => html`<status-reporter></status-reporter>`,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
app.base = "viewer";
|
|
120
|
+
app.layouts = {
|
|
121
|
+
main: {
|
|
122
|
+
label: "Main",
|
|
123
|
+
icon: "solar:home-bold",
|
|
124
|
+
template: `"panel" 1fr / 22rem`,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
app.layout = "main";
|
|
128
|
+
|
|
129
|
+
// dropMessage as a function is called at render time so it can read live state.
|
|
130
|
+
// Here it reads how many models are already loaded and adjusts the hint text.
|
|
131
|
+
let loadedCount = 0;
|
|
132
|
+
app.dropMessage = () =>
|
|
133
|
+
loadedCount === 0
|
|
134
|
+
? "Drop an IFC or FRAG file to load your first model"
|
|
135
|
+
: `Drop a file to add to the ${loadedCount} loaded model${loadedCount === 1 ? "" : "s"}`;
|
|
136
|
+
|
|
137
|
+
// top:files-dropped fires when the user drops files onto the window.
|
|
138
|
+
// e.detail.files is a File[]. Handle upload, load, or validation here.
|
|
139
|
+
app.addEventListener("top:files-dropped", (e) => {
|
|
140
|
+
const { files } = (e as CustomEvent<{ files: File[] }>).detail;
|
|
141
|
+
const supported = files.filter((f) =>
|
|
142
|
+
f.name.endsWith(".ifc") || f.name.endsWith(".frag"),
|
|
143
|
+
);
|
|
144
|
+
if (!supported.length) {
|
|
145
|
+
app.showToast("No supported files dropped", "warning");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
app.showToast(`Loading ${supported.length} file${supported.length === 1 ? "" : "s"}…`, "info");
|
|
149
|
+
loadedCount += supported.length;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// thatopen:app-ready fires after all waitUntil promises resolve.
|
|
153
|
+
// e.detail.app is a reference to the top-app element itself.
|
|
154
|
+
app.addEventListener("thatopen:app-ready", (e) => {
|
|
155
|
+
const { app: readyApp } = (e as AppReadyEvent).detail;
|
|
156
|
+
readyApp.showToast("App ready", "success");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const container = document.getElementById("that-open-app") ?? document.body;
|
|
160
|
+
container.appendChild(app);
|
|
161
|
+
document.body.style.margin = "0";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* MD
|
|
2
|
+
## Sidebar navigation
|
|
3
|
+
---
|
|
4
|
+
Product teams building BIM apps often need multiple distinct views — a 3D model
|
|
5
|
+
viewer, a data browser, a settings screen — but wiring a custom nav bar and
|
|
6
|
+
managing view transitions from scratch adds complexity before any real feature
|
|
7
|
+
work begins.
|
|
8
|
+
|
|
9
|
+
top-app supports named layouts with a built-in sidebar navigation bar that
|
|
10
|
+
switches the active grid template on click, so teams get multi-view navigation
|
|
11
|
+
without building the scaffolding themselves.
|
|
12
|
+
|
|
13
|
+
This tutorial covers defining three named layouts with their own grid templates
|
|
14
|
+
and icons; providing a dedicated element for each view; enabling the sidebar nav
|
|
15
|
+
bar; and setting the initial active layout.
|
|
16
|
+
|
|
17
|
+
By the end, you'll have a multi-view BIM app with a persistent sidebar that lets
|
|
18
|
+
users switch between a 3D viewer, a data panel, and a settings screen.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { html } from "lit";
|
|
22
|
+
import * as THREE from "three";
|
|
23
|
+
import * as OBC from "@thatopen/components";
|
|
24
|
+
import * as OBF from "@thatopen/components-front";
|
|
25
|
+
import * as FRAGS from "@thatopen/fragments";
|
|
26
|
+
import * as BUI from "@thatopen/ui";
|
|
27
|
+
import { PlatformClient } from "thatopen-services";
|
|
28
|
+
import uiManagerDef from "../../../../index";
|
|
29
|
+
import type { App } from "../../index";
|
|
30
|
+
|
|
31
|
+
const UIManager = uiManagerDef.componentDefinition;
|
|
32
|
+
const client = PlatformClient.fromPlatformContext();
|
|
33
|
+
const { components } = (await client.setup(
|
|
34
|
+
{ OBC, OBF, BUI, THREE, FRAGS },
|
|
35
|
+
{ uuid: UIManager.uuid },
|
|
36
|
+
)) as { components: OBC.Components };
|
|
37
|
+
components.get(UIManager).init();
|
|
38
|
+
|
|
39
|
+
const app = document.createElement("top-app") as unknown as App;
|
|
40
|
+
|
|
41
|
+
app.setup = (waitUntil) => {
|
|
42
|
+
waitUntil(
|
|
43
|
+
(async () => {
|
|
44
|
+
const fragments = components.get(OBC.FragmentsManager);
|
|
45
|
+
const workerUrl = await FRAGS.FragmentsModels.getWorker();
|
|
46
|
+
fragments.init(await FRAGS.toClassicWorker(workerUrl), {
|
|
47
|
+
classicWorker: true,
|
|
48
|
+
});
|
|
49
|
+
})(),
|
|
50
|
+
"Fragments Core",
|
|
51
|
+
);
|
|
52
|
+
return { components, client };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
app.elements = {
|
|
56
|
+
viewer: () =>
|
|
57
|
+
html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
|
|
58
|
+
|
|
59
|
+
data: () => html`
|
|
60
|
+
<bim-panel label="Data">
|
|
61
|
+
<bim-panel-section label="Properties" icon="solar:document-bold">
|
|
62
|
+
<bim-label>Select an element in the viewer to inspect its properties.</bim-label>
|
|
63
|
+
</bim-panel-section>
|
|
64
|
+
</bim-panel>
|
|
65
|
+
`,
|
|
66
|
+
|
|
67
|
+
settings: () => html`
|
|
68
|
+
<bim-panel label="Settings">
|
|
69
|
+
<bim-panel-section label="Appearance" icon="solar:settings-bold">
|
|
70
|
+
<bim-checkbox label="Show grid" checked></bim-checkbox>
|
|
71
|
+
<bim-checkbox label="Show axes"></bim-checkbox>
|
|
72
|
+
</bim-panel-section>
|
|
73
|
+
</bim-panel>
|
|
74
|
+
`,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
app.layouts = {
|
|
78
|
+
// Each layout defines its own CSS grid template. Only areas used in a template
|
|
79
|
+
// are rendered for that layout — unused elements are not created.
|
|
80
|
+
viewer: {
|
|
81
|
+
label: "Viewer",
|
|
82
|
+
icon: "solar:3d-square-bold",
|
|
83
|
+
template: `"viewer" 1fr / 1fr`,
|
|
84
|
+
},
|
|
85
|
+
data: {
|
|
86
|
+
label: "Data",
|
|
87
|
+
icon: "solar:document-bold",
|
|
88
|
+
template: `"data" 1fr / 1fr`,
|
|
89
|
+
},
|
|
90
|
+
settings: {
|
|
91
|
+
label: "Settings",
|
|
92
|
+
icon: "solar:settings-bold",
|
|
93
|
+
template: `"settings" 1fr / 1fr`,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// sidebar=true adds a vertical nav column on the left with one button per layout.
|
|
98
|
+
// Each button shows the layout's label and icon and activates that layout on click.
|
|
99
|
+
app.sidebar = true;
|
|
100
|
+
app.layout = "viewer";
|
|
101
|
+
|
|
102
|
+
const container = document.getElementById("that-open-app") ?? document.body;
|
|
103
|
+
container.appendChild(app);
|
|
104
|
+
document.body.style.margin = "0";
|
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
import * as THREE from "three";
|
|
2
|
-
import * as OBC from "@thatopen/components";
|
|
3
|
-
import * as BUI from "@thatopen/ui";
|
|
4
|
-
|
|
5
|
-
import appManagerDef from "../AppManager/index";
|
|
6
|
-
import viewportManagerDef from "./index";
|
|
7
|
-
|
|
8
|
-
const AppManager = appManagerDef.componentDefinition;
|
|
9
|
-
const ViewportManager = viewportManagerDef.componentDefinition;
|
|
10
|
-
|
|
11
|
-
// ── App type ──────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
type PanelState = { components: OBC.Components };
|
|
14
|
-
|
|
15
|
-
interface ViewportApp {
|
|
16
|
-
icons: ["LOAD", "VIEW", "SPLIT"];
|
|
17
|
-
grid: BUI.Grid<
|
|
18
|
-
["Viewer", "Split"],
|
|
19
|
-
[
|
|
20
|
-
{ name: "viewer"; state: null },
|
|
21
|
-
{ name: "panel"; state: PanelState }
|
|
22
|
-
]
|
|
23
|
-
>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ── Initialise OBC & BUI ──────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
BUI.Manager.init();
|
|
29
|
-
const components = new OBC.Components();
|
|
30
|
-
|
|
31
|
-
const app = components.get(AppManager<ViewportApp>);
|
|
32
|
-
const viewports = components.get(ViewportManager);
|
|
33
|
-
|
|
34
|
-
// ── Create a viewport ─────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
const { element: viewerElement, world } = await viewports.create({
|
|
37
|
-
backgroundColor: "#202932",
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// ── Add a simple cube to the scene ────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
const geometry = new THREE.BoxGeometry(3, 3, 3);
|
|
43
|
-
const material = new THREE.MeshStandardMaterial({ color: "#6528D7" });
|
|
44
|
-
const cube = new THREE.Mesh(geometry, material);
|
|
45
|
-
cube.position.set(0, 1.5, 0);
|
|
46
|
-
world.scene.three.add(cube);
|
|
47
|
-
|
|
48
|
-
await world.camera.controls.setLookAt(10, 8, 10, 0, 1.5, 0);
|
|
49
|
-
|
|
50
|
-
// Load a sample model
|
|
51
|
-
const loadModel = async () => {
|
|
52
|
-
const fragments = components.get(OBC.FragmentsManager);
|
|
53
|
-
const file = await fetch(
|
|
54
|
-
"https://thatopen.github.io/engine_components/resources/frags/school_arq.frag",
|
|
55
|
-
);
|
|
56
|
-
const buffer = await file.arrayBuffer();
|
|
57
|
-
await fragments.core.load(buffer, { modelId: "school_arq" });
|
|
58
|
-
await fragments.core.update(true);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// ── Templates ─────────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
const panelTemplate: BUI.StatefullComponent<PanelState> = (_state) => {
|
|
64
|
-
return BUI.html`
|
|
65
|
-
<bim-panel active label="App Info">
|
|
66
|
-
<bim-panel-section label="Actions">
|
|
67
|
-
<bim-button @click=${loadModel} label="Load School Model"></bim-button>
|
|
68
|
-
</bim-panel-section>
|
|
69
|
-
<bim-panel-section label="Info">
|
|
70
|
-
<bim-label>Use the sidebar to switch layouts.</bim-label>
|
|
71
|
-
</bim-panel-section>
|
|
72
|
-
</bim-panel>
|
|
73
|
-
`;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// ── Init ──────────────────────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
app.init({
|
|
79
|
-
icons: {
|
|
80
|
-
LOAD: "solar:upload-bold",
|
|
81
|
-
VIEW: "mdi:monitor",
|
|
82
|
-
SPLIT: "solar:settings-bold",
|
|
83
|
-
},
|
|
84
|
-
grid: (grid) => {
|
|
85
|
-
grid.elements = {
|
|
86
|
-
viewer: viewerElement,
|
|
87
|
-
panel: { template: panelTemplate, initialState: { components } },
|
|
88
|
-
};
|
|
89
|
-
grid.layouts = {
|
|
90
|
-
Viewer: {
|
|
91
|
-
template: `"viewer" 1fr / 1fr`,
|
|
92
|
-
icon: app.icons.VIEW,
|
|
93
|
-
},
|
|
94
|
-
Split: {
|
|
95
|
-
template: `"panel viewer" 1fr / 20rem 1fr`,
|
|
96
|
-
icon: app.icons.SPLIT,
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
grid.layout = "Viewer";
|
|
100
|
-
},
|
|
101
|
-
container: document.getElementById("container")!,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
app.showSidebar = true;
|
|
105
|
-
|
|
106
|
-
components.init();
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import * as OBC from "@thatopen/components";
|
|
3
|
+
import * as BUI from "@thatopen/ui";
|
|
4
|
+
|
|
5
|
+
import appManagerDef from "../AppManager/index";
|
|
6
|
+
import viewportManagerDef from "./index";
|
|
7
|
+
|
|
8
|
+
const AppManager = appManagerDef.componentDefinition;
|
|
9
|
+
const ViewportManager = viewportManagerDef.componentDefinition;
|
|
10
|
+
|
|
11
|
+
// ── App type ──────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
type PanelState = { components: OBC.Components };
|
|
14
|
+
|
|
15
|
+
interface ViewportApp {
|
|
16
|
+
icons: ["LOAD", "VIEW", "SPLIT"];
|
|
17
|
+
grid: BUI.Grid<
|
|
18
|
+
["Viewer", "Split"],
|
|
19
|
+
[
|
|
20
|
+
{ name: "viewer"; state: null },
|
|
21
|
+
{ name: "panel"; state: PanelState }
|
|
22
|
+
]
|
|
23
|
+
>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Initialise OBC & BUI ──────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
BUI.Manager.init();
|
|
29
|
+
const components = new OBC.Components();
|
|
30
|
+
|
|
31
|
+
const app = components.get(AppManager<ViewportApp>);
|
|
32
|
+
const viewports = components.get(ViewportManager);
|
|
33
|
+
|
|
34
|
+
// ── Create a viewport ─────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
const { element: viewerElement, world } = await viewports.create({
|
|
37
|
+
backgroundColor: "#202932",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ── Add a simple cube to the scene ────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
const geometry = new THREE.BoxGeometry(3, 3, 3);
|
|
43
|
+
const material = new THREE.MeshStandardMaterial({ color: "#6528D7" });
|
|
44
|
+
const cube = new THREE.Mesh(geometry, material);
|
|
45
|
+
cube.position.set(0, 1.5, 0);
|
|
46
|
+
world.scene.three.add(cube);
|
|
47
|
+
|
|
48
|
+
await world.camera.controls.setLookAt(10, 8, 10, 0, 1.5, 0);
|
|
49
|
+
|
|
50
|
+
// Load a sample model
|
|
51
|
+
const loadModel = async () => {
|
|
52
|
+
const fragments = components.get(OBC.FragmentsManager);
|
|
53
|
+
const file = await fetch(
|
|
54
|
+
"https://thatopen.github.io/engine_components/resources/frags/school_arq.frag",
|
|
55
|
+
);
|
|
56
|
+
const buffer = await file.arrayBuffer();
|
|
57
|
+
await fragments.core.load(buffer, { modelId: "school_arq" });
|
|
58
|
+
await fragments.core.update(true);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// ── Templates ─────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
const panelTemplate: BUI.StatefullComponent<PanelState> = (_state) => {
|
|
64
|
+
return BUI.html`
|
|
65
|
+
<bim-panel active label="App Info">
|
|
66
|
+
<bim-panel-section label="Actions">
|
|
67
|
+
<bim-button @click=${loadModel} label="Load School Model"></bim-button>
|
|
68
|
+
</bim-panel-section>
|
|
69
|
+
<bim-panel-section label="Info">
|
|
70
|
+
<bim-label>Use the sidebar to switch layouts.</bim-label>
|
|
71
|
+
</bim-panel-section>
|
|
72
|
+
</bim-panel>
|
|
73
|
+
`;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ── Init ──────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
app.init({
|
|
79
|
+
icons: {
|
|
80
|
+
LOAD: "solar:upload-bold",
|
|
81
|
+
VIEW: "mdi:monitor",
|
|
82
|
+
SPLIT: "solar:settings-bold",
|
|
83
|
+
},
|
|
84
|
+
grid: (grid) => {
|
|
85
|
+
grid.elements = {
|
|
86
|
+
viewer: viewerElement,
|
|
87
|
+
panel: { template: panelTemplate, initialState: { components } },
|
|
88
|
+
};
|
|
89
|
+
grid.layouts = {
|
|
90
|
+
Viewer: {
|
|
91
|
+
template: `"viewer" 1fr / 1fr`,
|
|
92
|
+
icon: app.icons.VIEW,
|
|
93
|
+
},
|
|
94
|
+
Split: {
|
|
95
|
+
template: `"panel viewer" 1fr / 20rem 1fr`,
|
|
96
|
+
icon: app.icons.SPLIT,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
grid.layout = "Viewer";
|
|
100
|
+
},
|
|
101
|
+
container: document.getElementById("container")!,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
app.showSidebar = true;
|
|
105
|
+
|
|
106
|
+
components.init();
|