terriajs
Version:
Geospatial data visualization platform.
238 lines (220 loc) • 8.04 kB
JSX
import React from "react";
import { removeMarker } from "../../Models/LocationMarkerUtils";
import { reaction, runInAction } from "mobx";
import { Trans } from "react-i18next";
import PropTypes from "prop-types";
import { observer } from "mobx-react";
import styled from "styled-components";
// import { ThemeContext } from "styled-components";
import SearchBox from "../Search/SearchBox";
// import SidebarSearch from "../Search/SidebarSearch";
import LocationSearchResults from "../Search/LocationSearchResults";
import Icon, { StyledIcon } from "../../Styled/Icon";
import Box from "../../Styled/Box";
import Text from "../../Styled/Text";
import Spacing from "../../Styled/Spacing";
import { RawButton } from "../../Styled/Button";
import { addMarker } from "../../Models/LocationMarkerUtils";
export function SearchInDataCatalog({ viewState, handleClick }) {
const locationSearchText = viewState.searchState.locationSearchText;
return (
<RawButton
fullWidth
onClick={() => {
const { searchState } = viewState;
// Set text here as a separate action so that it doesn't get batched up and the catalog
// search text has a chance to set isWaitingToStartCatalogSearch
searchState.setCatalogSearchText(searchState.locationSearchText);
viewState.searchInCatalog(searchState.locationSearchText);
handleClick && handleClick();
}}
>
<Box paddedRatio={2} rounded charcoalGreyBg>
<StyledIcon styledWidth={"14px"} glyph={Icon.GLYPHS["dataCatalog"]} />
<Spacing right={2} />
<Text textAlignLeft textLight large fullWidth>
<Trans
i18nKey="search.searchInDataCatalog"
values={{ locationSearchText }}
>
Search <strong>{locationSearchText}</strong> in the Data Catalogue
</Trans>
</Text>
<StyledIcon glyph={Icon.GLYPHS.right2} styledWidth={"14px"} light />
</Box>
</RawButton>
);
}
SearchInDataCatalog.propTypes = {
handleClick: PropTypes.func.isRequired,
viewState: PropTypes.object.isRequired
};
const PresentationBox = styled(Box).attrs({
fullWidth: true
})`
${(props) =>
props.highlightBottom &&
`
// styled-components doesn't seem to prefix linear-gradient.. soo
background-image: linear-gradient(bottom, ${props.theme.greyLightest} 50%, transparent 50%);
background-image: -o-linear-gradient(bottom, ${props.theme.greyLightest} 50%, transparent 50%);
background-image: -moz-linear-gradient(bottom, ${props.theme.greyLightest} 50%, transparent 50%);
background-image: -webkit-linear-gradient(bottom, ${props.theme.greyLightest} 50%, transparent 50%);
background-image: -ms-linear-gradient(bottom, ${props.theme.greyLightest} 50%, transparent 50%);
`}
`;
export const LOCATION_SEARCH_INPUT_NAME = "LocationSearchInput";
export class SearchBoxAndResultsRaw extends React.Component {
constructor(props) {
super(props);
this.locationSearchRef = React.createRef();
}
componentDidMount() {
this.props.viewState.updateAppRef(
LOCATION_SEARCH_INPUT_NAME,
this.locationSearchRef
);
this.subscribeToProps();
}
componentDidUpdate() {
this.subscribeToProps();
}
componentWillUnmount() {
this.unsubscribeFromProps();
}
subscribeToProps() {
this.unsubscribeFromProps();
// TODO(wing): why is this a reaction here and not in viewState itself?
// Close the search results when the Now Viewing changes (so that it's visible).
this._nowViewingChangeSubscription = reaction(
() => this.props.terria.workbench.items,
() => {
this.props.viewState.searchState.showLocationSearchResults = false;
}
);
}
unsubscribeFromProps() {
if (this._nowViewingChangeSubscription) {
this._nowViewingChangeSubscription();
this._nowViewingChangeSubscription = undefined;
}
}
changeSearchText(newText) {
runInAction(() => {
this.props.viewState.searchState.locationSearchText = newText;
});
if (newText.length === 0) {
removeMarker(this.props.terria);
runInAction(() => {
this.toggleShowLocationSearchResults(false);
});
}
if (
newText.length > 0 &&
!this.props.viewState.searchState.showLocationSearchResults
) {
runInAction(() => {
this.toggleShowLocationSearchResults(true);
});
}
}
search() {
this.props.viewState.searchState.searchLocations();
}
toggleShowLocationSearchResults(bool) {
runInAction(() => {
this.props.viewState.searchState.showLocationSearchResults = bool;
});
}
startLocationSearch() {
this.toggleShowLocationSearchResults(true);
}
render() {
const { viewState, placeholder } = this.props;
const searchState = viewState.searchState;
const locationSearchText = searchState.locationSearchText;
const shouldShowResults =
searchState.locationSearchText.length > 0 &&
searchState.showLocationSearchResults;
return (
<Text textDarker>
<Box fullWidth>
<PresentationBox highlightBottom={shouldShowResults}>
<SearchBox
ref={this.locationSearchRef}
onSearchTextChanged={this.changeSearchText.bind(this)}
onDoSearch={this.search.bind(this)}
onFocus={this.startLocationSearch.bind(this)}
searchText={searchState.locationSearchText}
placeholder={placeholder}
/>
</PresentationBox>
{/* Results */}
<If condition={shouldShowResults}>
<Box
position="absolute"
fullWidth
column
css={`
top: 100%;
background-color: ${(props) => props.theme.greyLightest};
max-height: calc(100vh - 120px);
border-radius: 0 0 ${(props) => props.theme.radius40Button}px
${(props) => props.theme.radius40Button}px;
`}
>
{/* search {searchterm} in data catalog */}
{/* ~TODO: Put this back once we add a MobX DataCatalogSearch Provider~ */}
{/* TODO2: Implement a more generic MobX DataCatalogSearch */}
{searchState.catalogSearchProvider && (
<Box column paddedRatio={2}>
<SearchInDataCatalog
viewState={viewState}
handleClick={() => {
this.toggleShowLocationSearchResults(false);
}}
/>
</Box>
)}
<Box
column
css={`
overflow-y: auto;
`}
>
<For
each="search"
of={this.props.viewState.searchState.locationSearchResults}
>
<LocationSearchResults
key={search.searchProvider.name}
terria={this.props.terria}
viewState={this.props.viewState}
search={search}
locationSearchText={locationSearchText}
onLocationClick={(result) => {
addMarker(this.props.terria, result);
result.clickAction();
runInAction(() => {
searchState.showLocationSearchResults = false;
});
}}
isWaitingForSearchToStart={
searchState.isWaitingToStartLocationSearch
}
/>
</For>
</Box>
</Box>
</If>
</Box>
</Text>
);
}
}
SearchBoxAndResultsRaw.propTypes = {
terria: PropTypes.object.isRequired,
viewState: PropTypes.object.isRequired,
placeholder: PropTypes.string.isRequired
};
export default observer(SearchBoxAndResultsRaw);