11import { useMemo , useState } from 'react' ;
22import styled from '@emotion/styled' ;
33
4+ import { Button } from '@sentry/scraps/button' ;
45import { Disclosure } from '@sentry/scraps/disclosure' ;
56import { InputGroup } from '@sentry/scraps/input' ;
67import { Flex , Stack } from '@sentry/scraps/layout' ;
@@ -9,6 +10,7 @@ import {Text} from '@sentry/scraps/text';
910import {
1011 IconAdd ,
1112 IconCheckmark ,
13+ IconChevron ,
1214 IconCopy ,
1315 IconEdit ,
1416 IconSearch ,
@@ -112,8 +114,8 @@ export function SnapshotSidebarContent({
112114 const isGroupedDiff = isDiffMode && groupedItems !== null ;
113115
114116 return (
115- < Flex direction = "column" gap = "md" height = "100%" width = "100%" >
116- < Flex padding = "xl" paddingBottom = "0 ">
117+ < Stack height = "100%" width = "100%" >
118+ < Stack gap = "xl" padding = "xl" borderBottom = "primary ">
117119 < InputGroup style = { { flex : 1 } } >
118120 < InputGroup . LeadingItems disablePointerEvents >
119121 < IconSearch size = "sm" />
@@ -125,94 +127,93 @@ export function SnapshotSidebarContent({
125127 onChange = { e => onSearchChange ( e . target . value ) }
126128 />
127129 </ InputGroup >
128- </ Flex >
129- < Stack overflow = "auto" flex = "1" >
130- { isGroupedDiff
131- ? SECTION_ORDER . map ( section => {
132- const sectionItems = groupedItems . get ( section . type ) ;
133- if ( ! sectionItems || sectionItems . length === 0 ) {
134- return null ;
135- }
136- const isExpanded = isSearching || expandedSections [ section . type ] ;
137- return (
138- < SectionWrapper key = { section . type } >
139- < Disclosure
140- size = "xs"
141- expanded = { isExpanded }
142- onExpandedChange = { expanded =>
143- handleExpandedChange ( section . type , expanded )
144- }
145- >
146- < Disclosure . Title
147- trailingItems = {
148- < Flex align = "center" gap = "xs" >
149- < Text size = "xs" bold >
150- { sectionItems . length }
151- </ Text >
152- { section . icon }
153- </ Flex >
154- }
155- >
156- < Text size = "xs" bold uppercase >
130+ </ Stack >
131+ < Stack overflow = "auto" flex = "1" paddingRight = "0" >
132+ { isGroupedDiff &&
133+ SECTION_ORDER . map ( section => {
134+ const sectionItems = groupedItems . get ( section . type ) ;
135+ if ( ! sectionItems || sectionItems . length === 0 ) {
136+ return null ;
137+ }
138+ const isExpanded = isSearching || expandedSections [ section . type ] ;
139+ return (
140+ < Disclosure key = { section . type } size = "md" expanded = { isExpanded } >
141+ < SidebarSectionTitle
142+ priority = "transparent"
143+ size = "md"
144+ onClick = { ( ) => handleExpandedChange ( section . type , ! isExpanded ) }
145+ >
146+ < Flex align = "center" justify = "between" width = "100%" >
147+ < Flex align = "center" gap = "xs" >
148+ < IconChevron direction = { isExpanded ? 'down' : 'right' } size = "sm" />
149+ < Text size = "sm" bold uppercase >
157150 { section . label }
158151 </ Text >
159- </ Disclosure . Title >
160- < Disclosure . Content >
161- { sectionItems . map ( item => (
162- < SidebarItemRow
163- key = { item . name }
164- isSelected = { item . name === currentItemName }
165- onClick = { ( ) => onSelectItem ( item . name ) }
166- >
167- < Flex align = "center" gap = "sm" flex = "1" minWidth = "0" >
168- < Text
169- size = "md"
170- variant = { item . name === currentItemName ? 'accent' : 'muted' }
171- bold = { item . name === currentItemName }
172- ellipsis
173- >
174- { item . name }
175- </ Text >
176- </ Flex >
177- { item . type === 'changed' && item . pair . diff !== null && (
178- < Text variant = "muted" size = "xs" >
179- { `${ ( item . pair . diff * 100 ) . toFixed ( 1 ) } %` }
180- </ Text >
181- ) }
182- </ SidebarItemRow >
183- ) ) }
184- </ Disclosure . Content >
185- </ Disclosure >
186- </ SectionWrapper >
187- ) ;
188- } )
189- : items . map ( item => {
190- const isSelected = item . name === currentItemName ;
191-
192- return (
193- < SidebarItemRow
194- key = { item . name }
195- isSelected = { isSelected }
196- onClick = { ( ) => onSelectItem ( item . name ) }
197- >
198- < Flex align = "center" gap = "sm" flex = "1" minWidth = "0" >
199- < Text
200- size = "md"
201- variant = { isSelected ? 'accent' : 'muted' }
202- bold = { isSelected }
203- ellipsis
204- >
205- { item . name }
206- </ Text >
152+ </ Flex >
153+ < Flex align = "center" gap = "xs" >
154+ < Text size = "sm" > { sectionItems . length } </ Text >
155+ { section . icon }
156+ </ Flex >
207157 </ Flex >
208- { item . type === 'solo' && item . images . length > 1 && (
209- < Text variant = "muted" size = "xs" >
210- { item . images . length }
211- </ Text >
212- ) }
213- </ SidebarItemRow >
214- ) ;
215- } ) }
158+ </ SidebarSectionTitle >
159+ { isExpanded && (
160+ < SidebarSectionContent >
161+ { sectionItems . map ( item => (
162+ < SidebarItemRow
163+ key = { item . name }
164+ isSelected = { item . name === currentItemName }
165+ onClick = { ( ) => onSelectItem ( item . name ) }
166+ >
167+ < Flex align = "center" gap = "sm" flex = "1" minWidth = "0" >
168+ < Text
169+ size = "md"
170+ variant = { item . name === currentItemName ? 'accent' : 'muted' }
171+ bold = { item . name === currentItemName }
172+ ellipsis
173+ >
174+ { item . name }
175+ </ Text >
176+ </ Flex >
177+ { item . type === 'changed' && item . pair . diff !== null && (
178+ < Text variant = "muted" size = "xs" >
179+ { `${ ( item . pair . diff * 100 ) . toFixed ( 1 ) } %` }
180+ </ Text >
181+ ) }
182+ </ SidebarItemRow >
183+ ) ) }
184+ </ SidebarSectionContent >
185+ ) }
186+ </ Disclosure >
187+ ) ;
188+ } ) }
189+ { ! isGroupedDiff &&
190+ items . map ( item => {
191+ const isSelected = item . name === currentItemName ;
192+
193+ return (
194+ < SidebarItemRow
195+ key = { item . name }
196+ isSelected = { isSelected }
197+ onClick = { ( ) => onSelectItem ( item . name ) }
198+ >
199+ < Flex align = "center" gap = "sm" flex = "1" minWidth = "0" >
200+ < Text
201+ size = "md"
202+ variant = { isSelected ? 'accent' : 'muted' }
203+ bold = { isSelected }
204+ ellipsis
205+ >
206+ { item . name }
207+ </ Text >
208+ </ Flex >
209+ { item . type === 'solo' && item . images . length > 1 && (
210+ < Text variant = "muted" size = "xs" >
211+ { item . images . length }
212+ </ Text >
213+ ) }
214+ </ SidebarItemRow >
215+ ) ;
216+ } ) }
216217 { items . length === 0 && (
217218 < Flex align = "center" justify = "center" padding = "lg" >
218219 < Text variant = "muted" size = "sm" >
@@ -221,44 +222,47 @@ export function SnapshotSidebarContent({
221222 </ Flex >
222223 ) }
223224 </ Stack >
224- </ Flex >
225+ </ Stack >
225226 ) ;
226227}
227228
228- const SectionWrapper = styled ( 'div' ) `
229- &:not(:first-child) {
230- border-top: 1px solid ${ p => p . theme . tokens . border . primary } ;
231- }
229+ const SidebarSectionTitle = styled ( Button ) `
230+ padding: ${ p => p . theme . space . lg } ${ p => p . theme . space . xl } ;
231+ display: block;
232+ width: 100%;
233+ border-radius: 0;
234+ border-bottom: 1px solid ${ p => p . theme . tokens . border . secondary } ;
232235
233- [data-disclosure] > :not(:first-child) {
234- padding-left: 0;
235- padding-right: 0;
236+ &:hover {
237+ background: ${ p => p . theme . tokens . background . secondary } ;
236238 }
239+ ` ;
237240
238- [data-disclosure] > div > button {
239- min-width: 0;
240- overflow: hidden;
241- }
241+ const SidebarSectionContent = styled ( 'div' ) `
242+ width: 100%;
242243
243- [data-disclosure] > div > button ~ * {
244- flex-shrink: 0;
245- padding-right: ${ p => p . theme . space . md } ;
244+ &:last-child {
245+ border-bottom: 1px solid ${ p => p . theme . tokens . border . secondary } ;
246246 }
247247` ;
248248
249249const SidebarItemRow = styled ( 'div' ) < { isSelected : boolean } > `
250250 display: flex;
251251 align-items: center;
252- justify-content: space-between;
253- gap: ${ p => p . theme . space . sm } ;
254252 padding: ${ p => p . theme . space . lg } ${ p => p . theme . space . xl } ;
253+ gap: ${ p => p . theme . space . sm } ;
255254 cursor: pointer;
256255 border-right: 3px solid
257256 ${ p => ( p . isSelected ? p . theme . tokens . border . accent . vibrant : 'transparent' ) } ;
257+ border-bottom: 1px solid ${ p => p . theme . tokens . border . secondary } ;
258258 background: ${ p =>
259259 p . isSelected ? p . theme . tokens . background . transparent . accent . muted : 'transparent' } ;
260260
261261 &:hover {
262262 background: ${ p => p . theme . tokens . background . secondary } ;
263263 }
264+
265+ &:last-child {
266+ border-bottom: none;
267+ }
264268` ;
0 commit comments