reactstrap#Button TypeScript Examples

The following examples show how to use reactstrap#Button. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: SiopApproval.tsx    From health-cards-tests with MIT License 6 votes vote down vote up
SiopApprovalModal: React.FC<SiopApprovalProps | null> = (props) => {
    if (!props.onApproval) {
        return null;
    }
    const focusCallback = useCallback(b => {
        setTimeout(() => b && b.focus());
    }, []);
    return <>
        <Modal isOpen={true}>
            <ModalHeader>Share with {props.with}?</ModalHeader>
            <ModalBody>The following details will be shared:
        <ul>
                    <li><b>Your ID Card:</b> allows secure communications</li>
                    {props.share.includes('https://smarthealth.cards#covid19') && <li>
                        <b>Your COVID Card:</b> labs and vaccines for COVID-19
            </li>}
                    {props.share.includes('https://smarthealth.cards#immunization') && <li>
                        <b>Your Immunizations Card:</b> full vaccine history
            </li>}
                </ul>
            </ModalBody>
            <ModalFooter>
                <Button color="danger" onClick={props.onDenial}>Do not share</Button>
                <Button innerRef={focusCallback} color="success" onClick={props.onApproval}>
                    Share {props.share.length > 0 ? "these cards" : "this card"}
                </Button>
            </ModalFooter>
        </Modal>
    </>;
}
Example #2
Source File: ErrorPopup.tsx    From nextclade with MIT License 6 votes vote down vote up
export function ErrorPopup() {
  const { t } = useTranslationSafe()
  const error = useRecoilValue(globalErrorAtom)
  const dismissError = useResetRecoilState(globalErrorAtom)

  const reload = useReloadPage('/')

  if (!error) {
    return null
  }

  return (
    <Modal centered isOpen backdrop="static" toggle={dismissError} fade={false} size="lg">
      <ModalHeader toggle={dismissError} tag="div">
        <h3 className="text-center text-danger">{t('Error')}</h3>
      </ModalHeader>

      <ModalBody>
        <ErrorContent error={error} />
        <ErrorContentExplanation />
      </ModalBody>

      <ModalFooter>
        <div className="ml-auto">
          <Button type="button" color="danger" title={t('Reload the page and start Nextclade fresh')} onClick={reload}>
            {t('Restart Nextclade')}
          </Button>
          <ButtonOk type="button" color="secondary" title={t('Close this dialog window')} onClick={dismissError}>
            {t('Dismiss')}
          </ButtonOk>
        </div>
      </ModalFooter>
    </Modal>
  )
}
Example #3
Source File: text_search_bar.tsx    From website with Apache License 2.0 6 votes vote down vote up
/**
 * Component for the search box in the rich search interface. The value of the
 * is not controlled to prevent constantly updating the hash URL.
 */
export function TextSearchBar({
  onSearch,
  initialValue,
  placeholder,
}: TextSearchBarPropType): JSX.Element {
  const [invalid, setInvalid] = useState(false);
  const [value, setValue] = useState(initialValue);
  const callback = () => (value ? onSearch(value) : setInvalid(true));
  return (
    <div className="input-query">
      <InputGroup>
        <Input
          invalid={invalid}
          placeholder={placeholder}
          value={value}
          onChange={(e) => {
            setValue(e.target.value);
            setInvalid(false);
          }}
          onKeyPress={(e) => e.key === "Enter" && callback()}
          className="pac-target-input"
        />
        <Button onClick={callback} className="rich-search-button">
          <span className="material-icons search rich-search-icon">search</span>
        </Button>
      </InputGroup>
    </div>
  );
}
Example #4
Source File: History.tsx    From mops-vida-pm-watchdog with MIT License 6 votes vote down vote up
History = () => {
  const history = useSelector((state) => state.report.history);
  const onDownload = () => {
    const encoded = JSON.stringify(history, null, 2);
    const link = document.createElement('a');
    link.href = URL.createObjectURL(new Blob([encoded], { type: 'application/json' }));
    link.download = makeDownloadFile();
    link.click();
  };
  return (
    <Row hidden={history.length === 0}>
      <h1>
        History{' '}
        <Button size='sm' color='link' onClick={onDownload}>
          Download
        </Button>
      </h1>
      <Table responsive borderless size='sm'>
        <thead>
          <tr>
            <th>#</th>
            <th>
              PM <sub>2.5</sub>
            </th>
          </tr>
        </thead>
        <tbody>
          {history.map(({ recordDate, pm25 }, index) => (
            <tr key={index}>
              <td>{recordDate?.toLocaleString()}</td>
              <td>{pm25}</td>
            </tr>
          ))}
        </tbody>
      </Table>
    </Row>
  );
}
Example #5
Source File: index.tsx    From atorch-console with MIT License 6 votes vote down vote up
AtorchConsole: React.FC = () => {
  const dispatch = useDispatch();
  const connected = useSelector((state) => state.report.connected);
  const latest = useSelector((state) => state.report.latest);
  const onConnect = () => dispatch(connect());
  return (
    <Container className={locals.container}>
      <Row className='ml-2 justify-content-center'>
        <Button outline onClick={onConnect}>
          {connected ? 'Disconnect' : 'Connect'}
        </Button>
      </Row>
      <PrintReport packet={latest} />
    </Container>
  );
}
Example #6
Source File: Modals.tsx    From health-cards-tests with MIT License 6 votes vote down vote up
ConfigEditOption: React.FC<{
    title: string;
    default: string;
    value: string;
    onChange: (string) => void;
}> = (props) => {
    return <>
        <InputGroup>
            <InputGroupAddon addonType='prepend' className='config-prepend'>
                <InputGroupText>{props.title}</InputGroupText>
            </InputGroupAddon>
            <Input type="text" value={props.value} onChange={e => props.onChange(e.target.value)}>
            </Input>
            <InputGroupAddon addonType="prepend">
                <Button onClick={e => props.onChange(props.default)}>↻</Button>
            </InputGroupAddon>
        </InputGroup>
        <br />
    </>;
}
Example #7
Source File: TutorPanel.tsx    From TutorBase with MIT License 6 votes vote down vote up
Panel = (props: IProps) => {
    let dispatch = useDispatch();
    let sidebarToggled = useSelector(selectSidebarToggled);

    let params : string = useLocation().pathname;
    let extension:string = params.split('/')[2];

    return (
        <div id="panel-wrapper">
            <Navbar className={classNames("navbar-expand-lg", "navbar-light", "bg-light", "border-bottom", "shadow")}>
                <Button className="btn-red" id="menu-toggle" onClick={() => {
                    dispatch(actions.toggleSidebar());
                }}>☰</Button>
            </Navbar>
            {/* <div class="container-fluid">
                <h2 className={classNames("mt-4", "hr")}>Tutor Dashboard</h2>
            </div>

            <div class="container-fluid">
                <h5 className={classNames("mt-4", "hr")}>Courses</h5>
                <p>This is where the tutor will be able to add or drop classes they are tutoring for.</p>
                <Button variant="danger">Add New Course</Button>
                <Button variant="danger">Drop Course</Button>

            </div> */}
            {extension === "overview" ? <TutorOverview></TutorOverview> : null}
            {extension === "meetings" ? <Meetings mode="Tutor"></Meetings> : null}
            {extension === "history" ? <TutorHistory></TutorHistory> : null}
            {extension === "analytics" ? <DataVisualization /> : null}
            {extension === "settings" ? <Settings></Settings> : null}

        </div>
    );
}
Example #8
Source File: TabPanelPaste.tsx    From nextclade with MIT License 5 votes vote down vote up
export function TabPanelPaste({ onConfirm, instructions, inputRef }: TabPanelPasteProps) {
  const { t } = useTranslationSafe()
  const [seqData, setSeqData] = useState<string>('')
  const hasSeqData = seqData.length > 0
  const change = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { setSeqData(e.target.value) }, []) // prettier-ignore
  const clear = useCallback(() => { setSeqData('') }, []) // prettier-ignore
  const confirm = useCallback(() => { onConfirm(seqData) }, [onConfirm, seqData]) // prettier-ignore

  return (
    <Form>
      <Row noGutters className="w-100 h-100">
        <Col className="w-100 h-100 d-flex flex-column">
          <Label className="w-100 h-100 d-flex flex-column flex-1">
            <span className="w-100 flex-grow-0">{instructions}</span>
            <TextInputMonospace
              className="w-100 flex-grow-1"
              type="textarea"
              autoComplete="off"
              autoCorrect="off"
              autoCapitalize="off"
              spellCheck="false"
              data-gramm="false"
              wrap="hard"
              data-gramm_editor="false"
              value={seqData}
              onChange={change}
              innerRef={inputRef}
            />
          </Label>

          <ButtonContainer>
            <Button
              className="mr-auto"
              disabled={!hasSeqData}
              type="button"
              color="link"
              title={t('Clear the text field')}
              onClick={clear}
            >
              {t('Clear')}
            </Button>

            <ButtonStyled
              className="ml-auto"
              disabled={!hasSeqData}
              type="button"
              color="primary"
              title={hasSeqData ? t('Accept the data') : t('Please provide the data first')}
              onClick={confirm}
            >
              {t('OK')}
            </ButtonStyled>
          </ButtonContainer>
        </Col>
      </Row>
    </Form>
  )
}
Example #9
Source File: index.tsx    From MLH-Fellow-Map with MIT License 5 votes vote down vote up
function MapPopup({ fellow }: { fellow: Fellow }) {
  const SocialLink = ({ name }: { name: SocialType }) => {
    if (!fellow[name]) return null;
    return (
      <a
        href={`${SocialLinks[name]}/${fellow[name]}`}
        target="_blank"
        rel="noreferrer"
      >
        <i className={`fab fa-${name}`} />
      </a>
    );
  };

  const socialLinks = Object.keys(SocialLinks).map((socialName, i) => (
    <SocialLink name={socialName as SocialType} key={i} />
  ));

  return (
    <div className="profile text-center">
      <div>
        <h4>{fellow.name}</h4>
      </div>
      <div>
        <p>{fellow.bio}</p>
      </div>
      <div className="divider" />
      <div className="social-links">{socialLinks}</div>
      <Link
        to={`/${fellow.github}`}
        state={{
          modal: true,
          noScroll: true,
        }}
      >
        <Button className="mt-4" color={'success'}>
          More Details
        </Button>
      </Link>
    </div>
  );
}
Example #10
Source File: Home.tsx    From reference-merchant with Apache License 2.0 5 votes vote down vote up
function Home(props) {
  const { t } = useTranslation("layout");
  const [selectedProduct, setSelectedProduct] = useState<Product>();
  const [products, setProducts] = useState<Product[] | undefined>();
  const [demoMode, setDemoMode] = useState<boolean>(props.demoMode === undefined ? false : true);

  const getProducts = async () => {
    try {
      setProducts(await new BackendClient().getProductsList());
    } catch (e) {
      console.error(e);
    }
  };

  useEffect(() => {
    //noinspection JSIgnoredPromiseFromCall
    getProducts();
  }, []);

  return (
    <>
      <TestnetWarning />
      <Container>
        <h1 className="text-center font-weight-bold mt-5">{t("name")}</h1>

        <section className="mt-5">
          {products && (
            <Row>
              {products.map((product, i) => (
                <Col key={product.gtin} md={6} lg={4}>
                  <Card key={product.gtin} className="mb-4">
                    <CardImg top src={product.image_url} />
                    <CardBody>
                      <CardTitle className="font-weight-bold h5">{product.name}</CardTitle>
                      <CardText>{product.description}</CardText>
                    </CardBody>
                    <CardFooter>
                      <Row>
                        <Col>
                          <div>
                            <strong>Price:</strong> {product.price / 1000000} {product.currency}
                          </div>
                        </Col>
                        <Col lg={4} className="text-right">
                          <Button
                            color="secondary"
                            block
                            className="btn-sm"
                            onClick={() => setSelectedProduct(products[i])}
                          >
                            Buy Now
                          </Button>
                        </Col>
                      </Row>
                    </CardFooter>
                  </Card>
                </Col>
              ))}
            </Row>
          )}
          {!products && <ProductsLoader />}
        </section>
      </Container>
      <Payment
        demoMode={demoMode}
        product={selectedProduct}
        isOpen={!!selectedProduct}
        onClose={() => setSelectedProduct(undefined)}
      />
    </>
  );
}
Example #11
Source File: DatasetCurrent.tsx    From nextclade with MIT License 5 votes vote down vote up
ChangeButton = styled(Button)`
  flex: 0 0 auto;
  height: 2.1rem;
  min-width: 100px;
  margin-left: auto;
`
Example #12
Source File: SiopApproval.tsx    From health-cards-tests with MIT License 5 votes vote down vote up
SiopRequestReceiver: React.FC<SiopRequestReceiverProps> = (props) => {

    const [currentCode, setCurrentCode] = useState("")

    const runningQrScanner = useRef(null)

    let qrScanner; // scope bound to callback
    const videoCallback = useCallback((videoElement) => {
        if (!videoElement) {
            if (qrScanner) {
                qrScanner.destroy()
            }
            return;
        }
        qrScanner = new QrScanner(videoElement, result => {
            if (result.length)
                props.onReady(result)
        });
        runningQrScanner.current = qrScanner 
        qrScanner.start();
    }, [])

    useEffect(() => {
        if (props.redirectMode === "window-open") {
            const onMessage = ({ data, source }) => {
                props.onReady(data)
            }
            const registered = window.addEventListener("message", onMessage)
            const opened = window.open(props.startUrl)
            return () => {
                window.removeEventListener("message", onMessage)
            }
        }
    }, [])

    return props.redirectMode === "qr" ? <>
        <Modal isOpen={true}>
            <ModalHeader>Connect to {props.label}</ModalHeader>
            <ModalBody>
                <div>Scan a QR Code</div>
                <video ref={videoCallback} style={{ maxWidth: "100%", maxHeight: "30vh" }} />
            </ModalBody>
            <ModalFooter  >
                <InputGroup>
                    <Input placeholder="Or paste a URL directly..." type="text" autoFocus={true} value={currentCode} onChange={e => setCurrentCode(e.target.value)}>
                    </Input>
                </InputGroup>

                {props.onCancel ?
                    <Button color="error" onClick={e => props.onCancel()}>
                        Cancel
                </Button> : ""
                }

                <Button color="success" onClick={e => props.onReady(currentCode)}>
                    Connect
                </Button>

            </ModalFooter>
        </Modal>
    </> : <>
            <span>Waiting for redirect...</span>
        </>
}
Example #13
Source File: UploadedFileInfoCompact.tsx    From nextclade with MIT License 5 votes vote down vote up
UploadZoneButton = styled(Button)`
  flex: 0 0 120px;
  margin-left: auto;
`
Example #14
Source File: page.tsx    From website with Apache License 2.0 5 votes vote down vote up
render(): JSX.Element {
    return (
      <>
        <StatVarWidget
          openSvHierarchyModal={this.state.showSvHierarchyModal}
          openSvHierarchyModalCallback={this.toggleSvHierarchyModal}
          collapsible={false}
          svHierarchyType={StatVarHierarchyType.STAT_VAR}
          samplePlaces={[]}
          deselectSVs={() => this.updateHash("")}
          selectedSVs={{ [this.state.statVar]: {} }}
          selectSV={(sv) => this.updateHash(sv)}
        />
        <div id="plot-container">
          <div className="container">
            {!this.state.statVar && (
              <>
                <Info />
                <Button
                  className="d-lg-none"
                  color="primary"
                  onClick={this.toggleSvHierarchyModal}
                >
                  Select variable
                </Button>
              </>
            )}
            {this.state.error && this.state.statVar && (
              <div>Error fetching data for {this.state.statVar}.</div>
            )}
            {!this.state.error && this.state.statVar && (
              <>
                <Explorer
                  description={this.state.description}
                  displayName={this.state.displayName}
                  statVar={this.state.statVar}
                  summary={this.state.summary}
                  urls={this.state.urls}
                >
                  <div className="d-lg-none pt-3">
                    <Button
                      color="primary"
                      onClick={this.toggleSvHierarchyModal}
                    >
                      Explore another variable
                    </Button>
                  </div>
                </Explorer>
              </>
            )}
          </div>
        </div>
      </>
    );
  }
Example #15
Source File: TabPanelPaste.tsx    From nextclade with MIT License 5 votes vote down vote up
ButtonStyled = styled(Button)`
  width: 100px;
`
Example #16
Source File: ClientPanel.tsx    From TutorBase with MIT License 5 votes vote down vote up
Panel = () => {
    const isMobile = useMediaQuery('(max-width: 1200px)')

    let dispatch = useDispatch();
    let sidebarToggled = useSelector(selectSidebarToggled);
    let clientFlowData = useSelector(selectClientFlowData);
    let steps = ["Subject", "Class", "Tutor", "Time", "Notes"];
    let params : string = useLocation().pathname;
    params = params.split('/')[2];

    let body = <FormParent />;
    if (params === 'meetings') {
        body = <Meetings mode="Client" />;
    } else if (params === 'history') {
        body = <ClientHistory />;
    } else if (params === 'settings') {
        body = <ClientSettings />
    }

    const renderSteps = () => {
       let flow = [];

       if(!isMobile)
        flow.push(<FlowText>Schedule a tutoring session {"→"} &nbsp;</FlowText>);

       steps.forEach((step, index) => {
           if(clientFlowData.currentStep === index)
               if(isMobile)
                   flow.push(<FlowText><b style={{color: 'black'}}> {index + 1} </b></FlowText>)
               else
                   flow.push(<FlowText><b style={{color: 'black'}}> {step} </b></FlowText>)
           else
               if(isMobile)
                   flow.push(<FlowText>{index + 1}</FlowText>)
               else
                   flow.push(<FlowText> {step} </FlowText>)

           if(index != 4)
               flow.push(<FlowText> &nbsp; {"→"} &nbsp;</FlowText>)
       })

        return flow;
    }

    return (
        <div id="panel-wrapper" style={{width:'100vw'}}>
            <Helmet>
                <meta charSet="utf-8" />
                <title>TutorBase - Dashboard</title>
            </Helmet>

            <Navbar className={classNames("navbar-expand-lg", "navbar-light", "bg-light", "border-bottom", "shadow")}>
                <Button className="btn-red" id="menu-toggle" onClick={() => {
                    dispatch(actions.toggleSidebar());
                }} style={{marginLeft: '0.5em'}}>
                    ☰
                </Button>

                
            </Navbar>

            <div className="container-fluid" style={{maxWidth:'100vw'}}>
            {params === "schedule" && (
                    <Container >
                        {renderSteps().map((component, index) => (
                            <React.Fragment key={index}>
                                { component }
                            </React.Fragment>
                        ))}
                    </Container>
                )}
                {body}</div>
        </div>
    );
}
Example #17
Source File: place_options.tsx    From website with Apache License 2.0 5 votes vote down vote up
export function PlaceOptions(props: PlaceOptionsProps): JSX.Element {
  const { placeInfo } = useContext(Context);

  useEffect(() => {
    if (!placeInfo.value.selectedPlace.dcid) {
      // Do nothing here because no place has been chosen yet.
      return;
    }
    if (_.isNull(placeInfo.value.selectedPlace.types)) {
      getNamedTypedPlace(placeInfo.value.selectedPlace.dcid).then(
        (selectedPlace) => {
          placeInfo.set({ ...placeInfo.value, selectedPlace });
        }
      );
      return;
    }
    if (_.isNull(placeInfo.value.parentPlaces)) {
      getParentPlacesPromise(
        placeInfo.value.selectedPlace.dcid
      ).then((parentPlaces) => placeInfo.setParentPlaces(parentPlaces));
      return;
    }
    if (
      placeInfo.value.enclosedPlaceType &&
      _.isEmpty(placeInfo.value.enclosingPlace.dcid)
    ) {
      loadEnclosingPlace(placeInfo);
    }
  }, [
    placeInfo.value.selectedPlace,
    placeInfo.value.parentPlaces,
    placeInfo.value.enclosedPlaceType,
  ]);

  return (
    <PlaceSelector
      selectedPlace={placeInfo.value.selectedPlace}
      enclosedPlaceType={placeInfo.value.enclosedPlaceType}
      onPlaceSelected={placeInfo.setSelectedPlace}
      onEnclosedPlaceTypeSelected={placeInfo.setEnclosedPlaceType}
      getEnclosedPlaceTypes={getAllChildPlaceTypes}
      customSearchPlaceholder={"Enter a country or state to get started"}
    >
      <Row className="d-lg-none">
        <Col>
          <Button color="primary" onClick={props.toggleSvHierarchyModal}>
            Select variable
          </Button>
        </Col>
      </Row>
    </PlaceSelector>
  );
}
Example #18
Source File: SettingsButton.tsx    From nextclade with MIT License 5 votes vote down vote up
ButtonOk = styled(Button)<ButtonProps>`
  width: 100px;
`
Example #19
Source File: source_selector.tsx    From website with Apache License 2.0 5 votes vote down vote up
export function SourceSelector(props: SourceSelectorPropType): JSX.Element {
  const [modalOpen, setModalOpen] = useState(false);
  const [modalSelections, setModalSelections] = useState(
    getModalSelections(props.svInfoList)
  );

  useEffect(() => {
    // If modal is closed without updating sources, we want to reset the
    // selections in the modal.
    if (!modalOpen) {
      setModalSelections(getModalSelections(props.svInfoList));
    }
  }, [props.svInfoList, modalOpen]);

  return (
    <>
      <Button
        className={`${SELECTOR_PREFIX}-open-modal-button`}
        size="sm"
        color="light"
        onClick={() => setModalOpen(true)}
      >
        Select {props.svInfoList.length > 1 ? "Sources" : "Source"}
      </Button>
      <Modal
        isOpen={modalOpen}
        className={`${SELECTOR_PREFIX}-modal`}
        style={{ maxWidth: MODAL_MAX_WIDTH }}
      >
        <ModalHeader toggle={() => setModalOpen(false)}>
          Source Selector
        </ModalHeader>
        <ModalBody>
          {props.svInfoList.map((svInfo) => {
            const selectedMetahash = modalSelections[svInfo.dcid];
            return (
              <Collapsible
                key={svInfo.dcid}
                trigger={getSVTriggerJsx(false, svInfo, selectedMetahash)}
                triggerWhenOpen={getSVTriggerJsx(
                  true,
                  svInfo,
                  selectedMetahash
                )}
                open={props.svInfoList.length < 2}
              >
                <div className={`${SELECTOR_PREFIX}-options-section`}>
                  {getSourceOptionJsx(
                    svInfo,
                    "",
                    modalSelections,
                    setModalSelections
                  )}
                  {getSourceOptionSectionJsx(
                    svInfo,
                    modalSelections,
                    setModalSelections
                  )}
                </div>
              </Collapsible>
            );
          })}
        </ModalBody>
        <ModalFooter>
          <Button color="primary" onClick={onConfirm}>
            Update
          </Button>
        </ModalFooter>
      </Modal>
    </>
  );

  function onConfirm(): void {
    props.onSvMetahashUpdated(modalSelections);
    setModalOpen(false);
  }
}
Example #20
Source File: _error.tsx    From nextclade with MIT License 5 votes vote down vote up
function ErrorPage({ statusCode, title, error }: ErrorPageProps) {
  const { t } = useTranslationSafe()

  const reload = useReloadPage('/')

  const titleText = useMemo(() => {
    const statusCodes: { [code: number]: string } = {
      400: t('Bad Request'),
      404: t('This page could not be found'),
      405: t('Method not allowed'),
      500: t('Internal server error'),
    }
    const statusText = get(statusCodes, statusCode ?? 0, undefined)
    return title ?? statusText ?? t('An unexpected error has occurred')
  }, [statusCode, t, title])

  const errorContent = useMemo(() => {
    if (!error) {
      return null
    }

    return (
      <Row noGutters>
        <Col>
          <ErrorContent error={error} />
        </Col>
      </Row>
    )
  }, [error])

  return (
    <LayoutResults>
      <MainContent>
        <MainSectionTitle />

        <Row noGutters>
          <Col className="text-center text-danger">
            <h2>{titleText}</h2>
          </Col>
        </Row>

        {errorContent}

        <Row noGutters>
          <Col>
            <ErrorContentExplanation />
          </Col>
        </Row>

        <Row noGutters>
          <Col className="w-100 d-flex">
            <Button
              className="ml-auto"
              type="button"
              color="danger"
              title={t('Reload the page and start Nextclade fresh')}
              onClick={reload}
            >
              {t('Restart Nextclade')}
            </Button>
          </Col>
        </Row>
      </MainContent>
    </LayoutResults>
  )
}
Example #21
Source File: MeasurementInterval.tsx    From mops-vida-pm-watchdog with MIT License 5 votes vote down vote up
MeasurementInterval: React.FC = () => {
  const [open, setOpen] = React.useState(false);
  const dispatch = useDispatch();
  const connected = useSelector((state) => state.report.connected);
  const interval = useSelector((state) => state.report.latest.measurementInterval);
  const enabled = useSelector((state) => state.report.latest.measurementIntervalEnabled);
  const onToggle = () => setOpen(connected && !open);
  const onChange = async (value: number) => {
    setOpen(false);
    await dispatch(setMeasurementInterval(value));
    await dispatch(setMeasurementEnable(true));
  };
  const onDisable = async () => {
    setOpen(false);
    dispatch(setMeasurementEnable(false));
  };
  return (
    <>
      <span onClick={onToggle}>{enabled ? `${interval} minutes` : 'Disabled'}</span>
      <Modal placement='bottom' isOpen={open} toggle={onToggle}>
        <ModalHeader>Measurement interval</ModalHeader>
        <ModalBody>
          <p>Current Interval: {interval} minutes</p>
          <ListGroup>
            {steps.map((step) => (
              <ListGroupItem onClick={() => onChange(step)} key={step} active={step === interval}>
                {step} minutes
              </ListGroupItem>
            ))}
          </ListGroup>
        </ModalBody>
        <ModalFooter>
          <Button color='primary' onClick={onDisable} hidden={!enabled}>
            Disable
          </Button>
          <Button color='secondary' onClick={onToggle}>
            Cancel
          </Button>
        </ModalFooter>
      </Modal>
    </>
  );
}
Example #22
Source File: Modals.tsx    From health-cards-tests with MIT License 5 votes vote down vote up
ConfigEditModal: React.FC<{
    defaultUiState: UiState;
    uiState: UiState;
    dispatch: any;
}> = ({ defaultUiState, uiState, dispatch }) => {

    const onSave = ui => {
        window.location.hash = JSON.stringify({ ...ui, editingConfig: undefined }, null, 0)
        dispatch({ type: 'save-ui-state', newState: ui })
    }
    const onDiscard = () => {
        dispatch({ type: 'toggle-editing-config' });
    }

    const [ui, setUi] = useState(uiState);
    return <>
        <Modal isOpen={true}>
            <ModalHeader>Edit Config Settings</ModalHeader>
            <ModalBody>
                <ConfigEditOption
                    title="FHIR Server"
                    default={defaultUiState.fhirClient.server}
                    value={ui.fhirClient.server}
                    onChange={v => setUi({ ...ui, fhirClient: { ...ui.fhirClient, server: v } })} />
                <ConfigEditOption
                    title="Client ID"
                    default={defaultUiState.fhirClient.client_id}
                    value={ui.fhirClient.client_id}
                    onChange={v => setUi({ ...ui, fhirClient: { ...ui.fhirClient, client_id: v } })} />
                <ConfigEditOption
                    title="Client Secret"
                    default={defaultUiState.fhirClient.client_secret}
                    value={ui.fhirClient.client_secret}
                    onChange={v => setUi({ ...ui, fhirClient: { ...ui.fhirClient, client_secret: v } })} />
                <ConfigEditOption
                    title="Scopes"
                    default={defaultUiState.fhirClient.scope}
                    value={ui.fhirClient.scope}
                    onChange={v => setUi({ ...ui, fhirClient: { ...ui.fhirClient, scope: v } })} />
            </ModalBody>
            <ModalFooter>
                <Button color="danger" onClick={e => onDiscard()}>Cancel</Button>
                <Button color="success" onClick={e => onSave(ui)}>Save to Bookmarkable URL</Button>
            </ModalFooter>
        </Modal>
    </>;
}
Example #23
Source File: WhatsNewButton.tsx    From nextclade with MIT License 5 votes vote down vote up
ButtonOk = styled(Button)<ButtonProps>`
  width: 100px;
`
Example #24
Source File: DiemCheckout.tsx    From reference-merchant with Apache License 2.0 4 votes vote down vote up
export default function DiemCheckout({ paymentId, orderId, demoMode }: DiemCheckoutProps) {
  const [paymentOptions, setPaymentOptions] = useState<
    PaymentOptions | undefined
  >();
  const [selectedOption, setSelectedOption] = useState(0);
  const [chooseWallet, setChooseWallet] = useState(true);

  useEffect(() => {
    let isOutdated = false;

    const fetchOrder = async () => {
      try {
        const fetched = await new Vasp().getPaymentOptions(paymentId);

        if (!isOutdated) {
          setPaymentOptions(fetched);
        }
      } catch (e) {
        console.error("Unexpected error", e);
      }
    };

    // noinspection JSIgnoredPromiseFromCall
    fetchOrder();

    return () => {
      isOutdated = true;
    };
  }, [paymentId]);

  const onCurrencyClick = (index: number) => setSelectedOption(index);

  if (!paymentOptions) {
    return <div>Loading...</div>;
  }

  const handleClick = () => {
    setChooseWallet(false)
  }

  return (
    <>
      <div className="w-100">
        <Row>
          <Col className="text-nowrap text-right">Total price:</Col>
          <Col className="d-flex align-items-center">
            <span className="text-nowrap">
            {fiatToHumanFriendly(paymentOptions.fiatPrice)}{" "}
            {paymentOptions.fiatCurrency}
            </span>
            <FontAwesomeIcon size="xs" icon={faQuestionCircle} className="ml-2" id="totalPriceHelp" />
            <UncontrolledTooltip target="totalPriceHelp">
              The price in fiat set by the merchant
            </UncontrolledTooltip>
          </Col>
        </Row>
        <Row>
          <Col className="text-nowrap text-right align-self-center">
            Payment currency:
          </Col>
          <Col className="d-flex align-items-center">
            <UncontrolledDropdown>
              <DropdownToggle caret color="outline-dark" className="py-0 px-2">
                {paymentOptions.options[selectedOption].currency}
              </DropdownToggle>
              <DropdownMenu>
                {paymentOptions.options.map((op, i) => (
                  <DropdownItem
                    key={op.currency}
                    onClick={() => onCurrencyClick(i)}
                  >
                    {op.currency}
                  </DropdownItem>
                ))}
              </DropdownMenu>
            </UncontrolledDropdown>
            <FontAwesomeIcon size="xs" icon={faQuestionCircle} className="ml-2" id="currencyHelp" />
            <UncontrolledTooltip target="currencyHelp">
              Please select a Diem currency
            </UncontrolledTooltip>
          </Col>
        </Row>
        <Row>
          <Col className="text-nowrap text-right">Total to pay:</Col>
          <Col className="d-flex align-items-center">
            <span className="text-nowrap">
              {diemToHumanFriendly(
                paymentOptions.options[selectedOption].amount
              )}{" "}
              {paymentOptions.options[selectedOption].currency}
            </span>
            <FontAwesomeIcon size="xs" icon={faQuestionCircle} className="ml-2" id="totalToPayHelp" />
            <UncontrolledTooltip target="totalToPayHelp">
              The amount you will be changed in Diem
            </UncontrolledTooltip>
          </Col>
        </Row>
      </div>
      <div>
        {!chooseWallet ? (
          <>
            <QRCode
              className="img-fluid mt-4"
              size={192}
              value={paymentOptions.options[selectedOption].paymentLink}
              imageSettings={{
                src: require("../logo.svg"),
                height: 32,
                width: 32,
                excavate: true,
              }}
            />
            <div className="text-center small py-4 font-weight-bold">
              - OR -
            </div>
            <div className="text-center">
              <Button color="primary" size="sm" onClick={() => setChooseWallet(true)}>
                Open in Diem wallet
              </Button>
            </div>
          </>
        ) : (
          <>
            <div className="mt-4">
              <div className="text-center">Choose your wallet:</div>
              <PayWithDiem
                paymentInfo={paymentOptions}
                orderId={orderId}
                demoMode={demoMode}
              />
            </div>
            <div className="text-center small py-4 font-weight-bold">
              - OR -
            </div>
            <div className="text-center">
              <Button color="primary" size="sm" onClick={()=>handleClick()}>
                Scan QR
              </Button>
            </div>
          </>
        )}
      </div>
    </>
  );
}
Example #25
Source File: MainInputFormSequenceFilePicker.tsx    From nextclade with MIT License 4 votes vote down vote up
export function MainInputFormSequenceFilePicker() {
  const { t } = useTranslationSafe()

  const datasetCurrent = useRecoilValue(datasetCurrentAtom)
  const [qrySeq, setQrySeq] = useRecoilState(qrySeqInputAtom)
  const removeQrySeq = useResetRecoilState(qrySeqInputAtom)
  const qrySeqError = useRecoilValue(qrySeqErrorAtom)

  const canRun = useRecoilValue(canRunAtom)
  const [shouldRunAutomatically, setShouldRunAutomatically] = useRecoilState(shouldRunAutomaticallyAtom)
  const hasRequiredInputs = useRecoilValue(hasRequiredInputsAtom)
  const hasInputErrors = useRecoilValue(hasInputErrorsAtom)

  const icon = useMemo(() => <FileIconFasta />, [])

  const run = useRunAnalysis()

  const setSequences = useCallback(
    (input: AlgorithmInput) => {
      setQrySeq(input)

      if (shouldRunAutomatically) {
        run()
      }
    },
    [run, setQrySeq, shouldRunAutomatically],
  )

  const setExampleSequences = useCallback(() => {
    if (datasetCurrent) {
      setQrySeq(new AlgorithmInputDefault(datasetCurrent))

      if (shouldRunAutomatically) {
        run()
      }
    }
  }, [datasetCurrent, run, setQrySeq, shouldRunAutomatically])

  const { isRunButtonDisabled, runButtonColor, runButtonTooltip } = useMemo(() => {
    const isRunButtonDisabled = !(canRun && hasRequiredInputs) || hasInputErrors
    return {
      isRunButtonDisabled,
      runButtonColor: isRunButtonDisabled ? 'secondary' : 'success',
      runButtonTooltip: isRunButtonDisabled
        ? t('Please provide input files for the algorithm')
        : t('Launch the algorithm!'),
    }
  }, [canRun, hasInputErrors, hasRequiredInputs, t])

  const LoadExampleLink = useMemo(() => {
    const cannotLoadExample = hasRequiredInputs || hasInputErrors || !datasetCurrent
    return (
      <Button color="link" onClick={setExampleSequences} disabled={cannotLoadExample}>
        {t('Load example')}
      </Button>
    )
  }, [datasetCurrent, hasInputErrors, hasRequiredInputs, setExampleSequences, t])

  const onToggleRunAutomatically = useCallback(() => {
    setShouldRunAutomatically((shouldRunAutomatically) => !shouldRunAutomatically)
  }, [setShouldRunAutomatically])

  return (
    <SequenceFilePickerContainer>
      <FilePicker
        title={t('Provide sequence data')}
        icon={icon}
        exampleUrl="https://example.com/sequences.fasta"
        pasteInstructions={t('Enter sequence data in FASTA or plain text format')}
        input={qrySeq}
        error={qrySeqError}
        isInProgress={false}
        onRemove={removeQrySeq}
        onInput={setSequences}
      />

      <Row noGutters className="mt-2">
        <Col className="w-100 d-flex">
          <FlexLeft>
            <Form className="d-flex h-100 mt-1">
              <FormGroup className="my-auto">
                <Toggle
                  identifier="toggle-run-automatically"
                  checked={shouldRunAutomatically}
                  onCheckedChanged={onToggleRunAutomatically}
                >
                  <span title="Run Nextclade automatically after sequence data is provided">
                    {t('Run automatically')}
                  </span>
                </Toggle>
              </FormGroup>
            </Form>
          </FlexLeft>

          <FlexRight>
            {LoadExampleLink}

            <ButtonRunStyled
              disabled={isRunButtonDisabled}
              color={runButtonColor}
              onClick={run}
              title={runButtonTooltip}
            >
              {t('Run')}
            </ButtonRunStyled>
          </FlexRight>
        </Col>
      </Row>
    </SequenceFilePickerContainer>
  )
}
Example #26
Source File: CovidCard.tsx    From health-cards-tests with MIT License 4 votes vote down vote up
CovidCard: React.FC<{
    holderState: HolderState,
    smartState: SmartState,
    uiState: UiState,
    displayQr: (vc) => Promise<void>,
    openScannerUi: () => Promise<void>,
    connectToIssuer: () => Promise<void>,
    connectToFhir: () => Promise<void>,
    dispatchToHolder: (e: Promise<any>) => Promise<void>
}> = ({ holderState, smartState, uiState, displayQr, openScannerUi, connectToFhir, connectToIssuer, dispatchToHolder }) => {
    const issuerInteractions = holderState.interactions.filter(i => i.siopPartnerRole === 'issuer').slice(-1)
    const issuerInteraction = issuerInteractions.length ? issuerInteractions[0] : null


    let currentStep = CardStep.CONFIGURE_WALLET;
    /* tslint:disable-next-line:prefer-conditional-expression */
    if (issuerInteraction?.status !== 'complete') {
        currentStep = CardStep.CONNECT_TO_ISSUER;
    } else {
        currentStep = CardStep.DOWNLOAD_CREDENTIAL;
    }
    if (holderState.vcStore.length) {
        currentStep = CardStep.COMPLETE
    }

    const retrieveVcClick = async () => {
        const onMessage = async ({ data, source }) => {
            const { verifiableCredential } = data
            window.removeEventListener("message", onMessage)
            await dispatchToHolder(receiveVcs(verifiableCredential, holderState))
        }
        window.addEventListener("message", onMessage)
        window.open(uiState.issuer.issuerDownloadUrl)
    }

    useEffect(() => {
        if (smartState?.access_token && holderState.vcStore.length === 0) {
            const credentials = axios.post(uiState.fhirClient.server + `/Patient/${smartState.patient}/$health-cards-issue`, {
                "resourceType": "Parameters",
                "parameter": [{
                    "name": "credentialType",
                    "valueUri": "https://smarthealth.cards#immunization"
                },{
                    "name": "presentationContext",
                    "valueUri": "https://smarthealth.cards#presentation-context-online"
                }, {
                    "name": "encryptForKeyId",
                    "valueString": "#encryption-key-1"
                }]
            })
            credentials.then(response => {
                const vcs = response.data.parameter.filter(p => p.name === 'verifiableCredential').map(p => p.valueString)
                dispatchToHolder(receiveVcs(vcs, holderState))
            })
        }
    }, [smartState])


    const covidVcs = holderState.vcStore.filter(vc => vc.type.includes("https://smarthealth.cards#covid19"));

    const resources = covidVcs.flatMap(vc =>
        vc.vcPayload.vc.credentialSubject.fhirBundle.entry
            .flatMap(e => e.resource))

    const doses = resources.filter(r => r.resourceType === 'Immunization').length;
    const patient = resources.filter(r => r.resourceType === 'Patient')[0];
    console.log("P", patient);


    useEffect(() => {
        if (covidVcs.length === 0) {
            return;
        }
        let file = new File([JSON.stringify({
            "verifiableCredential": covidVcs.map(v => v.vcSigned)
        })], "c19.smart-health-card", {
            type: "application/smart-health-card"
        })
        const url = window.URL.createObjectURL(file);
        setDownloadFileUrl(url)
    }, [covidVcs.length])
    const [downloadFileUrl, setDownloadFileUrl] = useState("");

    return <> {
        currentStep === CardStep.COMPLETE && <Card style={{ border: "1px solid grey", padding: ".5em", marginBottom: "1em" }}>
            <CardTitle style={{ fontWeight: "bolder" }}>
                COVID Cards ({covidVcs.length})
                </CardTitle>

            <CardSubtitle className="text-muted">Your COVID results are ready to share, based on {" "}
                {resources && <>{resources.length} FHIR Resource{resources.length > 1 ? "s" : ""} <br /> </>}
            </CardSubtitle>
            <ul>
                <li> Name: {patient.name[0].given[0]} {patient.name[0].family}</li>
                <li> Birthdate: {patient.birthDate}</li>
                <li> Immunization doses received: {doses}</li>
            </ul>
            <Button className="mb-1" color="info" onClick={() => displayQr(covidVcs[0])}>Display QR</Button>
            <a href={downloadFileUrl} download="covid19.smart-health-card">Download file</a>
        </Card>
    } {currentStep < CardStep.COMPLETE &&
        <Card style={{ border: ".25em dashed grey", padding: ".5em", marginBottom: "1em" }}>
            <CardTitle>COVID Cards </CardTitle>
            <CardSubtitle className="text-muted">You don't have any COVID cards in your wallet yet.</CardSubtitle>

            <Button disabled={true} className="mb-1" color="info">
                {currentStep > CardStep.CONFIGURE_WALLET && '✓ '} 1. Set up your Health Wallet</Button>

            <RS.UncontrolledButtonDropdown className="mb-1" >
                <DropdownToggle caret color={currentStep === CardStep.CONNECT_TO_ISSUER ? 'success' : 'info'} >
                    {currentStep > CardStep.CONNECT_TO_ISSUER && '✓ '}
                                    2. Get your Vaccination Credential
                                </DropdownToggle>
                <DropdownMenu style={{ width: "100%" }}>
                    <DropdownItem onClick={connectToFhir} >Connect with SMART on FHIR </DropdownItem>
                    <DropdownItem >Load from file (todo)</DropdownItem>
                </DropdownMenu>
            </RS.UncontrolledButtonDropdown>
            <Button
                disabled={currentStep !== CardStep.DOWNLOAD_CREDENTIAL}
                onClick={retrieveVcClick}
                className="mb-1"
                color={currentStep === CardStep.DOWNLOAD_CREDENTIAL ? 'success' : 'info'} >
                3. Save COVID card to wallet</Button>
        </Card>
        }
    </>

}
Example #27
Source File: FeedbackForm.tsx    From TutorBase with MIT License 4 votes vote down vote up
export default function FeedbackForm({apptTutorId}: IProps) {
    const [formOpen, setFormOpen] = useState(false);
    const clientData = useSelector(selectClientData);
    const [feedbackMessage, setFeedbackMessage] = useState("");
    const [rating, setRating] = useState(0);

    const submitFeedback = async () => {
        let clientId = clientData.clientId;
        let tutorId = apptTutorId;

        setFormOpen(false)

        await api.SubmitFeedback({
            clientId: clientId,
            tutorId: tutorId,
            message: feedbackMessage,
            rating: rating
        });

        // TODO: Show some Toast UI confirming that the rating was submitted
    }

    return (
        <Container>
            <Button onClick={(e) => {
                setFormOpen(true);
                e.stopPropagation();
            }}
                    style={{
                        minWidth: '60px',
                        lineHeight: '1',
                        position: "relative",
                    }}>
                <div style={{
                    fontSize: "clamp(8px, 1vw, 12px)"
                }}>
                    Rate
                    <FontAwesomeIcon icon={faStar} style={{marginLeft: '5px'}}/>
                </div>
            </Button>

            <Modal
                isOpen={formOpen}
                toggle={() => setFormOpen(!formOpen)}
            >
                <ModalHeader toggle={() => setFormOpen(!formOpen)}>
                    Please give feedback on your session below.
                </ModalHeader>
                <StyledBody>
                    <Label for="exampleText">
                        What did you think of your session?
                    </Label>
                    <Input
                        id="exampleText"
                        name="text"
                        type="textarea"
                        value={feedbackMessage}
                        onChange={(element) => setFeedbackMessage(element.target.value)}
                    />

                    <Label style={{marginTop: '1em'}}>
                        How would you rate your session?
                    </Label>
                    <div style={{lineHeight: '0.75'}}>
                        <ReactStars size={40} value={rating} onChange={new_rating => setRating(new_rating)}/>
                    </div>
                </StyledBody>
                <ModalFooter>
                    <Button
                        color="primary"
                        onClick={() => submitFeedback()}
                    >
                        Submit
                    </Button>
                    {' '}
                    <Button onClick={() => setFormOpen(false)}>
                        Cancel
                    </Button>
                </ModalFooter>
            </Modal>
        </Container>
    )
}
Example #28
Source File: OrderDetails.tsx    From reference-merchant with Apache License 2.0 4 votes vote down vote up
function OrderDetails({ orderId }: OrderDetailsProps) {
  const { t } = useTranslation("order");
  const [order, setOrder] = useState<Order | undefined | null>();

  const tx =
    order && order.paymentStatus.blockchainTxs.length > 0
      ? order.paymentStatus.blockchainTxs[0]
      : null;
  const refundTx =
    order &&
    order.paymentStatus.blockchainTxs.length > 1 &&
    order.paymentStatus.blockchainTxs[1].isRefund
      ? order.paymentStatus.blockchainTxs[1]
      : null;

  useEffect(() => {
    let isOutdated = false;

    const fetchOrder = async () => {
      try {
        const fetched = await new BackendClient().getOrderDetails(orderId);

        if (!isOutdated) {
          setOrder(fetched);
        }
      } catch (e) {
        console.error("Unexpected error", e);
      }
    };

    // noinspection JSIgnoredPromiseFromCall
    fetchOrder();

    return () => {
      isOutdated = true;
    };
  }, [orderId]);

  const cashOut = async () => {
    const client = new BackendClient();
    await client.payout(order!.vaspPaymentRef);
    const fetched = await new BackendClient().getOrderDetails(orderId);
    if (fetched) {
      setOrder(fetched);
    }
  };

  const refund = async () => {
    const client = new BackendClient();
    await client.refund(order!.vaspPaymentRef);
    const fetched = await new BackendClient().getOrderDetails(orderId);
    if (fetched) {
      setOrder(fetched);
    }
  };

  // Show spinner if order is undefined - it is being loaded
  let orderInfo = (
    <div className="d-flex justify-content-center">
      <Spinner color="primary" />
    </div>
  );

  if (order !== undefined) {
    if (order === null) {
      // There is no order with this ID
      orderInfo = (
        <Alert color="danger">
          <i className="fa fa-close" /> {t("unknown")}
        </Alert>
      );
    } else {
      orderInfo = (
        <>
          <InfoField caption="Order ID" value={orderId.toUpperCase()} />
          <InfoField caption="Creation time" value={order.createdAt.toLocaleString()} />
          <InfoField caption="Current status" value={t(`status.${order?.paymentStatus.status}`)} />

          <div className="mt-5">
            <OrderProducts
              productOrders={order.products}
              total={order.totalPrice}
              currency={order.currency}
            />
          </div>

          <div className="mt-5">
            <h2 className="h5 font-weight-normal text-body">Payment details</h2>

            <InfoField caption="Payment ID" value={order.vaspPaymentRef.toUpperCase()} />
            <InfoField caption="Payment type" value="Direct" />
            <InfoField
              caption="Merchant Diem address"
              value={order.paymentStatus.merchantAddress}
            />
            <InfoField caption="Payer Diem address" value={tx ? tx.senderAddress : "N/A"} />
            <InfoField caption="Payer wallet type" value="Non-hosted" />
            <InfoField
              caption="Diem amount"
              value={tx ? `${tx.amount / 1000000} ${tx.currency}` : "N/A"}
            />

            <LinkField
              caption="Diem transaction ID"
              text={tx ? `${tx.transactionId}` : undefined}
              url={`https://diemexplorer.com/testnet/version/${tx?.transactionId}`}
              external
            />

            {refundTx && (
              <LinkField
                caption="Diem refund transaction ID"
                text={`${refundTx.transactionId}`}
                url={`https://diemexplorer.com/testnet/version/${refundTx.transactionId}`}
                external
              />
            )}

            <div className="mt-4">Payment events</div>
            <div className="mt-3">
              <PaymentEvents events={order.paymentStatus.events} />
            </div>

            <div className="d-flex justify-content-center m-2">
              <Button
                disabled={!order.paymentStatus.canCashOut}
                onClick={cashOut}
                className="m-2"
                color="primary"
              >
                Cash out
              </Button>
              <Button
                disabled={!order.paymentStatus.canRefund}
                onClick={refund}
                className="m-2"
                color="primary"
              >
                Refund
              </Button>
            </div>

            <div className="d-flex justify-content-center mt-5">
              <a href={`admin/order/${orderId}`} target="_blank" rel="noopener noreferrer">
                #permalink
              </a>
            </div>
          </div>
        </>
      );
    }
  }

  return (
    <Container className="container-narrow pt-5">
      <div className="text-center">
        <div className="h2">{t("title")}</div>
      </div>

      <div className="d-flex flex-column justify-content-center m-5">{orderInfo}</div>
    </Container>
  );
}
Example #29
Source File: page.tsx    From website with Apache License 2.0 4 votes vote down vote up
render(): JSX.Element {
    const numPlaces = Object.keys(this.state.placeName).length;
    const numStatVarInfo = Object.keys(this.state.statVarInfo).length;
    const namedPlaces: NamedPlace[] = [];
    for (const place in this.state.placeName) {
      namedPlaces.push({ dcid: place, name: this.state.placeName[place] });
    }
    const statVarTokens = Array.from(
      getTokensFromUrl(TIMELINE_URL_PARAM_KEYS.STAT_VAR, statVarSep)
    );
    const statVars = statVarTokens.map((sv) =>
      sv.includes("|") ? sv.split("|")[0] : sv
    );

    const deselectSVs = (svList: string[]) => {
      const availableSVs = statVars.filter((sv) => svList.indexOf(sv) === -1);
      const statVarTokenInfo = {
        name: TIMELINE_URL_PARAM_KEYS.STAT_VAR,
        sep: statVarSep,
        tokens: new Set(availableSVs),
      };
      setTokensToUrl([statVarTokenInfo]);
    };

    const svToSvInfo = {};
    for (const sv of statVars) {
      svToSvInfo[sv] =
        sv in this.state.statVarInfo ? this.state.statVarInfo[sv] : {};
    }

    return (
      <>
        <StatVarWidget
          openSvHierarchyModal={this.state.showSvHierarchyModal}
          openSvHierarchyModalCallback={this.toggleSvHierarchyModal}
          collapsible={true}
          svHierarchyType={StatVarHierarchyType.SCATTER}
          samplePlaces={namedPlaces}
          deselectSVs={deselectSVs}
          selectedSVs={svToSvInfo}
          selectSV={(sv) =>
            addToken(TIMELINE_URL_PARAM_KEYS.STAT_VAR, statVarSep, sv)
          }
        />
        <div id="plot-container">
          <Container fluid={true}>
            {numPlaces === 0 && <h1 className="mb-4">Timelines Explorer</h1>}
            <Card id="place-search">
              <Row>
                <Col sm={12}>
                  <p>Select places:</p>
                </Col>
                <Col sm={12}>
                  <SearchBar
                    places={this.state.placeName}
                    addPlace={(place) =>
                      addToken(TIMELINE_URL_PARAM_KEYS.PLACE, placeSep, place)
                    }
                    removePlace={(place) => {
                      removeToken(
                        TIMELINE_URL_PARAM_KEYS.PLACE,
                        placeSep,
                        place
                      );
                    }}
                  />
                </Col>
              </Row>
              <Row className="d-lg-none">
                <Col>
                  <Button color="primary" onClick={this.toggleSvHierarchyModal}>
                    Select variables
                  </Button>
                </Col>
              </Row>
            </Card>
            {numPlaces === 0 && <Info />}
            {numPlaces !== 0 && numStatVarInfo !== 0 && (
              <div id="chart-region">
                <ChartRegion
                  placeName={this.state.placeName}
                  statVarInfo={this.state.statVarInfo}
                  statVarOrder={statVars}
                ></ChartRegion>
              </div>
            )}
          </Container>
        </div>
      </>
    );
  }