// Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import React, {Component} from 'react'; import styled from 'styled-components'; import classnames from 'classnames'; import {createSelector} from 'reselect'; import {Tooltip} from 'components/common/styled-components'; import KeplerGlLogo from 'components/common/logo'; import {Save, DataTable, Save2, Picture, Db, Map as MapIcon, Share} from 'components/common/icons'; import ClickOutsideCloseDropdown from 'components/side-panel/panel-dropdown'; import Toolbar from 'components/common/toolbar'; import ToolbarItem, {ToolbarItemProps} from 'components/common/toolbar-item'; import {FormattedMessage} from 'localization'; import {UiState} from 'reducers'; import {BaseProps} from 'components/common/icons/base'; type StyledPanelActionProps = { active?: boolean; }; type ActionItem = { id: string; label?: string; blank?: boolean; href?: string; tooltip?: string; iconComponent: React.ComponentType>; iconComponentProps?: BaseProps; dropdownComponent?: React.ComponentType; onClick?: (p: PanelHeaderProps) => void; }; type PanelActionProps = { item: ActionItem; onClick: () => void; }; type PanelHeaderDropdownProps = { id: string; items: ToolbarItemProps[]; show?: boolean; onClose: () => void; }; type DropdownCallbacks = { logoComponent?: React.ComponentType<{ appName: string; appWebsite: string; version: string; }>; onExportImage: () => void; onExportData: () => void; onExportConfig?: () => void; onExportMap: () => void; onSaveToStorage: (() => void) | null; onSaveAsToStorage: (() => void) | null; onSaveMap?: () => void; onShareMap: (() => void) | null; }; type Item = { label: string; icon: React.ComponentType>; key: string; onClick: (p: DropdownComponentProps) => (() => void) | null; }; type DropdownComponentProps = { show: boolean; onClose: () => void; items?: Item[]; } & DropdownCallbacks; type PanelHeaderProps = { appName: string; appWebsite: string; version: string; visibleDropdown: UiState['visibleDropdown']; actionItems?: ActionItem[]; showExportDropdown: (i: string) => void; hideExportDropdown: () => void; } & DropdownCallbacks; const StyledPanelHeader = styled.div.attrs(props => ({ className: classnames('side-side-panel__header', props.className) }))` background-color: ${props => props.theme.sidePanelHeaderBg}; padding: 12px 16px 0 16px; `; const StyledPanelHeaderTop = styled.div.attrs(props => ({ className: classnames('side-panel__header__top', props.className) }))` display: flex; justify-content: space-between; margin-bottom: 16px; width: 100%; `; const StyledPanelTopActions = styled.div.attrs({ className: 'side-panel__top__actions' })` display: flex; `; const StyledPanelAction = styled.div.attrs({ className: 'side-panel__panel-header__action' })` align-items: center; border-radius: 2px; color: ${props => (props.active ? props.theme.textColorHl : props.theme.subtextColor)}; display: flex; height: 26px; justify-content: space-between; margin-left: 4px; padding: 5px; font-weight: bold; p { display: inline-block; margin-right: 6px; } a { height: 20px; } :hover { cursor: pointer; color: ${props => props.theme.textColorHl}; a { color: ${props => props.theme.textColorHl}; } } `; const StyledToolbar = styled(Toolbar)` position: absolute; `; export const PanelAction: React.FC = ({item, onClick}) => ( {item.label ?

{item.label}

: null} {item.tooltip ? ( ) : null}
); export const PanelHeaderDropdownFactory = () => { const PanelHeaderDropdown: React.FC = ({items, show, onClose, id}) => { return ( {items.map(item => ( ))} ); }; return PanelHeaderDropdown; }; const getDropdownItemsSelector = () => createSelector( (props: DropdownComponentProps) => props, props => (props.items || []) .map(t => ({ ...t, onClick: t.onClick && t.onClick(props) ? t.onClick(props) : null })) .filter(l => l.onClick) ); export const SaveExportDropdownFactory = ( PanelHeaderDropdown: ReturnType ) => { const dropdownItemsSelector = getDropdownItemsSelector(); const SaveExportDropdown: React.FC = props => ( ); SaveExportDropdown.defaultProps = { items: [ { label: 'toolbar.exportImage', icon: Picture, key: 'image', onClick: props => props.onExportImage }, { label: 'toolbar.exportData', icon: DataTable, key: 'data', onClick: props => props.onExportData }, { label: 'toolbar.exportMap', icon: MapIcon, key: 'map', onClick: props => props.onExportMap }, { label: 'toolbar.saveMap', icon: Save2, key: 'save', onClick: props => props.onSaveMap! }, { label: 'toolbar.shareMapURL', icon: Share, key: 'share', onClick: props => props.onShareMap } ] }; return SaveExportDropdown; }; SaveExportDropdownFactory.deps = [PanelHeaderDropdownFactory]; export const CloudStorageDropdownFactory = ( PanelHeaderDropdown: ReturnType ) => { const dropdownItemsSelector = getDropdownItemsSelector(); const CloudStorageDropdown: React.FC = props => ( ); CloudStorageDropdown.defaultProps = { items: [ { label: 'Save', icon: Save2, key: 'save', onClick: props => props.onSaveToStorage }, { label: 'Save As', icon: Save2, key: 'saveAs', onClick: props => props.onSaveAsToStorage } ] }; return CloudStorageDropdown; }; CloudStorageDropdownFactory.deps = [PanelHeaderDropdownFactory]; PanelHeaderFactory.deps = [SaveExportDropdownFactory, CloudStorageDropdownFactory]; function PanelHeaderFactory( SaveExportDropdown: ReturnType, CloudStorageDropdown: ReturnType ): React.ComponentType { return class PanelHeader extends Component { static defaultProps = { logoComponent: KeplerGlLogo, actionItems: [ { id: 'storage', iconComponent: Db, tooltip: 'tooltip.cloudStorage', onClick: () => {}, dropdownComponent: CloudStorageDropdown }, { id: 'save', iconComponent: Save, onClick: () => {}, label: 'Share', dropdownComponent: SaveExportDropdown } ] }; render() { const { appName, appWebsite, version, actionItems, visibleDropdown, showExportDropdown, hideExportDropdown, ...dropdownCallbacks } = this.props; let items = actionItems || []; // don't render cloud storage icon if onSaveToStorage is not provided if (typeof this.props.onSaveToStorage !== 'function') { items = items.filter(ai => ai.id !== 'storage'); } return ( {this.props.logoComponent && ( )} {items.map(item => (
{ if (item.dropdownComponent) { showExportDropdown(item.id); } else { item.onClick && item.onClick(this.props); } }} /> {item.dropdownComponent ? ( ) : null}
))}
); } }; } export default PanelHeaderFactory;