1
- import { SearchIcon , TriangleDownIcon } from '@primer/octicons-react'
1
+ import { SearchIcon , TriangleDownIcon , XIcon } from '@primer/octicons-react'
2
2
import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react'
3
3
import type { AnchoredOverlayProps } from '../AnchoredOverlay'
4
4
import { AnchoredOverlay } from '../AnchoredOverlay'
@@ -11,7 +11,7 @@ import type {OverlayProps} from '../Overlay'
11
11
import type { TextInputProps } from '../TextInput'
12
12
import type { ItemProps , ItemInput } from './types'
13
13
14
- import { Button } from '../Button'
14
+ import { Button , IconButton } from '../Button'
15
15
import { useProvidedRefOrCreate } from '../hooks'
16
16
import type { FocusZoneHookSettings } from '../hooks/useFocusZone'
17
17
import { useId } from '../hooks/useId'
@@ -22,7 +22,6 @@ import type {FilteredActionListLoadingType} from '../FilteredActionList/Filtered
22
22
import { FilteredActionListLoadingTypes } from '../FilteredActionList/FilteredActionListLoaders'
23
23
import { useFeatureFlag } from '../FeatureFlags'
24
24
import { announce } from '@primer/live-region-element'
25
-
26
25
import classes from './SelectPanel.module.css'
27
26
import { clsx } from 'clsx'
28
27
@@ -128,6 +127,7 @@ interface SelectPanelBaseProps {
128
127
footer ?: string | React . ReactElement
129
128
initialLoadingType ?: InitialLoadingType
130
129
className ?: string
130
+ onCancel ?: ( ) => void
131
131
}
132
132
133
133
export type SelectPanelProps = SelectPanelBaseProps &
@@ -189,6 +189,7 @@ export function SelectPanel({
189
189
height,
190
190
width,
191
191
id,
192
+ onCancel,
192
193
...listProps
193
194
} : SelectPanelProps ) : JSX . Element {
194
195
const titleId = useId ( )
@@ -352,7 +353,7 @@ export function SelectPanel({
352
353
[ onOpenChange ] ,
353
354
)
354
355
const onClose = useCallback (
355
- ( gesture : Parameters < Exclude < AnchoredOverlayProps [ 'onClose' ] , undefined > > [ 0 ] | 'selection' ) => {
356
+ ( gesture : Parameters < Exclude < AnchoredOverlayProps [ 'onClose' ] , undefined > > [ 0 ] | 'selection' | 'escape' ) => {
356
357
onOpenChange ( false , gesture )
357
358
} ,
358
359
[ onOpenChange ] ,
@@ -454,6 +455,7 @@ export function SelectPanel({
454
455
height = { height }
455
456
width = { width }
456
457
anchorId = { id }
458
+ variant = { { regular : 'anchored' , narrow : 'fullscreen' } }
457
459
pinPosition = { ! height }
458
460
>
459
461
< LiveRegionOutlet />
@@ -472,24 +474,54 @@ export function SelectPanel({
472
474
sx = { enabled ? undefined : { display : 'flex' , flexDirection : 'column' , height : 'inherit' , maxHeight : 'inherit' } }
473
475
className = { enabled ? classes . Wrapper : undefined }
474
476
>
475
- < Box sx = { enabled ? undefined : { pt : 2 , px : 3 } } className = { enabled ? classes . Content : undefined } >
476
- < Heading
477
- as = "h1"
478
- id = { titleId }
479
- sx = { enabled ? undefined : { fontSize : 1 } }
480
- className = { enabled ? classes . Title : undefined }
481
- >
482
- { title }
483
- </ Heading >
484
- { subtitle ? (
485
- < Box
486
- id = { subtitleId }
487
- sx = { enabled ? undefined : { fontSize : 0 , color : 'fg.muted' } }
488
- className = { enabled ? classes . Subtitle : undefined }
477
+ < Box
478
+ sx = {
479
+ enabled
480
+ ? undefined
481
+ : {
482
+ display : 'flex' ,
483
+ justifyContent : 'space-between' ,
484
+ alignItems : 'center' ,
485
+ paddingTop : 2 ,
486
+ paddingRight : 2 ,
487
+ paddingLeft : 2 ,
488
+ }
489
+ }
490
+ className = { enabled ? classes . Header : undefined }
491
+ >
492
+ < div >
493
+ < Heading
494
+ as = "h1"
495
+ id = { titleId }
496
+ sx = { enabled ? undefined : { fontSize : 1 , marginLeft : 2 } }
497
+ className = { enabled ? classes . Title : undefined }
489
498
>
490
- { subtitle }
491
- </ Box >
492
- ) : null }
499
+ { title }
500
+ </ Heading >
501
+ { subtitle ? (
502
+ < Box
503
+ id = { subtitleId }
504
+ sx = { enabled ? undefined : { marginLeft : 2 , fontSize : 0 , color : 'fg.muted' } }
505
+ className = { enabled ? classes . Subtitle : undefined }
506
+ >
507
+ { subtitle }
508
+ </ Box >
509
+ ) : null }
510
+ </ div >
511
+ { onCancel && (
512
+ < IconButton
513
+ type = "button"
514
+ variant = "invisible"
515
+ icon = { XIcon }
516
+ aria-label = "Cancel and close"
517
+ sx = { enabled ? undefined : { display : [ 'inline-grid' , 'inline-grid' , 'none' , 'none' ] } }
518
+ className = { enabled ? classes . ResponsiveCloseButton : undefined }
519
+ onClick = { ( ) => {
520
+ onCancel ( )
521
+ onClose ( 'escape' )
522
+ } }
523
+ />
524
+ ) }
493
525
</ Box >
494
526
< FilteredActionList
495
527
filterValue = { filterValue }
@@ -514,7 +546,7 @@ export function SelectPanel({
514
546
className = { enabled ? clsx ( className , classes . FilteredActionList ) : className }
515
547
announcementsEnabled = { false }
516
548
/>
517
- { footer && (
549
+ { footer ? (
518
550
< Box
519
551
sx = {
520
552
enabled
@@ -530,7 +562,31 @@ export function SelectPanel({
530
562
>
531
563
{ footer }
532
564
</ Box >
533
- ) }
565
+ ) : isMultiSelectVariant ( selected ) ? (
566
+ /* Save and Cancel buttons are only useful for multiple selection, single selection instantly closes the panel */
567
+ < div className = { clsx ( classes . Footer , classes . ResponsiveFooter ) } >
568
+ { /* we add a save and cancel button on narrow screens when SelectPanel is full-screen */ }
569
+ { onCancel && (
570
+ < Button
571
+ size = "medium"
572
+ onClick = { ( ) => {
573
+ onCancel ( )
574
+ onClose ( 'escape' )
575
+ } }
576
+ >
577
+ Cancel
578
+ </ Button >
579
+ ) }
580
+ < Button
581
+ variant = "primary"
582
+ size = "medium"
583
+ block = { onCancel ? false : true }
584
+ onClick = { ( ) => onClose ( 'click-outside' ) }
585
+ >
586
+ Save
587
+ </ Button >
588
+ </ div >
589
+ ) : null }
534
590
</ Box >
535
591
</ AnchoredOverlay >
536
592
</ LiveRegion >
0 commit comments