@thatopen/services 0.0.2 → 0.0.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.
Files changed (63) hide show
  1. package/AGENTS.md +86 -76
  2. package/dist/cli.js +10 -1
  3. package/docs/access-backend-data.md +19 -19
  4. package/docs/app-architecture.md +94 -94
  5. package/docs/app-layout.md +139 -139
  6. package/docs/app-wiring.md +123 -123
  7. package/docs/bim-components/coding-conventions.md +128 -128
  8. package/docs/bim-components/element-collections.md +69 -69
  9. package/docs/bim-components/expose-events.md +84 -84
  10. package/docs/bim-components/library-examples.md +28 -28
  11. package/docs/bim-components/observable-collections.md +83 -83
  12. package/docs/bim-components/overview.md +160 -160
  13. package/docs/bim-components/per-frame-updates.md +20 -20
  14. package/docs/bim-components/save-and-restore-state.md +21 -21
  15. package/docs/bim-components/setup-and-cleanup.md +64 -64
  16. package/docs/bim-components/type-conventions.md +49 -49
  17. package/docs/bim-components/user-driven-object-creation.md +56 -56
  18. package/docs/cli-setup.md +54 -54
  19. package/docs/cloud-components.md +74 -74
  20. package/docs/connect-logic-to-ui.md +113 -113
  21. package/docs/previewing.md +45 -45
  22. package/docs/publishing.md +35 -35
  23. package/docs/scaffolding.md +71 -54
  24. package/docs/ui-components/async-actions.md +18 -18
  25. package/docs/ui-components/confirmation-dialog.md +39 -39
  26. package/docs/ui-components/data-table.md +185 -185
  27. package/docs/ui-components/display-text.md +39 -39
  28. package/docs/ui-components/inline-form.md +44 -44
  29. package/docs/ui-components/library-examples.md +24 -24
  30. package/docs/ui-components/overview.md +194 -194
  31. package/docs/ui-components/rendering-patterns.md +129 -129
  32. package/docs/ui-components/sections-layout.md +123 -123
  33. package/docs/update-grid-elements.md +46 -46
  34. package/docs/using-colors.md +32 -32
  35. package/docs/using-icons.md +37 -37
  36. package/package.json +2 -2
  37. package/src/cli/templates/bim/CONTEXT.md +1 -4
  38. package/src/cli/templates/bim/package.json +0 -1
  39. package/src/cli/templates/bim/src/main.ts +1 -2
  40. package/src/cli/templates/bim-beta/CONTEXT.md +241 -0
  41. package/src/cli/templates/bim-beta/package.json +25 -0
  42. package/src/cli/templates/bim-beta/src/app.ts +16 -0
  43. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/index.ts +91 -0
  44. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/index.ts +1 -0
  45. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/types.ts +5 -0
  46. package/src/cli/templates/bim-beta/src/bim-components/index.ts +1 -0
  47. package/src/cli/templates/bim-beta/src/globals.ts +1 -0
  48. package/src/cli/templates/bim-beta/src/main.ts +89 -0
  49. package/src/cli/templates/bim-beta/src/setups/cloud-runner.ts +13 -0
  50. package/src/cli/templates/bim-beta/src/setups/index.ts +3 -0
  51. package/src/cli/templates/bim-beta/src/setups/ui-manager.ts +27 -0
  52. package/src/cli/templates/bim-beta/src/setups/viewports-manager.ts +22 -0
  53. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/index.ts +26 -0
  54. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/index.ts +1 -0
  55. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/types.ts +15 -0
  56. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/index.ts +37 -0
  57. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/index.ts +1 -0
  58. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/types.ts +14 -0
  59. package/src/cli/templates/bim-beta/src/ui-components/index.ts +2 -0
  60. package/src/cli/templates/cloud/CONTEXT.md +205 -205
  61. package/src/cli/templates/cloud/src/main.ts +70 -70
  62. package/src/cli/templates/shared/AGENTS.md +30 -23
  63. package/src/cli/templates/shared/CLAUDE.md +1 -1
@@ -1,194 +1,194 @@
1
- # UI Component Creation
2
-
3
- Guides and best practices for creating UI components with That Open Engine (BUI/OBC) — panels, tables, dropdowns, buttons, inputs, or any visual element.
4
-
5
- ## Working mode
6
-
7
- Before doing anything else, **read [`./library-examples.md`](./library-examples.md) and fetch the `paths.json` for `engine_ui-components` to understand what BUI components are already available.**
8
-
9
- Only after that, **propose an implementation plan and wait for the user's approval.**
10
-
11
- The proposal must describe:
12
- - Which templates will be created or modified
13
- - What state (`State`) each template will need
14
- - Whether any template needs an `onInstanceCreated` callback
15
- - Whether it composes other existing templates
16
-
17
- Only proceed with changes after the user explicitly confirms the plan. If the scope is unclear, ask first — do not assume.
18
-
19
- ---
20
-
21
- ## Creating a UI Component
22
-
23
- ### Step 1 — Define the component name
24
-
25
- The component name is the source of truth. Everything else derives from it.
26
-
27
- Given a name like *Files Section* or *Models List*, the full naming cascade is:
28
-
29
- | Artifact | Convention | Example |
30
- |---|---|---|
31
- | Folder | kebab-case | `files-section/`, `models-list/` |
32
- | Component identifier | camelCase | `filesSection`, `modelsList` |
33
- | Types prefix | PascalCase | `FilesSection`, `ModelsList` |
34
- | Template function | `{camelCase}Template` | `filesSectionTemplate` |
35
- | Callback | `on{PascalCase}Created` | `onFilesSectionCreated` |
36
-
37
- The identifier suffix describes what the component *is* from the consumer's perspective — typically the BUI element name:
38
-
39
- | Identifier | Root element |
40
- |---|---|
41
- | `filesSection` | `<bim-panel-section>` |
42
- | `collisionsTable` | `<bim-table>` |
43
- | `modelsDropdown` | `<bim-dropdown>` |
44
- | `actionsToolbar` | `<div>` |
45
-
46
- > **Exception:** `<bim-panel-section>` uses the suffix `*Section`, not `*PanelSection`.
47
-
48
- ---
49
-
50
- ### Step 2 — Create the file structure
51
-
52
- Each component lives in its own folder:
53
-
54
- ```
55
- files-section/
56
- index.ts → template, onInstanceCreated (if needed), re-exports ./src
57
- src/
58
- index.ts → re-exports types and support files
59
- types.ts → state types and any supporting types
60
- ```
61
-
62
- The `src/` subfolder holds types and any support files the template needs — helpers, constants, sub-types, etc. If `index.ts` is growing too long, move the extra logic into `src/`.
63
-
64
- ---
65
-
66
- ### Step 3 — Define types in `src/types.ts`
67
-
68
- Every component needs at minimum these types:
69
-
70
- ```ts
71
- import * as OBC from "@thatopen/components"
72
- import * as BUI from "@thatopen/ui"
73
-
74
- export interface FilesSectionState {
75
- components: OBC.Components
76
- // ...additional state
77
- }
78
-
79
- export type FilesSectionComponent = BUI.StatefullComponent<FilesSectionState>
80
- ```
81
-
82
- - **`{ComponentName}State`** — the reactive state passed to the template on every render. Must always include `components: OBC.Components`.
83
- - **`{ComponentName}Component`** — type alias for `BUI.StatefullComponent<{ComponentName}State>`. Used to type the template function.
84
-
85
- Additional types depend on the component. For example, tables also need `{ComponentName}Data` — which must be a `type` alias, not an `interface`, as `BUI.Table<T>` requires a type alias:
86
-
87
- ```ts
88
- // ✗ Avoid — interface does not satisfy BUI.Table<T>
89
- export interface MyTableData { Name: string }
90
-
91
- // ✓ Correct
92
- export type MyTableData = { Name: string }
93
- ```
94
-
95
- #### `components` is the only entry point to the engine
96
-
97
- Inside a template, any engine component must be accessed via `components.get()`. State must never receive component instances directly:
98
-
99
- ```ts
100
- // ✗ Avoid — receiving a component instance directly
101
- const myTemplate = (state: { highlighter: OBF.Highlighter }) => { ... }
102
-
103
- // ✓ Correct — access everything through components
104
- const myTemplate = ({ components }: FilesSectionState) => {
105
- const highlighter = components.get(OBF.Highlighter)
106
- }
107
- ```
108
-
109
- #### How much logic belongs in a template?
110
-
111
- In the ideal case, a template is a thin facade: it reads state from BIM components via `components.get()` and triggers actions on them — nothing more.
112
-
113
- In practice, some UI-level logic is fine to keep in the template:
114
-
115
- - **Local coordination** — passing data from one sub-component to another within the same section
116
- - **Transient UI state** — loading flags, intermediate form values before the user confirms
117
- - **Orchestration** — calling several BIM components in sequence for a single user action
118
-
119
- What doesn't belong in a template is **domain logic**: calculations, data transformations, business rules. If a template starts computing things beyond what's needed to render, that logic belongs in a BIM component instead.
120
-
121
- The guiding question: *does this logic exist because the UI needs it, or because the domain requires it?* If the domain requires it, move it to a BIM component. See [`../connect-logic-to-ui.md`](../connect-logic-to-ui.md) for how UI templates connect to BIM components.
122
-
123
- ---
124
-
125
- ### Step 4 — Implement the template in `index.ts`
126
-
127
- ### Rules
128
-
129
- - **Always prefer BUI components over native HTML elements.** Before using `<select>`, `<input>`, `<button>`, `<dialog>`, etc., use the `paths.json` descriptions fetched in the working mode step to identify what's available. If you need the full list of available elements, fetch https://raw.githubusercontent.com/ThatOpen/engine_ui-components/refs/heads/main/packages/core/src/components/index.ts. Before implementing any BUI component, check [`./library-examples.md`](./library-examples.md) for an official example of the element you're working with — it's the best starting point for correct usage patterns. See [`./rendering-patterns.md`](./rendering-patterns.md) for general rendering patterns. For displaying and updating text dynamically, see [`./display-text.md`](./display-text.md).
130
- - **Never use `<table>`.** The only acceptable element for tabular data is `<bim-table>`. A `<bim-table>` must always be its own standalone UI component — never defined inline inside another template. The consuming template instantiates the table component and composes it in. See [`./data-table.md`](./data-table.md) for how to build a table component.
131
- - **For async actions** (loading states, async button handlers, etc.), see [`./async-actions.md`](./async-actions.md). **Before triggering a destructive or irreversible action**, always ask the user to confirm — see [`./confirmation-dialog.md`](./confirmation-dialog.md).
132
- - **Do not configure element properties inside the template** that a consumer might want to override after instantiation — they will be reset on every render. Use `onInstanceCreated` instead.
133
- - **Never nest sections.** If the user asks to group content, create independent templates — one per section. See [`./sections-layout.md`](./sections-layout.md).
134
-
135
- #### Template
136
-
137
- A `{ComponentName}Component` function that receives the current state and returns a `BUI.html` tagged template. Re-runs on every state update.
138
-
139
- ```ts
140
- export const filesSectionTemplate: FilesSectionComponent = ({ components }) => {
141
- return BUI.html`<bim-panel-section></bim-panel-section>`
142
- }
143
- ```
144
-
145
- #### `onInstanceCreated` — one-time setup
146
-
147
- Called once when an instance is created. Use it for imperative, one-time configuration: event listeners, fixed element properties, etc. For building a create/edit form anchored to a button, see [`./inline-form.md`](./inline-form.md).
148
-
149
- Receives the tuple `[element, updateFn, componentUtils]`:
150
-
151
- - `element` — the created HTML element
152
- - `updateFn` — triggers a re-render with a partial state update
153
- - `componentUtils` — provides `getCurrentState()`, which always returns fresh state. Use it inside closures to avoid stale state captures:
154
-
155
- ```ts
156
- export const onFilesSectionCreated = (
157
- [section, update, { getCurrentState }]: [
158
- BUI.PanelSection,
159
- BUI.UpdateFunction<FilesSectionState>,
160
- BUI.ComponentUtils<FilesSectionState>
161
- ]
162
- ) => {
163
- section.addEventListener("click", () => {
164
- const { components } = getCurrentState() // always fresh state
165
- const highlighter = components.get(OBF.Highlighter)
166
- // ...
167
- })
168
- }
169
- ```
170
-
171
- ---
172
-
173
- ### Step 5 — Export from the barrel
174
-
175
- Re-export the component from the project's barrel index so it's accessible from a single import point:
176
-
177
- ```ts
178
- export * from "./files-section"
179
- ```
180
-
181
- When planning a new UI component, **always consider breaking it into smaller, reusable templates** rather than building a single monolithic one. Discuss the granularity with the developer — but the default stance is to favor small, composable units.
182
-
183
- ---
184
-
185
- ## See also
186
-
187
- - [`./library-examples.md`](./library-examples.md) — Find official usage examples and discover what BUI provides
188
- - [`./data-table.md`](./data-table.md) — Display data in a table, load columns, group rows
189
- - [`./sections-layout.md`](./sections-layout.md) — Organize the layout of a panel section (zones, toolbar, status messages)
190
- - [`./inline-form.md`](./inline-form.md) — Create or edit an entity in a popup form anchored to a button
191
- - [`./confirmation-dialog.md`](./confirmation-dialog.md) — Ask for confirmation before a destructive or irreversible action
192
- - [`./async-actions.md`](./async-actions.md) — Handle an async action with loading state on a button
193
- - [`./rendering-patterns.md`](./rendering-patterns.md) — General rendering patterns (BUI.ref, requestUpdate, data-active)
194
- - [`./display-text.md`](./display-text.md) — Display text, update text dynamically, `bim-label` vs `.label`
1
+ # UI Component Creation
2
+
3
+ Guides and best practices for creating UI components with That Open Engine (BUI/OBC) — panels, tables, dropdowns, buttons, inputs, or any visual element.
4
+
5
+ ## Working mode
6
+
7
+ Before doing anything else, **read [`./library-examples.md`](./library-examples.md) and fetch the `paths.json` for `engine_ui-components` to understand what BUI components are already available.**
8
+
9
+ Only after that, **propose an implementation plan and wait for the user's approval.**
10
+
11
+ The proposal must describe:
12
+ - Which templates will be created or modified
13
+ - What state (`State`) each template will need
14
+ - Whether any template needs an `onInstanceCreated` callback
15
+ - Whether it composes other existing templates
16
+
17
+ Only proceed with changes after the user explicitly confirms the plan. If the scope is unclear, ask first — do not assume.
18
+
19
+ ---
20
+
21
+ ## Creating a UI Component
22
+
23
+ ### Step 1 — Define the component name
24
+
25
+ The component name is the source of truth. Everything else derives from it.
26
+
27
+ Given a name like *Files Section* or *Models List*, the full naming cascade is:
28
+
29
+ | Artifact | Convention | Example |
30
+ |---|---|---|
31
+ | Folder | kebab-case | `files-section/`, `models-list/` |
32
+ | Component identifier | camelCase | `filesSection`, `modelsList` |
33
+ | Types prefix | PascalCase | `FilesSection`, `ModelsList` |
34
+ | Template function | `{camelCase}Template` | `filesSectionTemplate` |
35
+ | Callback | `on{PascalCase}Created` | `onFilesSectionCreated` |
36
+
37
+ The identifier suffix describes what the component *is* from the consumer's perspective — typically the BUI element name:
38
+
39
+ | Identifier | Root element |
40
+ |---|---|
41
+ | `filesSection` | `<bim-panel-section>` |
42
+ | `collisionsTable` | `<bim-table>` |
43
+ | `modelsDropdown` | `<bim-dropdown>` |
44
+ | `actionsToolbar` | `<div>` |
45
+
46
+ > **Exception:** `<bim-panel-section>` uses the suffix `*Section`, not `*PanelSection`.
47
+
48
+ ---
49
+
50
+ ### Step 2 — Create the file structure
51
+
52
+ Each component lives in its own folder:
53
+
54
+ ```
55
+ files-section/
56
+ index.ts → template, onInstanceCreated (if needed), re-exports ./src
57
+ src/
58
+ index.ts → re-exports types and support files
59
+ types.ts → state types and any supporting types
60
+ ```
61
+
62
+ The `src/` subfolder holds types and any support files the template needs — helpers, constants, sub-types, etc. If `index.ts` is growing too long, move the extra logic into `src/`.
63
+
64
+ ---
65
+
66
+ ### Step 3 — Define types in `src/types.ts`
67
+
68
+ Every component needs at minimum these types:
69
+
70
+ ```ts
71
+ import * as OBC from "@thatopen/components"
72
+ import * as BUI from "@thatopen/ui"
73
+
74
+ export interface FilesSectionState {
75
+ components: OBC.Components
76
+ // ...additional state
77
+ }
78
+
79
+ export type FilesSectionComponent = BUI.StatefullComponent<FilesSectionState>
80
+ ```
81
+
82
+ - **`{ComponentName}State`** — the reactive state passed to the template on every render. Must always include `components: OBC.Components`.
83
+ - **`{ComponentName}Component`** — type alias for `BUI.StatefullComponent<{ComponentName}State>`. Used to type the template function.
84
+
85
+ Additional types depend on the component. For example, tables also need `{ComponentName}Data` — which must be a `type` alias, not an `interface`, as `BUI.Table<T>` requires a type alias:
86
+
87
+ ```ts
88
+ // ✗ Avoid — interface does not satisfy BUI.Table<T>
89
+ export interface MyTableData { Name: string }
90
+
91
+ // ✓ Correct
92
+ export type MyTableData = { Name: string }
93
+ ```
94
+
95
+ #### `components` is the only entry point to the engine
96
+
97
+ Inside a template, any engine component must be accessed via `components.get()`. State must never receive component instances directly:
98
+
99
+ ```ts
100
+ // ✗ Avoid — receiving a component instance directly
101
+ const myTemplate = (state: { highlighter: OBF.Highlighter }) => { ... }
102
+
103
+ // ✓ Correct — access everything through components
104
+ const myTemplate = ({ components }: FilesSectionState) => {
105
+ const highlighter = components.get(OBF.Highlighter)
106
+ }
107
+ ```
108
+
109
+ #### How much logic belongs in a template?
110
+
111
+ In the ideal case, a template is a thin facade: it reads state from BIM components via `components.get()` and triggers actions on them — nothing more.
112
+
113
+ In practice, some UI-level logic is fine to keep in the template:
114
+
115
+ - **Local coordination** — passing data from one sub-component to another within the same section
116
+ - **Transient UI state** — loading flags, intermediate form values before the user confirms
117
+ - **Orchestration** — calling several BIM components in sequence for a single user action
118
+
119
+ What doesn't belong in a template is **domain logic**: calculations, data transformations, business rules. If a template starts computing things beyond what's needed to render, that logic belongs in a BIM component instead.
120
+
121
+ The guiding question: *does this logic exist because the UI needs it, or because the domain requires it?* If the domain requires it, move it to a BIM component. See [`../connect-logic-to-ui.md`](../connect-logic-to-ui.md) for how UI templates connect to BIM components.
122
+
123
+ ---
124
+
125
+ ### Step 4 — Implement the template in `index.ts`
126
+
127
+ ### Rules
128
+
129
+ - **Always prefer BUI components over native HTML elements.** Before using `<select>`, `<input>`, `<button>`, `<dialog>`, etc., use the `paths.json` descriptions fetched in the working mode step to identify what's available. If you need the full list of available elements, fetch https://raw.githubusercontent.com/ThatOpen/engine_ui-components/refs/heads/main/packages/core/src/components/index.ts. Before implementing any BUI component, check [`./library-examples.md`](./library-examples.md) for an official example of the element you're working with — it's the best starting point for correct usage patterns. See [`./rendering-patterns.md`](./rendering-patterns.md) for general rendering patterns. For displaying and updating text dynamically, see [`./display-text.md`](./display-text.md).
130
+ - **Never use `<table>`.** The only acceptable element for tabular data is `<bim-table>`. A `<bim-table>` must always be its own standalone UI component — never defined inline inside another template. The consuming template instantiates the table component and composes it in. See [`./data-table.md`](./data-table.md) for how to build a table component.
131
+ - **For async actions** (loading states, async button handlers, etc.), see [`./async-actions.md`](./async-actions.md). **Before triggering a destructive or irreversible action**, always ask the user to confirm — see [`./confirmation-dialog.md`](./confirmation-dialog.md).
132
+ - **Do not configure element properties inside the template** that a consumer might want to override after instantiation — they will be reset on every render. Use `onInstanceCreated` instead.
133
+ - **Never nest sections.** If the user asks to group content, create independent templates — one per section. See [`./sections-layout.md`](./sections-layout.md).
134
+
135
+ #### Template
136
+
137
+ A `{ComponentName}Component` function that receives the current state and returns a `BUI.html` tagged template. Re-runs on every state update.
138
+
139
+ ```ts
140
+ export const filesSectionTemplate: FilesSectionComponent = ({ components }) => {
141
+ return BUI.html`<bim-panel-section></bim-panel-section>`
142
+ }
143
+ ```
144
+
145
+ #### `onInstanceCreated` — one-time setup
146
+
147
+ Called once when an instance is created. Use it for imperative, one-time configuration: event listeners, fixed element properties, etc. For building a create/edit form anchored to a button, see [`./inline-form.md`](./inline-form.md).
148
+
149
+ Receives the tuple `[element, updateFn, componentUtils]`:
150
+
151
+ - `element` — the created HTML element
152
+ - `updateFn` — triggers a re-render with a partial state update
153
+ - `componentUtils` — provides `getCurrentState()`, which always returns fresh state. Use it inside closures to avoid stale state captures:
154
+
155
+ ```ts
156
+ export const onFilesSectionCreated = (
157
+ [section, update, { getCurrentState }]: [
158
+ BUI.PanelSection,
159
+ BUI.UpdateFunction<FilesSectionState>,
160
+ BUI.ComponentUtils<FilesSectionState>
161
+ ]
162
+ ) => {
163
+ section.addEventListener("click", () => {
164
+ const { components } = getCurrentState() // always fresh state
165
+ const highlighter = components.get(OBF.Highlighter)
166
+ // ...
167
+ })
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ### Step 5 — Export from the barrel
174
+
175
+ Re-export the component from the project's barrel index so it's accessible from a single import point:
176
+
177
+ ```ts
178
+ export * from "./files-section"
179
+ ```
180
+
181
+ When planning a new UI component, **always consider breaking it into smaller, reusable templates** rather than building a single monolithic one. Discuss the granularity with the developer — but the default stance is to favor small, composable units.
182
+
183
+ ---
184
+
185
+ ## See also
186
+
187
+ - [`./library-examples.md`](./library-examples.md) — Find official usage examples and discover what BUI provides
188
+ - [`./data-table.md`](./data-table.md) — Display data in a table, load columns, group rows
189
+ - [`./sections-layout.md`](./sections-layout.md) — Organize the layout of a panel section (zones, toolbar, status messages)
190
+ - [`./inline-form.md`](./inline-form.md) — Create or edit an entity in a popup form anchored to a button
191
+ - [`./confirmation-dialog.md`](./confirmation-dialog.md) — Ask for confirmation before a destructive or irreversible action
192
+ - [`./async-actions.md`](./async-actions.md) — Handle an async action with loading state on a button
193
+ - [`./rendering-patterns.md`](./rendering-patterns.md) — General rendering patterns (BUI.ref, requestUpdate, data-active)
194
+ - [`./display-text.md`](./display-text.md) — Display text, update text dynamically, `bim-label` vs `.label`
@@ -1,129 +1,129 @@
1
- # Rendering Patterns
2
-
3
- Common patterns that apply across all BUI components.
4
-
5
- ## Lit and LitElement
6
-
7
- All components in `@thatopen/ui` are Web Components built with [Lit](https://lit.dev/) and extend `LitElement`. Since `BUI.html` is Lit's `html` tagged template, the full Lit template syntax applies — property bindings, boolean attributes, event listeners, directives, etc.
8
-
9
- ## requestUpdate()
10
-
11
- Reassigning a reactive property (like `table.data`) automatically schedules a re-render. **Mutating a property in place** does not — call `requestUpdate()` manually in those cases:
12
-
13
- ```ts
14
- // Reassignment → automatic update
15
- table.data = newData
16
-
17
- // In-place mutation → manual update required
18
- table.data.push(newRow)
19
- table.requestUpdate()
20
- ```
21
-
22
- ## BUI.ref
23
-
24
- `BUI.ref(callback)` provides an imperative reference to an element inside a `BUI.html` template. The callback is called once the element is mounted in the DOM:
25
-
26
- ```ts
27
- // Initialization
28
- const onTableCreated = (e?: Element) => {
29
- if (!e) return
30
- const table = e as BUI.Table<MyTableData>
31
- table.loadFunction = async () => { ... }
32
- table.loadData(true)
33
- }
34
-
35
- return BUI.html`<bim-table ${BUI.ref(onTableCreated)}></bim-table>`
36
- ```
37
-
38
- ```ts
39
- // Capture
40
- let input: BUI.TextInput | undefined
41
-
42
- const onInputCreated = (e?: Element) => {
43
- if (!(e instanceof BUI.TextInput)) return
44
- input = e
45
- }
46
-
47
- return BUI.html`
48
- <bim-text-input ${BUI.ref(onInputCreated)}></bim-text-input>
49
- <bim-button @click=${() => console.log(input?.value)}></bim-button>
50
- `
51
- ```
52
-
53
- ## Creating Elements
54
-
55
- When an element must be created imperatively outside of `BUI.html`, use `BUI.Component.create` instead of `document.createElement`:
56
-
57
- ```ts
58
- // ✗ Avoid
59
- const button = document.createElement("bim-button")
60
-
61
- // ✓ Correct
62
- const button = BUI.Component.create(() => BUI.html`<bim-button label="Click me"></bim-button>`)
63
- ```
64
-
65
- ## Visual State with data-active
66
-
67
- Use `toggleAttribute("data-active")` to reflect an element's active/inactive state visually:
68
-
69
- ```ts
70
- target.toggleAttribute("data-active") // toggle
71
- target.toggleAttribute("data-active", true) // force active
72
- target.toggleAttribute("data-active", false) // force inactive
73
- ```
74
-
75
- ## Event Handlers
76
-
77
- ```ts
78
- return BUI.html`
79
- <bim-text-input @input=${onSearch}></bim-text-input>
80
- <bim-button @click=${onClick}></bim-button>
81
- `
82
- ```
83
-
84
- ## BUI.Manager.newRandomId()
85
-
86
- Use when a template needs to associate DOM elements by ID. Since templates can be instantiated multiple times, hardcoded IDs would cause collisions:
87
-
88
- ```ts
89
- const inputId = BUI.Manager.newRandomId()
90
-
91
- return BUI.html`
92
- <label for=${inputId}>Name</label>
93
- <input id=${inputId} type="text" />
94
- `
95
- ```
96
-
97
- ## Triggering re-renders from inside a template (update)
98
-
99
- `BUI.StatefullComponent` receives an optional second parameter `update`:
100
-
101
- ```ts
102
- export const myTemplate: BUI.StatefullComponent<MyState> = (state, update) => {
103
- const onAdd = () => {
104
- doSomething()
105
- update() // re-render with same state
106
- update({ count: 5 }) // re-render with partial state change
107
- }
108
- }
109
- ```
110
-
111
- Avoid calling `update` in response to changes that themselves trigger another `update` — this causes an infinite render loop.
112
-
113
- ## Role-based rendering
114
-
115
- When content depends on a user role or any runtime condition, use an optional variable before the `return`:
116
-
117
- ```ts
118
- let actionArea: BUI.TemplateResult | undefined
119
- if (userHasPermission) {
120
- actionArea = BUI.html`<bim-button label="Run Action" @click=${onRun}></bim-button>`
121
- }
122
-
123
- return BUI.html`
124
- <bim-panel-section label="...">
125
- ${content}
126
- ${actionArea}
127
- </bim-panel-section>
128
- `
129
- ```
1
+ # Rendering Patterns
2
+
3
+ Common patterns that apply across all BUI components.
4
+
5
+ ## Lit and LitElement
6
+
7
+ All components in `@thatopen/ui` are Web Components built with [Lit](https://lit.dev/) and extend `LitElement`. Since `BUI.html` is Lit's `html` tagged template, the full Lit template syntax applies — property bindings, boolean attributes, event listeners, directives, etc.
8
+
9
+ ## requestUpdate()
10
+
11
+ Reassigning a reactive property (like `table.data`) automatically schedules a re-render. **Mutating a property in place** does not — call `requestUpdate()` manually in those cases:
12
+
13
+ ```ts
14
+ // Reassignment → automatic update
15
+ table.data = newData
16
+
17
+ // In-place mutation → manual update required
18
+ table.data.push(newRow)
19
+ table.requestUpdate()
20
+ ```
21
+
22
+ ## BUI.ref
23
+
24
+ `BUI.ref(callback)` provides an imperative reference to an element inside a `BUI.html` template. The callback is called once the element is mounted in the DOM:
25
+
26
+ ```ts
27
+ // Initialization
28
+ const onTableCreated = (e?: Element) => {
29
+ if (!e) return
30
+ const table = e as BUI.Table<MyTableData>
31
+ table.loadFunction = async () => { ... }
32
+ table.loadData(true)
33
+ }
34
+
35
+ return BUI.html`<bim-table ${BUI.ref(onTableCreated)}></bim-table>`
36
+ ```
37
+
38
+ ```ts
39
+ // Capture
40
+ let input: BUI.TextInput | undefined
41
+
42
+ const onInputCreated = (e?: Element) => {
43
+ if (!(e instanceof BUI.TextInput)) return
44
+ input = e
45
+ }
46
+
47
+ return BUI.html`
48
+ <bim-text-input ${BUI.ref(onInputCreated)}></bim-text-input>
49
+ <bim-button @click=${() => console.log(input?.value)}></bim-button>
50
+ `
51
+ ```
52
+
53
+ ## Creating Elements
54
+
55
+ When an element must be created imperatively outside of `BUI.html`, use `BUI.Component.create` instead of `document.createElement`:
56
+
57
+ ```ts
58
+ // ✗ Avoid
59
+ const button = document.createElement("bim-button")
60
+
61
+ // ✓ Correct
62
+ const button = BUI.Component.create(() => BUI.html`<bim-button label="Click me"></bim-button>`)
63
+ ```
64
+
65
+ ## Visual State with data-active
66
+
67
+ Use `toggleAttribute("data-active")` to reflect an element's active/inactive state visually:
68
+
69
+ ```ts
70
+ target.toggleAttribute("data-active") // toggle
71
+ target.toggleAttribute("data-active", true) // force active
72
+ target.toggleAttribute("data-active", false) // force inactive
73
+ ```
74
+
75
+ ## Event Handlers
76
+
77
+ ```ts
78
+ return BUI.html`
79
+ <bim-text-input @input=${onSearch}></bim-text-input>
80
+ <bim-button @click=${onClick}></bim-button>
81
+ `
82
+ ```
83
+
84
+ ## BUI.Manager.newRandomId()
85
+
86
+ Use when a template needs to associate DOM elements by ID. Since templates can be instantiated multiple times, hardcoded IDs would cause collisions:
87
+
88
+ ```ts
89
+ const inputId = BUI.Manager.newRandomId()
90
+
91
+ return BUI.html`
92
+ <label for=${inputId}>Name</label>
93
+ <input id=${inputId} type="text" />
94
+ `
95
+ ```
96
+
97
+ ## Triggering re-renders from inside a template (update)
98
+
99
+ `BUI.StatefullComponent` receives an optional second parameter `update`:
100
+
101
+ ```ts
102
+ export const myTemplate: BUI.StatefullComponent<MyState> = (state, update) => {
103
+ const onAdd = () => {
104
+ doSomething()
105
+ update() // re-render with same state
106
+ update({ count: 5 }) // re-render with partial state change
107
+ }
108
+ }
109
+ ```
110
+
111
+ Avoid calling `update` in response to changes that themselves trigger another `update` — this causes an infinite render loop.
112
+
113
+ ## Role-based rendering
114
+
115
+ When content depends on a user role or any runtime condition, use an optional variable before the `return`:
116
+
117
+ ```ts
118
+ let actionArea: BUI.TemplateResult | undefined
119
+ if (userHasPermission) {
120
+ actionArea = BUI.html`<bim-button label="Run Action" @click=${onRun}></bim-button>`
121
+ }
122
+
123
+ return BUI.html`
124
+ <bim-panel-section label="...">
125
+ ${content}
126
+ ${actionArea}
127
+ </bim-panel-section>
128
+ `
129
+ ```