Skip to content

Commit 260f351

Browse files
agviegasclaude
andcommitted
chore: update built-in components types
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2925813 commit 260f351

1 file changed

Lines changed: 225 additions & 5 deletions

File tree

src/built-in/index.ts

Lines changed: 225 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import type * as OBC from '@thatopen/components';
88
import type * as BUI from '@thatopen/ui';
99
import type * as THREE from 'three';
10-
type DataSet<_A = any, _B = any, _C = any, _D = any> = any;
11-
import type { EngineServicesClient, ProjectData } from '../';
10+
type DataMap<_A = any, _B = any, _C = any, _D = any> = any;
11+
import type { EngineServicesClient, Item, ProjectData } from '../';
1212

1313
export type ComponentSetup = (components: OBC.Components) => Promise<void> | void;
1414
export type ComponentSetups = {
@@ -118,6 +118,115 @@ export type AppManager = InstanceType<typeof _AppManager>;
118118
*/
119119
export const AppManager = { uuid: '2e32d873-02c9-421c-8743-d8a5ca6ad38a' } as typeof _AppManager & { uuid: '2e32d873-02c9-421c-8743-d8a5ca6ad38a' };
120120

121+
/**
122+
* Which action buttons to expose per file row. All default to `true`.
123+
*/
124+
export interface FileListActions {
125+
download?: boolean;
126+
rename?: boolean;
127+
delete?: boolean;
128+
}
129+
/**
130+
* Config passed to {@link FileList.create}.
131+
*/
132+
export interface FileListConfig {
133+
/** Services client used to list and mutate files. */
134+
client: EngineServicesClient;
135+
/** Optional folder ID to scope the list. If omitted, lists root files. */
136+
folderId?: string;
137+
/** Which action buttons to show per row. Defaults to all enabled. */
138+
actions?: FileListActions;
139+
/** Called when a file row is clicked (not an action button). */
140+
onFileClick?: (file: Item) => void;
141+
}
142+
/**
143+
* The result returned by {@link FileList.create}.
144+
*/
145+
export interface FileListInstance {
146+
/** The DOM element to mount somewhere in your layout. */
147+
element: HTMLElement;
148+
/** Reloads the list from the services client. */
149+
refresh: () => Promise<void>;
150+
/** Changes the folder scope and reloads. Pass `undefined` for the root. */
151+
setFolder: (folderId?: string) => Promise<void>;
152+
/** Removes the element and forgets the instance. */
153+
dispose: () => void;
154+
}
155+
156+
/**
157+
* Built-in component that renders a file list backed by the services client,
158+
* with built-in download / rename / delete actions.
159+
*
160+
* The backing data comes from {@link EngineServicesClient.listFiles}, so the
161+
* list stays in sync with the storage service. Each call to {@link create}
162+
* returns an independent instance with its own DOM element.
163+
*
164+
* @example
165+
* ```ts
166+
* const fileList = components.get(FileList);
167+
* const { element, refresh } = fileList.create({
168+
* client: app.client,
169+
* folderId: someFolderId,
170+
* });
171+
* container.appendChild(element);
172+
* ```
173+
*/
174+
declare class _FileList extends OBC.Component {
175+
static readonly uuid: "b0b5e2a2-0b3a-4a6b-8b1c-0b1c4a6b8b1c";
176+
enabled: boolean;
177+
readonly name = "FileList";
178+
private _instances;
179+
/**
180+
* Creates a new file-list instance. Returns the DOM element to mount plus a
181+
* `refresh` handle to reload the list, a `setFolder` to change scope, and a
182+
* `dispose` to remove it.
183+
*/
184+
create(config: FileListConfig): FileListInstance;
185+
/**
186+
* Removes every instance this manager has created.
187+
*/
188+
dispose(): void;
189+
}
190+
191+
/**
192+
* Built-in component that renders a file list backed by the services client,
193+
* with built-in download / rename / delete actions.
194+
*
195+
* The backing data comes from {@link EngineServicesClient.listFiles}, so the
196+
* list stays in sync with the storage service. Each call to {@link create}
197+
* returns an independent instance with its own DOM element.
198+
*
199+
* @example
200+
* ```ts
201+
* const fileList = components.get(FileList);
202+
* const { element, refresh } = fileList.create({
203+
* client: app.client,
204+
* folderId: someFolderId,
205+
* });
206+
* container.appendChild(element);
207+
* ```
208+
*/
209+
export type FileList = InstanceType<typeof _FileList>;
210+
/**
211+
* Built-in component that renders a file list backed by the services client,
212+
* with built-in download / rename / delete actions.
213+
*
214+
* The backing data comes from {@link EngineServicesClient.listFiles}, so the
215+
* list stays in sync with the storage service. Each call to {@link create}
216+
* returns an independent instance with its own DOM element.
217+
*
218+
* @example
219+
* ```ts
220+
* const fileList = components.get(FileList);
221+
* const { element, refresh } = fileList.create({
222+
* client: app.client,
223+
* folderId: someFolderId,
224+
* });
225+
* container.appendChild(element);
226+
* ```
227+
*/
228+
export const FileList = { uuid: 'b0b5e2a2-0b3a-4a6b-8b1c-0b1c4a6b8b1c' } as typeof _FileList & { uuid: 'b0b5e2a2-0b3a-4a6b-8b1c-0b1c4a6b8b1c' };
229+
121230
/**
122231
* A simple test component to validate the built-in component pipeline.
123232
* Replace this with real components once the infrastructure is verified.
@@ -150,6 +259,108 @@ export type HelloWorld = InstanceType<typeof _HelloWorld>;
150259
*/
151260
export const HelloWorld = { uuid: '2c4ae432-fc24-43e9-9783-0c960c674e96' } as typeof _HelloWorld & { uuid: '2c4ae432-fc24-43e9-9783-0c960c674e96' };
152261

262+
/**
263+
* Config passed to {@link TabbedNavigation.create}. All fields are optional.
264+
*/
265+
export interface TabbedNavigationConfig {
266+
/** Hide the text labels and show only icons. Defaults to `false`. */
267+
iconsOnly?: boolean;
268+
/**
269+
* Fallback icon used when a layout doesn't declare one in its definition.
270+
* Defaults to `"mdi:view-dashboard"`.
271+
*/
272+
fallbackIcon?: string;
273+
/** Called after the user switches to a different layout via the tabs. */
274+
onLayoutChange?: (layoutName: string) => void;
275+
}
276+
/**
277+
* The result returned by {@link TabbedNavigation.create}.
278+
*/
279+
export interface TabbedNavigationInstance {
280+
/** The tab-bar DOM element. Mount it inside an AppManager grid slot. */
281+
element: HTMLElement;
282+
/** Re-reads the grid's layouts and re-renders the tabs. Call after mutating `grid.layouts`. */
283+
refresh: () => void;
284+
/** Tears down listeners and removes the element. */
285+
dispose: () => void;
286+
}
287+
288+
/**
289+
* Built-in tabbed navigation for a platform app. Renders one tab per layout
290+
* declared on the `AppManager`'s grid, with the current layout highlighted.
291+
* Clicking a tab switches `grid.layout`, which swaps the visible area set.
292+
*
293+
* This is the default top-of-app navigation pattern the platform proposes —
294+
* Claude Code should use it by default when scaffolding apps with multiple
295+
* layouts, instead of hand-rolling a custom tab bar.
296+
*
297+
* Must be called after `AppManager.init()` has mounted the grid.
298+
*
299+
* @example
300+
* ```ts
301+
* const app = components.get(AppManager<MyApp>);
302+
* await app.init({ ... });
303+
* const tabs = components.get(TabbedNavigation);
304+
* const { element } = tabs.create();
305+
* // Mount `element` in a grid area that sits above the main content.
306+
* ```
307+
*/
308+
declare class _TabbedNavigation extends OBC.Component {
309+
static readonly uuid: "3c2a9f34-8b6c-4d1b-8f7a-1e4a5b2c9f08";
310+
enabled: boolean;
311+
readonly name = "TabbedNavigation";
312+
private _instances;
313+
/**
314+
* Creates a new tab-bar instance bound to the current `AppManager` grid.
315+
*/
316+
create(config?: TabbedNavigationConfig): TabbedNavigationInstance;
317+
/** Disposes every instance this manager has created. */
318+
dispose(): void;
319+
}
320+
321+
/**
322+
* Built-in tabbed navigation for a platform app. Renders one tab per layout
323+
* declared on the `AppManager`'s grid, with the current layout highlighted.
324+
* Clicking a tab switches `grid.layout`, which swaps the visible area set.
325+
*
326+
* This is the default top-of-app navigation pattern the platform proposes —
327+
* Claude Code should use it by default when scaffolding apps with multiple
328+
* layouts, instead of hand-rolling a custom tab bar.
329+
*
330+
* Must be called after `AppManager.init()` has mounted the grid.
331+
*
332+
* @example
333+
* ```ts
334+
* const app = components.get(AppManager<MyApp>);
335+
* await app.init({ ... });
336+
* const tabs = components.get(TabbedNavigation);
337+
* const { element } = tabs.create();
338+
* // Mount `element` in a grid area that sits above the main content.
339+
* ```
340+
*/
341+
export type TabbedNavigation = InstanceType<typeof _TabbedNavigation>;
342+
/**
343+
* Built-in tabbed navigation for a platform app. Renders one tab per layout
344+
* declared on the `AppManager`'s grid, with the current layout highlighted.
345+
* Clicking a tab switches `grid.layout`, which swaps the visible area set.
346+
*
347+
* This is the default top-of-app navigation pattern the platform proposes —
348+
* Claude Code should use it by default when scaffolding apps with multiple
349+
* layouts, instead of hand-rolling a custom tab bar.
350+
*
351+
* Must be called after `AppManager.init()` has mounted the grid.
352+
*
353+
* @example
354+
* ```ts
355+
* const app = components.get(AppManager<MyApp>);
356+
* await app.init({ ... });
357+
* const tabs = components.get(TabbedNavigation);
358+
* const { element } = tabs.create();
359+
* // Mount `element` in a grid area that sits above the main content.
360+
* ```
361+
*/
362+
export const TabbedNavigation = { uuid: '3c2a9f34-8b6c-4d1b-8f7a-1e4a5b2c9f08' } as typeof _TabbedNavigation & { uuid: '3c2a9f34-8b6c-4d1b-8f7a-1e4a5b2c9f08' };
363+
153364
export interface ColorsPaletteState {
154365
components: OBC.Components;
155366
onApply: (color: string, name?: string) => void | Promise<void>;
@@ -367,13 +578,16 @@ export declare class UIDataMap<T extends UICustomShape> {
367578
get<K extends keyof T & string>(key: K): UIFactoryMap<T>[K];
368579
get(key: keyof T & string): UIFactoryMap<T>[keyof T];
369580
}
581+
export type UIFactoryInstance<TElement extends HTMLElement, TState extends Record<string, any>> = [TElement, BUI.UpdateFunction<TState>, BUI.ComponentUtils<TState>];
370582
export declare abstract class UIFactory<TElement extends HTMLElement, TState extends Record<string, any>> {
371-
readonly instances: DataSet<[TElement, BUI.UpdateFunction<TState>, BUI.ComponentUtils<TState>]>;
583+
readonly instances: DataMap<string, UIFactoryInstance<TElement, TState>>;
372584
abstract readonly template: BUI.StatefullComponent<TState>;
373-
create(state: TState): [element: TElement, update: BUI.UpdateFunction<TState>, utils: BUI.ComponentUtils<TState>];
585+
create(state: TState, options?: {
586+
id?: string;
587+
}): UIFactoryInstance<TElement, TState>;
374588
updateInstances(state?: Partial<TState>): void;
375589
}
376-
export declare function createFactory<TEl extends HTMLElement, TState extends Record<string, any>>(template: BUI.StatefullComponent<TState>, onInstanceCreated?: (item: [TEl, BUI.UpdateFunction<TState>, BUI.ComponentUtils<TState>]) => void): UIFactory<TEl, TState>;
590+
export declare function createFactory<TEl extends HTMLElement, TState extends Record<string, any>>(template: BUI.StatefullComponent<TState>, onInstanceCreated?: (item: UIFactoryInstance<TEl, TState>) => void): UIFactory<TEl, TState>;
377591

378592
declare class _UIManager<TCustom extends UICustomShape = UICustomShape> extends OBC.Component {
379593
static readonly uuid: "234f1416-528d-452a-9cc9-a16c5239b2eb";
@@ -489,6 +703,12 @@ declare class _ViewportsManager extends OBC.Component {
489703
* model loading for the given world.
490704
*/
491705
private _initFragments;
706+
/**
707+
* Grows the camera's far plane so it always encompasses every loaded
708+
* model. Without this, large models clip against the default far plane
709+
* and disappear from view.
710+
*/
711+
private _expandCameraFar;
492712
}
493713

494714
/**

0 commit comments

Comments
 (0)