@material-ui/icons#ExpandMore TypeScript Examples

The following examples show how to use @material-ui/icons#ExpandMore. 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: DiscloseSlippageSelector.tsx    From anchor-web-app with Apache License 2.0 6 votes vote down vote up
function Component({
  className,
  value,
  ...selectorProps
}: DiscloseSlippageSelectorProps) {
  const [{ open }, setOpen] = useLocalStorage<{ open: boolean }>(
    '__anchor_slippage__',
    { open: false },
  );

  return (
    <details className={className} {...(open ? { open: true } : {})}>
      <summary
        onClick={(event) => {
          event.preventDefault();
          setOpen({ open: !open });
        }}
      >
        {open ? <ExpandLess /> : <ExpandMore />}
        <IconSpan>
          Slippage Tolerance{' '}
          <InfoTooltip>
            The transaction will revert if the price changes by more than the
            defined percentage.{' '}
          </InfoTooltip>
          : {big(value).mul(100).toFixed()}%
        </IconSpan>
      </summary>

      <SlippageSelector value={value} {...selectorProps} className="selector" />
    </details>
  );
}
Example #2
Source File: ExpandableElement.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
export default function ExpandableElement({ children, expandable, defaultOpen }: Props): ReactElement | null {
  const classes = useStyles()
  const [open, setOpen] = useState<boolean>(Boolean(defaultOpen))

  const handleClick = () => {
    setOpen(!open)
  }

  return (
    <div className={`${classes.root} ${classes.rootLevel2}`}>
      <ListItem button onClick={handleClick} className={classes.header}>
        {children}
        {open ? <ExpandLess /> : <ExpandMore />}
      </ListItem>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <div className={classes.contentLevel12}>{expandable}</div>
      </Collapse>
    </div>
  )
}
Example #3
Source File: Swap.tsx    From swap-ui with Apache License 2.0 6 votes vote down vote up
function TokenButton({
  mint,
  onClick,
}: {
  mint: PublicKey;
  onClick: () => void;
}) {
  const styles = useStyles();
  const theme = useTheme();

  return (
    <div onClick={onClick} className={styles.tokenButton}>
      <TokenIcon mint={mint} style={{ width: theme.spacing(4) }} />
      <TokenName mint={mint} style={{ fontSize: 14, fontWeight: 700 }} />
      <ExpandMore />
    </div>
  );
}
Example #4
Source File: MoreMenu.tsx    From anchor-web-app with Apache License 2.0 5 votes vote down vote up
function MoreMenuBase({ children, className }: MoreMenuProps) {
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);

  return (
    <>
      <MoreButton
        onClick={(event: MouseEvent<HTMLButtonElement>) =>
          setAnchorEl(event.currentTarget)
        }
      >
        More {anchorEl ? <ExpandLess /> : <ExpandMore />}
      </MoreButton>

      <Popover
        open={!!anchorEl}
        anchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'right',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'right',
        }}
        className={className}
      >
        <MenuList variant="menu">
          {Children.map(children, (child) => {
            return cloneElement(child, {
              children: (
                <IconSpan>
                  {child.props.children} <ChevronRightRounded />
                </IconSpan>
              ),
            });
          })}
        </MenuList>
      </Popover>
    </>
  );
}
Example #5
Source File: GovBanner.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
GovBanner: React.FC = () => {
  const classes = useStyles();
  const [expanded, setExpanded] = useState(false);

  return (
    <>
      <div className={classes.root}>
        <div className={classes.inner}>
          <img src={flagIcon} alt="usa flag" className={classes.flag} />
          <div className={classes.textWrap}>
            <div className={classes.text}>
              An official website of the United States government
            </div>
            <button
              className={clsx(classes.text, classes.btn, classes.btnExpand)}
              onClick={() => setExpanded((exp) => !exp)}
            >
              Here&apos;s how you know{' '}
              {expanded ? (
                <ExpandLess fontSize="small" />
              ) : (
                <ExpandMore fontSize="small" />
              )}
            </button>
          </div>
        </div>
      </div>
      {expanded && (
        <div className={classes.root}>
          <div className={clsx(classes.inner, classes.infoInner)}>
            <div className={classes.info}>
              <div className={classes.infoIcon}>
                <img src={govIcon} alt="Dot Gov" />
              </div>
              <div className={classes.infoText}>
                <p>
                  <strong>The .gov means it’s official.</strong>
                  <br />
                  Federal government websites often end in .gov or .mil. Before
                  sharing sensitive information, make sure you’re on a federal
                  government site.
                </p>
              </div>
            </div>
            <div className={classes.info}>
              <div className={classes.infoIcon}>
                <img src={httpsIcon} alt="HTTPS" />
              </div>
              <div className={classes.infoText}>
                <p>
                  <strong>This site is secure.</strong>
                  <br />
                  The <strong>https://</strong> ensures that you are connecting
                  to the official website and that any information you provide
                  is encrypted and transmitted securely.
                </p>
              </div>
            </div>
          </div>
        </div>
      )}
    </>
  );
}
Example #6
Source File: ExpandableList.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
export default function ExpandableList({ children, label, level, defaultOpen, info }: Props): ReactElement | null {
  const classes = useStyles()
  const [open, setOpen] = useState<boolean>(Boolean(defaultOpen))

  const handleClick = () => {
    setOpen(!open)
  }

  let rootLevelClass = ''
  let typographyVariant: 'h1' | 'h2' | 'h3' = 'h1'
  let contentLevelClass = classes.contentLevel0

  if (level === 1) {
    rootLevelClass = classes.rootLevel1
    typographyVariant = 'h2'
    contentLevelClass = classes.contentLevel12
  } else if (level === 2) {
    rootLevelClass = classes.rootLevel2
    typographyVariant = 'h3'
    contentLevelClass = classes.contentLevel12
  }

  return (
    <div className={`${classes.root} ${rootLevelClass}`}>
      <ListItem button onClick={handleClick} className={classes.header}>
        <ListItemText primary={<Typography variant={typographyVariant}>{label}</Typography>} />
        <div style={{ display: 'flex' }}>
          {!open && (
            <Typography variant="body2" className={classes.infoText}>
              {info}
            </Typography>
          )}
          {open ? <ExpandLess /> : <ExpandMore />}
        </div>
      </ListItem>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <div className={contentLevelClass}>{children}</div>
      </Collapse>
    </div>
  )
}
Example #7
Source File: Scores.tsx    From dashboard with Apache License 2.0 5 votes vote down vote up
Scores = ({ score, nested }: ScoreProps) => {
  const [show, setShow] = useState(false)
  const { palette } = useTheme()

  let { op_name, operands, value, ref_id, description } = score
  if (!op_name && !operands && !value) {
    return <></>
  }

  const toggleShow = () => {
    setShow((prev) => !prev)
  }

  const canToggle = operands && operands.length > 0

  return (
    <div style={{ paddingLeft: nested ? "10px" : "0px" }}>
      <ListItem button={canToggle} onClick={toggleShow}>
        <Grid container>
          <Grid item xs={4}>
            <Box paddingTop="0.75rem">
              <Typography noWrap fontSize="1.25rem">
                {score.value || "0.12341234"}
              </Typography>
            </Box>
          </Grid>
          <Grid item xs={7}>
            <Box>
              <Typography noWrap>{op_name}</Typography>
              <Typography noWrap color={palette.grey[500]}>
                {description}
              </Typography>
            </Box>
          </Grid>
        </Grid>
        {canToggle && (
          <ListItemSecondaryAction>
            ({operands?.length}){show ? <ExpandLess /> : <ExpandMore />}
          </ListItemSecondaryAction>
        )}
      </ListItem>
      {operands && (
        <div>
          <Collapse in={show}>
            <List>
              {operands.map((operand, index) => (
                <Scores score={operand} key={`${index}-${ref_id}`} nested />
              ))}
            </List>
          </Collapse>
        </div>
      )}
    </div>
  )
}
Example #8
Source File: SwaggerView.tsx    From dashboard with Apache License 2.0 5 votes vote down vote up
SwaggerView = () => {
  const [show, setShow] = useState(false)
  const [host, setHost] = useState(getInitialHostAndPort().host)
  const [port, setPort] = useState(getInitialHostAndPort().port)
  const [endpoint, setEndpoint] = useState(DEFAULT_ENDPOINT)
  const [url, setURL] = useState(formatURL(host, port, endpoint))

  const updateURL = () => {
    setURL(formatURL(host, port, endpoint))
  }

  const toggleShow = () => {
    setShow((prev) => !prev)
  }

  return (
    <Container data-name="debuggingTool">
      <Box textAlign="right">
        <Button onClick={toggleShow}>
          endpoint settings {show ? <ExpandLess /> : <ExpandMore />}
        </Button>
      </Box>
      <Collapse in={show}>
        <Box paddingTop="2.5em">
          <Grid container spacing={2}>
            <Grid item xs={4}>
              <TextInput
                label="Host"
                variant="outlined"
                value={host}
                onChange={(e) => setHost(e.target.value)}
              />
            </Grid>
            <Grid item xs={2}>
              <TextInput
                label="Port"
                variant="outlined"
                value={port}
                onChange={(e) => setPort(e.target.value)}
              />
            </Grid>
            <Grid item xs={4}>
              <TextInput
                label="OpenAPI Schema"
                variant="outlined"
                value={endpoint}
                onChange={(e) => setEndpoint(e.target.value)}
              />
            </Grid>
            <Grid item xs={2}>
              <FullSizeButton onClick={updateURL} variant="contained">
                Set
              </FullSizeButton>
            </Grid>
          </Grid>
        </Box>
      </Collapse>
      <SwaggerUIReact
        url={url}
        presets={[WrappedComponents]}
        requestInterceptor={(r) => console.log("request:", r)}
        responseInterceptor={(r) => console.log("response:", r)}
      />
    </Container>
  )
}
Example #9
Source File: Facets.tsx    From cognitive-search-static-web-apps-sample-ui with MIT License 5 votes vote down vote up
render(): JSX.Element {

        const state = this.props.state;

        return (<FacetList component="nav">

            {state.facets.map(facetState => {

                var facetComponent: JSX.Element = null;
                switch (facetState.facetType) {
                    case FacetTypeEnum.BooleanFacet:
                        facetComponent = (<BooleanFacet state={facetState.state as BooleanFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.NumericFacet:
                        facetComponent = (<NumericFacet state={facetState.state as NumericFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.DateFacet:
                        facetComponent = (<DateFacet state={facetState.state as DateFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.StringFacet:
                        facetComponent = (<StringFacet state={facetState.state as StringFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.StringCollectionFacet:
                        facetComponent = (<StringCollectionFacet state={facetState.state as StringCollectionFacetState} inProgress={this.props.inProgress} />);
                    break;
                }

                // Getting reference to a proper getHintText method in this a bit unusual and not very strongly typed way
                const getHintTextFunc = facetComponent?.type.getHintText;

                return (<div key={facetState.displayName}>
                
                    <FacetListItem disableRipple={true} button onClick={() => state.toggleExpand(facetState.fieldName)}>
                        <ListItemText
                            primary={facetState.displayName}
                            secondary={getHintTextFunc ? getHintTextFunc(facetState.state) : ''}
                        />
                        {!!facetState.isExpanded ? <ExpandLess /> : <ExpandMore />}
                    </FacetListItem>

                    <Collapse in={facetState.isExpanded} timeout={200} unmountOnExit>
                        {facetComponent}
                    </Collapse>
                    
                </div>);
            })}
                
        </FacetList>);
    }
Example #10
Source File: MetricAssignmentResults.tsx    From abacus with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Display results for a MetricAssignment
 */
export default function MetricAssignmentResults({
  strategy,
  metricAssignment,
  metric,
  analysesByStrategyDateAsc,
  experiment,
  recommendation,
  variationDiffKey,
}: {
  strategy: AnalysisStrategy
  metricAssignment: MetricAssignment
  metric: Metric
  analysesByStrategyDateAsc: Record<AnalysisStrategy, Analysis[]>
  experiment: ExperimentFull
  recommendation: Recommendations.Recommendation
  variationDiffKey: string
}): JSX.Element | null {
  const classes = useStyles()

  const [isShowObservedData, setIsShowObservedData] = useState<boolean>(false)
  const toggleIsShowObservedData = () => {
    setIsShowObservedData((isShowObservedData) => !isShowObservedData)
  }

  const isConversion = metric.parameterType === MetricParameterType.Conversion
  const estimateTransform: (estimate: number | null) => number | null = isConversion
    ? (estimate: number | null) => estimate && estimate * 100
    : identity
  const analyses = analysesByStrategyDateAsc[strategy]
  const latestAnalysis = _.last(analyses)
  const latestEstimates = latestAnalysis?.metricEstimates
  if (!latestAnalysis || !latestEstimates) {
    return <MissingAnalysisMessage />
  }

  const [_changeVariationId, baseVariationId] = variationDiffKey.split('_')

  const dates = analyses.map(({ analysisDatetime }) => analysisDatetime.toISOString())

  const plotlyDataVariationGraph: Array<Partial<PlotData>> = [
    ..._.flatMap(experiment.variations, (variation, index) => {
      return [
        {
          name: `${variation.name}: lower bound`,
          x: dates,
          y: analyses
            .map(
              ({ metricEstimates }) => metricEstimates && metricEstimates.variations[variation.variationId].bottom_95,
            )
            .map(estimateTransform),
          line: {
            color: Visualizations.variantColors[index],
          },
          mode: 'lines' as const,
          type: 'scatter' as const,
        },
        {
          name: `${variation.name}: upper bound`,
          x: dates,
          y: analyses
            .map(({ metricEstimates }) => metricEstimates && metricEstimates.variations[variation.variationId].top_95)
            .map(estimateTransform),
          line: {
            color: Visualizations.variantColors[index],
          },
          fill: 'tonexty' as const,
          fillcolor: Visualizations.variantColors[index],
          mode: 'lines' as const,
          type: 'scatter' as const,
        },
      ]
    }),
  ]

  const plotlyDataDifferenceGraph: Array<Partial<PlotData>> = [
    {
      name: `difference: 99% lower bound`,
      x: dates,
      y: analyses
        .map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].bottom_99)
        .map(estimateTransform),
      line: { width: 0 },
      marker: { color: '444' },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
    {
      name: `difference: 99% upper bound`,
      x: dates,
      y: analyses
        .map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].top_99)
        .map(estimateTransform),
      fill: 'tonexty',
      fillcolor: 'rgba(0,0,0,.2)',
      line: { width: 0 },
      marker: { color: '444' },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
    {
      name: `difference: 95% lower bound`,
      x: dates,
      y: analyses
        .map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].bottom_95)
        .map(estimateTransform),
      line: { width: 0 },
      marker: { color: '444' },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
    {
      name: `difference: 95% upper bound`,
      x: dates,
      y: analyses
        .map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].top_95)
        .map(estimateTransform),
      fill: 'tonexty',
      fillcolor: 'rgba(0,0,0,.2)',
      line: { width: 0 },
      marker: { color: '444' },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
    {
      name: `difference: 50% lower bound`,
      x: dates,
      y: analyses
        .map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].bottom_50)
        .map(estimateTransform),
      line: { width: 0 },
      marker: { color: '444' },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
    {
      name: `difference: 50% upper bound`,
      x: dates,
      y: analyses
        .map(({ metricEstimates }) => metricEstimates && metricEstimates.diffs[variationDiffKey].top_50)
        .map(estimateTransform),
      fill: 'tonexty',
      fillcolor: 'rgba(0,0,0,.2)',
      line: { width: 0 },
      marker: { color: '444' },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
    {
      name: 'ROPE: lower bound',
      x: dates,
      y: analyses.map((_) => -metricAssignment.minDifference).map(estimateTransform),
      line: {
        color: 'rgba(0,0,0,.4)',
        dash: 'dash',
      },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
    {
      name: 'ROPE: upper bound',
      x: dates,
      y: analyses.map((_) => metricAssignment.minDifference).map(estimateTransform),
      line: {
        color: 'rgba(0,0,0,.4)',
        dash: 'dash',
      },
      mode: 'lines' as const,
      type: 'scatter' as const,
    },
  ]

  return (
    <div className={clsx(classes.root, 'analysis-detail-panel')}>
      <Typography className={classes.dataTableHeader}>Summary</Typography>
      <TableContainer component={Paper}>
        <Table>
          <TableBody>
            <TableRow>
              <TableCell>
                <Typography variant='h5' gutterBottom className={classes.recommendation}>
                  <AnalysisDisplay {...{ experiment, analysis: recommendation }} />
                </Typography>
                {recommendation.decision === Recommendations.Decision.ManualAnalysisRequired && (
                  <Typography variant='body1' gutterBottom>
                    <strong> Different strategies are recommending conflicting variations! </strong>
                  </Typography>
                )}
                <Typography variant='body1'>
                  {getOverviewMessage(experiment, recommendation)}{' '}
                  <Link
                    href={`https://github.com/Automattic/experimentation-platform/wiki/Experimenter's-Guide#reading-the-data`}
                    target='_blank'
                  >
                    Learn more
                  </Link>
                </Typography>
              </TableCell>
            </TableRow>
            <TableRow>
              <TableCell>
                <Typography variant='body1' gutterBottom>
                  The absolute change in the {isConversion ? 'conversion rate' : 'ARPU'} of{' '}
                  <MetricValue
                    metricParameterType={metric.parameterType}
                    isDifference={true}
                    value={latestEstimates.diffs[variationDiffKey].bottom_95}
                    displayPositiveSign
                    displayUnit={false}
                  />{' '}
                  to{' '}
                  <MetricValue
                    metricParameterType={metric.parameterType}
                    isDifference={true}
                    value={latestEstimates.diffs[variationDiffKey].top_95}
                    displayPositiveSign
                  />{' '}
                  is {recommendation.statisticallySignificant ? '' : ' not '}
                  statistically different from zero because the interval
                  {recommendation.statisticallySignificant ? ' excludes ' : ' includes '}
                  zero.{' '}
                  {
                    explanationLine2[
                      recommendation.practicallySignificant as Recommendations.PracticalSignificanceStatus
                    ]
                  }
                  <MetricValue
                    metricParameterType={metric.parameterType}
                    isDifference={true}
                    value={-metricAssignment.minDifference}
                    displayPositiveSign
                    displayUnit={false}
                  />{' '}
                  to{' '}
                  <MetricValue
                    metricParameterType={metric.parameterType}
                    isDifference={true}
                    value={metricAssignment.minDifference}
                    displayPositiveSign
                  />
                  .
                </Typography>
                <strong>Last analyzed:</strong>{' '}
                <DatetimeText datetime={latestAnalysis.analysisDatetime} excludeTime={true} />.
              </TableCell>
            </TableRow>
            <TableRow>
              <TableCell>
                <strong>Metric description:</strong> {metric.description}
              </TableCell>
            </TableRow>
          </TableBody>
        </Table>
      </TableContainer>
      <Typography className={classes.dataTableHeader}>Analysis</Typography>
      <TableContainer component={Paper}>
        <Table className={classes.coolTable}>
          <TableHead>
            <TableRow>
              <TableCell>Variant</TableCell>
              <TableCell align='right'>
                {metric.parameterType === MetricParameterType.Revenue
                  ? 'Average revenue per user (ARPU) interval'
                  : 'Conversion rate interval'}
              </TableCell>
              <TableCell align='right'>Absolute change</TableCell>
              <TableCell align='right'>Relative change (lift)</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {experiment.variations.map((variation) => (
              <React.Fragment key={variation.variationId}>
                <TableRow>
                  <TableCell
                    component='th'
                    scope='row'
                    variant='head'
                    valign='top'
                    className={clsx(classes.rowHeader, classes.headerCell, classes.credibleIntervalHeader)}
                  >
                    <span className={classes.monospace}>{variation.name}</span>
                  </TableCell>
                  <TableCell className={classes.monospace} align='right'>
                    <MetricValueInterval
                      intervalName={'the metric value'}
                      metricParameterType={metric.parameterType}
                      bottomValue={latestEstimates.variations[variation.variationId].bottom_95}
                      topValue={latestEstimates.variations[variation.variationId].top_95}
                      displayPositiveSign={false}
                    />
                  </TableCell>
                  <TableCell className={classes.monospace} align='right'>
                    {variation.isDefault ? (
                      'Baseline'
                    ) : (
                      <MetricValueInterval
                        intervalName={'the absolute change between variations'}
                        metricParameterType={metric.parameterType}
                        isDifference={true}
                        bottomValue={latestEstimates.diffs[`${variation.variationId}_${baseVariationId}`].bottom_95}
                        topValue={latestEstimates.diffs[`${variation.variationId}_${baseVariationId}`].top_95}
                      />
                    )}
                  </TableCell>
                  <TableCell className={classes.monospace} align='right'>
                    {variation.isDefault ? (
                      'Baseline'
                    ) : (
                      <MetricValueInterval
                        intervalName={'the relative change between variations'}
                        metricParameterType={MetricParameterType.Conversion}
                        bottomValue={Analyses.ratioToDifferenceRatio(
                          latestEstimates.ratios[`${variation.variationId}_${baseVariationId}`].bottom_95,
                        )}
                        topValue={Analyses.ratioToDifferenceRatio(
                          latestEstimates.ratios[`${variation.variationId}_${baseVariationId}`].top_95,
                        )}
                      />
                    )}
                  </TableCell>
                </TableRow>
              </React.Fragment>
            ))}
          </TableBody>
        </Table>
      </TableContainer>
      <Typography className={classes.analysisFinePrint}>
        95% Credible Intervals (CIs). <strong> Experimenter-set minimum practical difference: </strong>{' '}
        <MetricValue
          value={metricAssignment.minDifference}
          metricParameterType={metric.parameterType}
          isDifference={true}
        />
        .
      </Typography>
      {dates.length > 1 ? (
        <div className={classes.metricEstimatePlots}>
          <Plot
            layout={{
              ...Visualizations.plotlyLayoutDefault,
              title: isConversion
                ? `Conversion rate estimates by variation (%)`
                : `Revenue estimates by variation (USD)`,
            }}
            data={plotlyDataVariationGraph}
            className={classes.metricEstimatePlot}
          />
          <Plot
            layout={{
              ...Visualizations.plotlyLayoutDefault,
              title: isConversion
                ? `Conversion rate difference estimates (percentage points)`
                : `Revenue difference estimates (USD)`,
            }}
            data={plotlyDataDifferenceGraph}
            className={classes.metricEstimatePlot}
          />
        </div>
      ) : (
        <Typography variant='body1' className={classes.noPlotMessage}>
          Past values will be plotted once we have more than one day of results.
        </Typography>
      )}
      <Typography
        className={clsx(classes.dataTableHeader, classes.clickable)}
        onClick={toggleIsShowObservedData}
        role='button'
      >
        {isShowObservedData ? (
          <ExpandMore className={classes.expandCollapseIcon} />
        ) : (
          <ChevronRight className={classes.expandCollapseIcon} />
        )}
        &quot;Observed&quot; data
      </Typography>
      {isShowObservedData && (
        <>
          <TableContainer component={Paper}>
            <Table className={classes.coolTable}>
              <TableHead>
                <TableRow>
                  <TableCell>Variant</TableCell>
                  <TableCell align='right'>Users</TableCell>
                  <TableCell align='right'>
                    {metric.parameterType === MetricParameterType.Revenue ? 'Revenue' : 'Conversions'}
                  </TableCell>
                  <TableCell align='right'>
                    {metric.parameterType === MetricParameterType.Revenue
                      ? 'Average revenue per user (ARPU)'
                      : 'Conversion rate'}
                  </TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
                {experiment.variations.map((variation) => (
                  <React.Fragment key={variation.variationId}>
                    <TableRow>
                      <TableCell
                        component='th'
                        scope='row'
                        variant='head'
                        valign='top'
                        className={clsx(classes.rowHeader, classes.headerCell, classes.credibleIntervalHeader)}
                      >
                        <span className={classes.monospace}>{variation.name}</span>
                      </TableCell>
                      <TableCell className={classes.monospace} align='right'>
                        {latestAnalysis.participantStats[`variation_${variation.variationId}`].toLocaleString()}
                      </TableCell>
                      <TableCell className={classes.monospace} align='right'>
                        <MetricValue
                          value={
                            latestAnalysis.participantStats[`variation_${variation.variationId}`] *
                            latestEstimates.variations[variation.variationId].mean
                          }
                          metricParameterType={
                            metric.parameterType === MetricParameterType.Conversion
                              ? MetricParameterType.Count
                              : metric.parameterType
                          }
                        />
                      </TableCell>
                      <TableCell className={classes.monospace} align='right'>
                        <MetricValue
                          value={latestEstimates.variations[variation.variationId].mean}
                          metricParameterType={metric.parameterType}
                        />
                      </TableCell>
                    </TableRow>
                  </React.Fragment>
                ))}
              </TableBody>
            </Table>
          </TableContainer>
          <Typography variant='caption' gutterBottom>
            <Link href='https://wp.me/PCYsg-Fqg/#observed-data-uses-posterior-means' target='_blank'>
              &quot;Observed&quot; data as produced from our model, not raw observed data.
            </Link>{' '}
            For illustrative purposes only.
          </Typography>
        </>
      )}
    </div>
  )
}
Example #11
Source File: ModTableRow.tsx    From ow-mod-manager with MIT License 4 votes vote down vote up
ModTableRow: React.FunctionComponent<Props> = ({ mod }) => {
  const styles = useStyles();
  const theme = useTheme();
  const missingDependencyNames = useRecoilValue(missingDependencyIdsState(mod));
  const isModOutdated = isOutdated(mod);
  const isModBroken = isBroken(mod);
  const addonMods = useRecoilValue(addonModList);
  const [isAddonsExpanded, setIsAddonsExpanded] = useState(false);
  const isAddon = mod.parent && !mod.localVersion;
  const enabledMods = useRecoilValue(enabledModList);
  const forceExpandAddons = useRecoilValue(isFiltering);
  const shouldExpandAddons = forceExpandAddons || isAddonsExpanded;
  const rowRef = useRef<HTMLTableRowElement>(null);
  const isLoading = useRecoilValue(modIsLoadingState(mod.uniqueName));

  useEffect(() => {
    if (!isLoading || !rowRef.current) return;

    rowRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'nearest',
    });
  }, [isLoading]);

  const addons = useMemo(
    () => addonMods.filter((addon) => addon.parent === mod.uniqueName),
    [addonMods, mod.uniqueName]
  );

  const conflictingMods = useMemo(
    () =>
      mod.conflicts && mod.conflicts.length > 0
        ? enabledMods
            .filter((enabledMod) =>
              mod.conflicts?.includes(enabledMod.uniqueName)
            )
            .map((enabledMod) => enabledMod.name)
        : [],
    [enabledMods, mod.conflicts]
  );

  const isModConflicting = mod.isEnabled && conflictingMods.length > 0;

  const handleExpandClick = () =>
    setIsAddonsExpanded((isExpanded) => !isExpanded);

  const getVersionColor = () => {
    if (isModBroken) {
      return 'default';
    }
    if (isModOutdated) {
      return 'secondary';
    }
    if (isInstalled(mod)) {
      return 'primary';
    }
    return 'default';
  };

  const getVersion = () => {
    if (isInstalled(mod)) {
      return mod.localVersion;
    }
    if (mod.remoteVersion) {
      return mod.remoteVersion;
    }
    return modsText.versionNotAvailable;
  };

  const getClassName = () => {
    let className = styles.tableRow;
    if (isModBroken || isModConflicting) {
      className += ` ${styles.brokenRow}`;
    } else if (isLoading) {
      className += ` ${styles.loading}`;
    } else if (missingDependencyNames.length > 0) {
      className += ` ${styles.missingDependencyRow}`;
    } else if (isAddon) {
      className += ` ${styles.addonRow}`;
    }
    return className;
  };

  const getModText = () => {
    if (isModBroken) {
      return modsText.modLoadError(mod.errors);
    }
    if (missingDependencyNames.length > 0) {
      return modsText.missingDependencyWarning(
        missingDependencyNames.join(', ')
      );
    }
    if (isModConflicting) {
      return modsText.conflictingModWarning(conflictingMods.join(', '));
    }
    return mod.description;
  };

  return (
    <>
      <TableRow className={getClassName()} key={mod.uniqueName} ref={rowRef}>
        <TableCell className={styles.tableCell}>
          <Box display="flex">
            {isAddon && (
              <Box
                bgcolor={theme.palette.background.paper}
                width="8px"
                minWidth="8px"
                marginRight={2}
                marginLeft={1}
                borderRadius="8px"
              />
            )}
            <Box width="100%">
              <Typography variant="subtitle1">
                <Box display="inline-block" mr={2}>
                  {mod.name}
                </Box>
                <Typography className={styles.modAuthor} variant="caption">
                  {' by '}
                  {mod.author}
                </Typography>
                <Typography variant="caption" />
              </Typography>
              <Box
                color={
                  isModBroken || isModConflicting
                    ? theme.palette.secondary.light
                    : theme.palette.text.secondary
                }
              >
                <Typography className={styles.modText} variant="caption">
                  {getModText()}
                </Typography>
              </Box>
              {addons.length > 0 && !forceExpandAddons && (
                <ButtonBase
                  className={styles.addonExpander}
                  onClick={handleExpandClick}
                >
                  <Box
                    display="flex"
                    alignItems="center"
                    borderRadius={theme.shape.borderRadius}
                    maxWidth="100%"
                  >
                    {shouldExpandAddons ? <ExpandLess /> : <ExpandMore />}

                    <Typography variant="caption" noWrap>
                      {addons.length}
                      {' addons available: '}
                      {addons.map((addon) => addon.name).join(', ')}
                    </Typography>
                  </Box>
                </ButtonBase>
              )}
            </Box>
          </Box>
        </TableCell>
        <TableCell className={styles.tableCell} align="right">
          {mod.downloadCount || '-'}
        </TableCell>
        <TableCell className={styles.tableCell}>
          <Chip
            color={getVersionColor()}
            label={getVersion()}
            className={styles.versionChip}
          />
          {!isModBroken && isModOutdated && (
            <div className={styles.outdatedChip}>{modsText.outdated}</div>
          )}
        </TableCell>
        <TableCell className={styles.tableCell}>
          <ModActions mod={mod} />
        </TableCell>
      </TableRow>
      {shouldExpandAddons &&
        addons.map((addon) => (
          <ModTableRow key={addon.uniqueName} mod={addon} />
        ))}
    </>
  );
}
Example #12
Source File: DomainDetails.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
DomainDetails: React.FC<Props> = (props) => {
  const { domainId } = props;
  const { getDomain } = useDomainApi(false);
  const { user } = useAuthContext();
  const [domain, setDomain] = useState<Domain>();
  const classes = useStyles();
  const history = useHistory();

  const fetchDomain = useCallback(async () => {
    try {
      setDomain(undefined);
      const result = await getDomain(domainId);
      setDomain(result);
    } catch (e) {
      console.error(e);
    }
  }, [domainId, getDomain]);

  useEffect(() => {
    fetchDomain();
  }, [fetchDomain]);

  const webInfo = useMemo(() => {
    if (!domain) {
      return [];
    }
    const categoriesToProducts: Record<string, Set<string>> = {};
    for (const service of domain.services) {
      for (const product of service.products) {
        const version = product.version ? ` ${product.version}` : '';
        const value = product.name + version;
        const name =
          product.tags && product.tags.length > 0 ? product.tags[0] : 'Misc';
        if (!categoriesToProducts[name]) {
          categoriesToProducts[name] = new Set();
        }
        categoriesToProducts[name].add(value);
      }
    }
    return Object.entries(categoriesToProducts).reduce(
      (acc, [name, value]) => [
        ...acc,
        {
          label: name,
          value: Array.from(value).join(', ')
        }
      ],
      [] as any
    );
  }, [domain]);

  const overviewInfo = useMemo(() => {
    if (!domain) {
      return [];
    }
    const ret = [];
    if (domain.ip) {
      ret.push({
        label: 'IP',
        value: domain.ip
      });
    }
    ret.push({
      label: 'First Seen',
      value: `${differenceInCalendarDays(
        Date.now(),
        parseISO(domain.createdAt)
      )} days ago`
    });
    ret.push({
      label: 'Last Seen',
      value: `${differenceInCalendarDays(
        Date.now(),
        parseISO(domain.updatedAt)
      )} days ago`
    });
    if (domain.country) {
      ret.push({
        label: 'Country',
        value: domain.country
      });
    }
    if (domain.cloudHosted) {
      ret.push({
        label: 'Cloud Hosted',
        value: 'Yes'
      });
    }
    ret.push({
      label: 'Organization',
      value: domain.organization.name
    });
    return ret;
  }, [domain]);

  const [hiddenRows, setHiddenRows] = React.useState<{
    [key: string]: boolean;
  }>({});

  const formatBytes = (bytes: number, decimals = 2): string => {
    if (bytes === 0) return '0 Bytes';

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
  };

  const generateWebpageList = (tree: any, prefix = '') => {
    return (
      <List
        className={`${classes.listRoot}${prefix ? ' ' + classes.nested : ''}`}
      >
        {Object.keys(tree).map((key) => {
          const isWebpage =
            'url' in tree[key] && typeof tree[key]['url'] === 'string';
          if (!isWebpage) {
            const newPrefix = prefix + '/' + key;
            return (
              <>
                <ListItem
                  button
                  onClick={() => {
                    setHiddenRows((hiddenRows: any) => {
                      hiddenRows[newPrefix] =
                        newPrefix in hiddenRows ? !hiddenRows[newPrefix] : true;
                      return { ...hiddenRows };
                    });
                  }}
                  key={newPrefix}
                >
                  <ListItemText primary={(prefix ? '' : '/') + key + '/'} />
                  {hiddenRows[newPrefix] ? <ExpandLess /> : <ExpandMore />}
                </ListItem>
                <Collapse
                  in={!hiddenRows[newPrefix]}
                  timeout="auto"
                  unmountOnExit
                >
                  {generateWebpageList(tree[key], newPrefix)}
                </Collapse>
              </>
            );
          }
          const page = tree[key] as Webpage;
          const parsed = new URL(page.url);
          const split = parsed.pathname
            .replace(/\/$/, '') // Remove trailing slash
            .split('/');
          return (
            <ListItem
              button
              divider={true}
              key={page.url}
              onClick={() => window.open(page.url, '_blank')}
            >
              <ListItemText
                primary={(prefix ? '' : '/') + split.pop()}
                secondary={
                  page.status + ' • ' + formatBytes(page.responseSize ?? 0, 1)
                }
              ></ListItemText>
            </ListItem>
          );
        })}
      </List>
    );
  };

  if (!domain) {
    return null;
  }

  const url =
    (domain.services.find((service) => service.port === 443)
      ? 'https://'
      : 'http://') + domain.name;

  const { webpages = [] } = domain;
  webpages.sort((a, b) => (a.url > b.url ? 1 : -1));
  const webpageTree = generateWebpageTree(webpages);
  const webpageList = generateWebpageList(webpageTree);

  return (
    <Paper classes={{ root: classes.root }}>
      <div className={classes.title}>
        <h4>
          <Link to={`/inventory/domain/${domain.id}`}>{domain.name}</Link>
        </h4>

        <a href={url} target="_blank" rel="noopener noreferrer">
          <LinkOffIcon />
        </a>
      </div>
      <div className={classes.inner}>
        {overviewInfo.length > 0 && (
          <div className={classes.section}>
            <h4 className={classes.subtitle}>Overview</h4>
            <DefinitionList items={overviewInfo} />
          </div>
        )}
        {webInfo.length > 0 && (
          <div className={classes.section}>
            <h4 className={classes.subtitle}>Known Products</h4>
            <DefinitionList items={webInfo} />
          </div>
        )}

        {domain.vulnerabilities.length > 0 && (
          <div className={classes.section}>
            <h4 className={classes.subtitle}>Vulnerabilities</h4>
            <Accordion className={classes.accordionHeaderRow} disabled>
              <AccordionSummary>
                <Typography className={classes.accordionHeading}>
                  Title
                </Typography>
                <Typography className={classes.vulnDescription}>
                  Serverity
                </Typography>
                <Typography className={classes.vulnDescription}>
                  State
                </Typography>
                <Typography className={classes.vulnDescription}>
                  Created
                </Typography>
              </AccordionSummary>
            </Accordion>
            {domain.vulnerabilities.map((vuln) => (
              <Accordion
                className={classes.accordion}
                key={vuln.id}
                onClick={(event) => {
                  event.stopPropagation();
                  history.push('/inventory/vulnerability/' + vuln.id);
                }}
              >
                <AccordionSummary>
                  <Typography className={classes.accordionHeading}>
                    {vuln.title}
                  </Typography>
                  <Typography className={classes.vulnDescription}>
                    {vuln.severity}
                  </Typography>
                  <Typography className={classes.vulnDescription}>
                    {vuln.state}
                  </Typography>
                  <Typography className={classes.vulnDescription}>
                    {vuln.createdAt
                      ? `${differenceInCalendarDays(
                          Date.now(),
                          parseISO(vuln.createdAt)
                        )} days ago`
                      : ''}
                  </Typography>
                </AccordionSummary>
              </Accordion>
            ))}
          </div>
        )}
        {domain.services.length > 0 && (
          <div className={classes.section}>
            <h4 className={classes.subtitle}>Ports</h4>
            <Accordion className={classes.accordionHeaderRow} disabled>
              <AccordionSummary expandIcon={<ExpandMore />}>
                <Typography className={classes.accordionHeading}>
                  Port
                </Typography>
                <Typography className={classes.accordionHeading}>
                  Products
                </Typography>
                <Typography className={classes.lastSeen}>Last Seen</Typography>
              </AccordionSummary>
            </Accordion>
            {domain.services.map((service) => {
              const products = service.products
                .map(
                  (product) =>
                    product.name +
                    (product.version ? ` ${product.version}` : '')
                )
                .join(', ');
              return (
                <Accordion className={classes.accordion} key={service.id}>
                  <AccordionSummary expandIcon={<ExpandMore />}>
                    <Typography className={classes.accordionHeading}>
                      {service.port}
                    </Typography>
                    <Typography className={classes.accordionHeading}>
                      {products}
                    </Typography>
                    <Typography className={classes.lastSeen}>
                      {service.lastSeen
                        ? `${differenceInCalendarDays(
                            Date.now(),
                            parseISO(service.lastSeen)
                          )} days ago`
                        : ''}
                    </Typography>
                  </AccordionSummary>
                  {service.products.length > 0 && (
                    <AccordionDetails>
                      <DefinitionList
                        items={[
                          {
                            label: 'Products',
                            value: products
                          },
                          {
                            label: 'Banner',
                            value:
                              (user?.userType === 'globalView' ||
                                user?.userType === 'globalAdmin') &&
                              service.banner
                                ? service.banner
                                : 'None'
                          }
                        ]}
                      />
                    </AccordionDetails>
                  )}
                </Accordion>
              );
            })}
          </div>
        )}
        {domain.webpages?.length > 0 && (
          <div className={classes.section}>
            <h4 className={classes.subtitle}>Site Map</h4>
            {webpageList}
          </div>
        )}
      </div>
    </Paper>
  );
}
Example #13
Source File: FilterDrawer.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
FilterDrawer: React.FC<Props> = (props) => {
  const { filters, addFilter, removeFilter, facets, clearFilters } = props;
  const classes = useStyles();

  const filtersByColumn = useMemo(
    () =>
      filters.reduce(
        (allFilters, nextFilter) => ({
          ...allFilters,
          [nextFilter.field]: nextFilter.values
        }),
        {} as Record<string, string[]>
      ),
    [filters]
  );

  const portFacet: any[] = facets['services.port']
    ? facets['services.port'][0].data
    : [];

  const fromDomainFacet: any[] = facets['fromRootDomain']
    ? facets['fromRootDomain'][0].data
    : [];

  const cveFacet: any[] = facets['vulnerabilities.cve']
    ? facets['vulnerabilities.cve'][0].data
    : [];

  const severityFacet: any[] = facets['vulnerabilities.severity']
    ? facets['vulnerabilities.severity'][0].data
    : [];

  // Always show all severities
  for (const value of ['Critical', 'High', 'Medium', 'Low']) {
    if (!severityFacet.find((severity) => value === severity.value))
      severityFacet.push({ value, count: 0 });
  }

  return (
    <Wrapper>
      <div className={classes.header}>
        <div className={classes.filter}>
          <FaFilter /> <h3>Filter</h3>
        </div>
        {clearFilters && (
          <div>
            <button onClick={clearFilters}>Clear All Filters</button>
          </div>
        )}
      </div>
      <Accordion elevation={0} square>
        <AccordionSummary expandIcon={<ExpandMore />}>
          <div>IP(s)</div>
          {filtersByColumn['ip']?.length > 0 && <FiltersApplied />}
        </AccordionSummary>
        <AccordionDetails classes={{ root: classes.details }}>
          <TaggedArrayInput
            placeholder="IP address"
            values={filtersByColumn.ip ?? []}
            onAddTag={(value) => addFilter('ip', value, 'any')}
            onRemoveTag={(value) => removeFilter('ip', value, 'any')}
          />
        </AccordionDetails>
      </Accordion>
      <Accordion elevation={0} square>
        <AccordionSummary expandIcon={<ExpandMore />}>
          <div>Domain(s)</div>
          {filtersByColumn['name']?.length > 0 && <FiltersApplied />}
        </AccordionSummary>
        <AccordionDetails classes={{ root: classes.details }}>
          <TaggedArrayInput
            placeholder="Domain"
            values={filtersByColumn.name ?? []}
            onAddTag={(value) => addFilter('name', value, 'any')}
            onRemoveTag={(value) => removeFilter('name', value, 'any')}
          />
        </AccordionDetails>
      </Accordion>
      {fromDomainFacet.length > 0 && (
        <Accordion elevation={0} square>
          <AccordionSummary expandIcon={<ExpandMore />}>
            <div>Root Domain(s)</div>
            {filtersByColumn['fromRootDomain']?.length > 0 && (
              <FiltersApplied />
            )}
          </AccordionSummary>
          <AccordionDetails classes={{ root: classes.details }}>
            <FacetFilter
              options={fromDomainFacet}
              selected={filtersByColumn['fromRootDomain'] ?? []}
              onSelect={(value) => addFilter('fromRootDomain', value, 'any')}
              onDeselect={(value) =>
                removeFilter('fromRootDomain', value, 'any')
              }
            />
          </AccordionDetails>
        </Accordion>
      )}
      {portFacet.length > 0 && (
        <Accordion elevation={0} square>
          <AccordionSummary expandIcon={<ExpandMore />}>
            <div>Port(s)</div>
            {filtersByColumn['services.port']?.length > 0 && <FiltersApplied />}
          </AccordionSummary>
          <AccordionDetails classes={{ root: classes.details }}>
            <FacetFilter
              options={portFacet}
              selected={filtersByColumn['services.port'] ?? []}
              onSelect={(value) => addFilter('services.port', value, 'any')}
              onDeselect={(value) =>
                removeFilter('services.port', value, 'any')
              }
            />
          </AccordionDetails>
        </Accordion>
      )}
      {cveFacet.length > 0 && (
        <Accordion elevation={0} square>
          <AccordionSummary expandIcon={<ExpandMore />}>
            <div>CVE(s)</div>
            {filtersByColumn['vulnerabilities.cve']?.length > 0 && (
              <FiltersApplied />
            )}
          </AccordionSummary>
          <AccordionDetails classes={{ root: classes.details }}>
            <FacetFilter
              options={cveFacet}
              selected={filtersByColumn['vulnerabilities.cve'] ?? []}
              onSelect={(value) =>
                addFilter('vulnerabilities.cve', value, 'any')
              }
              onDeselect={(value) =>
                removeFilter('vulnerabilities.cve', value, 'any')
              }
            />
          </AccordionDetails>
        </Accordion>
      )}
      {severityFacet.length > 0 && (
        <Accordion elevation={0} square>
          <AccordionSummary expandIcon={<ExpandMore />}>
            <div>Severity</div>
            {filtersByColumn['vulnerabilities.severity']?.length > 0 && (
              <FiltersApplied />
            )}
          </AccordionSummary>
          <AccordionDetails classes={{ root: classes.details }}>
            <FacetFilter
              options={severityFacet}
              selected={filtersByColumn['vulnerabilities.severity'] ?? []}
              onSelect={(value) =>
                addFilter('vulnerabilities.severity', value, 'any')
              }
              onDeselect={(value) =>
                removeFilter('vulnerabilities.severity', value, 'any')
              }
            />
          </AccordionDetails>
        </Accordion>
      )}
    </Wrapper>
  );
}
Example #14
Source File: FlowChartNodes.tsx    From dashboard with Apache License 2.0 4 votes vote down vote up
PropertyListItem = ({
  itemKey,
  itemValue,
  nested,
}: {
  itemKey: string
  itemValue: any
  nested?: boolean
}) => {
  const [show, setShow] = useState(false)
  const { palette } = useTheme()

  const toggleShow = () => {
    setShow((prev) => !prev)
  }

  let isObject = false
  if (typeof itemValue === "object" && itemValue !== null) {
    isObject = true
  }

  if (isObject && isEmpty(itemValue)) return null
  if (
    itemValue === 0 ||
    itemValue === "unset" ||
    itemValue === null ||
    itemValue === "" ||
    itemValue === {}
  )
    return null

  return (
    <div
      style={{
        marginLeft: nested ? "2em" : "0px",
        borderLeft: `1px solid ${palette.grey[500]}`,
        backgroundColor: show
          ? isObject
            ? `${palette.grey[100]}80`
            : palette.grey[200]
          : "inherit",
      }}
    >
      <ListItem button onClick={toggleShow} style={{ paddingLeft: "0px" }}>
        <Grid container>
          <Grid item xs={6}>
            <Typography noWrap>
              <ListMarker color={palette.grey[500]} />
              {itemKey}
            </Typography>
          </Grid>
          <Grid item xs={6}>
            <Typography noWrap>
              {isObject ? (
                <LightText color={palette.grey[500]}>Object</LightText>
              ) : (
                String(itemValue)
              )}
            </Typography>
          </Grid>
        </Grid>
        <ListItemSecondaryAction>
          {isObject && <>({Object.keys(itemValue).length})</>}
          {show ? <ExpandLess /> : <ExpandMore />}
        </ListItemSecondaryAction>
      </ListItem>
      <Collapse in={show}>
        {isObject ? (
          Object.keys(itemValue).length ? (
            show && <PropertyList data={itemValue} nested />
          ) : (
            <ListItem>
              <WrappedText>This Object is empty</WrappedText>
            </ListItem>
          )
        ) : (
          <ListItem>
            <WrappedText>
              <div>
                <b>key: </b>
                {itemKey}
              </div>
              <div>
                <b>value: </b>
                {itemValue}
              </div>
            </WrappedText>
          </ListItem>
        )}
      </Collapse>
    </div>
  )
}
Example #15
Source File: ServerListItemGroup.tsx    From shadowsocks-electron with GNU General Public License v3.0 4 votes vote down vote up
ServerListItemGroup: React.FC<ServerListItemGroupProps> = props => {
  // const styles = useStyles();
  const dispatch = useDispatch();
  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };
  const { t } = useTranslation();

  const {
    item,
    selectedServer
  } = props;

  const [expanded, handleChange] = useState(!!item.servers?.find(server => server.id === selectedServer));
  const [ContextMenu, handleMenuOpen] = useContextMenu([
    { label: t('copy'), action: 'copy', icon: <CopyIcon fontSize="small" /> },
    { label: t('update'), action: 'update_subscription', icon: <Refresh fontSize="small" /> },
    { label: t('top'), action: 'top', icon: <VerticalAlignTopIcon fontSize="small" />},
    { label: t('move_up'), action: 'move_up', icon: <ArrowUpwardIcon fontSize="small" /> },
    { label: t('move_down'), action: 'move_down', icon: <ArrowDownwardIcon fontSize="small" /> },
    { label: t('delete'), action: 'delete', icon: <DeleteIcon fontSize="small" />}
  ]);

  useEffect(() => {
    handleChange(!!item.servers?.find(server => server.id === selectedServer));
  }, [selectedServer]);

  const handleRemoveButtonClick = () => {
    props.onRemove?.(item.id);
  };

  function onContextMenuClick (action: string) {
    switch (action) {
      case 'copy':
        clipboard.writeText(JSON.stringify(item));
        break;
      case 'update_subscription':
        if (item.url) {
          dispatch(updateSubscription(item.id, item.url, {
            success: t('subscription_updated'),
            error: t('failed_to_update_subscription')
          }));
        } else {
          enqueueSnackbar(t('server_url_not_set'), { variant: 'warning' });
        }
        break;
      case 'top':
        dispatch(top(item.id));
        break;
      case 'move_up':
        dispatch(moveUp(item.id));
        break;
      case 'move_down':
        dispatch(moveDown(item.id));
        break;
      case 'delete':
        handleRemoveButtonClick();
      default:
        break;
    }
  }

  const onContextMenu = (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    e.stopPropagation();
    handleMenuOpen(e);
  };

  return (
    <div
    >
      <Accordion expanded={expanded} onChange={() => handleChange(!expanded)}>
        <StyledAccordionSummary
          expandIcon={<ExpandMore />}
          aria-controls="panel1bh-content"
          onContextMenu={onContextMenu}
        >
          { item.name }
        </StyledAccordionSummary>
        <StyledAccordionDetails>
          {
            item.servers.map(server => (
              <ServerListItemSingle
                selected={selectedServer === server.id}
                moveable={false}
                deleteable={false}
                topable={false}
                key={server.id}
                {...props}
                item={server}
              />
            ))
          }
        </StyledAccordionDetails>
      </Accordion>
      <ContextMenu onItemClick={onContextMenuClick} />
    </div>
  );
}
Example #16
Source File: Summary.tsx    From UsTaxes with GNU Affero General Public License v3.0 4 votes vote down vote up
F1040Summary = ({ summary }: F1040SummaryProps): ReactElement => (
  <>
    {(() => {
      if (summary.amountOwed !== undefined && summary.amountOwed > 0) {
        return (
          <div>
            <Typography variant="body2" color="textSecondary">
              Amount Owed: <Currency value={-summary.amountOwed} />
            </Typography>
          </div>
        )
      }
      if (summary.refundAmount !== undefined && summary.refundAmount > 0) {
        return (
          <div>
            <Typography variant="body2" color="textSecondary">
              Refund Amount: <Currency value={summary.refundAmount} />
            </Typography>
          </div>
        )
      }
    })()}

    <h3>Credits</h3>
    <Grid container>
      <Grid item zeroMinWidth>
        <List>
          {summary.credits.map((credit) => (
            <BinaryStateListItem key={credit.name} active={credit.allowed}>
              <Typography variant="body2" color="textPrimary">
                {credit.name}
              </Typography>
              {(() => {
                if (credit.value !== undefined) {
                  return (
                    <Typography variant="body2" color="textSecondary">
                      Credit: <Currency value={credit.value} />
                    </Typography>
                  )
                }
                return <></>
              })()}
            </BinaryStateListItem>
          ))}
        </List>
      </Grid>
    </Grid>

    {(() => {
      if (summary.worksheets.length > 0) {
        return (
          <Grid container>
            <Grid item zeroMinWidth>
              <h3>Worksheets</h3>
              <List>
                {summary.worksheets.map((worksheet, idx) => (
                  <Accordion key={idx}>
                    <AccordionSummary expandIcon={<ExpandMore />}>
                      {worksheet.name}
                    </AccordionSummary>
                    <AccordionDetails>
                      <TableContainer>
                        <Table size="small">
                          <TableBody>
                            {worksheet.lines.map((line) => (
                              <TableRow key={line.line}>
                                <TableCell component="th">
                                  Line {line.line}
                                </TableCell>
                                <TableCell>
                                  {typeof line.value === 'number' ? (
                                    <Currency
                                      value={displayRound(line.value) ?? 0}
                                    />
                                  ) : (
                                    line.value
                                  )}
                                </TableCell>
                              </TableRow>
                            ))}
                          </TableBody>
                        </Table>
                      </TableContainer>
                    </AccordionDetails>
                  </Accordion>
                ))}
              </List>
            </Grid>
          </Grid>
        )
      }
      return <></>
    })()}
  </>
)