UNPKG

10.4 kBJavaScriptView Raw
1/*
2 Terminal Kit
3
4 Copyright (c) 2009 - 2020 Cédric Ronvel
5
6 The MIT License (MIT)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26
27"use strict" ;
28
29
30
31/* Base class for menus (ColumnMenu, RowMenu, etc) */
32
33
34
35const Element = require( './Element.js' ) ;
36const Button = require( './Button.js' ) ;
37
38
39
40function BaseMenu( options = {} ) {
41 Element.call( this , options ) ;
42
43 this.backgroundAttr = options.backgroundAttr || { bgColor: 'white' , color: 'black' } ;
44 this.contentEllipsis = options.contentEllipsis || '…' ;
45 this.previousPageContent = options.previousPageContent || '«' ;
46 this.previousPageContentHasMarkup = !! options.previousPageContentHasMarkup ;
47 this.nextPageContent = options.nextPageContent || '»' ;
48 this.nextPageContentHasMarkup = !! options.nextPageContentHasMarkup ;
49 this.itemsDef = options.items || [] ;
50 this.previousPageDef = options.previousPage ;
51 this.nextPageDef = options.nextPage ;
52 this.masterDef = options.master ;
53 this.separatorDef = options.separator ;
54 this.buttons = [] ;
55 this.focusChild = null ;
56
57 // Pagination
58 this.page = 0 ;
59 this.maxPage = 0 ;
60
61 this.onButtonSubmit = this.onButtonSubmit.bind( this ) ;
62 this.onButtonToggle = this.onButtonToggle.bind( this ) ;
63 this.onKey = this.onKey.bind( this ) ;
64 this.onWheel = this.onWheel.bind( this ) ;
65 this.onFocus = this.onFocus.bind( this ) ;
66
67 // Global default attributes
68 this.buttonBlurAttr = options.buttonBlurAttr || this.defaultOptions.buttonBlurAttr || { bgColor: 'black' , color: 'white' , bold: true } ;
69 this.buttonEvenBlurAttr = options.buttonEvenBlurAttr || null ;
70 this.buttonFocusAttr = options.buttonFocusAttr || this.defaultOptions.buttonFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ;
71 this.buttonDisabledAttr = options.buttonDisabledAttr || this.defaultOptions.buttonDisabledAttr || { bgColor: 'black' , color: 'brightBlack' , bold: true } ;
72 this.buttonSubmittedAttr = options.buttonSubmittedAttr || this.defaultOptions.buttonSubmittedAttr || { bgColor: 'brightBlack' , color: 'brightWhite' , bold: true } ;
73 this.turnedOnBlurAttr = options.turnedOnBlurAttr || this.defaultOptions.turnedOnBlurAttr || { bgColor: 'cyan' } ;
74 this.turnedOnFocusAttr = options.turnedOnFocusAttr || this.defaultOptions.turnedOnFocusAttr || { bgColor: 'brightCyan' , bold: true } ;
75 this.turnedOffBlurAttr = options.turnedOffBlurAttr || this.defaultOptions.turnedOffBlurAttr || { bgColor: 'gray' , dim: true } ;
76 this.turnedOffFocusAttr = options.turnedOffFocusAttr || this.defaultOptions.turnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ;
77
78 // Padding
79 this.blurLeftPadding = options.blurLeftPadding || options.leftPadding || '' ;
80 this.blurRightPadding = options.blurRightPadding || options.rightPadding || '' ;
81 this.focusLeftPadding = options.focusLeftPadding || options.leftPadding || '' ;
82 this.focusRightPadding = options.focusRightPadding || options.rightPadding || '' ;
83 this.disabledLeftPadding = options.disabledLeftPadding || options.leftPadding || '' ;
84 this.disabledRightPadding = options.disabledRightPadding || options.rightPadding || '' ;
85 this.submittedLeftPadding = options.submittedLeftPadding || options.leftPadding || '' ;
86 this.submittedRightPadding = options.submittedRightPadding || options.rightPadding || '' ;
87 this.turnedOnFocusLeftPadding = options.turnedOnFocusLeftPadding || options.turnedOnLeftPadding || options.leftPadding || '' ;
88 this.turnedOnFocusRightPadding = options.turnedOnFocusRightPadding || options.turnedOnRightPadding || options.rightPadding || '' ;
89 this.turnedOffFocusLeftPadding = options.turnedOffFocusLeftPadding || options.turnedOffLeftPadding || options.leftPadding || '' ;
90 this.turnedOffFocusRightPadding = options.turnedOffFocusRightPadding || options.turnedOffRightPadding || options.rightPadding || '' ;
91 this.turnedOnBlurLeftPadding = options.turnedOnBlurLeftPadding || options.turnedOnLeftPadding || options.leftPadding || '' ;
92 this.turnedOnBlurRightPadding = options.turnedOnBlurRightPadding || options.turnedOnRightPadding || options.rightPadding || '' ;
93 this.turnedOffBlurLeftPadding = options.turnedOffBlurLeftPadding || options.turnedOffLeftPadding || options.leftPadding || '' ;
94 this.turnedOffBlurRightPadding = options.turnedOffBlurRightPadding || options.turnedOffRightPadding || options.rightPadding || '' ;
95 this.paddingHasMarkup = !! options.paddingHasMarkup ;
96
97 if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }
98 if ( options.buttonKeyBindings ) { this.buttonKeyBindings = options.buttonKeyBindings ; }
99 if ( options.buttonActionKeyBindings ) { this.buttonActionKeyBindings = options.buttonActionKeyBindings ; }
100 if ( options.toggleButtonKeyBindings ) { this.toggleButtonKeyBindings = options.toggleButtonKeyBindings ; }
101 if ( options.toggleButtonActionKeyBindings ) { this.toggleButtonActionKeyBindings = options.toggleButtonActionKeyBindings ; }
102
103 this.on( 'key' , this.onKey ) ;
104 this.on( 'wheel' , this.onWheel ) ;
105 this.on( 'focus' , this.onFocus ) ;
106}
107
108module.exports = BaseMenu ;
109
110BaseMenu.prototype = Object.create( Element.prototype ) ;
111BaseMenu.prototype.constructor = BaseMenu ;
112BaseMenu.prototype.elementType = 'BaseMenu' ;
113
114
115
116BaseMenu.prototype.destroy = function( isSubDestroy ) {
117 this.off( 'key' , this.onKey ) ;
118 this.off( 'wheel' , this.onWheel ) ;
119 this.off( 'focus' , this.onFocus ) ;
120
121 Element.prototype.destroy.call( this , isSubDestroy ) ;
122} ;
123
124
125
126BaseMenu.prototype.previousPage = function( focusType ) {
127 var focusAware ;
128
129 if ( this.maxPage && this.page > 0 ) {
130 this.page -- ;
131 this.initPage() ;
132 this.focusChild = this.children[ this.children.length - 2 ] ;
133 focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
134 if ( ! focusAware ) { this.document.focusPrevious() ; }
135 this.draw() ;
136 }
137} ;
138
139
140
141BaseMenu.prototype.nextPage = function( focusType ) {
142 var focusAware ;
143
144 if ( this.maxPage && this.page < this.maxPage ) {
145 this.page ++ ;
146 this.initPage() ;
147 this.focusChild = this.children[ 1 ] ;
148 focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
149 if ( ! focusAware ) { this.document.focusNext() ; }
150 this.draw() ;
151 }
152} ;
153
154
155
156BaseMenu.prototype.toPage = function( page , focusType ) {
157 var focusAware ;
158
159 if ( this.maxPage && page !== this.page ) {
160 this.page = page ;
161 this.initPage() ;
162 this.focusChild = this.children[ 1 ] ;
163 focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
164 if ( ! focusAware ) { this.document.focusNext() ; }
165 this.draw() ;
166 }
167} ;
168
169
170
171BaseMenu.prototype.focusValue = function( value , focusType ) {
172 var focusAware , itemDef , item ;
173
174 itemDef = this.itemsDef.find( it => ! it.disabled && it.value === value ) ;
175 if ( ! itemDef ) { return ; }
176
177 if ( this.page !== itemDef.page ) {
178 this.page = itemDef.page ;
179 this.initPage() ;
180 }
181
182 item = this.buttons.find( it => it.def === itemDef ) ;
183 if ( ! item ) { return ; } // Not possible, but well...
184
185 this.focusChild = item ;
186
187 focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
188 if ( ! focusAware ) { this.document.focusNext() ; }
189
190 this.draw() ;
191} ;
192
193
194
195BaseMenu.prototype.onKey = function( key , trash , data ) {
196 switch( this.keyBindings[ key ] ) {
197 case 'previous' :
198 this.focusChild = this.focusPreviousChild( ! this.maxPage ) ;
199 if ( this.focusChild === this.children[ 0 ] && this.maxPage && this.page > 0 ) {
200 this.previousPage( 'cycle' ) ;
201 }
202 break ;
203 case 'next' :
204 this.focusChild = this.focusNextChild( ! this.maxPage ) ;
205 if ( this.focusChild === this.children[ this.children.length - 1 ] && this.maxPage && this.page < this.maxPage ) {
206 this.nextPage( 'cycle' ) ;
207 }
208 break ;
209 case 'previousPage' :
210 if ( this.maxPage && this.page > 0 ) {
211 this.previousPage( 'cycle' ) ;
212 }
213 break ;
214 case 'nextPage' :
215 if ( this.maxPage && this.page < this.maxPage ) {
216 this.nextPage( 'cycle' ) ;
217 }
218 break ;
219 case 'firstPage' :
220 if ( this.maxPage && this.page !== 0 ) {
221 this.toPage( 0 , 'cycle' ) ;
222 }
223 break ;
224 case 'lastPage' :
225 if ( this.maxPage && this.page !== this.maxPage ) {
226 this.toPage( this.maxPage , 'cycle' ) ;
227 }
228 break ;
229 default :
230 return ; // Bubble up
231 }
232
233 return true ; // Do not bubble up
234} ;
235
236
237
238BaseMenu.prototype.onWheel = function( data ) {
239 if ( data.yDirection < 0 ) { this.previousPage( 'cycle' ) ; }
240 else if ( data.yDirection > 0 ) { this.nextPage( 'cycle' ) ; }
241} ;
242
243
244
245BaseMenu.prototype.onFocus = function( focus , type ) {
246 if ( type === 'cycle' ) { return ; }
247
248 if ( focus ) {
249 if ( this.focusChild ) { this.document.giveFocusTo( this.focusChild , 'delegate' ) ; }
250 else { this.focusChild = this.focusNextChild() ; }
251 }
252} ;
253
254
255
256BaseMenu.prototype.onButtonSubmit = function( buttonValue , action , button ) {
257 switch ( button.internalRole ) {
258 case 'previousPage' :
259 this.previousPage() ;
260 break ;
261 case 'nextPage' :
262 this.nextPage() ;
263 break ;
264 default :
265 this.emit( 'submit' , buttonValue , action , this ) ;
266 }
267} ;
268
269
270
271// Should be redefined in the derivative class
272BaseMenu.prototype.defaultOptions = {} ;
273BaseMenu.prototype.keyBindings = {} ;
274BaseMenu.prototype.buttonKeyBindings = {} ;
275BaseMenu.prototype.buttonActionKeyBindings = {} ;
276BaseMenu.prototype.toggleButtonKeyBindings = {} ;
277BaseMenu.prototype.toggleButtonActionKeyBindings = {} ;
278BaseMenu.prototype.initPage = function() {} ;
279BaseMenu.prototype.onButtonToggle = function() {} ;
280BaseMenu.prototype.childUseParentKeyValue = false ;
281