@@ -2,7 +2,8 @@ import * as DOM from '../../util/dom';
22import { bindAll } from '../../util/util' ;
33
44import type { Map , ControlPosition , IControl } from '../map' ;
5- import type { IndoorControlModel , IndoorControlFloor } from '../../style/indoor_data' ;
5+ import type { IndoorControlModel } from '../../style/indoor_data' ;
6+ const VISIBLE_FLOORS = 3 ;
67
78/**
89 * An `IndoorControl` control presents the map's indoor floors.
@@ -18,11 +19,12 @@ class IndoorControl implements IControl {
1819 _map : Map | null ;
1920 _container : HTMLElement | null ;
2021 _model : IndoorControlModel | null ;
22+ _visibleFloorStart : number ;
2123
2224 constructor ( ) {
23- bindAll ( [ '_onIndoorUpdate' , '_onStyleData' ] , this ) ;
25+ bindAll ( [ '_onIndoorUpdate' , '_onStyleData' , '_scrollUp' , '_scrollDown' ] , this ) ;
26+ this . _visibleFloorStart = 0 ;
2427 }
25-
2628 onAdd ( map : Map ) : HTMLElement {
2729 this . _map = map ;
2830 this . _container = DOM . create ( 'div' , 'mapboxgl-ctrl mapboxgl-ctrl-group' ) ;
@@ -31,11 +33,9 @@ class IndoorControl implements IControl {
3133 this . _updateConnection ( ) ;
3234 return this . _container ;
3335 }
34-
3536 _onStyleData ( ) {
3637 this . _updateConnection ( ) ;
3738 }
38-
3939 _updateConnection ( ) {
4040 if ( this . _map && this . _map . style && this . _map . style . indoorManager ) {
4141 const manager = this . _map . style . indoorManager ;
@@ -44,24 +44,16 @@ class IndoorControl implements IControl {
4444 this . _onIndoorUpdate ( manager . getControlState ( ) ) ;
4545 }
4646 }
47-
4847 _createButton ( className : string , fn : ( e : Event ) => unknown ) : HTMLButtonElement {
4948 const a = DOM . create ( 'button' , className , this . _container ) ;
5049 a . type = 'button' ;
5150 a . addEventListener ( 'click' , fn ) ;
5251 return a ;
5352 }
54-
55- _createSeparator ( ) : HTMLElement {
56- return DOM . create ( 'div' , 'mapboxgl-ctrl-separator' , this . _container ) ;
57- }
58-
5953 _setButtonTitle ( button : HTMLButtonElement , title : string ) {
60- if ( ! this . _map ) return ;
6154 button . setAttribute ( 'aria-label' , title ) ;
6255 button . textContent = title ;
6356 }
64-
6557 onRemove ( ) {
6658 if ( this . _container ) {
6759 this . _container . remove ( ) ;
@@ -74,11 +66,9 @@ class IndoorControl implements IControl {
7466 this . _map = null ;
7567 }
7668 }
77-
7869 getDefaultPosition ( ) : ControlPosition {
7970 return 'top-right' ;
8071 }
81-
8272 _onIndoorUpdate ( model : IndoorControlModel | null ) {
8373 if ( ! model || ! model . floors ) {
8474 this . _model = model ;
@@ -88,62 +78,84 @@ class IndoorControl implements IControl {
8878 const oldModel = this . _model ;
8979 this . _model = model ;
9080 this . _container . style . display = 'inline-block' ;
91- if ( oldModel ) {
92- Array . from ( this . _container . children ) . forEach ( child => child . remove ( ) ) ;
93- }
94- if ( model . floors . length > 0 ) {
95- this . addBuildingsToggleButton ( ) ;
96- this . addCurrentFloors ( model . floors , model . activeFloorsVisible ) ;
97- this . _updateBuildingsButtonState ( ) ;
81+
82+ const floorsChanged = ! oldModel || oldModel . floors . length !== model . floors . length || oldModel . floors . some ( ( f , i ) => f . id !== model . floors [ i ] . id ) ;
83+
84+ if ( floorsChanged ) {
85+ this . _visibleFloorStart = 0 ;
9886 }
99- }
10087
101- addBuildingsToggleButton ( ) {
102- const buildingsButton = this . _createButton ( 'mapboxgl-ctrl-buildings-toggle' , ( ) => {
103- const map = this . _map ;
104- if ( this . _model && map ) {
105- map . _setIndoorActiveFloorsVisibility ( ! this . _model . activeFloorsVisible ) ;
88+ 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 ) ;
95+ }
10696 }
107- } ) ;
108- DOM . create ( 'span' , `mapboxgl-ctrl-icon` , buildingsButton ) . setAttribute ( 'aria-hidden' , 'true' ) ;
109- buildingsButton . classList . add ( 'mapboxgl-ctrl-level-button' , 'mapboxgl-ctrl-buildings-toggle' ) ;
110- if ( this . _model && ! this . _model . activeFloorsVisible ) {
111- buildingsButton . classList . add ( 'mapboxgl-ctrl-level-button-selected' ) ;
11297 }
113- this . _container . append ( buildingsButton ) ;
98+
99+ this . _render ( ) ;
114100 }
115101
116- _updateBuildingsButtonState ( ) {
117- const buildingsButton = this . _container . querySelector ( '.mapboxgl-ctrl-buildings-toggle' ) ;
118- if ( buildingsButton && this . _model ) {
119- if ( ! this . _model . activeFloorsVisible ) {
120- buildingsButton . classList . add ( 'mapboxgl-ctrl-level-button-selected' ) ;
121- } else {
122- buildingsButton . classList . remove ( 'mapboxgl-ctrl-level-button-selected' ) ;
102+ _render ( ) {
103+ if ( ! this . _container || ! this . _model || ! this . _model . floors ) return ;
104+
105+ this . _container . innerHTML = '' ;
106+ const floors = this . _model . floors ;
107+ const totalFloors = floors . length ;
108+
109+ if ( totalFloors > VISIBLE_FLOORS ) {
110+ const upButton = this . _createButton ( 'mapboxgl-ctrl-arrow-up' , this . _scrollUp ) ;
111+ if ( this . _visibleFloorStart === 0 ) {
112+ upButton . disabled = true ;
123113 }
114+ DOM . create ( 'span' , 'mapboxgl-ctrl-icon' , upButton ) . setAttribute ( 'aria-hidden' , 'true' ) ;
115+ this . _container . appendChild ( upButton ) ;
124116 }
125- }
126117
127- addCurrentFloors ( floors : Array < IndoorControlFloor > , showSelectedFloor : boolean ) {
128- for ( let i = 0 ; i < floors . length ; i ++ ) {
129- const floor = floors [ i ] ;
118+ const visibleFloors = floors . slice ( this . _visibleFloorStart , this . _visibleFloorStart + VISIBLE_FLOORS ) ;
119+ visibleFloors . forEach ( floor => {
130120 const levelButton = this . _createButton ( 'mapboxgl-ctrl-level-button' , ( ) => {
131- this . _map . _selectIndoorFloor ( floor . id ) ;
121+ if ( this . _model && this . _model . selectedFloorId === floor . id ) return ;
122+ if ( this . _map ) {
123+ this . _map . _setIndoorActiveFloorsVisibility ( true ) ;
124+ this . _map . _selectIndoorFloor ( floor . id ) ;
125+ }
132126 } ) ;
133-
134127 const floorName = ( floor . name || '' ) . trim ( ) ;
135128 const zIndexText = floor . zIndex . toString ( ) ;
136129 const buttonTitle = floorName ? Array . from ( floorName ) . slice ( 0 , 3 ) . join ( '' ) : zIndexText ;
137130 this . _setButtonTitle ( levelButton , buttonTitle ) ;
138- if ( this . _model && floor . id === this . _model . selectedFloorId && showSelectedFloor ) {
131+
132+ if ( this . _model && floor . id === this . _model . selectedFloorId ) {
139133 levelButton . classList . add ( 'mapboxgl-ctrl-level-button-selected' ) ;
140134 }
141- this . _container . append ( levelButton ) ;
135+ this . _container . appendChild ( levelButton ) ;
136+ } ) ;
142137
143- // Add separator after each button except the last one
144- if ( i < floors . length - 1 ) {
145- this . _createSeparator ( ) ;
138+ if ( totalFloors > VISIBLE_FLOORS ) {
139+ const downButton = this . _createButton ( 'mapboxgl-ctrl-arrow-down' , this . _scrollDown ) ;
140+ if ( this . _visibleFloorStart + VISIBLE_FLOORS >= totalFloors ) {
141+ downButton . disabled = true ;
146142 }
143+ DOM . create ( 'span' , 'mapboxgl-ctrl-icon' , downButton ) . setAttribute ( 'aria-hidden' , 'true' ) ;
144+ this . _container . appendChild ( downButton ) ;
145+ }
146+ }
147+
148+ _scrollUp ( ) {
149+ if ( this . _visibleFloorStart > 0 ) {
150+ this . _visibleFloorStart -- ;
151+ this . _render ( ) ;
152+ }
153+ }
154+
155+ _scrollDown ( ) {
156+ if ( this . _model && this . _model . floors && this . _visibleFloorStart + VISIBLE_FLOORS < this . _model . floors . length ) {
157+ this . _visibleFloorStart ++ ;
158+ this . _render ( ) ;
147159 }
148160 }
149161}
0 commit comments