@thatopen/services 0.0.2 → 0.0.4
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/AGENTS.md +86 -76
- package/dist/cli.js +10 -1
- package/docs/access-backend-data.md +19 -19
- package/docs/app-architecture.md +94 -94
- package/docs/app-layout.md +139 -139
- package/docs/app-wiring.md +123 -123
- package/docs/bim-components/coding-conventions.md +128 -128
- package/docs/bim-components/element-collections.md +69 -69
- package/docs/bim-components/expose-events.md +84 -84
- package/docs/bim-components/library-examples.md +28 -28
- package/docs/bim-components/observable-collections.md +83 -83
- package/docs/bim-components/overview.md +160 -160
- package/docs/bim-components/per-frame-updates.md +20 -20
- package/docs/bim-components/save-and-restore-state.md +21 -21
- package/docs/bim-components/setup-and-cleanup.md +64 -64
- package/docs/bim-components/type-conventions.md +49 -49
- package/docs/bim-components/user-driven-object-creation.md +56 -56
- package/docs/cli-setup.md +54 -54
- package/docs/cloud-components.md +74 -74
- package/docs/connect-logic-to-ui.md +113 -113
- package/docs/previewing.md +45 -45
- package/docs/publishing.md +35 -35
- package/docs/scaffolding.md +71 -54
- package/docs/ui-components/async-actions.md +18 -18
- package/docs/ui-components/confirmation-dialog.md +39 -39
- package/docs/ui-components/data-table.md +185 -185
- package/docs/ui-components/display-text.md +39 -39
- package/docs/ui-components/inline-form.md +44 -44
- package/docs/ui-components/library-examples.md +24 -24
- package/docs/ui-components/overview.md +194 -194
- package/docs/ui-components/rendering-patterns.md +129 -129
- package/docs/ui-components/sections-layout.md +123 -123
- package/docs/update-grid-elements.md +46 -46
- package/docs/using-colors.md +32 -32
- package/docs/using-icons.md +37 -37
- package/package.json +2 -2
- package/src/cli/templates/bim/CONTEXT.md +1 -4
- package/src/cli/templates/bim/package.json +0 -1
- package/src/cli/templates/bim/src/main.ts +1 -2
- package/src/cli/templates/bim-beta/CONTEXT.md +241 -0
- package/src/cli/templates/bim-beta/package.json +25 -0
- package/src/cli/templates/bim-beta/src/app.ts +16 -0
- package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/index.ts +91 -0
- package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/index.ts +1 -0
- package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/types.ts +5 -0
- package/src/cli/templates/bim-beta/src/bim-components/index.ts +1 -0
- package/src/cli/templates/bim-beta/src/globals.ts +1 -0
- package/src/cli/templates/bim-beta/src/main.ts +93 -0
- package/src/cli/templates/bim-beta/src/setups/cloud-runner.ts +13 -0
- package/src/cli/templates/bim-beta/src/setups/index.ts +3 -0
- package/src/cli/templates/bim-beta/src/setups/ui-manager.ts +27 -0
- package/src/cli/templates/bim-beta/src/setups/viewports-manager.ts +22 -0
- package/src/cli/templates/bim-beta/src/ui-components/app-info-section/index.ts +26 -0
- package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/index.ts +1 -0
- package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/types.ts +15 -0
- package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/index.ts +37 -0
- package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/index.ts +1 -0
- package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/types.ts +14 -0
- package/src/cli/templates/bim-beta/src/ui-components/index.ts +2 -0
- package/src/cli/templates/cloud/CONTEXT.md +205 -205
- package/src/cli/templates/cloud/src/main.ts +70 -70
- package/src/cli/templates/shared/AGENTS.md +30 -23
- package/src/cli/templates/shared/CLAUDE.md +1 -1
package/docs/app-layout.md
CHANGED
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
# Configure App Layout
|
|
2
|
-
|
|
3
|
-
## Grid element constraint
|
|
4
|
-
|
|
5
|
-
Every entry in `grid.elements` must be a single `bim-panel-section`. This is a hard constraint of the platform — the grid only knows how to host panel sections, not arbitrary HTML containers.
|
|
6
|
-
|
|
7
|
-
**Correct:** one `grid.elements` entry = one `bim-panel-section`
|
|
8
|
-
**Wrong:** a UI component that returns a `<div>` wrapping multiple `<bui-panel-section>` elements used as a grid entry
|
|
9
|
-
|
|
10
|
-
If you need multiple sections in one area, register each section separately in `grid.elements` and group them using `panel:` or `tabs:` in the grid template — never wrap them in a container component.
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
// ✗ Wrong — MyDashboard returns <div><bui-panel-section>...</bui-panel-section><bui-panel-section>...</bui-panel-section></div>
|
|
14
|
-
grid.elements = {
|
|
15
|
-
dashboard: { template: uis.custom.get("myDashboard").template, initialState: { components } },
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// ✓ Correct — each section is its own grid element; grouping happens in the template
|
|
19
|
-
grid.elements = {
|
|
20
|
-
stats: { template: uis.custom.get("statsSection").template, initialState: { components } },
|
|
21
|
-
charts: { template: uis.custom.get("chartsSection").template, initialState: { components } },
|
|
22
|
-
}
|
|
23
|
-
// Then in the layout template:
|
|
24
|
-
// "panel:right(stats,charts) viewer" 1fr / 22rem 1fr
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Grid sync rules
|
|
30
|
-
|
|
31
|
-
Any change to the grid requires updates in multiple places. Never update one without the others.
|
|
32
|
-
|
|
33
|
-
**Adding or removing a layout:**
|
|
34
|
-
1. `app.ts` → first argument of `BUI.Grid<[layouts], ...>`
|
|
35
|
-
2. `main.ts` → `grid.layouts` (add/remove the definition) and `grid.layout` if it was the initial layout
|
|
36
|
-
|
|
37
|
-
**Adding or removing an element:**
|
|
38
|
-
1. `app.ts` → second argument of `BUI.Grid<..., [elements]>`
|
|
39
|
-
2. `main.ts` → `grid.elements`, `grid.areaGroups` (if applicable), and any layout templates that reference it
|
|
40
|
-
|
|
41
|
-
**Renaming an element** — the name originates in the component, so the change cascades outward:
|
|
42
|
-
1. Rename the folder in `ui-components/` (kebab-case of the new name)
|
|
43
|
-
2. Update all internal identifiers: types (`{ComponentName}State`, `{ComponentName}Component`, `{ComponentName}GridElement`), functions (`{camelCase}Template`, `on{PascalCase}Created`)
|
|
44
|
-
3. Update the `ui-components/index.ts` barrel to point to the renamed folder
|
|
45
|
-
4. `app.ts` → second argument of `BUI.Grid` reflects the new `{ComponentName}GridElement`
|
|
46
|
-
5. `main.ts` → `grid.elements` and all layout templates that use that area name
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Defining layouts
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
grid.layouts = {
|
|
54
|
-
Viewer: {
|
|
55
|
-
template: `"viewer" 1fr / 1fr`,
|
|
56
|
-
},
|
|
57
|
-
Quantities: {
|
|
58
|
-
icon: app.icons.QUANTITY,
|
|
59
|
-
template: `
|
|
60
|
-
"qtos viewer" 1fr
|
|
61
|
-
/40rem 1fr
|
|
62
|
-
`,
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
grid.layout = "Collider"
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Special area tokens
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
{groupType}:{areaName}(elementA, elementB)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
There are two group types: `tabs` and `panel`.
|
|
77
|
-
|
|
78
|
-
### `tabs:` — one element at a time
|
|
79
|
-
|
|
80
|
-
Shows one element at a time with tab switchers. The user picks which section is visible. Supports nested sub-groups:
|
|
81
|
-
|
|
82
|
-
```
|
|
83
|
-
tabs:{areaName}({groupName}[elementA, elementB], elementC)
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
```ts
|
|
87
|
-
Collider: {
|
|
88
|
-
icon: appIcons.COLLISION,
|
|
89
|
-
template: `
|
|
90
|
-
"tabs:left(group[collider,qtos],collider) viewer" 1fr
|
|
91
|
-
/30rem 1fr
|
|
92
|
-
`,
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
`areaGroups` options for `tabs:`: `switchersCompact` or `switchersFull`.
|
|
97
|
-
|
|
98
|
-
### `panel:` — all elements stacked vertically
|
|
99
|
-
|
|
100
|
-
Stacks all elements and keeps them all visible simultaneously, scrollable as a single panel. Use when sections are related and should all be visible at once (e.g. an inspector with multiple info sections):
|
|
101
|
-
|
|
102
|
-
```ts
|
|
103
|
-
app: {
|
|
104
|
-
template: `
|
|
105
|
-
"tabs:left viewport panel:right" 1fr
|
|
106
|
-
/ 22rem 1fr 20rem
|
|
107
|
-
`,
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
`areaGroups` options for `panel:`: `label` to give the panel a title.
|
|
112
|
-
|
|
113
|
-
### Choosing between `tabs:` and `panel:`
|
|
114
|
-
|
|
115
|
-
- Use `tabs:` when sections are **alternatives** — the user focuses on one at a time
|
|
116
|
-
- Use `panel:` when sections are **complementary** — the user needs all of them visible together
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## `grid.elements` and `grid.areaGroups`
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
grid.elements = {
|
|
124
|
-
viewer: viewport,
|
|
125
|
-
qtos: {
|
|
126
|
-
template: uis.custom.get("qtosSection").template,
|
|
127
|
-
initialState: { components },
|
|
128
|
-
label: "Quantities",
|
|
129
|
-
},
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
grid.areaGroups = {
|
|
133
|
-
left: { switchersFull: true },
|
|
134
|
-
right: { label: "Inspector" },
|
|
135
|
-
group: { label: "Configuration", icon: appIcons.APPLY },
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
> **Grouping sections always happens at the grid level.** To group, combine, or stack panel sections together, the answer is always `tabs:` or `panel:` in the grid template — never nesting one section inside another in the template code.
|
|
1
|
+
# Configure App Layout
|
|
2
|
+
|
|
3
|
+
## Grid element constraint
|
|
4
|
+
|
|
5
|
+
Every entry in `grid.elements` must be a single `bim-panel-section`. This is a hard constraint of the platform — the grid only knows how to host panel sections, not arbitrary HTML containers.
|
|
6
|
+
|
|
7
|
+
**Correct:** one `grid.elements` entry = one `bim-panel-section`
|
|
8
|
+
**Wrong:** a UI component that returns a `<div>` wrapping multiple `<bui-panel-section>` elements used as a grid entry
|
|
9
|
+
|
|
10
|
+
If you need multiple sections in one area, register each section separately in `grid.elements` and group them using `panel:` or `tabs:` in the grid template — never wrap them in a container component.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// ✗ Wrong — MyDashboard returns <div><bui-panel-section>...</bui-panel-section><bui-panel-section>...</bui-panel-section></div>
|
|
14
|
+
grid.elements = {
|
|
15
|
+
dashboard: { template: uis.custom.get("myDashboard").template, initialState: { components } },
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ✓ Correct — each section is its own grid element; grouping happens in the template
|
|
19
|
+
grid.elements = {
|
|
20
|
+
stats: { template: uis.custom.get("statsSection").template, initialState: { components } },
|
|
21
|
+
charts: { template: uis.custom.get("chartsSection").template, initialState: { components } },
|
|
22
|
+
}
|
|
23
|
+
// Then in the layout template:
|
|
24
|
+
// "panel:right(stats,charts) viewer" 1fr / 22rem 1fr
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Grid sync rules
|
|
30
|
+
|
|
31
|
+
Any change to the grid requires updates in multiple places. Never update one without the others.
|
|
32
|
+
|
|
33
|
+
**Adding or removing a layout:**
|
|
34
|
+
1. `app.ts` → first argument of `BUI.Grid<[layouts], ...>`
|
|
35
|
+
2. `main.ts` → `grid.layouts` (add/remove the definition) and `grid.layout` if it was the initial layout
|
|
36
|
+
|
|
37
|
+
**Adding or removing an element:**
|
|
38
|
+
1. `app.ts` → second argument of `BUI.Grid<..., [elements]>`
|
|
39
|
+
2. `main.ts` → `grid.elements`, `grid.areaGroups` (if applicable), and any layout templates that reference it
|
|
40
|
+
|
|
41
|
+
**Renaming an element** — the name originates in the component, so the change cascades outward:
|
|
42
|
+
1. Rename the folder in `ui-components/` (kebab-case of the new name)
|
|
43
|
+
2. Update all internal identifiers: types (`{ComponentName}State`, `{ComponentName}Component`, `{ComponentName}GridElement`), functions (`{camelCase}Template`, `on{PascalCase}Created`)
|
|
44
|
+
3. Update the `ui-components/index.ts` barrel to point to the renamed folder
|
|
45
|
+
4. `app.ts` → second argument of `BUI.Grid` reflects the new `{ComponentName}GridElement`
|
|
46
|
+
5. `main.ts` → `grid.elements` and all layout templates that use that area name
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Defining layouts
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
grid.layouts = {
|
|
54
|
+
Viewer: {
|
|
55
|
+
template: `"viewer" 1fr / 1fr`,
|
|
56
|
+
},
|
|
57
|
+
Quantities: {
|
|
58
|
+
icon: app.icons.QUANTITY,
|
|
59
|
+
template: `
|
|
60
|
+
"qtos viewer" 1fr
|
|
61
|
+
/40rem 1fr
|
|
62
|
+
`,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
grid.layout = "Collider"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Special area tokens
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
{groupType}:{areaName}(elementA, elementB)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
There are two group types: `tabs` and `panel`.
|
|
77
|
+
|
|
78
|
+
### `tabs:` — one element at a time
|
|
79
|
+
|
|
80
|
+
Shows one element at a time with tab switchers. The user picks which section is visible. Supports nested sub-groups:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
tabs:{areaName}({groupName}[elementA, elementB], elementC)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
Collider: {
|
|
88
|
+
icon: appIcons.COLLISION,
|
|
89
|
+
template: `
|
|
90
|
+
"tabs:left(group[collider,qtos],collider) viewer" 1fr
|
|
91
|
+
/30rem 1fr
|
|
92
|
+
`,
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`areaGroups` options for `tabs:`: `switchersCompact` or `switchersFull`.
|
|
97
|
+
|
|
98
|
+
### `panel:` — all elements stacked vertically
|
|
99
|
+
|
|
100
|
+
Stacks all elements and keeps them all visible simultaneously, scrollable as a single panel. Use when sections are related and should all be visible at once (e.g. an inspector with multiple info sections):
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
app: {
|
|
104
|
+
template: `
|
|
105
|
+
"tabs:left viewport panel:right" 1fr
|
|
106
|
+
/ 22rem 1fr 20rem
|
|
107
|
+
`,
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`areaGroups` options for `panel:`: `label` to give the panel a title.
|
|
112
|
+
|
|
113
|
+
### Choosing between `tabs:` and `panel:`
|
|
114
|
+
|
|
115
|
+
- Use `tabs:` when sections are **alternatives** — the user focuses on one at a time
|
|
116
|
+
- Use `panel:` when sections are **complementary** — the user needs all of them visible together
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## `grid.elements` and `grid.areaGroups`
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
grid.elements = {
|
|
124
|
+
viewer: viewport,
|
|
125
|
+
qtos: {
|
|
126
|
+
template: uis.custom.get("qtosSection").template,
|
|
127
|
+
initialState: { components },
|
|
128
|
+
label: "Quantities",
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
grid.areaGroups = {
|
|
133
|
+
left: { switchersFull: true },
|
|
134
|
+
right: { label: "Inspector" },
|
|
135
|
+
group: { label: "Configuration", icon: appIcons.APPLY },
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> **Grouping sections always happens at the grid level.** To group, combine, or stack panel sections together, the answer is always `tabs:` or `panel:` in the grid template — never nesting one section inside another in the template code.
|
package/docs/app-wiring.md
CHANGED
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
# App Wiring
|
|
2
|
-
|
|
3
|
-
## `app.ts` — App Identity
|
|
4
|
-
|
|
5
|
-
Defines the app's typed shape and the typed accessor for `AppManager`. The `App` type uses `GridElement` types exported from `ui-components/`:
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
import * as OBC from "@thatopen/components"
|
|
9
|
-
import * as BUI from "@thatopen/ui"
|
|
10
|
-
import { AppManager } from "@thatopen/services"
|
|
11
|
-
import { icons } from "./globals"
|
|
12
|
-
import { FilesSectionGridElement, QtosSectionGridElement } from "./ui-components"
|
|
13
|
-
|
|
14
|
-
export type App = {
|
|
15
|
-
icons: (keyof typeof icons)[]
|
|
16
|
-
grid: BUI.Grid<
|
|
17
|
-
["Viewer", "Quantities", "Collider"],
|
|
18
|
-
[
|
|
19
|
-
"viewer",
|
|
20
|
-
FilesSectionGridElement,
|
|
21
|
-
QtosSectionGridElement,
|
|
22
|
-
]
|
|
23
|
-
>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const getAppManager = (components: OBC.Components) =>
|
|
27
|
-
components.get(AppManager<App>)
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
`getAppManager` is the **only** valid way to access `AppManager` in an app. Never use `components.get(AppManager)` directly — it drops the `App` type parameter and loses all type information about the app's icons, grid layout, and grid elements.
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
// ✗ Avoid — loses the App type
|
|
34
|
-
const app = components.get(AppManager)
|
|
35
|
-
|
|
36
|
-
// ✓ Correct — fully typed
|
|
37
|
-
const app = getAppManager(components)
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Grid element types
|
|
41
|
-
|
|
42
|
-
- Raw elements → typed as a string literal: `"viewer"`
|
|
43
|
-
- Template-based sections → typed as `{ComponentName}GridElement`, defined in the component's `src/types.ts` and exported through `ui-components/index.ts`
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
// ui-components/files-section/src/types.ts
|
|
47
|
-
export type FilesSectionGridElement = {
|
|
48
|
-
name: "filesSection"
|
|
49
|
-
state: FilesSectionState
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## `main.ts` — Entry Point
|
|
56
|
-
|
|
57
|
-
`main.ts` boots the platform and calls `app.init()`. No business logic belongs here.
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
async function main() {
|
|
61
|
-
const client = EngineServicesClient.fromPlatformContext()
|
|
62
|
-
|
|
63
|
-
const { components } = await client.setup(
|
|
64
|
-
{ OBC, OBF, BUI, CUI, THREE, FRAGS },
|
|
65
|
-
{ uuid: ViewportsManager.uuid },
|
|
66
|
-
{ uuid: AppManager.uuid },
|
|
67
|
-
{ uuid: UIManager.uuid },
|
|
68
|
-
) as { components: OBC.Components }
|
|
69
|
-
|
|
70
|
-
await viewportsManager(components)
|
|
71
|
-
const viewports = components.get(ViewportsManager)
|
|
72
|
-
// @ts-ignore
|
|
73
|
-
const { element: viewport } = [...viewports._instances.values()][0]
|
|
74
|
-
|
|
75
|
-
const app = getAppManager(components)
|
|
76
|
-
await app.init({
|
|
77
|
-
client,
|
|
78
|
-
icons: appIcons,
|
|
79
|
-
componentSetups: { ... },
|
|
80
|
-
grid: (grid) => { ... },
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
main().catch(console.error)
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### `client.setup()` — Platform initialization
|
|
88
|
-
|
|
89
|
-
- **First argument** — libraries object (`OBC`, `OBF`, `BUI`, `CUI`, `THREE`, `FRAGS`, etc.)
|
|
90
|
-
- **Subsequent arguments** — `{ uuid }` for each platform built-in component needed. Always present: `ViewportsManager`, `AppManager`, `UIManager`
|
|
91
|
-
|
|
92
|
-
Custom components (from `bim-components/`) do not need to be listed here — they self-register via `components.add()` in their constructor.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## `componentSetups` in `app.init()`
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
componentSetups: {
|
|
100
|
-
core: [uiManager, fragmentsManager, highlighter, ifcLoader],
|
|
101
|
-
lazy: [
|
|
102
|
-
{ uuid: MyComponent.uuid, fn: myComponentSetup },
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
**`core`** — runs before the grid mounts, in parallel. `uiManager` always goes here. Other engine component setups (fragments, highlighter, IFC loader) also typically go here.
|
|
108
|
-
|
|
109
|
-
**`lazy`** — runs the first time the corresponding component is instantiated. The `uuid` is imported from the `bim-components/` barrel. Use for custom components that can be loaded on demand.
|
|
110
|
-
|
|
111
|
-
The `uuid` of each custom component — a static property defined on the class — is imported from the `bim-components/` barrel when referencing it in `lazy`:
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
import { MyComponent } from "../bim-components"
|
|
115
|
-
|
|
116
|
-
componentSetups: {
|
|
117
|
-
lazy: [
|
|
118
|
-
{ uuid: MyComponent.uuid, fn: myComponentSetup },
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
When it's not obvious whether a custom component should be `core` or `lazy`, **decide based on whether the component is needed at boot or only on demand.**
|
|
1
|
+
# App Wiring
|
|
2
|
+
|
|
3
|
+
## `app.ts` — App Identity
|
|
4
|
+
|
|
5
|
+
Defines the app's typed shape and the typed accessor for `AppManager`. The `App` type uses `GridElement` types exported from `ui-components/`:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import * as OBC from "@thatopen/components"
|
|
9
|
+
import * as BUI from "@thatopen/ui"
|
|
10
|
+
import { AppManager } from "@thatopen/services"
|
|
11
|
+
import { icons } from "./globals"
|
|
12
|
+
import { FilesSectionGridElement, QtosSectionGridElement } from "./ui-components"
|
|
13
|
+
|
|
14
|
+
export type App = {
|
|
15
|
+
icons: (keyof typeof icons)[]
|
|
16
|
+
grid: BUI.Grid<
|
|
17
|
+
["Viewer", "Quantities", "Collider"],
|
|
18
|
+
[
|
|
19
|
+
"viewer",
|
|
20
|
+
FilesSectionGridElement,
|
|
21
|
+
QtosSectionGridElement,
|
|
22
|
+
]
|
|
23
|
+
>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const getAppManager = (components: OBC.Components) =>
|
|
27
|
+
components.get(AppManager<App>)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`getAppManager` is the **only** valid way to access `AppManager` in an app. Never use `components.get(AppManager)` directly — it drops the `App` type parameter and loses all type information about the app's icons, grid layout, and grid elements.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// ✗ Avoid — loses the App type
|
|
34
|
+
const app = components.get(AppManager)
|
|
35
|
+
|
|
36
|
+
// ✓ Correct — fully typed
|
|
37
|
+
const app = getAppManager(components)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Grid element types
|
|
41
|
+
|
|
42
|
+
- Raw elements → typed as a string literal: `"viewer"`
|
|
43
|
+
- Template-based sections → typed as `{ComponentName}GridElement`, defined in the component's `src/types.ts` and exported through `ui-components/index.ts`
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// ui-components/files-section/src/types.ts
|
|
47
|
+
export type FilesSectionGridElement = {
|
|
48
|
+
name: "filesSection"
|
|
49
|
+
state: FilesSectionState
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## `main.ts` — Entry Point
|
|
56
|
+
|
|
57
|
+
`main.ts` boots the platform and calls `app.init()`. No business logic belongs here.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
async function main() {
|
|
61
|
+
const client = EngineServicesClient.fromPlatformContext()
|
|
62
|
+
|
|
63
|
+
const { components } = await client.setup(
|
|
64
|
+
{ OBC, OBF, BUI, CUI, THREE, FRAGS },
|
|
65
|
+
{ uuid: ViewportsManager.uuid },
|
|
66
|
+
{ uuid: AppManager.uuid },
|
|
67
|
+
{ uuid: UIManager.uuid },
|
|
68
|
+
) as { components: OBC.Components }
|
|
69
|
+
|
|
70
|
+
await viewportsManager(components)
|
|
71
|
+
const viewports = components.get(ViewportsManager)
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
const { element: viewport } = [...viewports._instances.values()][0]
|
|
74
|
+
|
|
75
|
+
const app = getAppManager(components)
|
|
76
|
+
await app.init({
|
|
77
|
+
client,
|
|
78
|
+
icons: appIcons,
|
|
79
|
+
componentSetups: { ... },
|
|
80
|
+
grid: (grid) => { ... },
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
main().catch(console.error)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `client.setup()` — Platform initialization
|
|
88
|
+
|
|
89
|
+
- **First argument** — libraries object (`OBC`, `OBF`, `BUI`, `CUI`, `THREE`, `FRAGS`, etc.)
|
|
90
|
+
- **Subsequent arguments** — `{ uuid }` for each platform built-in component needed. Always present: `ViewportsManager`, `AppManager`, `UIManager`
|
|
91
|
+
|
|
92
|
+
Custom components (from `bim-components/`) do not need to be listed here — they self-register via `components.add()` in their constructor.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## `componentSetups` in `app.init()`
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
componentSetups: {
|
|
100
|
+
core: [uiManager, fragmentsManager, highlighter, ifcLoader],
|
|
101
|
+
lazy: [
|
|
102
|
+
{ uuid: MyComponent.uuid, fn: myComponentSetup },
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**`core`** — runs before the grid mounts, in parallel. `uiManager` always goes here. Other engine component setups (fragments, highlighter, IFC loader) also typically go here.
|
|
108
|
+
|
|
109
|
+
**`lazy`** — runs the first time the corresponding component is instantiated. The `uuid` is imported from the `bim-components/` barrel. Use for custom components that can be loaded on demand.
|
|
110
|
+
|
|
111
|
+
The `uuid` of each custom component — a static property defined on the class — is imported from the `bim-components/` barrel when referencing it in `lazy`:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { MyComponent } from "../bim-components"
|
|
115
|
+
|
|
116
|
+
componentSetups: {
|
|
117
|
+
lazy: [
|
|
118
|
+
{ uuid: MyComponent.uuid, fn: myComponentSetup },
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
When it's not obvious whether a custom component should be `core` or `lazy`, **decide based on whether the component is needed at boot or only on demand.**
|