semantic-ui-react#Card TypeScript Examples

The following examples show how to use semantic-ui-react#Card. 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: crew_card.tsx    From website with MIT License 6 votes vote down vote up
function CrewCard({crew}) {
    return (
        <Card>
            <Card.Content>
                <Image
                    floated="left"
                    size="tiny"
                    src={crew.image}
                    bordered
                    style={{
                        borderColor: `${getRarityColor(crew.rarity)}`
                    }}
                />
                <Card.Header>{crew.name}</Card.Header>
                <Card.Meta>
                    <p>{getRarityStars(crew.rarity)}</p>
                    <p>
                        {crew.skills.map(skill => (
                            <Image key={skill.key} width={30} height={30} inline spaced src={skill.imageUrl} />
                        ))}
                    </p>
                </Card.Meta>
                <Card.Description>
                    {crew.traits.join(', ')}
                </Card.Description>
            </Card.Content>
        </Card>
    );
}
Example #2
Source File: MemberList.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
MemberList: VFC<{ users: User[] }> = ({ users = [] }) => (
  <>
    <Card.Group>
      {users.map((user) => (
        <Card
          key={user.id}
          href={`https://github.com/${user.login}`}
          target="_blank"
          rel="noopener noreferrer"
        >
          <Card.Content>
            <Image floated="right" size="mini" src={user.avatarUrl} />
            <Card.Header>{user.login}</Card.Header>
            <Card.Meta>GitHub ID: {user.id}</Card.Meta>
          </Card.Content>
        </Card>
      ))}
    </Card.Group>
  </>
)
Example #3
Source File: App.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
render(): ReactElement {
    const { count } = this.state;

    return (
      <div className="container">
        <header>
          <h1>カウンター</h1>
        </header>
        <Card>
          <Statistic className="number-board">
            <Statistic.Label>count</Statistic.Label>
            <Statistic.Value>{count}</Statistic.Value>
          </Statistic>
          <Card.Content>
            <div className="ui two buttons">
              <Button color="red" onClick={() => this.reset()}>
                Reset
              </Button>
              <Button color="green" onClick={() => this.increment()}>
                +1
              </Button>
            </div>
          </Card.Content>
        </Card>
      </div>
    );
  }
Example #4
Source File: App.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
render = (): ReactElement => {
    const { timeLeft } = this.state;

    return (
      <div className="container">
        <header>
          <h1>タイマー</h1>
        </header>
        <Card>
          <Statistic className="number-board">
            <Statistic.Label>time</Statistic.Label>
            <Statistic.Value>{timeLeft}</Statistic.Value>
          </Statistic>
          <Card.Content>
            <Button color="red" fluid onClick={this.reset}>
              <Icon name="redo" />
              Reset
            </Button>
          </Card.Content>
        </Card>
      </div>
    );
  };
Example #5
Source File: Counter.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
Counter: VFC = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount((c) => c + 1);
  const reset = () => setCount(0);

  return (
    <Card>
      <Statistic className="number-board">
        <Statistic.Label>count</Statistic.Label>
        <Statistic.Value>{count}</Statistic.Value>
      </Statistic>
      <Card.Content>
        <div className="ui two buttons">
          <Button color="red" onClick={reset}>
            Reset
          </Button>
          <Button color="green" onClick={increment}>
            +1
          </Button>
        </div>
      </Card.Content>
    </Card>
  );
}
Example #6
Source File: Timer.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
Timer: VFC<{ limit: number }> = ({ limit }) => {
  const [timeLeft, setTimeLeft] = useState(limit);
  const reset = (): void => setTimeLeft(limit);
  const tick = (): void => setTimeLeft((t) => t - 1);

  useEffect(() => {
    const timerId = setInterval(tick, 1000);

    return () => clearInterval(timerId);
  }, []);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  useEffect(() => {
    if (timeLeft === 0) setTimeLeft(limit);
  });

  return (
    <Card>
      <Statistic className="number-board">
        <Statistic.Label>time</Statistic.Label>
        <Statistic.Value>{timeLeft}</Statistic.Value>
      </Statistic>
      <Card.Content>
        <Button color="red" fluid onClick={reset}>
          <Icon name="redo" />
          Reset
        </Button>
      </Card.Content>
    </Card>
  );
}
Example #7
Source File: Timer.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
Timer: VFC<TimerProps> = ({ limit }) => {
  const [timeLeft, setTimeLeft] = useState(limit);
  const primes = useMemo(() => getPrimes(limit), [limit]);
  const reset = () => setTimeLeft(limit);
  const tick = () => setTimeLeft((t) => t - 1);

  useEffect(() => {
    const timerId = setInterval(tick, 1000);

    return () => clearInterval(timerId);
  }, []);

  useEffect(() => {
    if (timeLeft === 0) setTimeLeft(limit);
  }, [timeLeft, limit]);

  return (
    <Card>
      <Statistic className="number-board">
        <Statistic.Label>time</Statistic.Label>
        <Statistic.Value
          className={primes.includes(timeLeft) ? 'prime-number' : undefined}
        >
          {timeLeft}
        </Statistic.Value>
      </Statistic>
      <Card.Content>
        <Button color="red" fluid onClick={reset}>
          <Icon name="redo" />
          Reset
        </Button>
      </Card.Content>
    </Card>
  );
}
Example #8
Source File: MemberList.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
MemberList: VFC<{ users: User[] }> = ({ users = [] }) => (
  <>
    <Card.Group>
      {users.map((user) => (
        <Card
          key={user.id}
          href={`https://github.com/${user.login}`}
          target="_blank"
        >
          <Card.Content>
            <Image floated="right" size="mini" src={user.avatarUrl} />
            <Card.Header>{user.login}</Card.Header>
            <Card.Meta>GitHub ID: {user.id}</Card.Meta>
          </Card.Content>
        </Card>
      ))}
    </Card.Group>
  </>
)
Example #9
Source File: MemberList.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
MemberList: VFC<{ users: User[] }> = ({ users = [] }) => (
  <>
    <Card.Group>
      {users.map((user) => (
        <Card
          key={user.id}
          href={`https://github.com/${user.login}`}
          target="_blank"
        >
          <Card.Content>
            <Image floated="right" size="mini" src={user.avatarUrl} />
            <Card.Header>{user.login}</Card.Header>
            <Card.Meta>GitHub ID: {user.id}</Card.Meta>
          </Card.Content>
        </Card>
      ))}
    </Card.Group>
  </>
)
Example #10
Source File: Timer.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
Timer: VFC<{ limit: number }> = ({ limit }) => {
  const [timeLeft, isPrime, reset] = useTimer(limit);

  return (
    <Card>
      <Statistic className="number-board">
        <Statistic.Label>time</Statistic.Label>
        <Statistic.Value className={isPrime ? 'prime-number' : undefined}>
          {timeLeft}
        </Statistic.Value>
      </Statistic>
      <Card.Content>
        <Button color="red" fluid onClick={reset}>
          <Icon name="redo" />
          Reset
        </Button>
      </Card.Content>
    </Card>
  );
}
Example #11
Source File: Timer.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
Timer: VFC<Props> = ({
  timeLeft = 0,
  isPrime = false,
  reset = () => undefined,
}) => (
  <Card>
    <Statistic className="number-board">
      <Statistic.Label>time</Statistic.Label>
      <Statistic.Value className={isPrime ? 'prime-number' : undefined}>
        {timeLeft}
      </Statistic.Value>
    </Statistic>
    <Card.Content>
      <Button color="red" fluid onClick={reset}>
        <Icon name="redo" />
        Reset
      </Button>
    </Card.Content>
  </Card>
)
Example #12
Source File: CounterBoard.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
CounterBoard: VFC<Props> = ({
  count = 0,
  add = () => undefined,
  decrement = () => undefined,
  increment = () => undefined,
}) => (
  <Card>
    <Statistic className="number-board">
      <Statistic.Label>count</Statistic.Label>
      <Statistic.Value>{count}</Statistic.Value>
    </Statistic>
    <Card.Content>
      <div className="ui two buttons">
        <Button color="red" onClick={decrement}>
          -1
        </Button>
        <Button color="green" onClick={increment}>
          +1
        </Button>
      </div>
      <div className="fluid-button">
        <Button fluid color="grey" onClick={() => add(BULK_UNIT)}>
          +{BULK_UNIT}
        </Button>
      </div>
    </Card.Content>
  </Card>
)
Example #13
Source File: CounterBoard.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
CounterBoard: VFC<Props> = ({
  count = 0,
  add = () => undefined,
  decrement = () => undefined,
  increment = () => undefined,
}) => (
  <Card>
    <Statistic className="number-board">
      <Statistic.Label>count</Statistic.Label>
      <Statistic.Value>{count}</Statistic.Value>
    </Statistic>
    <Card.Content>
      <div className="ui two buttons">
        <Button color="red" onClick={decrement}>
          -1
        </Button>
        <Button color="green" onClick={increment}>
          +1
        </Button>
      </div>
      <div className="fluid-button">
        <Button fluid color="grey" onClick={() => add(BULK_UNIT)}>
          +{BULK_UNIT}
        </Button>
      </div>
    </Card.Content>
  </Card>
)
Example #14
Source File: CounterBoard.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
CounterBoard: VFC<CounterBoardProps> = ({
  count = 0,
  add = () => undefined,
  decrement = () => undefined,
  increment = () => undefined,
}) => (
  <Card>
    <Statistic className="number-board">
      <Statistic.Label>count</Statistic.Label>
      <Statistic.Value>{count}</Statistic.Value>
    </Statistic>
    <Card.Content>
      <div className="ui two buttons">
        <Button color="red" onClick={decrement}>
          -1
        </Button>
        <Button color="green" onClick={increment}>
          +1
        </Button>
      </div>
      <div className="fluid-button">
        <Button fluid color="grey" onClick={() => add(BULK_UNIT)}>
          +{BULK_UNIT}
        </Button>
      </div>
    </Card.Content>
  </Card>
)
Example #15
Source File: LobbyUserPanel.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 5 votes vote down vote up
render(){
        const props = this.props as any
        return(
            <div>
                {this.state.open ?
                    (
                        <div>

                        <Card width="100%">
                            <Button basic size="tiny"  compact onClick={()=>{this.handleClick()}} >
                                {/* <Header as='h5'> */}
                                    <Icon name="angle up" />Configurations
                                {/* </Header> */}
                            </Button>                            
                            <Card.Content>
                                <p>
                                    <MicControl {...props} />
                                </p>
                                <p>
                                    <VideoControl {...props} />
                                </p>
                                <p>
                                    <SpeakerControl {...props} />
                                </p>
                                <Divider />
                                <p>
                                    <VideoResolutionControl {...props} />
                                </p>
                                <p>
                                    <SettingControl {...props}/>
                                </p>
                            </Card.Content>
                        </Card>
                        </div>
                    )
                    :
                    (
                        <div>
                        <Card  >
                            <Button basic size="tiny"  compact onClick={()=>{this.handleClick()}} >
                                {/* <Header as='h5'> */}
                                    <Icon name="angle down" />Configurations
                                {/* </Header> */}
                            </Button>
                        </Card>
                        </div>

                    )

                }
            </div>
        )
    }
Example #16
Source File: voyagecalculator.tsx    From website with MIT License 5 votes vote down vote up
VoyageMain = (props: VoyageMainProps) => {
	const { myCrew, allShips } = props;

	const [voyageData, setVoyageData] = useStateWithStorage('tools/voyageData', undefined);
	const [voyageConfig, setVoyageConfig] = React.useState(undefined);
	const [voyageState, setVoyageState] = React.useState('input');	// input or from voyageData: started, recalled

	if (!voyageConfig) {
		if (voyageData) {
			// Voyage started, config will be full voyage data
			if (voyageData.voyage && voyageData.voyage.length > 0) {
				setVoyageConfig(voyageData.voyage[0]);
				setVoyageState(voyageData.voyage[0].state);
			}
			// Voyage awaiting input, config will be input parameters only
			else {
				setVoyageConfig(voyageData.voyage_descriptions[0]);
			}
		}
		else {
			// voyageData not found in cache, config will be blank voyage
			setVoyageConfig({ skills: {} });
		}
		return (<></>);
	}

	return (
		<React.Fragment>
			{voyageState == 'input' &&
				<Grid columns={2} stackable>
					<Grid.Column width={14}>
						<Card.Group>
							<Card>
								<Card.Content>
									<Image floated='right' src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_${voyageConfig.skills.primary_skill}.png`} style={{ height: '2em' }} />
									<Card.Header>{CONFIG.SKILLS[voyageConfig.skills.primary_skill]}</Card.Header>
									<p>primary</p>
								</Card.Content>
							</Card>
							<Card>
								<Card.Content>
									<Image floated='right' src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_${voyageConfig.skills.secondary_skill}.png`} style={{ height: '2em' }} />
									<Card.Header>{CONFIG.SKILLS[voyageConfig.skills.secondary_skill]}</Card.Header>
									<p>secondary</p>
								</Card.Content>
							</Card>
							<Card>
								<Card.Content>
									<Card.Header>{allTraits.ship_trait_names[voyageConfig.ship_trait]}</Card.Header>
									<p>ship trait</p>
								</Card.Content>
							</Card>
						</Card.Group>
					</Grid.Column>
					<Grid.Column width={2} textAlign='right'>
						<VoyageEditConfigModal voyageConfig={voyageConfig} updateConfig={updateConfig} />
					</Grid.Column>
				</Grid>
			}
			{voyageState != 'input' && (<VoyageExisting voyageConfig={voyageConfig} allShips={allShips} useCalc={() => setVoyageState('input')} />)}
			{voyageState == 'input' && (<VoyageInput voyageConfig={voyageConfig} myCrew={myCrew} allShips={allShips} useInVoyage={() => setVoyageState(voyageConfig.state)} />)}
		</React.Fragment>
	);

	function updateConfig(voyageConfig: any): void {
		setVoyageConfig({...voyageConfig});
		// Update stored voyageData with new voyageConfig
		setVoyageData({
			voyage_descriptions: [{...voyageConfig}],
			voyage: []
		});
		setVoyageState('input');
	}
}
Example #17
Source File: Timer3.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 5 votes vote down vote up
Timer: VFC<{ limit: number }> = ({ limit }) => {
  const [timeLeft, setTimeLeft] = useState(limit);
  const primes = useMemo(() => getPrimes(limit), [limit]);
  const timerId = useRef<NodeJS.Timeout>();
  const tick = () => setTimeLeft((t) => t - 1);

  const clearTimer = () => {
    if (timerId.current) clearInterval(timerId.current);
  };

  const reset = useCallback(() => {
    clearTimer();
    timerId.current = setInterval(tick, 1000);
    setTimeLeft(limit);
  }, [limit]);

  useEffect(() => {
    reset();

    return clearTimer;
  }, [reset]);

  useEffect(() => {
    if (timeLeft === 0) reset();
  }, [timeLeft, reset]);

  return (
    <Card>
      <Statistic className="number-board">
        <Statistic.Label>time</Statistic.Label>
        <Statistic.Value
          className={primes.includes(timeLeft) ? 'prime-number' : undefined}
        >
          {timeLeft}
        </Statistic.Value>
      </Statistic>
      <Card.Content>
        <Button color="red" fluid onClick={reset}>
          <Icon name="redo" />
          Reset
        </Button>
      </Card.Content>
    </Card>
  );
}
Example #18
Source File: Timer2.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 5 votes vote down vote up
Timer: VFC<{ limit: number }> = ({ limit }) => {
  const [timeLeft, setTimeLeft] = useState(limit);
  const primes = useMemo(() => getPrimes(limit), [limit]);
  const timerId = useRef<NodeJS.Timeout>();
  const reset = useCallback(() => setTimeLeft(limit), [limit]);
  const tick = () => setTimeLeft((t) => t - 1);

  useEffect(() => {
    const clearTimer = () => {
      if (timerId.current) clearInterval(timerId.current);
    };

    reset();
    clearTimer();
    timerId.current = setInterval(tick, 1000);

    return clearTimer;
  }, [limit, reset]);

  useEffect(() => {
    if (timeLeft === 0) reset();
  }, [timeLeft, reset]);

  return (
    <Card>
      <Statistic className="number-board">
        <Statistic.Label>time</Statistic.Label>
        <Statistic.Value
          className={primes.includes(timeLeft) ? 'prime-number' : undefined}
        >
          {timeLeft}
        </Statistic.Value>
      </Statistic>
      <Card.Content>
        <Button color="red" fluid onClick={reset}>
          <Icon name="redo" />
          Reset
        </Button>
      </Card.Content>
    </Card>
  );
}
Example #19
Source File: LobbyUserPanel.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 5 votes vote down vote up
render(){
        const props = this.props as any
        return(
            <div>
                {this.state.open ?
                    (
                        <div>

                        <Card width="100%">
                            <Button basic size="tiny"  compact onClick={()=>{this.handleClick()}} >
                                {/* <Header as='h5'> */}
                                    <Icon name="angle up" />Actions
                                {/* </Header> */}
                            </Button>                            
                            <Card.Content>
                                <VideoShareControl {...props} />
                                <DisplayShareControl {...props} />
                                <StampAccordion {...props} />
                                <SendTextAccordion {...props}/>

                                <SecondaryCameraAccordion {...props} />
                                <StampAccordionBySignal {...props} />


                                <FileShareControl {...props} />
                            </Card.Content>
                        </Card>
                        </div>
                    )
                    :
                    (
                        <div>
                        <Card  >
                            <Button basic size="tiny"  compact onClick={()=>{this.handleClick()}} >
                                {/* <Header as='h5'> */}
                                    <Icon name="angle down" />Actions
                                {/* </Header> */}
                            </Button>
                        </Card>
                        </div>

                    )

                }
            </div>
        )
    }
Example #20
Source File: LobbyUserPanel.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 5 votes vote down vote up
render(){
        const gs = this.props as GlobalState
        const props = this.props as any
        const appState = props.appState as AppState
        return(
            <div>
                {/* {appState.isSafari ?
                        // <video ref={appState.localVideoElementRef} style={{ display: "block", width: "720px", margin: "auto" }} playsInline />
                        appState.localVideoElement
                        :
                        <div/>
                } */}
                {this.state.open ?
                    (
                        <div>

                        <Card width="100%">
                            <Button basic size="tiny"  compact onClick={()=>{this.handleClick()}} >
                                {/* <Header as='h5'> */}
                                    <Icon name="angle up" />Preview
                                {/* </Header> */}
                            </Button>
                            <canvas ref={this.previewCanvasRef} style={{ display: "block" }} width="100%" height="100%" />
                        <Card.Content>
                            <Card.Header>{gs.userName} </Card.Header>
                            <Card.Meta>[email protected]</Card.Meta>
                            <Card.Description>
                                xxxxx
                            </Card.Description>
                        </Card.Content>
                        <Card.Content extra>
                            xxxxxx
                        </Card.Content>
                        </Card>
                        </div>
                    )
                    :
                    (
                        <div>
                        <Card  >
                            <Button basic size="tiny"  compact onClick={()=>{this.handleClick()}} >
                                {/* <Header as='h5'> */}
                                    <Icon name="angle down" />Preview
                                {/* </Header> */}
                            </Button>
                        </Card>
                        </div>

                    )

                }
            </div>
        )
    }
Example #21
Source File: ProfileWidgetPlus.tsx    From communitymap-ui with Apache License 2.0 4 votes vote down vote up
EditUserProfile: React.FC<{ user: firebase.User }> = ({ user }) => {
  const [toggled, setToggle] = useState(false);
  const info = useUserPublicInfo(user.uid, true);
  const [changed, setChanged] = useState<{ name: string } | null>(null);

  const { status, func: saveInfo } = useAsyncStatus(async () => {
    return saveUserPublicInfo({
      ...(info || { id: user.uid }),
      ...(changed || { name: '' }),
    });
  });

  if (info === undefined) return <Loader active />;
  // if (info === null) return <div>User not found :(</div>;
  const UserForm = () => (
    <Form
      onSubmit={() => saveInfo()}
      loading={status.pending}
      error={!!status.error}
      success={status.success}
    >
      <Form.Input
        label="Name/Nickname"
        required
        value={changed?.name || info?.name || ''}
        onChange={(e, { value }) => setChanged({ ...changed, name: value })}
      />
      {/* <Form.Input label="Gender"/> */}

      <Message error>{status.error}</Message>
      <Message success>Successfully saved</Message>

      <Form.Group>
        <Form.Button primary disabled={!changed}>
          Save
        </Form.Button>
        <Form.Button
          type="button"
          disabled={!changed}
          onClick={() => setChanged(null)}
        >
          Clear changes
        </Form.Button>
      </Form.Group>
    </Form>
  );
  const BtnSettings = () => (
    <Button
      circular
      icon="settings"
      onClick={() => setToggle((toggled) => !toggled)}
    />
  );

  return (
    <Card>
      <Image
        src="http://dwinery.com/ocm/wp-content/uploads/elementor/thumbs/avatar-ookvknm0adkt2o1cwyctxzbsfccgeo4fo00qr8xhao.png"
        wrapped
        ui={false}
      />
      <Card.Content>
        <Card.Header>
          <Grid>
            <Grid.Column floated="left" width={8}>
              {info?.name}
            </Grid.Column>
            <Grid.Column floated="right" width={3}>
              {BtnSettings()}
            </Grid.Column>
          </Grid>
        </Card.Header>
        <Card.Meta>
          <span className="date">Joined in: {info?.created}</span>
        </Card.Meta>
        <Card.Description>{toggled && <>{UserForm()}</>}</Card.Description>
      </Card.Content>
      <Card.Content extra>
        <Grid columns={2} divided>
          <Grid.Row>
            <Grid.Column>
              <Icon name="handshake" /> 11 People
            </Grid.Column>
            <Grid.Column>
              <Icon name="globe" /> 12 Places
            </Grid.Column>
          </Grid.Row>
        </Grid>
      </Card.Content>
    </Card>
  );
}
Example #22
Source File: event_information.tsx    From website with MIT License 4 votes vote down vote up
function EventInformationTab({ eventData }) {
	const { crewJson } = useStaticQuery(graphql`
		query {
			crewJson: allCrewJson {
				edges {
					node {
						name
						max_rarity
						imageUrlPortrait
						symbol
						traits
						traits_hidden
						traits_named
						base_skills {
							security_skill {
								core
							}
							command_skill {
								core
							}
							diplomacy_skill {
								core
							}
							engineering_skill {
								core
							}
							medicine_skill {
								core
							}
							science_skill {
								core
							}
						}
					}
				}
			}
		}
	`);
	const crewData = crewJson.edges.map(edge => edge.node);
	const crewMap = {};
	crewData.forEach(crew => {
		crewMap[crew.symbol] = crew;
	})

	const {
		name,
		description,
		bonus_text,
		content_types,
	} = eventData;

	const { bonus, featured } = getEventData(eventData, crewData);
	const featuredCrewData = featured.map(symbol => {
		const crew = crewMap[symbol];
		return {
			key: `crew_${crew.symbol}`,
			name: crew.name,
			image: getIconPath({file: crew.imageUrlPortrait}),
			rarity: crew.max_rarity,
			skills: Object.keys(crew.base_skills)
				.filter(skill => !!crew.base_skills[skill])
				.sort((a, b) => crew.base_skills[a].core > crew.base_skills[b].core ? -1 : 1)
				.map(skill => ({
					key: skill,
					imageUrl: `${process.env.GATSBY_ASSETS_URL}atlas/icon_${skill}.png`
				})),
			traits: crew.traits_named,
		};
	});
	const bonusCrew = crewData.filter(crew => bonus.includes(crew.symbol) && !featured.includes(crew.symbol));

	return (
		<>
			<Card fluid raised>
				<Card.Content>
					<Card.Header>{name}</Card.Header>
					<Card.Meta>{getEventType(content_types)}</Card.Meta>
					<Card.Description>{description}</Card.Description>
				</Card.Content>
				<Card.Content extra>
					<p>{bonus_text}</p>
				</Card.Content>
			</Card>
			<Header as="h3">Featured Crew</Header>
			<Card.Group>
				{featuredCrewData.map(crew => (
					<CrewCard key={crew.key} crew={crew} />
				))}
			</Card.Group>
			<Header as="h3">Bonus Crew</Header>
			{bonusCrew.length === 0 && (
				<p>Bonus crew not yet determined for this event.</p>
			)}
			{sortCrew(bonusCrew).map(crew => (
				<Label key={`crew_${crew.symbol}`} color="black" style={{ marginBottom: '5px' }}>
					<Image
						src={getIconPath({ file: crew.imageUrlPortrait })}
						size="massive"
						inline
						spaced="right"
						bordered
						style={{
							borderColor: getRarityColor(crew.max_rarity)
						}}
						alt={crew.name}
					/>
					{crew.name}
				</Label>
			))}
		</>
	);
}
Example #23
Source File: voyagecalculator.tsx    From website with MIT License 4 votes vote down vote up
VoyageInput = (props: VoyageInputProps) => {
	const { voyageConfig, myCrew, allShips, useInVoyage } = props;

	const [bestShip, setBestShip] = React.useState(undefined);
	const [consideredCrew, setConsideredCrew] = React.useState([]);
	const [calculator, setCalculator] = React.useState(isMobile ? 'ussjohnjay' : 'iampicard');
	const [calcOptions, setCalcOptions] = React.useState({});
	const [telemetryOptOut, setTelemetryOptOut] = useStateWithStorage('telemetryOptOut', false, { rememberForever: true });
	const [requests, setRequests] = React.useState([]);
	const [results, setResults] = React.useState([]);

	React.useEffect(() => {
		// Note that allShips is missing the default ship for some reason (1* Constellation Class)
		//	This WILL break voyagecalculator if that's the only ship a player owns
		const consideredShips = [];
		allShips.filter(ship => ship.owned).forEach(ship => {
			const traited = ship.traits.find(trait => trait === voyageConfig.ship_trait);
			let entry = {
				ship: ship,
				score: ship.antimatter + (traited ? 150 : 0),
				traited: traited,
				bestIndex: Math.min(ship.index.left, ship.index.right)
			};
			consideredShips.push(entry);
		});
		consideredShips.sort((a, b) => {
			if (a.score === b.score) return a.bestIndex - b.bestIndex;
			return b.score - a.score;
		});
		setBestShip(consideredShips[0]);
		setRequests([]);
		setResults([]);
	}, [voyageConfig]);

	React.useEffect(() => {
		return function cleanup() {
			// Cancel active calculations when leaving page
			requests.forEach(request => {
				if (request.calcState == CalculatorState.InProgress)
					request.abort();
			});
		}
	}, []);

	// Scroll here when calculator started, finished
	const topAnchor = React.useRef(null);

	const calculators = CALCULATORS.helpers.map(helper => {
		return { key: helper.id, value: helper.id, text: helper.name };
	});
	calculators.push({ key: 'all', value: 'all', text: 'All calculators (slower)' });

	return (
		<React.Fragment>
			<div ref={topAnchor} />
			{renderBestShip()}
			{renderResults()}
			{requests.length > 0 && <Header as='h3'>Options</Header>}
			<Form>
				<InputCrewOptions myCrew={myCrew} updateConsideredCrew={setConsideredCrew} />
				<Form.Group inline>
					<Form.Field
						control={Select}
						label='Calculator'
						options={calculators}
						value={calculator}
						onChange={(e, { value }) => setCalculator(value)}
						placeholder='Select calculator'
					/>
					{CALCULATORS.fields.filter(field => field.calculators.includes(calculator) || calculator == 'all').map(field => (
						<Form.Field
							key={field.id}
							control={Select}	/* Only control allowed at the moment */
							label={field.name}
							options={field.options}
							value={calcOptions[field.id] ?? field.default}
							placeholder={field.description}
							onChange={(e, { value }) => setCalcOptions(prevOptions =>
								{
									const newValue = { [field.id]: value };
									return {...prevOptions, ...newValue};
								}
							)}
						/>
					))}
				</Form.Group>

				<Form.Group>
					<Form.Button primary onClick={() => startCalculation()}>
						Calculate best crew selection
					</Form.Button>
					{voyageConfig.state &&
						<Form.Button onClick={()=> useInVoyage()}>
							Return to in voyage calculator
						</Form.Button>
					}
				</Form.Group>
			</Form>
			<Message style={{ marginTop: '2em' }}>
				<Message.Content>
					<Message.Header>Privacy Notice</Message.Header>
					<p>We use anonymous statistics aggregated from voyage calculations to improve DataCore and power our <b><Link to='/hall_of_fame'>Voyage Hall of Fame</Link></b>.</p>
					<Form>
						<Form.Field
							control={Checkbox}
							label={<label>Permit DataCore to collect anonymous voyage stats</label>}
							checked={!telemetryOptOut}
							onChange={(e, { checked }) => setTelemetryOptOut(!checked) }
						/>
					</Form>
				</Message.Content>
			</Message>
		</React.Fragment>
	);

	function renderBestShip(): JSX.Element {
		if (!bestShip) return (<></>);

		const direction = bestShip.ship.index.right < bestShip.ship.index.left ? 'right' : 'left';
		const index = bestShip.ship.index[direction] ?? 0;

		return (
			<Card fluid>
				<Card.Content>
					<Image floated='left' src={`${process.env.GATSBY_ASSETS_URL}${bestShip.ship.icon.file.substr(1).replace('/', '_')}.png`} style={{ height: '4em' }} />
					<Card.Header>{bestShip.ship.name}</Card.Header>
					<p>best ship{bestShip.traited && (<span style={{ marginLeft: '1em' }}>{` +`}{allTraits.ship_trait_names[voyageConfig.ship_trait]}</span>)}</p>
					<p style={{ marginTop: '.5em' }}>Tap <Icon name={`arrow ${direction}`} />{index} time{index != 1 ? 's' : ''} on your voyage ship selection screen to select {bestShip.ship.name}.</p>
				</Card.Content>
			</Card>
		);
	}

	function renderResults(): JSX.Element {
		if (results.length == 0)
			return (<></>);

		const showPopup = (result) => <Popup basic content={<p>{result.result.postscript}</p>} trigger={<p>{result.name}</p>} />
		const panes = results.map(result => ({
			menuItem: { key: result.id, content: result.result ? showPopup(result) : result.name },
			render: () => (
				<VoyageResultPane result={result.result}
					requests={requests} requestId={result.requestId}
					calcState={result.calcState} abortCalculation={abortCalculation}
				/>
			)
		}));

		return (
			<React.Fragment>
				<Header as='h3'>Recommended Lineups</Header>
				<Tab menu={{ pointing: true }}
					panes={panes}
				/>
			</React.Fragment>
		);
	}

	function scrollToAnchor(): void {
		if (!topAnchor.current) return;
		topAnchor.current.scrollIntoView({
			behavior: 'smooth'
		}, 500);
	}

	function startCalculation(): void {
		const helperConfig = {
			voyageConfig, bestShip, consideredCrew, calcOptions,
			resultsCallback: handleResults,
			isMobile
		};
		CALCULATORS.helpers.forEach(helper => {
			if (helper.id == calculator || calculator == 'all') {
				const request = helper.helper(helperConfig);
				requests.push(request);
				results.push({
					id: request.id,
					requestId: request.id,
					name: 'Calculating...',
					calcState: CalculatorState.InProgress
				});
				request.start();
			}
		});
		setRequests([...requests]);
		setResults([...results]);
		scrollToAnchor();
	}

	function handleResults(requestId: string, reqResults: any[], calcState: number): void {
		reqResults.forEach((reqResult, idx) => {
			// Update existing pane with results
			if (idx == 0) {
				setResults(prevResults => {
					const result = prevResults.find(r => r.id == requestId);
					if (calcState == CalculatorState.Done) {
						result.name = formatTime(reqResult.estimate.refills[0].result);
						result.calcState = CalculatorState.Done;
						sendTelemetry(requestId, reqResult);
					}
					result.result = reqResult;
					return [...prevResults];
				});
			}
			// Add new panes if multiple results generated by this request
			else {
				setResults(prevResults => [...prevResults, {
					id: requestId+'-'+idx,
					requestId,
					name: formatTime(reqResult.estimate.refills[0].result),
					calcState: CalculatorState.Done,
					result: reqResult
				}]);
			}
		});
		if (calcState == CalculatorState.Done) scrollToAnchor();
	}

	function abortCalculation(requestId: string): void {
		const request = requests.find(r => r.id == requestId);
		if (request) {
			request.abort();
			setResults(prevResults => {
				const result = prevResults.find(prev => prev.id == requestId);
				if (result.result) {
					result.name = formatTime(result.result.estimate.refills[0].result);
					result.calcState = CalculatorState.Done;
				}
				else {
					const index = prevResults.findIndex(prev => prev.id == requestId);
					prevResults.splice(index, 1);
				}
				return [...prevResults];
			});
		}
	}

	function sendTelemetry(requestId: string, result: any): void {
		if (telemetryOptOut) return;
		const request = requests.find(r => r.id == requestId);
		const estimatedDuration = result.estimate.refills[0].result*60*60;
		try {
			fetch(`${process.env.GATSBY_DATACORE_URL}api/telemetry`, {
				method: 'post',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify({
					type: 'voyageCalc',
					data: {
						voyagers: result.entries.map((entry) => entry.choice.symbol),
						estimatedDuration,
						calculator: request ? request.calculator : ''
					}
				})
			});
		}
		catch (err) {
			console.log('An error occurred while sending telemetry', err);
		}
	}
}