@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
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
# Coding Conventions
|
|
2
|
-
|
|
3
|
-
General coding conventions that apply across all BIM components.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## No shorthand property declaration in constructors
|
|
8
|
-
|
|
9
|
-
Declare properties explicitly on the class body. Never use TypeScript's constructor shorthand (`public`, `private`, `readonly` parameter modifiers):
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
// ✗ Avoid
|
|
13
|
-
constructor(public position: THREE.Vector3) {}
|
|
14
|
-
|
|
15
|
-
// ✓ Correct
|
|
16
|
-
position: THREE.Vector3;
|
|
17
|
-
|
|
18
|
-
constructor(position: THREE.Vector3) {
|
|
19
|
-
this.position = position;
|
|
20
|
-
}
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Private member naming
|
|
26
|
-
|
|
27
|
-
Private **properties** use an underscore prefix. Private **methods** do not:
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
// Properties — underscore prefix
|
|
31
|
-
private _enabled = true;
|
|
32
|
-
private _mesh: THREE.Mesh | null = null;
|
|
33
|
-
|
|
34
|
-
// Methods — no prefix
|
|
35
|
-
private setupEvents() { ... }
|
|
36
|
-
private calculateResult() { ... }
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Backing fields with getter/setter
|
|
42
|
-
|
|
43
|
-
When a property needs to trigger side effects on assignment — firing an event, propagating a value to Three.js objects — use a private backing field with a public getter/setter:
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
private _visible = true;
|
|
47
|
-
|
|
48
|
-
get visible() {
|
|
49
|
-
return this._visible;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
set visible(value: boolean) {
|
|
53
|
-
this._visible = value;
|
|
54
|
-
for (const mesh of this._meshes) {
|
|
55
|
-
mesh.visible = value;
|
|
56
|
-
}
|
|
57
|
-
this.onStateChanged.trigger(["visible"]);
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Throw in late-initialization getters
|
|
64
|
-
|
|
65
|
-
When a property only exists after `setup()` or another explicit initialization step, the getter throws a descriptive error rather than returning `undefined`. This makes the missing initialization obvious at the call site:
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
private _data: Data | null = null;
|
|
69
|
-
|
|
70
|
-
get data() {
|
|
71
|
-
if (!this._data) {
|
|
72
|
-
throw new Error("ActivityTracker: call setup() before accessing data.");
|
|
73
|
-
}
|
|
74
|
-
return this._data;
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## `interface` vs `type`
|
|
81
|
-
|
|
82
|
-
Use `interface` for object shapes that may be extended or implemented by other classes. Use `type` for everything else — unions, primitive aliases, computed types, and tuples:
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
// ✓ interface — extendable object shape
|
|
86
|
-
export interface ActivityConfig {
|
|
87
|
-
color: THREE.Color;
|
|
88
|
-
autoColorize: boolean;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ✓ type — union
|
|
92
|
-
export type ActivityStatus = "notStarted" | "inProgress" | "completed";
|
|
93
|
-
|
|
94
|
-
// ✓ type — primitive alias
|
|
95
|
-
export type ActivityId = string;
|
|
96
|
-
|
|
97
|
-
// ✓ type — tuple / computed
|
|
98
|
-
export type SerializedMap = [string, number[]][];
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## Prefer `DataMap` and `DataSet` over native `Map` and `Set`
|
|
104
|
-
|
|
105
|
-
When storing collections inside a component, always use `FRAGS.DataMap` and `FRAGS.DataSet` instead of the native `Map` and `Set`. They have an identical API but additionally emit lifecycle events (`onItemSet`, `onBeforeDelete`, `onItemDeleted`) that other parts of the app can react to.
|
|
106
|
-
|
|
107
|
-
See [`./observable-collections.md`](./observable-collections.md) for usage patterns.
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## `async/await` over `.then()/.catch()`
|
|
112
|
-
|
|
113
|
-
Always prefer `async/await` for asynchronous code. It is more readable and consistent with the rest of the codebase:
|
|
114
|
-
|
|
115
|
-
```ts
|
|
116
|
-
// ✗ Avoid
|
|
117
|
-
this.loadModel(buffer).then((model) => {
|
|
118
|
-
this.process(model);
|
|
119
|
-
}).catch((err) => {
|
|
120
|
-
console.error(err);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// ✓ Correct
|
|
124
|
-
async loadAndProcess(buffer: ArrayBuffer) {
|
|
125
|
-
const model = await this.loadModel(buffer);
|
|
126
|
-
await this.process(model);
|
|
127
|
-
}
|
|
128
|
-
```
|
|
1
|
+
# Coding Conventions
|
|
2
|
+
|
|
3
|
+
General coding conventions that apply across all BIM components.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## No shorthand property declaration in constructors
|
|
8
|
+
|
|
9
|
+
Declare properties explicitly on the class body. Never use TypeScript's constructor shorthand (`public`, `private`, `readonly` parameter modifiers):
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// ✗ Avoid
|
|
13
|
+
constructor(public position: THREE.Vector3) {}
|
|
14
|
+
|
|
15
|
+
// ✓ Correct
|
|
16
|
+
position: THREE.Vector3;
|
|
17
|
+
|
|
18
|
+
constructor(position: THREE.Vector3) {
|
|
19
|
+
this.position = position;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Private member naming
|
|
26
|
+
|
|
27
|
+
Private **properties** use an underscore prefix. Private **methods** do not:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// Properties — underscore prefix
|
|
31
|
+
private _enabled = true;
|
|
32
|
+
private _mesh: THREE.Mesh | null = null;
|
|
33
|
+
|
|
34
|
+
// Methods — no prefix
|
|
35
|
+
private setupEvents() { ... }
|
|
36
|
+
private calculateResult() { ... }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Backing fields with getter/setter
|
|
42
|
+
|
|
43
|
+
When a property needs to trigger side effects on assignment — firing an event, propagating a value to Three.js objects — use a private backing field with a public getter/setter:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
private _visible = true;
|
|
47
|
+
|
|
48
|
+
get visible() {
|
|
49
|
+
return this._visible;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
set visible(value: boolean) {
|
|
53
|
+
this._visible = value;
|
|
54
|
+
for (const mesh of this._meshes) {
|
|
55
|
+
mesh.visible = value;
|
|
56
|
+
}
|
|
57
|
+
this.onStateChanged.trigger(["visible"]);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Throw in late-initialization getters
|
|
64
|
+
|
|
65
|
+
When a property only exists after `setup()` or another explicit initialization step, the getter throws a descriptive error rather than returning `undefined`. This makes the missing initialization obvious at the call site:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
private _data: Data | null = null;
|
|
69
|
+
|
|
70
|
+
get data() {
|
|
71
|
+
if (!this._data) {
|
|
72
|
+
throw new Error("ActivityTracker: call setup() before accessing data.");
|
|
73
|
+
}
|
|
74
|
+
return this._data;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## `interface` vs `type`
|
|
81
|
+
|
|
82
|
+
Use `interface` for object shapes that may be extended or implemented by other classes. Use `type` for everything else — unions, primitive aliases, computed types, and tuples:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// ✓ interface — extendable object shape
|
|
86
|
+
export interface ActivityConfig {
|
|
87
|
+
color: THREE.Color;
|
|
88
|
+
autoColorize: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ✓ type — union
|
|
92
|
+
export type ActivityStatus = "notStarted" | "inProgress" | "completed";
|
|
93
|
+
|
|
94
|
+
// ✓ type — primitive alias
|
|
95
|
+
export type ActivityId = string;
|
|
96
|
+
|
|
97
|
+
// ✓ type — tuple / computed
|
|
98
|
+
export type SerializedMap = [string, number[]][];
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Prefer `DataMap` and `DataSet` over native `Map` and `Set`
|
|
104
|
+
|
|
105
|
+
When storing collections inside a component, always use `FRAGS.DataMap` and `FRAGS.DataSet` instead of the native `Map` and `Set`. They have an identical API but additionally emit lifecycle events (`onItemSet`, `onBeforeDelete`, `onItemDeleted`) that other parts of the app can react to.
|
|
106
|
+
|
|
107
|
+
See [`./observable-collections.md`](./observable-collections.md) for usage patterns.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## `async/await` over `.then()/.catch()`
|
|
112
|
+
|
|
113
|
+
Always prefer `async/await` for asynchronous code. It is more readable and consistent with the rest of the codebase:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// ✗ Avoid
|
|
117
|
+
this.loadModel(buffer).then((model) => {
|
|
118
|
+
this.process(model);
|
|
119
|
+
}).catch((err) => {
|
|
120
|
+
console.error(err);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ✓ Correct
|
|
124
|
+
async loadAndProcess(buffer: ArrayBuffer) {
|
|
125
|
+
const model = await this.loadModel(buffer);
|
|
126
|
+
await this.process(model);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
# Element Collections
|
|
2
|
-
|
|
3
|
-
`OBC.ModelIdMap` is the only acceptable way to reference a collection of elements in a `FragmentsModel`. Any method that operates on items — highlighting, hiding, filtering, calculating — takes or returns a `ModelIdMap`.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## `OBC.ModelIdMap`
|
|
8
|
-
|
|
9
|
-
Maps a model ID to a `Set` of local IDs:
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
// Shape: { [modelId: string]: Set<number> }
|
|
13
|
-
const items: OBC.ModelIdMap = {
|
|
14
|
-
"model-a": new Set([1, 2, 3]),
|
|
15
|
-
"model-b": new Set([7, 8]),
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// Iterating
|
|
19
|
-
for (const [modelId, localIds] of Object.entries(items)) {
|
|
20
|
-
const model = fragments.list.get(modelId);
|
|
21
|
-
for (const localId of localIds) { ... }
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## `OBC.ModelIdDataMap<T>`
|
|
28
|
-
|
|
29
|
-
Associates data of type `T` per item — a `DataMap<string, DataMap<number, T>>`. Used when a component stores per-item state:
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
const progress: OBC.ModelIdDataMap<{ startDate: Date }> = new FRAGS.DataMap();
|
|
33
|
-
|
|
34
|
-
// Write
|
|
35
|
-
let byLocalId = progress.get("model-a");
|
|
36
|
-
if (!byLocalId) {
|
|
37
|
-
byLocalId = new FRAGS.DataMap();
|
|
38
|
-
progress.set("model-a", byLocalId);
|
|
39
|
-
}
|
|
40
|
-
byLocalId.set(42, { startDate: new Date() });
|
|
41
|
-
|
|
42
|
-
// Read
|
|
43
|
-
const itemProgress = progress.get("model-a")?.get(42);
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## `OBC.ModelIdMapUtils`
|
|
49
|
-
|
|
50
|
-
Static utility class for operating on `ModelIdMap` collections. Use it to combine, filter, compare, and serialize selections rather than implementing those operations manually:
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
import * as OBC from "@thatopen/components"
|
|
54
|
-
|
|
55
|
-
// Combine two selections
|
|
56
|
-
const combined = OBC.ModelIdMapUtils.join([selectionA, selectionB]);
|
|
57
|
-
|
|
58
|
-
// Remove deselected items from current selection
|
|
59
|
-
OBC.ModelIdMapUtils.remove(current, deselected);
|
|
60
|
-
|
|
61
|
-
// Guard before processing
|
|
62
|
-
if (OBC.ModelIdMapUtils.isEmpty(items)) return;
|
|
63
|
-
|
|
64
|
-
// Serialize for storage (Set<number> → number[])
|
|
65
|
-
const raw = OBC.ModelIdMapUtils.toRaw(items);
|
|
66
|
-
|
|
67
|
-
// Restore from storage
|
|
68
|
-
const restored = OBC.ModelIdMapUtils.fromRaw(raw);
|
|
69
|
-
```
|
|
1
|
+
# Element Collections
|
|
2
|
+
|
|
3
|
+
`OBC.ModelIdMap` is the only acceptable way to reference a collection of elements in a `FragmentsModel`. Any method that operates on items — highlighting, hiding, filtering, calculating — takes or returns a `ModelIdMap`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## `OBC.ModelIdMap`
|
|
8
|
+
|
|
9
|
+
Maps a model ID to a `Set` of local IDs:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// Shape: { [modelId: string]: Set<number> }
|
|
13
|
+
const items: OBC.ModelIdMap = {
|
|
14
|
+
"model-a": new Set([1, 2, 3]),
|
|
15
|
+
"model-b": new Set([7, 8]),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Iterating
|
|
19
|
+
for (const [modelId, localIds] of Object.entries(items)) {
|
|
20
|
+
const model = fragments.list.get(modelId);
|
|
21
|
+
for (const localId of localIds) { ... }
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## `OBC.ModelIdDataMap<T>`
|
|
28
|
+
|
|
29
|
+
Associates data of type `T` per item — a `DataMap<string, DataMap<number, T>>`. Used when a component stores per-item state:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const progress: OBC.ModelIdDataMap<{ startDate: Date }> = new FRAGS.DataMap();
|
|
33
|
+
|
|
34
|
+
// Write
|
|
35
|
+
let byLocalId = progress.get("model-a");
|
|
36
|
+
if (!byLocalId) {
|
|
37
|
+
byLocalId = new FRAGS.DataMap();
|
|
38
|
+
progress.set("model-a", byLocalId);
|
|
39
|
+
}
|
|
40
|
+
byLocalId.set(42, { startDate: new Date() });
|
|
41
|
+
|
|
42
|
+
// Read
|
|
43
|
+
const itemProgress = progress.get("model-a")?.get(42);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## `OBC.ModelIdMapUtils`
|
|
49
|
+
|
|
50
|
+
Static utility class for operating on `ModelIdMap` collections. Use it to combine, filter, compare, and serialize selections rather than implementing those operations manually:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import * as OBC from "@thatopen/components"
|
|
54
|
+
|
|
55
|
+
// Combine two selections
|
|
56
|
+
const combined = OBC.ModelIdMapUtils.join([selectionA, selectionB]);
|
|
57
|
+
|
|
58
|
+
// Remove deselected items from current selection
|
|
59
|
+
OBC.ModelIdMapUtils.remove(current, deselected);
|
|
60
|
+
|
|
61
|
+
// Guard before processing
|
|
62
|
+
if (OBC.ModelIdMapUtils.isEmpty(items)) return;
|
|
63
|
+
|
|
64
|
+
// Serialize for storage (Set<number> → number[])
|
|
65
|
+
const raw = OBC.ModelIdMapUtils.toRaw(items);
|
|
66
|
+
|
|
67
|
+
// Restore from storage
|
|
68
|
+
const restored = OBC.ModelIdMapUtils.fromRaw(raw);
|
|
69
|
+
```
|
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
# Expose Events
|
|
2
|
-
|
|
3
|
-
## `OBC.Event<T>`
|
|
4
|
-
|
|
5
|
-
Events are declared as `readonly` properties on the class. The type parameter `T` defines the payload — use `undefined` when there's nothing to pass.
|
|
6
|
-
|
|
7
|
-
Payload types are defined in `src/types.ts`, not inline, following the `{EventName}Payload` convention:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
// src/types.ts
|
|
11
|
-
export type OperationCompletedPayload = { result: CalculationResult }
|
|
12
|
-
|
|
13
|
-
// index.ts
|
|
14
|
-
readonly onOperationCompleted = new OBC.Event<OperationCompletedPayload>();
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
**Triggering** — always after the fact, once the operation or change is done:
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
this.onOperationCompleted.trigger({ result });
|
|
21
|
-
this.onDisposed.trigger(undefined);
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
**Subscribing** — done externally by consumers:
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
const tracker = components.get(ActivityTracker);
|
|
28
|
-
tracker.onOperationCompleted.add(({ result }) => { ... });
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## `onStateChanged` pattern
|
|
34
|
-
|
|
35
|
-
When a component has several reactive properties, a single `onStateChanged` event with a union payload is cleaner than one event per property:
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
readonly onStateChanged = new OBC.Event<("enabled" | "color" | "mode")[]>();
|
|
39
|
-
|
|
40
|
-
private _enabled = true;
|
|
41
|
-
|
|
42
|
-
get enabled() {
|
|
43
|
-
return this._enabled;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
set enabled(value: boolean) {
|
|
47
|
-
this._enabled = value;
|
|
48
|
-
this.onStateChanged.trigger(["enabled"]);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Consumers can inspect the payload to react only to the properties they care about:
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
tracker.onStateChanged.add((changed) => {
|
|
56
|
-
if (changed.includes("color")) updateColorUI();
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## `OBC.Eventable` and `EventManager`
|
|
63
|
-
|
|
64
|
-
Any component that exposes events must implement `OBC.Eventable` and use `EventManager`. Register every event in the constructor and call `events.reset()` in `dispose()`:
|
|
65
|
-
|
|
66
|
-
```ts
|
|
67
|
-
export class ActivityTracker extends OBC.Component implements OBC.Eventable {
|
|
68
|
-
readonly onDisposed = new OBC.Event<undefined>();
|
|
69
|
-
readonly onStateChanged = new OBC.Event<string[]>();
|
|
70
|
-
|
|
71
|
-
readonly events = new OBC.EventManager();
|
|
72
|
-
|
|
73
|
-
constructor(components: OBC.Components) {
|
|
74
|
-
super(components);
|
|
75
|
-
components.add(ActivityTracker.uuid, this);
|
|
76
|
-
this.events.list.add(this.onDisposed);
|
|
77
|
-
this.events.list.add(this.onStateChanged);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
dispose() {
|
|
81
|
-
this.events.reset();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
```
|
|
1
|
+
# Expose Events
|
|
2
|
+
|
|
3
|
+
## `OBC.Event<T>`
|
|
4
|
+
|
|
5
|
+
Events are declared as `readonly` properties on the class. The type parameter `T` defines the payload — use `undefined` when there's nothing to pass.
|
|
6
|
+
|
|
7
|
+
Payload types are defined in `src/types.ts`, not inline, following the `{EventName}Payload` convention:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// src/types.ts
|
|
11
|
+
export type OperationCompletedPayload = { result: CalculationResult }
|
|
12
|
+
|
|
13
|
+
// index.ts
|
|
14
|
+
readonly onOperationCompleted = new OBC.Event<OperationCompletedPayload>();
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Triggering** — always after the fact, once the operation or change is done:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
this.onOperationCompleted.trigger({ result });
|
|
21
|
+
this.onDisposed.trigger(undefined);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Subscribing** — done externally by consumers:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const tracker = components.get(ActivityTracker);
|
|
28
|
+
tracker.onOperationCompleted.add(({ result }) => { ... });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## `onStateChanged` pattern
|
|
34
|
+
|
|
35
|
+
When a component has several reactive properties, a single `onStateChanged` event with a union payload is cleaner than one event per property:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
readonly onStateChanged = new OBC.Event<("enabled" | "color" | "mode")[]>();
|
|
39
|
+
|
|
40
|
+
private _enabled = true;
|
|
41
|
+
|
|
42
|
+
get enabled() {
|
|
43
|
+
return this._enabled;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
set enabled(value: boolean) {
|
|
47
|
+
this._enabled = value;
|
|
48
|
+
this.onStateChanged.trigger(["enabled"]);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Consumers can inspect the payload to react only to the properties they care about:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
tracker.onStateChanged.add((changed) => {
|
|
56
|
+
if (changed.includes("color")) updateColorUI();
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## `OBC.Eventable` and `EventManager`
|
|
63
|
+
|
|
64
|
+
Any component that exposes events must implement `OBC.Eventable` and use `EventManager`. Register every event in the constructor and call `events.reset()` in `dispose()`:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
export class ActivityTracker extends OBC.Component implements OBC.Eventable {
|
|
68
|
+
readonly onDisposed = new OBC.Event<undefined>();
|
|
69
|
+
readonly onStateChanged = new OBC.Event<string[]>();
|
|
70
|
+
|
|
71
|
+
readonly events = new OBC.EventManager();
|
|
72
|
+
|
|
73
|
+
constructor(components: OBC.Components) {
|
|
74
|
+
super(components);
|
|
75
|
+
components.add(ActivityTracker.uuid, this);
|
|
76
|
+
this.events.list.add(this.onDisposed);
|
|
77
|
+
this.events.list.add(this.onStateChanged);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
dispose() {
|
|
81
|
+
this.events.reset();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
# Library Examples
|
|
2
|
-
|
|
3
|
-
Before writing any implementation that uses That Open Engine — whether you know the component name or are trying to figure out how to achieve something — check whether an official example covers it. Official examples are the best starting point: they show correct initialization sequences, idiomatic API usage, and working patterns from the source authors.
|
|
4
|
-
|
|
5
|
-
## How to find an example
|
|
6
|
-
|
|
7
|
-
1. Fetch the `paths.json` for each repo below (you can do all three in parallel). Do not summarize the response — retain the full JSON in context and use the descriptions to reason about which examples are relevant.
|
|
8
|
-
2. Use the descriptions in the JSON to reason about which examples best match the user's intent. Prefer semantic matching over path name matching — a description may cover what the user needs even if the component name doesn't match exactly.
|
|
9
|
-
3. Only proceed to fetch an example if its description confirms that it directly covers the user's intent, or that it can serve as a building block for composing a new custom component. Do not fetch speculatively.
|
|
10
|
-
4. Construct the full URL: `{base_url}{path_entry}` and fetch the example file to use as your implementation reference.
|
|
11
|
-
|
|
12
|
-
Path names are descriptive — `VisibilityOperations`, `EditElements`, `SteelDetailing` — so matching by intent works well even when the user hasn't named a specific component.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## Repositories
|
|
17
|
-
|
|
18
|
-
### engine_components
|
|
19
|
-
Components from `@thatopen/components` (OBC) and `@thatopen/components-front` (OBF).
|
|
20
|
-
|
|
21
|
-
- **paths.json**: `https://raw.githubusercontent.com/ThatOpen/engine_components/refs/heads/main/examples/paths.json`
|
|
22
|
-
- **Base URL**: `https://raw.githubusercontent.com/ThatOpen/engine_components/refs/heads/main/`
|
|
23
|
-
|
|
24
|
-
### engine_fragment
|
|
25
|
-
The `@thatopen/fragments` low-level library. There are no named OBC components here — users typically describe what they want to do (e.g., "edit model properties", "modify elements", "work with visibility").
|
|
26
|
-
|
|
27
|
-
- **paths.json**: `https://raw.githubusercontent.com/ThatOpen/engine_fragment/refs/heads/main/examples/paths.json`
|
|
28
|
-
- **Base URL**: `https://raw.githubusercontent.com/ThatOpen/engine_fragment/refs/heads/main/`
|
|
1
|
+
# Library Examples
|
|
2
|
+
|
|
3
|
+
Before writing any implementation that uses That Open Engine — whether you know the component name or are trying to figure out how to achieve something — check whether an official example covers it. Official examples are the best starting point: they show correct initialization sequences, idiomatic API usage, and working patterns from the source authors.
|
|
4
|
+
|
|
5
|
+
## How to find an example
|
|
6
|
+
|
|
7
|
+
1. Fetch the `paths.json` for each repo below (you can do all three in parallel). Do not summarize the response — retain the full JSON in context and use the descriptions to reason about which examples are relevant.
|
|
8
|
+
2. Use the descriptions in the JSON to reason about which examples best match the user's intent. Prefer semantic matching over path name matching — a description may cover what the user needs even if the component name doesn't match exactly.
|
|
9
|
+
3. Only proceed to fetch an example if its description confirms that it directly covers the user's intent, or that it can serve as a building block for composing a new custom component. Do not fetch speculatively.
|
|
10
|
+
4. Construct the full URL: `{base_url}{path_entry}` and fetch the example file to use as your implementation reference.
|
|
11
|
+
|
|
12
|
+
Path names are descriptive — `VisibilityOperations`, `EditElements`, `SteelDetailing` — so matching by intent works well even when the user hasn't named a specific component.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Repositories
|
|
17
|
+
|
|
18
|
+
### engine_components
|
|
19
|
+
Components from `@thatopen/components` (OBC) and `@thatopen/components-front` (OBF).
|
|
20
|
+
|
|
21
|
+
- **paths.json**: `https://raw.githubusercontent.com/ThatOpen/engine_components/refs/heads/main/examples/paths.json`
|
|
22
|
+
- **Base URL**: `https://raw.githubusercontent.com/ThatOpen/engine_components/refs/heads/main/`
|
|
23
|
+
|
|
24
|
+
### engine_fragment
|
|
25
|
+
The `@thatopen/fragments` low-level library. There are no named OBC components here — users typically describe what they want to do (e.g., "edit model properties", "modify elements", "work with visibility").
|
|
26
|
+
|
|
27
|
+
- **paths.json**: `https://raw.githubusercontent.com/ThatOpen/engine_fragment/refs/heads/main/examples/paths.json`
|
|
28
|
+
- **Base URL**: `https://raw.githubusercontent.com/ThatOpen/engine_fragment/refs/heads/main/`
|