semantic-ui-react#Dropdown TypeScript Examples

The following examples show how to use semantic-ui-react#Dropdown. 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: Lobby.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 6 votes vote down vote up
render() {
        //const { activeItem } = this.state
        const props = this.props as any
        const gs = this.props as GlobalState
        return (
            <Menu stackable pointing secondary>
                {/* <Menu.Item>
                    <img src='/logo.png' />
                </Menu.Item> */}

                <Menu.Item as="h4"
                    name='FLECT Meetings with Amazon Chime SDK '
                >
                </Menu.Item>

                <Menu.Item
                    name=' '
                    active={this.state.activeItem === 'testimonials'}
                    onClick={(e,v)=>this.handleItemClick(v)}
                >
                </Menu.Item>

                <Menu.Menu position='right'>
                    <Dropdown item text='View'>
                        <Dropdown.Menu>
                            <Dropdown.Item onClick={(e)=>{props.toggleLeftBar()}}>
                                {gs.windowConfig.leftBarDisplay ? "Hide Left Pane": "Show Left Pane"}
                            </Dropdown.Item>
                            <Dropdown.Item onClick={(e)=>{props.toggleRightBar()}}>
                                {gs.windowConfig.rightBarDisplay ? "Hide Right Pane": "Show Right Pane"}
                            </Dropdown.Item>
                        </Dropdown.Menu>
                    </Dropdown>

                </Menu.Menu>
            </Menu>
        )
    }
Example #2
Source File: SecondaryCameraAccordion.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 6 votes vote down vote up
////////////////////////////////
  /// UI
  ///////////////////////////////
  generateAccordion = () => {
    const gs = this.props as GlobalState
    const inputVideoDevicesOpts = gs.inputVideoDevices!.map(info => { return { key: info.label, text: info.label, value: info.deviceId } })
    inputVideoDevicesOpts.push({key:"none", text:"none", value:"none"})

    const grid = (
      <Accordion>
        <Accordion.Title
          active={this.state.open}
          index={0}
          onClick={() => { this.handleClick() }}
        >
          <Icon name='dropdown' />
            Secondary camera(experimental)
        </Accordion.Title>
        <Accordion.Content active={this.state.open}>
          <Dropdown
            pointing='top left'
            options={inputVideoDevicesOpts}
            trigger={trigger}
            onChange={(e, { value }) => this.setSecondaryCamera(value as string)}
          />

          <video ref={this.secondaryVideoRef} autoPlay width="200px" height="160px"/>
        </Accordion.Content>
      </Accordion>
    )
    return grid
  }
Example #3
Source File: VideoResolutionControl.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 6 votes vote down vote up
render() {
        const props = this.props as any
        const gs = this.props as GlobalState
        const inputVideoResolutionsOpts=gs.inputVideoResolutions!.map(info => { return { key: info, text: info, value: info } })

        return (
            <Grid>
                <Grid.Row>
                    <Grid.Column>
                    
                    <Dropdown
                        pointing='top left'
                        options={inputVideoResolutionsOpts}
                        trigger={trigger}
                        onChange={(e, { value }) => props.selectInputVideoResolution(value as string)}
                    />
                    </Grid.Column>
                </Grid.Row>

            </Grid>

        )
    }
Example #4
Source File: dropdownopts.tsx    From website with MIT License 6 votes vote down vote up
render() {
		const { opts, settings } = this.props;
		const { selected, selectedSettings } = this.state;

		return (
            <div style={{fontSize: '140%'}}>
			<Dropdown item text={selected}>
				<Dropdown.Menu>
					{opts.map(opt => (
						<Dropdown.Item
							icon={opt === selected ? 'caret right' : ''}
							text={opt}
							key={opt}
							onClick={(e, { text }) => this._onSelectionChanged(text.toString())}
						/>
					))}
					<Dropdown.Divider />
					{settings.map(opt => (
						<Dropdown.Item
							icon={selectedSettings.indexOf(opt) >= 0 ? 'check' : ''}
							text={opt}
							key={opt}
							onClick={(e, { text }) => this._onSettingChanged(text.toString())}
						/>
					))}
				</Dropdown.Menu>
			</Dropdown>
            </div>
		);
	}
Example #5
Source File: MicControl.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 5 votes vote down vote up
render() {
        const props = this.props as any
        const gs = this.props as GlobalState
        const appState = props.appState as AppState
        const inputAudioDevicesOpts=gs.inputAudioDevices!.map(info => { return { key: info.label, text: info.label, value: info.deviceId } })

        const muteIcon=appState.currentSettings.mute ?
        (
            <Popup
            trigger={
                <Icon.Group link onClick={() => { props.toggleMute() }}>
                    <Icon size="large" color='black' name='microphone' />
                    <Icon size="large" color='red' name='dont' />
                </Icon.Group>        
            }       
            content="unmute."
            />
        )
        :
        (
            <Popup
            trigger={
                <Icon size="large" name="microphone"  color="black" link onClick={() => { props.toggleMute() }}/>
            }
            content="mute."
            />
        )

        return (
            <Grid>
                <Grid.Row>
                    <Grid.Column >
                    {muteIcon}
                    <Dropdown
                        style={{paddingLeft:"10px"}}
                        pointing='top left'
                        options={inputAudioDevicesOpts}
                        trigger={trigger}
                        onChange={(e, { value }) => props.selectInputAudioDevice(value as string)}
                    />

                        {/* <List style={{paddingLeft:"15px",paddingTop:"0px",paddingBottom:"0px"}} link>
                            <List.Item as='a' active onClick={() => { props.toggleMute() }}><Icon name="ban" color={appState.currentSettings.mute ? "red" : "grey"} />Mute</List.Item>
                        </List>  */}



                    </Grid.Column>
                </Grid.Row>
            </Grid>            


        )
    }
Example #6
Source File: crewpage.tsx    From website with MIT License 5 votes vote down vote up
renderEquipment(crew) {
		let options = [];
		crew.equipment_slots.forEach(es => {
			let equipment = this.state.items.find(item => item.symbol === es.symbol);
			if (!equipment) {
				console.warn(`Could not find item ${es.symbol}`);
				return;
			}

			options.push({
				key: es.symbol + '_' + es.level,
				text: `${equipment.name} (level ${es.level})`,
				value: es.symbol,
				content: (
					<Header
						icon={
							<ItemDisplay
								src={`${process.env.GATSBY_ASSETS_URL}${equipment.imageUrl}`}
								size={48}
								maxRarity={equipment.rarity}
								rarity={equipment.rarity}
							/>
						}
						content={equipment.name}
						subheader={`Level ${es.level}`}
					/>
				)
			});
		});

		return (
			<Dropdown
				selection
				fluid
				options={options}
				placeholder='Choose an equipment to see its details'
				onChange={(ev, { value }) => this.setState({ selectedEquipment: value as number })}
			/>
		);
	}
Example #7
Source File: bridgecrew.tsx    From website with MIT License 5 votes vote down vote up
render() {
		if (this.state.allcrew.length === 0) {
			return (
				<Layout title='Bridge crew assembly'>
					<div className='ui medium centered text active inline loader'>Loading data...</div>
				</Layout>
			);
		}

		let peopleToShow = [...this.state.peopleList];

		return (
			<Layout title='Bridge crew assembly'>
				<Header as='h4'>Bridge crew assembly</Header>
				<p>Assemble your bridge crew.</p>
				<Form>
					<Form.Group>
						<Dropdown
							clearable
							fluid
							multiple
							search
							selection
							closeOnChange
							options={peopleToShow}
							placeholder='Select or search for crew'
							label='Bridge crew'
							value={this.state.currentSelectedItems}
							onChange={(e, { value }) => this._selectionChanged(value)}
						/>
					</Form.Group>
				</Form>

				<Divider horizontal hidden />

				<div style={{ height: '500px', overflow: 'hidden', textAlign: 'center', padding: '25px', backgroundColor: '#203147', border: '2px solid lightblue' }}>
					<Header as='h3'>Bridge Crew</Header>
					{this.state.entries.map((entry, idx) => (
							<img src={`${process.env.GATSBY_ASSETS_URL}${entry.crew.imageUrlFullBody}`} style={{ height: '725px', margin: '0 -6.5%' }} />
					))}
				</div>
			</Layout>
		);
	}
Example #8
Source File: behold.tsx    From website with MIT License 5 votes vote down vote up
render() {
		if (this.state.allcrew.length === 0) {
			return (
				<Layout title='Behold helper / crew comparison'>
					<div className='ui medium centered text active inline loader'>Loading data...</div>
				</Layout>
			);
		}

		let peopleToShow = [...this.state.peopleList];
		if (this.state.minRarity) {
			peopleToShow = peopleToShow.filter((crew) => crew.max_rarity >= this.state.minRarity);
		}

		return (
			<Layout title='Behold helper / crew comparison'>
				<Header as='h2'>Behold helper / crew comparison</Header>
				<Form>
					<Form.Group>
						<Dropdown
							clearable
							fluid
							multiple
							search
							selection
							closeOnChange
							options={peopleToShow}
							placeholder='Search for crew to compare'
							label='Behold crew'
							value={this.state.currentSelectedItems}
							onChange={(e, { value }) => this._selectionChanged(value)}
						/>
						<Form.Field
							control={Dropdown}
							placeholder={this.state.minRarity ? `Minimum rarity: ${this.state.minRarity}` : `Minimum rarity`}
							selection
							options={rarityOptions}
							value={this.state.minRarity}
							onChange={(e, { value }) => this.setState({ minRarity: value })}
						/>
					</Form.Group>
				</Form>
				{this.state.currentSelectedItems?.length > 0 && (
					<Button compact icon='add user' color='green' content='Preview all in your roster' onClick={() => { this._addProspects(); }} />
				)}

				<Divider horizontal hidden />

				<Grid columns={3} stackable centered padded divided>
					{this.state.entries.map((entry, idx) => (
						<Grid.Column key={idx}>
							<Header as='h5'>
								<Link to={`/crew/${entry.crew.symbol}/`}>
									{entry.crew.name}{' '}
									<Rating defaultRating={entry.crew.max_rarity} maxRating={entry.crew.max_rarity} icon='star' size='small' disabled />
								</Link>
							</Header>
							<CommonCrewData compact={true} crewDemands={entry.crewDemands} crew={entry.crew} markdownRemark={entry.markdownRemark} roster={this.state.roster}/>
							{entry.markdown && (
								<React.Fragment>
									<div dangerouslySetInnerHTML={{ __html: entry.markdown }} />
									<div style={{ marginTop: '1em' }}>
										<a href={`https://www.bigbook.app/crew/${entry.crew.symbol}`}>View {entry.crew.name} on Big Book</a>
									</div>
								</React.Fragment>
							)}
						</Grid.Column>
					))}
				</Grid>
			</Layout>
		);
	}
Example #9
Source File: topmenu.tsx    From website with MIT License 5 votes vote down vote up
useMainMenuItems = (verticalLayout: boolean) => {
	const createSubMenu = (title, children) => {
		const menuKey = title.toLowerCase().replace(/[^a-z0-9_]/g, '');
		if (verticalLayout) {
			return (
				<Menu.Item key={`/${menuKey}`}>
					<Menu.Header>{title}</Menu.Header>
					<Menu.Menu>
						{children.map(item => (
							<Menu.Item key={`${menuKey}${item.link}`} onClick={() => navigate(item.link)}>
								{item.title}
							</Menu.Item>
						))}
					</Menu.Menu>
				</Menu.Item>
			);
		} else {
			return (
				<Dropdown key={`/${menuKey}`} item simple text={title}>
					<Dropdown.Menu>
						{children.map(item => (
							<Dropdown.Item key={`${menuKey}${item.link}`} onClick={() => navigate(item.link)}>
								{item.title}
							</Dropdown.Item>
						))}
					</Dropdown.Menu>
				</Dropdown>
			);
		}
	};

	let items = [
		<Menu.Item key='/' onClick={() => navigate('/')}>
			Crew stats
		</Menu.Item>,
		<Menu.Item key='/behold' onClick={() => navigate('/behold')}>
			Behold
		</Menu.Item>
	];

	items.push(createSubMenu('Player tools', Object.entries(playerTools).map(([key, value]) => ({
			title: value.title,
			link: `/playertools?tool=${key}`
		})))
	);

	const pages = [
		{ title: 'Events', link: '/events' },
		{ title: 'Collections', link: '/collections' },
		{ title: 'Items', link: '/items' },
		{ title: 'Misc stats', link: '/stats' },
		{ title: 'Episodes', link: '/episodes' },
		{ title: 'Hall of Fame', link: '/hall_of_fame' },
		{ title: 'Worfle', link: '/crewchallenge' }
	];
	items.push(createSubMenu('Pages', pages));

	items.push(<Menu.Item key='bigbook' onClick={() => navigate('https://bigbook.app')}>Big book</Menu.Item>);

	const about = [
		{ title: 'About DataCore', link: '/about' },
		{ title: 'Announcements', link: '/announcements' }
	];
	// Show other markdowns as discovered by Gatsby in About menu
	const otherPages = useOtherPages();
	otherPages.map((page) => {
		about.push(
			{ title: page.title, link: page.slug }
		);
	});
	items.push(createSubMenu('About', about));

	if (verticalLayout) {
		return items;
	} else {
		return <Container>{items}</Container>;
	}
}
Example #10
Source File: factions.tsx    From website with MIT License 5 votes vote down vote up
render() {
    const { factionInfo, shuttleBays } = this.props;
    const { successOdds } = this.state;
    const updateSuccessOdds = odds => this.setState({successOdds: odds});

    return (
      <>
        <p><span>Running shuttles at average odds of </span>
          <Dropdown text={`${successOdds}%`}>
            <Dropdown.Menu>
              {oddsValues.map(val => (<Dropdown.Item onClick={(e, { value }) => updateSuccessOdds(value)} text={`${val}%`} value={val} />))}
            </Dropdown.Menu>
          </Dropdown>
          <p>(Note: Shuttles cannot be run with a probability of success less than 14%. Shuttles need a probability of less than 60% to be tanked.)</p>
        </p>
        <Table>
          <Table.Header>
          <Table.Row>
            <Table.HeaderCell>Faction</Table.HeaderCell>
            <Table.HeaderCell>Reputation</Table.HeaderCell>
            <Table.HeaderCell>Shuttles needed</Table.HeaderCell>
            <Table.HeaderCell>Time needed</Table.HeaderCell>
          </Table.Row>
          </Table.Header>
          <Table.Body>
          {factionInfo.map((faction, index) => {
            let shuttlesNeededToMaxRep = this._shuttlesToHonouredStatus(faction.reputation);
            let hoursNeededToMaxRep = Math.ceil(shuttlesNeededToMaxRep/shuttleBays)*3;
            let shuttlesNeededToTank = Math.ceil(faction.completed_shuttle_adventures/this._expectedCSA(successOdds/100));
            let hoursNeededToTank = Math.ceil(shuttlesNeededToTank/shuttleBays)*3;

            return (
              <Table.Row key={index}>
                <Table.Cell><span><Image floated='left' size='mini' src={`${process.env.GATSBY_ASSETS_URL}icons_icon_faction_${factionImageLocations[index]}.png`} />{faction.name}</span></Table.Cell>
                <Table.Cell>{this._reputations(faction.reputation)}</Table.Cell>
                <Table.Cell>
                  {faction.reputation < 980 && <p>You need {shuttlesNeededToMaxRep} successful shuttle missions to achieve honored status.</p>}
                  {shuttlesNeededToTank > 0 && <p>To tank your shuttles you need to run {shuttlesNeededToTank} shuttles.</p>}
                  {shuttlesNeededToTank == 0 && <p>Already tanked</p>}
                </Table.Cell>
                <Table.Cell>
                  {faction.reputation < 980 && <p>{this._formatTime(hoursNeededToMaxRep)}</p>}
                  <p>{this._formatTime(hoursNeededToTank)}</p>
                </Table.Cell>
              </Table.Row>
            );
          })}
          </Table.Body>
        </Table>
        <p>Note: <a href="https://www.reddit.com/r/StarTrekTimelines/comments/aq5qzg/guide_tanked_shuttles_why_and_how/">Tanking</a> shuttles is the process of deliberately failing shuttles so that the difficulty and duration of shuttle missions go down.</p>
      </>
    );
  }
Example #11
Source File: crewretrieval.tsx    From website with MIT License 5 votes vote down vote up
ComboGrid = (props: ComboGridProps) => {
	const { crew, fuseGroups } = props;
	let combos = [...props.combos];

	let [fuseIndex, setFuseIndex] = React.useState(1);
	let [groupIndex, setGroupIndex] = React.useState(0);

	React.useEffect(() => {
		setGroupIndex(0);
	}, [fuseIndex]);

	// Reset indices if out of bounds after changing filters
	if (!fuseGroups['x'+fuseIndex]) fuseIndex = 1;
	const groups = fuseGroups['x'+fuseIndex];
	if (!groups[groupIndex]) groupIndex = 0;

	const fuseOptions = [];
	[1, 2, 3, 4, 5].forEach(fuse => {
		const fuseId = 'x'+fuse;
		if (fuseGroups[fuseId] && fuseGroups[fuseId].length > 0) {
			fuseOptions.push({ key: fuse, value: fuse, text: fuseId });
		}
	});

	let groupOptions = [];
	if (fuseIndex > 1) {
		combos = groups[groupIndex].map((comboId) => combos[comboId]);
		groupOptions = groups.map((group, groupId) => {
			return { key: groupId, value: groupId, text: 'Option '+(groupId+1) };
		});
		// Only show first 200 options
		if (groupOptions.length > 200)
			groupOptions = groupOptions.slice(0, 200);
	}

	return (
		<div>
			<div className='title' style={{ marginBottom: '1em' }}>
				Use <b>{fuseIndex == 1 ? combos.length == 1 ? 'the combo' : 'any combo' : 'all combos'}</b> below to retrieve <b>{crew.name}</b>
				{fuseOptions.length > 1 && (
					<Dropdown
						style={{ marginLeft: '1em' }}
						options={fuseOptions}
						value={fuseIndex}
						onChange={(e, { value }) => setFuseIndex(value)}
					/>
				)}
				{groupOptions.length > 1 && (
					<Dropdown scrolling
						style={{ marginLeft: '1em' }}
						options={groupOptions}
						value={groupIndex}
						onChange={(e, { value }) => setGroupIndex(value)}
					/>
				)}
			</div>
			<div className='content'>
				<Grid columns='equal' onClick={() => cycleGroupOptions()}>
					{combos.map((combo, cdx) =>
						<Grid.Row key={'combo'+cdx}>
							{combo.map((polestar, pdx) => (
								<Grid.Column key={'combo'+cdx+',polestar'+pdx}>
									<img width={32} src={`${process.env.GATSBY_ASSETS_URL}${polestar.icon.file.substr(1).replace(/\//g, '_')}`} />
									<br />{polestar.short_name}
									<br /><small>({polestar.loaned ? `${polestar.quantity-polestar.loaned} +${polestar.loaned} added` : polestar.quantity})</small>
								</Grid.Column>
							))}
						</Grid.Row>
					)}
				</Grid>
			</div>
		</div>
	);

	function cycleGroupOptions(): void {
		if (groupOptions.length <= 1) return;
		setGroupIndex(groupIndex+1 < groupOptions.length ? groupIndex+1 : 0);
	}
}
Example #12
Source File: crewretrieval.tsx    From website with MIT License 5 votes vote down vote up
CrewPicker = (props: CrewPickerProps) => {
	const { updateCrew } = props;

	const [options, setOptions] = React.useState(undefined);

	React.useEffect(() => {
		if (props.value != '' && options && !options.initialized)
			populatePlaceholders();
	}, [props.value]);

	if (!options) {
		populatePlaceholders();
		return (<></>);
	}

	return (
		<Dropdown
			placeholder='Search for desired crew'
			noResultsMessage='No unretrievable crew found.'
			style={{ minWidth: '20em' }}
			search
			selection
			clearable
			options={options.list}
			value={props.value}
			onFocus={() => { if (!options.initialized) populateOptions(); }}
			onChange={(e, { value }) => updateCrew(value) }
		/>
	);

	function populatePlaceholders(): void {
		const options = { initialized: false, list: [] };
		if (props.value != '') {
			const c = props.crew.find(c => c.symbol === props.value);
			options.list = [{ key: c.symbol, value: c.symbol, text: c.name, image: { avatar: true, src: `${process.env.GATSBY_ASSETS_URL}${c.imageUrlPortrait}` }}];
		}
		else {
			options.list = [{ key: 0, value: 0, text: 'Loading...' }];
		}
		setOptions({...options});
	}

	function populateOptions(): void {
		let crewList = [...props.crew];
		options.list = crewList.sort((a, b) => a.name.localeCompare(b.name)).map(c => {
			return { key: c.symbol, value: c.symbol, text: c.name, image: { avatar: true, src: `${process.env.GATSBY_ASSETS_URL}${c.imageUrlPortrait}` }};
		});
		options.initialized = true;
		setOptions({...options});
	}
}
Example #13
Source File: ProfileWidget.tsx    From communitymap-ui with Apache License 2.0 5 votes vote down vote up
ProfileWidget: React.FC = () => {
  const user = useAuth();

  const [login, setLogin] = useState(false);
  useEffect(() => {
    if (user && login) {
      setLogin(false);
    }
  }, [user, login]);

  const signOut = () => getFirebaseApp().auth().signOut();

  return (
    <div id="profile-widget">
      {login && <Login title="" onClose={() => setLogin(false)} />}
      {user ? (
        <Dropdown
          trigger={
            <Button className="profile-button" icon size="large">
              <Icon.Group>
                <Icon name="user outline" />
              </Icon.Group>
            </Button>
          }
          pointing="top right"
          icon={null}
        >
          <Dropdown.Menu>
            <Dropdown.Item disabled>{user.email}</Dropdown.Item>
            <Dropdown.Divider />
            <Dropdown.Item onClick={signOut}>
              <Icon name="log out" />
              Log out
            </Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      ) : (
        <Button primary size="large" onClick={() => setLogin(true)}>
          Sign in
        </Button>
      )}
    </div>
  );
}
Example #14
Source File: TopBar.tsx    From watchparty with MIT License 5 votes vote down vote up
render() {
    return (
      <Dropdown
        style={{ height: '36px' }}
        icon="group"
        labeled
        className="icon"
        button
        text="My Rooms"
        onClick={this.refreshRooms}
        scrolling
      >
        <Dropdown.Menu>
          {this.state.rooms.length === 0 && (
            <Dropdown.Item disabled>You have no permanent rooms.</Dropdown.Item>
          )}
          {this.state.rooms.map((room: any) => {
            return (
              <Dropdown.Item
                link
                href={
                  room.vanity
                    ? '/r/' + room.vanity
                    : '/' + room.roomId.replace('/', '#')
                }
                onClick={() => {
                  if (!room.vanity) {
                    setTimeout(() => window.location.reload(), 100);
                  }
                }}
              >
                <div style={{ display: 'flex', alignItems: 'center' }}>
                  {room.vanity
                    ? `/r/${room.vanity}`
                    : room.roomId.replace('/', '#')}
                  <div style={{ marginLeft: 'auto', paddingLeft: '20px' }}>
                    <Button
                      icon
                      onClick={(e: React.MouseEvent) => {
                        e.stopPropagation();
                        e.nativeEvent.stopImmediatePropagation();
                        e.preventDefault();
                        this.deleteRoom(room.roomId);
                      }}
                      color="red"
                      size="mini"
                    >
                      <Icon name="trash" />
                    </Button>
                  </div>
                </div>
              </Dropdown.Item>
            );
          })}
        </Dropdown.Menu>
      </Dropdown>
    );
  }
Example #15
Source File: SpeakerControl.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 5 votes vote down vote up
render() {
        const props = this.props as any
        const gs = this.props as GlobalState
        const appState = props.appState as AppState
        const outputAudioDevicesOpts=gs.outputAudioDevices!.map(info => { return { key: info.label, text: info.label, value: info.deviceId } })

        const enableIcon=appState.currentSettings.speakerEnable ?
        (
            <Popup
            trigger={
                <Icon size="large" name="sound"  color="black" link onClick={() => { props.toggleSpeaker() }}/>
            }
            content="disable."
            />
        )
        :
        (
            <Popup
            trigger={
                <Icon.Group link onClick={() => { props.toggleSpeaker() }}>
                    <Icon size="large" color='black' name='sound' />
                    <Icon size="large" color='red' name='dont' />
                </Icon.Group>        
            }       
            content="enable."
        />
        )

        return (

            <Grid>
                <Grid.Row>
                    <Grid.Column >
                    {enableIcon}
                    <Dropdown
                        style={{paddingLeft:"10px"}}
                        pointing='top left'
                        options={outputAudioDevicesOpts}
                        trigger={trigger}
                        onChange={(e, { value }) => props.selectOutputAudioDevice(value as string)}
                    />
                        {/* <List style={{paddingLeft:"15px",paddingTop:"0px",paddingBottom:"0px"}} link>
                            <List.Item as='a' active onClick={() => { props.toggleSpeaker() }}><Icon name="ban" color={appState.currentSettings.speakerEnable ? "grey" : "red"}/>Disable Speaker</List.Item>
                        </List>  */}


                    </Grid.Column>
                </Grid.Row>
            </Grid>

        )
    }
Example #16
Source File: VideoControl.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 5 votes vote down vote up
render() {
        const props = this.props as any
        const gs = this.props as GlobalState
        const appState = props.appState as AppState
        const inputVideoDevicesOpts=gs.inputVideoDevices!.map(info => { return { key: info.label, text: info.label, value: info.deviceId } })

        const enableIcon=appState.currentSettings.videoEnable ?
        (
            <Popup
            trigger={
                <Icon size="large" name="video camera"  color="black" link onClick={() => { props.toggleVideo() }}/>
            }
            content="disable."
            />
        )
        :
        (
            <Popup
            trigger={
                <Icon.Group link onClick={() => { props.toggleVideo() }}>
                    <Icon size="large" color='black' name='video camera' />
                    <Icon size="large" color='red' name='dont' />
                </Icon.Group>        
            }       
            content="enable."
        />
        )
        
        return (
            <Grid>
                <Grid.Row>
                    <Grid.Column>

                    {enableIcon}
                    
                    <Dropdown
                        style={{paddingLeft:"10px"}}
                        pointing='top left'
                        options={inputVideoDevicesOpts}
                        trigger={trigger}
                        onChange={(e, { value }) => props.selectInputVideoDevice(value as string)}
                    />
                        {/* <List style={{paddingLeft:"15px",paddingTop:"0px",paddingBottom:"0px"}} link>
                            <List.Item as='a' active onClick={() => { props.toggleVideo() }}><Icon name="ban" color={appState.currentSettings.videoEnable ? "grey" : "red"}/>Disable Camera</List.Item>
                        </List>  */}
                    </Grid.Column>
                </Grid.Row>
                <Grid.Row>
                    <Grid.Column>
                        <List link>
                            <List.Item as='a' active onClick={() => this.fileInputRef.current!.click()}>
                                <Icon name="folder"  active />dummy mov.
                            </List.Item>
                        </List>
                        <input
                            ref={this.fileInputRef}
                            type="file"
                            hidden
                            onChange={(e) => props.setSelectedVideo(e)}
                        />

                    </Grid.Column>
                </Grid.Row>


            </Grid>

        )
    }
Example #17
Source File: index.tsx    From frontegg-react with MIT License 5 votes vote down vote up
Select = (props: SelectProps) => {
  const [open, setOpen] = useState(false);
  const p = mapper(props);
  const { t } = useT();
  const { multiple, options, label, loading, getOptionLabel, renderOption } = p;
  const {
    noOptionsText,
    loadingText,
    onOpen,
    open: openProps,
    onClose,
    fullWidth,
    onBlur,
    value,
    onChange,
    options: propsOptions,
  } = props;

  const handleOnChange = useCallback(
    (e, data) => {
      onChange && onChange(e, data);
    },
    [onChange]
  );

  const optionsMessage = useMemo(
    () => (loading ? loadingText ?? `${t('common.loading')}...` : noOptionsText ?? t('common.empty-items')),
    [loading, loadingText, noOptionsText]
  );

  const renderLabel = useCallback((option, state) => {
    const { flag, image, text } = option;
    const content = renderOption ? (
      <>{renderOption({ label: option.text, value: option.value }, state)}</>
    ) : (
      <>
        {(Flag as any).create(flag)}
        {(Image as any).create(image)}
        {text}
      </>
    );
    return { content };
  }, []);

  const preparedValue = useMemo(() => {
    return Array.isArray(value) ? value?.map((v) => v.value) : value?.value;
  }, [value]);

  const onHandleChange = useCallback(
    (e, newValue) => {
      const data = propsOptions?.filter((o) => newValue.includes(o.value)) || [];
      onChange?.(e, data);
    },
    [propsOptions]
  );

  return (
    <Dropdown
      {...p}
      search
      selection
      value={preparedValue}
      fluid={fullWidth ?? true}
      open={openProps ?? open}
      onBlur={(e, data) => onBlur?.({ ...e, target: { ...e.target, name: data.name } } as any)}
      options={options}
      loading={loading}
      onOpen={() => (onOpen ? onOpen() : setOpen(true))}
      onClose={() => (onClose ? onClose() : setOpen(false))}
      multiple={multiple ?? false}
      placeholder={value ? '' : label}
      onChange={(e, data) => onHandleChange(e, data.value)}
      renderLabel={(option, _index, state) => renderLabel(option, state)}
      noResultsMessage={optionsMessage}
      getoptionlabel={getOptionLabel}
    />
  );
}
Example #18
Source File: SearchComponent.tsx    From watchparty with MIT License 5 votes vote down vote up
render() {
    const setMedia = this.setMedia;
    let placeholder = '[BETA] Search streams';
    let icon = 'film';
    if (this.props.type === 'youtube') {
      placeholder = 'Search YouTube';
      icon = 'youtube';
    }
    if (this.state.loading) {
      icon = 'loading circle notch';
    }
    return (
      <React.Fragment>
        <Dropdown
          key={this.state.resetDropdown}
          fluid
          style={{ height: '36px' }}
          button
          icon={icon}
          className="icon"
          labeled
          disabled={this.props.disabled}
          search={(() => {}) as any}
          text={placeholder}
          onSearchChange={this.doSearch}
          scrolling
          // onBlur={() => this.setState({ results: this.state.watchOptions })}
          //searchQuery={this.state.query}
          //loading={this.state.loading}
        >
          {Boolean(this.state.results.length) ? (
            <Dropdown.Menu>
              {this.state.results.map((result: SearchResult) => {
                if (this.props.type === 'youtube') {
                  return (
                    <YouTubeSearchResult
                      key={result.url}
                      {...result}
                      setMedia={setMedia}
                      playlistAdd={this.props.playlistAdd}
                    />
                  );
                }
                return (
                  <StreamPathSearchResult
                    key={result.url}
                    {...result}
                    setMedia={setMedia}
                    launchMultiSelect={this.props.launchMultiSelect as Function}
                    streamPath={this.props.streamPath || ''}
                  />
                );
              })}
            </Dropdown.Menu>
          ) : null}
        </Dropdown>
      </React.Fragment>
    );
  }
Example #19
Source File: TopBar.tsx    From watchparty with MIT License 5 votes vote down vote up
render() {
    if (this.props.user) {
      return (
        <div
          style={{
            margin: '4px',
            width: '100px',
            alignItems: 'center',
            cursor: 'pointer',
          }}
        >
          <Image
            avatar
            src={this.state.userImage}
            onClick={() => this.setState({ isProfileOpen: true })}
          />
          {this.state.isProfileOpen && this.props.user && (
            <ProfileModal
              user={this.props.user}
              userImage={this.state.userImage}
              close={() => this.setState({ isProfileOpen: false })}
            />
          )}
        </div>
      );
    }
    return (
      <React.Fragment>
        {this.state.isLoginOpen && (
          <LoginModal
            closeLogin={() => this.setState({ isLoginOpen: false })}
          />
        )}
        <Popup
          basic
          content="Sign in to set your name and picture, subscribe, or launch VBrowsers"
          trigger={
            <Dropdown
              style={{ height: '36px' }}
              icon="sign in"
              labeled
              className="icon"
              button
              text="Sign in"
              fluid={this.props.fluid}
            >
              <Dropdown.Menu>
                <Dropdown.Item onClick={this.facebookSignIn}>
                  <Icon name="facebook" />
                  Facebook
                </Dropdown.Item>
                <Dropdown.Item onClick={this.googleSignIn}>
                  <Icon name="google" />
                  Google
                </Dropdown.Item>
                <Dropdown.Item
                  onClick={() => this.setState({ isLoginOpen: true })}
                >
                  <Icon name="mail" />
                  Email
                </Dropdown.Item>
              </Dropdown.Menu>
            </Dropdown>
          }
        />
      </React.Fragment>
    );
  }
Example #20
Source File: searchabletable.tsx    From website with MIT License 4 votes vote down vote up
SearchableTable = (props: SearchableTableProps) => {
	let data = [...props.data];
	const tableId = props.id ?? '';

	const hasDate = data.length > 0 && data[0].date_added;
	const defaultSort = {
		column: data.length > 0 && hasDate ? 'date_added' : 'name',
		direction: data.length > 0 && hasDate ? 'descending' : 'ascending'
	};

	const [searchFilter, setSearchFilter] = useStateWithStorage(tableId+'searchFilter', '');
	const [filterType, setFilterType] = useStateWithStorage(tableId+'filterType', 'Any match');
	const [column, setColumn] = useStateWithStorage(tableId+'column', defaultSort.column);
	const [direction, setDirection] = useStateWithStorage(tableId+'direction', defaultSort.direction);
	const [pagination_rows, setPaginationRows] = useStateWithStorage(tableId+'paginationRows', 10);
	const [pagination_page, setPaginationPage] = useStateWithStorage(tableId+'paginationPage', 1);

	const [activeLock, setActiveLock] = React.useState(undefined);

	// Override stored values with custom initial options and reset all others to defaults
	//	Previously stored values will be rendered before an override triggers a re-render
	React.useEffect(() => {
		if (props.initOptions) {
			setSearchFilter(props.initOptions['search'] ?? '');
			setFilterType(props.initOptions['filter'] ?? 'Any match');
			setColumn(props.initOptions['column'] ?? defaultSort.column);
			setDirection(props.initOptions['direction'] ?? defaultSort.direction);
			setPaginationRows(props.initOptions['rows'] ?? 10);
			setPaginationPage(props.initOptions['page'] ?? 1);
		}
	}, [props.initOptions]);

	// Activate lock by default if only 1 lockable
	React.useEffect(() => {
		setActiveLock(props.lockable?.length === 1 ? props.lockable[0] : undefined);
	}, [props.lockable]);

	// Update column and/or toggle direction, and store new values in state
	//	Actual sorting of full dataset will occur on next render before filtering and pagination
	function onHeaderClick(newColumn) {
		if (!newColumn.column) return;

		const lastColumn = column, lastDirection = direction;

		const sortConfig = {
			field: newColumn.column,
			direction: lastDirection === 'ascending' ? 'descending' : 'ascending'
		};
		if (newColumn.pseudocolumns && newColumn.pseudocolumns.includes(lastColumn)) {
			if (direction === 'descending') {
				const nextIndex = newColumn.pseudocolumns.indexOf(lastColumn) + 1; // Will be 0 if previous column was not a pseudocolumn
				sortConfig.field = newColumn.pseudocolumns[nextIndex === newColumn.pseudocolumns.length ? 0 : nextIndex];
				sortConfig.direction = 'ascending';
			}
			else {
				sortConfig.field = lastColumn;
				sortConfig.direction = 'descending';
			}
		}
		else if (newColumn.column !== lastColumn) {
			sortConfig.direction = newColumn.reverse ? 'descending' : 'ascending';
		}

		setColumn(sortConfig.field);
		setDirection(sortConfig.direction);
		setPaginationPage(1);
	}

	function onChangeFilter(value) {
		setSearchFilter(value);
		setPaginationPage(1);
	}

	function renderTableHeader(column: any, direction: 'descending' | 'ascending' | null): JSX.Element {
		return (
			<Table.Row>
				{props.config.map((cell, idx) => (
					<Table.HeaderCell
						key={idx}
						width={cell.width as any}
						sorted={((cell.pseudocolumns && cell.pseudocolumns.includes(column)) || (column === cell.column)) ? direction : null}
						onClick={() => onHeaderClick(cell)}
						textAlign={cell.width === 1 ? 'center' : 'left'}
					>
						{cell.title}{cell.pseudocolumns?.includes(column) && <><br/><small>{column.replace('_',' ').replace('.length', '')}</small></>}
					</Table.HeaderCell>
				))}
			</Table.Row>
		);
	}

	function renderPermalink(): JSX.Element {
		// Will not catch custom options (e.g. highlight)
		const params = new URLSearchParams();
		if (searchFilter != '') params.append('search', searchFilter);
		if (filterType != 'Any match') params.append('filter', filterType);
		if (column != defaultSort.column) params.append('column', column);
		if (direction != defaultSort.direction) params.append('direction', direction);
		if (pagination_rows != 10) params.append('rows', pagination_rows);
		if (pagination_page != 1) params.append('page', pagination_page);
		let permalink = window.location.protocol + '//' + window.location.host + window.location.pathname;
		if (params.toString() != '') permalink += '?' + params.toString();
		return (
			<Link to={permalink}>
				<Icon name='linkify' /> Permalink
			</Link>
		);
	}

	function onLockableClick(lock: any): void {
		if (lock) {
			setActiveLock(lock);
		}
		else {
			setActiveLock(undefined);
			// Remember active page after removing lock
			setPaginationPage(activePage);
		}
	}

	function isRowActive(row: any, highlight: any): boolean {
		if (!highlight) return false;
		let isMatch = true;
		Object.keys(highlight).forEach(key => {
			if (row[key] !== highlight[key]) isMatch = false;
		});
		return isMatch;
	}

	// Sorting
	if (column) {
		const sortConfig: IConfigSortData = {
			field: column,
			direction: direction,
			keepSortOptions: true
		};

		// Define tiebreaker rules with names in alphabetical order as default
		//	Hack here to sort rarity in the same direction as max_rarity
		let subsort = [];
		const columnConfig = props.config.find(col => col.column === column);
		if (columnConfig && columnConfig.tiebreakers) {
			subsort = columnConfig.tiebreakers.map(subfield => {
				const subdirection = subfield.substr(subfield.length-6) === 'rarity' ? direction : 'ascending';
				return { field: subfield, direction: subdirection };
			});
		}
		if (column != 'name') subsort.push({ field: 'name', direction: 'ascending' });
		sortConfig.subsort = subsort;

		// Use original dataset for sorting
		const sorted: IResultSortDataBy = sortDataBy([...props.data], sortConfig);
		data = sorted.result;

		// Sorting by pre-calculated ranks should filter out crew without matching skills
		//	Otherwise crew without skills show up first (because 0 comes before 1)
		if (column.substr(0, 5) === 'ranks') {
			const rank = column.split('.')[1];
			data = data.filter(row => row.ranks[rank] > 0);
		}
	}

	// Filtering
	let filters = [];
	if (searchFilter) {
		let grouped = searchFilter.split(/\s+OR\s+/i);
		grouped.forEach(group => {
			filters.push(SearchString.parse(group));
		});
	}
	data = data.filter(row => props.filterRow(row, filters, filterType));

	// Pagination
	let activePage = pagination_page;
	if (activeLock) {
		const index = data.findIndex(row => isRowActive(row, activeLock));
		// Locked crew is not viewable in current filter
		if (index < 0) {
			setActiveLock(undefined);
			return (<></>);
		}
		activePage = Math.floor(index / pagination_rows) + 1;
	}
	let totalPages = Math.ceil(data.length / pagination_rows);
	if (activePage > totalPages) activePage = totalPages;
	data = data.slice(pagination_rows * (activePage - 1), pagination_rows * activePage);

	return (
		<div>
			<Input
				style={{ width: isMobile ? '100%' : '50%' }}
				iconPosition="left"
				placeholder="Search..."
				value={searchFilter}
				onChange={(e, { value }) => onChangeFilter(value)}>
					<input />
					<Icon name='search' />
					<Button icon onClick={() => onChangeFilter('')} >
						<Icon name='delete' />
					</Button>
			</Input>

			{props.showFilterOptions && (
				<span style={{ paddingLeft: '2em' }}>
					<Dropdown inline
								options={filterTypeOptions}
								value={filterType}
								onChange={(event, {value}) => setFilterType(value as number)}
					/>
				</span>
			)}

			<Popup wide trigger={<Icon name="help" />}
				header={'Advanced search'}
				content={props.explanation ? props.explanation : renderDefaultExplanation()}
			/>

			{props.lockable && <LockButtons lockable={props.lockable} activeLock={activeLock} setLock={onLockableClick} />}

			<Table sortable celled selectable striped collapsing unstackable compact="very">
				<Table.Header>{renderTableHeader(column, direction)}</Table.Header>
				<Table.Body>{data.map((row, idx) => props.renderTableRow(row, idx, isRowActive(row, activeLock)))}</Table.Body>
				<Table.Footer>
					<Table.Row>
						<Table.HeaderCell colSpan={props.config.length}>
							<Pagination
								totalPages={totalPages}
								activePage={activePage}
								onPageChange={(event, { activePage }) => {
									setPaginationPage(activePage as number);
									setActiveLock(undefined);	// Remove lock when changing pages
								}}
							/>
							<span style={{ paddingLeft: '2em'}}>
								Rows per page:{' '}
								<Dropdown
									inline
									options={pagingOptions}
									value={pagination_rows}
									onChange={(event, {value}) => {
										setPaginationPage(1);
										setPaginationRows(value as number);
									}}
								/>
							</span>
							{props.showPermalink && (<span style={{ paddingLeft: '5em'}}>{renderPermalink()}</span>)}
						</Table.HeaderCell>
					</Table.Row>
				</Table.Footer>
			</Table>
		</div>
	);
}
Example #21
Source File: GradeDist.tsx    From peterportal-client with MIT License 4 votes vote down vote up
GradeDist: FC<GradeDistProps> = (props) => {
  /*
 * Initialize a GradeDist block on the webpage.
 * @param props attributes received from the parent element
 */

  const [gradeDistData, setGradeDistData] = useState<GradeDistData>(null!);
  const [chartType, setChartType] = useState<ChartTypes>('bar');
  const [currentQuarter, setCurrentQuarter] = useState('');
  const [currentProf, setCurrentProf] = useState('');
  const [profEntries, setProfEntries] = useState<Entry[]>(null!);
  const [currentCourse, setCurrentCourse] = useState('');
  const [courseEntries, setCourseEntries] = useState<Entry[]>(null!);
  const [quarterEntries, setQuarterEntries] = useState<Entry[]>(null!);

  const fetchGradeDistData = () => {
    let url = '';
    let params = {};
    // course context
    if (props.course) {
      url = `/courses/api/grades`;
      params = {
        department: props.course.department,
        number: props.course.number
      }
    }
    else if (props.professor) {
      url = `/professors/api/grades/${props.professor.shortened_name}`;
    }
    const res = axios.get<GradeDistData>(url, {
      params: params
    })
      .then(res => {
        setGradeDistData(res.data);
      });
  }

  // initial request to get grade dist data
  useEffect(() => {
    if (gradeDistData == null) {
      fetchGradeDistData();
    }
  })

  // get new data if choose a new course or professor
  useEffect(() => {
    setGradeDistData([]);
    fetchGradeDistData();
  }, [props.course?.id, props.professor?.ucinetid])

  // update list of professors/courses when new course/professor is detected
  useEffect(() => {
    if (gradeDistData && gradeDistData.length !== 0) {
      if (props.course) {
        createProfEntries();
      }
      else if (props.professor) {
        createCourseEntries();
      }
    }
  }, [gradeDistData])

  // update list of quarters when new professor/course is chosen
  useEffect(() => {
    if ((currentProf || currentCourse) && gradeDistData.length !== 0) {
      createQuarterEntries();
    }
  }, [currentProf, currentCourse])

  /*
   * Create an array of objects to feed into the quarter dropdown menu.
   * @return an array of JSON objects recording each quarter
   */
  const createQuarterEntries = () => {
    let quarters: Set<string> = new Set()
    let result: Entry[] = [{ value: 'ALL', text: 'All Quarters' }];

    gradeDistData
      .filter(entry => {
        if (props.course && entry.instructor === currentProf) {
          return true;
        }
        if (props.professor && (entry.department + ' ' + entry.number) == currentCourse) {
          return true;
        }
        return false;
      })
      .forEach(data => quarters.add(data.quarter + ' ' + data.year));
    quarters.forEach(quarter => result.push({ value: quarter, text: quarter }));

    setQuarterEntries(result);
    setCurrentQuarter(result[0].value);
  }

  /*
   * Create an array of objects to feed into the professor dropdown menu.
   * @return an array of JSON objects recording professor's names
   */
  const createProfEntries = () => {
    let professors: Set<string> = new Set()
    let result: Entry[] = [];

    gradeDistData
      .forEach(match => professors.add(match.instructor));

    professors.forEach(professor => result.push(
      { value: professor, text: professor }
    ));

    setProfEntries(result);
    setCurrentProf(result[0].value);
  }

  /*
 * Create an array of objects to feed into the course dropdown menu.
 * @return an array of JSON objects recording course's names
 */
  const createCourseEntries = () => {
    let courses: Set<string> = new Set()
    let result: Entry[] = [];

    gradeDistData
      .forEach(match => courses.add(match.department + ' ' + match.number));

    courses.forEach(course => result.push(
      { value: course, text: course }
    ));

    setCourseEntries(result);
    setCurrentCourse(result[0].value);
  }

  /*
   * Record what is in the quarter dropdown menu at the moment.
   * @param event an event object recording the mouse movement, etc.
   * @param status details about the status in the dropdown menu
   */
  const updateCurrentQuarter = (event: React.SyntheticEvent<HTMLElement>, status: DropdownProps) => {
    setCurrentQuarter(status.value as string);
  }

  /*
   * Record what is in the professor dropdown menu at the moment.
   * @param event an event object recording the mouse movement, etc.
   * @param status details about the status in the dropdown menu
   */
  const updateCurrentProf = (event: React.SyntheticEvent<HTMLElement>, status: DropdownProps) => {
    setCurrentProf(status.value as string);
  }

  /*
 * Record what is in the course dropdown menu at the moment.
 * @param event an event object recording the mouse movement, etc.
 * @param status details about the status in the dropdown menu
 */
  const updateCurrentCourse = (event: React.SyntheticEvent<HTMLElement>, status: DropdownProps) => {
    setCurrentCourse(status.value as string);
  }

  if (gradeDistData !== null && gradeDistData.length !== 0) {
    let graphProps = {
      gradeData: gradeDistData,
      quarter: currentQuarter,
      course: currentCourse,
      professor: currentProf
    }
    return (
      <div className={`gradedist-module-container ${props.minify ? 'grade-dist-mini' : ''}`}>
        <Grid.Row id='menu'>
          {
            props.minify && <Grid.Column className='gradedist-filter'>
              <Dropdown
                placeholder='Chart Type'
                scrolling
                selection
                options={[{ text: 'Bar', value: 'bar' }, { text: 'Pie', value: 'pie' }]}
                value={chartType}
                onChange={(e, s) => setChartType(s.value as ChartTypes)}
              />
            </Grid.Column>
          }

          <Grid.Column className='gradedist-filter'>
            <Dropdown
              placeholder={props.course ? 'Professor' : 'Course'}
              scrolling
              selection
              options={props.course ? profEntries : courseEntries}
              value={props.course ? currentProf : currentCourse}
              onChange={props.course ? updateCurrentProf : updateCurrentCourse}
            />
          </Grid.Column>

          <Grid.Column className='gradedist-filter'>
            <Dropdown
              placeholder='Quarter'
              scrolling
              selection
              options={quarterEntries}
              value={currentQuarter}
              onChange={updateCurrentQuarter}
            />
          </Grid.Column>
        </Grid.Row>

        <Grid.Row id='chart'>
          {
            (props.minify && chartType == 'bar' || !props.minify) && <div className={'grade_distribution_chart-container chart'}>
              <Chart {...graphProps} />
            </div>
          }
          {
            (props.minify && chartType == 'pie' || !props.minify) && <div className={'grade_distribution_chart-container pie'}>
              <Pie {...graphProps} />
            </div>
          }
        </Grid.Row>
      </div>
    );
  } else {
    return (
      <div>
      </div>
    );
  }
}
Example #22
Source File: profile.tsx    From website with MIT License 4 votes vote down vote up
renderDesktop() {
		const { playerData } = this.state;

		const panes = [
			{
				menuItem: 'Crew',
				render: () => <ProfileCrew playerData={this.state.playerData} />
			},
			{
				menuItem: 'Crew (mobile)',
				render: () => <ProfileCrewMobile playerData={this.state.playerData} isMobile={false} />
			},
			{
				menuItem: 'Ships',
				render: () => <ProfileShips playerData={this.state.playerData} />
			},
			{
				menuItem: 'Items',
				render: () => <ProfileItems playerData={this.state.playerData} />
			},
			{
				menuItem: 'Other',
				render: () => <ProfileOther playerData={this.state.playerData} />
			},
			{
				menuItem: 'Charts & Stats',
				render: () => <ProfileCharts playerData={this.state.playerData} />
			}
		];

		return (
			<Layout title={playerData.player.character.display_name}>
				<Item.Group>
					<Item>
						<Item.Image
							size='tiny'
							src={`${process.env.GATSBY_ASSETS_URL}${playerData.player.character.crew_avatar
									? playerData.player.character.crew_avatar.portrait
									: 'crew_portraits_cm_empty_sm.png'
								}`}
						/>

						<Item.Content>
							<Item.Header>{playerData.player.character.display_name}</Item.Header>
							<Item.Meta>
								<Label>VIP {playerData.player.vip_level}</Label>
								<Label>Level {playerData.player.character.level}</Label>
								<Label>{playerData.calc.numImmortals} crew</Label>
								<Label>{playerData.player.character.shuttle_bays} shuttles</Label>
							</Item.Meta>
							<Item.Description>
								{playerData.player.fleet && (
									<p>
										Fleet{' '}
										<Link to={`/fleet_info?fleetid=${playerData.player.fleet.id}`}>
											<b>{playerData.player.fleet.slabel}</b>
										</Link>{' '}
											({playerData.player.fleet.rank}) Starbase level {playerData.player.fleet.nstarbase_level}{' '}
									</p>
								)}
							</Item.Description>
						</Item.Content>
					</Item>
				</Item.Group>

				<Menu compact>
					<Menu.Item>
						{playerData.calc.lastModified ? <span>Last updated: {playerData.calc.lastModified.toLocaleString()}</span> : <span />}
					</Menu.Item>
					<Dropdown item text='Download'>
						<Dropdown.Menu>
							<Dropdown.Item onClick={() => this._exportExcel()}>Complete spreadsheet (XLSX)</Dropdown.Item>
							<Dropdown.Item onClick={() => this._exportCrew()}>Crew table (CSV)</Dropdown.Item>
							<Dropdown.Item onClick={() => this._exportShips()}>Ship table (CSV)</Dropdown.Item>
							<Dropdown.Item onClick={() => this._exportItems()}>Item table (CSV)</Dropdown.Item>
						</Dropdown.Menu>
					</Dropdown>
				</Menu>
				<Tab menu={{ secondary: true, pointing: true }} panes={panes} />
			</Layout>
		);
	}
Example #23
Source File: playertools.tsx    From website with MIT License 4 votes vote down vote up
PlayerToolsPanes = (props: PlayerToolsPanesProps) => {
	const { playerData, strippedPlayerData, voyageData, eventData, activeCrew, dataSource,
			allCrew, allItems, requestShowForm, requestClearData } = props;

	const [showIfStale, setShowIfStale] = useStateWithStorage('tools/showStale', true);

	const [showShare, setShowShare] = useStateWithStorage(playerData.player.dbid+'/tools/showShare', true, { rememberForever: true, onInitialize: variableReady });
	const [profileAutoUpdate, setProfileAutoUpdate] = useStateWithStorage(playerData.player.dbid+'/tools/profileAutoUpdate', false, { rememberForever: true });
	const [profileUploaded, setProfileUploaded] = React.useState(false);
	const [profileUploading, setProfileUploading] = React.useState(false);
	const [profileShared, setProfileShared] = useStateWithStorage('tools/profileShared', false);

	const [varsReady, setVarsReady] = React.useState(false);
	const [activeTool, setActiveTool] = React.useState('voyage');

	React.useEffect(() => {
		if (dataSource == 'input' && profileAutoUpdate && !profileUploaded) {
			console.log('Uploading profile');
			shareProfile();
		}
	}, [profileAutoUpdate, strippedPlayerData]);

	const tools = playerTools;
	React.useEffect(() => {
		const urlParams = new URLSearchParams(window.location.search);
		if (urlParams.has('tool') && tools[urlParams.get('tool')])
			setActiveTool(urlParams.get('tool'));
	}, [window.location.search]);

	const StaleMessage = () => {
		const STALETHRESHOLD = 3;	// in hours
		if (showIfStale && new Date().getTime()-playerData.calc.lastModified.getTime() > STALETHRESHOLD*60*60*1000) {
			return (
				<Message
					warning
					icon='clock'
					header='Update your player data'
					content="It's been a few hours since you last updated your player data. We recommend that you update now to make sure our tools are providing you recent information about your crew."
					onDismiss={() => setShowIfStale(false)}
				/>
			);
		}
		else {
			return (<></>);
		}
	};

	const ShareMessage = () => {
		if (!showShare) return (<></>);

		// The option to auto-share profile only appears after a profile is uploaded or if previously set to auto-update
		const bShowUploaded = profileUploaded || profileAutoUpdate;

		return (
			<Message icon onDismiss={() => setShowShare(false)}>
				<Icon name='share alternate' />
				<Message.Content>
					<Message.Header>Share your player profile!</Message.Header>
					{!bShowUploaded && (
						<div>
							<p>
								Click here to{' '}
								<Button size='small' color='green' onClick={() => shareProfile()}>
									{profileUploading && <Icon loading name='spinner' />}share your profile
									</Button>{' '}
									and unlock more tools and export options for items and ships. More details:
							</p>
							<Message.List>
								<Message.Item>
									Once shared, the profile will be publicly accessible, will be accessible by your DBID link, and linked on related pages (such as fleet pages & event pages)
									</Message.Item>
								<Message.Item>
									There is no private information included in the player profile; information being shared is limited to:{' '}
									<b>captain name, level, vip level, fleet name and role, achievements, completed missions, your crew, items and ships.</b>
								</Message.Item>
							</Message.List>
						</div>
					)}
					{bShowUploaded && (
						<Form.Group>
							<p>
								Your profile was uploaded. Share the link:{' '}
								<a
									href={`${process.env.GATSBY_DATACORE_URL}profile/?dbid=${playerData.player.dbid}`}
									target='_blank'>{`${process.env.GATSBY_DATACORE_URL}profile/?dbid=${playerData.player.dbid}`}</a>
							</p>
							<Form.Field
								control={Checkbox}
								label='Automatically share profile after every import'
								checked={profileAutoUpdate}
								onChange={(e, { checked }) => setProfileAutoUpdate(checked)}
							/>
						</Form.Group>
					)}
				</Message.Content>
			</Message>
		);
	};

	if (!varsReady)
		return (<PlayerToolsLoading />);

	const PlayerLevelProgress = () => {
		const endingValue = playerData.player.character.xp_for_next_level - playerData.player.character.xp_for_current_level;
		const currentValue = playerData.player.character.xp - playerData.player.character.xp_for_current_level;
		const percent = (currentValue / endingValue) * 100;
		return (
		  <Progress
			percent={percent.toPrecision(3)}
			label={`Level ${playerData.player.character.level}: ${playerData.player.character.xp} / ${playerData.player.character.xp_for_next_level}`}
			progress
		  />
		);
	 };

	return (
		<Layout title='Player tools'>
			<Header as='h4'>Hello, {playerData.player.character.display_name}</Header>
			<PlayerLevelProgress />
			<StaleMessage />
			<Menu compact stackable>
				<Menu.Item>
					Last imported: {playerData.calc.lastModified.toLocaleString()}
				</Menu.Item>
				<Dropdown item text='Profile options'>
					<Dropdown.Menu>
						<Dropdown.Item onClick={() => requestShowForm(true)}>Update now...</Dropdown.Item>
						{!showShare && (<Dropdown.Item onClick={() => setShowShare(true)}>Share profile...</Dropdown.Item>)}
						<Dropdown.Item onClick={() => requestClearData()}>Clear player data</Dropdown.Item>
					</Dropdown.Menu>
				</Dropdown>
			  <Dropdown item text='Export'>
				<Dropdown.Menu>
					<Popup basic content='Download crew data as traditional comma delimited CSV file' trigger={
						<Dropdown.Item onClick={() => exportCrewTool()} content='Download CSV...' />
					} />
					<Popup basic content='Copy crew data to clipboard in Google Sheets format' trigger={
						<Dropdown.Item onClick={() => exportCrewToClipboard()} content='Copy to clipboard' />
					} />
				</Dropdown.Menu>
			</Dropdown>
			</Menu>

			<React.Fragment>
				<ShareMessage />
				<Header as='h3'>{tools[activeTool].title}</Header>
				{tools[activeTool].render(props)}
			</React.Fragment>
		</Layout>
	);

	function variableReady(keyName: string) {
		setVarsReady(true);
	}

	function shareProfile() {
		setProfileUploading(true);

		let jsonBody = JSON.stringify({
			dbid: playerData.player.dbid,
			player_data: strippedPlayerData
		});

		fetch(`${process.env.GATSBY_DATACORE_URL}api/post_profile`, {
			method: 'post',
			headers: {
				'Content-Type': 'application/json'
			},
			body: jsonBody
		}).then(() => {
			if (!profileAutoUpdate) window.open(`${process.env.GATSBY_DATACORE_URL}profile/?dbid=${playerData.player.dbid}`, '_blank');
			setProfileUploading(false);
			setProfileUploaded(true);
			setProfileShared(true);
		});
	}

	function exportCrewTool() {
		let text = exportCrew(playerData.player.character.crew.concat(playerData.player.character.unOwnedCrew));
		downloadData(`data:text/csv;charset=utf-8,${encodeURIComponent(text)}`, 'crew.csv');
	}

	function exportCrewToClipboard() {
		let text = exportCrew(playerData.player.character.crew.concat(playerData.player.character.unOwnedCrew), '\t');
		navigator.clipboard.writeText(text);
	}
}
Example #24
Source File: crewchallenge.tsx    From website with MIT License 4 votes vote down vote up
CustomRules = (props: CustomRulesProps) => {
	const portalCrew = React.useContext(PortalCrewContext);
	const [modalIsOpen, setModalIsOpen] = React.useState(false);
	const [guesses, setGuesses] = React.useState(props.rules.guesses);
	const [series, setSeries] = React.useState(props.rules.series);
	const [rarities, setRarities] = React.useState(props.rules.rarities);
	const [excludedCrew, setExcludedCrew] = React.useState([]);

	React.useEffect(() => {
		const excludes = portalCrew.filter(crew => !series.includes(crew.series) || !rarities.includes(crew.max_rarity)).map(crew => crew.symbol);
		setExcludedCrew([...excludes]);
	}, [series, rarities]);

	const guessOptions = [];
	for (let i = 1; i <= 20; i++) {
		guessOptions.push(
			{ key: i, value: i, text: i }
		);
	}

	const seriesOptions = [
		{ key: 'tos', value: 'tos', text: 'The Original Series' },
		{ key: 'tas', value: 'tas', text: 'The Animated Series' },
		{ key: 'tng', value: 'tng', text: 'The Next Generation' },
		{ key: 'ds9', value: 'ds9', text: 'Deep Space Nine' },
		{ key: 'voy', value: 'voy', text: 'Voyager' },
		{ key: 'ent', value: 'ent', text: 'Enterprise' },
		{ key: 'dsc', value: 'dsc', text: 'Discovery' },
		{ key: 'pic', value: 'pic', text: 'Picard' },
		{ key: 'low', value: 'low', text: 'Lower Decks' },
		{ key: 'snw', value: 'snw', text: 'Strange New Worlds' },
		{ key: 'original', value: 'original', text: 'Timelines Originals' }
	];

	const rarityOptions = [
		{ key: '1*', value: 1, text: '1* Common' },
		{ key: '2*', value: 2, text: '2* Uncommon' },
		{ key: '3*', value: 3, text: '3* Rare' },
		{ key: '4*', value: 4, text: '4* Super Rare' },
		{ key: '5*', value: 5, text: '5* Legendary' }
	];

	const isDefault = guesses === DEFAULT_GUESSES && series.length === DEFAULT_SERIES.length && rarities.length === DEFAULT_RARITIES.length;
	const isDirty = guesses !== props.rules.guesses || series.length !== props.rules.series.length || rarities.length !== props.rules.rarities.length;
	const isValid = portalCrew.length - excludedCrew.length > 0;

	return (
		<Modal
			open={modalIsOpen}
			onClose={() => { revertRules(); setModalIsOpen(false); }}
			onOpen={() => setModalIsOpen(true)}
			trigger={renderTrigger()}
			size='tiny'
		>
			<Modal.Header>
				Custom rules
				<span style={{ paddingLeft: '1em', fontSize: '.9em', fontWeight: 'normal' }}>
					(Possible solutions: {portalCrew.length - excludedCrew.length})
				</span>
			</Modal.Header>
			<Modal.Content>
				<div>
					Max guesses:{' '}
					<Dropdown selection
						options={guessOptions}
						value={guesses}
						onChange={(e, { value }) => setGuesses(value)}
					/>
				</div>
				<div style={{ marginTop: '1em' }}>
					Include crew by series:
					<Dropdown selection multiple fluid clearable closeOnChange
						placeholder='Select at least 1 series'
						options={seriesOptions}
						value={series}
						onChange={(e, { value }) => setSeries(value)}
					/>
				</div>
				<div style={{ marginTop: '1em' }}>
					Include crew by rarity:
					<Dropdown selection multiple fluid clearable closeOnChange
						placeholder='Select at least 1 rarity'
						options={rarityOptions}
						value={rarities}
						onChange={(e, { value }) => setRarities(value)}
					/>
				</div>
			</Modal.Content>
			<Modal.Actions>
				{!isDefault && <Button content='Reset' onClick={() => resetRules()} />}
				{isDirty && <Button positive={isValid ? true : undefined} content='New Practice Game' onClick={() => applyRules()} />}
				{!isDirty && <Button content='Close' onClick={() => setModalIsOpen(false)} />}
			</Modal.Actions>
		</Modal>
	);

	function renderTrigger(): JSX.Element {
		return (
			<span style={{ paddingLeft: '1em' }}>
				<Button compact>
					{!isDefault && <span><Icon name='check' color='green' /> Use custom rules</span>}
					{isDefault && <span>Use custom rules...</span>}
				</Button>
			</span>
		);
	}

	function revertRules(): void {
		setGuesses(props.rules.guesses);
		setSeries(props.rules.series);
		setRarities(props.rules.rarities);
	}

	function resetRules(): void {
		setGuesses(DEFAULT_GUESSES);
		setSeries(DEFAULT_SERIES);
		setRarities(DEFAULT_RARITIES);
	}

	function applyRules(): void {
		if (!isValid) return;
		const newRules = new GameRules();
		newRules.guesses = guesses;
		newRules.excludedCrew = excludedCrew;
		newRules.series = series;
		newRules.rarities = rarities;
		props.changeRules(newRules);
		setModalIsOpen(false);
	}
}
Example #25
Source File: ProfileWidgetPlus.tsx    From communitymap-ui with Apache License 2.0 4 votes vote down vote up
ProfileWidgetPlus: React.FC = () => {
  const user = useAuth();
  const { dialogs } = useMyDirectMessages();
  const unreadDialogs =
    dialogs?.filter((dlg) => dlg.lastMsgId !== dlg.lastReadBy[user?.uid || ''])
      .length || 0;

  const [login, setLogin] = useState(false);
  useEffect(() => {
    if (user && login) {
      setLogin(false);
    }
  }, [user, login]);

  const [showProfile, setShowProfile] = useState(false);

  const signOut = () => getFirebaseApp().auth().signOut();

  return (
    <div id="profile-widget">
      {login && <Login title="" onClose={() => setLogin(false)} />}
      {showProfile && !!user && (
        <Modal open closeIcon onClose={() => setShowProfile(false)}>
          <Modal.Header>Your profile</Modal.Header>
          <Modal.Content>
            <EditUserProfile user={user} />
          </Modal.Content>
        </Modal>
      )}
      {user ? (
        <Dropdown
          trigger={
            <Button className="profile-button" icon size="large">
              <Icon.Group>
                <Icon name="user outline" />
                {unreadDialogs > 0 && (
                  <Icon
                    corner="top right"
                    className="has-unread-messages"
                    name="mail"
                    color="red"
                  />
                )}
              </Icon.Group>
            </Button>
          }
          pointing="top right"
          icon={null}
        >
          <Dropdown.Menu>
            <Dropdown.Item disabled>{user.email}</Dropdown.Item>
            <Dropdown.Divider />
            <Dropdown.Item onClick={() => setShowProfile(true)}>
              <Icon name="user" />
              Profile
            </Dropdown.Item>
            <Dropdown.Item as={Link} to="/my-messages">
              <Icon name="mail" />
              Messages
              {unreadDialogs > 0 && (
                <Label className="user-menu-label" color="blue">
                  {unreadDialogs}
                </Label>
              )}
            </Dropdown.Item>
            <Dropdown.Divider />
            <Dropdown.Item as={Link} to="/terms">
              Terms of Service
            </Dropdown.Item>
            <Dropdown.Item as={Link} to="/privacy">
              Privacy Policy
            </Dropdown.Item>
            <Dropdown.Divider />
            <Dropdown.Item onClick={signOut}>
              <Icon name="log out" />
              Log out
            </Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      ) : (
        // <Button primary size="large" onClick={() => setLogin(true)}>
        //   Sign in
        // </Button>
        <Dropdown
          trigger={
            <Button className="profile-button" icon size="large">
              <Icon name="bars" />
            </Button>
          }
          pointing="top right"
          icon={null}
        >
          <Dropdown.Menu>
            <Dropdown.Item as={Link} to="/terms">
              Terms of Service
            </Dropdown.Item>
            <Dropdown.Item as={Link} to="/privacy">
              Privacy Policy
            </Dropdown.Item>
            <Dropdown.Divider />
            <Dropdown.Item onClick={() => setLogin(true)}>
              <Icon name="sign in" />
              Sign in
            </Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      )}
    </div>
  );
}
Example #26
Source File: citeoptimizer.tsx    From website with MIT License 4 votes vote down vote up
renderTable(data, training = true) {
		const createStateAccessors = (name) => [
			this.state[name],
			(value) => this.setState((prevState) => { prevState[name] = value; return prevState; })
		];
		const [paginationPage, setPaginationPage] = createStateAccessors(training ? 'trainingPage' : 'citePage');
		const [otherPaginationPage, setOtherPaginationPage] = createStateAccessors(training ? 'citePage' : 'trainingPage');
		const [paginationRows, setPaginationRows] = createStateAccessors('paginationRows');

		const baseRow = (paginationPage - 1) * paginationRows;
		const totalPages = Math.ceil(data.length / paginationRows);

		return (
			<Table sortable celled selectable striped collapsing unstackable compact="very">
				<Table.Header>
					<Table.Row>
						<Table.HeaderCell>Rank</Table.HeaderCell>
						<Table.HeaderCell>Crew</Table.HeaderCell>
						<Table.HeaderCell>Rarity</Table.HeaderCell>
						<Table.HeaderCell>Final EV</Table.HeaderCell>
						{!training &&
						<React.Fragment>
							<Table.HeaderCell>Remaining EV</Table.HeaderCell>
							<Table.HeaderCell>EV Per Citation</Table.HeaderCell>
						</React.Fragment>
						}
						<Table.HeaderCell>Voyages Improved</Table.HeaderCell>
					</Table.Row>
				</Table.Header>
				<Table.Body>
					{data.slice(baseRow, baseRow + paginationRows).map((row, idx) => {
						const crew = this.props.playerData.player.character.crew.find(c => c.name == row.name);

						return (
							<Table.Row>
								<Table.Cell>{baseRow + idx + 1}</Table.Cell>
								<Table.Cell>
									<div
										style={{
											display: 'grid',
											gridTemplateColumns: '60px auto',
											gridTemplateAreas: `'icon stats' 'icon description'`,
											gridGap: '1px'
										}}>
										<div style={{ gridArea: 'icon' }}>
											<img width={48} src={`${process.env.GATSBY_ASSETS_URL}${crew.imageUrlPortrait}`} />
										</div>
										<div style={{ gridArea: 'stats' }}>
											<span style={{ fontWeight: 'bolder', fontSize: '1.25em' }}>{crew.name}</span>
										</div>
									</div>
								</Table.Cell>
								<Table.Cell>
									<Rating icon='star' rating={crew.rarity} maxRating={crew.max_rarity} size='large' disabled />
								</Table.Cell>
								<Table.Cell>
									{Math.ceil(training ? row.addedEV : row.totalEVContribution)}
								</Table.Cell>
								{
									!training &&
									<React.Fragment>
										<Table.Cell>
											{Math.ceil(row.totalEVRemaining)}
										</Table.Cell>
										<Table.Cell>
											{Math.ceil(row.evPerCitation)}
										</Table.Cell>
									</React.Fragment>
								}
								<Table.Cell>
									<Popup trigger={<b>{row.voyagesImproved.length}</b>} content={row.voyagesImproved.join(', ')} />
								</Table.Cell>
							</Table.Row>
						);
					})}
				</Table.Body>
				<Table.Footer>
					<Table.Row>
						<Table.HeaderCell colSpan={9}>
							<Pagination
								totalPages={totalPages}
								activePage={paginationPage}
								onPageChange={(event, { activePage }) => setPaginationPage(activePage as number)}
							/>
							<span style={{ paddingLeft: '2em' }}>
								Rows per page:{' '}
								<Dropdown
									inline
									options={pagingOptions}
									value={paginationRows}
									onChange={(event, { value }) => {
										setPaginationPage(1);
										setOtherPaginationPage(1);
										setPaginationRows(value as number);
									}}
								/>
							</span>
						</Table.HeaderCell>
					</Table.Row>
				</Table.Footer>
			</Table>
		);
	}
Example #27
Source File: voyagecalculator.tsx    From website with MIT License 4 votes vote down vote up
InputCrewExcluder = (props: InputCrewExcluderProps) => {
	const { updateExclusions } = props;

	const [eventData, setEventData] = useStateWithStorage('tools/eventData', undefined);
	const [activeEvent, setActiveEvent] = React.useState(undefined);
	const [options, setOptions] = React.useState(undefined);

	React.useEffect(() => {
		if (props.excludedCrew)
			if (options && !options.initialized) populatePlaceholders();
	}, [props.excludedCrew]);

	React.useEffect(() => {
		if (!options) return;
		if (!options.initialized)
			populatePlaceholders();
		else
			populateOptions();
	}, [props.showFrozen]);

	React.useEffect(() => {
		if (activeEvent && activeEvent.seconds_to_end > 0 && activeEvent.seconds_to_start < 86400) {
			if (activeEvent.content_types.includes('shuttles') || activeEvent.content_types.includes('gather')) {
				const crewIds = props.myCrew.filter(c => activeEvent.bonus.includes(c.symbol)).map(c => c.id);
				updateExclusions([...crewIds]);
			}
		}
	}, [activeEvent]);

	if (!activeEvent) {
		identifyActiveEvent();
		return (<></>);
	}

	if (!options) {
		populatePlaceholders();
		return (<></>);
	}

	const label = (
		<React.Fragment>
			<label>Crew to exclude from voyage</label>
			{activeEvent && activeEvent.bonus.length > 0 &&
				(<div style={{ margin: '-.5em 0 .5em' }}>Preselected crew give bonuses for the event <b>{activeEvent.name}</b></div>)
			}
		</React.Fragment>
	);

	return (
		<React.Fragment>
			<Form.Field
				label={label}
				placeholder='Search for crew to exclude'
				control={Dropdown}
				clearable
				fluid
				multiple
				search
				selection
				options={options.list}
				value={props.excludedCrew}
				onFocus={() => { if (!options.initialized) populateOptions(); }}
				onChange={(e, { value }) => updateExclusions(value) }
			/>
		</React.Fragment>
	);

	function identifyActiveEvent(): void {
		// Get event data from recently uploaded playerData
		if (eventData && eventData.length > 0) {
			const currentEvent = getEventData(eventData.sort((a, b) => (a.seconds_to_start - b.seconds_to_start))[0]);
			setActiveEvent({...currentEvent});
		}
		// Otherwise guess event from autosynced events
		else {
			guessCurrentEvent().then(currentEvent => {
				setActiveEvent({...currentEvent});
			});
		}
	}

	function populatePlaceholders(): void {
		const options = { initialized: false, list: [] };
		if (props.excludedCrew.length > 0) {
			let crewList = [...props.myCrew];
			if (!props.showFrozen) crewList = crewList.filter(c => c.immortal == 0);
			options.list = crewList.filter(c => props.excludedCrew.includes(c.id)).map(c => {
				return { key: c.id, value: c.id, text: c.name, image: { avatar: true, src: `${process.env.GATSBY_ASSETS_URL}${c.imageUrlPortrait}` }};
			});
		}
		else {
			options.list = [{ key: 0, value: 0, text: 'Loading...' }];
		}
		setOptions({...options});
	}

	function populateOptions(): void {
		let crewList = [...props.myCrew];
		if (!props.showFrozen) crewList = crewList.filter(c => c.immortal == 0);
		options.list = crewList.sort((a, b) => a.name.localeCompare(b.name)).map(c => {
			return { key: c.id, value: c.id, text: c.name, image: { avatar: true, src: `${process.env.GATSBY_ASSETS_URL}${c.imageUrlPortrait}` }};
		});
		options.initialized = true;
		setOptions({...options});
	}
}
Example #28
Source File: voyagecalculator.tsx    From website with MIT License 4 votes vote down vote up
VoyageEditConfigModal = (props: VoyageEditConfigModalProps) => {
	const { updateConfig } = props;

	const [voyageConfig, setVoyageConfig] = React.useState(props.voyageConfig);

	const [modalIsOpen, setModalIsOpen] = React.useState(false);
	const [updateOnClose, setUpdateOnClose] = React.useState(false);
	const [options, setOptions] = React.useState(undefined);

	React.useEffect(() => {
		if (!modalIsOpen && updateOnClose) {
			updateConfig(voyageConfig);
			setUpdateOnClose(false);
		}

	}, [modalIsOpen]);

	const defaultSlots = [
		{ symbol: 'captain_slot', name: 'First Officer', skill: 'command_skill', trait: '' },
		{ symbol: 'first_officer', name: 'Helm Officer', skill: 'command_skill', trait: '' },
		{ symbol: 'chief_communications_officer', name: 'Communications Officer', skill: 'diplomacy_skill', trait: '' },
		{ symbol: 'communications_officer', name: 'Diplomat', skill: 'diplomacy_skill', trait: '' },
		{ symbol: 'chief_security_officer', name: 'Chief Security Officer', skill: 'security_skill', trait: '' },
		{ symbol: 'security_officer', name: 'Tactical Officer', skill: 'security_skill', trait: '' },
		{ symbol: 'chief_engineering_officer', name: 'Chief Engineer', skill: 'engineering_skill', trait: '' },
		{ symbol: 'engineering_officer', name: 'Engineer', skill: 'engineering_skill', trait: '' },
		{ symbol: 'chief_science_officer', name: 'Chief Science Officer', skill: 'science_skill', trait: '' },
		{ symbol: 'science_officer', name: 'Deputy Science Officer', skill: 'science_skill', trait: '' },
		{ symbol: 'chief_medical_officer', name: 'Chief Medical Officer', skill: 'medicine_skill', trait: '' },
		{ symbol: 'medical_officer', name: 'Ship\'s Counselor', skill: 'medicine_skill', trait: '' }
	];
	const crewSlots = voyageConfig.crew_slots ?? defaultSlots;
	crewSlots.sort((s1, s2) => CONFIG.VOYAGE_CREW_SLOTS.indexOf(s1.symbol) - CONFIG.VOYAGE_CREW_SLOTS.indexOf(s2.symbol));

	return (
		<Modal
			open={modalIsOpen}
			onClose={() => setModalIsOpen(false)}
			onOpen={() => setModalIsOpen(true)}
			trigger={<Button size='small'><Icon name='edit' />Edit</Button>}
		>
			<Modal.Header>Edit Voyage</Modal.Header>
			<Modal.Content scrolling>
				{renderContent()}
			</Modal.Content>
			<Modal.Actions>
				<Button positive onClick={() => setModalIsOpen(false)}>
					Close
				</Button>
			</Modal.Actions>
		</Modal>
	);

	function renderContent(): JSX.Element {
		if (!modalIsOpen) return (<></>);

		if (!options) {
			// Renders a lot faster by using known voyage traits rather than calculate list from all possible traits
			const knownShipTraits = ['andorian','battle_cruiser','borg','breen','cardassian','cloaking_device',
				'dominion','emp','explorer','federation','ferengi','freighter','historic','hologram',
				'klingon','malon','maquis','orion_syndicate','pioneer','reman','romulan','ruthless',
				'scout','spore_drive','terran','tholian','transwarp','vulcan','warship','war_veteran','xindi'];
			const knownCrewTraits = ['android','astrophysicist','bajoran','borg','brutal',
				'cardassian','civilian','communicator','costumed','crafty','cultural_figure','cyberneticist',
				'desperate','diplomat','doctor','duelist','exobiology','explorer','federation','ferengi',
				'gambler','hero','hologram','human','hunter','innovator','inspiring','jury_rigger','klingon',
				'marksman','maverick','pilot','prodigy','resourceful','romantic','romulan',
				'saboteur','scoundrel','starfleet','survivalist','tactician','telepath','undercover_operative',
				'veteran','villain','vulcan'];

			const skillsList = [];
			for (let skill in CONFIG.SKILLS) {
				skillsList.push({
					key: skill,
					value: skill,
					text: CONFIG.SKILLS[skill]
				});
			}

			const shipTraitsList = knownShipTraits.map(trait => {
				return {
					key: trait,
					value: trait,
					text: allTraits.ship_trait_names[trait]
				};
			});
			shipTraitsList.sort((a, b) => a.text.localeCompare(b.text));

			const crewTraitsList = knownCrewTraits.map(trait => {
				return {
					key: trait,
					value: trait,
					text: allTraits.trait_names[trait]
				};
			});
			crewTraitsList.sort((a, b) => a.text.localeCompare(b.text));

			setOptions({ skills: skillsList, ships: shipTraitsList, traits: crewTraitsList });
			return (<></>);
		}

		return (
			<React.Fragment>
				<Message>Editing this voyage will reset all existing recommendations and estimates.</Message>
				<Form>
					<Form.Group>
						<Form.Select
							label='Primary skill'
							options={options.skills}
							value={voyageConfig.skills.primary_skill ?? 'command_skill'}
							onChange={(e, { value }) => setSkill('primary_skill', value)}
							placeholder='Primary'
						/>
						<Form.Select
							label='Secondary skill'
							options={options.skills}
							value={voyageConfig.skills.secondary_skill ?? 'science_skill'}
							onChange={(e, { value }) => setSkill('secondary_skill', value)}
							placeholder='Secondary'
						/>
						<Form.Select
							search clearable
							label='Ship trait'
							options={options.ships}
							value={voyageConfig.ship_trait}
							onChange={(e, { value }) => setShipTrait(value)}
							placeholder='Ship trait'
						/>
					</Form.Group>
				</Form>
				<Table compact striped>
					<Table.Header>
						<Table.Row>
							<Table.HeaderCell textAlign='center'>Skill</Table.HeaderCell>
							<Table.HeaderCell>Seat</Table.HeaderCell>
							<Table.HeaderCell>Trait</Table.HeaderCell>
						</Table.Row>
					</Table.Header>
					<Table.Body>
						{crewSlots.map((seat, idx) => (
							<Table.Row key={seat.symbol}>
								{ idx % 2 == 0 ?
									(
										<Table.Cell rowSpan='2' textAlign='center'>
											<img alt="{seat.skill}" src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_${seat.skill}.png`} style={{ height: '2em' }} />
										</Table.Cell>
									)
									: (<></>)
								}
								<Table.Cell>{seat.name}</Table.Cell>
								<Table.Cell>
									<Dropdown search selection clearable
										options={options.traits}
										value={seat.trait}
										onChange={(e, { value }) => setSeatTrait(seat.symbol, value)}
										placeholder='Trait'
									/>
								</Table.Cell>
							</Table.Row>
						))}
					</Table.Body>
				</Table>
			</React.Fragment>
		);
	}

	function setSkill(prime: string, value: string): void {
		// Flip skill values if changing to value that's currently set as the other prime
		if (prime == 'primary_skill' && value == voyageConfig.skills.secondary_skill)
			voyageConfig.skills.secondary_skill = voyageConfig.skills.primary_skill;
		else if (prime == 'secondary_skill' && value == voyageConfig.skills.primary_skill)
			voyageConfig.skills.primary_skill = voyageConfig.skills.secondary_skill;
		voyageConfig.skills[prime] = value;
		setVoyageConfig({...voyageConfig});
		setUpdateOnClose(true);
	}

	function setShipTrait(value: string): void {
		voyageConfig.ship_trait = value;
		setVoyageConfig({...voyageConfig});
		setUpdateOnClose(true);
	}

	function setSeatTrait(seat: symbol, value: string): void {
		voyageConfig.crew_slots.find(s => s.symbol === seat).trait = value;
		setVoyageConfig({...voyageConfig});
		setUpdateOnClose(true);
	}
}
Example #29
Source File: App.tsx    From watchparty with MIT License 4 votes vote down vote up
render() {
    const sharer = this.state.participants.find((p) => p.isScreenShare);
    const controls = (
      <Controls
        key={this.state.controlsTimestamp}
        togglePlay={this.togglePlay}
        onSeek={this.onSeek}
        fullScreen={this.fullScreen}
        toggleMute={this.toggleMute}
        toggleSubtitle={this.toggleSubtitle}
        setVolume={this.setVolume}
        jumpToLeader={this.jumpToLeader}
        paused={this.state.currentMediaPaused}
        muted={this.isMuted()}
        subtitled={this.isSubtitled()}
        currentTime={this.getCurrentTime()}
        duration={this.getDuration()}
        disabled={!this.haveLock()}
        leaderTime={this.isHttp() ? this.getLeaderTime() : undefined}
        isPauseDisabled={this.isPauseDisabled()}
      />
    );
    const subscribeButton = (
      <SubscribeButton
        user={this.props.user}
        isSubscriber={this.props.isSubscriber}
        isCustomer={this.props.isCustomer}
      />
    );
    const displayRightContent =
      this.state.showRightBar || this.state.fullScreen;
    const rightBar = (
      <Grid.Column
        width={displayRightContent ? 4 : 1}
        style={{ display: 'flex', flexDirection: 'column' }}
        className={`${
          this.state.fullScreen
            ? 'fullHeightColumnFullscreen'
            : 'fullHeightColumn'
        }`}
      >
        <Input
          inverted
          fluid
          label={'My name is:'}
          value={this.state.myName}
          onChange={this.updateName}
          style={{ visibility: displayRightContent ? '' : 'hidden' }}
          icon={
            <Icon
              onClick={() => this.updateName(null, { value: generateName() })}
              name="refresh"
              inverted
              circular
              link
            />
          }
        />
        {
          <Menu
            inverted
            widths={3}
            style={{
              marginTop: '4px',
              marginBottom: '4px',
              visibility: displayRightContent ? '' : 'hidden',
            }}
          >
            <Menu.Item
              name="chat"
              active={this.state.currentTab === 'chat'}
              onClick={() => {
                this.setState({ currentTab: 'chat', unreadCount: 0 });
              }}
              as="a"
            >
              Chat
              {this.state.unreadCount > 0 && (
                <Label circular color="red">
                  {this.state.unreadCount}
                </Label>
              )}
            </Menu.Item>
            <Menu.Item
              name="people"
              active={this.state.currentTab === 'people'}
              onClick={() => this.setState({ currentTab: 'people' })}
              as="a"
            >
              People
              <Label
                circular
                color={
                  getColorForString(
                    this.state.participants.length.toString()
                  ) as SemanticCOLORS
                }
              >
                {this.state.participants.length}
              </Label>
            </Menu.Item>
            <Menu.Item
              name="settings"
              active={this.state.currentTab === 'settings'}
              onClick={() => this.setState({ currentTab: 'settings' })}
              as="a"
            >
              {/* <Icon name="setting" /> */}
              Settings
            </Menu.Item>
          </Menu>
        }
        <Chat
          chat={this.state.chat}
          nameMap={this.state.nameMap}
          pictureMap={this.state.pictureMap}
          socket={this.socket}
          scrollTimestamp={this.state.scrollTimestamp}
          getMediaDisplayName={this.getMediaDisplayName}
          hide={this.state.currentTab !== 'chat' || !displayRightContent}
          isChatDisabled={this.state.isChatDisabled}
          owner={this.state.owner}
          user={this.props.user}
          ref={this.chatRef}
        />
        {this.state.state === 'connected' && (
          <VideoChat
            socket={this.socket}
            participants={this.state.participants}
            nameMap={this.state.nameMap}
            pictureMap={this.state.pictureMap}
            tsMap={this.state.tsMap}
            rosterUpdateTS={this.state.rosterUpdateTS}
            hide={this.state.currentTab !== 'people' || !displayRightContent}
            owner={this.state.owner}
            user={this.props.user}
          />
        )}
        <SettingsTab
          hide={this.state.currentTab !== 'settings' || !displayRightContent}
          user={this.props.user}
          roomLock={this.state.roomLock}
          setRoomLock={this.setRoomLock}
          socket={this.socket}
          isSubscriber={this.props.isSubscriber}
          roomId={this.state.roomId}
          isChatDisabled={this.state.isChatDisabled}
          setIsChatDisabled={this.setIsChatDisabled}
          owner={this.state.owner}
          setOwner={this.setOwner}
          vanity={this.state.vanity}
          setVanity={this.setVanity}
          roomLink={this.state.roomLink}
          password={this.state.password}
          setPassword={this.setPassword}
          clearChat={this.clearChat}
          roomTitle={this.state.roomTitle}
          setRoomTitle={this.setRoomTitle}
          roomDescription={this.state.roomDescription}
          setRoomDescription={this.setRoomDescription}
          roomTitleColor={this.state.roomTitleColor}
          setRoomTitleColor={this.setRoomTitleColor}
          mediaPath={this.state.mediaPath}
          setMediaPath={this.setMediaPath}
        />
      </Grid.Column>
    );
    return (
      <React.Fragment>
        {!this.state.isAutoPlayable && (
          <Modal inverted basic open>
            <div style={{ display: 'flex', justifyContent: 'center' }}>
              <Button
                primary
                size="large"
                onClick={() => {
                  this.setState({ isAutoPlayable: true });
                  this.setMute(false);
                  this.setVolume(1);
                }}
                icon
                labelPosition="left"
              >
                <Icon name="volume up" />
                Click to unmute
              </Button>
            </div>
          </Modal>
        )}
        {this.state.multiStreamSelection && (
          <MultiStreamModal
            streams={this.state.multiStreamSelection}
            setMedia={this.setMedia}
            resetMultiSelect={this.resetMultiSelect}
          />
        )}
        {this.state.isVBrowserModalOpen && (
          <VBrowserModal
            isSubscriber={this.props.isSubscriber}
            subscribeButton={subscribeButton}
            closeModal={() => this.setState({ isVBrowserModalOpen: false })}
            startVBrowser={this.startVBrowser}
            user={this.props.user}
            beta={this.props.beta}
          />
        )}
        {this.state.isScreenShareModalOpen && (
          <ScreenShareModal
            closeModal={() => this.setState({ isScreenShareModalOpen: false })}
            startScreenShare={this.setupScreenShare}
          />
        )}
        {this.state.isFileShareModalOpen && (
          <FileShareModal
            closeModal={() => this.setState({ isFileShareModalOpen: false })}
            startFileShare={this.setupFileShare}
          />
        )}
        {this.state.isSubtitleModalOpen && (
          <SubtitleModal
            closeModal={() => this.setState({ isSubtitleModalOpen: false })}
            socket={this.socket}
            currentSubtitle={this.state.currentSubtitle}
            src={this.state.currentMedia}
            haveLock={this.haveLock}
            getMediaDisplayName={this.getMediaDisplayName}
            beta={this.props.beta}
          />
        )}
        {this.state.error && <ErrorModal error={this.state.error} />}
        {this.state.isErrorAuth && (
          <PasswordModal
            savedPasswords={this.state.savedPasswords}
            roomId={this.state.roomId}
          />
        )}
        {this.state.errorMessage && (
          <Message
            negative
            header="Error"
            content={this.state.errorMessage}
            style={{
              position: 'fixed',
              bottom: '10px',
              right: '10px',
              zIndex: 1000,
            }}
          ></Message>
        )}
        {this.state.successMessage && (
          <Message
            positive
            header="Success"
            content={this.state.successMessage}
            style={{
              position: 'fixed',
              bottom: '10px',
              right: '10px',
              zIndex: 1000,
            }}
          ></Message>
        )}
        <TopBar
          user={this.props.user}
          isCustomer={this.props.isCustomer}
          isSubscriber={this.props.isSubscriber}
          roomTitle={this.state.roomTitle}
          roomDescription={this.state.roomDescription}
          roomTitleColor={this.state.roomTitleColor}
        />
        {
          <Grid stackable celled="internally">
            <Grid.Row id="theaterContainer">
              <Grid.Column
                width={this.state.showRightBar ? 12 : 15}
                className={
                  this.state.fullScreen
                    ? 'fullHeightColumnFullscreen'
                    : 'fullHeightColumn'
                }
              >
                <div
                  style={{
                    display: 'flex',
                    flexDirection: 'column',
                    height: '100%',
                  }}
                >
                  {!this.state.fullScreen && (
                    <React.Fragment>
                      <ComboBox
                        setMedia={this.setMedia}
                        playlistAdd={this.playlistAdd}
                        playlistDelete={this.playlistDelete}
                        playlistMove={this.playlistMove}
                        currentMedia={this.state.currentMedia}
                        getMediaDisplayName={this.getMediaDisplayName}
                        launchMultiSelect={this.launchMultiSelect}
                        streamPath={this.props.streamPath}
                        mediaPath={this.state.mediaPath}
                        disabled={!this.haveLock()}
                        playlist={this.state.playlist}
                      />
                      <Separator />
                      <div
                        className="mobileStack"
                        style={{ display: 'flex', gap: '4px' }}
                      >
                        {this.screenShareStream && (
                          <Button
                            fluid
                            className="toolButton"
                            icon
                            labelPosition="left"
                            color="red"
                            onClick={this.stopScreenShare}
                            disabled={sharer?.id !== this.socket?.id}
                          >
                            <Icon name="cancel" />
                            Stop Share
                          </Button>
                        )}
                        {!this.screenShareStream &&
                          !sharer &&
                          !this.isVBrowser() && (
                            <Popup
                              content={`Share a tab or an application. Make sure to check "Share audio" for best results.`}
                              trigger={
                                <Button
                                  fluid
                                  className="toolButton"
                                  disabled={!this.haveLock()}
                                  icon
                                  labelPosition="left"
                                  color={'instagram'}
                                  onClick={() => {
                                    this.setState({
                                      isScreenShareModalOpen: true,
                                    });
                                  }}
                                >
                                  <Icon name={'slideshare'} />
                                  Screenshare
                                </Button>
                              }
                            />
                          )}
                        {!this.screenShareStream &&
                          !sharer &&
                          !this.isVBrowser() && (
                            <Popup
                              content="Launch a shared virtual browser"
                              trigger={
                                <Button
                                  fluid
                                  className="toolButton"
                                  disabled={!this.haveLock()}
                                  icon
                                  labelPosition="left"
                                  color="green"
                                  onClick={() => {
                                    this.setState({
                                      isVBrowserModalOpen: true,
                                    });
                                  }}
                                >
                                  <Icon name="desktop" />
                                  VBrowser
                                </Button>
                              }
                            />
                          )}
                        {this.isVBrowser() && (
                          <Popup
                            content="Choose the person controlling the VBrowser"
                            trigger={
                              <Dropdown
                                icon="keyboard"
                                labeled
                                className="icon"
                                style={{ height: '36px' }}
                                button
                                value={this.state.controller}
                                placeholder="No controller"
                                clearable
                                onChange={this.changeController}
                                selection
                                disabled={!this.haveLock()}
                                options={this.state.participants.map((p) => ({
                                  text: this.state.nameMap[p.id] || p.id,
                                  value: p.id,
                                }))}
                              ></Dropdown>
                            }
                          />
                        )}
                        {this.isVBrowser() && (
                          <Dropdown
                            icon="desktop"
                            labeled
                            className="icon"
                            style={{ height: '36px' }}
                            button
                            disabled={!this.haveLock()}
                            value={this.state.vBrowserResolution}
                            onChange={(_e, data) =>
                              this.setState({
                                vBrowserResolution: data.value as string,
                              })
                            }
                            selection
                            options={[
                              {
                                text: '1080p (Plus only)',
                                value: '1920x1080@30',
                                disabled: !this.state.isVBrowserLarge,
                              },
                              {
                                text: '720p',
                                value: '1280x720@30',
                              },
                              {
                                text: '576p',
                                value: '1024x576@60',
                              },
                              {
                                text: '486p',
                                value: '864x486@60',
                              },
                              {
                                text: '360p',
                                value: '640x360@60',
                              },
                            ]}
                          ></Dropdown>
                        )}
                        {this.isVBrowser() && (
                          <Button
                            fluid
                            className="toolButton"
                            icon
                            labelPosition="left"
                            color="red"
                            disabled={!this.haveLock()}
                            onClick={this.stopVBrowser}
                          >
                            <Icon name="cancel" />
                            Stop VBrowser
                          </Button>
                        )}
                        {!this.screenShareStream &&
                          !sharer &&
                          !this.isVBrowser() && (
                            <Popup
                              content="Stream your own video file"
                              trigger={
                                <Button
                                  fluid
                                  className="toolButton"
                                  disabled={!this.haveLock()}
                                  icon
                                  labelPosition="left"
                                  onClick={() => {
                                    this.setState({
                                      isFileShareModalOpen: true,
                                    });
                                  }}
                                >
                                  <Icon name="file" />
                                  File
                                </Button>
                              }
                            />
                          )}
                        {false && (
                          <SearchComponent
                            setMedia={this.setMedia}
                            playlistAdd={this.playlistAdd}
                            type={'youtube'}
                            streamPath={this.props.streamPath}
                            disabled={!this.haveLock()}
                          />
                        )}
                        {Boolean(this.props.streamPath) && (
                          <SearchComponent
                            setMedia={this.setMedia}
                            playlistAdd={this.playlistAdd}
                            type={'stream'}
                            streamPath={this.props.streamPath}
                            launchMultiSelect={this.launchMultiSelect}
                            disabled={!this.haveLock()}
                          />
                        )}
                      </div>
                      <Separator />
                    </React.Fragment>
                  )}
                  <div style={{ flexGrow: 1 }}>
                    <div id="playerContainer">
                      {(this.state.loading ||
                        !this.state.currentMedia ||
                        this.state.nonPlayableMedia) && (
                        <div
                          id="loader"
                          className="videoContent"
                          style={{
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center',
                          }}
                        >
                          {this.state.loading && (
                            <Dimmer active>
                              <Loader>
                                {this.isVBrowser()
                                  ? 'Launching virtual browser. This can take up to a minute.'
                                  : ''}
                              </Loader>
                            </Dimmer>
                          )}
                          {!this.state.loading && !this.state.currentMedia && (
                            <Message
                              color="yellow"
                              icon="hand point up"
                              header="You're not watching anything!"
                              content="Pick something to watch above."
                            />
                          )}
                          {!this.state.loading &&
                            this.state.nonPlayableMedia && (
                              <Message
                                color="red"
                                icon="frown"
                                header="It doesn't look like this is a media file!"
                                content="Maybe you meant to launch a VBrowser if you're trying to visit a web page?"
                              />
                            )}
                        </div>
                      )}
                      <iframe
                        style={{
                          display:
                            this.isYouTube() && !this.state.loading
                              ? 'block'
                              : 'none',
                        }}
                        title="YouTube"
                        id="leftYt"
                        className="videoContent"
                        allowFullScreen
                        frameBorder="0"
                        allow="autoplay"
                        src="https://www.youtube.com/embed/?enablejsapi=1&controls=0&rel=0"
                      />
                      {this.isVBrowser() &&
                      this.getVBrowserPass() &&
                      this.getVBrowserHost() ? (
                        <VBrowser
                          username={this.socket.id}
                          password={this.getVBrowserPass()}
                          hostname={this.getVBrowserHost()}
                          controlling={this.state.controller === this.socket.id}
                          setLoadingFalse={this.setLoadingFalse}
                          resolution={this.state.vBrowserResolution}
                          doPlay={this.doPlay}
                          setResolution={(data: string) =>
                            this.setState({ vBrowserResolution: data })
                          }
                        />
                      ) : (
                        <video
                          style={{
                            display:
                              (this.isVideo() && !this.state.loading) ||
                              this.state.fullScreen
                                ? 'block'
                                : 'none',
                            width: '100%',
                            maxHeight:
                              'calc(100vh - 62px - 36px - 36px - 8px - 41px - 16px)',
                          }}
                          id="leftVideo"
                          onEnded={this.onVideoEnded}
                          playsInline
                        ></video>
                      )}
                    </div>
                  </div>
                  {this.state.currentMedia && controls}
                  {Boolean(this.state.total) && (
                    <div>
                      <Progress
                        size="tiny"
                        color="green"
                        inverted
                        value={this.state.downloaded}
                        total={this.state.total}
                        // indicating
                        label={
                          Math.min(
                            (this.state.downloaded / this.state.total) * 100,
                            100
                          ).toFixed(2) +
                          '% - ' +
                          formatSpeed(this.state.speed) +
                          ' - ' +
                          this.state.connections +
                          ' connections'
                        }
                      ></Progress>
                    </div>
                  )}
                </div>
                <Button
                  style={{
                    position: 'absolute',
                    top: '50%',
                    right: 'calc(0% - 18px)',
                    zIndex: 900,
                  }}
                  circular
                  size="mini"
                  icon={this.state.showRightBar ? 'angle right' : 'angle left'}
                  onClick={() =>
                    this.setState({ showRightBar: !this.state.showRightBar })
                  }
                />
              </Grid.Column>
              {rightBar}
            </Grid.Row>
          </Grid>
        }
      </React.Fragment>
    );
  }