diff --git a/src/plugins/fresco/ImagesCacheOverview.js b/src/plugins/fresco/ImagesCacheOverview.js index 753a9b4a2..8f516f3d8 100644 --- a/src/plugins/fresco/ImagesCacheOverview.js +++ b/src/plugins/fresco/ImagesCacheOverview.js @@ -24,6 +24,7 @@ import { ToggleButton, Text, } from 'flipper'; +import MultipleSelect from './MultipleSelect.js'; import type {ImagesMap} from './ImagePool.js'; import {clipboard} from 'electron'; import {PureComponent} from 'react'; @@ -69,9 +70,10 @@ function Toggle(props: ToggleProps) { type ImagesCacheOverviewProps = { onColdStartChange: (checked: boolean) => void, coldStartFilter: boolean, - surfaceOptions: {[key: string]: string}, - selectedSurface: string, - onChangeSurface: (key: string) => void, + allSurfacesOption: string, + surfaceOptions: Set, + selectedSurfaces: Set, + onChangeSurface: (key: Set) => void, images: ImagesList, onClear: (type: string) => void, onTrimMemory: () => void, @@ -152,12 +154,47 @@ export default class ImagesCacheOverview extends PureComponent< onChangeSize = (e: SyntheticInputEvent) => this.setState({size: parseInt(e.target.value, 10)}); + onSurfaceOptionsChange = (selectedItem: string, checked: boolean) => { + const {allSurfacesOption, surfaceOptions} = this.props; + const selectedSurfaces = new Set([...this.props.selectedSurfaces]); + + if (checked && selectedItem === allSurfacesOption) { + this.props.onChangeSurface(surfaceOptions); + + return; + } + + if (!checked && selectedSurfaces.size === 1) { + return; + } + + if (selectedItem !== allSurfacesOption) { + selectedSurfaces.delete(allSurfacesOption); + + if (checked) { + selectedSurfaces.add(selectedItem); + } else { + selectedSurfaces.delete(selectedItem); + } + } + + if ( + surfaceOptions.size - selectedSurfaces.size === 1 && + !selectedSurfaces.has(allSurfacesOption) + ) { + selectedSurfaces.add(allSurfacesOption); + } + + this.props.onChangeSurface(selectedSurfaces); + }; + render() { const hasImages = this.props.images.reduce( (c, cacheInfo) => c + cacheInfo.imageIds.length, 0, ) > 0; + return ( - ) : ( - {this.props.images.map(data => { + {this.props.images.map((data, index) => { const maxSize = data.maxSizeBytes; const subtitle = maxSize ? formatMB(data.sizeBytes) + ' / ' + formatMB(maxSize) @@ -218,6 +256,7 @@ export default class ImagesCacheOverview extends PureComponent< : null; return ( ({ + display: props.visibleList ? 'flex' : 'none', + position: 'absolute', + top: '32px', + left: 0, + zIndex: 4, + width: 'auto', + minWidth: '200px', + backgroundColor: colors.white, + borderWidth: '1px', + borderStyle: 'solid', + borderColor: colors.macOSTitleBarButtonBorderBottom, + borderRadius: 4, +})); + +const ListItem = styled('label')({ + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + width: '100%', + color: colors.light50, + fontSize: '11px', + padding: '0 5px', + '&:hover': { + backgroundColor: colors.macOSTitleBarButtonBackgroundActiveHighlight, + }, +}); + +const Checkbox = styled('input')({ + display: 'inline-block', + marginRight: 5, + verticalAlign: 'middle', +}); + +const StyledGlyph = styled(Glyph)({ + marginLeft: '4px', +}); + +type State = { + visibleList: boolean, +}; + +export default class MultipleSelect extends Component< + { + selected: Set, + + options: Set, + + onChange: (selectedItem: string, checked: boolean) => void, + + label: string, + }, + State, +> { + state = { + visibleList: false, + }; + + handleOnChange = (event: SyntheticInputEvent) => { + const { + target: {value, checked}, + } = event; + this.props.onChange(value, checked); + }; + + toggleList = () => this.setState({visibleList: !this.state.visibleList}); + + render() { + const {selected, label, options} = this.props; + const {visibleList} = this.state; + const icon = visibleList ? 'chevron-up' : 'chevron-down'; + + return ( + + + + {Array.from(options).map((option, index) => ( + + + {option} + + ))} + + + ); + } +} diff --git a/src/plugins/fresco/index.js b/src/plugins/fresco/index.js index 73ea1b291..4ce8df756 100644 --- a/src/plugins/fresco/index.js +++ b/src/plugins/fresco/index.js @@ -48,7 +48,7 @@ export type PersistedState = { }; type PluginState = { - selectedSurface: string, + selectedSurfaces: Set, selectedImage: ?ImageId, isDebugOverlayEnabled: boolean, isAutoRefreshEnabled: boolean, @@ -272,7 +272,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin< nextEventId: number = 1; state = { - selectedSurface: surfaceDefaultText, + selectedSurfaces: new Set([surfaceDefaultText]), selectedImage: null, isDebugOverlayEnabled: false, isAutoRefreshEnabled: false, @@ -283,12 +283,13 @@ export default class FlipperImagesPlugin extends FlipperPlugin< filterImages = ( images: ImagesList, events: Array, - surface: string, + surfaces: Set, coldStart: boolean, ): ImagesList => { - if (!surface || (surface === surfaceDefaultText && !coldStart)) { + if (!surfaces || (surfaces.has(surfaceDefaultText) && !coldStart)) { return images; } + const imageList = images.map((image: CacheInfo) => { const imageIdList = image.imageIds.filter(imageID => { const filteredEvents = events.filter((event: ImageEventWithId) => { @@ -298,13 +299,14 @@ export default class FlipperImagesPlugin extends FlipperPlugin< event.imageIds && event.imageIds.includes(imageID); - if (surface === surfaceDefaultText) { + if (surfaces.has(surfaceDefaultText)) { return output && coldStart && event.coldStart; } + return ( (!coldStart || (coldStart && event.coldStart)) && output && - event.attribution[0] == surface + surfaces.has(event.attribution[0]) ); }); return filteredEvents.length > 0; @@ -330,9 +332,10 @@ export default class FlipperImagesPlugin extends FlipperPlugin< const images = this.filterImages( this.props.persistedState.images, this.props.persistedState.events, - this.state.selectedSurface, + this.state.selectedSurfaces, this.state.coldStartFilter, ); + this.setState({images}); } @@ -342,17 +345,18 @@ export default class FlipperImagesPlugin extends FlipperPlugin< updateImagesOnUI = ( images: ImagesList, - surface: string, + surfaces: Set, coldStart: boolean, ) => { const filteredImages = this.filterImages( images, this.props.persistedState.events, - surface, + surfaces, coldStart, ); + this.setState({ - selectedSurface: surface, + selectedSurfaces: surfaces, images: filteredImages, coldStartFilter: coldStart, }); @@ -366,7 +370,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin< this.props.setPersistedState({images: response.levels}); this.updateImagesOnUI( this.props.persistedState.images, - this.state.selectedSurface, + this.state.selectedSurfaces, this.state.coldStartFilter, ); }); @@ -431,10 +435,10 @@ export default class FlipperImagesPlugin extends FlipperPlugin< return ; }; - onSurfaceChange = (surface: string) => { + onSurfaceChange = (surfaces: Set) => { this.updateImagesOnUI( this.props.persistedState.images, - surface, + surfaces, this.state.coldStartFilter, ); }; @@ -442,7 +446,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin< onColdStartChange = (checked: boolean) => { this.updateImagesOnUI( this.props.persistedState.images, - this.state.selectedSurface, + this.state.selectedSurfaces, checked, ); }; @@ -457,15 +461,22 @@ export default class FlipperImagesPlugin extends FlipperPlugin< render() { const options = [...this.props.persistedState.surfaceList].reduce( (acc, item) => { - return {...acc, [item]: item}; + return [...acc, item]; }, - {[surfaceDefaultText]: surfaceDefaultText}, + [surfaceDefaultText], ); + let {selectedSurfaces} = this.state; + + if (selectedSurfaces.has(surfaceDefaultText)) { + selectedSurfaces = new Set(options); + } + return (