@@ -20,10 +20,12 @@ class IndoorControl implements IControl {
2020 _container : HTMLElement | null ;
2121 _model : IndoorControlModel | null ;
2222 _visibleFloorStart : number ;
23+ _lastSelectedFloorId : string | null ;
2324
2425 constructor ( ) {
25- bindAll ( [ '_onIndoorUpdate' , '_onStyleData' , '_scrollUp' , '_scrollDown' ] , this ) ;
26+ bindAll ( [ '_onIndoorUpdate' , '_onStyleData' , '_scrollUp' , '_scrollDown' , '_toggleIndoor' ] , this ) ;
2627 this . _visibleFloorStart = 0 ;
28+ this . _lastSelectedFloorId = null ;
2729 }
2830 onAdd ( map : Map ) : HTMLElement {
2931 this . _map = map ;
@@ -70,7 +72,7 @@ class IndoorControl implements IControl {
7072 return 'top-right' ;
7173 }
7274 _onIndoorUpdate ( model : IndoorControlModel | null ) {
73- if ( ! model || ! model . floors ) {
75+ if ( ! model || ! model . floors || model . floors . length === 0 ) {
7476 this . _model = model ;
7577 this . _container . style . display = 'none' ;
7678 return ;
@@ -83,17 +85,45 @@ class IndoorControl implements IControl {
8385
8486 if ( floorsChanged ) {
8587 this . _visibleFloorStart = 0 ;
88+ this . _lastSelectedFloorId = null ;
8689 }
8790
8891 if ( model . selectedFloorId ) {
89- const selectedIndex = model . floors . findIndex ( f => f . id === model . selectedFloorId ) ;
90- if ( selectedIndex !== - 1 ) {
91- if ( selectedIndex < this . _visibleFloorStart ) {
92- this . _visibleFloorStart = selectedIndex ;
93- } else if ( selectedIndex >= this . _visibleFloorStart + VISIBLE_FLOORS ) {
94- this . _visibleFloorStart = selectedIndex - ( VISIBLE_FLOORS - 1 ) ;
92+ const selectionChanged = model . selectedFloorId !== this . _lastSelectedFloorId ;
93+
94+ if ( selectionChanged || floorsChanged ) {
95+ const selectedIndex = model . floors . findIndex ( f => f . id === model . selectedFloorId ) ;
96+ if ( selectedIndex !== - 1 ) {
97+ const totalFloors = model . floors . length ;
98+ let minVisible , maxVisible ;
99+
100+ if ( totalFloors <= VISIBLE_FLOORS + 2 ) {
101+ minVisible = 0 ;
102+ maxVisible = totalFloors - 1 ;
103+ } else {
104+ const isAtTop = this . _visibleFloorStart === 0 ;
105+ const isAtBottom = this . _visibleFloorStart >= totalFloors - VISIBLE_FLOORS ;
106+
107+ if ( isAtTop ) {
108+ minVisible = 0 ;
109+ maxVisible = VISIBLE_FLOORS ;
110+ } else if ( isAtBottom ) {
111+ minVisible = totalFloors - VISIBLE_FLOORS - 1 ;
112+ maxVisible = totalFloors - 1 ;
113+ } else {
114+ minVisible = this . _visibleFloorStart ;
115+ maxVisible = this . _visibleFloorStart + VISIBLE_FLOORS - 1 ;
116+ }
117+ }
118+
119+ if ( selectedIndex < minVisible ) {
120+ this . _visibleFloorStart = selectedIndex ;
121+ } else if ( selectedIndex > maxVisible ) {
122+ this . _visibleFloorStart = selectedIndex - ( VISIBLE_FLOORS - 1 ) ;
123+ }
95124 }
96125 }
126+ this . _lastSelectedFloorId = model . selectedFloorId ;
97127 }
98128
99129 this . _render ( ) ;
@@ -103,58 +133,129 @@ class IndoorControl implements IControl {
103133 if ( ! this . _container || ! this . _model || ! this . _model . floors ) return ;
104134
105135 this . _container . innerHTML = '' ;
136+
137+ // Add Toggle Button
138+ const toggleButton = this . _createButton ( 'mapboxgl-ctrl-indoor-toggle' , this . _toggleIndoor ) ;
139+ const toggleIcon = DOM . create ( 'span' , 'mapboxgl-ctrl-icon' , toggleButton ) ;
140+ toggleIcon . setAttribute ( 'aria-hidden' , 'true' ) ;
141+
142+ if ( ! this . _model . activeFloorsVisible ) {
143+ toggleButton . classList . add ( 'mapboxgl-ctrl-level-button-selected' ) ;
144+ }
145+ this . _container . appendChild ( toggleButton ) ;
146+
106147 const floors = this . _model . floors ;
107148 const totalFloors = floors . length ;
108149
109- if ( totalFloors > VISIBLE_FLOORS ) {
150+ // If we can fit all floors in the space of (Arrow + 3 Floors + Arrow = 5 slots), do it.
151+ if ( totalFloors <= VISIBLE_FLOORS + 2 ) {
152+ floors . forEach ( floor => this . _createFloorButton ( floor ) ) ;
153+ return ;
154+ }
155+
156+ const isAtTop = this . _visibleFloorStart === 0 ;
157+ const isAtBottom = this . _visibleFloorStart >= totalFloors - VISIBLE_FLOORS ;
158+
159+ // Top Button
160+ if ( isAtTop ) {
161+ this . _createFloorButton ( floors [ 0 ] ) ;
162+ } else {
110163 const upButton = this . _createButton ( 'mapboxgl-ctrl-arrow-up' , this . _scrollUp ) ;
111- if ( this . _visibleFloorStart === 0 ) {
112- upButton . disabled = true ;
113- }
114164 DOM . create ( 'span' , 'mapboxgl-ctrl-icon' , upButton ) . setAttribute ( 'aria-hidden' , 'true' ) ;
115165 this . _container . appendChild ( upButton ) ;
116166 }
117167
118- const visibleFloors = floors . slice ( this . _visibleFloorStart , this . _visibleFloorStart + VISIBLE_FLOORS ) ;
119- visibleFloors . forEach ( floor => {
120- const levelButton = this . _createButton ( 'mapboxgl-ctrl-level-button' , ( ) => {
121- if ( this . _model && this . _model . selectedFloorId === floor . id ) return ;
122- if ( this . _map ) {
123- this . _map . _selectIndoorFloor ( floor . id ) ;
168+ // Middle Floors
169+ let middleFloors : { id : string ; name : string ; zIndex : number } [ ] = [ ] ;
170+ if ( isAtTop ) {
171+ middleFloors = floors . slice ( 1 , 1 + VISIBLE_FLOORS ) ;
172+ } else if ( isAtBottom ) {
173+ middleFloors = floors . slice ( this . _visibleFloorStart - 1 , this . _visibleFloorStart + VISIBLE_FLOORS - 1 ) ;
174+ } else {
175+ middleFloors = floors . slice ( this . _visibleFloorStart , this . _visibleFloorStart + VISIBLE_FLOORS ) ;
176+ }
177+
178+ middleFloors . forEach ( floor => this . _createFloorButton ( floor ) ) ;
179+
180+ // Bottom Button
181+ if ( isAtBottom ) {
182+ this . _createFloorButton ( floors [ totalFloors - 1 ] ) ;
183+ } else {
184+ const downButton = this . _createButton ( 'mapboxgl-ctrl-arrow-down' , this . _scrollDown ) ;
185+ DOM . create ( 'span' , 'mapboxgl-ctrl-icon' , downButton ) . setAttribute ( 'aria-hidden' , 'true' ) ;
186+ this . _container . appendChild ( downButton ) ;
187+ }
188+ }
189+
190+ _createFloorButton ( floor : { id : string ; name : string ; zIndex : number } ) {
191+ const levelButton = this . _createButton ( 'mapboxgl-ctrl-level-button' , ( ) => {
192+ const floorId = floor . id ;
193+ // If the floor is already selected, do nothing.
194+ if ( this . _model && this . _model . selectedFloorId === floorId && this . _model . activeFloorsVisible ) return ;
195+
196+ if ( this . _map ) {
197+ if ( this . _model && ! this . _model . activeFloorsVisible && this . _map . style && this . _map . style . indoorManager ) {
198+ this . _map . style . indoorManager . setActiveFloorsVisibility ( true ) ;
124199 }
125- } ) ;
126- const floorName = ( floor . name || '' ) . trim ( ) ;
127- const zIndexText = floor . zIndex . toString ( ) ;
128- const buttonTitle = floorName ? Array . from ( floorName ) . slice ( 0 , 3 ) . join ( '' ) : zIndexText ;
129- this . _setButtonTitle ( levelButton , buttonTitle ) ;
130-
131- if ( this . _model && floor . id === this . _model . selectedFloorId ) {
132- levelButton . classList . add ( 'mapboxgl-ctrl-level-button-selected' ) ;
200+ this . _map . _selectIndoorFloor ( floorId ) ;
133201 }
134- this . _container . appendChild ( levelButton ) ;
135202 } ) ;
203+ const floorName = ( floor . name || '' ) . trim ( ) ;
204+ const zIndexText = floor . zIndex . toString ( ) ;
205+ const buttonTitle = floorName ? Array . from ( floorName ) . slice ( 0 , 3 ) . join ( '' ) : zIndexText ;
206+ this . _setButtonTitle ( levelButton , buttonTitle ) ;
136207
137- if ( totalFloors > VISIBLE_FLOORS ) {
138- const downButton = this . _createButton ( 'mapboxgl-ctrl-arrow-down' , this . _scrollDown ) ;
139- if ( this . _visibleFloorStart + VISIBLE_FLOORS >= totalFloors ) {
140- downButton . disabled = true ;
208+ if ( this . _model && this . _model . activeFloorsVisible && floor . id === this . _model . selectedFloorId ) {
209+ levelButton . classList . add ( 'mapboxgl-ctrl-level-button-selected' ) ;
210+ }
211+ if ( this . _container ) {
212+ this . _container . appendChild ( levelButton ) ;
213+ }
214+ }
215+
216+ _toggleIndoor ( ) {
217+ if ( this . _map && this . _map . style && this . _map . style . indoorManager && this . _model ) {
218+ if ( this . _model . activeFloorsVisible ) {
219+ this . _map . style . indoorManager . setActiveFloorsVisibility ( false ) ;
141220 }
142- DOM . create ( 'span' , 'mapboxgl-ctrl-icon' , downButton ) . setAttribute ( 'aria-hidden' , 'true' ) ;
143- this . _container . appendChild ( downButton ) ;
144221 }
145222 }
146223
147224 _scrollUp ( ) {
148225 if ( this . _visibleFloorStart > 0 ) {
149226 this . _visibleFloorStart -- ;
227+ if ( this . _visibleFloorStart === 1 ) {
228+ this . _visibleFloorStart = 0 ;
229+ }
230+ const floors = ( this . _model && this . _model . floors ) || [ ] ;
231+ if ( floors . length > VISIBLE_FLOORS ) {
232+ const maxStart = floors . length - VISIBLE_FLOORS ;
233+ if ( this . _visibleFloorStart === maxStart - 1 ) {
234+ this . _visibleFloorStart = maxStart - 2 ;
235+ }
236+ }
150237 this . _render ( ) ;
151238 }
152239 }
153240
154241 _scrollDown ( ) {
155- if ( this . _model && this . _model . floors && this . _visibleFloorStart + VISIBLE_FLOORS < this . _model . floors . length ) {
156- this . _visibleFloorStart ++ ;
157- this . _render ( ) ;
242+ if ( this . _model && this . _model . floors ) {
243+ const maxStart = this . _model . floors . length - VISIBLE_FLOORS ;
244+ if ( this . _visibleFloorStart < maxStart ) {
245+ this . _visibleFloorStart ++ ;
246+ if ( this . _visibleFloorStart === 1 ) {
247+ this . _visibleFloorStart = 2 ;
248+ }
249+ if ( this . _visibleFloorStart === maxStart - 1 ) {
250+ this . _visibleFloorStart = maxStart ;
251+ }
252+
253+ if ( this . _visibleFloorStart > maxStart ) {
254+ this . _visibleFloorStart = maxStart ;
255+ }
256+
257+ this . _render ( ) ;
258+ }
158259 }
159260 }
160261}
0 commit comments