1
1
// eslint-disable-next-line import/no-internal-modules
2
- import type { IDisposable , Nullable , Scene } from "core/index" ;
2
+ import type { IReadonlyObservable , Nullable , Scene } from "core/index" ;
3
3
4
4
import type { TreeItemValue , TreeOpenChangeData , TreeOpenChangeEvent } from "@fluentui/react-components" ;
5
5
import type { ComponentType , FunctionComponent } from "react" ;
6
6
7
- import { Body1 , Body1Strong , Button , FlatTree , FlatTreeItem , makeStyles , tokens , ToggleButton , Tooltip , TreeItemLayout } from "@fluentui/react-components" ;
7
+ import { Body1 , Body1Strong , Button , FlatTree , FlatTreeItem , makeStyles , ToggleButton , tokens , Tooltip , TreeItemLayout } from "@fluentui/react-components" ;
8
8
import { VirtualizerScrollView } from "@fluentui/react-components/unstable" ;
9
+ import { MoviesAndTvRegular } from "@fluentui/react-icons" ;
9
10
10
11
import { useCallback , useEffect , useMemo , useState } from "react" ;
11
12
import { TraverseGraph } from "../../misc/graphUtils" ;
@@ -47,9 +48,14 @@ export type SceneExplorerSection<T extends EntityBase> = Readonly<{
47
48
entityIcon ?: ComponentType < { entity : T } > ;
48
49
49
50
/**
50
- * A function that watches for changes in the scene and calls the provided callbacks when entities are added or removed .
51
+ * A function that returns an array of observables for when entities are added to the scene .
51
52
*/
52
- watch : ( scene : Scene , onAdded : ( entity : T ) => void , onRemoved : ( entity : T ) => void ) => IDisposable ;
53
+ getEntityAddedObservables : ( scene : Scene ) => readonly IReadonlyObservable < T > [ ] ;
54
+
55
+ /**
56
+ * A function that returns an array of observables for when entities are removed from the scene.
57
+ */
58
+ getEntityRemovedObservables : ( scene : Scene ) => readonly IReadonlyObservable < T > [ ] ;
53
59
} > ;
54
60
55
61
type EntityCommandBase < T extends EntityBase > = Readonly < {
@@ -106,6 +112,7 @@ type ToggleCommand<T extends EntityBase> = EntityCommandBase<T> &
106
112
export type SceneExplorerEntityCommand < T extends EntityBase > = ActionCommand < T > | ToggleCommand < T > ;
107
113
108
114
type TreeItemData =
115
+ | { type : "scene" ; scene : Scene }
109
116
| {
110
117
type : "section" ;
111
118
sectionName : string ;
@@ -130,11 +137,14 @@ const useStyles = makeStyles({
130
137
flexDirection : "column" ,
131
138
} ,
132
139
tree : {
133
- margin : tokens . spacingHorizontalXS ,
140
+ margin : ` ${ tokens . spacingVerticalS } ${ tokens . spacingHorizontalM } ` ,
134
141
rowGap : 0 ,
135
142
overflow : "hidden" ,
136
143
flex : 1 ,
137
144
} ,
145
+ sceneTreeItemLayout : {
146
+ padding : 0 ,
147
+ } ,
138
148
} ) ;
139
149
140
150
const ActionCommand : FunctionComponent < { command : ActionCommand < EntityBase > ; entity : EntityBase ; scene : Scene } > = ( props ) => {
@@ -210,11 +220,15 @@ export const SceneExplorer: FunctionComponent<{
210
220
}
211
221
} ;
212
222
213
- const watchTokens = sections . map ( ( section ) => section . watch ( scene , onSceneItemAdded , onSceneItemRemoved ) ) ;
223
+ const addObservers = sections . flatMap ( ( section ) => section . getEntityAddedObservables ( scene ) . map ( ( observable ) => observable . add ( onSceneItemAdded ) ) ) ;
224
+ const removeObservers = sections . flatMap ( ( section ) => section . getEntityRemovedObservables ( scene ) . map ( ( observable ) => observable . add ( onSceneItemRemoved ) ) ) ;
214
225
215
226
return ( ) => {
216
- for ( const token of watchTokens ) {
217
- token . dispose ( ) ;
227
+ for ( const observer of addObservers ) {
228
+ observer . remove ( ) ;
229
+ }
230
+ for ( const observer of removeObservers ) {
231
+ observer . remove ( ) ;
218
232
}
219
233
} ;
220
234
} , [ sections , openItems ] ) ;
@@ -223,6 +237,11 @@ export const SceneExplorer: FunctionComponent<{
223
237
const visibleItems : TreeItemData [ ] = [ ] ;
224
238
const entityParents = new Map < EntityBase , EntityBase > ( ) ;
225
239
240
+ visibleItems . push ( {
241
+ type : "scene" ,
242
+ scene : scene ,
243
+ } ) ;
244
+
226
245
for ( const section of sections ) {
227
246
visibleItems . push ( {
228
247
type : "section" ,
@@ -283,7 +302,30 @@ export const SceneExplorer: FunctionComponent<{
283
302
{ ( index : number ) => {
284
303
const item = visibleItems [ index ] ;
285
304
286
- if ( item . type === "section" ) {
305
+ if ( item . type === "scene" ) {
306
+ return (
307
+ < FlatTreeItem
308
+ key = "scene"
309
+ value = "scene"
310
+ itemType = "leaf"
311
+ parentValue = { undefined }
312
+ aria-level = { 1 }
313
+ aria-setsize = { 1 }
314
+ aria-posinset = { 1 }
315
+ onClick = { ( ) => setSelectedEntity ?.( scene ) }
316
+ >
317
+ < TreeItemLayout
318
+ iconBefore = { < MoviesAndTvRegular /> }
319
+ className = { classes . sceneTreeItemLayout }
320
+ style = { scene === selectedEntity ? { backgroundColor : tokens . colorNeutralBackground1Selected } : undefined }
321
+ >
322
+ < Body1Strong wrap = { false } truncate >
323
+ Scene
324
+ </ Body1Strong >
325
+ </ TreeItemLayout >
326
+ </ FlatTreeItem >
327
+ ) ;
328
+ } else if ( item . type === "section" ) {
287
329
return (
288
330
< FlatTreeItem
289
331
key = { item . sectionName }
0 commit comments