11import { TranslationBundle } from '@jupyterlab/translation' ;
2+ import { checkIcon } from '@jupyterlab/ui-components' ;
23import { CommandRegistry } from '@lumino/commands' ;
4+ import Button from '@material-ui/core/Button' ;
5+ import ButtonGroup from '@material-ui/core/ButtonGroup' ;
6+ import ClickAwayListener from '@material-ui/core/ClickAwayListener' ;
7+ import Grow from '@material-ui/core/Grow' ;
8+ import MenuItem from '@material-ui/core/MenuItem' ;
9+ import MenuList from '@material-ui/core/MenuList' ;
10+ import Paper from '@material-ui/core/Paper' ;
11+ import Popper from '@material-ui/core/Popper' ;
312import * as React from 'react' ;
4- import TextareaAutosize from 'react-textarea-autosize' ;
13+ import { classes } from 'typestyle' ;
14+ import { listItemIconClass } from '../style/BranchMenu' ;
515import {
616 commitButtonClass ,
7- commitDescriptionClass ,
817 commitFormClass ,
9- commitSummaryClass ,
10- commitInputWrapperClass
18+ commitPaperClass ,
19+ commitRoot ,
20+ commitVariantSelector ,
21+ disabledStyle
1122} from '../style/CommitBox' ;
23+ import { verticalMoreIcon } from '../style/icons' ;
24+ import {
25+ listItemBoldTitleClass ,
26+ listItemContentClass ,
27+ listItemDescClass
28+ } from '../style/NewBranchDialog' ;
1229import { CommandIDs } from '../tokens' ;
30+ import { CommitMessage } from './CommitMessage' ;
31+
32+ /**
33+ * Commit action
34+ */
35+ interface ICommitVariant {
36+ /**
37+ * Action title
38+ */
39+ title : string ;
40+ /**
41+ * Action description
42+ */
43+ description : string ;
44+ }
1345
1446/**
1547 * Interface describing component properties.
@@ -46,7 +78,7 @@ export interface ICommitBoxProps {
4678 description : string ;
4779
4880 /**
49- * Whether the "amend" checkbox is checked
81+ * Whether commit is amending the previous one or not
5082 */
5183 amend : boolean ;
5284
@@ -79,10 +111,23 @@ export interface ICommitBoxProps {
79111 onCommit : ( ) => Promise < void > ;
80112}
81113
114+ /**
115+ * CommitBox state
116+ */
117+ export interface ICommitBoxState {
118+ /**
119+ * Whether the commit variant menu is opened or not.
120+ */
121+ open : boolean ;
122+ }
123+
82124/**
83125 * React component for entering a commit message.
84126 */
85- export class CommitBox extends React . Component < ICommitBoxProps > {
127+ export class CommitBox extends React . Component <
128+ ICommitBoxProps ,
129+ ICommitBoxState
130+ > {
86131 /**
87132 * Returns a React component for entering a commit message.
88133 *
@@ -91,6 +136,25 @@ export class CommitBox extends React.Component<ICommitBoxProps> {
91136 */
92137 constructor ( props : ICommitBoxProps ) {
93138 super ( props ) ;
139+ this . _options . push (
140+ {
141+ title : this . props . trans . __ ( 'Create a new commit' ) ,
142+ description : this . props . trans . __ (
143+ 'New commit will be created and show up as a next one after the previous commit (default).'
144+ )
145+ } ,
146+ {
147+ title : this . props . trans . __ ( 'Amend previous commit' ) ,
148+ description : this . props . trans . __ (
149+ 'Staged changes will be added to the previous commit and its date will be updated.'
150+ )
151+ }
152+ ) ;
153+ this . _anchorRef = React . createRef < HTMLDivElement > ( ) ;
154+
155+ this . state = {
156+ open : false
157+ } ;
94158 }
95159
96160 componentDidMount ( ) : void {
@@ -122,58 +186,92 @@ export class CommitBox extends React.Component<ICommitBoxProps> {
122186 shortcutHint
123187 ) ;
124188 return (
125- < form className = { [ commitFormClass , 'jp-git-CommitBox' ] . join ( ' ' ) } >
126- < input
127- className = { commitSummaryClass }
128- type = "text"
129- placeholder = { summaryPlaceholder }
130- title = {
131- this . props . amend
132- ? this . props . trans . __ (
133- 'Amending the commit will re-use the previous commit summary'
134- )
135- : this . props . trans . __ (
136- 'Enter a commit message summary (a single line, preferably less than 50 characters)'
137- )
138- }
139- value = { this . props . summary }
140- onChange = { this . _onSummaryChange }
141- onKeyPress = { this . _onSummaryKeyPress }
189+ < div className = { classes ( commitFormClass , 'jp-git-CommitBox' ) } >
190+ < CommitMessage
191+ trans = { this . props . trans }
192+ summary = { this . props . summary }
193+ summaryPlaceholder = { summaryPlaceholder }
194+ description = { this . props . description }
142195 disabled = { this . props . amend }
196+ setSummary = { this . props . setSummary }
197+ setDescription = { this . props . setDescription }
143198 />
144- < TextareaAutosize
145- className = { commitDescriptionClass }
146- minRows = { 5 }
147- placeholder = { this . props . trans . __ ( 'Description (optional)' ) }
148- title = {
149- this . props . amend
150- ? this . props . trans . __ (
151- 'Amending the commit will re-use the previous commit summary'
152- )
153- : this . props . trans . __ ( 'Enter a commit message description' )
154- }
155- value = { this . props . description }
156- onChange = { this . _onDescriptionChange }
157- disabled = { this . props . amend }
158- />
159- < div className = { commitInputWrapperClass } >
160- < input
161- className = { commitButtonClass }
162- type = "button"
199+ < ButtonGroup ref = { this . _anchorRef } fullWidth = { true } size = "small" >
200+ < Button
201+ classes = { {
202+ root : commitButtonClass ,
203+ disabled : disabledStyle
204+ } }
163205 title = { title }
164- value = { this . props . label }
165206 disabled = { disabled }
166207 onClick = { this . props . onCommit }
167- />
168- < input
169- type = "checkbox"
170- id = "commit-amend"
171- onChange = { this . _onAmendChange }
172- checked = { this . props . amend }
173- />
174- < label htmlFor = "commit-amend" > Amend</ label >
175- </ div >
176- </ form >
208+ >
209+ { this . props . label }
210+ </ Button >
211+ < Button
212+ classes = { {
213+ root : commitButtonClass
214+ } }
215+ className = { commitVariantSelector }
216+ size = "small"
217+ aria-controls = { this . state . open ? 'split-button-menu' : undefined }
218+ aria-expanded = { this . state . open ? 'true' : undefined }
219+ aria-label = "select commit variant"
220+ aria-haspopup = "menu"
221+ onClick = { this . _handleToggle }
222+ >
223+ < verticalMoreIcon . react tag = "span" />
224+ </ Button >
225+ </ ButtonGroup >
226+ < Popper
227+ open = { this . state . open }
228+ anchorEl = { this . _anchorRef . current }
229+ role = { undefined }
230+ transition
231+ disablePortal
232+ >
233+ { ( { TransitionProps } ) => (
234+ < Grow { ...TransitionProps } >
235+ < Paper
236+ classes = { { root : commitRoot } }
237+ className = { commitPaperClass }
238+ >
239+ < ClickAwayListener onClickAway = { this . _handleClose } >
240+ < MenuList id = "split-button-menu" >
241+ { this . _options . map ( ( option , index ) => (
242+ < MenuItem
243+ key = { option . title }
244+ classes = { { root : commitRoot } }
245+ selected = { this . props . amend ? index === 1 : index === 0 }
246+ onClick = { event =>
247+ this . _handleMenuItemClick ( event , index )
248+ }
249+ >
250+ { ( this . props . amend ? index === 1 : index === 0 ) ? (
251+ < checkIcon . react
252+ className = { listItemIconClass }
253+ tag = "span"
254+ />
255+ ) : (
256+ < span className = { listItemIconClass } />
257+ ) }
258+ < div className = { listItemContentClass } >
259+ < p className = { listItemBoldTitleClass } >
260+ { option . title }
261+ </ p >
262+ < p className = { listItemDescClass } >
263+ { option . description }
264+ </ p >
265+ </ div >
266+ </ MenuItem >
267+ ) ) }
268+ </ MenuList >
269+ </ ClickAwayListener >
270+ </ Paper >
271+ </ Grow >
272+ ) }
273+ </ Popper >
274+ </ div >
177275 ) ;
178276 }
179277
@@ -198,45 +296,39 @@ export class CommitBox extends React.Component<ICommitBoxProps> {
198296 } ;
199297
200298 /**
201- * Callback invoked upon updating a commit message description.
202- *
203- * @param event - event object
299+ * Close the commit variant menu if needed.
204300 */
205- private _onDescriptionChange = ( event : any ) : void => {
206- this . props . setDescription ( event . target . value ) ;
207- } ;
301+ private _handleClose = (
302+ event : React . MouseEvent < Document , MouseEvent >
303+ ) : void => {
304+ if (
305+ this . _anchorRef . current &&
306+ this . _anchorRef . current . contains ( event . target as HTMLElement )
307+ ) {
308+ return ;
309+ }
208310
209- /**
210- * Callback invoked upon updating a commit message summary.
211- *
212- * @param event - event object
213- */
214- private _onSummaryChange = ( event : any ) : void => {
215- this . props . setSummary ( event . target . value ) ;
311+ this . setState ( { open : false } ) ;
216312 } ;
217313
218314 /**
219- * Callback invoked when the amend checkbox is toggled
220- *
221- * @param event - event object
315+ * Handle commit variant menu item click
222316 */
223- private _onAmendChange = ( event : any ) : void => {
224- this . props . setAmend ( event . target . checked ) ;
317+ private _handleMenuItemClick = (
318+ event : React . MouseEvent < HTMLLIElement , MouseEvent > ,
319+ index : number
320+ ) : void => {
321+ this . setState ( {
322+ open : false
323+ } ) ;
324+ this . props . setAmend ( index === 1 ) ;
225325 } ;
226326
227327 /**
228- * Callback invoked upon a `'keypress'` event when entering a commit message summary.
229- *
230- * ## Notes
231- *
232- * - Prevents triggering a `'submit'` action when hitting the `ENTER` key while entering a commit message summary.
233- *
234- * @param event - event object
328+ * Toggle state of the commit variant menu visibility
235329 */
236- private _onSummaryKeyPress = ( event : React . KeyboardEvent ) : void => {
237- if ( event . key === 'Enter' ) {
238- event . preventDefault ( ) ;
239- }
330+ private _handleToggle = ( ) : void => {
331+ this . setState ( { open : ! this . state . open } ) ;
240332 } ;
241333
242334 /**
@@ -255,4 +347,7 @@ export class CommitBox extends React.Component<ICommitBoxProps> {
255347 this . props . onCommit ( ) ;
256348 }
257349 } ;
350+
351+ private _anchorRef : React . RefObject < HTMLDivElement > ;
352+ private _options : ICommitVariant [ ] = [ ] ;
258353}
0 commit comments