import { observer } from "mobx-react"; import type { ReactNode } from "react"; import { ChangeEvent, Component, Fragment } from "react"; import { Translation, WithTranslation, withTranslation, TFunction } from "react-i18next"; import styled, { DefaultTheme, withTheme } from "styled-components"; import { HelpContentItem, PaneMode, StepItem, TrainerItem } from "../../../ReactViewModels/defaultHelpContent"; import ViewState from "../../../ReactViewModels/ViewState"; import Box from "../../../Styled/Box"; import Button, { RawButton } from "../../../Styled/Button"; import { GLYPHS, StyledIcon } from "../../../Styled/Icon"; import Select from "../../../Styled/Select"; import Spacing from "../../../Styled/Spacing"; import Text, { TextSpan } from "../../../Styled/Text"; import measureElement, { MeasureElementProps } from "../../HOCs/measureElement"; import { WithViewState, withViewState } from "../../Context"; import { applyTranslationIfExists } from "../../../Language/languageHelpers"; import StyledHtml from "../../Map/Panels/HelpPanel/StyledHtml"; import CloseButton from "../../Generic/CloseButton"; const TrainerBarWrapper = styled(Box)<{ isMapFullScreen: boolean }>` top: 0; left: ${(p) => p.isMapFullScreen ? 0 : Number(p.theme.workbenchWidth) + Number(p.theme.workbenchMargin) * 2}px; z-index: ${(p) => Number(p.theme.frontComponentZIndex) + 100}; `; // Help with discoverability const BoxTrainerExpandedSteps = styled(Box)``; const getSelectedTrainerFromHelpContent = ( viewState: ViewState, helpContent: HelpContentItem[] ) => { const selected = viewState.selectedTrainerItem; const found = helpContent.find((item) => item.itemName === selected); // Try and find the item that we selected, otherwise find the first trainer pane return ( found || helpContent.find((item) => item.paneMode === PaneMode.trainer) ); }; // Ripped from StyledHtml.jsx const Numbers = styled(Text)<{ darkBg: boolean }>` width: 22px; height: 22px; line-height: 22px; border-radius: 50%; background-color: ${(props) => props.theme.textLight}; `; const StepText = styled(Text).attrs({})` ol, ul { padding: 0; margin: 0; // Dislike these arbitrary aligned numbers but leaving it in for now padding-left: 17px; } li { padding-left: 8px; } `; const renderStep = ( step: StepItem, number: number, viewState: ViewState, options: { renderDescription: boolean; comfortable: boolean; footerComponent?: () => ReactNode; } = { renderDescription: true, comfortable: false, footerComponent: undefined } ) => { return ( {number} {(_t, { i18n }) => ( {applyTranslationIfExists(step.title, i18n)} )} {options.renderDescription && step?.markdownDescription && ( <> {/* {options.comfortable && } */} )} {options.footerComponent?.()} ); }; const renderOrderedStepList = function ( steps: StepItem[], viewState: ViewState ) { return steps.map((step: StepItem, index: number) => ( {renderStep(step, index + 1, viewState)} {index + 1 !== steps.length && } )); }; interface StepAccordionProps { selectedTrainerSteps: StepItem[]; t: TFunction; theme: DefaultTheme; selectedTrainer: TrainerItem; isShowingAllSteps: boolean; setIsShowingAllSteps: (bool: boolean) => void; isExpanded: boolean; setIsExpanded: (bool: boolean) => void; } interface StepAccordionState { isExpanded: boolean; } // Originally written as a SFC but measureElement only supports class components at the moment class StepAccordionRaw extends Component< StepAccordionProps & MeasureElementProps & WithTranslation & WithViewState, StepAccordionState > { refToMeasure: any; render() { const { viewState, selectedTrainerSteps, t, theme, selectedTrainer, isShowingAllSteps, setIsShowingAllSteps, isExpanded, setIsExpanded, heightFromMeasureElementHOC } = this.props; return ( setIsPeeking(true)} > {/* Non-expanded step */} { if (!isExpanded) this.refToMeasure = component; }} > {renderStep( selectedTrainerSteps[viewState.currentTrainerStepIndex], viewState.currentTrainerStepIndex + 1, viewState, { renderDescription: false, comfortable: true } )} {/* expanded version of the box step */} {isExpanded && ( (this.refToMeasure = component)} > {renderStep( selectedTrainerSteps[viewState.currentTrainerStepIndex], viewState.currentTrainerStepIndex + 1, viewState, { renderDescription: true, comfortable: true, footerComponent: () => ( <> setIsShowingAllSteps(!isShowingAllSteps)} title={ isShowingAllSteps ? t("trainer.hideAllSteps") : t("trainer.showAllSteps") } > {isShowingAllSteps ? t("trainer.hideAllSteps") : t("trainer.showAllSteps")} ) } )} )} setIsExpanded(!isExpanded)} // onMouseOver={() => setIsPeeking(true)} // onFocus={() => setIsPeeking(true)} title={ isExpanded ? t("trainer.collapseTrainer") : t("trainer.expandTrainer") } // onBlur={() => { // if (!isExpanded) setIsPeeking(false); // }} css={"z-index:2;"} > {/* Accordion / child steps? */} {isShowingAllSteps && ( {renderOrderedStepList(selectedTrainerSteps, viewState)} {selectedTrainer.footnote ? ( <> ) : ( )} )} ); } } const StepAccordion = withTranslation()( withViewState(measureElement(StepAccordionRaw)) ); interface TrainerBarProps extends WithTranslation, WithViewState { t: TFunction; theme: DefaultTheme; } export const TrainerBar = observer((props: TrainerBarProps) => { const { i18n, t, theme, viewState } = props; const terria = viewState.terria; const { helpContent } = terria.configParameters; // All these null guards are because we are rendering based on nested // map-owner defined (helpContent)content which could be malformed if (!viewState.trainerBarVisible || !helpContent) { return null; } const selectedTrainer = getSelectedTrainerFromHelpContent( viewState, helpContent ); const selectedTrainerItems = selectedTrainer?.trainerItems; if (!selectedTrainerItems) { return null; } const trainerItemIndex = viewState.currentTrainerItemIndex <= selectedTrainerItems.length ? viewState.currentTrainerItemIndex : 0; const selectedTrainerItem = selectedTrainerItems[trainerItemIndex]; const selectedTrainerSteps = selectedTrainerItem?.steps; if (!selectedTrainerSteps) { return null; } const isMapFullScreen = viewState.isMapFullScreen; return ( viewState.setTopElement("TrainerBar")} > {/* Trainer Items Dropdown */} {/* */} {/* */} {/* Trainer Steps within a Trainer Item */} viewState.setTrainerBarShowingAllSteps(bool) } isExpanded={viewState.trainerBarExpanded} setIsExpanded={(bool: boolean) => viewState.setTrainerBarExpanded(bool) } selectedTrainer={selectedTrainerItem} theme={theme} /> {/* Navigation & Close */} viewState.setTrainerBarVisible(false)} /> ); }); export default withTranslation()(withViewState(withTheme(TrainerBar)));