1
- import { InfoOutlineIcon } from '@sanity/icons'
2
- import { Card , Flex , Stack , TabList , TabPanel , Text } from '@sanity/ui'
1
+ import { ChevronDownIcon , InfoOutlineIcon } from '@sanity/icons'
2
+ import {
3
+ type BadgeTone ,
4
+ // eslint-disable-next-line no-restricted-imports -- fine-grained control needed
5
+ Button ,
6
+ Flex ,
7
+ Menu ,
8
+ // eslint-disable-next-line no-restricted-imports -- fine-grained control needed
9
+ MenuItem ,
10
+ Stack ,
11
+ TabPanel ,
12
+ Text ,
13
+ } from '@sanity/ui'
3
14
import { addHours , isValid , startOfHour } from 'date-fns'
4
- import { useCallback , useEffect , useState } from 'react'
15
+ import {
16
+ type ComponentType ,
17
+ type MouseEventHandler ,
18
+ useCallback ,
19
+ useEffect ,
20
+ useId ,
21
+ useState ,
22
+ } from 'react'
5
23
6
- import { Tab , Tooltip } from '../../../../ui-components'
24
+ import { MenuButton , Tooltip } from '../../../../ui-components'
7
25
import { useTranslation } from '../../../i18n'
8
26
import useTimeZone from '../../../scheduledPublishing/hooks/useTimeZone'
9
- import { type EditableReleaseDocument , type ReleaseType } from '../../store/types'
27
+ import { type EditableReleaseDocument , isReleaseType , type ReleaseType } from '../../store/types'
28
+ import { ReleaseAvatar } from '../ReleaseAvatar'
10
29
import { ScheduleDatePicker } from '../ScheduleDatePicker'
11
30
import { TitleDescriptionForm } from './TitleDescriptionForm'
12
31
13
- const RELEASE_TYPES : ReleaseType [ ] = [ 'asap' , 'scheduled' , 'undecided' ]
32
+ const RELEASE_TYPES : Record < ReleaseType , { tone : BadgeTone } > = {
33
+ asap : {
34
+ tone : 'critical' ,
35
+ } ,
36
+ scheduled : {
37
+ tone : 'primary' ,
38
+ } ,
39
+ undecided : {
40
+ tone : 'suggest' ,
41
+ } ,
42
+ }
14
43
15
44
/** @internal */
16
45
export function ReleaseForm ( props : {
@@ -36,8 +65,14 @@ export function ReleaseForm(props: {
36
65
[ onChange , value ] ,
37
66
)
38
67
39
- const handleButtonReleaseTypeChange = useCallback (
40
- ( pickedReleaseType : ReleaseType ) => {
68
+ const handleButtonReleaseTypeChange = useCallback < MouseEventHandler < HTMLDivElement > > (
69
+ ( event ) => {
70
+ const pickedReleaseType = event . currentTarget . dataset . value
71
+
72
+ if ( ! isReleaseType ( pickedReleaseType ) ) {
73
+ return
74
+ }
75
+
41
76
setButtonReleaseType ( pickedReleaseType )
42
77
43
78
// select the start of the next hour
@@ -87,12 +122,17 @@ export function ReleaseForm(props: {
87
122
}
88
123
} , [ currentTimezone , intendedPublishAt , timeZone , utcToCurrentZoneDate ] )
89
124
125
+ const menuButtonId = useId ( )
126
+ const [ menuButton , setMenuButton ] = useState < HTMLElement | null > ( null )
127
+
90
128
return (
91
129
< Stack space = { 5 } >
92
- < Stack space = { 2 } style = { { margin : - 1 } } >
93
- < Text muted size = { 1 } >
94
- { t ( 'release.dialog.tooltip.title' ) }
95
- < span style = { { marginLeft : 10 , opacity : 0.5 } } >
130
+ < Stack space = { 4 } >
131
+ < Flex gap = { 2 } align = "center" >
132
+ < Text as = "label" htmlFor = { menuButtonId } >
133
+ { t ( 'release.dialog.tooltip.title' ) }
134
+ </ Text >
135
+ < Text muted size = { 1 } >
96
136
< Tooltip
97
137
content = {
98
138
< Stack space = { 3 } style = { { maxWidth : 320 - 16 } } >
@@ -108,48 +148,66 @@ export function ReleaseForm(props: {
108
148
>
109
149
< InfoOutlineIcon />
110
150
</ Tooltip >
111
- </ span >
112
- </ Text >
113
- < Flex gap = { 1 } >
114
- < Card
115
- border
116
- overflow = "hidden"
117
- padding = { 1 }
118
- style = { { borderRadius : 3.5 , alignSelf : 'baseline' } }
119
- tone = "inherit"
120
- >
121
- < Flex gap = { 1 } >
122
- < TabList space = { 0.5 } >
123
- { RELEASE_TYPES . map ( ( type ) => (
124
- < Tab
125
- aria-controls = { `release-timing-${ type } ` }
126
- id = { `release-timing-${ type } -tab` }
127
- key = { type }
128
- onClick = { ( ) => handleButtonReleaseTypeChange ( type ) }
129
- selected = { buttonReleaseType === type }
130
- label = { t ( `release.type.${ type } ` ) }
151
+ </ Text >
152
+ </ Flex >
153
+ < Stack space = { 3 } >
154
+ < MenuButton
155
+ id = { menuButtonId }
156
+ ref = { setMenuButton }
157
+ button = {
158
+ < Button mode = "ghost" >
159
+ < Flex justify = "space-between" align = "center" >
160
+ < ReleaseTypeOption
161
+ text = { t ( `release.type.${ buttonReleaseType } ` ) }
162
+ tone = { RELEASE_TYPES [ buttonReleaseType ] . tone }
131
163
/>
164
+ < Text size = { 1 } >
165
+ < ChevronDownIcon />
166
+ </ Text >
167
+ </ Flex >
168
+ </ Button >
169
+ }
170
+ popover = { {
171
+ placement : 'bottom' ,
172
+ matchReferenceWidth : true ,
173
+ boundaryElement : menuButton ,
174
+ } }
175
+ menu = {
176
+ < Menu >
177
+ { Object . entries ( RELEASE_TYPES ) . map ( ( [ type , { tone} ] ) => (
178
+ < MenuItem key = { type } data-value = { type } onClick = { handleButtonReleaseTypeChange } >
179
+ < ReleaseTypeOption text = { t ( `release.type.${ type } ` ) } tone = { tone } />
180
+ </ MenuItem >
132
181
) ) }
133
- </ TabList >
134
- </ Flex >
135
- </ Card >
136
- { buttonReleaseType === 'scheduled' && (
137
- < TabPanel
138
- aria-labelledby = "release-timing-at-time-tab"
139
- flex = { 1 }
140
- id = "release-timing-at-time"
141
- style = { { outline : 'none' } }
142
- tabIndex = { - 1 }
143
- >
144
- < ScheduleDatePicker
145
- initialValue = { intendedPublishAt || new Date ( ) }
146
- onChange = { handleBundlePublishAtCalendarChange }
147
- />
148
- </ TabPanel >
149
- ) }
150
- </ Flex >
182
+ </ Menu >
183
+ }
184
+ />
185
+ < Flex gap = { 1 } >
186
+ { buttonReleaseType === 'scheduled' && (
187
+ < TabPanel
188
+ aria-labelledby = "release-timing-at-time-tab"
189
+ flex = { 1 }
190
+ id = "release-timing-at-time"
191
+ style = { { outline : 'none' } }
192
+ tabIndex = { - 1 }
193
+ >
194
+ < ScheduleDatePicker
195
+ initialValue = { intendedPublishAt || new Date ( ) }
196
+ onChange = { handleBundlePublishAtCalendarChange }
197
+ />
198
+ </ TabPanel >
199
+ ) }
200
+ </ Flex >
201
+ </ Stack >
151
202
</ Stack >
152
203
< TitleDescriptionForm release = { value } onChange = { handleTitleDescriptionChange } />
153
204
</ Stack >
154
205
)
155
206
}
207
+
208
+ const ReleaseTypeOption : ComponentType < { text : string ; tone : BadgeTone } > = ( { tone, text} ) => (
209
+ < Flex gap = { 3 } align = "center" >
210
+ < ReleaseAvatar padding = { 1 } tone = { tone } />
211
+ < Text > { text } </ Text >
212
+ </ Flex >
213
+ )
0 commit comments