@@ -9,11 +9,13 @@ import {useTabListState} from '@react-stately/tabs';
9
9
import type { Node , Orientation } from '@react-types/shared' ;
10
10
import { Reorder } from 'framer-motion' ;
11
11
12
+ import { Button } from 'sentry/components/button' ;
12
13
import type { SelectOption } from 'sentry/components/compactSelect' ;
13
14
import { TabsContext } from 'sentry/components/tabs' ;
14
15
import { type BaseTabProps , Tab } from 'sentry/components/tabs/tab' ;
15
16
import { OverflowMenu , useOverflowTabs } from 'sentry/components/tabs/tabList' ;
16
17
import { tabsShouldForwardProp } from 'sentry/components/tabs/utils' ;
18
+ import { IconAdd } from 'sentry/icons' ;
17
19
import { space } from 'sentry/styles/space' ;
18
20
import { browserHistory } from 'sentry/utils/browserHistory' ;
19
21
@@ -29,6 +31,8 @@ function BaseDraggableTabList({
29
31
className,
30
32
outerWrapStyles,
31
33
onReorder,
34
+ onAddView,
35
+ showTempTab = false ,
32
36
tabVariant = 'filled' ,
33
37
...props
34
38
} : BaseDraggableTabListProps ) {
@@ -103,6 +107,11 @@ function BaseDraggableTabList({
103
107
} ) ;
104
108
} , [ state . collection , overflowTabs ] ) ;
105
109
110
+ const persistentTabs = [ ...state . collection ] . filter (
111
+ item => item . key !== 'temporary-tab'
112
+ ) ;
113
+ const tempTab = [ ...state . collection ] . find ( item => item . key === 'temporary-tab' ) ;
114
+
106
115
return (
107
116
< TabListOuterWrap style = { outerWrapStyles } >
108
117
< Reorder . Group
@@ -115,10 +124,11 @@ function BaseDraggableTabList({
115
124
{ ...tabListProps }
116
125
orientation = { orientation }
117
126
hideBorder = { hideBorder }
127
+ borderStyle = { state . selectedKey === 'temporary-tab' ? 'dashed' : 'solid' }
118
128
className = { className }
119
129
ref = { tabListRef }
120
130
>
121
- { [ ... state . collection ] . map ( item => (
131
+ { persistentTabs . map ( item => (
122
132
< Reorder . Item
123
133
key = { item . key }
124
134
value = { item }
@@ -136,12 +146,32 @@ function BaseDraggableTabList({
136
146
variant = { tabVariant }
137
147
/>
138
148
139
- { state . selectedKey !== item . key &&
140
- state . collection . getKeyAfter ( item . key ) !== state . selectedKey && (
141
- < TabDivider />
142
- ) }
149
+ { ( state . selectedKey === 'temporary-tab' ||
150
+ ( state . selectedKey !== item . key &&
151
+ state . collection . getKeyAfter ( item . key ) !== state . selectedKey ) ) && (
152
+ < TabDivider />
153
+ ) }
143
154
</ Reorder . Item >
144
155
) ) }
156
+ < AddViewButton borderless size = "zero" onClick = { onAddView } >
157
+ < StyledIconAdd size = "xs" />
158
+ t('Add View')
159
+ </ AddViewButton >
160
+ < TabDivider />
161
+ { showTempTab && tempTab && (
162
+ < Tab
163
+ key = { tempTab . key }
164
+ item = { tempTab }
165
+ state = { state }
166
+ orientation = { orientation }
167
+ overflowing = {
168
+ orientation === 'horizontal' && overflowTabs . includes ( tempTab . key )
169
+ }
170
+ ref = { element => ( tabItemsRef . current [ tempTab . key ] = element ) }
171
+ variant = { tabVariant }
172
+ borderStyle = "dashed"
173
+ />
174
+ ) }
145
175
</ TabListWrap >
146
176
</ Reorder . Group >
147
177
@@ -164,15 +194,22 @@ export interface DraggableTabListProps
164
194
onReorder : ( newOrder : Node < DraggableTabListItemProps > [ ] ) => void ;
165
195
className ?: string ;
166
196
hideBorder ?: boolean ;
197
+ onAddView ?: React . MouseEventHandler ;
167
198
outerWrapStyles ?: React . CSSProperties ;
199
+ showTempTab ?: boolean ;
168
200
tabVariant ?: BaseTabProps [ 'variant' ] ;
169
201
}
170
202
171
203
/**
172
204
* To be used as a direct child of the <Tabs /> component. See example usage
173
205
* in tabs.stories.js
174
206
*/
175
- export function DraggableTabList ( { items, ...props } : DraggableTabListProps ) {
207
+ export function DraggableTabList ( {
208
+ items,
209
+ onAddView,
210
+ showTempTab,
211
+ ...props
212
+ } : DraggableTabListProps ) {
176
213
const collection = useCollection ( { items, ...props } , collectionFactory ) ;
177
214
178
215
const parsedItems = useMemo (
@@ -190,7 +227,13 @@ export function DraggableTabList({items, ...props}: DraggableTabListProps) {
190
227
) ;
191
228
192
229
return (
193
- < BaseDraggableTabList items = { parsedItems } disabledKeys = { disabledKeys } { ...props } >
230
+ < BaseDraggableTabList
231
+ items = { parsedItems }
232
+ onAddView = { onAddView }
233
+ showTempTab = { showTempTab }
234
+ disabledKeys = { disabledKeys }
235
+ { ...props }
236
+ >
194
237
{ item => < Item { ...item } /> }
195
238
</ BaseDraggableTabList >
196
239
) ;
@@ -213,6 +256,7 @@ const TabListOuterWrap = styled('div')`
213
256
const TabListWrap = styled ( 'ul' , {
214
257
shouldForwardProp : tabsShouldForwardProp ,
215
258
} ) < {
259
+ borderStyle : 'dashed' | 'solid' ;
216
260
hideBorder : boolean ;
217
261
orientation : Orientation ;
218
262
} > `
@@ -229,7 +273,7 @@ const TabListWrap = styled('ul', {
229
273
? `
230
274
grid-auto-flow: column;
231
275
justify-content: start;
232
- ${ ! p . hideBorder && `border-bottom: solid 1px ${ p . theme . border } ;` }
276
+ ${ ! p . hideBorder && `border-bottom: ${ p . borderStyle } 1px ${ p . theme . border } ;` }
233
277
stroke-dasharray: 4, 3;
234
278
`
235
279
: `
@@ -238,6 +282,18 @@ const TabListWrap = styled('ul', {
238
282
align-content: start;
239
283
gap: 1px;
240
284
padding-right: ${ space ( 2 ) } ;
241
- ${ ! p . hideBorder && `border-right: solid 1px ${ p . theme . border } ;` }
285
+ ${ ! p . hideBorder && `border-right: ${ p . borderStyle } 1px ${ p . theme . border } ;` }
242
286
` } ;
243
287
` ;
288
+
289
+ const AddViewButton = styled ( Button ) `
290
+ color: ${ p => p . theme . gray300 } ;
291
+ padding-right: ${ space ( 0.5 ) } ;
292
+ margin: 3px 2px 2px 2px;
293
+ font-weight: normal;
294
+ ` ;
295
+
296
+ const StyledIconAdd = styled ( IconAdd ) `
297
+ margin-right: 4px;
298
+ margin-left: 2px;
299
+ ` ;
0 commit comments